diff --git a/apps/backoffice/src/app/admin/admin.module.ts b/apps/backoffice/src/app/admin/admin.module.ts index f507a5d0..ac5be78c 100644 --- a/apps/backoffice/src/app/admin/admin.module.ts +++ b/apps/backoffice/src/app/admin/admin.module.ts @@ -2,10 +2,8 @@ import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { RouterModule } from '@angular/router'; -import { TranslateModule } from '@ngx-translate/core'; import { ROUTES } from './admin.routes'; -import { SharedContentModule } from 'apps/backoffice/src/app/ui/ui.module'; import { PlaceComponent } from './admin.component'; import { PlaceDatabaseDetailsComponent } from './database-details.component'; @@ -33,6 +31,7 @@ import { ViewUploadModalComponent } from './view-upload-modal.component'; import { ResourceImportsComponent } from './resource-imports.component'; import { EmailTemplatesComponent } from './mailing-lists/email-templates.component'; import { EmailTemplateFormComponent } from './mailing-lists/email-template-form.component'; +import { SharedContentModule } from '../ui/ui.module'; @NgModule({ declarations: [ @@ -68,7 +67,6 @@ import { EmailTemplateFormComponent } from './mailing-lists/email-template-form. ReactiveFormsModule, RouterModule.forChild(ROUTES), SharedContentModule, - TranslateModule.forChild(), ], providers: [APIKeyService], }) diff --git a/apps/backoffice/src/app/alert-dashboard/dashboard.module.ts b/apps/backoffice/src/app/alert-dashboard/dashboard.module.ts index 4e677480..468f0856 100644 --- a/apps/backoffice/src/app/alert-dashboard/dashboard.module.ts +++ b/apps/backoffice/src/app/alert-dashboard/dashboard.module.ts @@ -5,7 +5,6 @@ import { MatButtonModule } from '@angular/material/button'; import { MatFormFieldModule } from '@angular/material/form-field'; import { MatSelectModule } from '@angular/material/select'; import { Route, RouterModule } from '@angular/router'; -import { TranslateModule } from '@ngx-translate/core'; import { SharedContentModule } from '../ui/ui.module'; @@ -27,7 +26,6 @@ const ROUTES: Route[] = [ MatSelectModule, FormsModule, SharedContentModule, - TranslateModule.forChild(), ], }) export class MqttDashboardModule {} diff --git a/apps/backoffice/src/app/app.component.ts b/apps/backoffice/src/app/app.component.ts index 233181d4..823e91aa 100644 --- a/apps/backoffice/src/app/app.component.ts +++ b/apps/backoffice/src/app/app.component.ts @@ -30,8 +30,8 @@ import { ActivatedRoute, NavigationEnd, Router } from '@angular/router'; import { currentUser } from './common/user-state'; import { addDays, format, getUnixTime } from 'date-fns'; -import { TranslateService } from '@ngx-translate/core'; import { setTranslationService } from './common/translate'; +import { LocaleService } from './common/locale.service'; @Component({ selector: 'placeos-root', @@ -100,7 +100,7 @@ export class AppComponent extends AsyncHandler implements OnInit { private _snackbar: MatSnackBar, private _router: Router, private _route: ActivatedRoute, - @Optional() private _translate: TranslateService + @Optional() private _locale: LocaleService ) { super(); } @@ -114,7 +114,7 @@ export class AppComponent extends AsyncHandler implements OnInit { this._route.queryParamMap.subscribe((params) => { if (params.has('lang')) { const locale = params.get('lang'); - this._translate?.use(locale); + this._locale?.setLocale(locale); localStorage.setItem('BACKOFFICE.locale', locale); } if (params.has('x-api-key')) { @@ -122,7 +122,7 @@ export class AppComponent extends AsyncHandler implements OnInit { } }); setNotifyOutlet(this._snackbar); - setTranslationService(this._translate); + setTranslationService(this._locale); this._loading.next(true); /** Wait for settings to initialise */ await this._settings.initialised.pipe(first((_) => _)).toPromise(); @@ -197,9 +197,8 @@ export class AppComponent extends AsyncHandler implements OnInit { const locales = this._settings.get('app.locales') || [ { id: 'en', name: 'English' }, ]; - this._translate?.addLangs(locales.map((_) => _.id)); if (locale) { - this._translate?.use(locale); + this._locale?.setLocale(locale); } else { const list = navigator.languages; for (const lang of list) { @@ -207,7 +206,7 @@ export class AppComponent extends AsyncHandler implements OnInit { if (!locale) locale = locales.find((_) => lang.includes(_.id)); if (locale) { - this._translate?.use(lang); + this._locale?.setLocale(lang); localStorage.setItem('BACKOFFICE.locale', lang); break; } diff --git a/apps/backoffice/src/app/app.module.ts b/apps/backoffice/src/app/app.module.ts index 782bd302..8cbd17f8 100644 --- a/apps/backoffice/src/app/app.module.ts +++ b/apps/backoffice/src/app/app.module.ts @@ -1,11 +1,6 @@ import { BrowserModule } from '@angular/platform-browser'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; -import { NgModule, ErrorHandler } from '@angular/core'; -import { - HttpClient, - provideHttpClient, - withInterceptorsFromDi, -} from '@angular/common/http'; +import { NgModule, ErrorHandler, LOCALE_ID } from '@angular/core'; import { ServiceWorkerModule } from '@angular/service-worker'; import { FormsModule } from '@angular/forms'; @@ -21,13 +16,7 @@ import { AuthorisedUserGuard } from './ui/guards/authorised-user.guard'; import { AuthorisedAdminGuard } from './ui/guards/authorised-admin.guard'; import './mocks'; -import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; -import { TranslateHttpLoader } from '@ngx-translate/http-loader'; - -// AoT requires an exported function for factories -export function HttpLoaderFactory(http: HttpClient) { - return new TranslateHttpLoader(http, './assets/locale/', '.json'); -} +import { LocaleService } from './common/locale.service'; @NgModule({ declarations: [AppComponent], @@ -48,20 +37,16 @@ export function HttpLoaderFactory(http: HttpClient) { // or after 30 seconds (whichever comes first). registrationStrategy: 'registerWhenStable:30000', }), - TranslateModule.forRoot({ - defaultLanguage: 'en', - loader: { - provide: TranslateLoader, - useFactory: HttpLoaderFactory, - deps: [HttpClient], - }, - }), ], providers: [ { provide: ErrorHandler, useClass: SentryService }, AuthorisedUserGuard, AuthorisedAdminGuard, - provideHttpClient(withInterceptorsFromDi()), + { + provide: LOCALE_ID, + deps: [LocaleService], + useFactory: (localeService: LocaleService) => localeService.locale, + }, ], }) export class AppModule {} diff --git a/apps/backoffice/src/app/common/locale.service.ts b/apps/backoffice/src/app/common/locale.service.ts new file mode 100644 index 00000000..73b173e4 --- /dev/null +++ b/apps/backoffice/src/app/common/locale.service.ts @@ -0,0 +1,117 @@ +import { Injectable } from '@angular/core'; + +import * as DEFAULT_LOCALE from 'apps/backoffice/src/assets/locale/en.json'; + +import { log } from './general'; + +interface LocaleStore { + expiry: number; + locale: string; + mappings: Record; +} + +function removeNesting(value: any, path = ''): Record { + let out_object: Record = {}; + for (const key in value) { + const out_key = path ? [path, key].join('.') : key; + if (value[key] instanceof Object) { + out_object = { + ...out_object, + ...removeNesting(value[key], out_key), + }; + } else { + out_object[out_key] = `${value[key]}`; + } + } + return out_object; +} + +const STORE_KEY = 'BACKOFFICE.locale'; + +@Injectable({ + providedIn: 'root', +}) +export class LocaleService { + private _default_locale = 'en-AU'; + private _current_locale = this._default_locale; + private _current_locale_short = this._current_locale.split('-')[0]; + private _cache_time = 7 * 24 * 60 * 60 * 1000; + private _load_promises: Record> = {}; + + private _default_mappings: Record = + removeNesting(DEFAULT_LOCALE); + private _locale_mappings: Record> = {}; + + public locale_folder = 'assets/locale'; + + constructor() { + this.setLocale( + localStorage.getItem(`${STORE_KEY}`) || this._default_locale + ); + } + + public get(key: string, args: Record = {}) { + let value = + (this._locale_mappings[this._current_locale] || {})[key] || + (this._locale_mappings[this._current_locale_short] || {})[key] || + this._default_mappings[key] || + key; + for (const id in args) { + value = value.replace(`{{ ${id} }}`, args[id]); + } + return value; + } + + public get default_locale() { + return this._default_locale; + } + + public get locale(): string { + return this._current_locale; + } + + public getLocaleShort(): string { + return this._current_locale_short; + } + + public setLocale(locale: string) { + this._current_locale = locale; + this._current_locale_short = this._current_locale.split('-')[0]; + if (!this._locale_mappings[locale] && !this._load_promises[locale]) { + this._load_promises[locale] = this._loadLocale(locale); + } + localStorage.setItem(`${STORE_KEY}`, locale); + log('LOCALE', `Locale set to "${locale}"`); + } + + private async _loadLocale(locale: string) { + const existing: LocaleStore = JSON.parse( + localStorage.getItem(`${STORE_KEY}.${locale}`) || '{}' + ); + if (!existing.expiry || existing.expiry < Date.now()) { + localStorage.removeItem(`${STORE_KEY}.${locale}`); + const resp = await fetch(`${this.locale_folder}/${locale}.json`); + if (!resp.ok) { + delete this._load_promises[locale]; + return console.error( + `Failed to loaded locale file for "${locale}".`, + resp + ); + } + const locale_data = await resp.json(); + this._locale_mappings[locale] = removeNesting(locale_data); + const store = { + expiry: Date.now() + this._cache_time, + locale, + mappings: this._locale_mappings[locale], + }; + localStorage.setItem( + `${STORE_KEY}.${locale}`, + JSON.stringify(store) + ); + } else { + this._locale_mappings[locale] = existing.mappings; + } + delete this._load_promises[locale]; + } +} diff --git a/apps/backoffice/src/app/common/translate.ts b/apps/backoffice/src/app/common/translate.ts index 72b7a14b..3e3cf814 100644 --- a/apps/backoffice/src/app/common/translate.ts +++ b/apps/backoffice/src/app/common/translate.ts @@ -1,12 +1,12 @@ -import { TranslateService } from '@ngx-translate/core'; +import { LocaleService } from './locale.service'; -let _service: TranslateService; +let _service: LocaleService; -export function setTranslationService(pipe: TranslateService) { - _service = pipe; +export function setTranslationService(service: LocaleService) { + _service = service; } export function i18n(key: string, args: Record = {}) { if (!_service) return key; - return _service.instant(key, args); + return _service.get(key, args); } diff --git a/apps/backoffice/src/app/domains/domains.module.ts b/apps/backoffice/src/app/domains/domains.module.ts index 36256579..d41c8e79 100644 --- a/apps/backoffice/src/app/domains/domains.module.ts +++ b/apps/backoffice/src/app/domains/domains.module.ts @@ -2,7 +2,6 @@ import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { RouterModule } from '@angular/router'; -import { TranslateModule } from '@ngx-translate/core'; import { ROUTES } from './domains.routes'; @@ -11,8 +10,8 @@ import { DomainAuthenticationComponent } from './domain-authentication.component import { DomainUsersComponent } from './domain-users.component'; import { DomainAboutComponent } from './domain-about.component'; -import { SharedContentModule } from 'apps/backoffice/src/app/ui/ui.module'; import { DomainsComponent } from './domains.component'; +import { SharedContentModule } from '../ui/ui.module'; @NgModule({ declarations: [ @@ -28,7 +27,6 @@ import { DomainsComponent } from './domains.component'; ReactiveFormsModule, RouterModule.forChild(ROUTES), SharedContentModule, - TranslateModule.forChild(), ], }) export class AppDomainsModule {} diff --git a/apps/backoffice/src/app/drivers/drivers.module.ts b/apps/backoffice/src/app/drivers/drivers.module.ts index c444114c..0da803df 100644 --- a/apps/backoffice/src/app/drivers/drivers.module.ts +++ b/apps/backoffice/src/app/drivers/drivers.module.ts @@ -2,15 +2,15 @@ import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { FormsModule } from '@angular/forms'; import { RouterModule } from '@angular/router'; -import { TranslateModule } from '@ngx-translate/core'; import { ROUTES } from './drivers.routes'; import { DriverAboutComponent } from './driver-about.component'; import { DriverModulesComponent } from './driver-devices.component'; -import { SharedContentModule } from 'apps/backoffice/src/app/ui/ui.module'; + import { DriversComponent } from './drivers.component'; import { DriverUpdateListModalComponent } from './driver-update-list-modal.component'; +import { SharedContentModule } from '../ui/ui.module'; @NgModule({ declarations: [ @@ -24,7 +24,6 @@ import { DriverUpdateListModalComponent } from './driver-update-list-modal.compo FormsModule, RouterModule.forChild(ROUTES), SharedContentModule, - TranslateModule.forChild(), ], }) export class AppDriversModule {} diff --git a/apps/backoffice/src/app/metrics/metrics.module.ts b/apps/backoffice/src/app/metrics/metrics.module.ts index 0bf8bea2..853ad7c2 100644 --- a/apps/backoffice/src/app/metrics/metrics.module.ts +++ b/apps/backoffice/src/app/metrics/metrics.module.ts @@ -2,13 +2,13 @@ import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { FormsModule } from '@angular/forms'; import { RouterModule } from '@angular/router'; -import { TranslateModule } from '@ngx-translate/core'; import { ROUTES } from './metrics.routes'; import { MetricsComponent } from './metrics.component'; -import { SharedContentModule } from 'apps/backoffice/src/app/ui/ui.module'; + import { ClockComponent } from './clock.component'; +import { SharedContentModule } from '../ui/ui.module'; @NgModule({ declarations: [MetricsComponent, ClockComponent], @@ -17,7 +17,6 @@ import { ClockComponent } from './clock.component'; FormsModule, RouterModule.forChild(ROUTES), SharedContentModule, - TranslateModule.forChild(), ], }) export class AppMetricsModule {} diff --git a/apps/backoffice/src/app/modules/modules.module.ts b/apps/backoffice/src/app/modules/modules.module.ts index 3e1f8423..1405ff7c 100644 --- a/apps/backoffice/src/app/modules/modules.module.ts +++ b/apps/backoffice/src/app/modules/modules.module.ts @@ -2,14 +2,14 @@ import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { FormsModule } from '@angular/forms'; import { RouterModule } from '@angular/router'; -import { TranslateModule } from '@ngx-translate/core'; import { ROUTES } from './modules.routes'; import { ModuleAboutComponent } from './module-about.component'; import { ModuleSystemsComponent } from './module-systems.component'; -import { SharedContentModule } from 'apps/backoffice/src/app/ui/ui.module'; + import { ModulesComponent } from './modules.component'; +import { SharedContentModule } from '../ui/ui.module'; @NgModule({ declarations: [ @@ -22,7 +22,6 @@ import { ModulesComponent } from './modules.component'; FormsModule, RouterModule.forChild(ROUTES), SharedContentModule, - TranslateModule.forChild(), ], }) export class AppModulesModule {} diff --git a/apps/backoffice/src/app/overlays/overlays.module.ts b/apps/backoffice/src/app/overlays/overlays.module.ts index 62b0dad9..bc1f296a 100644 --- a/apps/backoffice/src/app/overlays/overlays.module.ts +++ b/apps/backoffice/src/app/overlays/overlays.module.ts @@ -19,7 +19,6 @@ import { MatchFieldsComponent } from './bulk-item-modal/match-fields.component'; import { ListComponent } from './bulk-item-modal/list.component'; import { StatusListComponent } from './bulk-item-modal/status-list.component'; import { DuplicateModalComponent } from './duplicate-modal/duplicate-modal.component'; -import { TranslateModule } from '@ngx-translate/core'; const OVERLAYS: Type[] = [ ConfirmModalComponent, @@ -48,7 +47,6 @@ const OVERLAYS: Type[] = [ FormsModule, ReactiveFormsModule, SharedContentModule, - TranslateModule.forChild(), ], exports: [...OVERLAYS], }) diff --git a/apps/backoffice/src/app/repositories/repositories.module.ts b/apps/backoffice/src/app/repositories/repositories.module.ts index be9161e1..46fbdb3f 100644 --- a/apps/backoffice/src/app/repositories/repositories.module.ts +++ b/apps/backoffice/src/app/repositories/repositories.module.ts @@ -2,14 +2,14 @@ import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { FormsModule } from '@angular/forms'; import { RouterModule } from '@angular/router'; -import { TranslateModule } from '@ngx-translate/core'; import { ROUTES } from './repositories.routes'; import { RepositoryAboutComponent } from './repository-about.component'; import { RepositoryDriversComponent } from './repository-drivers.component'; -import { SharedContentModule } from 'apps/backoffice/src/app/ui/ui.module'; + import { RepositoriesComponent } from './repositories.component'; +import { SharedContentModule } from '../ui/ui.module'; @NgModule({ declarations: [ @@ -22,7 +22,6 @@ import { RepositoriesComponent } from './repositories.component'; FormsModule, RouterModule.forChild(ROUTES), SharedContentModule, - TranslateModule.forChild(), ], }) export class AppRepositoriesModule {} diff --git a/apps/backoffice/src/app/systems/systems.module.ts b/apps/backoffice/src/app/systems/systems.module.ts index 24859f85..d6b8a073 100644 --- a/apps/backoffice/src/app/systems/systems.module.ts +++ b/apps/backoffice/src/app/systems/systems.module.ts @@ -3,7 +3,6 @@ import { CommonModule } from '@angular/common'; import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { RouterModule } from '@angular/router'; import { DragDropModule } from '@angular/cdk/drag-drop'; -import { TranslateModule } from '@ngx-translate/core'; import { ROUTES } from './systems.routes'; @@ -12,8 +11,9 @@ import { SystemModulesComponent } from './system-modules.component'; import { SystemTriggersComponent } from './system-triggers.component'; import { SystemZonesComponent } from './system-zones.component'; import { SystemMetadataComponent } from './system-metadata.component'; -import { SharedContentModule } from 'apps/backoffice/src/app/ui/ui.module'; + import { SystemsComponent } from './systems.component'; +import { SharedContentModule } from '../ui/ui.module'; @NgModule({ declarations: [ @@ -31,7 +31,6 @@ import { SystemsComponent } from './systems.component'; RouterModule.forChild(ROUTES), SharedContentModule, DragDropModule, - TranslateModule.forChild(), ], }) export class AppSystemsModule {} diff --git a/apps/backoffice/src/app/triggers/triggers.module.ts b/apps/backoffice/src/app/triggers/triggers.module.ts index 4b767563..b7e7e740 100644 --- a/apps/backoffice/src/app/triggers/triggers.module.ts +++ b/apps/backoffice/src/app/triggers/triggers.module.ts @@ -3,14 +3,14 @@ import { CommonModule } from '@angular/common'; import { FormsModule } from '@angular/forms'; import { RouterModule } from '@angular/router'; import { DragDropModule } from '@angular/cdk/drag-drop'; -import { TranslateModule } from '@ngx-translate/core'; import { ROUTES } from './triggers.routes'; import { TriggerAboutComponent } from './trigger-about.component'; import { TriggerInstancesComponent } from './trigger-instances.component'; -import { SharedContentModule } from 'apps/backoffice/src/app/ui/ui.module'; + import { TriggersComponent } from './triggers.component'; +import { SharedContentModule } from '../ui/ui.module'; @NgModule({ declarations: [ @@ -24,7 +24,6 @@ import { TriggersComponent } from './triggers.component'; RouterModule.forChild(ROUTES), SharedContentModule, DragDropModule, - TranslateModule.forChild(), ], }) export class AppTriggersModule {} diff --git a/apps/backoffice/src/app/ui/forms/trigger-condition-form/time-form.component.ts b/apps/backoffice/src/app/ui/forms/trigger-condition-form/time-form.component.ts index 83ed05ea..2045d7d8 100644 --- a/apps/backoffice/src/app/ui/forms/trigger-condition-form/time-form.component.ts +++ b/apps/backoffice/src/app/ui/forms/trigger-condition-form/time-form.component.ts @@ -1,14 +1,12 @@ import { Component, Input, OnChanges, SimpleChanges } from '@angular/core'; import { UntypedFormGroup } from '@angular/forms'; import { TriggerTimeConditionType } from '@placeos/ts-client'; -import { AsyncHandler } from 'apps/backoffice/src/app/common/async-handler.class'; -import { numberToPosition } from 'apps/backoffice/src/app/common/general'; -import { TIMEZONES_IANA } from 'apps/backoffice/src/app/common/timezones'; -import { Identity } from 'apps/backoffice/src/app/common/types'; +import { AsyncHandler } from '../../../common/async-handler.class'; +import { numberToPosition } from '../../../common/general'; +import { TIMEZONES_IANA } from '../../../common/timezones'; +import { Identity } from '../../../common/types'; import { format, setDay, setMonth } from 'date-fns'; import { i18n } from '../../../common/translate'; -import { TranslateService } from '@ngx-translate/core'; -import { DatePipe } from '@angular/common'; @Component({ selector: 'trigger-condition-time-form', @@ -268,7 +266,7 @@ export class TriggerConditionTimeFormComponent public timezones: string[] = []; - public pad(str: any, digits: number = 2) { + public pad(str: any, digits = 2) { return `${str}`.padStart(digits, '0'); } diff --git a/apps/backoffice/src/app/ui/translate.pipe.ts b/apps/backoffice/src/app/ui/translate.pipe.ts new file mode 100644 index 00000000..b9306f82 --- /dev/null +++ b/apps/backoffice/src/app/ui/translate.pipe.ts @@ -0,0 +1,14 @@ +import { Pipe, PipeTransform } from '@angular/core'; + +import { LocaleService } from '../common/locale.service'; + +@Pipe({ + name: 'translate', +}) +export class TranslatePipe implements PipeTransform { + constructor(private _locale: LocaleService) {} + + public transform(value: string, args: Record = {}) { + return this._locale.get(value, args); + } +} diff --git a/apps/backoffice/src/app/ui/ui.module.ts b/apps/backoffice/src/app/ui/ui.module.ts index 22206628..420fc0c8 100644 --- a/apps/backoffice/src/app/ui/ui.module.ts +++ b/apps/backoffice/src/app/ui/ui.module.ts @@ -90,13 +90,13 @@ import { GlobalBannerComponent } from './global-banner.component'; import { ModuleRuntimeErrorsModalComponent } from './module-runtime-errors.modal'; import { SimpleTableComponent } from './simple-table.component'; import { ReorderItemsModalComponent } from './reorder-items-modal.component'; -import { TranslateModule } from '@ngx-translate/core'; import { SettingsToggleComponent } from './settings-toggle.component'; import { FullscreenModalShellComponent } from './fullscreen-modal-shell.component'; import { CounterComponent } from './counter.component'; import { TriggerConditionModalComponent } from './forms/trigger-condition-modal.component'; import { TriggerActionModalComponent } from './forms/trigger-action-modal.component'; import { UserAvatarComponent } from './user-avatar.component'; +import { TranslatePipe } from './translate.pipe'; const FORM_COMPONENTS: Type[] = [ SystemFormComponent, @@ -169,6 +169,7 @@ const PIPES: Type[] = [ SanitizePipe, SettingsFormatPipe, UserPipe, + TranslatePipe, ]; const ENTRY_COMPONENT: Type[] = [ @@ -223,7 +224,6 @@ const MATERIAL_MODULES: any[] = [ ScrollingModule, ...MATERIAL_MODULES, RouterModule.forChild([]), - TranslateModule.forChild(), ], exports: [...COMPONENTS, ...PIPES, ...ENTRY_COMPONENT, ...MATERIAL_MODULES], }) diff --git a/apps/backoffice/src/app/ui/user-menu-tooltip.component.ts b/apps/backoffice/src/app/ui/user-menu-tooltip.component.ts index 31108624..9558c85d 100644 --- a/apps/backoffice/src/app/ui/user-menu-tooltip.component.ts +++ b/apps/backoffice/src/app/ui/user-menu-tooltip.component.ts @@ -1,11 +1,11 @@ -import { Component } from '@angular/core'; +import { Component, OnInit } from '@angular/core'; import { logout } from '@placeos/ts-client'; import { format } from 'date-fns'; import { VERSION } from '../../environments/version'; import { issueDescription } from '../common/general'; import { SettingsService } from '../common/settings.service'; -import { TranslateService } from '@ngx-translate/core'; import { i18n } from '../common/translate'; +import { LocaleService } from '../common/locale.service'; @Component({ selector: 'user-menu-tooltip', @@ -113,7 +113,7 @@ import { i18n } from '../common/translate'; `, ], }) -export class UserMenuTooltipComponent { +export class UserMenuTooltipComponent implements OnInit { /** Whether dark mode is enabled */ public get dark_mode(): boolean { return this._settings.get('theme') === 'dark'; @@ -147,12 +147,11 @@ export class UserMenuTooltipComponent { constructor( private _settings: SettingsService, - private _translate_service: TranslateService + private _locale: LocaleService ) {} public ngOnInit() { - this.lang = this._translate_service.currentLang; - console.log('Lang:', this.lang); + this.lang = this._locale.locale; this.languages = [ { id: 'en', name: i18n('COMMON.LANG_ENGLISH'), flag: '🇬🇧' }, { id: 'jp', name: i18n('COMMON.LANG_JAPANESE'), flag: '🇯🇵' }, @@ -163,7 +162,7 @@ export class UserMenuTooltipComponent { } public setLanguage(lang: string) { - this._translate_service.use(lang); + this._locale.setLocale(lang); localStorage.setItem('BACKOFFICE.locale', lang); this.languages = [ { id: 'en', name: i18n('COMMON.LANG_ENGLISH'), flag: '🇬🇧' }, diff --git a/apps/backoffice/src/app/users/users.module.ts b/apps/backoffice/src/app/users/users.module.ts index beb7c1b2..f845cf5c 100644 --- a/apps/backoffice/src/app/users/users.module.ts +++ b/apps/backoffice/src/app/users/users.module.ts @@ -2,15 +2,15 @@ import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { RouterModule } from '@angular/router'; -import { TranslateModule } from '@ngx-translate/core'; import { ROUTES } from './users.routes'; import { UserAboutComponent } from './user-about.component'; import { UserHistoryComponent } from './user-history.component'; -import { SharedContentModule } from 'apps/backoffice/src/app/ui/ui.module'; + import { UserMetadataComponent } from './user-metadata.component'; import { UsersComponent } from './users.component'; +import { SharedContentModule } from '../ui/ui.module'; @NgModule({ declarations: [ @@ -25,7 +25,6 @@ import { UsersComponent } from './users.component'; ReactiveFormsModule, RouterModule.forChild(ROUTES), SharedContentModule, - TranslateModule.forChild(), ], }) export class AppUsersModule {} diff --git a/apps/backoffice/src/app/users/users.service.ts b/apps/backoffice/src/app/users/users.service.ts index 54ff743c..9b173ff7 100644 --- a/apps/backoffice/src/app/users/users.service.ts +++ b/apps/backoffice/src/app/users/users.service.ts @@ -1,10 +1,8 @@ import { Injectable } from '@angular/core'; -import { HttpClient, HttpHeaders } from '@angular/common/http'; import { PlaceUser, PlaceUserQueryOptions, logout, - authorise, currentUser, queryUsers, onlineState, @@ -13,9 +11,8 @@ import { BehaviorSubject, Observable } from 'rxjs'; import { Md5 } from 'ts-md5/dist/md5'; import { first, map } from 'rxjs/operators'; -import { FilterFn } from 'apps/backoffice/src/app/common/types'; -import { toQueryString } from 'apps/backoffice/src/app/common/api'; -import { AsyncHandler } from 'apps/backoffice/src/app/common/async-handler.class'; +import { FilterFn } from '../common/types'; +import { AsyncHandler } from '../common/async-handler.class'; import { SettingsService } from '../common/settings.service'; import * as Sentry from '@sentry/browser'; @@ -66,10 +63,7 @@ export class BackofficeUsersService extends AsyncHandler { /** Default method for filtering the available list */ private _filter_fn: FilterFn = (_) => true; - constructor( - private _settings: SettingsService, - private http_unauth: HttpClient - ) { + constructor(private _settings: SettingsService) { super(); onlineState() .pipe(first((_) => _)) @@ -147,43 +141,7 @@ export class BackofficeUsersService extends AsyncHandler { * Login with given credentials * @param fields Key value pairs of post parameters */ - public login(fields: any = {}) { - return new Promise((resolve, reject) => { - this.state.next('loading'); - const query = toQueryString(fields); - let headers = new HttpHeaders(); - headers = headers.append( - 'Content-Type', - 'application/x-www-form-urlencoded' - ); - this.http_unauth.post('/auth/signin', query, { headers }).subscribe( - (res: any) => { - if (sessionStorage) { - const clientId = Md5.hashStr( - `${location.origin}/oauth-resp.html` - ); - sessionStorage.setItem(`${clientId}_login`, 'true'); - } - authorise().then((_) => resolve()); - }, - (err) => { - if (err.status >= 400) { - this.state.next('error'); - } else { - if (sessionStorage) { - const clientId = Md5.hashStr( - `${location.origin}/oauth-resp.html` - ); - sessionStorage.setItem(`${clientId}_login`, 'true'); - } - authorise(); - } - reject(); - }, - () => this.load() - ); - }); - } + public login(fields: any = {}) {} /** * Logout from the application diff --git a/apps/backoffice/src/app/zones/zones.module.ts b/apps/backoffice/src/app/zones/zones.module.ts index 3c63429b..49cc2c29 100644 --- a/apps/backoffice/src/app/zones/zones.module.ts +++ b/apps/backoffice/src/app/zones/zones.module.ts @@ -2,10 +2,8 @@ import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { RouterModule } from '@angular/router'; -import { TranslateModule } from '@ngx-translate/core'; import { ROUTES } from './zones.routes'; -import { SharedContentModule } from 'apps/backoffice/src/app/ui/ui.module'; import { ZoneAboutComponent } from './zone-about.component'; import { ZoneSystemsComponent } from './zone-systems.component'; @@ -13,6 +11,7 @@ import { ZoneTriggersComponent } from './zone-triggers.component'; import { ZoneChildrenComponent } from './zone-children.component'; import { ZoneMetadataComponent } from './zone-metadata.component'; import { ZonesComponent } from './zones.component'; +import { SharedContentModule } from '../ui/ui.module'; @NgModule({ declarations: [ @@ -29,7 +28,6 @@ import { ZonesComponent } from './zones.component'; ReactiveFormsModule, RouterModule.forChild(ROUTES), SharedContentModule, - TranslateModule.forChild(), ], }) export class AppZonesModule {} diff --git a/apps/backoffice/src/assets/locale/en-GB.json b/apps/backoffice/src/assets/locale/en-GB.json new file mode 100644 index 00000000..3edc6dff --- /dev/null +++ b/apps/backoffice/src/assets/locale/en-GB.json @@ -0,0 +1,966 @@ +{ + "COMMON": { + "SYSTEMS": "Systems", + "MODULES": "Modules", + "ZONES": "Zones", + "DRIVERS": "Drivers", + "REPOS": "Repositories", + "TRIGGERS": "Triggers", + "METRICS": "Metrics", + "USERS": "Users", + "DOMAINS": "Domains", + "EDGE": "Edge", + "EDGE_SEARCH": "Search for edges...", + "MANAGE": "Manage Instance", + "ADMIN": "Admin", + "USER_ADMIN": "Admin", + "USER_SUPPORT": "Support", + "USER_BASIC": "Basic", + "DEBUG_ENABLED": "Debugging Enabled", + "DEBUG_LISTENING_MSG": "Listening to {{ modules }} module(s)", + "DEBUG_MSG_COUNT_MSG": "{{ count }} module messages", + "DEBUG_NO_MESSAGES": "No debug messages to display", + "PROFILE": "Profile", + "DARK_MODE": "Dark Mode", + "LOGOUT": "Logout", + "UPLOAD_HISTORY": "Upload History", + "LANGUAGE": "Language", + "REPORT_ISSUE": "Report an Issue", + "UPLOAD_FILE": "Upload File", + "FILENAME": "Filename", + "PUBLIC": "Public", + "PERMISSIONS": "Permissions", + "NOTES": "Notes", + "NONE": "None", + "CANCEL": "Cancel", + "CONFIRM": "Confirm", + "SAVE": "Save", + "SAVE_ALL": "Save All", + "CLEAR": "Clear Changes", + "SAVE_CHANGES": "Save Changes", + "SAVE_ITEMS": "Save items", + "BACK": "Back", + "NEXT": "Next", + "CONTINUE": "Continue", + "UPDATE": "Update", + "UPLOAD": "Upload", + "UPLOADS": "Uploads", + "LINK": "Link", + "RETRY": "Retry", + "IMAGES": "Images", + "NEW_ITEM": "New item", + "ITEM_ADD": "Add {{ item }}", + "ITEM_ADD_SELECTED": "Add selected", + "PROCESSING_REQUEST": "Processing request...", + "PASSWORD": "Password", + "IMAGE_ADD_URL": "Add image via URL", + "IMAGE_UPLOADS": "Upload Image(s)", + "DROP_UPLOAD_MSG": "Drop files to upload them to the cloud", + "NO_UPLOADS": "No uploads to show", + "CLEAR_UPLOADS": "Clear completed uploads", + "FORBIDDEN": "Access forbidden.", + "INVALID_PAGE_PERMISSIONS": "You do not have permission to view this page and your access attempt has been recorded.", + "CONTACT_ADMIN": "Contact your Administrator if you feel that you should have access.", + "SELECTED_FIRST_VERSION": "Selected setting is the first version", + "SETTINGS_COMPARE_SELECT_MSG": "Select an current setting and old setting to compare them.", + "SELECT_OLD_SETTING": "Select old settings", + "SELECT_NEW_SETTING": "Select current settings", + "METADATA_NEW": "New Metadata block", + "METADATA_EDIT": "Edit Metadata details", + "METADATA_REMOVE": "Remove Metadata block", + "METADATA_EMPTY": "No Metadata blocks found", + "METADATA_SAVE": "Successfully saved metadata", + "TOTAL_ITEMS": "{{ count }} items(s)", + "ITEM_ERROR": "Failed to save item. Error: {{ error }}", + "ITEM_SAVE": "Successfully saved item", + "UPDATE_AVAILABLE": "Update Available", + "END_OF_LIST": "End of the list", + "SEARCH_EMPTY": "No matching {{ name }} found", + "LIST_EMPTY": "No {{ name }} available", + "SEARCH": "Search...", + "SEARCH_FOR": "Search for {{ name }}...", + "VIEW_TYPE": "View {{ name }}", + "ONLINE": "Online", + "OFFLINE": "Offline", + "SECURE": "Secure", + "TLS": "TLS", + "UDP": "UDP", + "EDIT_TYPE": "Edit {{ name }}", + "CREATE_FROM_TYPE": "Create new from this {{ name }}", + "DUPLICATE_TYPE": "Duplicate {{ name }}", + "EXPORT_TYPE_AS_TSV": "Export {{ name }} as TSV Template", + "DELETE_TYPE": "Delete {{ name }}", + "COPIED_ID": "ID copied to clipboard", + "MESSAGE_COUNT": "{{ count }} message(s)", + "DEBUG_DOWNLOAD_MESSAGES": "Download messages", + "DEBUG_TOGGLE_POSITION": "Toggle Position", + "DEBUG_CLEAR_MESSAGES": "Download messages", + "DEBUG_UNBIND_MODULES": "Unbind Modules", + "DEBUG_CLOSE_CONSOLE": "Close Console", + "TRUE": "Yes", + "FALSE": "No", + "TIMEZONE": "Timezone", + "TIMEZONE_EMPTY": "No matching timezones", + "CREATED_AT": "Created", + "UPDATED_AT": "Updated", + "SETTINGS": "Settings", + "LAST_EDIT": "Last edited", + "SETTINGS_PLAINTEXT": "Unencrypted", + "SETTINGS_SUPPORT": "Support", + "SETTINGS_ADMIN": "Admin", + "SETTINGS_ENCRYPTED": "Encrypted", + "SETTINGS_MERGED": "Merged", + "SETTINGS_MASKED": "MASKED", + "SETTINGS_INHERITED": "Setting inherited from [{{ parent }}]({{ path }})({{ type }})", + "SETTINGS_LOCAL": "Local setting from {{ type }}", + "SETTINGS_SAVE_ERROR": "Failed to save settings. Error {{ error }}", + "SETTINGS_SAVE_SUCCESS": "Successfully saved {{ type }} settings changes", + "SETTINGS_SAVE_SUCCESS_ALL": "Successfully saved all settings changes", + "ADD_EXISTING": "Add existing", + "ADD_NEW": "Add new", + "EXECUTE_COMMAND": "Execute command", + "EXECUTE_CLEAR": "Clear selection", + "EXECUTE_PERFORM": "Execute", + "EXECUTE_LOADING": "Executing method...", + "EXECUTE_MODULE_SELECT": "Select module", + "EXECUTE_MODULE_LOADING": "Loading module list...", + "EXECUTE_REQUIRED": "Required", + "EXECUTE_OPTIONAL": "Optional", + "EXECUTE_NO_ARGS": "No arguments for selected method", + "EXECUTE_METHOD_SELECT": "Select method", + "EXECUTE_METHOD_LOADING": "Loading method list...", + "EXECUTE_METHOD_EMPTY": "No available methods for module", + "BLANK_NAME": "BLANK", + "FIELD_NAME": "Name", + "FIELD_DESCRIPTION": "Description", + "FIELD_EMAIL": "Email", + "VERSION": "Version", + "VERSION_CURRENT": "Current Version", + "VERSION_LATEST": "Latest Version", + "GIT_BRANCH": "Branch", + "GIT_COMMIT": "Commit hash", + "GIT_PULL_LATEST": "Pull latest commit", + "DESCRIPTION_EMPTY": "No description", + "INVALID_FIELDS": "Some fields are invalid. [{{ field_list }}]", + "BULK_ADD": "Bulk add {{ type }}", + "BULK_DOWNLOAD": "Download Template", + "BULK_DROP_MSG": "Select or drop CSV file to begin", + "BULK_DROP_LOADING": "Processing CSV data...", + "BULK_UPLOADING": "Uploading items...", + "LANG_ENGLISH": "English", + "LANG_JAPANESE": "Japanese", + "LANG_FRENCH": "French", + "LANG_SPANISH": "Spanish", + "LANG_ARABIC": "Arabic", + "CHANGELOG": "Changelog", + "CHANGELOG_EMPTY": "No Changelog", + "VIEW_RESPONSE": "Exec Request Results", + "MINUTE": "Minute", + "HOUR": "Hour", + "DAY": "Day", + "WEEK": "Week", + "MONTH": "Month", + "YEAR": "Year", + "SUNDAY": "Sunday", + "MONDAY": "Monday", + "TUESDAY": "Tuesday", + "WEDNESDAY": "Wednesday", + "THURSDAY": "Thursday", + "FRIDAY": "Friday", + "JANUARY": "January", + "FEBRUARY": "February", + "MARCH": "March", + "APRIL": "April", + "MAY": "May", + "JUNE": "June", + "JULY": "July", + "AUGUST": "August", + "SEPTEMBER": "September", + "OCTOBER": "October", + "NOVEMBER": "November", + "DECEMBER": "December", + "DATE_JUST_NOW": "Just now", + "DATE_FROM_IN_LESS_MINUTE": "In less than a minute", + "DATE_FROM_LESS_MINUTE_AGO": "Less than a minute ago", + "DATE_FROM_IN_ABOUT_MINUTE": "In about 1 minute", + "DATE_FROM_ABOUT_MINUTE_AGO": "About a minute ago", + "DATE_FROM_IN_MINUTES": "In {{ minutes }} minutes", + "DATE_FROM_MINUTES_AGO": "{{ minutes }} minutes ago", + "DATE_FROM_IN_HOURS": "In {{ hours }} hour(s)", + "DATE_FROM_HOURS_AGO": "{{ hours }} hour(s) ago", + "DATE_FROM_IN_DAYS": "In {{ days }} days(s)", + "DATE_FROM_DAYS_AGO": "{{ days }} days(s) ago", + "DATE_FROM_IN_MONTHS": "In {{ months }} month(s)", + "DATE_FROM_MONTHS_AGO": "{{ months }} month(s) ago", + "DATE_FROM_IN_YEAS": "In {{ years }} year(s)", + "DATE_FROM_YEARS_AGO": "{{ years }} year(s) ago" + }, + "SYSTEMS": { + "SINGULAR": "System", + "PLURAL": "Systems", + "SEARCH": "Search for systems...", + "ADD": "Add system", + "EDIT": "Edit system", + "DELETE": "Delete system", + "DELETE_MSG": "

Are you sure you want delete this system({{ name }})?

Deleting this will immediately delete modules that are not in another system

", + "DELETE_LOADING": "Deleting system...", + "DELETE_SUCCESS": "Successfully deleted system.", + "DELETE_ERROR": "Failed to delete system. Error: {{ error }}", + "SAVING": "Saving system...", + "SAVE_ERROR": "Failed to save system. Error: {{ error }}", + "SAVE_SUCCESS": "Successfully saved system.", + "REMOVE": "Remove system", + "NEW": "New system", + "BULK": "Bulk add systems", + "SUPPORT_URL": "Support URL", + "BOOKABLE": "Bookable Room", + "FEATURES": "Features", + "SIGNAGE": "Signage", + "PUBLIC": "Public", + "CODE": "Code", + "EMAIL": "Email", + "CAPACITY": "Capacity", + "MAP_ID": "Map ID", + "PANEL_COUNT": "Installed Touch Panels", + "CONTROLS": "System Controls", + "START": "START", + "STOP": "STOP", + "REMOVE_ITEM": "Remove {{ item }}", + "LOADING_SETTINGS": "Loading system settings...", + "TAB_ABOUT": "About", + "TAB_MODULES": "Modules", + "TAB_ZONES": "Zones", + "TAB_TRIGGERS": "Triggers", + "TAB_METADATA": "Metadata", + "TAB_SETTINGS_HISTORY": "Settings History", + "FIND_MODULE": "Find existing module...", + "MODULE_LIST": "Module List", + "MODULE_FIELD_STATE": "State", + "MODULE_FIELD_NAME": "Name", + "MODULE_FIELD_TYPE": "Type", + "MODULE_FIELD_CLASS": "Class", + "MODULE_FIELD_ADDRESS": "Address", + "MODULE_FIELD_DEBUG": "Debug", + "MODULE_LIST_EMPTY": "No modules for the selected system", + "DEBUG_ENABLE": "Enable debugging", + "DEBUG_DISABLE": "Disable debugging", + "VIEW_ORIGINAL": "View Original", + "ZONE_FIELD_NAME": "Name", + "ZONE_FIELD_DESCRIPTION": "Description", + "ZONE_DESCRIPTION_EMPTY": "No description", + "ZONE_REMOVE": "Remove system from zone", + "ZONE_SEARCH": "Search for zones...", + "TRIGGER_EDIT": "Edit Trigger", + "TRIGGER_REMOVE": "Remove Trigger", + "COPY_WEBHOOK": "Copy webhook", + "COPIED_WEBHOOK": "Webhook link copied to clipboard", + "TRIGGER_FIELD_NAME": "Name", + "TRIGGER_FIELD_COUNT": "Count", + "TRIGGER_FIELD_ERRORS": "Errors", + "TRIGGER_FIELD_ADDED": "Added", + "TRIGGERS_EMPTY": "No triggers for selected system", + "TRIGGER_ADD": "Add trigger", + "TRIGGER_SEARCH": "Search triggers...", + "ZONE_REQUIRED": "A zone is required", + "NAME_REQUIRED": "A unique system name is required", + "EMAIL_REQUIRED": "A valid email is required", + "DISPLAY_NAME": "Display name", + "URL_VALID": "A valid URL is required", + "TRIGGER_NAME": "Trigger Name", + "TRIGGER_ACTIVE": "Trigger Active", + "TRIGGER_ENABLED": "Enabled", + "TRIGGER_EXECUTE_ENABLED": "Execute Enabled", + "TRIGGER_IMPORTANT": "Important" + }, + "MODULES": { + "SINGULAR": "Module", + "PLURAL": "Modules", + "SEARCH": "Search for modules...", + "ADD": "Add module", + "DELETE": "Delete module", + "DELETE_MSG": "

Are you sure you want delete this module?

Deleting this will module immediately remove it from any system associated with it

", + "DELETE_LOADING": "Deleting module...", + "DELETE_SUCCESS": "Successfully deleted module.", + "DELETE_ERROR": "Failed to delete module. Error: {{ error }}", + "SAVING": "Saving module...", + "SAVE_ERROR": "Failed to save module. Error: {{ error }}", + "SAVE_SUCCESS": "Successfully saved module.", + "REMOVE": "Remove module", + "RUNTIME_ERRORS_VIEW": "View Runtime Errors", + "RUNTIME_ERRORS_NO": "No runtime errors found", + "RUNTIME_ERRORS_LOADING": "Loading runtime errors details...", + "TOGGLE_POWER": "Toggle Power", + "VIEW_STATE": "View State", + "EDIT": "Edit Module", + "LOAD": "Load Module", + "RELOAD": "Reload Module", + "VIEW_ERRORS": "View runtime errors", + "ADD_TO_SYSTEM": "Add to another system", + "NOTES": "Notes", + "IP_ADDRESS": "IP", + "PORT_NUMBER": "Port Number", + "PROTOCOL": "Protocol", + "CONTROLS": "Module Controls", + "START": "START", + "STOP": "STOP", + "LOADING_SETTINGS": "Loading module settings...", + "TAB_ABOUT": "About", + "TAB_SYSTEMS": "Systems", + "TAB_SETTINGS_HISTORY": "Settings History", + "NEW": "New module", + "SYSTEMS_FIELD_NAME": "Name", + "SYSTEMS_FIELD_MODULE_COUNT": "No. Modules", + "SYSTEMS_EMPTY": "No systems with the selected module", + "FIELD_STATE": "State", + "DRIVER_REQUIRED": "A driver is required", + "CONTROL_SYSTEM": "Control System", + "SYSTEM_REQUIRED": "A control system is required", + "URI": "Module URI", + "URI_REQUIRED": "A valid URI is required", + "FIELD_IP": "IP Address of FQDN", + "IP_REQUIRED": "A valid IP address or domain name is required", + "PORT_REQUIRED": "A valid port number from 1 to 65535 is required", + "MAKEBREAK": "Makebreak", + "IGNORE_CONNECTED": "Ignore connected", + "CUSTOM_NAME": "Custom Name", + "STATE": "Module State", + "STATE_UPDATE": "Update state", + "STATE_LOADING": "Loading module's state..." + }, + "ZONES": { + "SINGULAR": "Zone", + "PLURAL": "Zones", + "SEARCH": "Search for zones...", + "ADD": "Add zone", + "EDIT": "Edit zone", + "DELETE": "Delete zone", + "DELETE_MSG": "

Are you sure you want delete this zone?

Deleting this zone will immediately remove systems without another zone

", + "DELETE_LOADING": "Deleting zone...", + "DELETE_SUCCESS": "Successfully deleted zone.", + "DELETE_ERROR": "Failed to delete zone. Error: {{ error }}", + "SAVING": "Saving zone...", + "SAVE_ERROR": "Failed to save zone. Error: {{ error }}", + "SAVE_SUCCESS": "Successfully saved zone.", + "REMOVE": "Remove zone", + "NEW": "New zone", + "BULK": "Bulk add zones", + "TAB_ABOUT": "About", + "TAB_SYSTEMS": "Systems", + "TAB_TRIGGERS": "Triggers", + "TAB_METADATA": "Metadata", + "TAB_CHILDREN": "Children", + "TAB_SETTINGS_HISTORY": "Settings History", + "TAG_WARNING": "Tags set in this zone require a parent zone to work correctly.", + "SELECT_SYSTEM": "Select system", + "PARENT_ID": "Parent ID", + "LOCATION": "Location", + "LOCATION_PLACEHOLDER": "Geo-location details for zone. ", + "CODE": "Code", + "CODE_PLACEHOLDER": "Organisation Code", + "TYPE": "Type", + "TYPE_PLACEHOLDER": "Organisation Category", + "COUNT": "Count", + "CAPACITY": "Capacity", + "MAP_URL": "Map URL", + "TAGS": "Tags", + "TAGS_EMPTY": "No tags for zone", + "DESCRIPTION": "Description", + "LOADING_SETTINGS": "Loading zone settings...", + "SYSTEMS_FIELD_MODULE_COUNT": "No. Modules", + "SYSTEMS_EMPTY": "No systems with the selected zone", + "CHILDREN_EMPTY": "No child zones with the selected zone", + "DESCRIPTION_EMPTY": "No description", + "TRIGGERS_EMPTY": "No triggers for selected zone", + "PARENT_ZONE": "Parent Zone", + "NAME_REQUIRED": "A unique zone name is required", + "DISPLAY_NAME": "Display name" + }, + "DRIVERS": { + "SINGULAR": "Driver", + "PLURAL": "Drivers", + "DEVICE": "Device", + "SSH": "SSH", + "SERVICE": "Service", + "WEBSOCKET": "Websocket", + "LOGIC": "Logic", + "SEARCH": "Search for drivers...", + "ADD": "Add driver", + "EDIT": "Edit driver", + "DELETE": "Delete driver", + "DELETE_MSG": "

Are you sure you want delete this driver?

", + "DELETE_LOADING": "Deleting driver...", + "DELETE_SUCCESS": "Successfully deleted driver.", + "DELETE_ERROR": "Failed to delete driver. Error: {{ error }}", + "SAVING": "Saving driver...", + "SAVE_ERROR": "Failed to save driver. Error: {{ error }}", + "SAVE_SUCCESS": "Successfully saved driver.", + "REMOVE": "Remove driver", + "NEW": "New driver", + "LOADING": "Loading drivers...", + "UPDATE": "Update Drivers", + "UPDATE_COUNT": "{{ count }} updates available", + "UPDATE_SELECTED": "Update selected drivers", + "NO_UPDATES": "No drivers require updating.", + "TAB_ABOUT": "About", + "TAB_MODULES": "Modules", + "TAB_SETTINGS_HISTORY": "Settings History", + "DEFAULT_URI": "Default URI", + "DEFAULT_PORT": "Default Port", + "MODULE_NAME": "Module Name", + "MODULE_NAME_REQUIRED": "A module name is required", + "COMPILED": "Compiled", + "VIEW_ERRORS": "View errors", + "FILENAME": "Filename", + "RECOMPILE": "Recompile", + "RELOAD": "Reload", + "LOADING_SETTINGS": "Loading driver settings...", + "MODULES_EMPTY": "No modules created for driver", + "VIEW_SYSTEMS": "View module's systems", + "SYSTEM_COUNT": "{{ count }} system(s)", + "LOADING_SYSTEMS": "Loading module systems...", + "DRIVER_LIST_ERROR": "Failed to load driver list. Error: {{ error }}", + "COMMIT_LIST_ERROR": "Failed to load driver's commit list. Error: {{ error }}", + "BASE": "Base Driver", + "COMMIT": "Driver Commit", + "COMMIT_SEARCH": "Search for commit...", + "NAME_REQUIRED": "A driver name is required", + "ROLE": "Role", + "COMMITS_LOADING": "Loading commits for the selected driver...", + "DRIVERS_LOADING": "Loading driver list from selected repository...", + "DETAILS_LOADING": "Loading driver details for selected commit...", + "DETAILS_ERROR_1": "Failed to load driver details for commit.", + "DETAILS_ERROR_2": "Please check that the driver compiles correctly and try again." + }, + "REPOS": { + "SINGULAR": "Repository", + "PLURAL": "Repositories", + "SEARCH": "Search for repositories...", + "ADD": "Add repository", + "EDIT": "Edit repository", + "DELETE": "Delete repository", + "REMOVE": "Remove repository", + "NEW": "New repository", + "DELETE_MSG": "

Deleting this repository will immediately remove assoicated drivers and modules

", + "DELETE_LOADING": "Deleting repository...", + "DELETE_SUCCESS": "Successfully deleted repository.", + "DELETE_ERROR": "Failed to delete repository. Error: {{ error }}", + "SAVING": "Saving repository...", + "SAVE_ERROR": "Failed to save repository. Error: {{ error }}", + "SAVE_SUCCESS": "Successfully saved repository.", + "LOADING": "Loading repositories...", + "TAB_ABOUT": "About", + "TAB_DRIVERS": "Drivers", + "INTERFACE_REPO": "Interface Repository", + "DRIVER_REPO": "Driver Repository", + "TYPE_INTERFACE": "Interface", + "TYPE_DRIVER": "Driver", + "FIELD_TYPE": "Type", + "TYPE": "Repository Type", + "FOLDER_NAME": "Folder name", + "FOLDER_NAME_EMPTY": "No folder set", + "FOLDER_NAME_REQUIRED": "A folder name is required", + "URI": "Repository URI", + "AVAILABLE_DRIVERS": "Available Drivers", + "DRIVER_LIST_EMPTY": "No available drivers in repository", + "GIT_PULL_ERROR": "Error pulling latest commit. Error: {{ error }}", + "GIT_PULL_TIMEOUT": "Pull timed out", + "USERNAME": "Repository Username", + "PASSWORD": "Repository Password", + "URI_REQUIRED": "A URI is required", + "BRANCH": "Branch", + "COMMIT": "Repository Commit", + "NAME_REQUIRED": "A repository name is required", + "BRANCH_REQUIRED": "A working branch is required", + "COMMIT_REQUIRED": "A commit is required" + }, + "TRIGGERS": { + "SINGULAR": "Trigger", + "PLURAL": "Triggers", + "SEARCH": "Search for triggers...", + "ADD": "Add trigger", + "EDIT": "Edit trigger", + "NEW": "New trigger", + "DELETE": "Delete trigger", + "DELETE_INSTANCE": "Delete instance", + "REMOVE": "Remove trigger", + "DELETE_MSG": "

Are you sure you want delete this trigger?

Deleting this trigger will immediately remove it from all associated systems and zones

", + "DELETE_LOADING": "Deleting trigger...", + "DELETE_SUCCESS": "Successfully deleted trigger.", + "DELETE_ERROR": "Failed to delete trigger. Error: {{ error }}", + "SAVING": "Saving trigger...", + "SAVE_ERROR": "Failed to save trigger. Error: {{ error }}", + "SAVE_SUCCESS": "Successfully saved trigger.", + "LOADING": "Loading triggers...", + "TAB_ABOUT": "About", + "TAB_INSTANCES": "Instances", + "FIELD_ADDED": "Added", + "FIELD_STATE": "State", + "FIELD_INSTANCE_NAME": "Instance Name", + "INSTANCES_EMPTY": "No instance of trigger", + "REFERENCE_SYSTEM": "Reference System", + "REFERENCE_SYSTEM_MSG": "System to use for available status variables and function calls", + "CONDITIONS": "Conditions", + "CONDITION_ADD": "Add Condition", + "CONDITION_NEW": "New Condition", + "CONDITION_FIELD_TYPE": "Condition Type", + "CONDITION_COMPARE": "Compare Values", + "CONDITION_TIME": "Particular Time", + "CONDITION_COMPARE_EMPTY": "No variable comparision conditions for this trigger", + "CONDITION_TIME_EMPTY": "No time dependant conditions for this trigger", + "CONDITION_SAVE_ERROR": "Failed to save trigger condition. Error {{ error }}", + "CONDITION_SAVE_SUCCESS": "Successfully saved trigger condtion", + "FIELD_VAR_COMPARE": "Variable Comparison Condition", + "FIELD_TIME_DEPS": "Time dependant Condition", + "ACTIONS": "Actions", + "ACTION_ADD": "Add Action", + "ACTION_NEW": "New Action", + "ACTION_FIELD_TYPE": "ACtion Type", + "ACTION_EXECUTE": "Execute Method", + "ACTION_EXECUTE_SELECT": "Select method to execute", + "ACTION_EMAIL": "Send Email", + "ACTION_EMAIL_BODY": "Email Body", + "ACTION_EMAIL_ADDRESS": "Email addresses", + "ACTION_EMAIL_ADDRESS_LIST": "List of email addresses", + "ACTION_EMAIL_EMPTY": "No email actions for this trigger", + "ACTION_FN_EMPTY": "No function call actions for this trigger", + "FIELD_ACTION_FN_CALL": "Function call Action", + "FIELD_ACTION_EMAIL": "Email Action", + "REORDER_CONFIRM_TITLE": "Reoreder trigger {{ type }} action", + "REORDER_CONFIRM_MSG": "Are you sure you want remove this trigger condition?
All systems using this trigger will be updated immediately.", + "REORDER_CONFIRM_LOADING": "Re-ordering trigger actions...", + "REORDER_CONFIRM_ERROR": "Failed to re-ordered trigger action. Error: {{ error }}", + "REORDER_CONFIRM_SUCCESS": "Successfully re-ordered trigger action", + "REMOVE_CONDITION_TITLE": "Remove trigger condition", + "REMOVE_CONDITION_MSG": "Are you sure you want remove this trigger condition?
All systems using this trigger will be updated immediately.", + "REMOVE_CONDITION_LOADING": "Removing trigger condition...", + "REMOVE_CONDITION_ERROR": "Failed to remove trigger condition. Error: {{ error }}", + "REMOVE_CONDITION_SUCCESS": "Successfully removed trigger condition.", + "REMOVE_ACTION_TITLE": "Remove trigger action", + "REMOVE_ACTION_MSG": "Are you sure you want remove this trigger action?
All systems using this trigger will be updated immediately.", + "REMOVE_ACTION_LOADING": "Removing trigger action...", + "REMOVE_ACTION_ERROR": "Failed to remove trigger action. Error: {{ error }}", + "REMOVE_ACTION_SUCCESS": "Successfully removed trigger action.", + "REMOVE_INSTANCE_TITLE": "Remove trigger from {{ type }}", + "REMOVE_INSTANCE_MSG": "Are you sure you want remove this trigger from {{ name }}?
The {{ type }} will be updated immediately.", + "REMOVE_INSTANCE_LOADING": "Removing trigger from {{ type }}", + "REMOVE_INSTANCE_ERROR": "Failed to remove trigger from {{ type }}. Error: {{ error }}", + "REMOVE_INSTANCE_SUCCESS": "Successfully remove trigger from {{ type }}", + "TIME_SCHEDULE": "Recurring Schedule", + "TIME_REPEAT": "Repeat Every", + "TIME_DAY_OF_MONTH": "Day of Month", + "TIME_MONTH_OF_YEAR": "Month of Year", + "TIME_DAY_OF_WEEK": "Day of Week", + "TIME_HOUR_OF_DAY": "Hour of the day", + "TIME_MINUTE_OF_HOUR": "Minute of the hour", + "TIME_FIELD_DATE": "Date", + "TIME_FIELD_TIME": "Time", + "COMPARE_CONSTANT": "Constant Value", + "COMPARE_VARIABLE": "Status Variable", + "COMPARE_VARIABLE_SELECT": "Select status variable", + "COMPARE_VARIABLE_ERROR": "A module status variable is required", + "COMPARE_OP": "Operation", + "COMPARE_OP_EQUAL": "equal to", + "COMPARE_OP_NOT_EQUAL": "not equal to", + "COMPARE_OP_GREATER": "greater than", + "COMPARE_OP_GREATER_EQUAL": "greater than or equal", + "COMPARE_OP_LESS": "less than", + "COMPARE_OP_LESS_EQUAL": "less than or equal", + "COMPARE_OP_AND": "truthy AND", + "COMPARE_OP_OR": "truthy OR", + "COMPARE_OP_XOR": "truthy XOR", + "COMPARE_OP_SELECT": "Select comparision operator", + "COMPARE_TO": "Compared to", + "COMPARE_JSON_REQUIRED": "Valid JSON is required", + "COMPARE_MODULE_SELECT": "Select module", + "COMPARE_SUBKEYS": "Subkeys", + "COMPARE_SUBKEYS_PLACEHOLDER": "Status variable subkeys", + "COMPARE_VARIABLE_LOAD_ERROR": "Failed to load status variables from {{ system }}, {{ module }}", + "ENABLE_WEBHOOK": "Enable Webhook", + "DEBOUNCE_PERIOD": "Debounce Period", + "SUPPORTED_METHODS": "Supported Methods" + }, + "USERS": { + "SINGULAR": "User", + "PLURAL": "Users", + "SEARCH": "Search for users...", + "ADD": "Add user", + "EDIT": "Edit user", + "NEW": "New user", + "BULK": "Bulk add users", + "REMOVE": "Remove user", + "DELETE_MSG": "

Are you sure you want delete this user?

The user will be removed from the system within 24 hours

", + "DELETE_LOADING": "Deleting user...", + "DELETE_SUCCESS": "Successfully deleted user.", + "DELETE_ERROR": "Failed to delete user. Error: {{ error }}", + "SAVING": "Saving user...", + "SAVE_ERROR": "Failed to save user. Error: {{ error }}", + "SAVE_SUCCESS": "Successfully saved user.", + "LOADING": "Loading users...", + "TAB_ABOUT": "About", + "TAB_METADATA": "Metadata", + "TAB_HISTORY": "History", + "ROLE_SUPPORT": "PlaceOS Support", + "ROLE_ADMIN": "System Admin", + "ROLE_NONE": "None", + "FIELD_GROUPS": "User Groups", + "FIELD_DEPARTMENT": "Department", + "AUTHORITY_ID": "Authority ID", + "NO_GROUPS": "No user groups", + "FIELD_SESSION_START": "Session Start", + "FIELD_SESSION_END": "End", + "FIELD_SYSTEMS_ACCESSED": "Systems Accessed", + "VIEW_LOGS": "View", + "LOGS_EMPTY": "No logs found for this user", + "FIRST_NAME": "First Name", + "FIRST_NAME_REQUIRED": "A first name is required", + "LAST_NAME": "Last Name", + "LAST_NAME_REQUIRED": "A last name is required", + "EMAIL_REQUIRED": "A valid email is required", + "STAFF_ID": "Staff ID", + "STAFF_CARD": "Card Number", + "PASSWORD_REQUIRED": "A valid password is required", + "PASSWORD_CONFIRM": "Confirm Password", + "PASSOWRD_MATCH": "Passwords do not match" + }, + "DOMAINS": { + "SINGULAR": "Domain", + "PLURAL": "Domains", + "SEARCH": "Search for domain...", + "ADD": "Add domain", + "EDIT": "Edit domain", + "NEW": "New domain", + "BULK": "Bulk add domains", + "REMOVE": "Remove domain", + "DELETE": "Delete domain", + "DELETE_MSG": "

Are you sure you want delete this domain?

The domain will be deleted immediately.

", + "DELETE_LOADING": "Deleting domain...", + "DELETE_SUCCESS": "Successfully deleted domain.", + "DELETE_ERROR": "Failed to delete domain. Error: {{ error }}", + "SAVING": "Saving domain...", + "SAVE_ERROR": "Failed to save domain. Error: {{ error }}", + "SAVE_SUCCESS": "Successfully saved domain.", + "LOADING": "Loading domains...", + "TAB_ABOUT": "About", + "TAB_APPLICATIONS": "Applications", + "TAB_AUTHENTICATION": "Authentication", + "TAB_USERS": "Users", + "NAME": "Domain Name", + "NAME_PLACEHOLDER": "Domain e.g. www.google.com", + "EMAIL_DOMAINS": "User Email Domains", + "SETTINGS_CONFIG": "Config", + "SETTINGS_INTERNALS": "Internals", + "COPIED_EMAIL_DOMAIN": "Copied email domain to clipboard.", + "SETTINGS_SAVED": "Successfully updated domain settings.", + "APPLICATION_NEW": "New Application", + "APPLICATION_EDIT": "Edit Application", + "APPLICATION_REMOVE": "Remove Application", + "FIELD_REDIRECT_URI": "Redirect URI", + "FIELD_CLIENT_ID": "Client ID", + "FIELD_CLIENT_SECRET": "Client Secret", + "FIELD_TYPE": "Type", + "FIELD_USER": "User", + "FIELD_ROLE": "Role", + "APPLICATIONS_EMPTY": "No applications for this domain", + "COPY_SECRET": "Copy secret to Clipboard", + "COPIED_SECRET": "Copied client secret to Clipboard", + "VIEW_SECRET": "View Secret", + "SECRET_HIDDEN": "Hidden", + + "USER_LIST_EMPTY": "No users associated with this domain", + "NAME_REQUIRED": "A name is required for this domain", + "DOMAIN_REQUIRED": "A valid domain is required", + "LOGIN_URL": "Login URL", + "LOGIN_URL_REQUIRED": "A valid login URL is required", + "LOGOUT_URL": "Logout URL", + "LOGOUT_URL_REQUIRED": "A valid logout URL is required", + "CLIENT_ID": "Client ID", + "CLIENT_SECRET": "Client Secret", + + "AUTHENTICATION_NEW": "New Auth Method", + "AUTHENTICATION_EDIT": "Edit Auth Method", + "AUTHENTICATION_REMOVE": "Remove Auth Method", + "AUTHENTICATION_EMPTY": "No authentication methods for this domain", + "AUTHENTICATION_SOURCE_TYPE": "Authentication Source Type", + "AUTHENTICATION_SOURCE_SELECT": "Select authentication source...", + "AUTHENTICATION_SAVE_ERROR": "Successfully updated authentication source", + "AUTHENTICATION_SAVE_SUCCESS": "Failed to update authentication source. Error: {{ error }}", + "AUTHENTICATION_NAME_REQUIRED": "An auth source name is required", + + "OAUTH_SITE": "Site", + "OAUTH_SITE_PLACEHOLDER": "URL of the SSO provider", + "OAUTH_SCOPES": "Scopes", + "OAUTH_TOKEN_METHOD": "Token Method", + "OAUTH_TOKEN_SCHEME": "Authentication Scheme", + "OAUTH_SCHEME_BODY": "Request Body", + "OAUTH_SCHEME_BASIC": "Basic Auth", + "OAUTH_TOKEN_URL": "Token URL", + "OAUTH_AUTHORISE_URL": "Authorise URL", + "OAUTH_PROFILE_URL": "User Profile URL", + "OAUTH_INFO_MAPPINGS": "Info Mappings", + "OAUTH_AUTHORISE_PARAMS": "Authorise Parameters", + "OAUTH_ENSURE_MATCHING": "Ensure Matching", + + "LDAP_HOST": "Host", + "LDAP_HOST_REQUIRED": "A hostname is required", + "LDAP_PORT": "Port number", + "LDAP_USER_ID": "User ID Key", + "LDAP_AUTH_METHOD": "Authentication Method", + "LDAP_BASE": "Base", + "LDAP_BASE_REQUIRED": "A base is required", + "LDAP_BIND_DN": "Bind DN", + "LDAP_FILTER": "Filter", + + "SAML_ISSUER": "Issuer", + "SAML_ISSUER_REQUIRED": "An issuer is required", + "SAML_IDP_TARGET_URL": "IdP Target URL", + "SAML_IDP_TARGET_URL_REQUIRED": "A IdP Target URL is required", + "SAML_NAME_ID_FORMAT": "Name Identifier Format", + "SAML_NAME_ID_FORMAT_REQUIRED": "A Name Identifier Format is required", + "SAML_REQUEST_ATTRIBUTES": "Request Attributes", + "SAML_REQUEST_ATTRIBUTES_REQUIRED": "Request Attributes are required", + "SAML_ASSERTION_URL": "Assertion URL", + "SAML_ASSERTION_URL_REQUIRED": "An Assertion URL is required", + "SAML_CERT_FINGERPRINT": "Certificate Fingerprint", + "SAML_CERT_FULL": "Full Certificate", + "SAML_UID_ATTRIBUTE": "UID Attribute", + "SAML_ATTRIBUTE_SERVICE_NAME": "Attribute Service Name", + "SAML_ATTRIBUTE_STATEMENTS": "Attribute Statements", + "SAML_IDP_SSO_RUNTIME_PARAMS": "IdP SSO Runtime Params", + "SAML_IDP_SLO_TARGET_URL": "IdP SLO Target URL", + "SAML_SLO_DEFAULT_RELAY_STATE": "SLO Default Relay State", + + "APP_SKIP": "Skip Authorisation", + "APP_NAME_REQUIRED": "An application name is required", + "APP_PRESERVE_ID": "Preserve Client ID", + "APP_CLIENT_ID": "Client ID", + "APP_CLIENT_PLACEHOLDER": "MD5 Hash of the Redirect URI", + "APP_SCOPES": "Access Scopes", + "APP_REDIRECT_URL": "Redirect URL", + "APP_REDIRECT_URL_REQUIRED": "A valid URL is required" + }, + "ADMIN": { + "TITLE": "Admin", + "TAB_ABOUT": "About", + "TAB_DATABASE": "Database", + "TAB_CLUSTERS": "Clusters", + "TAB_EDGES": "Edges", + "TAB_INTERFACES": "Interfaces", + "TAB_MQTT_BROKERS": "MQTT Brokers", + "TAB_TENANT_CONFIG": "Staff Tenants", + "TAB_RESOURCE_IMPORTS": "Resource Imports", + "TAB_EXTENSIONS": "Extensions", + "TAB_API_KEYS": "Application Keys", + "TAB_CUSTOM_SCHEMAS": "Custom Schemas", + "TAB_UPLOAD_STORAGE": "Data Stores", + "TAB_UPLOADS_LIBRARY": "Uploads Library", + "APPLICATION_DETAILS": "Application Details", + "VIEW_CHANGELOG": "View Changelog", + "BUILD": "Build", + "BUILT_AT": "Build", + "BACKEND_SERVICES": "Backend Services", + "PLATFORM": "Platform", + "COPIED": "Copied {{ name }} to clipboard", + "ALL_DOMAINS": "All Domains", + "SELECT_DOMAIN": "Select domain...", + + "BACKEND_SERVICES_EMPTY": "No API service details available.", + "BACKEND_SERVICES_ERROR": "Failed to load API details. Error: {{ error }}", + "DATABASE_REINDEX": "Re-index", + "DATABASE_REINDEX_MSG": "Re-index elasticsearch for the current state of the database", + "DATABASE_BACKFILL": "Backfill", + "DATABASE_BACKFILL_MSG": "Backfill elasticsearch with the current state of the database", + + "CLUSTERS": "PlaceOS Clusters", + "CLUSTERS_VIEW_PROCESSES": "View Processes", + "CLUSTERS_LIST_EMPTY": "No cluster details available", + "CLUSTERS_CPU_USAGE": "CPU Usage", + "CLUSTERS_MEMORY_USAGE": "Memory Usage", + "CLUSTERS_CPU_CORES": "{{ count }} cores", + "CLUSTER": "Cluster", + "CLUSTERS_SEARCH_PROCESSES": "Search processes...", + "CLUSTERS_FIELD_CPU_USAGE": "CPU %", + "CLUSTERS_FIELD_MEMORY_USAGE": "Memory", + "CLUSTERS_FIELD_INSTANCES": "Instances", + "CLUSTER_PROCESSES_EMPTY": "No tasks running on this cluster", + "CLUSTER_PROCESS_KILL": "Kill process", + "CLUSTER_PROCESS_KILL_MSG": "

Are you sure you want kill the process for \"{{ id }}\"?

The process will be terminated immediately.The process may be restarted after a short while.

", + "CLUSTER_PROCESS_KILL_LOADING": "Terminating process. Please wait...", + "CLUSTER_PROCESS_KILL_ERROR": "Failed to kill process. Error: {{ error }}", + + "EDGE_HEADER": "PlaceOS Edge Nodes", + "EDGE_ADD": "Add Edge node", + "EDGE_NEW": "New Edge node", + "EDGE_EDIT": "Edit Edge node", + "EDGE_SAVING": "Saving edge node...", + "EDGE_REMOVE": "Remove Edge node", + "EDGE_ERROR": "Failed to update edge node.", + "EDGE_NAME_PLACEHOLDER": "Name of the Edge node", + "EDGE_DESCRIPTION_PLACEHOLDER": "Description of the Edge node", + "EDGE_NAME_REQUIRED": "An Edge name is required", + "EDGE_NEW_SUCCESS": "Successfully updated edge.", + "EDGE_EDIT_SUCCESS": "Successfully added new edge. Please make sure to save the API key as you will not be able to view it again in the future.", + "EDGE_LIST_EMPTY": "No edges available on this cluster", + "INTERFACES_HEADER": "PlaceOS Interfaces", + "INTERFACES_COMMIT_EMPTY": "No commit hash", + + "BROKERS_HEADER": "MQTT Brokers", + "BROKERS_ADD": "Add MQTT Broker", + "BROKERS_NEW": "New MQTT Broker", + "BROKERS_EDIT": "New MQTT Broker", + "BROKERS_REMOVE": "Remove MQTT Broker", + "BROKER_LIST_EMPTY": "No MQTT Brokers", + "BROKERS_FIELD_HOST": "Host", + "BROKERS_FIELD_PORT": "Port", + "BROKERS_FIELD_TLS": "TLS", + "BROKERS_FIELD_FILTERS": "Filters", + "BROKERS_FIELD_AUTH_TYPE": "Auth Type", + "BROKERS_AUTH_TYPE_CERT": " Certificate", + "BROKERS_AUTH_TYPE_PASS": "User Password", + "BROKERS_AUTH_TYPE_NONE": "No Auth", + "BROKERS_FILTERS_EMPTY": "No filters", + "BROKERS_NAME_REQUIRED": "A broker name is required", + "BROKERS_HOST_REQUIRED": "A broker name is required", + "BROKERS_PORT_REQUIRED": "A valid port number between 1 - 65535 is required", + "BROKERS_USERNAME": "Username", + "BROKERS_USERNAME_REQUIRED": "A valid username is required", + "BROKERS_PASSWORD": "Password", + "BROKERS_PASSWORD_REQUIRED": "A valid password is required ", + "BROKERS_CERT": "Certificate", + "BROKERS_CERT_REQUIRED": "A valid certificate is required ", + + "TENANTS_HEADER": "Staff API Tenants", + "TENANTS_ADD": "Add Tenant", + "TENANTS_NEW": "New Tenant", + "TENANTS_EDIT": "Edit Tenant", + "TENANTS_REMOVE": "Remove Tenant", + "TENANTS_PLATFORM": "Platform", + "TENANTS_SECRET_EXPIRY": "Expires", + "TENANTS_EMPTY": "No tenants for the selected domain", + "TENANTS_EDIT_BOOKING_LIMITS": "Edit Booking Limits", + "TENANTS_FIELD_NAME": "Tenant name", + "TENANTS_NAME_ERROR": "A tenant name is required", + "TENANTS_EMAIL_DOMAIN": "Email Domain", + "TENANTS_EMAIL_PLACEHOLDER": "Domain of user's email for this tenant", + "TENANTS_EMAIL_ERROR": "A domain is required", + "TENANTS_EARLY_CHECKIN": "Early Check-in before Meeting", + "TENANTS_SERVICE_ACCOUNT": "Service Account", + "TENANTS_SERVICE_ACCOUNT_ERROR": "Service account should be a valid email address", + "TENANTS_DELEGATED": "Delegated", + "TENANTS_ITEM_REQUIRED": "A {{ name }} is required", + "TENANTS_CONFIG_OUTLOOK": "Configure Outlook Plugin", + "TENANTS_APP_ID": "Outlook App ID", + "TENANTS_APP_ID_REQUIRED": "An application ID is required", + "TENANTS_APP_DOMAIN": "Outlook App Domain", + "TENANTS_APP_DOMAIN_REQUIRED": "An application domain is required", + "TENANTS_APP_RESOURCE": "Outlook App Resource", + "TENANTS_APP_RESOURCE_REQUIRED": "An application resource is required", + "TENANTS_SOURCE_LOCATION": "Outlook Source Location", + "TENANTS_SOURCE_LOCATION_REQUIRED": "A source location is required", + "TENANTS_BASE_PATH": "Outlook Base Path", + "TENANTS_BASE_PATH_REQUIRED": "Base path to the application root folder is required", + "TENANTS_BOOKING_LIMITS": "Booking Limits", + "TENANTS_MINUTES": "{{ minute }} minutes", + "TENANTS_HOURS": "{{ hour }} hours", + "TENANTS_MINUTES_HOURS": "{{ hour }} hours {{ minute }} minutes", + "TENANTS_SAVE_ERROR": "Failed to update tenant.", + "TENANTS_SAVE_SUCCESS": "Successfully updated tenant.", + + "RESOURCE_IMPORTS_HEADER": "Resource Imports", + "RESOURCE_IMPORTS_ALL": "Import All", + "RESOURCE_IMPORTS_IMPORTED": "Imported", + "RESOURCE_IMPORTS_IMPORT": "Import resource", + "RESOURCE_IMPORTS_VIEW": "View System", + "RESOURCE_IMPORTS_EMPTY": "No resource available to import on selected domain", + "RESOURCE_IMPORTS_ALL_WARNING": "All resources are already imported.", + "RESOURCE_IMPORTS_ALL_TITLE": "Import missing resources?", + "RESOURCE_IMPORTS_ALL_MSG": "Are you sure you want to import the following {{ count }} resources?", + "RESOURCE_IMPORTS_ALL_LOADING": "Importing missing resources...", + "RESOURCE_IMPORTS_ALL_SUCCESS": "Successfully imported {{ count }} resources.", + "RESOURCE_IMPORTS_SUCCESS": "Successfully imported resource {{ name }}.", + + "EXTENSIONS_HEADER": "Extensions", + "EXTENSIONS_ADD": "Add Extension", + "EXTENSIONS_NEW": "New Extension", + "EXTENSIONS_EDIT": "Edit Extension", + "EXTENSIONS_REMOVE": "Remove Extension", + "EXTENSIONS_SAVING": "Saving extension...", + "EXTENSIONS_NOTICE": "Note: Backoffice requires a full page refresh for extension changes and additions to apply", + "EXTENSIONS_LIST_EMPTY": "No extensions available for the selected domain", + "EXTENSIONS_FIELD_CHECKS": "Checks", + "EXTENSIONS_FIELD_CONDITIONS": "Conditions", + "EXTENSIONS_FIELD_TYPE": "Type", + "EXTENSIONS_FIELD_NAME": "Extension Display Name", + "EXTENSIONS_NAME_REQUIRED": "A valid name is required", + "EXTENSIONS_FIELD_TAB": "Tab Name", + "EXTENSIONS_FIELD_URL": "URL", + "EXTENSIONS_URL_REQUIRED": "A valid URL is required", + "EXTENSIONS_CONDITION_ADD": "Add Condition", + "EXTENSIONS_CONDITION_VALUE": "Condition value", + "EXTENSIONS_CONDITION_OP": "Operation", + "EXTENSIONS_CONDITION_FIELD": "Condition Field", + + "APP_KEYS_HEADER": "PlaceOS Application Keys", + "APP_KEYS_ADD": "Add App Key", + "APP_KEYS_NEW": "New Application Key", + "APP_KEYS_REMOVE": "Remove Application Key", + "APP_KEYS_LAST_DETAILS": "Last App Key Details", + "APP_KEYS_FIELD_SCOPES": "Scopes", + "APP_KEYS_FIELD_PERMISSIONS": "Access", + "APP_KEYS_SCOPES_REQUIRE": "At least one scope is required", + "APP_KEYS_NAME_REQUIRED": "A name is required", + "APP_KEYS_PERMISSIONS_EMPTY": "No permissions", + "APP_KEYS_COPIED": "Copied Application Key to clipboard", + "APP_KEYS_LIST_EMPTY": "No application keys configured for this domain", + "PERMISSIONS": "Permissions", + "PERMISSIONS_ADMIN": "Admin", + "PERMISSIONS_SUPPORT": "Support", + "PERMISSIONS_USER": "User", + "PERMISSIONS_NONE": "None", + + "SCHEMA_HEADER": "Custom Schemas", + "SCHEMA_SELECT": "Select Schema...", + "SCHEMA_ADD": "Add Schema", + "SCHEMA_NAME": "Schema Name", + "SCHEMA_NEW": "New Schema", + "SCHEMA_SELECT_MSG": "Select a schema to edit", + + "STORAGE_HEADER": "PlaceOS Data Stores", + "STORAGE_ADD": "Add Provider", + "STORAGE_NEW": "New Storage Provider", + "STORAGE_EDIT": "Edit Storage Provider", + "STORAGE_REMOVE": "Remove Storage Provider", + "STORAGE_REMOVE_TITLE": "Remove Storage Provider", + "STORAGE_REMOVE_MSG": "Are you sure you want to remove the storage provider for {{ type }} {{name}}?", + "STORAGE_REMOVE_LOADING": "Removing storage provider...", + "STORAGE_LOADING": "Loading storage providers...", + "STORAGE_FIELD_TYPE": "Type", + "STORAGE_FIELD_REGION": "Region", + "STORAGE_LIST_EMPTY": "No storage providers for {{ item }} domain", + "STORAGE_SAVE_SUCCESS": "Successfully saved storage provider.", + "STORAGE_SAVE_ERROR": "Failed to save storage provider.", + "STORAGE_BUCKET_NAME_LABEL": "Bucket Name", + "STORAGE_BUCKET_NAME_REQUIRED": "Bucket Name is required", + "STORAGE_REGION_LABEL": "Region", + "STORAGE_REGION_REQUIRED": "Region is required", + "STORAGE_ENDPOINT_LABEL": "Endpoint", + "STORAGE_ACCESS_KEY_LABEL": "Access Key", + "STORAGE_ACCESS_KEY_REQUIRED": "Access Key is required", + "STORAGE_SECRET_LABEL": "Access Secret", + "STORAGE_SECRET_REQUIRED": "Access Secret is required", + "STORAGE_ALLOWED_EXTENSIONS_LABEL": "Allowed file extensions", + "STORAGE_ALLOWED_MIME_LABEL": "Allowed file MIME types", + "STORAGE_ALLOW_ALL_EXTENSIONS": "Allow all file extensions", + "STORAGE_ALLOW_ALL_MIME": "Allow all MIME types", + + "UPLOADS_LIB_HEADER": "Uploads Library", + "UPLOADS_LIB_DOWNLOAD": "Download Upload", + "UPLOADS_LIB_DOWNLOAD_ERROR": "Failed to download upload. Error: {{ error }}", + "UPLOADS_LIB_VIEW": "View Upload", + "UPLOADS_LIB_REMOVE": "Remove Upload", + "UPLOADS_LIB_REMOVE_MSG": "

Are you sure you want remove the upload \"{{ filename }}\"?

The upload will be deleted immediately.

", + "UPLOADS_LIB_REMOVE_LOADING": "Removing upload...", + "UPLOADS_LIB_FIELD_TYPE": "File Type", + "UPLOADS_LIB_FIELD_SIZE": "Size", + "UPLOADS_LIB_LIST_EMPTY": "No uploads for the selected domain" + } +} diff --git a/apps/backoffice/src/assets/locale/en-US.json b/apps/backoffice/src/assets/locale/en-US.json new file mode 100644 index 00000000..3edc6dff --- /dev/null +++ b/apps/backoffice/src/assets/locale/en-US.json @@ -0,0 +1,966 @@ +{ + "COMMON": { + "SYSTEMS": "Systems", + "MODULES": "Modules", + "ZONES": "Zones", + "DRIVERS": "Drivers", + "REPOS": "Repositories", + "TRIGGERS": "Triggers", + "METRICS": "Metrics", + "USERS": "Users", + "DOMAINS": "Domains", + "EDGE": "Edge", + "EDGE_SEARCH": "Search for edges...", + "MANAGE": "Manage Instance", + "ADMIN": "Admin", + "USER_ADMIN": "Admin", + "USER_SUPPORT": "Support", + "USER_BASIC": "Basic", + "DEBUG_ENABLED": "Debugging Enabled", + "DEBUG_LISTENING_MSG": "Listening to {{ modules }} module(s)", + "DEBUG_MSG_COUNT_MSG": "{{ count }} module messages", + "DEBUG_NO_MESSAGES": "No debug messages to display", + "PROFILE": "Profile", + "DARK_MODE": "Dark Mode", + "LOGOUT": "Logout", + "UPLOAD_HISTORY": "Upload History", + "LANGUAGE": "Language", + "REPORT_ISSUE": "Report an Issue", + "UPLOAD_FILE": "Upload File", + "FILENAME": "Filename", + "PUBLIC": "Public", + "PERMISSIONS": "Permissions", + "NOTES": "Notes", + "NONE": "None", + "CANCEL": "Cancel", + "CONFIRM": "Confirm", + "SAVE": "Save", + "SAVE_ALL": "Save All", + "CLEAR": "Clear Changes", + "SAVE_CHANGES": "Save Changes", + "SAVE_ITEMS": "Save items", + "BACK": "Back", + "NEXT": "Next", + "CONTINUE": "Continue", + "UPDATE": "Update", + "UPLOAD": "Upload", + "UPLOADS": "Uploads", + "LINK": "Link", + "RETRY": "Retry", + "IMAGES": "Images", + "NEW_ITEM": "New item", + "ITEM_ADD": "Add {{ item }}", + "ITEM_ADD_SELECTED": "Add selected", + "PROCESSING_REQUEST": "Processing request...", + "PASSWORD": "Password", + "IMAGE_ADD_URL": "Add image via URL", + "IMAGE_UPLOADS": "Upload Image(s)", + "DROP_UPLOAD_MSG": "Drop files to upload them to the cloud", + "NO_UPLOADS": "No uploads to show", + "CLEAR_UPLOADS": "Clear completed uploads", + "FORBIDDEN": "Access forbidden.", + "INVALID_PAGE_PERMISSIONS": "You do not have permission to view this page and your access attempt has been recorded.", + "CONTACT_ADMIN": "Contact your Administrator if you feel that you should have access.", + "SELECTED_FIRST_VERSION": "Selected setting is the first version", + "SETTINGS_COMPARE_SELECT_MSG": "Select an current setting and old setting to compare them.", + "SELECT_OLD_SETTING": "Select old settings", + "SELECT_NEW_SETTING": "Select current settings", + "METADATA_NEW": "New Metadata block", + "METADATA_EDIT": "Edit Metadata details", + "METADATA_REMOVE": "Remove Metadata block", + "METADATA_EMPTY": "No Metadata blocks found", + "METADATA_SAVE": "Successfully saved metadata", + "TOTAL_ITEMS": "{{ count }} items(s)", + "ITEM_ERROR": "Failed to save item. Error: {{ error }}", + "ITEM_SAVE": "Successfully saved item", + "UPDATE_AVAILABLE": "Update Available", + "END_OF_LIST": "End of the list", + "SEARCH_EMPTY": "No matching {{ name }} found", + "LIST_EMPTY": "No {{ name }} available", + "SEARCH": "Search...", + "SEARCH_FOR": "Search for {{ name }}...", + "VIEW_TYPE": "View {{ name }}", + "ONLINE": "Online", + "OFFLINE": "Offline", + "SECURE": "Secure", + "TLS": "TLS", + "UDP": "UDP", + "EDIT_TYPE": "Edit {{ name }}", + "CREATE_FROM_TYPE": "Create new from this {{ name }}", + "DUPLICATE_TYPE": "Duplicate {{ name }}", + "EXPORT_TYPE_AS_TSV": "Export {{ name }} as TSV Template", + "DELETE_TYPE": "Delete {{ name }}", + "COPIED_ID": "ID copied to clipboard", + "MESSAGE_COUNT": "{{ count }} message(s)", + "DEBUG_DOWNLOAD_MESSAGES": "Download messages", + "DEBUG_TOGGLE_POSITION": "Toggle Position", + "DEBUG_CLEAR_MESSAGES": "Download messages", + "DEBUG_UNBIND_MODULES": "Unbind Modules", + "DEBUG_CLOSE_CONSOLE": "Close Console", + "TRUE": "Yes", + "FALSE": "No", + "TIMEZONE": "Timezone", + "TIMEZONE_EMPTY": "No matching timezones", + "CREATED_AT": "Created", + "UPDATED_AT": "Updated", + "SETTINGS": "Settings", + "LAST_EDIT": "Last edited", + "SETTINGS_PLAINTEXT": "Unencrypted", + "SETTINGS_SUPPORT": "Support", + "SETTINGS_ADMIN": "Admin", + "SETTINGS_ENCRYPTED": "Encrypted", + "SETTINGS_MERGED": "Merged", + "SETTINGS_MASKED": "MASKED", + "SETTINGS_INHERITED": "Setting inherited from [{{ parent }}]({{ path }})({{ type }})", + "SETTINGS_LOCAL": "Local setting from {{ type }}", + "SETTINGS_SAVE_ERROR": "Failed to save settings. Error {{ error }}", + "SETTINGS_SAVE_SUCCESS": "Successfully saved {{ type }} settings changes", + "SETTINGS_SAVE_SUCCESS_ALL": "Successfully saved all settings changes", + "ADD_EXISTING": "Add existing", + "ADD_NEW": "Add new", + "EXECUTE_COMMAND": "Execute command", + "EXECUTE_CLEAR": "Clear selection", + "EXECUTE_PERFORM": "Execute", + "EXECUTE_LOADING": "Executing method...", + "EXECUTE_MODULE_SELECT": "Select module", + "EXECUTE_MODULE_LOADING": "Loading module list...", + "EXECUTE_REQUIRED": "Required", + "EXECUTE_OPTIONAL": "Optional", + "EXECUTE_NO_ARGS": "No arguments for selected method", + "EXECUTE_METHOD_SELECT": "Select method", + "EXECUTE_METHOD_LOADING": "Loading method list...", + "EXECUTE_METHOD_EMPTY": "No available methods for module", + "BLANK_NAME": "BLANK", + "FIELD_NAME": "Name", + "FIELD_DESCRIPTION": "Description", + "FIELD_EMAIL": "Email", + "VERSION": "Version", + "VERSION_CURRENT": "Current Version", + "VERSION_LATEST": "Latest Version", + "GIT_BRANCH": "Branch", + "GIT_COMMIT": "Commit hash", + "GIT_PULL_LATEST": "Pull latest commit", + "DESCRIPTION_EMPTY": "No description", + "INVALID_FIELDS": "Some fields are invalid. [{{ field_list }}]", + "BULK_ADD": "Bulk add {{ type }}", + "BULK_DOWNLOAD": "Download Template", + "BULK_DROP_MSG": "Select or drop CSV file to begin", + "BULK_DROP_LOADING": "Processing CSV data...", + "BULK_UPLOADING": "Uploading items...", + "LANG_ENGLISH": "English", + "LANG_JAPANESE": "Japanese", + "LANG_FRENCH": "French", + "LANG_SPANISH": "Spanish", + "LANG_ARABIC": "Arabic", + "CHANGELOG": "Changelog", + "CHANGELOG_EMPTY": "No Changelog", + "VIEW_RESPONSE": "Exec Request Results", + "MINUTE": "Minute", + "HOUR": "Hour", + "DAY": "Day", + "WEEK": "Week", + "MONTH": "Month", + "YEAR": "Year", + "SUNDAY": "Sunday", + "MONDAY": "Monday", + "TUESDAY": "Tuesday", + "WEDNESDAY": "Wednesday", + "THURSDAY": "Thursday", + "FRIDAY": "Friday", + "JANUARY": "January", + "FEBRUARY": "February", + "MARCH": "March", + "APRIL": "April", + "MAY": "May", + "JUNE": "June", + "JULY": "July", + "AUGUST": "August", + "SEPTEMBER": "September", + "OCTOBER": "October", + "NOVEMBER": "November", + "DECEMBER": "December", + "DATE_JUST_NOW": "Just now", + "DATE_FROM_IN_LESS_MINUTE": "In less than a minute", + "DATE_FROM_LESS_MINUTE_AGO": "Less than a minute ago", + "DATE_FROM_IN_ABOUT_MINUTE": "In about 1 minute", + "DATE_FROM_ABOUT_MINUTE_AGO": "About a minute ago", + "DATE_FROM_IN_MINUTES": "In {{ minutes }} minutes", + "DATE_FROM_MINUTES_AGO": "{{ minutes }} minutes ago", + "DATE_FROM_IN_HOURS": "In {{ hours }} hour(s)", + "DATE_FROM_HOURS_AGO": "{{ hours }} hour(s) ago", + "DATE_FROM_IN_DAYS": "In {{ days }} days(s)", + "DATE_FROM_DAYS_AGO": "{{ days }} days(s) ago", + "DATE_FROM_IN_MONTHS": "In {{ months }} month(s)", + "DATE_FROM_MONTHS_AGO": "{{ months }} month(s) ago", + "DATE_FROM_IN_YEAS": "In {{ years }} year(s)", + "DATE_FROM_YEARS_AGO": "{{ years }} year(s) ago" + }, + "SYSTEMS": { + "SINGULAR": "System", + "PLURAL": "Systems", + "SEARCH": "Search for systems...", + "ADD": "Add system", + "EDIT": "Edit system", + "DELETE": "Delete system", + "DELETE_MSG": "

Are you sure you want delete this system({{ name }})?

Deleting this will immediately delete modules that are not in another system

", + "DELETE_LOADING": "Deleting system...", + "DELETE_SUCCESS": "Successfully deleted system.", + "DELETE_ERROR": "Failed to delete system. Error: {{ error }}", + "SAVING": "Saving system...", + "SAVE_ERROR": "Failed to save system. Error: {{ error }}", + "SAVE_SUCCESS": "Successfully saved system.", + "REMOVE": "Remove system", + "NEW": "New system", + "BULK": "Bulk add systems", + "SUPPORT_URL": "Support URL", + "BOOKABLE": "Bookable Room", + "FEATURES": "Features", + "SIGNAGE": "Signage", + "PUBLIC": "Public", + "CODE": "Code", + "EMAIL": "Email", + "CAPACITY": "Capacity", + "MAP_ID": "Map ID", + "PANEL_COUNT": "Installed Touch Panels", + "CONTROLS": "System Controls", + "START": "START", + "STOP": "STOP", + "REMOVE_ITEM": "Remove {{ item }}", + "LOADING_SETTINGS": "Loading system settings...", + "TAB_ABOUT": "About", + "TAB_MODULES": "Modules", + "TAB_ZONES": "Zones", + "TAB_TRIGGERS": "Triggers", + "TAB_METADATA": "Metadata", + "TAB_SETTINGS_HISTORY": "Settings History", + "FIND_MODULE": "Find existing module...", + "MODULE_LIST": "Module List", + "MODULE_FIELD_STATE": "State", + "MODULE_FIELD_NAME": "Name", + "MODULE_FIELD_TYPE": "Type", + "MODULE_FIELD_CLASS": "Class", + "MODULE_FIELD_ADDRESS": "Address", + "MODULE_FIELD_DEBUG": "Debug", + "MODULE_LIST_EMPTY": "No modules for the selected system", + "DEBUG_ENABLE": "Enable debugging", + "DEBUG_DISABLE": "Disable debugging", + "VIEW_ORIGINAL": "View Original", + "ZONE_FIELD_NAME": "Name", + "ZONE_FIELD_DESCRIPTION": "Description", + "ZONE_DESCRIPTION_EMPTY": "No description", + "ZONE_REMOVE": "Remove system from zone", + "ZONE_SEARCH": "Search for zones...", + "TRIGGER_EDIT": "Edit Trigger", + "TRIGGER_REMOVE": "Remove Trigger", + "COPY_WEBHOOK": "Copy webhook", + "COPIED_WEBHOOK": "Webhook link copied to clipboard", + "TRIGGER_FIELD_NAME": "Name", + "TRIGGER_FIELD_COUNT": "Count", + "TRIGGER_FIELD_ERRORS": "Errors", + "TRIGGER_FIELD_ADDED": "Added", + "TRIGGERS_EMPTY": "No triggers for selected system", + "TRIGGER_ADD": "Add trigger", + "TRIGGER_SEARCH": "Search triggers...", + "ZONE_REQUIRED": "A zone is required", + "NAME_REQUIRED": "A unique system name is required", + "EMAIL_REQUIRED": "A valid email is required", + "DISPLAY_NAME": "Display name", + "URL_VALID": "A valid URL is required", + "TRIGGER_NAME": "Trigger Name", + "TRIGGER_ACTIVE": "Trigger Active", + "TRIGGER_ENABLED": "Enabled", + "TRIGGER_EXECUTE_ENABLED": "Execute Enabled", + "TRIGGER_IMPORTANT": "Important" + }, + "MODULES": { + "SINGULAR": "Module", + "PLURAL": "Modules", + "SEARCH": "Search for modules...", + "ADD": "Add module", + "DELETE": "Delete module", + "DELETE_MSG": "

Are you sure you want delete this module?

Deleting this will module immediately remove it from any system associated with it

", + "DELETE_LOADING": "Deleting module...", + "DELETE_SUCCESS": "Successfully deleted module.", + "DELETE_ERROR": "Failed to delete module. Error: {{ error }}", + "SAVING": "Saving module...", + "SAVE_ERROR": "Failed to save module. Error: {{ error }}", + "SAVE_SUCCESS": "Successfully saved module.", + "REMOVE": "Remove module", + "RUNTIME_ERRORS_VIEW": "View Runtime Errors", + "RUNTIME_ERRORS_NO": "No runtime errors found", + "RUNTIME_ERRORS_LOADING": "Loading runtime errors details...", + "TOGGLE_POWER": "Toggle Power", + "VIEW_STATE": "View State", + "EDIT": "Edit Module", + "LOAD": "Load Module", + "RELOAD": "Reload Module", + "VIEW_ERRORS": "View runtime errors", + "ADD_TO_SYSTEM": "Add to another system", + "NOTES": "Notes", + "IP_ADDRESS": "IP", + "PORT_NUMBER": "Port Number", + "PROTOCOL": "Protocol", + "CONTROLS": "Module Controls", + "START": "START", + "STOP": "STOP", + "LOADING_SETTINGS": "Loading module settings...", + "TAB_ABOUT": "About", + "TAB_SYSTEMS": "Systems", + "TAB_SETTINGS_HISTORY": "Settings History", + "NEW": "New module", + "SYSTEMS_FIELD_NAME": "Name", + "SYSTEMS_FIELD_MODULE_COUNT": "No. Modules", + "SYSTEMS_EMPTY": "No systems with the selected module", + "FIELD_STATE": "State", + "DRIVER_REQUIRED": "A driver is required", + "CONTROL_SYSTEM": "Control System", + "SYSTEM_REQUIRED": "A control system is required", + "URI": "Module URI", + "URI_REQUIRED": "A valid URI is required", + "FIELD_IP": "IP Address of FQDN", + "IP_REQUIRED": "A valid IP address or domain name is required", + "PORT_REQUIRED": "A valid port number from 1 to 65535 is required", + "MAKEBREAK": "Makebreak", + "IGNORE_CONNECTED": "Ignore connected", + "CUSTOM_NAME": "Custom Name", + "STATE": "Module State", + "STATE_UPDATE": "Update state", + "STATE_LOADING": "Loading module's state..." + }, + "ZONES": { + "SINGULAR": "Zone", + "PLURAL": "Zones", + "SEARCH": "Search for zones...", + "ADD": "Add zone", + "EDIT": "Edit zone", + "DELETE": "Delete zone", + "DELETE_MSG": "

Are you sure you want delete this zone?

Deleting this zone will immediately remove systems without another zone

", + "DELETE_LOADING": "Deleting zone...", + "DELETE_SUCCESS": "Successfully deleted zone.", + "DELETE_ERROR": "Failed to delete zone. Error: {{ error }}", + "SAVING": "Saving zone...", + "SAVE_ERROR": "Failed to save zone. Error: {{ error }}", + "SAVE_SUCCESS": "Successfully saved zone.", + "REMOVE": "Remove zone", + "NEW": "New zone", + "BULK": "Bulk add zones", + "TAB_ABOUT": "About", + "TAB_SYSTEMS": "Systems", + "TAB_TRIGGERS": "Triggers", + "TAB_METADATA": "Metadata", + "TAB_CHILDREN": "Children", + "TAB_SETTINGS_HISTORY": "Settings History", + "TAG_WARNING": "Tags set in this zone require a parent zone to work correctly.", + "SELECT_SYSTEM": "Select system", + "PARENT_ID": "Parent ID", + "LOCATION": "Location", + "LOCATION_PLACEHOLDER": "Geo-location details for zone. ", + "CODE": "Code", + "CODE_PLACEHOLDER": "Organisation Code", + "TYPE": "Type", + "TYPE_PLACEHOLDER": "Organisation Category", + "COUNT": "Count", + "CAPACITY": "Capacity", + "MAP_URL": "Map URL", + "TAGS": "Tags", + "TAGS_EMPTY": "No tags for zone", + "DESCRIPTION": "Description", + "LOADING_SETTINGS": "Loading zone settings...", + "SYSTEMS_FIELD_MODULE_COUNT": "No. Modules", + "SYSTEMS_EMPTY": "No systems with the selected zone", + "CHILDREN_EMPTY": "No child zones with the selected zone", + "DESCRIPTION_EMPTY": "No description", + "TRIGGERS_EMPTY": "No triggers for selected zone", + "PARENT_ZONE": "Parent Zone", + "NAME_REQUIRED": "A unique zone name is required", + "DISPLAY_NAME": "Display name" + }, + "DRIVERS": { + "SINGULAR": "Driver", + "PLURAL": "Drivers", + "DEVICE": "Device", + "SSH": "SSH", + "SERVICE": "Service", + "WEBSOCKET": "Websocket", + "LOGIC": "Logic", + "SEARCH": "Search for drivers...", + "ADD": "Add driver", + "EDIT": "Edit driver", + "DELETE": "Delete driver", + "DELETE_MSG": "

Are you sure you want delete this driver?

", + "DELETE_LOADING": "Deleting driver...", + "DELETE_SUCCESS": "Successfully deleted driver.", + "DELETE_ERROR": "Failed to delete driver. Error: {{ error }}", + "SAVING": "Saving driver...", + "SAVE_ERROR": "Failed to save driver. Error: {{ error }}", + "SAVE_SUCCESS": "Successfully saved driver.", + "REMOVE": "Remove driver", + "NEW": "New driver", + "LOADING": "Loading drivers...", + "UPDATE": "Update Drivers", + "UPDATE_COUNT": "{{ count }} updates available", + "UPDATE_SELECTED": "Update selected drivers", + "NO_UPDATES": "No drivers require updating.", + "TAB_ABOUT": "About", + "TAB_MODULES": "Modules", + "TAB_SETTINGS_HISTORY": "Settings History", + "DEFAULT_URI": "Default URI", + "DEFAULT_PORT": "Default Port", + "MODULE_NAME": "Module Name", + "MODULE_NAME_REQUIRED": "A module name is required", + "COMPILED": "Compiled", + "VIEW_ERRORS": "View errors", + "FILENAME": "Filename", + "RECOMPILE": "Recompile", + "RELOAD": "Reload", + "LOADING_SETTINGS": "Loading driver settings...", + "MODULES_EMPTY": "No modules created for driver", + "VIEW_SYSTEMS": "View module's systems", + "SYSTEM_COUNT": "{{ count }} system(s)", + "LOADING_SYSTEMS": "Loading module systems...", + "DRIVER_LIST_ERROR": "Failed to load driver list. Error: {{ error }}", + "COMMIT_LIST_ERROR": "Failed to load driver's commit list. Error: {{ error }}", + "BASE": "Base Driver", + "COMMIT": "Driver Commit", + "COMMIT_SEARCH": "Search for commit...", + "NAME_REQUIRED": "A driver name is required", + "ROLE": "Role", + "COMMITS_LOADING": "Loading commits for the selected driver...", + "DRIVERS_LOADING": "Loading driver list from selected repository...", + "DETAILS_LOADING": "Loading driver details for selected commit...", + "DETAILS_ERROR_1": "Failed to load driver details for commit.", + "DETAILS_ERROR_2": "Please check that the driver compiles correctly and try again." + }, + "REPOS": { + "SINGULAR": "Repository", + "PLURAL": "Repositories", + "SEARCH": "Search for repositories...", + "ADD": "Add repository", + "EDIT": "Edit repository", + "DELETE": "Delete repository", + "REMOVE": "Remove repository", + "NEW": "New repository", + "DELETE_MSG": "

Deleting this repository will immediately remove assoicated drivers and modules

", + "DELETE_LOADING": "Deleting repository...", + "DELETE_SUCCESS": "Successfully deleted repository.", + "DELETE_ERROR": "Failed to delete repository. Error: {{ error }}", + "SAVING": "Saving repository...", + "SAVE_ERROR": "Failed to save repository. Error: {{ error }}", + "SAVE_SUCCESS": "Successfully saved repository.", + "LOADING": "Loading repositories...", + "TAB_ABOUT": "About", + "TAB_DRIVERS": "Drivers", + "INTERFACE_REPO": "Interface Repository", + "DRIVER_REPO": "Driver Repository", + "TYPE_INTERFACE": "Interface", + "TYPE_DRIVER": "Driver", + "FIELD_TYPE": "Type", + "TYPE": "Repository Type", + "FOLDER_NAME": "Folder name", + "FOLDER_NAME_EMPTY": "No folder set", + "FOLDER_NAME_REQUIRED": "A folder name is required", + "URI": "Repository URI", + "AVAILABLE_DRIVERS": "Available Drivers", + "DRIVER_LIST_EMPTY": "No available drivers in repository", + "GIT_PULL_ERROR": "Error pulling latest commit. Error: {{ error }}", + "GIT_PULL_TIMEOUT": "Pull timed out", + "USERNAME": "Repository Username", + "PASSWORD": "Repository Password", + "URI_REQUIRED": "A URI is required", + "BRANCH": "Branch", + "COMMIT": "Repository Commit", + "NAME_REQUIRED": "A repository name is required", + "BRANCH_REQUIRED": "A working branch is required", + "COMMIT_REQUIRED": "A commit is required" + }, + "TRIGGERS": { + "SINGULAR": "Trigger", + "PLURAL": "Triggers", + "SEARCH": "Search for triggers...", + "ADD": "Add trigger", + "EDIT": "Edit trigger", + "NEW": "New trigger", + "DELETE": "Delete trigger", + "DELETE_INSTANCE": "Delete instance", + "REMOVE": "Remove trigger", + "DELETE_MSG": "

Are you sure you want delete this trigger?

Deleting this trigger will immediately remove it from all associated systems and zones

", + "DELETE_LOADING": "Deleting trigger...", + "DELETE_SUCCESS": "Successfully deleted trigger.", + "DELETE_ERROR": "Failed to delete trigger. Error: {{ error }}", + "SAVING": "Saving trigger...", + "SAVE_ERROR": "Failed to save trigger. Error: {{ error }}", + "SAVE_SUCCESS": "Successfully saved trigger.", + "LOADING": "Loading triggers...", + "TAB_ABOUT": "About", + "TAB_INSTANCES": "Instances", + "FIELD_ADDED": "Added", + "FIELD_STATE": "State", + "FIELD_INSTANCE_NAME": "Instance Name", + "INSTANCES_EMPTY": "No instance of trigger", + "REFERENCE_SYSTEM": "Reference System", + "REFERENCE_SYSTEM_MSG": "System to use for available status variables and function calls", + "CONDITIONS": "Conditions", + "CONDITION_ADD": "Add Condition", + "CONDITION_NEW": "New Condition", + "CONDITION_FIELD_TYPE": "Condition Type", + "CONDITION_COMPARE": "Compare Values", + "CONDITION_TIME": "Particular Time", + "CONDITION_COMPARE_EMPTY": "No variable comparision conditions for this trigger", + "CONDITION_TIME_EMPTY": "No time dependant conditions for this trigger", + "CONDITION_SAVE_ERROR": "Failed to save trigger condition. Error {{ error }}", + "CONDITION_SAVE_SUCCESS": "Successfully saved trigger condtion", + "FIELD_VAR_COMPARE": "Variable Comparison Condition", + "FIELD_TIME_DEPS": "Time dependant Condition", + "ACTIONS": "Actions", + "ACTION_ADD": "Add Action", + "ACTION_NEW": "New Action", + "ACTION_FIELD_TYPE": "ACtion Type", + "ACTION_EXECUTE": "Execute Method", + "ACTION_EXECUTE_SELECT": "Select method to execute", + "ACTION_EMAIL": "Send Email", + "ACTION_EMAIL_BODY": "Email Body", + "ACTION_EMAIL_ADDRESS": "Email addresses", + "ACTION_EMAIL_ADDRESS_LIST": "List of email addresses", + "ACTION_EMAIL_EMPTY": "No email actions for this trigger", + "ACTION_FN_EMPTY": "No function call actions for this trigger", + "FIELD_ACTION_FN_CALL": "Function call Action", + "FIELD_ACTION_EMAIL": "Email Action", + "REORDER_CONFIRM_TITLE": "Reoreder trigger {{ type }} action", + "REORDER_CONFIRM_MSG": "Are you sure you want remove this trigger condition?
All systems using this trigger will be updated immediately.", + "REORDER_CONFIRM_LOADING": "Re-ordering trigger actions...", + "REORDER_CONFIRM_ERROR": "Failed to re-ordered trigger action. Error: {{ error }}", + "REORDER_CONFIRM_SUCCESS": "Successfully re-ordered trigger action", + "REMOVE_CONDITION_TITLE": "Remove trigger condition", + "REMOVE_CONDITION_MSG": "Are you sure you want remove this trigger condition?
All systems using this trigger will be updated immediately.", + "REMOVE_CONDITION_LOADING": "Removing trigger condition...", + "REMOVE_CONDITION_ERROR": "Failed to remove trigger condition. Error: {{ error }}", + "REMOVE_CONDITION_SUCCESS": "Successfully removed trigger condition.", + "REMOVE_ACTION_TITLE": "Remove trigger action", + "REMOVE_ACTION_MSG": "Are you sure you want remove this trigger action?
All systems using this trigger will be updated immediately.", + "REMOVE_ACTION_LOADING": "Removing trigger action...", + "REMOVE_ACTION_ERROR": "Failed to remove trigger action. Error: {{ error }}", + "REMOVE_ACTION_SUCCESS": "Successfully removed trigger action.", + "REMOVE_INSTANCE_TITLE": "Remove trigger from {{ type }}", + "REMOVE_INSTANCE_MSG": "Are you sure you want remove this trigger from {{ name }}?
The {{ type }} will be updated immediately.", + "REMOVE_INSTANCE_LOADING": "Removing trigger from {{ type }}", + "REMOVE_INSTANCE_ERROR": "Failed to remove trigger from {{ type }}. Error: {{ error }}", + "REMOVE_INSTANCE_SUCCESS": "Successfully remove trigger from {{ type }}", + "TIME_SCHEDULE": "Recurring Schedule", + "TIME_REPEAT": "Repeat Every", + "TIME_DAY_OF_MONTH": "Day of Month", + "TIME_MONTH_OF_YEAR": "Month of Year", + "TIME_DAY_OF_WEEK": "Day of Week", + "TIME_HOUR_OF_DAY": "Hour of the day", + "TIME_MINUTE_OF_HOUR": "Minute of the hour", + "TIME_FIELD_DATE": "Date", + "TIME_FIELD_TIME": "Time", + "COMPARE_CONSTANT": "Constant Value", + "COMPARE_VARIABLE": "Status Variable", + "COMPARE_VARIABLE_SELECT": "Select status variable", + "COMPARE_VARIABLE_ERROR": "A module status variable is required", + "COMPARE_OP": "Operation", + "COMPARE_OP_EQUAL": "equal to", + "COMPARE_OP_NOT_EQUAL": "not equal to", + "COMPARE_OP_GREATER": "greater than", + "COMPARE_OP_GREATER_EQUAL": "greater than or equal", + "COMPARE_OP_LESS": "less than", + "COMPARE_OP_LESS_EQUAL": "less than or equal", + "COMPARE_OP_AND": "truthy AND", + "COMPARE_OP_OR": "truthy OR", + "COMPARE_OP_XOR": "truthy XOR", + "COMPARE_OP_SELECT": "Select comparision operator", + "COMPARE_TO": "Compared to", + "COMPARE_JSON_REQUIRED": "Valid JSON is required", + "COMPARE_MODULE_SELECT": "Select module", + "COMPARE_SUBKEYS": "Subkeys", + "COMPARE_SUBKEYS_PLACEHOLDER": "Status variable subkeys", + "COMPARE_VARIABLE_LOAD_ERROR": "Failed to load status variables from {{ system }}, {{ module }}", + "ENABLE_WEBHOOK": "Enable Webhook", + "DEBOUNCE_PERIOD": "Debounce Period", + "SUPPORTED_METHODS": "Supported Methods" + }, + "USERS": { + "SINGULAR": "User", + "PLURAL": "Users", + "SEARCH": "Search for users...", + "ADD": "Add user", + "EDIT": "Edit user", + "NEW": "New user", + "BULK": "Bulk add users", + "REMOVE": "Remove user", + "DELETE_MSG": "

Are you sure you want delete this user?

The user will be removed from the system within 24 hours

", + "DELETE_LOADING": "Deleting user...", + "DELETE_SUCCESS": "Successfully deleted user.", + "DELETE_ERROR": "Failed to delete user. Error: {{ error }}", + "SAVING": "Saving user...", + "SAVE_ERROR": "Failed to save user. Error: {{ error }}", + "SAVE_SUCCESS": "Successfully saved user.", + "LOADING": "Loading users...", + "TAB_ABOUT": "About", + "TAB_METADATA": "Metadata", + "TAB_HISTORY": "History", + "ROLE_SUPPORT": "PlaceOS Support", + "ROLE_ADMIN": "System Admin", + "ROLE_NONE": "None", + "FIELD_GROUPS": "User Groups", + "FIELD_DEPARTMENT": "Department", + "AUTHORITY_ID": "Authority ID", + "NO_GROUPS": "No user groups", + "FIELD_SESSION_START": "Session Start", + "FIELD_SESSION_END": "End", + "FIELD_SYSTEMS_ACCESSED": "Systems Accessed", + "VIEW_LOGS": "View", + "LOGS_EMPTY": "No logs found for this user", + "FIRST_NAME": "First Name", + "FIRST_NAME_REQUIRED": "A first name is required", + "LAST_NAME": "Last Name", + "LAST_NAME_REQUIRED": "A last name is required", + "EMAIL_REQUIRED": "A valid email is required", + "STAFF_ID": "Staff ID", + "STAFF_CARD": "Card Number", + "PASSWORD_REQUIRED": "A valid password is required", + "PASSWORD_CONFIRM": "Confirm Password", + "PASSOWRD_MATCH": "Passwords do not match" + }, + "DOMAINS": { + "SINGULAR": "Domain", + "PLURAL": "Domains", + "SEARCH": "Search for domain...", + "ADD": "Add domain", + "EDIT": "Edit domain", + "NEW": "New domain", + "BULK": "Bulk add domains", + "REMOVE": "Remove domain", + "DELETE": "Delete domain", + "DELETE_MSG": "

Are you sure you want delete this domain?

The domain will be deleted immediately.

", + "DELETE_LOADING": "Deleting domain...", + "DELETE_SUCCESS": "Successfully deleted domain.", + "DELETE_ERROR": "Failed to delete domain. Error: {{ error }}", + "SAVING": "Saving domain...", + "SAVE_ERROR": "Failed to save domain. Error: {{ error }}", + "SAVE_SUCCESS": "Successfully saved domain.", + "LOADING": "Loading domains...", + "TAB_ABOUT": "About", + "TAB_APPLICATIONS": "Applications", + "TAB_AUTHENTICATION": "Authentication", + "TAB_USERS": "Users", + "NAME": "Domain Name", + "NAME_PLACEHOLDER": "Domain e.g. www.google.com", + "EMAIL_DOMAINS": "User Email Domains", + "SETTINGS_CONFIG": "Config", + "SETTINGS_INTERNALS": "Internals", + "COPIED_EMAIL_DOMAIN": "Copied email domain to clipboard.", + "SETTINGS_SAVED": "Successfully updated domain settings.", + "APPLICATION_NEW": "New Application", + "APPLICATION_EDIT": "Edit Application", + "APPLICATION_REMOVE": "Remove Application", + "FIELD_REDIRECT_URI": "Redirect URI", + "FIELD_CLIENT_ID": "Client ID", + "FIELD_CLIENT_SECRET": "Client Secret", + "FIELD_TYPE": "Type", + "FIELD_USER": "User", + "FIELD_ROLE": "Role", + "APPLICATIONS_EMPTY": "No applications for this domain", + "COPY_SECRET": "Copy secret to Clipboard", + "COPIED_SECRET": "Copied client secret to Clipboard", + "VIEW_SECRET": "View Secret", + "SECRET_HIDDEN": "Hidden", + + "USER_LIST_EMPTY": "No users associated with this domain", + "NAME_REQUIRED": "A name is required for this domain", + "DOMAIN_REQUIRED": "A valid domain is required", + "LOGIN_URL": "Login URL", + "LOGIN_URL_REQUIRED": "A valid login URL is required", + "LOGOUT_URL": "Logout URL", + "LOGOUT_URL_REQUIRED": "A valid logout URL is required", + "CLIENT_ID": "Client ID", + "CLIENT_SECRET": "Client Secret", + + "AUTHENTICATION_NEW": "New Auth Method", + "AUTHENTICATION_EDIT": "Edit Auth Method", + "AUTHENTICATION_REMOVE": "Remove Auth Method", + "AUTHENTICATION_EMPTY": "No authentication methods for this domain", + "AUTHENTICATION_SOURCE_TYPE": "Authentication Source Type", + "AUTHENTICATION_SOURCE_SELECT": "Select authentication source...", + "AUTHENTICATION_SAVE_ERROR": "Successfully updated authentication source", + "AUTHENTICATION_SAVE_SUCCESS": "Failed to update authentication source. Error: {{ error }}", + "AUTHENTICATION_NAME_REQUIRED": "An auth source name is required", + + "OAUTH_SITE": "Site", + "OAUTH_SITE_PLACEHOLDER": "URL of the SSO provider", + "OAUTH_SCOPES": "Scopes", + "OAUTH_TOKEN_METHOD": "Token Method", + "OAUTH_TOKEN_SCHEME": "Authentication Scheme", + "OAUTH_SCHEME_BODY": "Request Body", + "OAUTH_SCHEME_BASIC": "Basic Auth", + "OAUTH_TOKEN_URL": "Token URL", + "OAUTH_AUTHORISE_URL": "Authorise URL", + "OAUTH_PROFILE_URL": "User Profile URL", + "OAUTH_INFO_MAPPINGS": "Info Mappings", + "OAUTH_AUTHORISE_PARAMS": "Authorise Parameters", + "OAUTH_ENSURE_MATCHING": "Ensure Matching", + + "LDAP_HOST": "Host", + "LDAP_HOST_REQUIRED": "A hostname is required", + "LDAP_PORT": "Port number", + "LDAP_USER_ID": "User ID Key", + "LDAP_AUTH_METHOD": "Authentication Method", + "LDAP_BASE": "Base", + "LDAP_BASE_REQUIRED": "A base is required", + "LDAP_BIND_DN": "Bind DN", + "LDAP_FILTER": "Filter", + + "SAML_ISSUER": "Issuer", + "SAML_ISSUER_REQUIRED": "An issuer is required", + "SAML_IDP_TARGET_URL": "IdP Target URL", + "SAML_IDP_TARGET_URL_REQUIRED": "A IdP Target URL is required", + "SAML_NAME_ID_FORMAT": "Name Identifier Format", + "SAML_NAME_ID_FORMAT_REQUIRED": "A Name Identifier Format is required", + "SAML_REQUEST_ATTRIBUTES": "Request Attributes", + "SAML_REQUEST_ATTRIBUTES_REQUIRED": "Request Attributes are required", + "SAML_ASSERTION_URL": "Assertion URL", + "SAML_ASSERTION_URL_REQUIRED": "An Assertion URL is required", + "SAML_CERT_FINGERPRINT": "Certificate Fingerprint", + "SAML_CERT_FULL": "Full Certificate", + "SAML_UID_ATTRIBUTE": "UID Attribute", + "SAML_ATTRIBUTE_SERVICE_NAME": "Attribute Service Name", + "SAML_ATTRIBUTE_STATEMENTS": "Attribute Statements", + "SAML_IDP_SSO_RUNTIME_PARAMS": "IdP SSO Runtime Params", + "SAML_IDP_SLO_TARGET_URL": "IdP SLO Target URL", + "SAML_SLO_DEFAULT_RELAY_STATE": "SLO Default Relay State", + + "APP_SKIP": "Skip Authorisation", + "APP_NAME_REQUIRED": "An application name is required", + "APP_PRESERVE_ID": "Preserve Client ID", + "APP_CLIENT_ID": "Client ID", + "APP_CLIENT_PLACEHOLDER": "MD5 Hash of the Redirect URI", + "APP_SCOPES": "Access Scopes", + "APP_REDIRECT_URL": "Redirect URL", + "APP_REDIRECT_URL_REQUIRED": "A valid URL is required" + }, + "ADMIN": { + "TITLE": "Admin", + "TAB_ABOUT": "About", + "TAB_DATABASE": "Database", + "TAB_CLUSTERS": "Clusters", + "TAB_EDGES": "Edges", + "TAB_INTERFACES": "Interfaces", + "TAB_MQTT_BROKERS": "MQTT Brokers", + "TAB_TENANT_CONFIG": "Staff Tenants", + "TAB_RESOURCE_IMPORTS": "Resource Imports", + "TAB_EXTENSIONS": "Extensions", + "TAB_API_KEYS": "Application Keys", + "TAB_CUSTOM_SCHEMAS": "Custom Schemas", + "TAB_UPLOAD_STORAGE": "Data Stores", + "TAB_UPLOADS_LIBRARY": "Uploads Library", + "APPLICATION_DETAILS": "Application Details", + "VIEW_CHANGELOG": "View Changelog", + "BUILD": "Build", + "BUILT_AT": "Build", + "BACKEND_SERVICES": "Backend Services", + "PLATFORM": "Platform", + "COPIED": "Copied {{ name }} to clipboard", + "ALL_DOMAINS": "All Domains", + "SELECT_DOMAIN": "Select domain...", + + "BACKEND_SERVICES_EMPTY": "No API service details available.", + "BACKEND_SERVICES_ERROR": "Failed to load API details. Error: {{ error }}", + "DATABASE_REINDEX": "Re-index", + "DATABASE_REINDEX_MSG": "Re-index elasticsearch for the current state of the database", + "DATABASE_BACKFILL": "Backfill", + "DATABASE_BACKFILL_MSG": "Backfill elasticsearch with the current state of the database", + + "CLUSTERS": "PlaceOS Clusters", + "CLUSTERS_VIEW_PROCESSES": "View Processes", + "CLUSTERS_LIST_EMPTY": "No cluster details available", + "CLUSTERS_CPU_USAGE": "CPU Usage", + "CLUSTERS_MEMORY_USAGE": "Memory Usage", + "CLUSTERS_CPU_CORES": "{{ count }} cores", + "CLUSTER": "Cluster", + "CLUSTERS_SEARCH_PROCESSES": "Search processes...", + "CLUSTERS_FIELD_CPU_USAGE": "CPU %", + "CLUSTERS_FIELD_MEMORY_USAGE": "Memory", + "CLUSTERS_FIELD_INSTANCES": "Instances", + "CLUSTER_PROCESSES_EMPTY": "No tasks running on this cluster", + "CLUSTER_PROCESS_KILL": "Kill process", + "CLUSTER_PROCESS_KILL_MSG": "

Are you sure you want kill the process for \"{{ id }}\"?

The process will be terminated immediately.The process may be restarted after a short while.

", + "CLUSTER_PROCESS_KILL_LOADING": "Terminating process. Please wait...", + "CLUSTER_PROCESS_KILL_ERROR": "Failed to kill process. Error: {{ error }}", + + "EDGE_HEADER": "PlaceOS Edge Nodes", + "EDGE_ADD": "Add Edge node", + "EDGE_NEW": "New Edge node", + "EDGE_EDIT": "Edit Edge node", + "EDGE_SAVING": "Saving edge node...", + "EDGE_REMOVE": "Remove Edge node", + "EDGE_ERROR": "Failed to update edge node.", + "EDGE_NAME_PLACEHOLDER": "Name of the Edge node", + "EDGE_DESCRIPTION_PLACEHOLDER": "Description of the Edge node", + "EDGE_NAME_REQUIRED": "An Edge name is required", + "EDGE_NEW_SUCCESS": "Successfully updated edge.", + "EDGE_EDIT_SUCCESS": "Successfully added new edge. Please make sure to save the API key as you will not be able to view it again in the future.", + "EDGE_LIST_EMPTY": "No edges available on this cluster", + "INTERFACES_HEADER": "PlaceOS Interfaces", + "INTERFACES_COMMIT_EMPTY": "No commit hash", + + "BROKERS_HEADER": "MQTT Brokers", + "BROKERS_ADD": "Add MQTT Broker", + "BROKERS_NEW": "New MQTT Broker", + "BROKERS_EDIT": "New MQTT Broker", + "BROKERS_REMOVE": "Remove MQTT Broker", + "BROKER_LIST_EMPTY": "No MQTT Brokers", + "BROKERS_FIELD_HOST": "Host", + "BROKERS_FIELD_PORT": "Port", + "BROKERS_FIELD_TLS": "TLS", + "BROKERS_FIELD_FILTERS": "Filters", + "BROKERS_FIELD_AUTH_TYPE": "Auth Type", + "BROKERS_AUTH_TYPE_CERT": " Certificate", + "BROKERS_AUTH_TYPE_PASS": "User Password", + "BROKERS_AUTH_TYPE_NONE": "No Auth", + "BROKERS_FILTERS_EMPTY": "No filters", + "BROKERS_NAME_REQUIRED": "A broker name is required", + "BROKERS_HOST_REQUIRED": "A broker name is required", + "BROKERS_PORT_REQUIRED": "A valid port number between 1 - 65535 is required", + "BROKERS_USERNAME": "Username", + "BROKERS_USERNAME_REQUIRED": "A valid username is required", + "BROKERS_PASSWORD": "Password", + "BROKERS_PASSWORD_REQUIRED": "A valid password is required ", + "BROKERS_CERT": "Certificate", + "BROKERS_CERT_REQUIRED": "A valid certificate is required ", + + "TENANTS_HEADER": "Staff API Tenants", + "TENANTS_ADD": "Add Tenant", + "TENANTS_NEW": "New Tenant", + "TENANTS_EDIT": "Edit Tenant", + "TENANTS_REMOVE": "Remove Tenant", + "TENANTS_PLATFORM": "Platform", + "TENANTS_SECRET_EXPIRY": "Expires", + "TENANTS_EMPTY": "No tenants for the selected domain", + "TENANTS_EDIT_BOOKING_LIMITS": "Edit Booking Limits", + "TENANTS_FIELD_NAME": "Tenant name", + "TENANTS_NAME_ERROR": "A tenant name is required", + "TENANTS_EMAIL_DOMAIN": "Email Domain", + "TENANTS_EMAIL_PLACEHOLDER": "Domain of user's email for this tenant", + "TENANTS_EMAIL_ERROR": "A domain is required", + "TENANTS_EARLY_CHECKIN": "Early Check-in before Meeting", + "TENANTS_SERVICE_ACCOUNT": "Service Account", + "TENANTS_SERVICE_ACCOUNT_ERROR": "Service account should be a valid email address", + "TENANTS_DELEGATED": "Delegated", + "TENANTS_ITEM_REQUIRED": "A {{ name }} is required", + "TENANTS_CONFIG_OUTLOOK": "Configure Outlook Plugin", + "TENANTS_APP_ID": "Outlook App ID", + "TENANTS_APP_ID_REQUIRED": "An application ID is required", + "TENANTS_APP_DOMAIN": "Outlook App Domain", + "TENANTS_APP_DOMAIN_REQUIRED": "An application domain is required", + "TENANTS_APP_RESOURCE": "Outlook App Resource", + "TENANTS_APP_RESOURCE_REQUIRED": "An application resource is required", + "TENANTS_SOURCE_LOCATION": "Outlook Source Location", + "TENANTS_SOURCE_LOCATION_REQUIRED": "A source location is required", + "TENANTS_BASE_PATH": "Outlook Base Path", + "TENANTS_BASE_PATH_REQUIRED": "Base path to the application root folder is required", + "TENANTS_BOOKING_LIMITS": "Booking Limits", + "TENANTS_MINUTES": "{{ minute }} minutes", + "TENANTS_HOURS": "{{ hour }} hours", + "TENANTS_MINUTES_HOURS": "{{ hour }} hours {{ minute }} minutes", + "TENANTS_SAVE_ERROR": "Failed to update tenant.", + "TENANTS_SAVE_SUCCESS": "Successfully updated tenant.", + + "RESOURCE_IMPORTS_HEADER": "Resource Imports", + "RESOURCE_IMPORTS_ALL": "Import All", + "RESOURCE_IMPORTS_IMPORTED": "Imported", + "RESOURCE_IMPORTS_IMPORT": "Import resource", + "RESOURCE_IMPORTS_VIEW": "View System", + "RESOURCE_IMPORTS_EMPTY": "No resource available to import on selected domain", + "RESOURCE_IMPORTS_ALL_WARNING": "All resources are already imported.", + "RESOURCE_IMPORTS_ALL_TITLE": "Import missing resources?", + "RESOURCE_IMPORTS_ALL_MSG": "Are you sure you want to import the following {{ count }} resources?", + "RESOURCE_IMPORTS_ALL_LOADING": "Importing missing resources...", + "RESOURCE_IMPORTS_ALL_SUCCESS": "Successfully imported {{ count }} resources.", + "RESOURCE_IMPORTS_SUCCESS": "Successfully imported resource {{ name }}.", + + "EXTENSIONS_HEADER": "Extensions", + "EXTENSIONS_ADD": "Add Extension", + "EXTENSIONS_NEW": "New Extension", + "EXTENSIONS_EDIT": "Edit Extension", + "EXTENSIONS_REMOVE": "Remove Extension", + "EXTENSIONS_SAVING": "Saving extension...", + "EXTENSIONS_NOTICE": "Note: Backoffice requires a full page refresh for extension changes and additions to apply", + "EXTENSIONS_LIST_EMPTY": "No extensions available for the selected domain", + "EXTENSIONS_FIELD_CHECKS": "Checks", + "EXTENSIONS_FIELD_CONDITIONS": "Conditions", + "EXTENSIONS_FIELD_TYPE": "Type", + "EXTENSIONS_FIELD_NAME": "Extension Display Name", + "EXTENSIONS_NAME_REQUIRED": "A valid name is required", + "EXTENSIONS_FIELD_TAB": "Tab Name", + "EXTENSIONS_FIELD_URL": "URL", + "EXTENSIONS_URL_REQUIRED": "A valid URL is required", + "EXTENSIONS_CONDITION_ADD": "Add Condition", + "EXTENSIONS_CONDITION_VALUE": "Condition value", + "EXTENSIONS_CONDITION_OP": "Operation", + "EXTENSIONS_CONDITION_FIELD": "Condition Field", + + "APP_KEYS_HEADER": "PlaceOS Application Keys", + "APP_KEYS_ADD": "Add App Key", + "APP_KEYS_NEW": "New Application Key", + "APP_KEYS_REMOVE": "Remove Application Key", + "APP_KEYS_LAST_DETAILS": "Last App Key Details", + "APP_KEYS_FIELD_SCOPES": "Scopes", + "APP_KEYS_FIELD_PERMISSIONS": "Access", + "APP_KEYS_SCOPES_REQUIRE": "At least one scope is required", + "APP_KEYS_NAME_REQUIRED": "A name is required", + "APP_KEYS_PERMISSIONS_EMPTY": "No permissions", + "APP_KEYS_COPIED": "Copied Application Key to clipboard", + "APP_KEYS_LIST_EMPTY": "No application keys configured for this domain", + "PERMISSIONS": "Permissions", + "PERMISSIONS_ADMIN": "Admin", + "PERMISSIONS_SUPPORT": "Support", + "PERMISSIONS_USER": "User", + "PERMISSIONS_NONE": "None", + + "SCHEMA_HEADER": "Custom Schemas", + "SCHEMA_SELECT": "Select Schema...", + "SCHEMA_ADD": "Add Schema", + "SCHEMA_NAME": "Schema Name", + "SCHEMA_NEW": "New Schema", + "SCHEMA_SELECT_MSG": "Select a schema to edit", + + "STORAGE_HEADER": "PlaceOS Data Stores", + "STORAGE_ADD": "Add Provider", + "STORAGE_NEW": "New Storage Provider", + "STORAGE_EDIT": "Edit Storage Provider", + "STORAGE_REMOVE": "Remove Storage Provider", + "STORAGE_REMOVE_TITLE": "Remove Storage Provider", + "STORAGE_REMOVE_MSG": "Are you sure you want to remove the storage provider for {{ type }} {{name}}?", + "STORAGE_REMOVE_LOADING": "Removing storage provider...", + "STORAGE_LOADING": "Loading storage providers...", + "STORAGE_FIELD_TYPE": "Type", + "STORAGE_FIELD_REGION": "Region", + "STORAGE_LIST_EMPTY": "No storage providers for {{ item }} domain", + "STORAGE_SAVE_SUCCESS": "Successfully saved storage provider.", + "STORAGE_SAVE_ERROR": "Failed to save storage provider.", + "STORAGE_BUCKET_NAME_LABEL": "Bucket Name", + "STORAGE_BUCKET_NAME_REQUIRED": "Bucket Name is required", + "STORAGE_REGION_LABEL": "Region", + "STORAGE_REGION_REQUIRED": "Region is required", + "STORAGE_ENDPOINT_LABEL": "Endpoint", + "STORAGE_ACCESS_KEY_LABEL": "Access Key", + "STORAGE_ACCESS_KEY_REQUIRED": "Access Key is required", + "STORAGE_SECRET_LABEL": "Access Secret", + "STORAGE_SECRET_REQUIRED": "Access Secret is required", + "STORAGE_ALLOWED_EXTENSIONS_LABEL": "Allowed file extensions", + "STORAGE_ALLOWED_MIME_LABEL": "Allowed file MIME types", + "STORAGE_ALLOW_ALL_EXTENSIONS": "Allow all file extensions", + "STORAGE_ALLOW_ALL_MIME": "Allow all MIME types", + + "UPLOADS_LIB_HEADER": "Uploads Library", + "UPLOADS_LIB_DOWNLOAD": "Download Upload", + "UPLOADS_LIB_DOWNLOAD_ERROR": "Failed to download upload. Error: {{ error }}", + "UPLOADS_LIB_VIEW": "View Upload", + "UPLOADS_LIB_REMOVE": "Remove Upload", + "UPLOADS_LIB_REMOVE_MSG": "

Are you sure you want remove the upload \"{{ filename }}\"?

The upload will be deleted immediately.

", + "UPLOADS_LIB_REMOVE_LOADING": "Removing upload...", + "UPLOADS_LIB_FIELD_TYPE": "File Type", + "UPLOADS_LIB_FIELD_SIZE": "Size", + "UPLOADS_LIB_LIST_EMPTY": "No uploads for the selected domain" + } +} diff --git a/apps/backoffice/src/test-setup.ts b/apps/backoffice/src/test-setup.ts index 748ef8f3..22dfb87a 100644 --- a/apps/backoffice/src/test-setup.ts +++ b/apps/backoffice/src/test-setup.ts @@ -1,13 +1,14 @@ import { defineGlobalsInjections } from '@ngneat/spectator'; -import { TranslateModule } from '@ngx-translate/core'; import 'jest-preset-angular/setup-jest'; -import { MockModule } from 'ng-mocks'; +import { MockPipe } from 'ng-mocks'; import { TextEncoder, TextDecoder } from 'util'; +import { TranslatePipe } from 'apps/backoffice/src/app/ui/translate.pipe'; + global.TextEncoder = TextEncoder; global.TextDecoder = TextDecoder; defineGlobalsInjections({ - imports: [MockModule(TranslateModule.forRoot())], + declarations: [MockPipe(TranslatePipe)], }); diff --git a/package-lock.json b/package-lock.json index 48d02dbf..a331b81d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -38,8 +38,6 @@ "@angular/language-service": "18.2.3", "@ngneat/spectator": "^18.0.2", "@ngneat/tailwind": "^7.0.3", - "@ngx-translate/core": "^16.0.3", - "@ngx-translate/http-loader": "^16.0.0", "@nrwl/tao": "19.6.5", "@nx/cypress": "20.1.4", "@nx/eslint": "20.1.4", @@ -6218,34 +6216,6 @@ "webpack": "^5.54.0" } }, - "node_modules/@ngx-translate/core": { - "version": "16.0.3", - "resolved": "https://registry.npmjs.org/@ngx-translate/core/-/core-16.0.3.tgz", - "integrity": "sha512-UPse66z9tRUmIpeorYodXBQY6O4foUmj9jy9cCuuja7lqdOwRBWPzCWqc+qYIXv5L2QoqZdxgHtqoUz+Q9weSA==", - "dev": true, - "license": "MIT", - "dependencies": { - "tslib": "^2.3.0" - }, - "peerDependencies": { - "@angular/common": ">=16", - "@angular/core": ">=16" - } - }, - "node_modules/@ngx-translate/http-loader": { - "version": "16.0.0", - "resolved": "https://registry.npmjs.org/@ngx-translate/http-loader/-/http-loader-16.0.0.tgz", - "integrity": "sha512-l3okOHGVxZ1Bm55OpakSfXvI2yYmVmhYqgwGU4aIQIRUqpkBCrSDZnmrHTcZfsGJzXKB5E2D2rko9i28gBijmA==", - "dev": true, - "license": "MIT", - "dependencies": { - "tslib": "^2.3.0" - }, - "peerDependencies": { - "@angular/common": ">=16", - "@angular/core": ">=16" - } - }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", diff --git a/package.json b/package.json index b40cee05..e315500e 100644 --- a/package.json +++ b/package.json @@ -58,8 +58,6 @@ "@angular/language-service": "18.2.3", "@ngneat/spectator": "^18.0.2", "@ngneat/tailwind": "^7.0.3", - "@ngx-translate/core": "^16.0.3", - "@ngx-translate/http-loader": "^16.0.0", "@nrwl/tao": "19.6.5", "@nx/cypress": "20.1.4", "@nx/eslint": "20.1.4", diff --git a/tsconfig.base.json b/tsconfig.base.json index 0960beb5..b62f7465 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -13,6 +13,7 @@ "lib": ["es2017", "dom"], "skipLibCheck": true, "skipDefaultLibCheck": true, + "resolveJsonModule": true, "baseUrl": ".", "paths": {} },