mirror of
https://github.com/Eugeny/tabby.git
synced 2025-10-05 14:34:54 +00:00
Translation infrastructure
This commit is contained in:
@@ -17,12 +17,15 @@
|
||||
"author": "Eugene Pankov",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"@ngx-translate/core": "^14.0.0",
|
||||
"bootstrap": "^4.1.3",
|
||||
"deepmerge": "^4.1.1",
|
||||
"js-yaml": "^4.0.0",
|
||||
"messageformat": "^2.3.0",
|
||||
"mixpanel": "^0.13.0",
|
||||
"ngx-filesize": "^2.0.16",
|
||||
"ngx-perfect-scrollbar": "^10.1.0",
|
||||
"ngx-translate-messageformat-compiler": "^4.11.0",
|
||||
"readable-stream": "3.6.0",
|
||||
"uuid": "^8.0.0"
|
||||
},
|
||||
|
@@ -35,4 +35,5 @@ export { TabsService, NewTabParameters, TabComponentType } from '../services/tab
|
||||
export { UpdaterService } from '../services/updater.service'
|
||||
export { VaultService, Vault, VaultSecret, VaultFileSecret, VAULT_SECRET_TYPE_FILE, StoredVault, VaultSecretKey } from '../services/vault.service'
|
||||
export { FileProvidersService } from '../services/fileProviders.service'
|
||||
export { LocaleService, TranslateServiceWrapper as TranslateService } from '../services/locale.service'
|
||||
export * from '../utils'
|
||||
|
@@ -1,5 +1,6 @@
|
||||
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
|
||||
import { Injectable } from '@angular/core'
|
||||
import { TranslateService } from '@ngx-translate/core'
|
||||
|
||||
import { ToolbarButton, ToolbarButtonProvider } from './api/toolbarButtonProvider'
|
||||
import { HostAppService, Platform } from './api/hostApp'
|
||||
@@ -12,6 +13,7 @@ export class ButtonProvider extends ToolbarButtonProvider {
|
||||
constructor (
|
||||
private hostApp: HostAppService,
|
||||
private profilesService: ProfilesService,
|
||||
private translate: TranslateService,
|
||||
hotkeys: HotkeysService,
|
||||
) {
|
||||
super()
|
||||
@@ -35,7 +37,7 @@ export class ButtonProvider extends ToolbarButtonProvider {
|
||||
icon: this.hostApp.platform === Platform.Web
|
||||
? require('./icons/plus.svg')
|
||||
: require('./icons/profiles.svg'),
|
||||
title: 'Profiles and connections',
|
||||
title: this.translate.instant('Profiles and connections'),
|
||||
click: () => this.activate(),
|
||||
},
|
||||
...this.profilesService.getRecentProfiles().map(profile => ({
|
||||
|
@@ -2,5 +2,5 @@
|
||||
input.form-control(type='text', #input, [(ngModel)]='value', (keyup.enter)='save()', autofocus)
|
||||
|
||||
.modal-footer
|
||||
button.btn.btn-primary((click)='save()') Save
|
||||
button.btn.btn-secondary((click)='close()') Cancel
|
||||
button.btn.btn-primary((click)='save()', translate) Save
|
||||
button.btn.btn-secondary((click)='close()', translate) Cancel
|
||||
|
@@ -1,7 +1,7 @@
|
||||
.modal-body
|
||||
.alert.alert-danger Tabby could not start with your plugins, so all third party plugins have been disabled in this session. The error was:
|
||||
.alert.alert-danger(translate) Tabby could not start with your plugins, so all third party plugins have been disabled in this session. The error was:
|
||||
|
||||
pre {{error}}
|
||||
|
||||
.modal-footer
|
||||
button.btn.btn-primary((click)='close()') Close
|
||||
button.btn.btn-primary((click)='close()', translate) Close
|
||||
|
@@ -15,9 +15,9 @@ footer.d-flex.align-items-center
|
||||
.btn-group.mr-auto
|
||||
button.btn.btn-dark((click)='homeBase.openGitHub()')
|
||||
i.fab.fa-github
|
||||
span GitHub
|
||||
span(translate) GitHub
|
||||
button.btn.btn-dark((click)='homeBase.reportBug()')
|
||||
i.fas.fa-bug
|
||||
span Report a problem
|
||||
span(translate) Report a problem
|
||||
|
||||
.form-control-static.selectable.no-drag Version: {{homeBase.appVersion}}
|
||||
.form-control-static.selectable.no-drag(translate, [translateParams]='{version: homeBase.appVersion}') Version: {version}
|
||||
|
@@ -1,5 +1,5 @@
|
||||
.d-flex.align-items-center
|
||||
.dropdown-header File transfers
|
||||
.dropdown-header(translate) File transfers
|
||||
button.btn.btn-link.ml-auto((click)='removeAll(); $event.stopPropagation()') !{require('../icons/times.svg')}
|
||||
.transfer(*ngFor='let transfer of transfers', (click)='showTransfer(transfer)')
|
||||
.icon(*ngIf='isDownload(transfer)') !{require('../icons/download.svg')}
|
||||
|
@@ -1,4 +1,5 @@
|
||||
import { Component, Input, Output, EventEmitter } from '@angular/core'
|
||||
import { TranslateService } from '@ngx-translate/core'
|
||||
import { FileDownload, FileTransfer, PlatformService } from '../api/platform'
|
||||
|
||||
/** @hidden */
|
||||
@@ -11,7 +12,10 @@ export class TransfersMenuComponent {
|
||||
@Input() transfers: FileTransfer[]
|
||||
@Output() transfersChange = new EventEmitter<FileTransfer[]>()
|
||||
|
||||
constructor (private platform: PlatformService) { }
|
||||
constructor (
|
||||
private platform: PlatformService,
|
||||
private translate: TranslateService,
|
||||
) { }
|
||||
|
||||
isDownload (transfer: FileTransfer): boolean {
|
||||
return transfer instanceof FileDownload
|
||||
@@ -40,8 +44,11 @@ export class TransfersMenuComponent {
|
||||
if (this.transfers.some(x => !x.isComplete())) {
|
||||
if ((await this.platform.showMessageBox({
|
||||
type: 'warning',
|
||||
message: 'There are active file transfers',
|
||||
buttons: ['Abort all', 'Do not abort'],
|
||||
message: this.translate.instant('There are active file transfers'),
|
||||
buttons: [
|
||||
this.translate.instant('Abort all'),
|
||||
this.translate.instant('Do not abort'),
|
||||
],
|
||||
defaultId: 1,
|
||||
cancelId: 1,
|
||||
})).response === 1) {
|
||||
|
@@ -1,13 +1,18 @@
|
||||
.modal-body
|
||||
.d-flex.align-items-center.mb-3
|
||||
h3.m-0 Vault is locked
|
||||
h3.m-0(translate) Vault is locked
|
||||
.ml-auto(ngbDropdown, placement='bottom-right')
|
||||
button.btn.btn-link(ngbDropdownToggle, (click)='$event.stopPropagation()')
|
||||
span(*ngIf='rememberFor') Remember for {{getRememberForDisplay(rememberFor)}}
|
||||
span(*ngIf='!rememberFor') Do not remember
|
||||
span(
|
||||
*ngIf='rememberFor',
|
||||
translate,
|
||||
[translateParams]='{time: getRememberForDisplay(rememberFor)}'
|
||||
) Remember for {time}
|
||||
span(*ngIf='!rememberFor', translate) Do not remember
|
||||
div(ngbDropdownMenu)
|
||||
button.dropdown-item(
|
||||
(click)='rememberFor = 0',
|
||||
translate
|
||||
) Do not remember
|
||||
button.dropdown-item(
|
||||
*ngFor='let x of rememberOptions',
|
||||
|
@@ -4,21 +4,21 @@
|
||||
h1.tabby-title Tabby
|
||||
sup α
|
||||
|
||||
.text-center.mb-5 Thank you for downloading Tabby!
|
||||
.text-center.mb-5(translate) Thank you for downloading Tabby!
|
||||
|
||||
.form-line
|
||||
.header
|
||||
.title Enable analytics
|
||||
.description Help track the number of Tabby installs across the world!
|
||||
.title(translate) Enable analytics
|
||||
.description(translate) Help track the number of Tabby installs across the world!
|
||||
toggle([(ngModel)]='config.store.enableAnalytics')
|
||||
|
||||
|
||||
.form-line
|
||||
.header
|
||||
.title Enable global hotkey (#[strong Ctrl-Space])
|
||||
.description Toggles the Tabby window visibility
|
||||
.title(translate) Enable global hotkey (Ctrl-Space)
|
||||
.description(translate) Toggles the Tabby window visibility
|
||||
toggle([(ngModel)]='enableGlobalHotkey')
|
||||
|
||||
|
||||
.text-center.mt-5
|
||||
button.btn.btn-primary((click)='closeAndDisable()') Close and never show again
|
||||
button.btn.btn-primary((click)='closeAndDisable()', translate) Close and never show again
|
||||
|
@@ -1,5 +1,6 @@
|
||||
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
|
||||
import { Component } from '@angular/core'
|
||||
import { TranslateService } from '@ngx-translate/core'
|
||||
import { BaseTabComponent } from './baseTab.component'
|
||||
import { ConfigService } from '../services/config.service'
|
||||
import { HostWindowService } from '../api/hostWindow'
|
||||
@@ -16,9 +17,10 @@ export class WelcomeTabComponent extends BaseTabComponent {
|
||||
constructor (
|
||||
private hostWindow: HostWindowService,
|
||||
public config: ConfigService,
|
||||
translate: TranslateService,
|
||||
) {
|
||||
super()
|
||||
this.setTitle('Welcome')
|
||||
this.setTitle(translate.instant('Welcome'))
|
||||
}
|
||||
|
||||
async closeAndDisable () {
|
||||
|
@@ -38,3 +38,4 @@ enableExperimentalFeatures: false
|
||||
pluginBlacklist: []
|
||||
hacks:
|
||||
disableGPU: false
|
||||
language: null
|
||||
|
@@ -1,4 +1,5 @@
|
||||
import { Injectable } from '@angular/core'
|
||||
import { TranslateService } from '@ngx-translate/core'
|
||||
import { ProfilesService } from './services/profiles.service'
|
||||
import { HotkeyDescription, HotkeyProvider } from './api/hotkeyProvider'
|
||||
import { PartialProfile, Profile } from './api'
|
||||
@@ -9,188 +10,189 @@ export class AppHotkeyProvider extends HotkeyProvider {
|
||||
hotkeys: HotkeyDescription[] = [
|
||||
{
|
||||
id: 'profile-selector',
|
||||
name: 'Show profile selector',
|
||||
name: this.translate.instant('Show profile selector'),
|
||||
},
|
||||
{
|
||||
id: 'toggle-fullscreen',
|
||||
name: 'Toggle fullscreen mode',
|
||||
name: this.translate.instant('Toggle fullscreen mode'),
|
||||
},
|
||||
{
|
||||
id: 'rename-tab',
|
||||
name: 'Rename Tab',
|
||||
name: this.translate.instant('Rename Tab'),
|
||||
},
|
||||
{
|
||||
id: 'close-tab',
|
||||
name: 'Close tab',
|
||||
name: this.translate.instant('Close tab'),
|
||||
},
|
||||
{
|
||||
id: 'reopen-tab',
|
||||
name: 'Reopen last tab',
|
||||
name: this.translate.instant('Reopen last tab'),
|
||||
},
|
||||
{
|
||||
id: 'toggle-last-tab',
|
||||
name: 'Toggle last tab',
|
||||
name: this.translate.instant('Toggle last tab'),
|
||||
},
|
||||
{
|
||||
id: 'next-tab',
|
||||
name: 'Next tab',
|
||||
name: this.translate.instant('Next tab'),
|
||||
},
|
||||
{
|
||||
id: 'previous-tab',
|
||||
name: 'Previous tab',
|
||||
name: this.translate.instant('Previous tab'),
|
||||
},
|
||||
{
|
||||
id: 'move-tab-left',
|
||||
name: 'Move tab to the left',
|
||||
name: this.translate.instant('Move tab to the left'),
|
||||
},
|
||||
{
|
||||
id: 'move-tab-right',
|
||||
name: 'Move tab to the right',
|
||||
name: this.translate.instant('Move tab to the right'),
|
||||
},
|
||||
{
|
||||
id: 'rearrange-panes',
|
||||
name: 'Show pane labels (for rearranging)',
|
||||
name: this.translate.instant('Show pane labels (for rearranging)'),
|
||||
},
|
||||
{
|
||||
id: 'duplicate-tab',
|
||||
name: 'Duplicate tab',
|
||||
name: this.translate.instant('Duplicate tab'),
|
||||
},
|
||||
{
|
||||
id: 'tab-1',
|
||||
name: 'Tab 1',
|
||||
name: this.translate.instant('Tab 1'),
|
||||
},
|
||||
{
|
||||
id: 'tab-2',
|
||||
name: 'Tab 2',
|
||||
name: this.translate.instant('Tab 2'),
|
||||
},
|
||||
{
|
||||
id: 'tab-3',
|
||||
name: 'Tab 3',
|
||||
name: this.translate.instant('Tab 3'),
|
||||
},
|
||||
{
|
||||
id: 'tab-4',
|
||||
name: 'Tab 4',
|
||||
name: this.translate.instant('Tab 4'),
|
||||
},
|
||||
{
|
||||
id: 'tab-5',
|
||||
name: 'Tab 5',
|
||||
name: this.translate.instant('Tab 5'),
|
||||
},
|
||||
{
|
||||
id: 'tab-6',
|
||||
name: 'Tab 6',
|
||||
name: this.translate.instant('Tab 6'),
|
||||
},
|
||||
{
|
||||
id: 'tab-7',
|
||||
name: 'Tab 7',
|
||||
name: this.translate.instant('Tab 7'),
|
||||
},
|
||||
{
|
||||
id: 'tab-8',
|
||||
name: 'Tab 8',
|
||||
name: this.translate.instant('Tab 8'),
|
||||
},
|
||||
{
|
||||
id: 'tab-9',
|
||||
name: 'Tab 9',
|
||||
name: this.translate.instant('Tab 9'),
|
||||
},
|
||||
{
|
||||
id: 'tab-10',
|
||||
name: 'Tab 10',
|
||||
name: this.translate.instant('Tab 10'),
|
||||
},
|
||||
{
|
||||
id: 'tab-11',
|
||||
name: 'Tab 11',
|
||||
name: this.translate.instant('Tab 11'),
|
||||
},
|
||||
{
|
||||
id: 'tab-12',
|
||||
name: 'Tab 12',
|
||||
name: this.translate.instant('Tab 12'),
|
||||
},
|
||||
{
|
||||
id: 'tab-13',
|
||||
name: 'Tab 13',
|
||||
name: this.translate.instant('Tab 13'),
|
||||
},
|
||||
{
|
||||
id: 'tab-14',
|
||||
name: 'Tab 14',
|
||||
name: this.translate.instant('Tab 14'),
|
||||
},
|
||||
{
|
||||
id: 'tab-15',
|
||||
name: 'Tab 15',
|
||||
name: this.translate.instant('Tab 15'),
|
||||
},
|
||||
{
|
||||
id: 'tab-16',
|
||||
name: 'Tab 16',
|
||||
name: this.translate.instant('Tab 16'),
|
||||
},
|
||||
{
|
||||
id: 'tab-17',
|
||||
name: 'Tab 17',
|
||||
name: this.translate.instant('Tab 17'),
|
||||
},
|
||||
{
|
||||
id: 'tab-18',
|
||||
name: 'Tab 18',
|
||||
name: this.translate.instant('Tab 18'),
|
||||
},
|
||||
{
|
||||
id: 'tab-19',
|
||||
name: 'Tab 19',
|
||||
name: this.translate.instant('Tab 19'),
|
||||
},
|
||||
{
|
||||
id: 'tab-20',
|
||||
name: 'Tab 20',
|
||||
name: this.translate.instant('Tab 20'),
|
||||
},
|
||||
{
|
||||
id: 'split-right',
|
||||
name: 'Split to the right',
|
||||
name: this.translate.instant('Split to the right'),
|
||||
},
|
||||
{
|
||||
id: 'split-bottom',
|
||||
name: 'Split to the bottom',
|
||||
name: this.translate.instant('Split to the bottom'),
|
||||
},
|
||||
{
|
||||
id: 'split-left',
|
||||
name: 'Split to the left',
|
||||
name: this.translate.instant('Split to the left'),
|
||||
},
|
||||
{
|
||||
id: 'split-top',
|
||||
name: 'Split to the top',
|
||||
name: this.translate.instant('Split to the top'),
|
||||
},
|
||||
{
|
||||
id: 'pane-maximize',
|
||||
name: 'Maximize the active pane',
|
||||
name: this.translate.instant('Maximize the active pane'),
|
||||
},
|
||||
{
|
||||
id: 'pane-nav-up',
|
||||
name: 'Focus the pane above',
|
||||
name: this.translate.instant('Focus the pane above'),
|
||||
},
|
||||
{
|
||||
id: 'pane-nav-down',
|
||||
name: 'Focus the pane below',
|
||||
name: this.translate.instant('Focus the pane below'),
|
||||
},
|
||||
{
|
||||
id: 'pane-nav-left',
|
||||
name: 'Focus the pane on the left',
|
||||
name: this.translate.instant('Focus the pane on the left'),
|
||||
},
|
||||
{
|
||||
id: 'pane-nav-right',
|
||||
name: 'Focus the pane on the right',
|
||||
name: this.translate.instant('Focus the pane on the right'),
|
||||
},
|
||||
{
|
||||
id: 'pane-nav-previous',
|
||||
name: 'Focus previous pane',
|
||||
name: this.translate.instant('Focus previous pane'),
|
||||
},
|
||||
{
|
||||
id: 'pane-nav-next',
|
||||
name: 'Focus next pane',
|
||||
name: this.translate.instant('Focus next pane'),
|
||||
},
|
||||
{
|
||||
id: 'switch-profile',
|
||||
name: 'Switch profile in the active pane',
|
||||
name: this.translate.instant('Switch profile in the active pane'),
|
||||
},
|
||||
{
|
||||
id: 'close-pane',
|
||||
name: 'Close focused pane',
|
||||
name: this.translate.instant('Close focused pane'),
|
||||
},
|
||||
]
|
||||
|
||||
constructor (
|
||||
private profilesService: ProfilesService,
|
||||
private translate: TranslateService,
|
||||
) { super() }
|
||||
|
||||
async provide (): Promise<HotkeyDescription[]> {
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import { NgModule, ModuleWithProviders } from '@angular/core'
|
||||
import { NgModule, ModuleWithProviders, LOCALE_ID } from '@angular/core'
|
||||
import { BrowserModule } from '@angular/platform-browser'
|
||||
import { BrowserAnimationsModule } from '@angular/platform-browser/animations'
|
||||
import { FormsModule } from '@angular/forms'
|
||||
@@ -7,6 +7,8 @@ import { PerfectScrollbarModule, PERFECT_SCROLLBAR_CONFIG } from 'ngx-perfect-sc
|
||||
import { NgxFilesizeModule } from 'ngx-filesize'
|
||||
import { SortablejsModule } from 'ngx-sortablejs'
|
||||
import { DragDropModule } from '@angular/cdk/drag-drop'
|
||||
import { TranslateModule, TranslateCompiler, TranslateService } from '@ngx-translate/core'
|
||||
import { TranslateMessageFormatCompiler, MESSAGE_FORMAT_CONFIG } from 'ngx-translate-messageformat-compiler'
|
||||
|
||||
import { AppRootComponent } from './components/appRoot.component'
|
||||
import { CheckboxComponent } from './components/checkbox.component'
|
||||
@@ -40,6 +42,7 @@ import { AppService } from './services/app.service'
|
||||
import { ConfigService } from './services/config.service'
|
||||
import { VaultFileProvider } from './services/vault.service'
|
||||
import { HotkeysService } from './services/hotkeys.service'
|
||||
import { LocaleService, TranslateServiceWrapper } from './services/locale.service'
|
||||
|
||||
import { StandardTheme, StandardCompactTheme, PaperTheme } from './theme'
|
||||
import { CoreConfigProvider } from './config'
|
||||
@@ -51,6 +54,10 @@ import { SplitLayoutProfilesService } from './profiles'
|
||||
|
||||
import 'perfect-scrollbar/css/perfect-scrollbar.css'
|
||||
|
||||
export function TranslateMessageFormatCompilerFactory (): TranslateMessageFormatCompiler {
|
||||
return new TranslateMessageFormatCompiler()
|
||||
}
|
||||
|
||||
const PROVIDERS = [
|
||||
{ provide: HotkeyProvider, useClass: AppHotkeyProvider, multi: true },
|
||||
{ provide: Theme, useClass: StandardTheme, multi: true },
|
||||
@@ -68,6 +75,19 @@ const PROVIDERS = [
|
||||
{ provide: FileProvider, useClass: VaultFileProvider, multi: true },
|
||||
{ provide: ToolbarButtonProvider, useClass: ButtonProvider, multi: true },
|
||||
{ provide: ProfileProvider, useExisting: SplitLayoutProfilesService, multi: true },
|
||||
{
|
||||
provide: LOCALE_ID,
|
||||
deps: [LocaleService],
|
||||
useFactory: locale => locale.getLocale(),
|
||||
},
|
||||
{
|
||||
provide: MESSAGE_FORMAT_CONFIG,
|
||||
useValue: LocaleService.allLocales,
|
||||
},
|
||||
{
|
||||
provide: TranslateService,
|
||||
useClass: TranslateServiceWrapper,
|
||||
},
|
||||
]
|
||||
|
||||
/** @hidden */
|
||||
@@ -81,6 +101,7 @@ const PROVIDERS = [
|
||||
PerfectScrollbarModule,
|
||||
DragDropModule,
|
||||
SortablejsModule.forRoot({ animation: 150 }),
|
||||
TranslateModule,
|
||||
],
|
||||
declarations: [
|
||||
AppRootComponent,
|
||||
@@ -127,6 +148,7 @@ const PROVIDERS = [
|
||||
AlwaysVisibleTypeaheadDirective,
|
||||
SortablejsModule,
|
||||
DragDropModule,
|
||||
TranslateModule,
|
||||
],
|
||||
})
|
||||
export default class AppModule { // eslint-disable-line @typescript-eslint/no-extraneous-class
|
||||
@@ -135,6 +157,8 @@ export default class AppModule { // eslint-disable-line @typescript-eslint/no-ex
|
||||
config: ConfigService,
|
||||
platform: PlatformService,
|
||||
hotkeys: HotkeysService,
|
||||
public locale: LocaleService,
|
||||
private translate: TranslateService,
|
||||
private profilesService: ProfilesService,
|
||||
private selector: SelectorService,
|
||||
) {
|
||||
@@ -182,8 +206,8 @@ export default class AppModule { // eslint-disable-line @typescript-eslint/no-ex
|
||||
|
||||
if (provider.supportsQuickConnect) {
|
||||
options.push({
|
||||
name: 'Quick connect',
|
||||
freeInputPattern: 'Connect to "%s"...',
|
||||
name: this.translate.instant('Quick connect'),
|
||||
freeInputPattern: this.translate.instant('Connect to "%s"...'),
|
||||
icon: 'fas fa-arrow-right',
|
||||
callback: query => {
|
||||
const p = provider.quickConnect(query)
|
||||
@@ -194,13 +218,23 @@ export default class AppModule { // eslint-disable-line @typescript-eslint/no-ex
|
||||
})
|
||||
}
|
||||
|
||||
await this.selector.show('Select profile', options)
|
||||
await this.selector.show(this.translate.instant('Select profile'), options)
|
||||
}
|
||||
|
||||
static forRoot (): ModuleWithProviders<AppModule> {
|
||||
const translateModule = TranslateModule.forRoot({
|
||||
defaultLanguage: 'en',
|
||||
compiler: {
|
||||
provide: TranslateCompiler,
|
||||
useFactory: TranslateMessageFormatCompilerFactory,
|
||||
},
|
||||
})
|
||||
return {
|
||||
ngModule: AppModule,
|
||||
providers: PROVIDERS,
|
||||
providers: [
|
||||
...PROVIDERS,
|
||||
...translateModule.providers!.filter(x => x !== TranslateService),
|
||||
],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,6 +1,7 @@
|
||||
import slugify from 'slugify'
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
import { Injectable } from '@angular/core'
|
||||
import { TranslateService } from '@ngx-translate/core'
|
||||
import { ConfigService, NewTabParameters, PartialProfile, Profile, ProfileProvider } from './api'
|
||||
import { SplitTabComponent, SplitTabRecoveryProvider } from './components/splitTab.component'
|
||||
|
||||
@@ -15,7 +16,7 @@ export interface SplitLayoutProfile extends Profile {
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class SplitLayoutProfilesService extends ProfileProvider<SplitLayoutProfile> {
|
||||
id = 'split-layout'
|
||||
name = 'Saved layout'
|
||||
name = this.translate.instant('Saved layout')
|
||||
configDefaults = {
|
||||
options: {
|
||||
recoveryToken: null,
|
||||
@@ -25,6 +26,7 @@ export class SplitLayoutProfilesService extends ProfileProvider<SplitLayoutProfi
|
||||
constructor (
|
||||
private splitTabRecoveryProvider: SplitTabRecoveryProvider,
|
||||
private config: ConfigService,
|
||||
private translate: TranslateService,
|
||||
) {
|
||||
super()
|
||||
}
|
||||
|
@@ -4,6 +4,7 @@ import { v4 as uuidv4 } from 'uuid'
|
||||
import * as yaml from 'js-yaml'
|
||||
import { Observable, Subject, AsyncSubject } from 'rxjs'
|
||||
import { Injectable, Inject } from '@angular/core'
|
||||
import { TranslateService } from '@ngx-translate/core'
|
||||
import { ConfigProvider } from '../api/configProvider'
|
||||
import { PlatformService } from '../api/platform'
|
||||
import { HostAppService } from '../api/hostApp'
|
||||
@@ -136,6 +137,7 @@ export class ConfigService {
|
||||
private hostApp: HostAppService,
|
||||
private platform: PlatformService,
|
||||
private vault: VaultService,
|
||||
private translate: TranslateService,
|
||||
@Inject(ConfigProvider) private configProviders: ConfigProvider[],
|
||||
) {
|
||||
this.defaults = this.mergeDefaults()
|
||||
@@ -360,9 +362,13 @@ export class ConfigService {
|
||||
} catch (e) {
|
||||
let result = await this.platform.showMessageBox({
|
||||
type: 'error',
|
||||
message: 'Could not decrypt config',
|
||||
message: this.translate.instant('Could not decrypt config'),
|
||||
detail: e.toString(),
|
||||
buttons: ['Try again', 'Erase config', 'Quit'],
|
||||
buttons: [
|
||||
this.translate.instant('Try again'),
|
||||
this.translate.instant('Erase config'),
|
||||
this.translate.instant('Quit'),
|
||||
],
|
||||
defaultId: 0,
|
||||
})
|
||||
if (result.response === 2) {
|
||||
@@ -371,9 +377,12 @@ export class ConfigService {
|
||||
if (result.response === 1) {
|
||||
result = await this.platform.showMessageBox({
|
||||
type: 'warning',
|
||||
message: 'Are you sure?',
|
||||
message: this.translate.instant('Are you sure?'),
|
||||
detail: e.toString(),
|
||||
buttons: ['Erase config', 'Quit'],
|
||||
buttons: [
|
||||
this.translate.instant('Erase config'),
|
||||
this.translate.instant('Quit'),
|
||||
],
|
||||
defaultId: 1,
|
||||
cancelId: 1,
|
||||
})
|
||||
|
@@ -1,4 +1,5 @@
|
||||
import { Inject, Injectable } from '@angular/core'
|
||||
import { TranslateService } from '@ngx-translate/core'
|
||||
import { FileProvider, NotificationsService, SelectorService } from '../api'
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
@@ -7,6 +8,7 @@ export class FileProvidersService {
|
||||
private constructor (
|
||||
private selector: SelectorService,
|
||||
private notifications: NotificationsService,
|
||||
private translate: TranslateService,
|
||||
@Inject(FileProvider) private fileProviders: FileProvider[],
|
||||
) { }
|
||||
|
||||
@@ -34,15 +36,18 @@ export class FileProvidersService {
|
||||
}
|
||||
}))
|
||||
if (!providers.length) {
|
||||
this.notifications.error('Vault master passphrase needs to be set to allow storing secrets')
|
||||
this.notifications.error(this.translate.instant('Vault master passphrase needs to be set to allow storing secrets'))
|
||||
throw new Error('No available file providers')
|
||||
}
|
||||
if (providers.length === 1) {
|
||||
return providers[0]
|
||||
}
|
||||
return this.selector.show('Select file storage', providers.map(p => ({
|
||||
name: p.name,
|
||||
result: p,
|
||||
})))
|
||||
return this.selector.show(
|
||||
this.translate.instant('Select file storage'),
|
||||
providers.map(p => ({
|
||||
name: p.name,
|
||||
result: p,
|
||||
}))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
134
tabby-core/src/services/locale.service.ts
Normal file
134
tabby-core/src/services/locale.service.ts
Normal file
@@ -0,0 +1,134 @@
|
||||
import { Injectable } from '@angular/core'
|
||||
import { registerLocaleData } from '@angular/common'
|
||||
import { TranslateService } from '@ngx-translate/core'
|
||||
|
||||
import localeEN from '@angular/common/locales/en-GB'
|
||||
import localeRU from '@angular/common/locales/ru'
|
||||
import { Observable, Subject } from 'rxjs'
|
||||
import { distinctUntilChanged } from 'rxjs/operators'
|
||||
import { ConfigService } from './config.service'
|
||||
import { LogService, Logger } from './log.service'
|
||||
|
||||
registerLocaleData(localeEN)
|
||||
registerLocaleData(localeRU)
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class TranslateServiceWrapper extends TranslateService {
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
||||
getParsedResult (translations: any, key: any, interpolateParams?: any): any {
|
||||
this.translations[this.defaultLang][key] ??= this.compiler.compile(key, this.defaultLang)
|
||||
return super.getParsedResult(translations, key, interpolateParams)
|
||||
}
|
||||
}
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class LocaleService {
|
||||
private logger: Logger
|
||||
|
||||
static readonly allLocales = ['en', 'de', 'fr', 'ru']
|
||||
|
||||
get localeChanged$ (): Observable<string> {
|
||||
return this.localeChanged.pipe(distinctUntilChanged())
|
||||
}
|
||||
|
||||
get catalogChanged$ (): Observable<Record<string, string | undefined>> {
|
||||
return this.catalogChanged.pipe(distinctUntilChanged())
|
||||
}
|
||||
|
||||
readonly allLanguages: { code: string, name: string }[]
|
||||
private translations = {
|
||||
en: {
|
||||
Close: 'Close',
|
||||
},
|
||||
ru: {
|
||||
Close: 'Закрыть',
|
||||
},
|
||||
}
|
||||
private locale = 'en'
|
||||
private localeChanged = new Subject<string>()
|
||||
private catalogChanged = new Subject<Record<string, string | undefined>>()
|
||||
|
||||
constructor (
|
||||
private config: ConfigService,
|
||||
private translate: TranslateService,
|
||||
log: LogService,
|
||||
) {
|
||||
this.logger = log.create('translate')
|
||||
config.changed$.subscribe(() => {
|
||||
this.refresh()
|
||||
})
|
||||
config.ready$.subscribe(() => {
|
||||
this.refresh()
|
||||
})
|
||||
|
||||
this.allLanguages = [
|
||||
{
|
||||
code: 'en',
|
||||
name: translate.instant('English'),
|
||||
},
|
||||
{
|
||||
code: 'de',
|
||||
name: translate.instant('German'),
|
||||
},
|
||||
{
|
||||
code: 'fr',
|
||||
name: translate.instant('French'),
|
||||
},
|
||||
/* {
|
||||
code: 'it',
|
||||
name: translate.instant('Italian'),
|
||||
},
|
||||
{
|
||||
code: 'es',
|
||||
name: translate.instant('Spanish'),
|
||||
}, */
|
||||
{
|
||||
code: 'ru',
|
||||
name: translate.instant('Russian'),
|
||||
},
|
||||
/* {
|
||||
code: 'ar',
|
||||
name: translate.instant('Arabic'),
|
||||
}, */
|
||||
]
|
||||
}
|
||||
|
||||
refresh (): void {
|
||||
let lang = this.config.store.language
|
||||
if (!lang) {
|
||||
const systemLanguage = navigator.language.toLowerCase().split('-')[0]
|
||||
if (this.allLanguages.some(x => x.code === systemLanguage)) {
|
||||
lang = systemLanguage
|
||||
}
|
||||
}
|
||||
lang ??= 'en'
|
||||
this.setLocale(lang)
|
||||
}
|
||||
|
||||
async setLocale (lang: string): Promise<void> {
|
||||
const strings = this.translations[lang]
|
||||
|
||||
if (!this.translate.langs.includes(lang)) {
|
||||
this.translate.addLangs([lang])
|
||||
|
||||
const po = require(`../../../locale/${lang}.po`).translations['']
|
||||
const translation = {}
|
||||
for (const k of Object.keys(po)) {
|
||||
translation[k] = po[k].msgstr[0] || k
|
||||
}
|
||||
|
||||
this.translate.setTranslation(lang, translation)
|
||||
}
|
||||
|
||||
this.translate.setDefaultLang(lang)
|
||||
|
||||
this.locale = lang
|
||||
this.localeChanged.next(lang)
|
||||
this.logger.debug('Setting language to', lang)
|
||||
this.catalogChanged.next(strings)
|
||||
}
|
||||
|
||||
getLocale (): string {
|
||||
return this.locale
|
||||
}
|
||||
}
|
@@ -1,4 +1,5 @@
|
||||
import { Injectable, Inject } from '@angular/core'
|
||||
import { TranslateService } from '@ngx-translate/core'
|
||||
import { NewTabParameters } from './tabs.service'
|
||||
import { BaseTabComponent } from '../components/baseTab.component'
|
||||
import { PartialProfile, Profile, ProfileProvider } from '../api/profileProvider'
|
||||
@@ -29,6 +30,7 @@ export class ProfilesService {
|
||||
private config: ConfigService,
|
||||
private notifications: NotificationsService,
|
||||
private selector: SelectorService,
|
||||
private translate: TranslateService,
|
||||
@Inject(ProfileProvider) private profileProviders: ProfileProvider<Profile>[],
|
||||
) { }
|
||||
|
||||
@@ -103,7 +105,7 @@ export class ProfilesService {
|
||||
|
||||
let options: SelectorOption<void>[] = recentProfiles.map(p => ({
|
||||
...this.selectorOptionForProfile(p),
|
||||
group: 'Recent',
|
||||
group: this.translate.instant('Recent'),
|
||||
icon: 'fas fa-history',
|
||||
color: p.color,
|
||||
callback: async () => {
|
||||
@@ -115,8 +117,8 @@ export class ProfilesService {
|
||||
}))
|
||||
if (recentProfiles.length) {
|
||||
options.push({
|
||||
name: 'Clear recent profiles',
|
||||
group: 'Recent',
|
||||
name: this.translate.instant('Clear recent profiles'),
|
||||
group: this.translate.instant('Recent'),
|
||||
icon: 'fas fa-eraser',
|
||||
callback: async () => {
|
||||
window.localStorage.removeItem('recentProfiles')
|
||||
@@ -142,7 +144,7 @@ export class ProfilesService {
|
||||
try {
|
||||
const { SettingsTabComponent } = window['nodeRequire']('tabby-settings')
|
||||
options.push({
|
||||
name: 'Manage profiles',
|
||||
name: this.translate.instant('Manage profiles'),
|
||||
icon: 'fas fa-window-restore',
|
||||
callback: () => {
|
||||
this.app.openNewTabRaw({
|
||||
@@ -156,8 +158,8 @@ export class ProfilesService {
|
||||
|
||||
if (this.getProviders().some(x => x.supportsQuickConnect)) {
|
||||
options.push({
|
||||
name: 'Quick connect',
|
||||
freeInputPattern: 'Connect to "%s"...',
|
||||
name: this.translate.instant('Quick connect'),
|
||||
freeInputPattern: this.translate.instant('Connect to "%s"...'),
|
||||
icon: 'fas fa-arrow-right',
|
||||
callback: query => {
|
||||
const profile = this.quickConnect(query)
|
||||
@@ -165,7 +167,7 @@ export class ProfilesService {
|
||||
},
|
||||
})
|
||||
}
|
||||
await this.selector.show('Select profile or enter an address', options)
|
||||
await this.selector.show(this.translate.instant('Select profile or enter an address'), options)
|
||||
} catch (err) {
|
||||
reject(err)
|
||||
}
|
||||
|
@@ -1,6 +1,7 @@
|
||||
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
|
||||
import { Injectable } from '@angular/core'
|
||||
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
|
||||
import { TranslateService } from '@ngx-translate/core'
|
||||
import { Subscription } from 'rxjs'
|
||||
import { AppService } from './services/app.service'
|
||||
import { BaseTabComponent } from './components/baseTab.component'
|
||||
@@ -22,6 +23,7 @@ export class TabManagementContextMenu extends TabContextMenuItemProvider {
|
||||
|
||||
constructor (
|
||||
private app: AppService,
|
||||
private translate: TranslateService,
|
||||
) {
|
||||
super()
|
||||
}
|
||||
@@ -29,7 +31,7 @@ export class TabManagementContextMenu extends TabContextMenuItemProvider {
|
||||
async getItems (tab: BaseTabComponent, tabHeader?: TabHeaderComponent): Promise<MenuItemOptions[]> {
|
||||
let items: MenuItemOptions[] = [
|
||||
{
|
||||
label: 'Close',
|
||||
label: this.translate.instant('Close'),
|
||||
click: () => {
|
||||
if (this.app.tabs.includes(tab)) {
|
||||
this.app.closeTab(tab, true)
|
||||
@@ -43,7 +45,7 @@ export class TabManagementContextMenu extends TabContextMenuItemProvider {
|
||||
items = [
|
||||
...items,
|
||||
{
|
||||
label: 'Close other tabs',
|
||||
label: this.translate.instant('Close other tabs'),
|
||||
click: () => {
|
||||
for (const t of this.app.tabs.filter(x => x !== tab)) {
|
||||
this.app.closeTab(t, true)
|
||||
@@ -51,7 +53,7 @@ export class TabManagementContextMenu extends TabContextMenuItemProvider {
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'Close tabs to the right',
|
||||
label: this.translate.instant('Close tabs to the right'),
|
||||
click: () => {
|
||||
for (const t of this.app.tabs.slice(this.app.tabs.indexOf(tab) + 1)) {
|
||||
this.app.closeTab(t, true)
|
||||
@@ -59,7 +61,7 @@ export class TabManagementContextMenu extends TabContextMenuItemProvider {
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'Close tabs to the left',
|
||||
label: this.translate.instant('Close tabs to the left'),
|
||||
click: () => {
|
||||
for (const t of this.app.tabs.slice(0, this.app.tabs.indexOf(tab))) {
|
||||
this.app.closeTab(t, true)
|
||||
@@ -71,13 +73,13 @@ export class TabManagementContextMenu extends TabContextMenuItemProvider {
|
||||
if (tab.parent instanceof SplitTabComponent) {
|
||||
const directions: SplitDirection[] = ['r', 'b', 'l', 't']
|
||||
items.push({
|
||||
label: 'Split',
|
||||
label: this.translate.instant('Split'),
|
||||
submenu: directions.map(dir => ({
|
||||
label: {
|
||||
r: 'Right',
|
||||
b: 'Down',
|
||||
l: 'Left',
|
||||
t: 'Up',
|
||||
r: this.translate.instant('Right'),
|
||||
b: this.translate.instant('Down'),
|
||||
l: this.translate.instant('Left'),
|
||||
t: this.translate.instant('Up'),
|
||||
}[dir],
|
||||
click: () => {
|
||||
(tab.parent as SplitTabComponent).splitTab(tab, dir)
|
||||
@@ -99,6 +101,7 @@ export class CommonOptionsContextMenu extends TabContextMenuItemProvider {
|
||||
private app: AppService,
|
||||
private ngbModal: NgbModal,
|
||||
private splitLayoutProfilesService: SplitLayoutProfilesService,
|
||||
private translate: TranslateService,
|
||||
) {
|
||||
super()
|
||||
}
|
||||
@@ -109,18 +112,18 @@ export class CommonOptionsContextMenu extends TabContextMenuItemProvider {
|
||||
items = [
|
||||
...items,
|
||||
{
|
||||
label: 'Rename',
|
||||
label: this.translate.instant('Rename'),
|
||||
click: () => tabHeader.showRenameTabModal(),
|
||||
},
|
||||
{
|
||||
label: 'Duplicate',
|
||||
label: this.translate.instant('Duplicate'),
|
||||
click: () => this.app.duplicateTab(tab),
|
||||
},
|
||||
{
|
||||
label: 'Color',
|
||||
label: this.translate.instant('Color'),
|
||||
sublabel: TAB_COLORS.find(x => x.value === tab.color)?.name,
|
||||
submenu: TAB_COLORS.map(color => ({
|
||||
label: color.name,
|
||||
label: this.translate.instant(color.name),
|
||||
type: 'radio',
|
||||
checked: tab.color === color.value,
|
||||
click: () => {
|
||||
@@ -132,10 +135,10 @@ export class CommonOptionsContextMenu extends TabContextMenuItemProvider {
|
||||
|
||||
if (tab instanceof SplitTabComponent && tab.getAllTabs().length > 1) {
|
||||
items.push({
|
||||
label: 'Save layout as profile',
|
||||
label: this.translate.instant('Save layout as profile'),
|
||||
click: async () => {
|
||||
const modal = this.ngbModal.open(PromptModalComponent)
|
||||
modal.componentInstance.prompt = 'Profile name'
|
||||
modal.componentInstance.prompt = this.translate.instant('Profile name')
|
||||
const name = (await modal.result)?.value
|
||||
if (!name) {
|
||||
return
|
||||
@@ -154,6 +157,7 @@ export class CommonOptionsContextMenu extends TabContextMenuItemProvider {
|
||||
export class TaskCompletionContextMenu extends TabContextMenuItemProvider {
|
||||
constructor (
|
||||
private app: AppService,
|
||||
private translate: TranslateService,
|
||||
) {
|
||||
super()
|
||||
}
|
||||
@@ -167,10 +171,10 @@ export class TaskCompletionContextMenu extends TabContextMenuItemProvider {
|
||||
if (process) {
|
||||
items.push({
|
||||
enabled: false,
|
||||
label: 'Current process: ' + process.name,
|
||||
label: this.translate.instant('Current process: {name}', process),
|
||||
})
|
||||
items.push({
|
||||
label: 'Notify when done',
|
||||
label: this.translate.instant('Notify when done'),
|
||||
type: 'checkbox',
|
||||
checked: extTab.__completionNotificationEnabled,
|
||||
click: () => {
|
||||
@@ -178,7 +182,7 @@ export class TaskCompletionContextMenu extends TabContextMenuItemProvider {
|
||||
|
||||
if (extTab.__completionNotificationEnabled) {
|
||||
this.app.observeTabCompletion(tab).subscribe(() => {
|
||||
new Notification('Process completed', {
|
||||
new Notification(this.translate.instant('Process completed'), {
|
||||
body: process.name,
|
||||
}).addEventListener('click', () => {
|
||||
this.app.selectTab(tab)
|
||||
@@ -192,7 +196,7 @@ export class TaskCompletionContextMenu extends TabContextMenuItemProvider {
|
||||
})
|
||||
}
|
||||
items.push({
|
||||
label: 'Notify on activity',
|
||||
label: this.translate.instant('Notify on activity'),
|
||||
type: 'checkbox',
|
||||
checked: !!extTab.__outputNotificationSubscription,
|
||||
click: () => {
|
||||
@@ -204,7 +208,7 @@ export class TaskCompletionContextMenu extends TabContextMenuItemProvider {
|
||||
if (extTab.__outputNotificationSubscription && active) {
|
||||
extTab.__outputNotificationSubscription.unsubscribe()
|
||||
extTab.__outputNotificationSubscription = null
|
||||
new Notification('Tab activity', {
|
||||
new Notification(this.translate.instant('Tab activity'), {
|
||||
body: tab.title,
|
||||
}).addEventListener('click', () => {
|
||||
this.app.selectTab(tab)
|
||||
@@ -228,6 +232,7 @@ export class ProfilesContextMenu extends TabContextMenuItemProvider {
|
||||
private profilesService: ProfilesService,
|
||||
private tabsService: TabsService,
|
||||
private app: AppService,
|
||||
private translate: TranslateService,
|
||||
hotkeys: HotkeysService,
|
||||
) {
|
||||
super()
|
||||
@@ -270,7 +275,7 @@ export class ProfilesContextMenu extends TabContextMenuItemProvider {
|
||||
if (!tabHeader && tab.parent instanceof SplitTabComponent && tab.parent.getAllTabs().length > 1) {
|
||||
return [
|
||||
{
|
||||
label: 'Switch profile',
|
||||
label: this.translate.instant('Switch profile'),
|
||||
click: () => this.switchTabProfile(tab),
|
||||
},
|
||||
]
|
||||
|
@@ -1,28 +1,41 @@
|
||||
import { Injectable } from '@angular/core'
|
||||
import { TranslateService } from '@ngx-translate/core'
|
||||
import { Theme } from './api'
|
||||
|
||||
/** @hidden */
|
||||
@Injectable()
|
||||
export class StandardTheme extends Theme {
|
||||
name = 'Standard'
|
||||
name = this.translate.instant('Standard')
|
||||
css = require('./theme.scss')
|
||||
terminalBackground = '#222a33'
|
||||
|
||||
constructor (private translate: TranslateService) {
|
||||
super()
|
||||
}
|
||||
}
|
||||
|
||||
/** @hidden */
|
||||
@Injectable()
|
||||
export class StandardCompactTheme extends Theme {
|
||||
name = 'Compact'
|
||||
name = this.translate.instant('Compact')
|
||||
css = require('./theme.compact.scss')
|
||||
terminalBackground = '#222a33'
|
||||
macOSWindowButtonsInsetX = 8
|
||||
macOSWindowButtonsInsetY = 6
|
||||
|
||||
constructor (private translate: TranslateService) {
|
||||
super()
|
||||
}
|
||||
}
|
||||
|
||||
/** @hidden */
|
||||
@Injectable()
|
||||
export class PaperTheme extends Theme {
|
||||
name = 'Paper'
|
||||
name = this.translate.instant('Paper')
|
||||
css = require('./theme.paper.scss')
|
||||
terminalBackground = '#f7f1e0'
|
||||
|
||||
constructor (private translate: TranslateService) {
|
||||
super()
|
||||
}
|
||||
}
|
||||
|
@@ -1,5 +1,6 @@
|
||||
import * as os from 'os'
|
||||
import { NgZone } from '@angular/core'
|
||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker'
|
||||
|
||||
export const WIN_BUILD_CONPTY_SUPPORTED = 17692
|
||||
export const WIN_BUILD_CONPTY_STABLE = 18309
|
||||
@@ -56,13 +57,13 @@ export class ResettableTimeout {
|
||||
}
|
||||
|
||||
export const TAB_COLORS = [
|
||||
{ name: 'No color', value: null },
|
||||
{ name: 'Blue', value: '#0275d8' },
|
||||
{ name: 'Green', value: '#5cb85c' },
|
||||
{ name: 'Orange', value: '#f0ad4e' },
|
||||
{ name: 'Purple', value: '#613d7c' },
|
||||
{ name: 'Red', value: '#d9534f' },
|
||||
{ name: 'Yellow', value: '#ffd500' },
|
||||
{ name: _('No color'), value: null },
|
||||
{ name: _('Blue'), value: '#0275d8' },
|
||||
{ name: _('Green'), value: '#5cb85c' },
|
||||
{ name: _('Orange'), value: '#f0ad4e' },
|
||||
{ name: _('Purple'), value: '#613d7c' },
|
||||
{ name: _('Red'), value: '#d9534f' },
|
||||
{ name: _('Yellow'), value: '#ffd500' },
|
||||
]
|
||||
|
||||
export function serializeFunction <T extends () => Promise<any>> (fn: T): T {
|
||||
|
@@ -2,6 +2,13 @@
|
||||
# yarn lockfile v1
|
||||
|
||||
|
||||
"@ngx-translate/core@^14.0.0":
|
||||
version "14.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@ngx-translate/core/-/core-14.0.0.tgz#af421d0e1a28376843f0fed375cd2fae7630a5ff"
|
||||
integrity sha512-UevdwNCXMRCdJv//0kC8h2eSfmi02r29xeE8E9gJ1Al4D4jEJ7eiLPdjslTMc21oJNGguqqWeEVjf64SFtvw2w==
|
||||
dependencies:
|
||||
tslib "^2.3.0"
|
||||
|
||||
agent-base@6:
|
||||
version "6.0.2"
|
||||
resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77"
|
||||
@@ -56,6 +63,37 @@ js-yaml@^4.0.0:
|
||||
dependencies:
|
||||
argparse "^2.0.1"
|
||||
|
||||
make-plural@^4.3.0:
|
||||
version "4.3.0"
|
||||
resolved "https://registry.yarnpkg.com/make-plural/-/make-plural-4.3.0.tgz#f23de08efdb0cac2e0c9ba9f315b0dff6b4c2735"
|
||||
integrity sha512-xTYd4JVHpSCW+aqDof6w/MebaMVNTVYBZhbB/vi513xXdiPT92JMVCo0Jq8W2UZnzYRFeVbQiQ+I25l13JuKvA==
|
||||
optionalDependencies:
|
||||
minimist "^1.2.0"
|
||||
|
||||
messageformat-formatters@^2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/messageformat-formatters/-/messageformat-formatters-2.0.1.tgz#0492c1402a48775f751c9b17c0354e92be012b08"
|
||||
integrity sha512-E/lQRXhtHwGuiQjI7qxkLp8AHbMD5r2217XNe/SREbBlSawe0lOqsFb7rflZJmlQFSULNLIqlcjjsCPlB3m3Mg==
|
||||
|
||||
messageformat-parser@^4.1.2:
|
||||
version "4.1.3"
|
||||
resolved "https://registry.yarnpkg.com/messageformat-parser/-/messageformat-parser-4.1.3.tgz#b824787f57fcda7d50769f5b63e8d4fda68f5b9e"
|
||||
integrity sha512-2fU3XDCanRqeOCkn7R5zW5VQHWf+T3hH65SzuqRvjatBK7r4uyFa5mEX+k6F9Bd04LVM5G4/BHBTUJsOdW7uyg==
|
||||
|
||||
messageformat@^2.3.0:
|
||||
version "2.3.0"
|
||||
resolved "https://registry.yarnpkg.com/messageformat/-/messageformat-2.3.0.tgz#de263c49029d5eae65d7ee25e0754f57f425ad91"
|
||||
integrity sha512-uTzvsv0lTeQxYI2y1NPa1lItL5VRI8Gb93Y2K2ue5gBPyrbJxfDi/EYWxh2PKv5yO42AJeeqblS9MJSh/IEk4w==
|
||||
dependencies:
|
||||
make-plural "^4.3.0"
|
||||
messageformat-formatters "^2.0.1"
|
||||
messageformat-parser "^4.1.2"
|
||||
|
||||
minimist@^1.2.0:
|
||||
version "1.2.5"
|
||||
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602"
|
||||
integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==
|
||||
|
||||
mixpanel@^0.13.0:
|
||||
version "0.13.0"
|
||||
resolved "https://registry.yarnpkg.com/mixpanel/-/mixpanel-0.13.0.tgz#699bf510d9ba013c75edcf979ff1e24085fde9d2"
|
||||
@@ -85,6 +123,13 @@ ngx-perfect-scrollbar@^10.1.0:
|
||||
resize-observer-polyfill "^1.5.0"
|
||||
tslib "^2.0.0"
|
||||
|
||||
ngx-translate-messageformat-compiler@^4.11.0:
|
||||
version "4.11.0"
|
||||
resolved "https://registry.yarnpkg.com/ngx-translate-messageformat-compiler/-/ngx-translate-messageformat-compiler-4.11.0.tgz#c9b71dd139ba5fcdcd809001e22622de589fd707"
|
||||
integrity sha512-OdGfWV4fF3DhZqGIHcLmOnQDufugmZ+E90NYr1UPGRZgT10lilr9oLmIrisy3lW4THnZFNo9JXsX7+fX84LbDw==
|
||||
dependencies:
|
||||
tslib "^1.10.0"
|
||||
|
||||
perfect-scrollbar@1.5.0:
|
||||
version "1.5.0"
|
||||
resolved "https://registry.yarnpkg.com/perfect-scrollbar/-/perfect-scrollbar-1.5.0.tgz#821d224ed8ff61990c23f26db63048cdc75b6b83"
|
||||
@@ -116,11 +161,21 @@ string_decoder@^1.1.1:
|
||||
dependencies:
|
||||
safe-buffer "~5.2.0"
|
||||
|
||||
tslib@^1.10.0:
|
||||
version "1.14.1"
|
||||
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00"
|
||||
integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==
|
||||
|
||||
tslib@^2.0.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.1.0.tgz#da60860f1c2ecaa5703ab7d39bc05b6bf988b97a"
|
||||
integrity sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A==
|
||||
|
||||
tslib@^2.3.0:
|
||||
version "2.3.1"
|
||||
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.1.tgz#e8a335add5ceae51aa261d32a490158ef042ef01"
|
||||
integrity sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==
|
||||
|
||||
util-deprecate@^1.0.1:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
|
||||
|
Reference in New Issue
Block a user