mirror of
https://github.com/Eugeny/tabby.git
synced 2025-07-19 18:07:58 +00:00
Translation infrastructure
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import { Injectable } from '@angular/core'
|
||||
import { ToolbarButtonProvider, ToolbarButton, AppService, HostAppService, HotkeysService } from 'tabby-core'
|
||||
import { ToolbarButtonProvider, ToolbarButton, AppService, HostAppService, HotkeysService, TranslateService } from 'tabby-core'
|
||||
|
||||
import { SettingsTabComponent } from './components/settingsTab.component'
|
||||
|
||||
@@ -10,6 +10,7 @@ export class ButtonProvider extends ToolbarButtonProvider {
|
||||
hostApp: HostAppService,
|
||||
hotkeys: HotkeysService,
|
||||
private app: AppService,
|
||||
private translate: TranslateService,
|
||||
) {
|
||||
super()
|
||||
hostApp.settingsUIRequest$.subscribe(() => this.open())
|
||||
@@ -24,7 +25,7 @@ export class ButtonProvider extends ToolbarButtonProvider {
|
||||
provide (): ToolbarButton[] {
|
||||
return [{
|
||||
icon: require('./icons/cog.svg'),
|
||||
title: 'Settings',
|
||||
title: this.translate.instant('Settings'),
|
||||
touchBarNSImage: 'NSTouchBarComposeTemplate',
|
||||
weight: 10,
|
||||
click: (): void => this.open(),
|
||||
|
@@ -1,12 +1,12 @@
|
||||
h3.mb-3 Config sync
|
||||
h3.mb-3(translate) Config sync
|
||||
|
||||
ul.nav-tabs(ngbNav, #nav='ngbNav')
|
||||
li(ngbNavItem)
|
||||
a(ngbNavLink) Sync
|
||||
a(ngbNavLink, translate) Sync
|
||||
ng-template(ngbNavContent)
|
||||
.form-line
|
||||
.header
|
||||
.title Sync host
|
||||
.title(translate) Sync host
|
||||
|
||||
.input-group.w-50
|
||||
input.form-control(
|
||||
@@ -20,8 +20,8 @@ ul.nav-tabs(ngbNav, #nav='ngbNav')
|
||||
|
||||
.form-line
|
||||
.header
|
||||
.title Secret sync token
|
||||
.description Get it from the Tabby Web settings window
|
||||
.title(translate) Secret sync token
|
||||
.description(translate) Get it from the Tabby Web settings window
|
||||
|
||||
.input-group
|
||||
input.form-control(
|
||||
@@ -38,16 +38,16 @@ ul.nav-tabs(ngbNav, #nav='ngbNav')
|
||||
ng-container(*ngIf='config.store.configSync.token')
|
||||
.alert.alert-danger(*ngIf='connectionSuccessful === false')
|
||||
i.fas.fa-exclamation-triangle
|
||||
span.ml-2 Connection failed: {{connectionError}}
|
||||
span.ml-2(translate, [translateParams]='{error: connectionError}') Connection failed: {error}
|
||||
|
||||
ng-container(*ngIf='connectionSuccessful')
|
||||
.form-line
|
||||
.header
|
||||
.title Configs
|
||||
.title(translate) Configs
|
||||
|
||||
div(*ngIf='configs === null')
|
||||
i.fas.fa-fw.fa-circle-notch.fa-spin
|
||||
span.ml-2 Loading configs...
|
||||
span.ml-2(translate) Loading configs...
|
||||
|
||||
ng-container(*ngIf='configs !== null')
|
||||
.list-group-light
|
||||
@@ -59,34 +59,34 @@ ul.nav-tabs(ngbNav, #nav='ngbNav')
|
||||
i.fas.fa-fw.fa-file
|
||||
.ml-2.d-flex.flex-column.align-items-start
|
||||
div {{cfg.name}}
|
||||
small.text-muted Modified on {{cfg.modified_at|date:'medium'}}
|
||||
small.text-muted(translate, [translateParams]='{date: cfg.modified_at|date:"medium"}') Modified on {date}
|
||||
.mr-auto
|
||||
button.btn.btn-link.ml-1(
|
||||
(click)='uploadAndSync(cfg)',
|
||||
[class.hover-reveal]='!isActiveConfig(cfg)'
|
||||
)
|
||||
i.fas.fa-arrow-up
|
||||
span.ml-2(*ngIf='isActiveConfig(cfg)') Upload
|
||||
span.ml-2(*ngIf='!isActiveConfig(cfg)') Replace
|
||||
span.ml-2(*ngIf='isActiveConfig(cfg)', translate) Upload
|
||||
span.ml-2(*ngIf='!isActiveConfig(cfg)', translate) Replace
|
||||
button.btn.btn-link.ml-1(
|
||||
(click)='downloadAndSync(cfg)',
|
||||
[class.hover-reveal]='!isActiveConfig(cfg)'
|
||||
)
|
||||
i.fas.fa-arrow-down
|
||||
span.ml-2 Download
|
||||
span.ml-2(translate) Download
|
||||
a.list-group-item.list-group-item-action.d-flex.align-items-center(
|
||||
href='#',
|
||||
(click)='uploadAsNew()'
|
||||
)
|
||||
i.fas.fa-fw
|
||||
i.fas.fa-fw.fa-cloud-upload-alt
|
||||
.ml-2 Upload as a new config
|
||||
.ml-2(translate) Upload as a new config
|
||||
|
||||
ng-container(*ngIf='hasMatchingRemoteConfig()')
|
||||
.form-line
|
||||
.header
|
||||
.title Sync automatically
|
||||
.description Automatically upload changes and check for updates every minute
|
||||
.title(translate) Sync automatically
|
||||
.description(translate) Automatically upload changes and check for updates every minute
|
||||
|
||||
toggle(
|
||||
[(ngModel)]='config.store.configSync.auto',
|
||||
@@ -94,11 +94,11 @@ ul.nav-tabs(ngbNav, #nav='ngbNav')
|
||||
)
|
||||
|
||||
li(ngbNavItem)
|
||||
a(ngbNavLink) Advanced
|
||||
a(ngbNavLink, translate) Advanced
|
||||
ng-template(ngbNavContent)
|
||||
.form-line
|
||||
.header
|
||||
.title Sync hotkeys
|
||||
.title(translate) Sync hotkeys
|
||||
toggle(
|
||||
[(ngModel)]='config.store.configSync.parts.hotkeys',
|
||||
(ngModelChange)='config.save()',
|
||||
@@ -106,7 +106,7 @@ ul.nav-tabs(ngbNav, #nav='ngbNav')
|
||||
|
||||
.form-line
|
||||
.header
|
||||
.title Sync window settings
|
||||
.title(translate) Sync window settings
|
||||
toggle(
|
||||
[(ngModel)]='config.store.configSync.parts.appearance',
|
||||
(ngModelChange)='config.save()',
|
||||
@@ -114,7 +114,7 @@ ul.nav-tabs(ngbNav, #nav='ngbNav')
|
||||
|
||||
.form-line
|
||||
.header
|
||||
.title Sync Vault
|
||||
.title(translate) Sync Vault
|
||||
toggle(
|
||||
[(ngModel)]='config.store.configSync.parts.vault',
|
||||
(ngModelChange)='config.save()',
|
||||
|
@@ -1,7 +1,7 @@
|
||||
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
|
||||
import { Component, HostBinding } from '@angular/core'
|
||||
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
|
||||
import { BaseComponent, ConfigService, PromptModalComponent, HostAppService, PlatformService, NotificationsService } from 'tabby-core'
|
||||
import { BaseComponent, ConfigService, PromptModalComponent, HostAppService, PlatformService, NotificationsService, TranslateService } from 'tabby-core'
|
||||
import { Config, ConfigSyncService } from '../services/configSync.service'
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ export class ConfigSyncSettingsTabComponent extends BaseComponent {
|
||||
private hostApp: HostAppService,
|
||||
private ngbModal: NgbModal,
|
||||
private notifications: NotificationsService,
|
||||
private translate: TranslateService,
|
||||
) {
|
||||
super()
|
||||
}
|
||||
@@ -54,9 +55,9 @@ export class ConfigSyncSettingsTabComponent extends BaseComponent {
|
||||
}
|
||||
|
||||
async uploadAsNew () {
|
||||
let name = `New config on ${this.hostApp.platform}`
|
||||
let name = this.translate.instant('New config on {platform}', this.hostApp)
|
||||
const modal = this.ngbModal.open(PromptModalComponent)
|
||||
modal.componentInstance.prompt = 'Name for the new config'
|
||||
modal.componentInstance.prompt = this.translate.instant('Name for the new config')
|
||||
modal.componentInstance.value = name
|
||||
name = (await modal.result)?.value
|
||||
if (!name) {
|
||||
@@ -72,8 +73,11 @@ export class ConfigSyncSettingsTabComponent extends BaseComponent {
|
||||
if (this.config.store.configSync.configID !== cfg.id) {
|
||||
if ((await this.platform.showMessageBox({
|
||||
type: 'warning',
|
||||
message: 'Overwrite the config on the remote side and start syncing?',
|
||||
buttons: ['Overwrite remote and sync', 'Cancel'],
|
||||
message: this.translate.instant('Overwrite the config on the remote side and start syncing?'),
|
||||
buttons: [
|
||||
this.translate.instant('Overwrite remote and sync'),
|
||||
this.translate.instant('Cancel'),
|
||||
],
|
||||
defaultId: 1,
|
||||
cancelId: 1,
|
||||
})).response === 1) {
|
||||
@@ -83,14 +87,17 @@ export class ConfigSyncSettingsTabComponent extends BaseComponent {
|
||||
this.configSync.setConfig(cfg)
|
||||
await this.configSync.upload()
|
||||
this.loadConfigs()
|
||||
this.notifications.info('Config uploaded')
|
||||
this.notifications.info(this.translate.instant('Config uploaded'))
|
||||
}
|
||||
|
||||
async downloadAndSync (cfg: Config) {
|
||||
if ((await this.platform.showMessageBox({
|
||||
type: 'warning',
|
||||
message: 'Overwrite the local config and start syncing?',
|
||||
buttons: ['Overwrite local and sync', 'Cancel'],
|
||||
message: this.translate.instant('Overwrite the local config and start syncing?'),
|
||||
buttons: [
|
||||
this.translate.instant('Overwrite local and sync'),
|
||||
this.translate.instant('Cancel'),
|
||||
],
|
||||
defaultId: 1,
|
||||
cancelId: 1,
|
||||
})).response === 1) {
|
||||
@@ -98,7 +105,7 @@ export class ConfigSyncSettingsTabComponent extends BaseComponent {
|
||||
}
|
||||
this.configSync.setConfig(cfg)
|
||||
await this.configSync.download()
|
||||
this.notifications.info('Config downloaded')
|
||||
this.notifications.info(this.translate.instant('Config downloaded'))
|
||||
}
|
||||
|
||||
hasMatchingRemoteConfig () {
|
||||
|
@@ -2,13 +2,13 @@
|
||||
h3.m-0 {{profile.name}}
|
||||
|
||||
.modal-header(*ngIf='defaultsMode')
|
||||
h3.m-0 Defaults for {{profileProvider.name}}
|
||||
h3.m-0(translate, [translateParams]='{type: profileProvider.name}') Defaults for {type}
|
||||
|
||||
.modal-body
|
||||
.row
|
||||
.col-12.col-lg-4
|
||||
.form-group(*ngIf='!defaultsMode')
|
||||
label Name
|
||||
label(translate) Name
|
||||
input.form-control(
|
||||
type='text',
|
||||
autofocus,
|
||||
@@ -16,7 +16,7 @@
|
||||
)
|
||||
|
||||
.form-group(*ngIf='!defaultsMode')
|
||||
label Group
|
||||
label(translate) Group
|
||||
input.form-control(
|
||||
type='text',
|
||||
alwaysVisibleTypeahead,
|
||||
@@ -26,7 +26,7 @@
|
||||
)
|
||||
|
||||
.form-group(*ngIf='!defaultsMode')
|
||||
label Icon
|
||||
label(translate) Icon
|
||||
.input-group
|
||||
input.form-control(
|
||||
type='text',
|
||||
@@ -45,7 +45,7 @@
|
||||
|
||||
.form-line
|
||||
.header
|
||||
.title Color
|
||||
.title(translate) Color
|
||||
input.form-control.w-50(
|
||||
type='text',
|
||||
[(ngModel)]='profile.color',
|
||||
@@ -56,8 +56,8 @@
|
||||
|
||||
.form-line
|
||||
.header
|
||||
.title Disable dynamic tab title
|
||||
.description Connection name will be used instead
|
||||
.title(translate) Disable dynamic tab title
|
||||
.description(translate) Connection name will be used instead
|
||||
toggle([(ngModel)]='profile.disableDynamicTitle')
|
||||
|
||||
.mb-4
|
||||
@@ -66,5 +66,5 @@
|
||||
ng-template(#placeholder)
|
||||
|
||||
.modal-footer
|
||||
button.btn.btn-primary((click)='save()') Save
|
||||
button.btn.btn-danger((click)='cancel()') Cancel
|
||||
button.btn.btn-primary((click)='save()', translate) Save
|
||||
button.btn.btn-danger((click)='cancel()', translate) Cancel
|
||||
|
@@ -1,5 +1,5 @@
|
||||
.modal-header
|
||||
h5 Press the key now
|
||||
h5(translate) Press the key now
|
||||
|
||||
.modal-body
|
||||
.input
|
||||
@@ -9,4 +9,4 @@
|
||||
div([style.width]='timeoutProgress + "%"')
|
||||
|
||||
.modal-footer
|
||||
button.btn.btn-primary((click)='close()') Cancel
|
||||
button.btn.btn-primary((click)='close()', translate) Cancel
|
||||
|
@@ -1,4 +1,4 @@
|
||||
h3.mb-3 Hotkeys
|
||||
h3.mb-3(translate) Hotkeys
|
||||
|
||||
.input-group.mb-4
|
||||
.input-group-prepend
|
||||
|
@@ -3,4 +3,4 @@
|
||||
.stroke(*ngFor='let stroke of item') {{stroke}}
|
||||
.remove((click)='removeItem(item)') ×
|
||||
|
||||
.add((click)='addItem()') Add...
|
||||
.add((click)='addItem()', translate) Add...
|
||||
|
@@ -1,12 +1,12 @@
|
||||
h3.mb-3 Profiles
|
||||
h3.mb-3(translate) Profiles
|
||||
|
||||
ul.nav-tabs(ngbNav, #nav='ngbNav')
|
||||
li(ngbNavItem)
|
||||
a(ngbNavLink) Profiles
|
||||
a(ngbNavLink, translate) Profiles
|
||||
ng-template(ngbNavContent)
|
||||
.form-line
|
||||
.header
|
||||
.title Default profile for new tabs
|
||||
.title(translate) Default profile for new tabs
|
||||
|
||||
select.form-control(
|
||||
[(ngModel)]='config.store.terminal.profile',
|
||||
@@ -30,7 +30,7 @@ ul.nav-tabs(ngbNav, #nav='ngbNav')
|
||||
|
||||
button.btn.btn-primary.flex-shrink-0.ml-3((click)='newProfile()')
|
||||
i.fas.fa-fw.fa-plus
|
||||
| New profile
|
||||
span(translate) New profile
|
||||
|
||||
.list-group.mt-3.mb-3
|
||||
ng-container(*ngFor='let group of profileGroups')
|
||||
@@ -40,7 +40,7 @@ ul.nav-tabs(ngbNav, #nav='ngbNav')
|
||||
)
|
||||
.fa.fa-fw.fa-chevron-right(*ngIf='group.collapsed')
|
||||
.fa.fa-fw.fa-chevron-down(*ngIf='!group.collapsed')
|
||||
span.ml-3.mr-auto {{group.name || "Ungrouped"}}
|
||||
span.ml-3.mr-auto {{group.name || ("Ungrouped"|translate)}}
|
||||
button.btn.btn-sm.btn-link.hover-reveal.ml-2(
|
||||
*ngIf='group.editable && group.name',
|
||||
(click)='$event.stopPropagation(); editGroup(group)'
|
||||
@@ -88,12 +88,12 @@ ul.nav-tabs(ngbNav, #nav='ngbNav')
|
||||
.ml-1(class='badge badge-{{getTypeColorClass(profile)}}') {{getTypeLabel(profile)}}
|
||||
|
||||
li(ngbNavItem)
|
||||
a(ngbNavLink) Advanced
|
||||
a(ngbNavLink, translate) Advanced
|
||||
ng-template(ngbNavContent)
|
||||
.form-line(*ngIf='config.store.profiles.length > 0')
|
||||
.header
|
||||
.title Show recent profiles in selector
|
||||
.description Set to 0 to disable recent profiles
|
||||
.title(translate) Show recent profiles in selector
|
||||
.description(translate) Set to 0 to disable recent profiles
|
||||
|
||||
input.form-control(
|
||||
type='number',
|
||||
@@ -105,8 +105,8 @@ ul.nav-tabs(ngbNav, #nav='ngbNav')
|
||||
|
||||
.form-line(*ngIf='config.store.profiles.length > 0')
|
||||
.header
|
||||
.title Show built-in profiles in selector
|
||||
.description If disabled, only custom profiles will show up in the profile selector
|
||||
.title(translate) Show built-in profiles in selector
|
||||
.description(translate) If disabled, only custom profiles will show up in the profile selector
|
||||
|
||||
toggle(
|
||||
[(ngModel)]='config.store.terminal.showBuiltinProfiles',
|
||||
@@ -115,8 +115,8 @@ ul.nav-tabs(ngbNav, #nav='ngbNav')
|
||||
|
||||
.form-line
|
||||
.header
|
||||
.title Default profile settings
|
||||
.description These apply to all profiles of a given type
|
||||
.title(translate) Default profile settings
|
||||
.description(translate) These apply to all profiles of a given type
|
||||
|
||||
.list-group.mt-3.mb-3
|
||||
a.list-group-item.list-group-item-action(
|
||||
|
@@ -3,7 +3,7 @@ import slugify from 'slugify'
|
||||
import deepClone from 'clone-deep'
|
||||
import { Component, HostBinding, Inject } from '@angular/core'
|
||||
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
|
||||
import { ConfigService, HostAppService, Profile, SelectorService, ProfilesService, PromptModalComponent, PlatformService, BaseComponent, PartialProfile, ProfileProvider } from 'tabby-core'
|
||||
import { ConfigService, HostAppService, Profile, SelectorService, ProfilesService, PromptModalComponent, PlatformService, BaseComponent, PartialProfile, ProfileProvider, TranslateService } from 'tabby-core'
|
||||
import { EditProfileModalComponent } from './editProfileModal.component'
|
||||
|
||||
interface ProfileGroup {
|
||||
@@ -35,6 +35,7 @@ export class ProfilesSettingsTabComponent extends BaseComponent {
|
||||
private selector: SelectorService,
|
||||
private ngbModal: NgbModal,
|
||||
private platform: PlatformService,
|
||||
private translate: TranslateService,
|
||||
) {
|
||||
super()
|
||||
this.profileProviders.sort((a, b) => a.name.localeCompare(b.name))
|
||||
@@ -58,7 +59,7 @@ export class ProfilesSettingsTabComponent extends BaseComponent {
|
||||
const profiles = [...this.templateProfiles, ...this.builtinProfiles, ...this.profiles]
|
||||
profiles.sort((a, b) => (a.weight ?? 0) - (b.weight ?? 0))
|
||||
base = await this.selector.show(
|
||||
'Select a base profile to use as a template',
|
||||
this.translate.instant('Select a base profile to use as a template'),
|
||||
profiles.map(p => ({
|
||||
icon: p.icon,
|
||||
description: this.profilesService.getDescription(p) ?? undefined,
|
||||
@@ -72,14 +73,14 @@ export class ProfilesSettingsTabComponent extends BaseComponent {
|
||||
if (base.isTemplate) {
|
||||
profile.name = ''
|
||||
} else if (!base.isBuiltin) {
|
||||
profile.name = `${base.name} copy`
|
||||
profile.name = this.translate.instant('{name} copy', base)
|
||||
}
|
||||
profile.isBuiltin = false
|
||||
profile.isTemplate = false
|
||||
await this.showProfileEditModal(profile)
|
||||
if (!profile.name) {
|
||||
const cfgProxy = this.profilesService.getConfigProxyForProfile(profile)
|
||||
profile.name = this.profilesService.providerForProfile(profile)?.getSuggestedName(cfgProxy) ?? `${base.name} copy`
|
||||
profile.name = this.profilesService.providerForProfile(profile)?.getSuggestedName(cfgProxy) ?? this.translate.instant('{name} copy', base)
|
||||
}
|
||||
profile.id = `${profile.type}:custom:${slugify(profile.name)}:${uuidv4()}`
|
||||
this.config.store.profiles = [profile, ...this.config.store.profiles]
|
||||
@@ -122,8 +123,11 @@ export class ProfilesSettingsTabComponent extends BaseComponent {
|
||||
if ((await this.platform.showMessageBox(
|
||||
{
|
||||
type: 'warning',
|
||||
message: `Delete "${profile.name}"?`,
|
||||
buttons: ['Delete', 'Keep'],
|
||||
message: this.translate.instant('Delete "{name}"?', profile),
|
||||
buttons: [
|
||||
this.translate.instant('Delete'),
|
||||
this.translate.instant('Keep'),
|
||||
],
|
||||
defaultId: 1,
|
||||
cancelId: 1,
|
||||
}
|
||||
@@ -156,7 +160,7 @@ export class ProfilesSettingsTabComponent extends BaseComponent {
|
||||
this.profileGroups.sort((a, b) => a.name?.localeCompare(b.name ?? '') ?? -1)
|
||||
|
||||
this.profileGroups.push({
|
||||
name: 'Built-in',
|
||||
name: this.translate.instant('Built-in'),
|
||||
profiles: this.builtinProfiles,
|
||||
editable: false,
|
||||
collapsed: false,
|
||||
@@ -165,7 +169,7 @@ export class ProfilesSettingsTabComponent extends BaseComponent {
|
||||
|
||||
async editGroup (group: ProfileGroup): Promise<void> {
|
||||
const modal = this.ngbModal.open(PromptModalComponent)
|
||||
modal.componentInstance.prompt = 'New name'
|
||||
modal.componentInstance.prompt = this.translate.instant('New name')
|
||||
modal.componentInstance.value = group.name
|
||||
const result = await modal.result
|
||||
if (result) {
|
||||
@@ -181,8 +185,11 @@ export class ProfilesSettingsTabComponent extends BaseComponent {
|
||||
if ((await this.platform.showMessageBox(
|
||||
{
|
||||
type: 'warning',
|
||||
message: `Delete "${group.name}"?`,
|
||||
buttons: ['Delete', 'Keep'],
|
||||
message: this.translate.instant('Delete "{name}"?', group),
|
||||
buttons: [
|
||||
this.translate.instant('Delete'),
|
||||
this.translate.instant('Keep'),
|
||||
],
|
||||
defaultId: 1,
|
||||
cancelId: 1,
|
||||
}
|
||||
@@ -190,8 +197,11 @@ export class ProfilesSettingsTabComponent extends BaseComponent {
|
||||
if ((await this.platform.showMessageBox(
|
||||
{
|
||||
type: 'warning',
|
||||
message: `Delete the group's profiles?`,
|
||||
buttons: ['Move to "Ungrouped"', 'Delete'],
|
||||
message: this.translate.instant(`Delete the group's profiles?`),
|
||||
buttons: [
|
||||
this.translate.instant('Move to "Ungrouped"'),
|
||||
this.translate.instant('Delete'),
|
||||
],
|
||||
defaultId: 0,
|
||||
cancelId: 0,
|
||||
}
|
||||
@@ -224,10 +234,10 @@ export class ProfilesSettingsTabComponent extends BaseComponent {
|
||||
|
||||
getTypeLabel (profile: PartialProfile<Profile>): string {
|
||||
const name = this.profilesService.providerForProfile(profile)?.name
|
||||
if (name === 'Local') {
|
||||
if (name === this.translate.instant('Local terminal')) {
|
||||
return ''
|
||||
}
|
||||
return name ?? 'Unknown'
|
||||
return name ?? this.translate.instant('Unknown')
|
||||
}
|
||||
|
||||
getTypeColorClass (profile: PartialProfile<Profile>): string {
|
||||
|
@@ -2,7 +2,7 @@
|
||||
import axios from 'axios'
|
||||
import { marked } from 'marked'
|
||||
import { Component } from '@angular/core'
|
||||
import { BaseTabComponent } from 'tabby-core'
|
||||
import { BaseTabComponent, TranslateService } from 'tabby-core'
|
||||
|
||||
export interface Release {
|
||||
name: string
|
||||
@@ -21,9 +21,9 @@ export class ReleaseNotesComponent extends BaseTabComponent {
|
||||
releases: Release[] = []
|
||||
lastPage = 1
|
||||
|
||||
constructor () {
|
||||
constructor (translate: TranslateService) {
|
||||
super()
|
||||
this.setTitle('Release notes')
|
||||
this.setTitle(translate.instant('Release notes'))
|
||||
this.loadReleases(1)
|
||||
}
|
||||
|
||||
|
@@ -1,6 +1,6 @@
|
||||
h3.modal-header.m-0.pb-0 Set master passphrase
|
||||
h3.modal-header.m-0.pb-0(translate) Set master passphrase
|
||||
.modal-body
|
||||
.mb-2 You can change it later, but it's unrecoverable if forgotten.
|
||||
.mb-2(translate) You can change it later, but it's unrecoverable if forgotten.
|
||||
.input-group
|
||||
input.form-control.form-control-lg(
|
||||
[type]='showPassphrase ? "text" : "password"',
|
||||
@@ -16,5 +16,5 @@ h3.modal-header.m-0.pb-0 Set master passphrase
|
||||
i.fas.fa-eye
|
||||
|
||||
.modal-footer
|
||||
button.btn.btn-primary((click)='ok()') Set passphrase
|
||||
button.btn.btn-danger((click)='cancel()') Cancel
|
||||
button.btn.btn-primary((click)='ok()', translate) Set passphrase
|
||||
button.btn.btn-danger((click)='cancel()', translate) Cancel
|
||||
|
@@ -3,7 +3,7 @@
|
||||
li(ngbNavItem='application')
|
||||
a(ngbNavLink)
|
||||
i.fas.fa-fw.fa-window-maximize.mr-2
|
||||
| Application
|
||||
span(translate) Application
|
||||
ng-template(ngbNavContent)
|
||||
.content-box
|
||||
.row
|
||||
@@ -23,59 +23,69 @@
|
||||
i.fas.fa-sync(
|
||||
[class.fa-spin]='checkingForUpdate'
|
||||
)
|
||||
span Check for updates
|
||||
span(translate) Check for updates
|
||||
|
||||
button.btn.btn-info.mt-3.mb-2(
|
||||
*ngIf='updateAvailable',
|
||||
(click)='updater.update()',
|
||||
)
|
||||
i.fas.fa-sync
|
||||
span Update
|
||||
span(translate) Update
|
||||
|
||||
.col-12.col-md-6
|
||||
.list-group.list-group-light.mb-5
|
||||
button.list-group-item.list-group-item-action.link-card((click)='homeBase.reportBug()')
|
||||
i.fas.fa-fw.fa-bug
|
||||
div
|
||||
div Report a problem
|
||||
small.text-muted Generate a pre-filled GitHub issue
|
||||
div(translate) Report a problem
|
||||
small.text-muted(translate) Generate a pre-filled GitHub issue
|
||||
|
||||
button.list-group-item.list-group-item-action.link-card((click)='homeBase.openDiscussions()')
|
||||
i.fas.fa-fw.fa-comments
|
||||
div
|
||||
div Ask a question
|
||||
small.text-muted On GitHub Discussions
|
||||
div(translate) Ask a question
|
||||
small.text-muted(translate) On GitHub Discussions
|
||||
|
||||
button.list-group-item.list-group-item-action.link-card((click)='homeBase.openGitHub()')
|
||||
i.fab.fa-fw.fa-github
|
||||
div
|
||||
div GitHub
|
||||
small.text-muted Source code
|
||||
small.text-muted(translate) Source code
|
||||
|
||||
button.list-group-item.list-group-item-action.link-card((click)='showReleaseNotes()')
|
||||
i.fas.fa-fw.fa-book
|
||||
div
|
||||
div What's new
|
||||
small.text-muted Show release notes
|
||||
div(translate) What's new
|
||||
small.text-muted(translate) Show release notes
|
||||
|
||||
button.list-group-item.list-group-item-action.link-card((click)='homeBase.openTwitter()')
|
||||
i.fab.fa-fw.fa-twitter
|
||||
div
|
||||
div Subscribe to updates
|
||||
small.text-muted Tabby news and updates on Twitter
|
||||
div(translate) Subscribe to updates
|
||||
small.text-muted(translate) Tabby news and updates on Twitter
|
||||
|
||||
|
||||
h3 Application settings
|
||||
h3(translate) Application settings
|
||||
.form-line
|
||||
.header
|
||||
.title(translate) Language
|
||||
select.form-control([(ngModel)]='config.store.language', (ngModelChange)='saveConfiguration()')
|
||||
option([value]='null', translate) Automatic
|
||||
option(
|
||||
[value]='lang.code',
|
||||
*ngFor='let lang of locale.allLanguages'
|
||||
) {{lang.name|translate}}
|
||||
|
||||
.form-line(*ngIf='platform.isShellIntegrationSupported()')
|
||||
.header
|
||||
.title Shell integration
|
||||
.description Allows quickly opening a terminal in the selected folder
|
||||
.title(translate) Shell integration
|
||||
.description(translate) Allows quickly opening a terminal in the selected folder
|
||||
toggle([ngModel]='isShellIntegrationInstalled', (ngModelChange)='toggleShellIntegration()')
|
||||
|
||||
.form-line(*ngIf='hostApp.platform !== Platform.Web')
|
||||
.header
|
||||
.title Enable analytics
|
||||
.description We're only tracking your Tabby and OS versions.
|
||||
.title(translate) Enable analytics
|
||||
.description(translate) We're only tracking your Tabby and OS versions.
|
||||
toggle(
|
||||
[(ngModel)]='config.store.enableAnalytics',
|
||||
(ngModelChange)='saveConfiguration(true)',
|
||||
@@ -83,23 +93,23 @@
|
||||
|
||||
.form-line(*ngIf='hostApp.platform !== Platform.Web')
|
||||
.header
|
||||
.title Automatic Updates
|
||||
.description Enable automatic installation of updates when they become available.
|
||||
.title(translate) Automatic Updates
|
||||
.description(translate) Enable automatic installation of updates when they become available.
|
||||
toggle([(ngModel)]='config.store.enableAutomaticUpdates', (ngModelChange)='saveConfiguration()')
|
||||
|
||||
.form-line(*ngIf='hostApp.platform !== Platform.Web')
|
||||
.header
|
||||
.title Debugging
|
||||
.title(translate) Debugging
|
||||
|
||||
button.btn.btn-secondary((click)='hostWindow.openDevTools()')
|
||||
i.fas.fa-bug
|
||||
span Open DevTools
|
||||
span(translate) Open DevTools
|
||||
|
||||
ng-container(*ngFor='let provider of settingsProviders')
|
||||
li(*ngIf='provider.prioritized', [ngbNavItem]='provider.id')
|
||||
a(ngbNavLink)
|
||||
i(class='fas fa-fw mr-2 fa-{{provider.icon}}')
|
||||
| {{provider.title}}
|
||||
span(translate) {{provider.title}}
|
||||
ng-template(ngbNavContent)
|
||||
settings-tab-body([provider]='provider')
|
||||
|
||||
@@ -109,24 +119,24 @@
|
||||
li(*ngIf='!provider.prioritized', [ngbNavItem]='provider.id')
|
||||
a(ngbNavLink)
|
||||
i(class='fas fa-fw mr-2 fa-{{provider.icon || "puzzle-piece"}}')
|
||||
| {{provider.title}}
|
||||
span(translate) {{provider.title}}
|
||||
ng-template(ngbNavContent)
|
||||
settings-tab-body([provider]='provider')
|
||||
|
||||
li(ngbNavItem='config-file')
|
||||
a(ngbNavLink)
|
||||
i.fas.fa-fw.fa-code.mr-2
|
||||
| Config file
|
||||
span(translate) Config file
|
||||
ng-template.test(ngbNavContent)
|
||||
.d-flex.flex-column.w-100.h-100
|
||||
.h-100.d-flex
|
||||
.w-100.d-flex.flex-column
|
||||
h3 Config file
|
||||
h3(translate) Config file
|
||||
textarea.form-control.h-100(
|
||||
[(ngModel)]='configFile'
|
||||
)
|
||||
.w-100.d-flex.flex-column(*ngIf='showConfigDefaults')
|
||||
h3 Defaults
|
||||
h3(translate) Defaults
|
||||
textarea.form-control.h-100(
|
||||
[(ngModel)]='configDefaults',
|
||||
readonly
|
||||
@@ -134,20 +144,25 @@
|
||||
.mt-3.d-flex
|
||||
button.btn.btn-primary((click)='saveConfigFile()', *ngIf='isConfigFileValid()')
|
||||
i.fas.fa-check.mr-2
|
||||
| Save and apply
|
||||
span(translate) Save and apply
|
||||
button.btn.btn-primary(disabled, *ngIf='!isConfigFileValid()')
|
||||
i.fas.fa-exclamation-triangle.mr-2
|
||||
| Invalid syntax
|
||||
span(translate) Invalid syntax
|
||||
button.btn.btn-secondary.ml-auto(
|
||||
(click)='showConfigDefaults = !showConfigDefaults'
|
||||
(click)='showConfigDefaults = !showConfigDefaults',
|
||||
translate
|
||||
) Show defaults
|
||||
button.btn.btn-secondary.ml-3(
|
||||
*ngIf='platform.getConfigPath()',
|
||||
(click)='showConfigFile()'
|
||||
)
|
||||
i.fas.fa-external-link-square-alt.mr-2
|
||||
| Show config file
|
||||
span(translate) Show config file
|
||||
|
||||
div([ngbNavOutlet]='nav')
|
||||
|
||||
button.btn.btn-warning.btn-block(*ngIf='config.restartRequested', '(click)'='restartApp()') Restart the app to apply changes
|
||||
button.btn.btn-warning.btn-block(
|
||||
*ngIf='config.restartRequested',
|
||||
(click)='restartApp()',
|
||||
translate
|
||||
) Restart the app to apply changes
|
||||
|
@@ -12,6 +12,8 @@ import {
|
||||
PlatformService,
|
||||
HostWindowService,
|
||||
AppService,
|
||||
LocaleService,
|
||||
TranslateService,
|
||||
} from 'tabby-core'
|
||||
|
||||
import { SettingsTabProvider } from '../api'
|
||||
@@ -43,12 +45,14 @@ export class SettingsTabComponent extends BaseTabComponent {
|
||||
public homeBase: HomeBaseService,
|
||||
public platform: PlatformService,
|
||||
public zone: NgZone,
|
||||
public locale: LocaleService,
|
||||
private updater: UpdaterService,
|
||||
private app: AppService,
|
||||
@Inject(SettingsTabProvider) public settingsProviders: SettingsTabProvider[],
|
||||
translate: TranslateService,
|
||||
) {
|
||||
super()
|
||||
this.setTitle('Settings')
|
||||
this.setTitle(translate.instant('Settings'))
|
||||
this.settingsProviders = config.enabledServices(this.settingsProviders)
|
||||
this.settingsProviders = this.settingsProviders.filter(x => !!x.getComponentType())
|
||||
this.settingsProviders.sort((a, b) => a.weight - b.weight + a.title.localeCompare(b.title))
|
||||
|
@@ -1,27 +1,27 @@
|
||||
.text-center(*ngIf='!vault.isEnabled()')
|
||||
i.fas.fa-key.fa-3x.m-3
|
||||
h3.m-3 Vault is not configured
|
||||
.m-3 Vault is an always-encrypted container for secrets such as SSH passwords and private key passphrases.
|
||||
button.btn.btn-primary.m-2((click)='enableVault()') Set master passphrase
|
||||
h3.m-3(translate) Vault is not configured
|
||||
.m-3(translate) Vault is an always-encrypted container for secrets such as SSH passwords and private key passphrases.
|
||||
button.btn.btn-primary.m-2((click)='enableVault()', translate) Set master passphrase
|
||||
|
||||
|
||||
div(*ngIf='vault.isEnabled()')
|
||||
.d-flex.align-items-center.mb-3
|
||||
h3.m-0 Vault
|
||||
h3.m-0(translate) Vault
|
||||
.d-flex.ml-auto(ngbDropdown, *ngIf='vault.isEnabled()')
|
||||
button.btn.btn-secondary(ngbDropdownToggle) Options
|
||||
button.btn.btn-secondary(ngbDropdownToggle, translate) Options
|
||||
div(ngbDropdownMenu)
|
||||
a(ngbDropdownItem, (click)='changePassphrase()')
|
||||
i.fas.fa-fw.fa-key
|
||||
span Change the master passphrase
|
||||
span(translate) Change the master passphrase
|
||||
a(ngbDropdownItem, (click)='disableVault()')
|
||||
i.fas.fa-fw.fa-radiation-alt
|
||||
span Erase the vault
|
||||
span(translate) Erase the Vault
|
||||
|
||||
div(*ngIf='vaultContents')
|
||||
.text-center(*ngIf='!vaultContents.secrets.length')
|
||||
i.fas.fa-empty-set.fa-3x
|
||||
h3.m-3 Vault is empty
|
||||
h3.m-3(translate) Vault is empty
|
||||
|
||||
.list-group
|
||||
.list-group-item.d-flex.align-items-center.p-1.pl-3(*ngFor='let secret of vaultContents.secrets')
|
||||
@@ -38,30 +38,30 @@ div(*ngIf='vault.isEnabled()')
|
||||
(click)='renameFile(secret)'
|
||||
)
|
||||
i.fas.fa-fw.fa-pencil-alt
|
||||
span Rename
|
||||
span(translate) Rename
|
||||
button(
|
||||
ngbDropdownItem,
|
||||
*ngIf='secret.type === VAULT_SECRET_TYPE_FILE',
|
||||
(click)='replaceFileContent(secret)'
|
||||
)
|
||||
i.fas.fa-fw.fa-file-import
|
||||
span Replace
|
||||
span(translate) Replace
|
||||
button(
|
||||
ngbDropdownItem,
|
||||
*ngIf='secret.type === VAULT_SECRET_TYPE_FILE',
|
||||
(click)='exportFile(secret)'
|
||||
)
|
||||
i.fas.fa-fw.fa-file-export
|
||||
span Export
|
||||
span(translate) Export
|
||||
button(ngbDropdownItem, (click)='removeSecret(secret)')
|
||||
i.fas.fa-fw.fa-trash
|
||||
span Delete
|
||||
span(translate) Delete
|
||||
|
||||
h3.mt-5 Options
|
||||
h3.mt-5(translate) Options
|
||||
.form-line
|
||||
.header
|
||||
.title Encrypt config file
|
||||
.description Puts all of Tabby's configuration into the vault
|
||||
.title(translate) Encrypt config file
|
||||
.description(translate) Puts all of Tabby's configuration into the vault
|
||||
toggle(
|
||||
[ngModel]='config.store.encrypted',
|
||||
(click)='toggleConfigEncrypted()',
|
||||
@@ -69,5 +69,5 @@ div(*ngIf='vault.isEnabled()')
|
||||
|
||||
.text-center(*ngIf='!vaultContents')
|
||||
i.fas.fa-key.fa-3x
|
||||
h3.m-3 Vault is locked
|
||||
button.btn.btn-primary.m-2((click)='loadVault()') Show vault contents
|
||||
h3.m-3(translate) Vault is locked
|
||||
button.btn.btn-primary.m-2((click)='loadVault()', translate) Show vault contents
|
||||
|
@@ -1,7 +1,7 @@
|
||||
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
|
||||
import { Component, HostBinding } from '@angular/core'
|
||||
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
|
||||
import { BaseComponent, VaultService, VaultSecret, Vault, PlatformService, ConfigService, VAULT_SECRET_TYPE_FILE, PromptModalComponent, VaultFileSecret } from 'tabby-core'
|
||||
import { BaseComponent, VaultService, VaultSecret, Vault, PlatformService, ConfigService, VAULT_SECRET_TYPE_FILE, PromptModalComponent, VaultFileSecret, TranslateService } from 'tabby-core'
|
||||
import { SetVaultPassphraseModalComponent } from './setVaultPassphraseModal.component'
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@ export class VaultSettingsTabComponent extends BaseComponent {
|
||||
public config: ConfigService,
|
||||
private platform: PlatformService,
|
||||
private ngbModal: NgbModal,
|
||||
private translate: TranslateService,
|
||||
) {
|
||||
super()
|
||||
if (vault.isOpen()) {
|
||||
@@ -43,8 +44,11 @@ export class VaultSettingsTabComponent extends BaseComponent {
|
||||
if ((await this.platform.showMessageBox(
|
||||
{
|
||||
type: 'warning',
|
||||
message: 'Delete vault contents?',
|
||||
buttons: ['Delete', 'Keep'],
|
||||
message: this.translate.instant('Delete vault contents?'),
|
||||
buttons: [
|
||||
this.translate.instant('Delete'),
|
||||
this.translate.instant('Keep'),
|
||||
],
|
||||
defaultId: 1,
|
||||
cancelId: 1,
|
||||
}
|
||||
@@ -77,16 +81,16 @@ export class VaultSettingsTabComponent extends BaseComponent {
|
||||
|
||||
getSecretLabel (secret: VaultSecret) {
|
||||
if (secret.type === 'ssh:password') {
|
||||
return `SSH password for ${(secret as any).key.user}@${(secret as any).key.host}:${(secret as any).key.port}`
|
||||
return this.translate.instant('SSH password for {user}@{host}:{port}', (secret as any).key)
|
||||
}
|
||||
if (secret.type === 'ssh:key-passphrase') {
|
||||
return `Passphrase for a private key with hash ${(secret as any).key.hash.substring(0, 8)}...`
|
||||
return this.translate.instant('Passphrase for a private key with hash {hash}...', { hash: (secret as any).key.hash.substring(0, 8) })
|
||||
}
|
||||
if (secret.type === VAULT_SECRET_TYPE_FILE) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
|
||||
return `File: ${(secret as VaultFileSecret).key.description}`
|
||||
return this.translate.instant('File: {description}', (secret as VaultFileSecret).key)
|
||||
}
|
||||
return `Unknown secret of type ${secret.type} for ${JSON.stringify(secret.key)}`
|
||||
return this.translate.instant('Unknown secret of type {type} for {key}', { type: secret.type, key: JSON.stringify(secret.key) })
|
||||
}
|
||||
|
||||
removeSecret (secret: VaultSecret) {
|
||||
@@ -111,7 +115,7 @@ export class VaultSettingsTabComponent extends BaseComponent {
|
||||
|
||||
async renameFile (secret: VaultFileSecret) {
|
||||
const modal = this.ngbModal.open(PromptModalComponent)
|
||||
modal.componentInstance.prompt = 'New name'
|
||||
modal.componentInstance.prompt = this.translate.instant('New name')
|
||||
modal.componentInstance.value = secret.key.description
|
||||
|
||||
const description = (await modal.result)?.value
|
||||
|
@@ -1,8 +1,8 @@
|
||||
h3.mb-3 Window
|
||||
h3.mb-3(translate) Window
|
||||
|
||||
.form-line
|
||||
.header
|
||||
.title Theme
|
||||
.title(translate) Theme
|
||||
select.form-control(
|
||||
[(ngModel)]='config.store.appearance.theme',
|
||||
(ngModelChange)='saveConfiguration()',
|
||||
@@ -12,8 +12,8 @@ h3.mb-3 Window
|
||||
|
||||
.form-line(*ngIf='hostApp.platform === Platform.Web')
|
||||
.header
|
||||
.title Ask before closing the browser tab
|
||||
.description Prevents accidental closing
|
||||
.title(translate) Ask before closing the browser tab
|
||||
.description(translate) Prevents accidental closing
|
||||
toggle(
|
||||
[(ngModel)]='config.store.web.preventAccidentalTabClosure',
|
||||
(ngModelChange)='saveConfiguration()',
|
||||
@@ -22,9 +22,9 @@ h3.mb-3 Window
|
||||
|
||||
.form-line(*ngIf='platform.supportsWindowControls')
|
||||
.header
|
||||
.title(*ngIf='hostApp.platform !== Platform.macOS') Acrylic background
|
||||
.title(*ngIf='hostApp.platform === Platform.macOS') Vibrancy
|
||||
.description Gives the window a blurred transparent background
|
||||
.title(*ngIf='hostApp.platform !== Platform.macOS', translate) Acrylic background
|
||||
.title(*ngIf='hostApp.platform === Platform.macOS', translate) Vibrancy
|
||||
.description(translate) Gives the window a blurred transparent background
|
||||
|
||||
toggle(
|
||||
[(ngModel)]='config.store.appearance.vibrancy',
|
||||
@@ -33,7 +33,7 @@ h3.mb-3 Window
|
||||
|
||||
.form-line(*ngIf='config.store.appearance.vibrancy && isFluentVibrancySupported')
|
||||
.header
|
||||
.title Background type
|
||||
.title(translate) Background type
|
||||
.btn-group(
|
||||
[(ngModel)]='config.store.appearance.vibrancyType',
|
||||
(ngModelChange)='saveConfiguration()',
|
||||
@@ -45,18 +45,18 @@ h3.mb-3 Window
|
||||
ngbButton,
|
||||
[value]='"blur"'
|
||||
)
|
||||
| Blur
|
||||
span(translate) Blur
|
||||
label.btn.btn-secondary(ngbButtonLabel)
|
||||
input(
|
||||
type='radio',
|
||||
ngbButton,
|
||||
[value]='"fluent"'
|
||||
)
|
||||
| Fluent
|
||||
span Fluent
|
||||
|
||||
.form-line(*ngIf='platform.supportsWindowControls')
|
||||
.header
|
||||
.title Opacity
|
||||
.title(translate) Opacity
|
||||
input(
|
||||
type='range',
|
||||
[(ngModel)]='config.store.appearance.opacity',
|
||||
@@ -68,8 +68,8 @@ h3.mb-3 Window
|
||||
|
||||
.form-line(*ngIf='platform.supportsWindowControls')
|
||||
.header
|
||||
.title Window frame
|
||||
.description Whether a custom window or an OS native window should be used
|
||||
.title(translate) Window frame
|
||||
.description(translate) Whether a custom window or an OS native window should be used
|
||||
|
||||
.btn-group(
|
||||
[(ngModel)]='config.store.appearance.frame',
|
||||
@@ -82,28 +82,28 @@ h3.mb-3 Window
|
||||
ngbButton,
|
||||
[value]='"native"'
|
||||
)
|
||||
| Native
|
||||
span(translate) Native
|
||||
label.btn.btn-secondary(ngbButtonLabel)
|
||||
input(
|
||||
type='radio',
|
||||
ngbButton,
|
||||
[value]='"thin"'
|
||||
)
|
||||
| Thin
|
||||
span(translate) Thin
|
||||
label.btn.btn-secondary(ngbButtonLabel)
|
||||
input(
|
||||
type='radio',
|
||||
ngbButton,
|
||||
[value]='"full"'
|
||||
)
|
||||
| Full
|
||||
span(translate) Full
|
||||
|
||||
h3.mt-4 Docking
|
||||
h3.mt-4(translate) Docking
|
||||
|
||||
.form-line(*ngIf='docking')
|
||||
.header
|
||||
.title Dock the terminal
|
||||
.description Snaps the window to a side of the screen
|
||||
.title(translate) Dock the terminal
|
||||
.description(translate) Snaps the window to a side of the screen
|
||||
|
||||
.btn-group(
|
||||
[(ngModel)]='config.store.appearance.dock',
|
||||
@@ -116,40 +116,40 @@ h3.mt-4 Docking
|
||||
ngbButton,
|
||||
[value]='"off"'
|
||||
)
|
||||
| Off
|
||||
span(translate) Off
|
||||
label.btn.btn-secondary(ngbButtonLabel)
|
||||
input(
|
||||
type='radio',
|
||||
ngbButton,
|
||||
[value]='"top"'
|
||||
)
|
||||
| Top
|
||||
span(translate) Top
|
||||
label.btn.btn-secondary(ngbButtonLabel)
|
||||
input(
|
||||
type='radio',
|
||||
ngbButton,
|
||||
[value]='"left"'
|
||||
)
|
||||
| Left
|
||||
span(translate) Left
|
||||
label.btn.btn-secondary(ngbButtonLabel)
|
||||
input(
|
||||
type='radio',
|
||||
ngbButton,
|
||||
[value]='"right"'
|
||||
)
|
||||
| Right
|
||||
span(translate) Right
|
||||
label.btn.btn-secondary(ngbButtonLabel)
|
||||
input(
|
||||
type='radio',
|
||||
ngbButton,
|
||||
[value]='"bottom"'
|
||||
)
|
||||
| Bottom
|
||||
span(translate) Bottom
|
||||
|
||||
.ml-5.form-line(*ngIf='docking && config.store.appearance.dock != "off"')
|
||||
.header
|
||||
.title Display on
|
||||
.description Snaps the window to a side of the screen
|
||||
.title(translate) Display on
|
||||
.description(translate) Snaps the window to a side of the screen
|
||||
|
||||
div(
|
||||
[(ngModel)]='config.store.appearance.dockScreen',
|
||||
@@ -162,7 +162,7 @@ h3.mt-4 Docking
|
||||
ngbButton,
|
||||
value='current'
|
||||
)
|
||||
| Current
|
||||
span(translate) Current
|
||||
label.btn.btn-secondary(*ngFor='let screen of screens', ngbButtonLabel)
|
||||
input(
|
||||
type='radio',
|
||||
@@ -173,8 +173,8 @@ h3.mt-4 Docking
|
||||
|
||||
.ml-5.form-line(*ngIf='docking && config.store.appearance.dock != "off"')
|
||||
.header
|
||||
.title Dock always on top
|
||||
.description Keep docked terminal always on top
|
||||
.title(translate) Dock always on top
|
||||
.description(translate) Keep docked terminal always on top
|
||||
toggle(
|
||||
[(ngModel)]='config.store.appearance.dockAlwaysOnTop',
|
||||
(ngModelChange)='saveConfiguration(); docking.dock()',
|
||||
@@ -182,7 +182,7 @@ h3.mt-4 Docking
|
||||
|
||||
.ml-5.form-line(*ngIf='docking && config.store.appearance.dock != "off"')
|
||||
.header
|
||||
.title Docked terminal size
|
||||
.title(translate) Docked terminal size
|
||||
input(
|
||||
type='range',
|
||||
[(ngModel)]='config.store.appearance.dockFill',
|
||||
@@ -194,7 +194,7 @@ h3.mt-4 Docking
|
||||
|
||||
.ml-5.form-line(*ngIf='docking && config.store.appearance.dock != "off"')
|
||||
.header
|
||||
.title Docked terminal space
|
||||
.title(translate) Docked terminal space
|
||||
input(
|
||||
type='range',
|
||||
[(ngModel)]='config.store.appearance.dockSpace',
|
||||
@@ -206,18 +206,18 @@ h3.mt-4 Docking
|
||||
|
||||
.ml-5.form-line(*ngIf='docking && config.store.appearance.dock != "off"')
|
||||
.header
|
||||
.title Hide dock on blur
|
||||
.description Hides the docked terminal when you click away.
|
||||
.title(translate) Hide dock on blur
|
||||
.description(translate) Hides the docked terminal when you click away.
|
||||
toggle(
|
||||
[(ngModel)]='config.store.appearance.dockHideOnBlur',
|
||||
(ngModelChange)='saveConfiguration(); ',
|
||||
)
|
||||
|
||||
h3.mt-4 Tabs
|
||||
h3.mt-4(translate) Tabs
|
||||
|
||||
.form-line
|
||||
.header
|
||||
.title Tabs location
|
||||
.title(translate) Tabs location
|
||||
.btn-group(
|
||||
[(ngModel)]='config.store.appearance.tabsLocation',
|
||||
(ngModelChange)='saveConfiguration()',
|
||||
@@ -229,32 +229,32 @@ h3.mt-4 Tabs
|
||||
ngbButton,
|
||||
[value]='"top"'
|
||||
)
|
||||
| Top
|
||||
span(translate) Top
|
||||
label.btn.btn-secondary(ngbButtonLabel)
|
||||
input(
|
||||
type='radio',
|
||||
ngbButton,
|
||||
[value]='"bottom"'
|
||||
)
|
||||
| Bottom
|
||||
span(translate) Bottom
|
||||
label.btn.btn-secondary(ngbButtonLabel)
|
||||
input(
|
||||
type='radio',
|
||||
ngbButton,
|
||||
[value]='"left"'
|
||||
)
|
||||
| Left
|
||||
span(translate) Left
|
||||
label.btn.btn-secondary(ngbButtonLabel)
|
||||
input(
|
||||
type='radio',
|
||||
ngbButton,
|
||||
[value]='"right"'
|
||||
)
|
||||
| Right
|
||||
span(translate) Right
|
||||
|
||||
.form-line
|
||||
.header
|
||||
.title Tabs width
|
||||
.title(translate) Tabs width
|
||||
.btn-group(
|
||||
[(ngModel)]='config.store.appearance.flexTabs',
|
||||
(ngModelChange)='saveConfiguration()',
|
||||
@@ -266,18 +266,18 @@ h3.mt-4 Tabs
|
||||
ngbButton,
|
||||
[value]='true'
|
||||
)
|
||||
| Dynamic
|
||||
span(translate) Dynamic
|
||||
label.btn.btn-secondary(ngbButtonLabel)
|
||||
input(
|
||||
type='radio',
|
||||
ngbButton,
|
||||
[value]='false'
|
||||
)
|
||||
| Fixed
|
||||
span(translate) Fixed
|
||||
|
||||
.form-line
|
||||
.header
|
||||
.title Hide tab index
|
||||
.title(translate) Hide tab index
|
||||
|
||||
toggle(
|
||||
[(ngModel)]='config.store.terminal.hideTabIndex',
|
||||
@@ -286,7 +286,7 @@ h3.mt-4 Tabs
|
||||
|
||||
.form-line
|
||||
.header
|
||||
.title Hide tab close button
|
||||
.title(translate) Hide tab close button
|
||||
|
||||
toggle(
|
||||
[(ngModel)]='config.store.terminal.hideCloseButton',
|
||||
@@ -297,8 +297,8 @@ h3.mt-4 Hacks
|
||||
|
||||
.form-line
|
||||
.header
|
||||
.title Disable GPU acceleration
|
||||
.description Tick this if you're experiencing aliasing, ghosting or other visual issues
|
||||
.title(translate) Disable GPU acceleration
|
||||
.description(translate) Tick this if you're experiencing aliasing, ghosting or other visual issues
|
||||
|
||||
toggle(
|
||||
[(ngModel)]='config.store.hacks.disableGPU',
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import { Injectable } from '@angular/core'
|
||||
import { HotkeyDescription, HotkeyProvider } from 'tabby-core'
|
||||
import { HotkeyDescription, HotkeyProvider, TranslateService } from 'tabby-core'
|
||||
|
||||
/** @hidden */
|
||||
@Injectable()
|
||||
@@ -7,10 +7,12 @@ export class SettingsHotkeyProvider extends HotkeyProvider {
|
||||
hotkeys: HotkeyDescription[] = [
|
||||
{
|
||||
id: 'settings',
|
||||
name: 'Open Settings',
|
||||
name: this.translate.instant('Open Settings'),
|
||||
},
|
||||
]
|
||||
|
||||
constructor (private translate: TranslateService) { super() }
|
||||
|
||||
async provide (): Promise<HotkeyDescription[]> {
|
||||
return this.hotkeys
|
||||
}
|
||||
|
@@ -5,13 +5,16 @@ import { WindowSettingsTabComponent } from './components/windowSettingsTab.compo
|
||||
import { VaultSettingsTabComponent } from './components/vaultSettingsTab.component'
|
||||
import { ConfigSyncSettingsTabComponent } from './components/configSyncSettingsTab.component'
|
||||
import { ProfilesSettingsTabComponent } from './components/profilesSettingsTab.component'
|
||||
import { TranslateService } from 'tabby-core'
|
||||
|
||||
/** @hidden */
|
||||
@Injectable()
|
||||
export class HotkeySettingsTabProvider extends SettingsTabProvider {
|
||||
id = 'hotkeys'
|
||||
icon = 'keyboard'
|
||||
title = 'Hotkeys'
|
||||
title = this.translate.instant('Hotkeys')
|
||||
|
||||
constructor (private translate: TranslateService) { super() }
|
||||
|
||||
getComponentType (): any {
|
||||
return HotkeySettingsTabComponent
|
||||
@@ -24,7 +27,9 @@ export class HotkeySettingsTabProvider extends SettingsTabProvider {
|
||||
export class WindowSettingsTabProvider extends SettingsTabProvider {
|
||||
id = 'window'
|
||||
icon = 'window-maximize'
|
||||
title = 'Window'
|
||||
title = this.translate.instant('Window')
|
||||
|
||||
constructor (private translate: TranslateService) { super() }
|
||||
|
||||
getComponentType (): any {
|
||||
return WindowSettingsTabComponent
|
||||
@@ -50,9 +55,11 @@ export class VaultSettingsTabProvider extends SettingsTabProvider {
|
||||
export class ProfilesSettingsTabProvider extends SettingsTabProvider {
|
||||
id = 'profiles'
|
||||
icon = 'window-restore'
|
||||
title = 'Profiles & connections'
|
||||
title = this.translate.instant('Profiles & connections')
|
||||
prioritized = true
|
||||
|
||||
constructor (private translate: TranslateService) { super() }
|
||||
|
||||
getComponentType (): any {
|
||||
return ProfilesSettingsTabComponent
|
||||
}
|
||||
@@ -63,7 +70,9 @@ export class ProfilesSettingsTabProvider extends SettingsTabProvider {
|
||||
export class ConfigSyncSettingsTabProvider extends SettingsTabProvider {
|
||||
id = 'config-sync'
|
||||
icon = 'cloud'
|
||||
title = 'Config sync'
|
||||
title = this.translate.instant('Config sync')
|
||||
|
||||
constructor (private translate: TranslateService) { super() }
|
||||
|
||||
getComponentType (): any {
|
||||
return ConfigSyncSettingsTabComponent
|
||||
|
Reference in New Issue
Block a user