1
0
mirror of https://github.com/bitwarden/web synced 2025-12-06 00:03:28 +00:00

Compare commits

...

70 Commits

Author SHA1 Message Date
Kyle Spearrin
b093ed33b2 update jslib 2018-11-06 15:53:51 -05:00
Kyle Spearrin
ec1a45ba18 update jslib 2018-11-06 15:51:52 -05:00
Kyle Spearrin
def5dc3b0f New Crowdin translations (#286)
* New translations messages.json (Chinese Simplified)

* New translations messages.json (Chinese Traditional)

* New translations messages.json (Danish)

* New translations messages.json (Dutch)

* New translations messages.json (Estonian)

* New translations messages.json (Finnish)

* New translations messages.json (French)

* New translations messages.json (Japanese)

* New translations messages.json (Norwegian Bokmal)

* New translations messages.json (Polish)

* New translations messages.json (Portuguese)

* New translations messages.json (Portuguese, Brazilian)

* New translations messages.json (Russian)

* New translations messages.json (Spanish)
2018-11-06 15:51:05 -05:00
Kyle Spearrin
24ec89c220 open PDF in new window using built-in browser viewer 2018-11-06 09:46:17 -05:00
Kyle Spearrin
303e70bb58 update jslib 2018-11-06 09:05:46 -05:00
Kyle Spearrin
ec3e92fc19 set blob type 2018-10-30 09:54:14 -04:00
Kyle Spearrin
5ae776309d New Crowdin translations (#284)
* New translations messages.json (Chinese Simplified)

* New translations messages.json (Korean)

* New translations messages.json (Turkish)

* New translations messages.json (Swedish)

* New translations messages.json (Spanish)

* New translations messages.json (Slovak)

* New translations messages.json (Russian)

* New translations messages.json (Portuguese, Brazilian)

* New translations messages.json (Portuguese)

* New translations messages.json (Polish)

* New translations messages.json (Norwegian Bokmal)

* New translations messages.json (Japanese)

* New translations messages.json (Chinese Traditional)

* New translations messages.json (Italian)

* New translations messages.json (Hungarian)

* New translations messages.json (German)

* New translations messages.json (French)

* New translations messages.json (Finnish)

* New translations messages.json (Estonian)

* New translations messages.json (Dutch)

* New translations messages.json (Danish)

* New translations messages.json (Czech)

* New translations messages.json (Ukrainian)
2018-10-29 10:26:38 -04:00
Kyle Spearrin
76dd606a48 additionalStorageIntervalDesc 2018-10-29 10:07:03 -04:00
Kyle Spearrin
8998798fa4 always load nested collections 2018-10-29 10:06:42 -04:00
Kyle Spearrin
60ee82ca47 always loading nested now 2018-10-26 10:49:14 -04:00
Kyle Spearrin
e1284002a9 cleanup imports 2018-10-26 08:29:33 -04:00
Kyle Spearrin
8252512784 nested collections 2018-10-25 12:19:35 -04:00
Kyle Spearrin
1390d7eb1d display nested folders 2018-10-25 09:38:52 -04:00
Kyle Spearrin
8da1bb13ff dont stop prob on label simple label click for cb list 2018-10-24 22:15:09 -04:00
Kyle Spearrin
340e377b37 filter collections for current org 2018-10-24 22:09:36 -04:00
Kyle Spearrin
171589fb3d missing searchText property 2018-10-24 22:01:38 -04:00
Kyle Spearrin
bcd07cce0d New Crowdin translations (#281)
* New translations messages.json (Chinese Simplified)

* New translations messages.json (Korean)

* New translations messages.json (Turkish)

* New translations messages.json (Swedish)

* New translations messages.json (Spanish)

* New translations messages.json (Slovak)

* New translations messages.json (Russian)

* New translations messages.json (Portuguese, Brazilian)

* New translations messages.json (Portuguese)

* New translations messages.json (Polish)

* New translations messages.json (Norwegian Bokmal)

* New translations messages.json (Japanese)

* New translations messages.json (Chinese Traditional)

* New translations messages.json (Italian)

* New translations messages.json (Hungarian)

* New translations messages.json (German)

* New translations messages.json (French)

* New translations messages.json (Finnish)

* New translations messages.json (Estonian)

* New translations messages.json (Dutch)

* New translations messages.json (Danish)

* New translations messages.json (Czech)

* New translations messages.json (Ukrainian)
2018-10-24 13:03:03 -04:00
Kyle Spearrin
68880114b4 bump version 2018-10-23 22:57:36 -04:00
Kyle Spearrin
eb2360ae24 update jslib 2018-10-23 16:18:01 -04:00
Kyle Spearrin
62712a352b update jslib 2018-10-23 12:04:28 -04:00
Kyle Spearrin
745e6c1715 use base collections component from jslib 2018-10-23 12:04:05 -04:00
Kyle Spearrin
e20a75eb0c use share component from jslib 2018-10-23 10:33:40 -04:00
Kyle Spearrin
a24c41ff25 set org id and collections if filtered 2018-10-22 16:46:48 -04:00
Kyle Spearrin
69f0339bd5 set collections for org admin 2018-10-22 14:48:17 -04:00
Kyle Spearrin
5e7c9a7278 add ownership and collection assignment from add/edit 2018-10-19 12:44:52 -04:00
Kyle Spearrin
726c323fe1 accessAll is only for collection assignments 2018-10-18 12:25:25 -04:00
SoulSeekkor
e96cbe2710 Added .gitattributes file to files requiring LF endings are properly checked out on Windows. (#279) 2018-10-18 12:15:54 -04:00
Kyle Spearrin
323e54b4bd filtering 2018-10-18 12:15:13 -04:00
Kyle Spearrin
7ab132bbf6 add thead for entity users 2018-10-17 23:04:39 -04:00
Kyle Spearrin
6b09210a80 manage group users 2018-10-17 22:56:49 -04:00
Kyle Spearrin
be80d62c01 manage collection users for entity-users 2018-10-17 22:20:42 -04:00
Kyle Spearrin
30587d625a fixes to showAdd and filtering on load for non-admins 2018-10-17 16:09:09 -04:00
Kyle Spearrin
af43cd407e undo manage rules for org groupings listing 2018-10-17 15:57:39 -04:00
Kyle Spearrin
647388e475 showAddNew only if admin 2018-10-17 15:51:31 -04:00
Kyle Spearrin
329e06ac30 null check in view 2018-10-17 15:46:13 -04:00
Kyle Spearrin
5d96138720 lint fix 2018-10-17 11:23:01 -04:00
Kyle Spearrin
66b275605c load manage collections a manager has access to 2018-10-17 11:20:27 -04:00
Kyle Spearrin
9b7478c0c7 manager sees their assigned collections from org vault view 2018-10-17 11:19:10 -04:00
Kyle Spearrin
668271bb31 add basic org manager access and UI elements 2018-10-17 10:53:04 -04:00
Kyle Spearrin
1aa93e7737 New Crowdin translations (#277)
* New translations messages.json (Danish)

* New translations messages.json (Estonian)

* New translations messages.json (Finnish)

* New translations messages.json (Norwegian Bokmal)

* New translations messages.json (Russian)

* New translations messages.json (Slovak)

* New translations messages.json (Turkish)
2018-10-16 08:58:37 -04:00
Kyle Spearrin
a0864f5f67 update jslib 2018-10-16 08:57:19 -04:00
Kyle Spearrin
6e9f71f942 move getDomain to jslib 2018-10-13 23:26:38 -04:00
Kyle Spearrin
65211372df New Crowdin translations (#275)
* New translations messages.json (Chinese Simplified)

* New translations messages.json (Spanish)

* New translations messages.json (Russian)

* New translations messages.json (Portuguese, Brazilian)

* New translations messages.json (Portuguese)

* New translations messages.json (Polish)

* New translations messages.json (Chinese Traditional)

* New translations messages.json (Italian)

* New translations messages.json (French)

* New translations messages.json (Estonian)

* New translations messages.json (Danish)

* New translations messages.json (Ukrainian)
2018-10-11 21:35:24 -04:00
Kyle Spearrin
2ca8d8817a update jslib 2018-10-11 20:59:55 -04:00
Kyle Spearrin
ec266ea657 update jslib 2018-10-10 17:52:29 -04:00
Kyle Spearrin
d117aa5139 update yubiKeyDesc for 5 series 2018-10-10 12:30:03 -04:00
Kyle Spearrin
4534b7d4dc Merge branch 'master' of github.com:bitwarden/web 2018-10-09 18:03:25 -04:00
Kyle Spearrin
707fe01d77 update signalr 2018-10-09 18:03:23 -04:00
Kyle Spearrin
0e09ba0dd5 New Crowdin translations (#273)
* New translations messages.json (Chinese Simplified)

* New translations messages.json (Korean)

* New translations messages.json (Swedish)

* New translations messages.json (Spanish)

* New translations messages.json (Slovak)

* New translations messages.json (Russian)

* New translations messages.json (Portuguese, Brazilian)

* New translations messages.json (Portuguese)

* New translations messages.json (Polish)

* New translations messages.json (Norwegian Bokmal)

* New translations messages.json (Japanese)

* New translations messages.json (Chinese Traditional)

* New translations messages.json (Italian)

* New translations messages.json (Hungarian)

* New translations messages.json (German)

* New translations messages.json (French)

* New translations messages.json (Finnish)

* New translations messages.json (Estonian)

* New translations messages.json (Dutch)

* New translations messages.json (Danish)

* New translations messages.json (Czech)

* New translations messages.json (Ukrainian)
2018-10-09 16:07:41 -04:00
Kyle Spearrin
989560f23c renamed event to updated2fa 2018-10-09 16:01:00 -04:00
Kyle Spearrin
844a9f934f New Crowdin translations (#272)
* New translations messages.json (Chinese Simplified)

* New translations messages.json (Korean)

* New translations messages.json (Swedish)

* New translations messages.json (Spanish)

* New translations messages.json (Slovak)

* New translations messages.json (Russian)

* New translations messages.json (Portuguese, Brazilian)

* New translations messages.json (Portuguese)

* New translations messages.json (Polish)

* New translations messages.json (Norwegian Bokmal)

* New translations messages.json (Japanese)

* New translations messages.json (Chinese Traditional)

* New translations messages.json (Italian)

* New translations messages.json (Hungarian)

* New translations messages.json (German)

* New translations messages.json (French)

* New translations messages.json (Finnish)

* New translations messages.json (Estonian)

* New translations messages.json (Dutch)

* New translations messages.json (Danish)

* New translations messages.json (Czech)

* New translations messages.json (Ukrainian)
2018-10-09 09:21:52 -04:00
Kyle Spearrin
b5348c593a bump version 2018-10-08 23:11:38 -04:00
Kyle Spearrin
7f809ba541 inline redios 2018-10-08 22:42:32 -04:00
Kyle Spearrin
f9058fcddc pass gen fixes. word sep option 2018-10-08 22:06:15 -04:00
Kyle Spearrin
05c9957fd2 passphrase cleanup 2018-10-08 17:55:07 -04:00
Martin Trigaux
675739d24f Adapt the interface to generate passphrase too (#267) 2018-10-08 17:27:25 -04:00
Kyle Spearrin
10be0867ad New Crowdin translations (#270)
* New translations messages.json (Chinese Simplified)

* New translations messages.json (Korean)

* New translations messages.json (Swedish)

* New translations messages.json (Spanish)

* New translations messages.json (Slovak)

* New translations messages.json (Russian)

* New translations messages.json (Portuguese, Brazilian)

* New translations messages.json (Portuguese)

* New translations messages.json (Polish)

* New translations messages.json (Norwegian Bokmal)

* New translations messages.json (Japanese)

* New translations messages.json (Chinese Traditional)

* New translations messages.json (Italian)

* New translations messages.json (Hungarian)

* New translations messages.json (German)

* New translations messages.json (French)

* New translations messages.json (Finnish)

* New translations messages.json (Estonian)

* New translations messages.json (Dutch)

* New translations messages.json (Danish)

* New translations messages.json (Czech)

* New translations messages.json (Ukrainian)
2018-10-08 16:05:16 -04:00
Kyle Spearrin
d2a4b85bdd update passman instructions 2018-10-08 15:54:58 -04:00
ServiusHack
782061ac5e Add instructions for passman (#269) 2018-10-08 15:51:59 -04:00
Kyle Spearrin
8d98e9e6f9 add back proper isDev check 2018-10-08 14:26:10 -04:00
Kyle Spearrin
4aa75e9376 support for setup of multiple u2f keys 2018-10-08 14:23:30 -04:00
Kyle Spearrin
c6d6eecb43 update jslib 2018-10-05 13:57:41 -04:00
Kyle Spearrin
1d6d7b8aa8 dont await void methods 2018-10-04 11:58:19 -04:00
Kyle Spearrin
68ed8e51bd convert analytics and toaster to platform utils 2018-10-03 10:33:04 -04:00
Kyle Spearrin
d4dd962193 update jslib 2018-10-02 09:22:49 -04:00
Kyle Spearrin
7dfb70eb8e purge org vault 2018-09-25 09:12:24 -04:00
Kyle Spearrin
53675eeba7 stop prop on checkbox clicks 2018-09-24 17:45:35 -04:00
Kyle Spearrin
6399973bfa nojekyll 2018-09-22 16:04:09 -04:00
Kyle Spearrin
f1384f5dc1 passpack importer 2018-09-21 13:54:17 -04:00
Kyle Spearrin
027cad9e52 switch to webpack-dev-server 2018-09-18 11:59:03 -04:00
90 changed files with 7612 additions and 3330 deletions

3
.gitattributes vendored Normal file
View File

@@ -0,0 +1,3 @@
*.sh eol=lf
.dockerignore eol=lf
dockerfile eol=lf

2
jslib

Submodule jslib updated: 85587e0672...7dcb9b5f8b

3011
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,23 +1,24 @@
{
"name": "bitwarden-web",
"version": "2.3.0",
"version": "2.5.0",
"scripts": {
"sub:init": "git submodule update --init --recursive",
"sub:update": "git submodule update --remote",
"sub:pull": "git submodule foreach git pull",
"postinstall": "npm run sub:init",
"build": "gulp prebuild && webpack --config webpack.config.js",
"build:watch": "gulp prebuild && webpack-serve --config webpack.config.js",
"build:prod": "gulp prebuild && cross-env NODE_ENV=production webpack --config webpack.config.js",
"build:prod:watch": "gulp prebuild && cross-env NODE_ENV=production webpack-serve --config webpack.config.js",
"build:selfhost": "gulp prebuild && cross-env SELF_HOST=true webpack-serve --config webpack.config.js",
"build:selfhost:watch": "gulp prebuild && cross-env SELF_HOST=true webpack-serve --config webpack.config.js",
"build:selfhost:prod": "gulp prebuild && cross-env SELF_HOST=true NODE_ENV=production webpack --config webpack.config.js",
"build:selfhost:prod:watch": "gulp prebuild && cross-env SELF_HOST=true NODE_ENV=production webpack-serve --config webpack.config.js",
"build": "gulp prebuild && webpack",
"build:watch": "gulp prebuild && webpack-dev-server",
"build:prod": "gulp prebuild && cross-env NODE_ENV=production webpack",
"build:prod:watch": "gulp prebuild && cross-env NODE_ENV=production webpack-dev-server",
"build:selfhost": "gulp prebuild && cross-env SELF_HOST=true webpack-dev-server",
"build:selfhost:watch": "gulp prebuild && cross-env SELF_HOST=true webpack-dev-server",
"build:selfhost:prod": "gulp prebuild && cross-env SELF_HOST=true NODE_ENV=production webpack",
"build:selfhost:prod:watch": "gulp prebuild && cross-env SELF_HOST=true NODE_ENV=production webpack-dev-server",
"clean:l10n": "git push origin --delete l10n_master",
"dist": "npm run build:prod && gulp postdist",
"dist:selfhost": "npm run build:selfhost:prod && gulp postdist",
"deploy": "npm run dist && gh-pages -d build",
"deploy:dev": "npm run dist && gh-pages -d build -r git@github.com:kspearrin/bitwarden-web-dev.git",
"lint": "tslint src/**/*.ts || true",
"lint:fix": "tslint src/**/*.ts --fix"
},
@@ -52,7 +53,7 @@
"typescript": "^2.7.2",
"webpack": "^4.18.0",
"webpack-cli": "^3.1.0",
"webpack-serve": "^2.0.2"
"webpack-dev-server": "^3.1.8"
},
"dependencies": {
"@angular/animations": "6.1.7",
@@ -65,8 +66,8 @@
"@angular/platform-browser-dynamic": "6.1.7",
"@angular/router": "6.1.7",
"@angular/upgrade": "6.1.7",
"@aspnet/signalr": "1.0.3",
"@aspnet/signalr-protocol-msgpack": "1.0.3",
"@aspnet/signalr": "1.0.4",
"@aspnet/signalr-protocol-msgpack": "1.0.4",
"angular2-toaster": "6.1.0",
"angulartics2": "6.3.0",
"bootstrap": "4.1.3",

0
src/.nojekyll Normal file
View File

View File

@@ -1,11 +1,9 @@
import { Component } from '@angular/core';
import { Router } from '@angular/router';
import { ToasterService } from 'angular2-toaster';
import { Angulartics2 } from 'angulartics2';
import { ApiService } from 'jslib/abstractions/api.service';
import { I18nService } from 'jslib/abstractions/i18n.service';
import { PlatformUtilsService } from 'jslib/abstractions/platformUtils.service';
import { HintComponent as BaseHintComponent } from 'jslib/angular/components/hint.component';
@@ -14,9 +12,8 @@ import { HintComponent as BaseHintComponent } from 'jslib/angular/components/hin
templateUrl: 'hint.component.html',
})
export class HintComponent extends BaseHintComponent {
constructor(router: Router, analytics: Angulartics2,
toasterService: ToasterService, i18nService: I18nService,
apiService: ApiService) {
super(router, analytics, toasterService, i18nService, apiService);
constructor(router: Router, i18nService: I18nService,
apiService: ApiService, platformUtilsService: PlatformUtilsService) {
super(router, i18nService, apiService, platformUtilsService);
}
}

View File

@@ -4,9 +4,6 @@ import {
} from '@angular/core';
import { Router } from '@angular/router';
import { ToasterService } from 'angular2-toaster';
import { Angulartics2 } from 'angulartics2';
import { CryptoService } from 'jslib/abstractions/crypto.service';
import { I18nService } from 'jslib/abstractions/i18n.service';
import { MessagingService } from 'jslib/abstractions/messaging.service';
@@ -22,13 +19,11 @@ import { LockComponent as BaseLockComponent } from 'jslib/angular/components/loc
templateUrl: 'lock.component.html',
})
export class LockComponent extends BaseLockComponent implements OnInit {
constructor(router: Router, analytics: Angulartics2,
toasterService: ToasterService, i18nService: I18nService,
constructor(router: Router, i18nService: I18nService,
platformUtilsService: PlatformUtilsService, messagingService: MessagingService,
userService: UserService, cryptoService: CryptoService,
private routerService: RouterService) {
super(router, analytics, toasterService, i18nService, platformUtilsService,
messagingService, userService, cryptoService);
super(router, i18nService, platformUtilsService, messagingService, userService, cryptoService);
}
async ngOnInit() {

View File

@@ -4,11 +4,9 @@ import {
Router,
} from '@angular/router';
import { ToasterService } from 'angular2-toaster';
import { Angulartics2 } from 'angulartics2';
import { AuthService } from 'jslib/abstractions/auth.service';
import { I18nService } from 'jslib/abstractions/i18n.service';
import { PlatformUtilsService } from 'jslib/abstractions/platformUtils.service';
import { StateService } from 'jslib/abstractions/state.service';
import { StorageService } from 'jslib/abstractions/storage.service';
@@ -20,10 +18,10 @@ import { LoginComponent as BaseLoginComponent } from 'jslib/angular/components/l
})
export class LoginComponent extends BaseLoginComponent {
constructor(authService: AuthService, router: Router,
analytics: Angulartics2, toasterService: ToasterService,
i18nService: I18nService, private route: ActivatedRoute,
storageService: StorageService, private stateService: StateService) {
super(authService, router, analytics, toasterService, i18nService, storageService);
storageService: StorageService, private stateService: StateService,
platformUtilsService: PlatformUtilsService) {
super(authService, router, platformUtilsService, i18nService, storageService);
this.onSuccessfulLoginNavigate = this.goAfterLogIn;
}

View File

@@ -4,9 +4,6 @@ import {
Router,
} from '@angular/router';
import { ToasterService } from 'angular2-toaster';
import { Angulartics2 } from 'angulartics2';
import { ApiService } from 'jslib/abstractions/api.service';
import { AuthService } from 'jslib/abstractions/auth.service';
import { CryptoService } from 'jslib/abstractions/crypto.service';
@@ -25,11 +22,10 @@ export class RegisterComponent extends BaseRegisterComponent {
showTerms = true;
constructor(authService: AuthService, router: Router,
analytics: Angulartics2, toasterService: ToasterService,
i18nService: I18nService, cryptoService: CryptoService,
apiService: ApiService, private route: ActivatedRoute,
stateService: StateService, platformUtilsService: PlatformUtilsService) {
super(authService, router, analytics, toasterService, i18nService, cryptoService, apiService, stateService);
super(authService, router, i18nService, cryptoService, apiService, stateService, platformUtilsService);
this.showTerms = !platformUtilsService.isSelfHost();
}

View File

@@ -1,9 +1,6 @@
import { Component } from '@angular/core';
import { Router } from '@angular/router';
import { ToasterService } from 'angular2-toaster';
import { Angulartics2 } from 'angulartics2';
import { AuthService } from 'jslib/abstractions/auth.service';
import { I18nService } from 'jslib/abstractions/i18n.service';
import { PlatformUtilsService } from 'jslib/abstractions/platformUtils.service';
@@ -18,8 +15,7 @@ import {
})
export class TwoFactorOptionsComponent extends BaseTwoFactorOptionsComponent {
constructor(authService: AuthService, router: Router,
analytics: Angulartics2, toasterService: ToasterService,
i18nService: I18nService, platformUtilsService: PlatformUtilsService) {
super(authService, router, analytics, toasterService, i18nService, platformUtilsService, window);
super(authService, router, i18nService, platformUtilsService, window);
}
}

View File

@@ -7,9 +7,6 @@ import {
import { Router } from '@angular/router';
import { ToasterService } from 'angular2-toaster';
import { Angulartics2 } from 'angulartics2';
import { TwoFactorOptionsComponent } from './two-factor-options.component';
import { ModalComponent } from '../modal.component';
@@ -33,12 +30,10 @@ export class TwoFactorComponent extends BaseTwoFactorComponent {
@ViewChild('twoFactorOptions', { read: ViewContainerRef }) twoFactorOptionsModal: ViewContainerRef;
constructor(authService: AuthService, router: Router,
analytics: Angulartics2, toasterService: ToasterService,
i18nService: I18nService, apiService: ApiService,
platformUtilsService: PlatformUtilsService, private stateService: StateService,
environmentService: EnvironmentService, private componentFactoryResolver: ComponentFactoryResolver) {
super(authService, router, analytics, toasterService, i18nService, apiService,
platformUtilsService, window, environmentService);
super(authService, router, i18nService, apiService, platformUtilsService, window, environmentService);
this.onSuccessfulLoginNavigate = this.goAfterLogIn;
}

View File

@@ -174,7 +174,13 @@ const routes: Routes = [
path: 'manage',
component: OrgManageComponent,
canActivate: [OrganizationTypeGuardService],
data: { allowedTypes: [OrganizationUserType.Owner, OrganizationUserType.Admin] },
data: {
allowedTypes: [
OrganizationUserType.Owner,
OrganizationUserType.Admin,
OrganizationUserType.Manager,
],
},
children: [
{ path: '', pathMatch: 'full', redirectTo: 'people' },
{ path: 'collections', component: OrgManageCollectionsComponent, data: { titleId: 'collections' } },

View File

@@ -3,7 +3,10 @@ import * as _swal from 'sweetalert';
import { SweetAlert } from 'sweetalert/typings/core';
import {
BodyOutputType,
Toast,
ToasterConfig,
ToasterContainerComponent,
ToasterService,
} from 'angular2-toaster';
import { Angulartics2 } from 'angulartics2';
@@ -14,7 +17,9 @@ import {
NgZone,
OnDestroy,
OnInit,
SecurityContext,
} from '@angular/core';
import { DomSanitizer } from '@angular/platform-browser';
import {
NavigationEnd,
Router,
@@ -75,7 +80,7 @@ export class AppComponent implements OnDestroy, OnInit {
private platformUtilsService: PlatformUtilsService, private ngZone: NgZone,
private lockService: LockService, private storageService: StorageService,
private cryptoService: CryptoService, private collectionService: CollectionService,
private routerService: RouterService, private searchService: SearchService,
private sanitizer: DomSanitizer, private searchService: SearchService,
private notificationsService: NotificationsService) { }
ngOnInit() {
@@ -126,6 +131,15 @@ export class AppComponent implements OnDestroy, OnInit {
this.router.navigate(['settings/premium']);
}
break;
case 'showToast':
this.showToast(message);
break;
case 'analyticsEventTrack':
this.analytics.eventTrack.next({
action: message.action,
properties: { label: message.label },
});
break;
default:
break;
}
@@ -202,6 +216,33 @@ export class AppComponent implements OnDestroy, OnInit {
}, IdleTimeout);
}
private showToast(msg: any) {
const toast: Toast = {
type: msg.type,
title: msg.title,
};
if (typeof (msg.text) === 'string') {
toast.body = msg.text;
} else if (msg.text.length === 1) {
toast.body = msg.text[0];
} else {
let message = '';
msg.text.forEach((t: string) =>
message += ('<p>' + this.sanitizer.sanitize(SecurityContext.HTML, t) + '</p>'));
toast.body = message;
toast.bodyOutputType = BodyOutputType.TrustedHtml;
}
if (msg.options != null) {
if (msg.options.trustedHtml === true) {
toast.bodyOutputType = BodyOutputType.TrustedHtml;
}
if (msg.options.timeout != null && msg.options.timeout > 0) {
toast.timeout = msg.options.timeout;
}
}
this.toasterService.popAsync(toast);
}
private idleStateChanged() {
if (this.isIdle) {
this.notificationsService.disconnectFromInactivity();

View File

@@ -14,7 +14,7 @@
</div>
</div>
</div>
<ul class="nav nav-tabs" *ngIf="organization.isAdmin">
<ul class="nav nav-tabs" *ngIf="organization.isManager">
<li class="nav-item">
<a class="nav-link" routerLink="vault" routerLinkActive="active">
<i class="fa fa-lock"></i>
@@ -27,7 +27,7 @@
{{'manage' | i18n}}
</a>
</li>
<li class="nav-item">
<li class="nav-item" *ngIf="organization.isAdmin">
<a class="nav-link" routerLink="tools" routerLinkActive="active">
<i class="fa fa-wrench"></i>
{{'tools' | i18n}}

View File

@@ -41,13 +41,11 @@
<tbody>
<tr *ngFor="let g of groups; let i = index">
<td class="table-list-checkbox" (click)="check(g)">
<input type="checkbox" [(ngModel)]="g.checked" name="Groups[{{i}}].Checked" [disabled]="g.accessAll">
<input type="checkbox" [(ngModel)]="g.checked" name="Groups[{{i}}].Checked" [disabled]="g.accessAll" appStopProp>
</td>
<td (click)="check(g)">
<span appStopProp>
{{g.name}}
<i class="fa fa-th text-muted fa-fw" *ngIf="g.accessAll" title="This group can access all items"></i>
</span>
{{g.name}}
<i class="fa fa-th text-muted fa-fw" *ngIf="g.accessAll" title="This group can access all items"></i>
</td>
<td class="text-center">
<input type="checkbox" [(ngModel)]="g.readOnly" name="Groups[{{i}}].ReadOnly" [disabled]="!g.checked || g.accessAll">
@@ -64,8 +62,8 @@
</button>
<button type="button" class="btn btn-outline-secondary" data-dismiss="modal">{{'cancel' | i18n}}</button>
<div class="ml-auto">
<button #deleteBtn type="button" (click)="delete()" class="btn btn-outline-danger" title="{{'delete' | i18n}}" *ngIf="editMode"
[disabled]="deleteBtn.loading" [appApiAction]="deletePromise">
<button #deleteBtn type="button" (click)="delete()" class="btn btn-outline-danger" title="{{'delete' | i18n}}"
*ngIf="editMode" [disabled]="deleteBtn.loading" [appApiAction]="deletePromise">
<i class="fa fa-trash-o fa-lg fa-fw" [hidden]="deleteBtn.loading"></i>
<i class="fa fa-spinner fa-spin fa-lg fa-fw" [hidden]="!deleteBtn.loading" title="{{'loading' | i18n}}"></i>
</button>

View File

@@ -14,10 +14,15 @@ import { ApiService } from 'jslib/abstractions/api.service';
import { CollectionService } from 'jslib/abstractions/collection.service';
import { I18nService } from 'jslib/abstractions/i18n.service';
import { PlatformUtilsService } from 'jslib/abstractions/platformUtils.service';
import { UserService } from 'jslib/abstractions/user.service';
import { CollectionData } from 'jslib/models/data/collectionData';
import { Collection } from 'jslib/models/domain/collection';
import { CollectionDetailsResponse } from 'jslib/models/response/collectionResponse';
import {
CollectionDetailsResponse,
CollectionResponse,
} from 'jslib/models/response/collectionResponse';
import { ListResponse } from 'jslib/models/response/listResponse';
import { CollectionView } from 'jslib/models/view/collectionView';
import { ModalComponent } from '../../modal.component';
@@ -42,7 +47,8 @@ export class CollectionsComponent implements OnInit {
constructor(private apiService: ApiService, private route: ActivatedRoute,
private collectionService: CollectionService, private componentFactoryResolver: ComponentFactoryResolver,
private analytics: Angulartics2, private toasterService: ToasterService,
private i18nService: I18nService, private platformUtilsService: PlatformUtilsService) { }
private i18nService: I18nService, private platformUtilsService: PlatformUtilsService,
private userService: UserService) { }
async ngOnInit() {
this.route.parent.parent.params.subscribe(async (params) => {
@@ -55,8 +61,14 @@ export class CollectionsComponent implements OnInit {
}
async load() {
const response = await this.apiService.getCollections(this.organizationId);
const collections = response.data.map((r) =>
const organization = await this.userService.getOrganization(this.organizationId);
let response: ListResponse<CollectionResponse>;
if (organization.isAdmin) {
response = await this.apiService.getCollections(this.organizationId);
} else {
response = await this.apiService.getUserCollections();
}
const collections = response.data.filter((c) => c.organizationId === this.organizationId).map((r) =>
new Collection(new CollectionData(r as CollectionDetailsResponse)));
this.collections = await this.collectionService.decryptMany(collections);
this.loading = false;
@@ -123,6 +135,10 @@ export class CollectionsComponent implements OnInit {
childComponent.entityId = collection.id;
childComponent.entityName = collection.name;
childComponent.onEditedUsers.subscribe(() => {
this.load();
this.modal.close();
});
this.modal.onClosed.subscribe(() => {
this.modal = null;
});

View File

@@ -1,6 +1,6 @@
<div class="modal fade">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<form class="modal-content" #form (ngSubmit)="submit()" [appApiAction]="formPromise" ngNativeValidate>
<div class="modal-header">
<h2 class="modal-title">
{{'userAccess' | i18n}}
@@ -10,48 +10,88 @@
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body" *ngIf="loading">
<div class="modal-body" *ngIf="loading || !users">
<i class="fa fa-spinner fa-spin text-muted" title="{{'loading' | i18n}}"></i>
</div>
<div class="modal-body" *ngIf="!loading">
<ng-container *ngIf="!users || !users.length">
<div class="modal-body" *ngIf="!loading && users && (users | search:searchText:'name':'email':'id') as searchedUsers">
<div class="d-flex">
<div class="mr-3">
<label class="sr-only" for="search">{{'search' | i18n}}</label>
<input type="search" class="form-control form-control-sm" id="search" placeholder="{{'search' | i18n}}"
name="SearchText" [(ngModel)]="searchText">
</div>
<div class="btn-group btn-group-sm" role="group">
<button type="button" class="btn btn-outline-secondary" [ngClass]="{active: !showSelected}"
(click)="filterSelected(false)">
{{'all' | i18n}}
</button>
<button type="button" class="btn btn-outline-secondary" [ngClass]="{active: showSelected}"
(click)="filterSelected(true)">
{{'selected' | i18n}}
<span class="badge badge-pill badge-info" *ngIf="selectedCount">{{selectedCount}}</span>
</button>
</div>
</div>
<ng-container *ngIf="!searchedUsers.length">
<hr>
{{'noUsersInList' | i18n}}
</ng-container>
<table class="table table-hover table-list mb-0" *ngIf="users && users.length">
<tbody>
<tr *ngFor="let u of users">
<td width="30">
<app-avatar [data]="u.name || u.email" [email]="u.email" size="25" [circle]="true" [fontSize]="14"></app-avatar>
</td>
<td>
{{u.email}}
<span class="badge badge-secondary" *ngIf="u.status === organizationUserStatusType.Invited">{{'invited' | i18n}}</span>
<span class="badge badge-warning" *ngIf="u.status === organizationUserStatusType.Accepted">{{'accepted' | i18n}}</span>
<small class="text-muted d-block" *ngIf="u.name">{{u.name}}</small>
</td>
<td *ngIf="entity === 'collection'">
<i class="fa fa-th" *ngIf="u.accessAll" title="{{'userAccessAllItems' | i18n}}"></i>
<i class="fa fa-eye" *ngIf="u.readOnly" title="{{'readOnly' | i18n}}"></i>
</td>
<td>
<span *ngIf="u.type === organizationUserType.Owner">{{'owner' | i18n}}</span>
<span *ngIf="u.type === organizationUserType.Admin">{{'admin' | i18n}}</span>
<span *ngIf="u.type === organizationUserType.User">{{'user' | i18n}}</span>
</td>
<td class="table-list-options wider">
<button type="button" class="btn btn-sm btn-outline-danger btn-submit" (click)="remove(u)" #removeBtn [disabled]="removeBtn.loading"
[appApiAction]="actionPromise" *ngIf="entity !== 'collection' || !u.accessAll">
<i class="fa fa-spinner fa-spin" title="{{'loading' | i18n}}"></i>
<span>{{'remove' | i18n}}</span>
</button>
</td>
</tr>
</tbody>
</table>
<ng-container *ngIf="searchedUsers.length">
<table class="table table-hover table-list mb-0">
<thead>
<tr>
<th>&nbsp;</th>
<th>&nbsp;</th>
<th>{{'name' | i18n}}</th>
<th *ngIf="entity === 'collection'">&nbsp;</th>
<th>{{'userType' | i18n}}</th>
<th width="100" class="text-center" *ngIf="entity === 'collection'">{{'readOnly' |
i18n}}</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let u of searchedUsers">
<td class="table-list-checkbox" (click)="check(u)">
<input type="checkbox" [(ngModel)]="u.checked" name="{{u.id.substr(0,8)}}_Checked"
[disabled]="entity === 'collection' && u.accessAll" (change)="selectedChanged(u)" appStopProp>
</td>
<td width="30" (click)="check(u)">
<app-avatar [data]="u.name || u.email" [email]="u.email" size="25" [circle]="true"
[fontSize]="14"></app-avatar>
</td>
<td>
{{u.email}}
<span class="badge badge-secondary" *ngIf="u.status === organizationUserStatusType.Invited">{{'invited'
| i18n}}</span>
<span class="badge badge-warning" *ngIf="u.status === organizationUserStatusType.Accepted">{{'accepted'
| i18n}}</span>
<small class="text-muted d-block" *ngIf="u.name">{{u.name}}</small>
</td>
<td *ngIf="entity === 'collection'">
<i class="fa fa-th" *ngIf="u.accessAll" title="{{'userAccessAllItems' | i18n}}"></i>
</td>
<td>
<span *ngIf="u.type === organizationUserType.Owner">{{'owner' | i18n}}</span>
<span *ngIf="u.type === organizationUserType.Admin">{{'admin' | i18n}}</span>
<span *ngIf="u.type === organizationUserType.Manager">{{'manager' | i18n}}</span>
<span *ngIf="u.type === organizationUserType.User">{{'user' | i18n}}</span>
</td>
<td class="text-center" *ngIf="entity === 'collection'">
<input type="checkbox" [(ngModel)]="u.readOnly" name="{{u.id.substr(0,8)}}_ReadOnly"
[disabled]="u.accessAll || !u.checked">
</td>
</tr>
</tbody>
</table>
</ng-container>
</div>
<div class="modal-footer">
<button type="submit" class="btn btn-primary btn-submit" [disabled]="form.loading">
<i class="fa fa-spinner fa-spin" title="{{'loading' | i18n}}"></i>
<span>{{'save' | i18n}}</span>
</button>
<button type="button" class="btn btn-outline-secondary" data-dismiss="modal">{{'close' | i18n}}</button>
</div>
</div>
</form>
</div>
</div>

View File

@@ -11,10 +11,11 @@ import { Angulartics2 } from 'angulartics2';
import { ApiService } from 'jslib/abstractions/api.service';
import { I18nService } from 'jslib/abstractions/i18n.service';
import { PlatformUtilsService } from 'jslib/abstractions/platformUtils.service';
import { OrganizationUserStatusType } from 'jslib/enums/organizationUserStatusType';
import { OrganizationUserType } from 'jslib/enums/organizationUserType';
import { SelectionReadOnlyRequest } from 'jslib/models/request/selectionReadOnlyRequest';
import { OrganizationUserUserDetailsResponse } from 'jslib/models/response/organizationUserResponse';
import { Utils } from 'jslib/misc/utils';
@@ -27,68 +28,110 @@ export class EntityUsersComponent implements OnInit {
@Input() entityId: string;
@Input() entityName: string;
@Input() organizationId: string;
@Output() onRemovedUser = new EventEmitter();
@Output() onEditedUsers = new EventEmitter();
organizationUserType = OrganizationUserType;
organizationUserStatusType = OrganizationUserStatusType;
showSelected = false;
loading = true;
users: any[] = [];
actionPromise: Promise<any>;
formPromise: Promise<any>;
selectedCount = 0;
searchText: string;
private allUsers: OrganizationUserUserDetailsResponse[] = [];
constructor(private apiService: ApiService, private i18nService: I18nService,
private analytics: Angulartics2, private toasterService: ToasterService,
private platformUtilsService: PlatformUtilsService) { }
private analytics: Angulartics2, private toasterService: ToasterService) { }
async ngOnInit() {
await this.loadUsers();
this.loading = false;
}
async loadUsers() {
let users: any[] = [];
if (this.entity === 'group') {
const response = await this.apiService.getGroupUsers(this.organizationId, this.entityId);
users = response.data.map((r) => r);
} else if (this.entity === 'collection') {
const response = await this.apiService.getCollectionUsers(this.organizationId, this.entityId);
users = response.data.map((r) => r);
get users() {
if (this.showSelected) {
return this.allUsers.filter((u) => (u as any).checked);
} else {
return this.allUsers;
}
users.sort(Utils.getSortFunction(this.i18nService, 'email'));
this.users = users;
}
async remove(user: any) {
if (this.actionPromise != null || (this.entity === 'collection' && user.accessAll)) {
async loadUsers() {
const users = await this.apiService.getOrganizationUsers(this.organizationId);
this.allUsers = users.data.map((r) => r).sort(Utils.getSortFunction(this.i18nService, 'email'));
if (this.entity === 'group') {
const response = await this.apiService.getGroupUsers(this.organizationId, this.entityId);
if (response != null && users.data.length > 0) {
response.forEach((s) => {
const user = users.data.filter((u) => u.id === s);
if (user != null && user.length > 0) {
(user[0] as any).checked = true;
}
});
}
} else if (this.entity === 'collection') {
const response = await this.apiService.getCollectionUsers(this.organizationId, this.entityId);
if (response != null && users.data.length > 0) {
response.forEach((s) => {
const user = users.data.filter((u) => !u.accessAll && u.id === s.id);
if (user != null && user.length > 0) {
(user[0] as any).checked = true;
(user[0] as any).readOnly = s.readOnly;
}
});
}
}
this.allUsers.forEach((u) => {
if (this.entity === 'collection' && u.accessAll) {
(u as any).checked = true;
}
if ((u as any).checked) {
this.selectedCount++;
}
});
}
check(u: OrganizationUserUserDetailsResponse) {
if (this.entity === 'collection' && u.accessAll) {
return;
}
(u as any).checked = !(u as any).checked;
this.selectedChanged(u);
}
const confirmed = await this.platformUtilsService.showDialog(
this.i18nService.t('removeUserConfirmation'), user.email,
this.i18nService.t('yes'), this.i18nService.t('no'), 'warning');
if (!confirmed) {
return false;
selectedChanged(u: OrganizationUserUserDetailsResponse) {
if ((u as any).checked) {
this.selectedCount++;
} else {
if (this.entity === 'collection') {
(u as any).readOnly = false;
}
this.selectedCount--;
}
}
filterSelected(showSelected: boolean) {
this.showSelected = showSelected;
}
async submit() {
try {
if (this.entity === 'group') {
this.actionPromise = this.apiService.deleteGroupUser(this.organizationId, this.entityId,
user.organizationUserId);
await this.actionPromise;
this.analytics.eventTrack.next({ action: 'Removed User From Group' });
} else if (this.entity === 'collection') {
this.actionPromise = this.apiService.deleteCollectionUser(this.organizationId, this.entityId,
user.organizationUserId);
await this.actionPromise;
this.analytics.eventTrack.next({ action: 'Removed User From Collection' });
}
this.toasterService.popAsync('success', null, this.i18nService.t('removedUserId', user.email));
this.onRemovedUser.emit();
const index = this.users.indexOf(user);
if (index > -1) {
this.users.splice(index, 1);
const selections = this.users.filter((u) => (u as any).checked).map((u) => u.id);
this.formPromise = this.apiService.putGroupUsers(this.organizationId, this.entityId, selections);
} else {
const selections = this.users.filter((u) => (u as any).checked && !u.accessAll)
.map((u) => new SelectionReadOnlyRequest(u.id, !!(u as any).readOnly));
this.formPromise = this.apiService.putCollectionUsers(this.organizationId, this.entityId, selections);
}
await this.formPromise;
this.analytics.eventTrack.next({
action: this.entity === 'group' ? 'Edited Group Users' : 'Edited Collection Users',
});
this.toasterService.popAsync('success', null, this.i18nService.t('updatedUsers'));
this.onEditedUsers.emit();
} catch { }
}
}

View File

@@ -62,10 +62,10 @@
<tbody>
<tr *ngFor="let c of collections; let i = index">
<td class="table-list-checkbox" (click)="check(c)">
<input type="checkbox" [(ngModel)]="c.checked" name="Collection[{{i}}].Checked">
<input type="checkbox" [(ngModel)]="c.checked" name="Collection[{{i}}].Checked" appStopProp>
</td>
<td (click)="check(c)">
<span appStopProp>{{c.name}}</span>
{{c.name}}
</td>
<td class="text-center">
<input type="checkbox" [(ngModel)]="c.readOnly" name="Collection[{{i}}].ReadOnly" [disabled]="!c.checked">

View File

@@ -131,6 +131,9 @@ export class GroupsComponent implements OnInit {
childComponent.entityId = group.id;
childComponent.entityName = group.name;
childComponent.onEditedUsers.subscribe(() => {
this.modal.close();
});
this.modal.onClosed.subscribe(() => {
this.modal = null;
});

View File

@@ -1,19 +1,19 @@
<div class="container page-content">
<div class="row">
<div class="col-3">
<div class="card">
<div class="card" *ngIf="organization">
<div class="card-header">{{'manage' | i18n}}</div>
<div class="list-group list-group-flush">
<a routerLink="people" class="list-group-item" routerLinkActive="active">
<a routerLink="people" class="list-group-item" routerLinkActive="active" *ngIf="organization.isAdmin">
{{'people' | i18n}}
</a>
<a routerLink="collections" class="list-group-item" routerLinkActive="active">
{{'collections' | i18n}}
</a>
<a routerLink="groups" class="list-group-item" routerLinkActive="active" *ngIf="accessGroups">
<a routerLink="groups" class="list-group-item" routerLinkActive="active" *ngIf="organization.isAdmin && accessGroups">
{{'groups' | i18n}}
</a>
<a routerLink="events" class="list-group-item" routerLinkActive="active" *ngIf="accessEvents">
<a routerLink="events" class="list-group-item" routerLinkActive="active" *ngIf="organization.isAdmin && accessEvents">
{{'eventLogs' | i18n}}
</a>
</div>

View File

@@ -6,11 +6,14 @@ import { ActivatedRoute } from '@angular/router';
import { UserService } from 'jslib/abstractions/user.service';
import { Organization } from 'jslib/models/domain/organization';
@Component({
selector: 'app-org-manage',
templateUrl: 'manage.component.html',
})
export class ManageComponent implements OnInit {
organization: Organization;
accessGroups = false;
accessEvents = false;
@@ -18,9 +21,9 @@ export class ManageComponent implements OnInit {
ngOnInit() {
this.route.parent.params.subscribe(async (params) => {
const organization = await this.userService.getOrganization(params.organizationId);
this.accessEvents = organization.useEvents;
this.accessGroups = organization.useGroups;
this.organization = await this.userService.getOrganization(params.organizationId);
this.accessEvents = this.organization.useEvents;
this.accessGroups = this.organization.useGroups;
});
}
}

View File

@@ -48,6 +48,7 @@
<td>
<span *ngIf="u.type === organizationUserType.Owner">{{'owner' | i18n}}</span>
<span *ngIf="u.type === organizationUserType.Admin">{{'admin' | i18n}}</span>
<span *ngIf="u.type === organizationUserType.Manager">{{'manager' | i18n}}</span>
<span *ngIf="u.type === organizationUserType.User">{{'user' | i18n}}</span>
</td>
<td class="table-list-options">

View File

@@ -5,7 +5,10 @@ import {
ViewChild,
ViewContainerRef,
} from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import {
ActivatedRoute,
Router,
} from '@angular/router';
import { ToasterService } from 'angular2-toaster';
import { Angulartics2 } from 'angulartics2';
@@ -58,12 +61,16 @@ export class PeopleComponent implements OnInit {
private i18nService: I18nService, private componentFactoryResolver: ComponentFactoryResolver,
private platformUtilsService: PlatformUtilsService, private analytics: Angulartics2,
private toasterService: ToasterService, private cryptoService: CryptoService,
private userService: UserService) { }
private userService: UserService, private router: Router) { }
async ngOnInit() {
this.route.parent.parent.params.subscribe(async (params) => {
this.organizationId = params.organizationId;
const organization = await this.userService.getOrganization(this.organizationId);
if (!organization.isAdmin) {
this.router.navigate(['../collections'], { relativeTo: this.route });
return;
}
this.accessEvents = organization.useEvents;
this.accessGroups = organization.useGroups;
await this.load();

View File

@@ -30,6 +30,13 @@
<small>{{'userDesc' | i18n}}</small>
</label>
</div>
<div class="form-check mt-2 form-check-block">
<input class="form-check-input" type="radio" name="userType" id="userTypeManager" [value]="organizationUserType.Manager" [(ngModel)]="type">
<label class="form-check-label" for="userTypeManager">
{{'manager' | i18n}}
<small>{{'managerDesc' | i18n}}</small>
</label>
</div>
<div class="form-check mt-2 form-check-block">
<input class="form-check-input" type="radio" name="userType" id="userTypeAdmin" [value]="organizationUserType.Admin" [(ngModel)]="type">
<label class="form-check-label" for="userTypeAdmin">
@@ -86,10 +93,10 @@
<tbody>
<tr *ngFor="let c of collections; let i = index">
<td class="table-list-checkbox" (click)="check(c)">
<input type="checkbox" [(ngModel)]="c.checked" name="Collection[{{i}}].Checked">
<input type="checkbox" [(ngModel)]="c.checked" name="Collection[{{i}}].Checked" appStopProp>
</td>
<td (click)="check(c)">
<span appStopProp>{{c.name}}</span>
{{c.name}}
</td>
<td class="text-center">
<input type="checkbox" [(ngModel)]="c.readOnly" name="Collection[{{i}}].ReadOnly" [disabled]="!c.checked">

View File

@@ -22,10 +22,10 @@
<tbody>
<tr *ngFor="let g of groups; let i = index">
<td class="table-list-checkbox" (click)="check(g)">
<input type="checkbox" [(ngModel)]="g.checked" name="Groups[{{i}}].Checked">
<input type="checkbox" [(ngModel)]="g.checked" name="Groups[{{i}}].Checked" appStopProp>
</td>
<td (click)="check(g)">
<span appStopProp>{{g.name}}</span>
{{g.name}}
</td>
</tr>
</tbody>

View File

@@ -43,6 +43,8 @@
<div class="card-body">
<p>{{'dangerZoneDesc' | i18n}}</p>
<button type="button" class="btn btn-outline-danger" (click)="deleteOrganization()">{{'deleteOrganization' | i18n}}</button>
<button type="button" class="btn btn-outline-danger" (click)="purgeVault()">{{'purgeVault' | i18n}}</button>
</div>
</div>
<ng-template #deleteOrganizationTemplate></ng-template>
<ng-template #purgeOrganizationTemplate></ng-template>

View File

@@ -17,6 +17,7 @@ import { OrganizationUpdateRequest } from 'jslib/models/request/organizationUpda
import { OrganizationResponse } from 'jslib/models/response/organizationResponse';
import { ModalComponent } from '../../modal.component';
import { PurgeVaultComponent } from '../../settings/purge-vault.component';
import { DeleteOrganizationComponent } from './delete-organization.component';
@Component({
@@ -25,6 +26,7 @@ import { DeleteOrganizationComponent } from './delete-organization.component';
})
export class AccountComponent {
@ViewChild('deleteOrganizationTemplate', { read: ViewContainerRef }) deleteModalRef: ViewContainerRef;
@ViewChild('purgeOrganizationTemplate', { read: ViewContainerRef }) purgeModalRef: ViewContainerRef;
loading = true;
org: OrganizationResponse;
@@ -78,4 +80,19 @@ export class AccountComponent {
this.modal = null;
});
}
purgeVault() {
if (this.modal != null) {
this.modal.close();
}
const factory = this.componentFactoryResolver.resolveComponentFactory(ModalComponent);
this.modal = this.purgeModalRef.createComponent(factory).instance;
const childComponent = this.modal.show<PurgeVaultComponent>(PurgeVaultComponent, this.purgeModalRef);
childComponent.organizationId = this.organizationId;
this.modal.onClosed.subscribe(async () => {
this.modal = null;
});
}
}

View File

@@ -1,6 +1,3 @@
import { ToasterService } from 'angular2-toaster';
import { Angulartics2 } from 'angulartics2';
import { Component } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
@@ -18,11 +15,10 @@ import { ExportComponent as BaseExportComponent } from '../../tools/export.compo
export class ExportComponent extends BaseExportComponent {
organizationId: string;
constructor(analytics: Angulartics2, toasterService: ToasterService,
cryptoService: CryptoService, i18nService: I18nService,
constructor(cryptoService: CryptoService, i18nService: I18nService,
platformUtilsService: PlatformUtilsService, exportService: ExportService,
private route: ActivatedRoute) {
super(analytics, toasterService, cryptoService, i18nService, platformUtilsService, exportService);
super(cryptoService, i18nService, platformUtilsService, exportService);
}
ngOnInit() {

View File

@@ -1,14 +1,9 @@
import {
Component,
OnInit,
} from '@angular/core';
import { ToasterService } from 'angular2-toaster';
import { Angulartics2 } from 'angulartics2';
import { Component } from '@angular/core';
import { ApiService } from 'jslib/abstractions/api.service';
import { AuditService } from 'jslib/abstractions/audit.service';
import { CipherService } from 'jslib/abstractions/cipher.service';
import { CollectionService } from 'jslib/abstractions/collection.service';
import { FolderService } from 'jslib/abstractions/folder.service';
import { I18nService } from 'jslib/abstractions/i18n.service';
import { MessagingService } from 'jslib/abstractions/messaging.service';
@@ -21,6 +16,7 @@ import { UserService } from 'jslib/abstractions/user.service';
import { CipherData } from 'jslib/models/data/cipherData';
import { Cipher } from 'jslib/models/domain/cipher';
import { Organization } from 'jslib/models/domain/organization';
import { CipherCreateRequest } from 'jslib/models/request/cipherCreateRequest';
import { CipherRequest } from 'jslib/models/request/cipherRequest';
import { AddEditComponent as BaseAddEditComponent } from '../../vault/add-edit.component';
@@ -29,20 +25,26 @@ import { AddEditComponent as BaseAddEditComponent } from '../../vault/add-edit.c
selector: 'app-org-vault-add-edit',
templateUrl: '../../vault/add-edit.component.html',
})
export class AddEditComponent extends BaseAddEditComponent implements OnInit {
export class AddEditComponent extends BaseAddEditComponent {
organization: Organization;
originalCipher: Cipher = null;
constructor(cipherService: CipherService, folderService: FolderService,
i18nService: I18nService, platformUtilsService: PlatformUtilsService,
analytics: Angulartics2, toasterService: ToasterService,
auditService: AuditService, stateService: StateService,
userService: UserService, totpService: TotpService,
passwordGenerationService: PasswordGenerationService, private apiService: ApiService,
userService: UserService, collectionService: CollectionService,
totpService: TotpService, passwordGenerationService: PasswordGenerationService,
private apiService: ApiService,
messagingService: MessagingService) {
super(cipherService, folderService, i18nService, platformUtilsService, analytics,
toasterService, auditService, stateService, userService, totpService, passwordGenerationService,
messagingService);
super(cipherService, folderService, i18nService, platformUtilsService, auditService, stateService,
userService, collectionService, totpService, passwordGenerationService, messagingService);
}
protected loadCollections() {
if (!this.organization.isAdmin) {
return super.loadCollections();
}
return Promise.resolve(this.collections);
}
protected async loadCipher() {
@@ -56,9 +58,6 @@ export class AddEditComponent extends BaseAddEditComponent implements OnInit {
}
protected encryptCipher() {
if (!this.editMode) {
this.cipher.organizationId = this.organization.id;
}
if (!this.organization.isAdmin) {
return super.encryptCipher();
}
@@ -69,10 +68,11 @@ export class AddEditComponent extends BaseAddEditComponent implements OnInit {
if (!this.organization.isAdmin) {
return super.saveCipher(cipher);
}
const request = new CipherRequest(cipher);
if (this.editMode) {
const request = new CipherRequest(cipher);
return this.apiService.putCipherAdmin(this.cipherId, request);
} else {
const request = new CipherCreateRequest(cipher);
return this.apiService.postCipherAdmin(request);
}
}

View File

@@ -1,8 +1,5 @@
import { Component } from '@angular/core';
import { ToasterService } from 'angular2-toaster';
import { Angulartics2 } from 'angulartics2';
import { ApiService } from 'jslib/abstractions/api.service';
import { CipherService } from 'jslib/abstractions/cipher.service';
import { CryptoService } from 'jslib/abstractions/crypto.service';
@@ -23,12 +20,10 @@ import { AttachmentsComponent as BaseAttachmentsComponent } from '../../vault/at
export class AttachmentsComponent extends BaseAttachmentsComponent {
organization: Organization;
constructor(cipherService: CipherService, analytics: Angulartics2,
toasterService: ToasterService, i18nService: I18nService,
constructor(cipherService: CipherService, i18nService: I18nService,
cryptoService: CryptoService, userService: UserService,
platformUtilsService: PlatformUtilsService, private apiService: ApiService) {
super(cipherService, analytics, toasterService, i18nService, cryptoService, userService,
platformUtilsService);
super(cipherService, i18nService, cryptoService, userService, platformUtilsService);
}
protected async loadCipher() {

View File

@@ -41,7 +41,7 @@ export class CiphersComponent extends BaseCiphersComponent {
async load(filter: (cipher: CipherView) => boolean = null) {
if (!this.organization.isAdmin) {
await super.load();
await super.load(filter);
return;
}
this.accessEvents = this.organization.useEvents;

View File

@@ -1,12 +1,10 @@
import { Component } from '@angular/core';
import { ToasterService } from 'angular2-toaster';
import { Angulartics2 } from 'angulartics2';
import { ApiService } from 'jslib/abstractions/api.service';
import { CipherService } from 'jslib/abstractions/cipher.service';
import { CollectionService } from 'jslib/abstractions/collection.service';
import { I18nService } from 'jslib/abstractions/i18n.service';
import { PlatformUtilsService } from 'jslib/abstractions/platformUtils.service';
import { CipherData } from 'jslib/models/data/cipherData';
import { Cipher } from 'jslib/models/domain/cipher';
@@ -22,10 +20,10 @@ import { CollectionsComponent as BaseCollectionsComponent } from '../../vault/co
export class CollectionsComponent extends BaseCollectionsComponent {
organization: Organization;
constructor(collectionService: CollectionService, analytics: Angulartics2,
toasterService: ToasterService, i18nService: I18nService,
cipherService: CipherService, private apiService: ApiService) {
super(collectionService, analytics, toasterService, i18nService, cipherService);
constructor(collectionService: CollectionService, platformUtilsService: PlatformUtilsService,
i18nService: I18nService, cipherService: CipherService,
private apiService: ApiService) {
super(collectionService, platformUtilsService, i18nService, cipherService);
}
protected async loadCipher() {

View File

@@ -30,6 +30,7 @@ export class GroupingsComponent extends BaseGroupingsComponent {
await super.loadCollections(this.organization.id);
return;
}
const collections = await this.apiService.getCollections(this.organization.id);
if (collections != null && collections.data != null && collections.data.length) {
const collectionDomains = collections.data.map((r) =>
@@ -45,5 +46,6 @@ export class GroupingsComponent extends BaseGroupingsComponent {
unassignedCollection.organizationId = this.organization.id;
unassignedCollection.readOnly = true;
this.collections.push(unassignedCollection);
this.nestedCollections = await this.collectionService.getAllNested(this.collections);
}
}

View File

@@ -13,7 +13,7 @@
<i *ngIf="actionSpinner.loading" class="fa fa-spinner fa-spin text-muted" title="{{'loading' | i18n}}"></i>
</small>
</h1>
<button type="button" class="btn btn-outline-primary btn-sm ml-auto" (click)="addCipher()" *ngIf="showAdd">
<button type="button" class="btn btn-outline-primary btn-sm ml-auto" (click)="addCipher()">
<i class="fa fa-plus fa-fw"></i>{{'addItem' | i18n}}
</button>
</div>

View File

@@ -52,7 +52,6 @@ export class VaultComponent implements OnInit, OnDestroy {
organization: Organization;
collectionId: string;
type: CipherType;
showAdd = true;
private modal: ModalComponent = null;
@@ -66,7 +65,6 @@ export class VaultComponent implements OnInit, OnDestroy {
ngOnInit() {
this.route.parent.params.subscribe(async (params) => {
this.organization = await this.userService.getOrganization(params.organizationId);
this.showAdd = this.organization.isAdmin;
this.groupingsComponent.organization = this.organization;
this.ciphersComponent.organization = this.organization;
@@ -146,7 +144,7 @@ export class VaultComponent implements OnInit, OnDestroy {
}
async filterCollection(collectionId: string, load = false) {
this.ciphersComponent.showAddNew = false;
this.ciphersComponent.showAddNew = true;
this.groupingsComponent.searchPlaceholder = this.i18nService.t('searchCollection');
const filter = (c: CipherView) => {
if (collectionId === 'unassigned') {
@@ -226,7 +224,14 @@ export class VaultComponent implements OnInit, OnDestroy {
addCipher() {
const component = this.editCipher(null);
component.organizationId = this.organization.id;
component.type = this.type;
if (this.organization.isAdmin) {
component.collections = this.groupingsComponent.collections.filter((c) => !c.readOnly);
}
if (this.collectionId != null) {
component.collectionIds = [this.collectionId];
}
}
editCipher(cipher: CipherView) {

View File

@@ -47,8 +47,8 @@ export class EventService {
case EventType.User_ChangedPassword:
msg = this.i18nService.t('changedPassword');
break;
case EventType.User_Enabled2fa:
msg = this.i18nService.t('enabled2fa');
case EventType.User_Updated2fa:
msg = this.i18nService.t('enabledUpdated2fa');
break;
case EventType.User_Disabled2fa:
msg = this.i18nService.t('disabled2fa');
@@ -124,6 +124,9 @@ export class EventService {
case EventType.Organization_Updated:
msg = this.i18nService.t('editedOrgSettings');
break;
case EventType.Organization_PurgedVault:
msg = this.i18nService.t('purgedOrganizationVault');
break;
default:
break;
}

View File

@@ -83,7 +83,7 @@ const i18nService = new I18nService(window.navigator.language, 'locales');
const stateService = new StateService();
const broadcasterService = new BroadcasterService();
const messagingService = new BroadcasterMessagingService(broadcasterService);
const platformUtilsService = new WebPlatformUtilsService(i18nService);
const platformUtilsService = new WebPlatformUtilsService(i18nService, messagingService);
const storageService: StorageServiceAbstraction = new HtmlStorageService(platformUtilsService);
const secureStorageService: StorageServiceAbstraction = new MemoryStorageService();
const cryptoFunctionService: CryptoFunctionServiceAbstraction = new WebCryptoFunctionService(window,
@@ -98,7 +98,7 @@ const userService = new UserService(tokenService, storageService);
const settingsService = new SettingsService(userService, storageService);
export let searchService: SearchService = null;
const cipherService = new CipherService(cryptoService, userService, settingsService,
apiService, storageService, i18nService, platformUtilsService, () => searchService);
apiService, storageService, i18nService, () => searchService);
const folderService = new FolderService(cryptoService, userService, apiService, storageService,
i18nService, cipherService);
const collectionService = new CollectionService(cryptoService, userService, storageService, i18nService);
@@ -110,7 +110,7 @@ const syncService = new SyncService(userService, apiService, settingsService,
async (expired: boolean) => messagingService.send('logout', { expired: expired }));
const passwordGenerationService = new PasswordGenerationService(cryptoService, storageService);
const totpService = new TotpService(storageService, cryptoFunctionService);
const containerService = new ContainerService(cryptoService, platformUtilsService);
const containerService = new ContainerService(cryptoService);
const authService = new AuthService(cryptoService, apiService,
userService, tokenService, appIdService, i18nService, platformUtilsService, messagingService);
const exportService = new ExportService(folderService, cipherService, apiService);
@@ -134,7 +134,7 @@ export function initFactory(): Function {
environmentService.notificationsUrl = isDev ? 'http://localhost:61840' :
'https://notifications.bitwarden.com'; // window.location.origin + '/notifications';
}
await apiService.setUrls({
apiService.setUrls({
base: isDev ? null : window.location.origin,
api: isDev ? 'http://localhost:4000' : null,
identity: isDev ? 'http://localhost:33656' : null,
@@ -151,7 +151,7 @@ export function initFactory(): Function {
lockService.init(true);
const locale = await storageService.get<string>(ConstantsService.localeKey);
await i18nService.init(locale);
await authService.init();
authService.init();
const htmlEl = window.document.documentElement;
htmlEl.classList.add('locale_' + i18nService.translationLocale);
let theme = await storageService.get<string>(ConstantsService.themeKey);

View File

@@ -121,7 +121,7 @@
<label for="additionalStorage">{{'additionalStorageGb' | i18n}}</label>
<input id="additionalStorage" class="form-control" type="number" name="AdditionalStorageGb" [(ngModel)]="additionalStorage"
min="0" max="99" step="1" placeholder="{{'additionalStorageGbDesc' | i18n}}">
<small class="text-muted form-text">{{'additionalStorageDesc' | i18n : '1 GB' : (storageGb.price | currency:'$')}}</small>
<small class="text-muted form-text">{{'additionalStorageIntervalDesc' | i18n : '1 GB' : (storageGb.price | currency:'$') : ('month' | i18n)}}</small>
</div>
</div>
<h2 class="spaced-header">{{'summary' | i18n}}</h2>

View File

@@ -55,7 +55,7 @@
<label for="additionalStorage">{{'additionalStorageGb' | i18n}}</label>
<input id="additionalStorage" class="form-control" type="number" name="AdditionalStorageGb" [(ngModel)]="additionalStorage"
min="0" max="99" step="1" placeholder="{{'additionalStorageGbDesc' | i18n}}">
<small class="text-muted form-text">{{'additionalStorageDesc' | i18n : '1 GB' : (storageGbPrice | currency:'$')}}</small>
<small class="text-muted form-text">{{'additionalStorageIntervalDesc' | i18n : '1 GB' : (storageGbPrice | currency:'$') : ('year' | i18n)}}</small>
</div>
</div>
<h2 class="spaced-header">{{'summary' | i18n}}</h2>

View File

@@ -8,7 +8,7 @@
</button>
</div>
<div class="modal-body">
<p>{{'purgeVaultDesc' | i18n}}</p>
<p>{{(organizationId ? 'purgeOrgVaultDesc' : 'purgeVaultDesc') | i18n}}</p>
<app-callout type="warning">{{'purgeVaultWarning' | i18n}}</app-callout>
<label for="masterPassword">{{'masterPass' | i18n}}</label>
<input id="masterPassword" type="password" name="MasterPasswordHash" class="form-control" [(ngModel)]="masterPassword" required

View File

@@ -1,4 +1,7 @@
import { Component } from '@angular/core';
import {
Component,
Input,
} from '@angular/core';
import { Router } from '@angular/router';
import { ToasterService } from 'angular2-toaster';
@@ -15,6 +18,8 @@ import { PasswordVerificationRequest } from 'jslib/models/request/passwordVerifi
templateUrl: 'purge-vault.component.html',
})
export class PurgeVaultComponent {
@Input() organizationId?: string = null;
masterPassword: string;
formPromise: Promise<any>;
@@ -32,11 +37,17 @@ export class PurgeVaultComponent {
const request = new PasswordVerificationRequest();
request.masterPasswordHash = await this.cryptoService.hashPassword(this.masterPassword, null);
try {
this.formPromise = this.apiService.postPurgeCiphers(request);
this.formPromise = this.apiService.postPurgeCiphers(request, this.organizationId);
await this.formPromise;
this.analytics.eventTrack.next({ action: 'Purged Vault' });
this.analytics.eventTrack.next({
action: this.organizationId != null ? 'Purged Organization Vault' : 'Purged Vault',
});
this.toasterService.popAsync('success', null, this.i18nService.t('vaultPurged'));
this.router.navigate(['vault']);
if (this.organizationId != null) {
this.router.navigate(['organizations', this.organizationId, 'vault']);
} else {
this.router.navigate(['vault']);
}
} catch { }
}
}

View File

@@ -1,5 +1,5 @@
<div class="modal fade">
<div class="modal-dialog">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<h2 class="modal-title">
@@ -23,43 +23,66 @@
<li>{{'twoFactorU2fSupportWeb' | i18n}}</li>
</ul>
</app-callout>
<ng-container *ngIf="!enabled">
<img src="../../images/two-factor/4.png" class="float-right ml-5" alt="">
<p>{{'twoFactorU2fAdd' | i18n}}:</p>
<ol>
<li>{{'twoFactorU2fPlugIn' | i18n}}</li>
<li>{{'twoFactorU2fTouchButton' | i18n}}</li>
</ol>
<hr>
<div class="text-center">
<ng-container *ngIf="u2fListening">
<p>
<i class="fa fa-spinner fa-spin fa-2x text-muted"></i>
</p>
{{'twoFactorU2fWaiting' | i18n}}...
</ng-container>
<ng-container *ngIf="u2fResponse">
<p>
<i class="fa fa-check-circle fa-2x text-success"></i>
</p>
{{'twoFactorU2fClickEnable' | i18n}}
</ng-container>
<ng-container *ngIf="u2fError">
<p>
<i class="fa fa-warning fa-2x text-danger"></i>
</p>
{{'twoFactorU2fProblemReading' | i18n}}
<img src="../../images/two-factor/4.png" class="float-right ml-5" alt="">
<ul class="fa-ul">
<li *ngFor="let k of keys; let i = index" #removeKeyBtn [appApiAction]="k.removePromise">
<i class="fa-li fa fa-key"></i>
<strong *ngIf="!k.configured || !k.name">{{'u2fkeyX' | i18n : i + 1}}</strong>
<strong *ngIf="k.configured && k.name">{{k.name}}</strong>
<i class="fa fa-fw" [ngClass]="{'fa-check text-success': !k.compromised, 'fa-exclamation-triangle text-warning': k.compromised}"
*ngIf="k.configured && !removeKeyBtn.loading" title="{{(k.compromised ? 'keyCompromised' : 'enabled') | i18n}}"></i>
<ng-container *ngIf="keysConfiguredCount > 1 && k.configured">
<i class="fa fa-spin fa-spinner text-muted fa-fw" title="{{'loading' | i18n}}" *ngIf="removeKeyBtn.loading"></i>
-
<a href="#" appStopClick (click)="remove(k)">{{'remove' | i18n}}</a>
</ng-container>
</li>
</ul>
<hr>
<p>{{'twoFactorU2fAdd' | i18n}}:</p>
<ol>
<li>{{'twoFactorU2fGiveName' | i18n}}</li>
<li>{{'twoFactorU2fPlugInReadKey' | i18n}}</li>
<li>{{'twoFactorU2fTouchButton' | i18n}}</li>
<li>{{'twoFactorU2fSaveForm' | i18n}}</li>
</ol>
<div class="row">
<div class="form-group col-6">
<label for="name">{{'name' | i18n}}</label>
<input id="name" type="text" name="Name" class="form-control" [(ngModel)]="name" [disabled]="!keyIdAvailable">
</div>
</div>
<button type="button" (click)="readKey()" class="btn btn-outline-secondary mr-2" [disabled]="readKeyBtn.loading || u2fListening || !keyIdAvailable"
#readKeyBtn [appApiAction]="challengePromise">
{{'readKey' | i18n}}
</button>
<ng-container *ngIf="readKeyBtn.loading">
<i class="fa fa-spinner fa-spin text-muted"></i>
</ng-container>
<ng-container *ngIf="!readKeyBtn.loading">
<ng-container *ngIf="u2fListening">
<i class="fa fa-spinner fa-spin text-muted"></i>
{{'twoFactorU2fWaiting' | i18n}}...
</ng-container>
<ng-container *ngIf="u2fResponse">
<i class="fa fa-check-circle text-success"></i>
{{'twoFactorU2fClickSave' | i18n}}
</ng-container>
<ng-container *ngIf="u2fError">
<i class="fa fa-warning text-danger"></i>
{{'twoFactorU2fProblemReadingTryAgain' | i18n}}
</ng-container>
</ng-container>
</div>
<div class="modal-footer">
<button type="submit" class="btn btn-primary" [disabled]="form.loading || (!enabled && !u2fResponse)">
<button type="submit" class="btn btn-primary" [disabled]="form.loading || !u2fResponse">
<i class="fa fa-spinner fa-spin" *ngIf="form.loading" title="{{'loading' | i18n}}"></i>
<ng-container *ngIf="!form.loading">
<span *ngIf="!enabled">{{'enable' | i18n}}</span>
<span *ngIf="enabled">{{'disable' | i18n}}</span>
</ng-container>
<span *ngIf="!form.loading">{{'save' | i18n}}</span>
</button>
<button #disableBtn type="button" class="btn btn-outline-secondary btn-submit" [appApiAction]="disablePromise"
[disabled]="disableBtn.loading" (click)="disable()" *ngIf="enabled">
<i class="fa fa-spinner fa-spin" title="{{'loading' | i18n}}"></i>
<span>{{'disableAllKeys' | i18n}}</span>
</button>
<button type="button" class="btn btn-outline-secondary" data-dismiss="modal">{{'close' | i18n}}</button>
</div>

View File

@@ -1,5 +1,6 @@
import {
Component,
NgZone,
OnDestroy,
OnInit,
} from '@angular/core';
@@ -12,6 +13,9 @@ import { I18nService } from 'jslib/abstractions/i18n.service';
import { PlatformUtilsService } from 'jslib/abstractions/platformUtils.service';
import { TwoFactorProviderType } from 'jslib/enums/twoFactorProviderType';
import { PasswordVerificationRequest } from 'jslib/models/request/passwordVerificationRequest';
import { UpdateTwoFactorU2fDeleteRequest } from 'jslib/models/request/updateTwoFactorU2fDeleteRequest';
import { UpdateTwoFactorU2fRequest } from 'jslib/models/request/updateTwoFactorU2fRequest';
import {
ChallengeResponse,
@@ -26,18 +30,21 @@ import { TwoFactorBaseComponent } from './two-factor-base.component';
})
export class TwoFactorU2fComponent extends TwoFactorBaseComponent implements OnInit, OnDestroy {
type = TwoFactorProviderType.U2f;
u2fChallenge: ChallengeResponse;
name: string;
keys: any[];
keyIdAvailable: number = null;
keysConfiguredCount = 0;
u2fError: boolean;
u2fListening: boolean;
u2fResponse: string;
challengePromise: Promise<ChallengeResponse>;
formPromise: Promise<any>;
private closed = false;
private u2fScript: HTMLScriptElement;
constructor(apiService: ApiService, i18nService: I18nService,
analytics: Angulartics2, toasterService: ToasterService,
platformUtilsService: PlatformUtilsService) {
platformUtilsService: PlatformUtilsService, private ngZone: NgZone) {
super(apiService, i18nService, analytics, toasterService, platformUtilsService);
this.u2fScript = window.document.createElement('script');
this.u2fScript.src = 'scripts/u2f.js';
@@ -49,28 +56,24 @@ export class TwoFactorU2fComponent extends TwoFactorBaseComponent implements OnI
}
ngOnDestroy() {
this.closed = true;
window.document.body.removeChild(this.u2fScript);
}
auth(authResponse: any) {
super.auth(authResponse);
this.processResponse(authResponse.response);
this.readDevice();
}
submit() {
if (this.enabled) {
return super.disable(this.formPromise);
} else {
return this.enable();
if (this.u2fResponse == null || this.keyIdAvailable == null) {
// Should never happen.
return Promise.reject();
}
}
protected enable() {
const request = new UpdateTwoFactorU2fRequest();
request.masterPasswordHash = this.masterPasswordHash;
request.deviceResponse = this.u2fResponse;
request.id = this.keyIdAvailable;
request.name = this.name;
return super.enable(async () => {
this.formPromise = this.apiService.putTwoFactorU2f(request);
@@ -79,38 +82,97 @@ export class TwoFactorU2fComponent extends TwoFactorBaseComponent implements OnI
});
}
private readDevice() {
if (this.closed || this.enabled) {
disable() {
return super.disable(this.formPromise);
}
async remove(key: any) {
if (this.keysConfiguredCount <= 1 || key.removePromise != null) {
return;
}
const name = key.name != null ? key.name : this.i18nService.t('u2fkeyX', key.id);
const confirmed = await this.platformUtilsService.showDialog(
this.i18nService.t('removeU2fConfirmation'), name,
this.i18nService.t('yes'), this.i18nService.t('no'), 'warning');
if (!confirmed) {
return;
}
const request = new UpdateTwoFactorU2fDeleteRequest();
request.id = key.id;
request.masterPasswordHash = this.masterPasswordHash;
try {
key.removePromise = this.apiService.deleteTwoFactorU2f(request);
const response = await key.removePromise;
key.removePromise = null;
await this.processResponse(response);
} catch { }
}
async readKey() {
if (this.keyIdAvailable == null) {
return;
}
const request = new PasswordVerificationRequest();
request.masterPasswordHash = this.masterPasswordHash;
try {
this.challengePromise = this.apiService.getTwoFactorU2fChallenge(request);
const challenge = await this.challengePromise;
this.readDevice(challenge);
} catch { }
}
private readDevice(u2fChallenge: ChallengeResponse) {
// tslint:disable-next-line
console.log('listening for key...');
this.resetU2f(true);
(window as any).u2f.register(u2fChallenge.appId, [{
version: u2fChallenge.version,
challenge: u2fChallenge.challenge,
}], [], (data: any) => {
this.ngZone.run(() => {
this.u2fListening = false;
if (data.errorCode) {
this.u2fError = true;
// tslint:disable-next-line
console.log('error: ' + data.errorCode);
return;
}
this.u2fResponse = JSON.stringify(data);
});
}, 15);
}
private resetU2f(listening = false) {
this.u2fResponse = null;
this.u2fError = false;
this.u2fListening = true;
(window as any).u2f.register(this.u2fChallenge.appId, [{
version: this.u2fChallenge.version,
challenge: this.u2fChallenge.challenge,
}], [], (data: any) => {
this.u2fListening = false;
if (data.errorCode === 5) {
this.readDevice();
return;
} else if (data.errorCode) {
this.u2fError = true;
// tslint:disable-next-line
console.log('error: ' + data.errorCode);
return;
}
this.u2fResponse = JSON.stringify(data);
}, 10);
this.u2fListening = listening;
}
private processResponse(response: TwoFactorU2fResponse) {
this.u2fChallenge = response.challenge;
this.resetU2f();
this.keys = [];
this.keyIdAvailable = null;
this.name = null;
this.keysConfiguredCount = 0;
for (let i = 1; i <= 5; i++) {
if (response.keys != null) {
const key = response.keys.filter((k) => k.id === i);
if (key.length > 0) {
this.keysConfiguredCount++;
this.keys.push({
id: i, name: key[0].name,
configured: true,
compromised: key[0].compromised,
removePromise: null,
});
continue;
}
}
this.keys.push({ id: i, name: null, configured: false, compromised: false, removePromise: null });
if (this.keyIdAvailable == null) {
this.keyIdAvailable = i;
}
}
this.enabled = response.enabled;
}
}

View File

@@ -1,6 +1,3 @@
import { ToasterService } from 'angular2-toaster';
import { Angulartics2 } from 'angulartics2';
import { Component } from '@angular/core';
import { CryptoService } from 'jslib/abstractions/crypto.service';
@@ -15,16 +12,14 @@ import { ExportComponent as BaseExportComponent } from 'jslib/angular/components
templateUrl: 'export.component.html',
})
export class ExportComponent extends BaseExportComponent {
constructor(analytics: Angulartics2, toasterService: ToasterService,
cryptoService: CryptoService, i18nService: I18nService,
constructor(cryptoService: CryptoService, i18nService: I18nService,
platformUtilsService: PlatformUtilsService, exportService: ExportService) {
super(analytics, toasterService, cryptoService, i18nService, platformUtilsService,
exportService, window);
super(cryptoService, i18nService, platformUtilsService, exportService, window);
}
protected saved() {
super.saved();
this.masterPassword = null;
this.toasterService.popAsync('success', null, this.i18nService.t('exportSuccess'));
this.platformUtilsService.showToast('success', null, this.i18nService.t('exportSuccess'));
}
}

View File

@@ -153,6 +153,15 @@
check all of the fields, change the "Output format" to "CSV", and then click the "Start" button to save the CSV
file.
</ng-container>
<ng-container *ngIf="format === 'passpackcsv'">
Log into the Passpack website vault and navigate to "Settings" &rarr; "Export", then click the "Download" button to save
the CSV file.
</ng-container>
<ng-container *ngIf="format === 'passmanjson'">
Open your Passman vault and click on "Settings" in the bottom left corner. In the "Settings" window switch to the
"Export credentials" tab and choose "JSON" as the export type. Enter your vault's passphrase and click the "Export"
button to save the JSON file.
</ng-container>
</app-callout>
<div class="row">
<div class="col-6">

View File

@@ -129,7 +129,7 @@ export class ImportComponent implements OnInit {
reader.onload = (evt) => {
if (this.format === 'lastpasscsv' && file.type === 'text/html') {
const parser = new DOMParser();
const doc = parser.parseFromString(evt.target.result, 'text/html');
const doc = parser.parseFromString((evt.target as any).result, 'text/html');
const pre = doc.querySelector('pre');
if (pre != null) {
resolve(pre.textContent);
@@ -139,7 +139,7 @@ export class ImportComponent implements OnInit {
return;
}
resolve(evt.target.result);
resolve((evt.target as any).result);
};
reader.onerror = () => {
reject();

View File

@@ -1,6 +1,3 @@
import { ToasterService } from 'angular2-toaster';
import { Angulartics2 } from 'angulartics2';
import { Component } from '@angular/core';
import { I18nService } from 'jslib/abstractions/i18n.service';
@@ -16,9 +13,8 @@ import {
templateUrl: 'password-generator-history.component.html',
})
export class PasswordGeneratorHistoryComponent extends BasePasswordGeneratorHistoryComponent {
constructor(passwordGenerationService: PasswordGenerationService, analytics: Angulartics2,
platformUtilsService: PlatformUtilsService, i18nService: I18nService,
toasterService: ToasterService) {
super(passwordGenerationService, analytics, platformUtilsService, i18nService, toasterService, window);
constructor(passwordGenerationService: PasswordGenerationService, platformUtilsService: PlatformUtilsService,
i18nService: I18nService) {
super(passwordGenerationService, platformUtilsService, i18nService, window);
}
}

View File

@@ -6,42 +6,69 @@
{{password}}
</div>
</div>
<div class="row">
<div class="form-group col-4">
<label for="length">{{'length' | i18n}}</label>
<input id="length" class="form-control" type="number" min="5" max="128" [(ngModel)]="options.length" (blur)="saveOptions()">
</div>
<div class="form-group col-4">
<label for="min-number">{{'minNumbers' | i18n}}</label>
<input id="min-number" class="form-control" type="number" min="0" max="9" (input)="saveOptions()" [(ngModel)]="options.minNumber">
</div>
<div class="form-group col-4">
<label for="min-special">{{'minSpecial' | i18n}}</label>
<input id="min-special" class="form-control" type="number" min="0" max="9" (input)="saveOptions()" [(ngModel)]="options.minSpecial">
</div>
</div>
<div class="form-group">
<div class="form-check">
<input id="uppercase" class="form-check-input" type="checkbox" (change)="saveOptions()" [(ngModel)]="options.uppercase">
<label for="uppercase" class="form-check-label">A-Z</label>
<div class="form-check form-check-inline">
<input id="generate-password" name="type" value="password" class="form-check-input" type="radio" (change)="saveOptions()"
[(ngModel)]="options.type">
<label for="generate-password" class="form-check-label">{{'password' | i18n}}</label>
</div>
<div class="form-check">
<input id="lowercase" class="form-check-input" type="checkbox" (change)="saveOptions()" [(ngModel)]="options.lowercase">
<label for="lowercase" class="form-check-label">a-z</label>
</div>
<div class="form-check">
<input id="numbers" class="form-check-input" type="checkbox" (change)="saveOptions()" [(ngModel)]="options.number">
<label for="numbers" class="form-check-label">0-9</label>
</div>
<div class="form-check">
<input id="special" class="form-check-input" type="checkbox" (change)="saveOptions()" [(ngModel)]="options.special">
<label for="special" class="form-check-label">!@#$%^&amp;*</label>
</div>
<div class="form-check">
<input id="ambiguous" class="form-check-input" type="checkbox" (change)="saveOptions()" [(ngModel)]="avoidAmbiguous">
<label for="ambiguous" class="form-check-label">{{'ambiguous' | i18n}}</label>
<div class="form-check form-check-inline">
<input id="generate-passphrase" name="type" value="passphrase" class="form-check-input" type="radio" (change)="saveOptions()"
[(ngModel)]="options.type">
<label for="generate-passphrase" class="form-check-label">{{'passphrase' | i18n}}</label>
</div>
</div>
<div class="row" *ngIf="options.type === 'passphrase'">
<div class="form-group col-4">
<label for="num-words">{{'numWords' | i18n}}</label>
<input id="num-words" class="form-control" type="number" min="3" max="20" [(ngModel)]="options.numWords" (blur)="saveOptions()">
</div>
<div class="form-group col-4">
<label for="word-separator">{{'wordSeparator' | i18n}}</label>
<input id="word-separator" class="form-control" type="text" maxlength="1" [(ngModel)]="options.wordSeparator"
(blur)="saveOptions()">
</div>
</div>
<ng-container *ngIf="options.type === 'password'">
<div class="row">
<div class="form-group col-4">
<label for="length">{{'length' | i18n}}</label>
<input id="length" class="form-control" type="number" min="5" max="128" [(ngModel)]="options.length" (blur)="saveOptions()">
</div>
<div class="form-group col-4">
<label for="min-number">{{'minNumbers' | i18n}}</label>
<input id="min-number" class="form-control" type="number" min="0" max="9" (input)="saveOptions()"
[(ngModel)]="options.minNumber">
</div>
<div class="form-group col-4">
<label for="min-special">{{'minSpecial' | i18n}}</label>
<input id="min-special" class="form-control" type="number" min="0" max="9" (input)="saveOptions()"
[(ngModel)]="options.minSpecial">
</div>
</div>
<div class="form-group">
<div class="form-check">
<input id="uppercase" class="form-check-input" type="checkbox" (change)="saveOptions()" [(ngModel)]="options.uppercase">
<label for="uppercase" class="form-check-label">A-Z</label>
</div>
<div class="form-check">
<input id="lowercase" class="form-check-input" type="checkbox" (change)="saveOptions()" [(ngModel)]="options.lowercase">
<label for="lowercase" class="form-check-label">a-z</label>
</div>
<div class="form-check">
<input id="numbers" class="form-check-input" type="checkbox" (change)="saveOptions()" [(ngModel)]="options.number">
<label for="numbers" class="form-check-label">0-9</label>
</div>
<div class="form-check">
<input id="special" class="form-check-input" type="checkbox" (change)="saveOptions()" [(ngModel)]="options.special">
<label for="special" class="form-check-label">!@#$%^&amp;*</label>
</div>
<div class="form-check">
<input id="ambiguous" class="form-check-input" type="checkbox" (change)="saveOptions()" [(ngModel)]="avoidAmbiguous">
<label for="ambiguous" class="form-check-label">{{'ambiguous' | i18n}}</label>
</div>
</div>
</ng-container>
<div class="d-flex">
<div>
<button type="button" class="btn btn-primary" (click)="regenerate()">

View File

@@ -1,6 +1,3 @@
import { ToasterService } from 'angular2-toaster';
import { Angulartics2 } from 'angulartics2';
import {
Component,
ComponentFactoryResolver,
@@ -28,10 +25,9 @@ export class PasswordGeneratorComponent extends BasePasswordGeneratorComponent {
private modal: ModalComponent = null;
constructor(passwordGenerationService: PasswordGenerationService, analytics: Angulartics2,
platformUtilsService: PlatformUtilsService, i18nService: I18nService,
toasterService: ToasterService, private componentFactoryResolver: ComponentFactoryResolver) {
super(passwordGenerationService, analytics, platformUtilsService, i18nService, toasterService, window);
constructor(passwordGenerationService: PasswordGenerationService, platformUtilsService: PlatformUtilsService,
i18nService: I18nService, private componentFactoryResolver: ComponentFactoryResolver) {
super(passwordGenerationService, platformUtilsService, i18nService, window);
}
history() {

View File

@@ -83,7 +83,7 @@
<a href="#" appStopClick class="badge badge-primary ml-3" (click)="premiumRequired()" *ngIf="!organization && !cipher.organizationId && !canAccessPremium">
{{'premium' | i18n}}
</a>
<a href="#" appStopClick class="badge badge-primary ml-3" (click)="upgradeOrganization()" *ngIf="(organization && !organization.useTotp) || (cipher.organizationId && !cipher.organizationUseTotp)">
<a href="#" appStopClick class="badge badge-primary ml-3" (click)="upgradeOrganization()" *ngIf="(organization && !organization.useTotp) || (!organization && !canAccessPremium && cipher.organizationId && !cipher.organizationUseTotp)">
{{'upgrade' | i18n}}
</a>
</div>
@@ -366,6 +366,30 @@
</select>
</div>
</div>
<ng-container *ngIf="!editMode && !organization && ownershipOptions && ownershipOptions.length > 1">
<h3 class="mt-4">{{'ownership' | i18n}}</h3>
<div class="row">
<div class="col-5">
<label for="organizationId">{{'whoOwnsThisItem' | i18n}}</label>
<select id="organizationId" class="form-control" name="OrganizationId" [(ngModel)]="cipher.organizationId"
(change)="organizationChanged()">
<option *ngFor="let o of ownershipOptions" [ngValue]="o.value">{{o.name}}</option>
</select>
</div>
</div>
</ng-container>
<ng-container *ngIf="!editMode && cipher.organizationId">
<h3 class="mt-4">{{'collections' | i18n}}</h3>
<div *ngIf="!collections || !collections.length">
{{'noCollectionsInList' | i18n}}
</div>
<ng-container *ngIf="collections && collections.length">
<div class="form-check" *ngFor="let c of collections; let i = index">
<input class="form-check-input" type="checkbox" [(ngModel)]="c.checked" id="collection-{{i}}" name="Collection[{{i}}].Checked">
<label class="form-check-label" for="collection-{{i}}">{{c.name}}</label>
</div>
</ng-container>
</ng-container>
<ng-container *ngIf="editMode">
<div class="small text-muted mt-4">
<div>

View File

@@ -1,15 +1,10 @@
import {
Component,
OnInit,
} from '@angular/core';
import { ToasterService } from 'angular2-toaster';
import { Angulartics2 } from 'angulartics2';
import { Component } from '@angular/core';
import { CipherType } from 'jslib/enums/cipherType';
import { AuditService } from 'jslib/abstractions/audit.service';
import { CipherService } from 'jslib/abstractions/cipher.service';
import { CollectionService } from 'jslib/abstractions/collection.service';
import { FolderService } from 'jslib/abstractions/folder.service';
import { I18nService } from 'jslib/abstractions/i18n.service';
import { MessagingService } from 'jslib/abstractions/messaging.service';
@@ -26,7 +21,7 @@ import { LoginUriView } from 'jslib/models/view/loginUriView';
selector: 'app-vault-add-edit',
templateUrl: 'add-edit.component.html',
})
export class AddEditComponent extends BaseAddEditComponent implements OnInit {
export class AddEditComponent extends BaseAddEditComponent {
canAccessPremium: boolean;
totpCode: string;
totpCodeFormatted: string;
@@ -41,16 +36,17 @@ export class AddEditComponent extends BaseAddEditComponent implements OnInit {
constructor(cipherService: CipherService, folderService: FolderService,
i18nService: I18nService, platformUtilsService: PlatformUtilsService,
analytics: Angulartics2, toasterService: ToasterService,
auditService: AuditService, stateService: StateService,
protected userService: UserService, protected totpService: TotpService,
protected passwordGenerationService: PasswordGenerationService, protected messagingService: MessagingService) {
super(cipherService, folderService, i18nService, platformUtilsService, analytics,
toasterService, auditService, stateService);
userService: UserService, collectionService: CollectionService,
protected totpService: TotpService, protected passwordGenerationService: PasswordGenerationService,
protected messagingService: MessagingService) {
super(cipherService, folderService, i18nService, platformUtilsService, auditService, stateService,
userService, collectionService);
}
async ngOnInit() {
await super.load();
await super.ngOnInit();
await this.load();
this.showRevisionDate = this.cipher.passwordRevisionDisplayDate != null;
this.hasPasswordHistory = this.cipher.hasPasswordHistory;
this.cleanUp();
@@ -77,7 +73,7 @@ export class AddEditComponent extends BaseAddEditComponent implements OnInit {
return;
}
this.analytics.eventTrack.next({ action: 'Launched Login URI' });
this.platformUtilsService.eventTrack('Launched Login URI');
this.platformUtilsService.launchUri(uri.uri);
}
@@ -86,9 +82,9 @@ export class AddEditComponent extends BaseAddEditComponent implements OnInit {
return;
}
this.analytics.eventTrack.next({ action: 'Copied ' + aType });
this.platformUtilsService.eventTrack('Copied ' + aType);
this.platformUtilsService.copyToClipboard(value, { window: window });
this.toasterService.popAsync('info', null,
this.platformUtilsService.showToast('info', null,
this.i18nService.t('valueCopied', this.i18nService.t(typeI18nKey)));
}
@@ -157,5 +153,4 @@ export class AddEditComponent extends BaseAddEditComponent implements OnInit {
await this.totpUpdateCode();
}
}
}

View File

@@ -1,8 +1,5 @@
import { Component } from '@angular/core';
import { ToasterService } from 'angular2-toaster';
import { Angulartics2 } from 'angulartics2';
import { CipherService } from 'jslib/abstractions/cipher.service';
import { CryptoService } from 'jslib/abstractions/crypto.service';
import { I18nService } from 'jslib/abstractions/i18n.service';
@@ -16,11 +13,9 @@ import { AttachmentsComponent as BaseAttachmentsComponent } from 'jslib/angular/
templateUrl: 'attachments.component.html',
})
export class AttachmentsComponent extends BaseAttachmentsComponent {
constructor(cipherService: CipherService, analytics: Angulartics2,
toasterService: ToasterService, i18nService: I18nService,
constructor(cipherService: CipherService, i18nService: I18nService,
cryptoService: CryptoService, userService: UserService,
platformUtilsService: PlatformUtilsService) {
super(cipherService, analytics, toasterService, i18nService, cryptoService, userService,
platformUtilsService, window);
super(cipherService, i18nService, cryptoService, userService, platformUtilsService, window);
}
}

View File

@@ -36,10 +36,10 @@
<tbody>
<tr *ngFor="let c of collections; let i = index" (click)="check(c)">
<td class="table-list-checkbox">
<input type="checkbox" [(ngModel)]="c.checked" name="Collection[{{i}}].Checked">
<input type="checkbox" [(ngModel)]="c.checked" name="Collection[{{i}}].Checked" appStopProp>
</td>
<td>
<span appStopProp>{{c.name}}</span>
{{c.name}}
</td>
</tr>
</tbody>

View File

@@ -3,7 +3,7 @@
<tbody>
<tr *ngFor="let c of ciphers">
<td (click)="checkCipher(c)" class="table-list-checkbox" *ngIf="!organization">
<input type="checkbox" [(ngModel)]="c.checked">
<input type="checkbox" [(ngModel)]="c.checked" appStopProp>
</td>
<td (click)="checkCipher(c)" class="table-list-icon">
<app-vault-icon [cipher]="c"></app-vault-icon>

View File

@@ -30,10 +30,10 @@
<tbody>
<tr *ngFor="let c of collections; let i = index" (click)="check(c)">
<td class="table-list-checkbox">
<input type="checkbox" [(ngModel)]="c.checked" name="Collection[{{i}}].Checked">
<input type="checkbox" [(ngModel)]="c.checked" name="Collection[{{i}}].Checked" appStopProp>
</td>
<td>
<span appStopProp>{{c.name}}</span>
{{c.name}}
</td>
</tr>
</tbody>

View File

@@ -1,72 +1,31 @@
import {
Component,
EventEmitter,
Input,
OnDestroy,
OnInit,
Output,
} from '@angular/core';
import { ToasterService } from 'angular2-toaster';
import { Angulartics2 } from 'angulartics2';
import { CipherService } from 'jslib/abstractions/cipher.service';
import { CollectionService } from 'jslib/abstractions/collection.service';
import { I18nService } from 'jslib/abstractions/i18n.service';
import { PlatformUtilsService } from 'jslib/abstractions/platformUtils.service';
import { CipherView } from 'jslib/models/view/cipherView';
import { CollectionView } from 'jslib/models/view/collectionView';
import { Cipher } from 'jslib/models/domain/cipher';
import { CollectionsComponent as BaseCollectionsComponent } from 'jslib/angular/components/collections.component';
@Component({
selector: 'app-vault-collections',
templateUrl: 'collections.component.html',
})
export class CollectionsComponent implements OnInit, OnDestroy {
@Input() cipherId: string;
@Output() onSavedCollections = new EventEmitter();
formPromise: Promise<any>;
cipher: CipherView;
collectionIds: string[];
collections: CollectionView[] = [];
protected cipherDomain: Cipher;
constructor(protected collectionService: CollectionService, protected analytics: Angulartics2,
protected toasterService: ToasterService, protected i18nService: I18nService,
protected cipherService: CipherService) { }
async ngOnInit() {
this.cipherDomain = await this.loadCipher();
this.collectionIds = this.loadCipherCollections();
this.cipher = await this.cipherDomain.decrypt();
this.collections = await this.loadCollections();
this.selectAll(false);
if (this.collectionIds != null) {
this.collections.forEach((c) => {
(c as any).checked = this.collectionIds != null && this.collectionIds.indexOf(c.id) > -1;
});
}
export class CollectionsComponent extends BaseCollectionsComponent implements OnDestroy {
constructor(collectionService: CollectionService, platformUtilsService: PlatformUtilsService,
i18nService: I18nService, cipherService: CipherService) {
super(collectionService, platformUtilsService, i18nService, cipherService);
}
ngOnDestroy() {
this.selectAll(false);
}
async submit() {
this.cipherDomain.collectionIds = this.collections
.filter((c) => !!(c as any).checked)
.map((c) => c.id);
this.formPromise = this.saveCollections();
await this.formPromise;
this.onSavedCollections.emit();
this.analytics.eventTrack.next({ action: 'Edited Cipher Collections' });
this.toasterService.popAsync('success', null, this.i18nService.t('editedItem'));
}
check(c: CollectionView, select?: boolean) {
(c as any).checked = select == null ? !(c as any).checked : select;
}
@@ -74,21 +33,4 @@ export class CollectionsComponent implements OnInit, OnDestroy {
selectAll(select: boolean) {
this.collections.forEach((c) => this.check(c, select));
}
protected loadCipher() {
return this.cipherService.get(this.cipherId);
}
protected loadCipherCollections() {
return this.cipherDomain.collectionIds;
}
protected async loadCollections() {
const allCollections = await this.collectionService.getAllDecrypted();
return allCollections.filter((c) => !c.readOnly && c.organizationId === this.cipher.organizationId);
}
protected saveCollections() {
return this.cipherService.saveCollectionsWithServer(this.cipherDomain);
}
}

View File

@@ -1,8 +1,5 @@
import { Component } from '@angular/core';
import { ToasterService } from 'angular2-toaster';
import { Angulartics2 } from 'angulartics2';
import { FolderService } from 'jslib/abstractions/folder.service';
import { I18nService } from 'jslib/abstractions/i18n.service';
import { PlatformUtilsService } from 'jslib/abstractions/platformUtils.service';
@@ -17,8 +14,7 @@ import {
})
export class FolderAddEditComponent extends BaseFolderAddEditComponent {
constructor(folderService: FolderService, i18nService: I18nService,
analytics: Angulartics2, toasterService: ToasterService,
platformUtilsService: PlatformUtilsService) {
super(folderService, i18nService, analytics, toasterService, platformUtilsService);
super(folderService, i18nService, platformUtilsService);
}
}

View File

@@ -3,8 +3,8 @@
{{'filters' | i18n}}
</div>
<div class="card-body">
<input type="search" placeholder="{{searchPlaceholder || ('searchVault' | i18n)}}" id="search" class="form-control" [(ngModel)]="searchText"
(input)="searchTextChanged()" appAutofocus>
<input type="search" placeholder="{{searchPlaceholder || ('searchVault' | i18n)}}" id="search" class="form-control"
[(ngModel)]="searchText" (input)="searchTextChanged()" appAutofocus>
<ul class="fa-ul card-ul">
<li [ngClass]="{active: selectedAll}">
<a href="#" appStopClick (click)="selectAll()">
@@ -52,22 +52,37 @@
</a>
</h3>
<ul class="fa-ul card-ul carets">
<li *ngFor="let f of folders" class="d-flex" [ngClass]="{active: selectedFolder && f.id === selectedFolderId}">
<a href="#" appStopClick (click)="selectFolder(f)">
<i class="fa-li fa fa-caret-right"></i> {{f.name}}</a>
<a href="#" class="text-muted ml-auto show-active" appStopClick (click)="editFolder(f)" title="{{'editFolder' | i18n}}" *ngIf="f.id">
<i class="fa fa-pencil fa-fw"></i>
</a>
</li>
<ng-template #recursiveFolders let-folders>
<li *ngFor="let f of folders" [ngClass]="{active: selectedFolder && f.node.id === selectedFolderId}">
<div class="d-flex">
<a href="#" appStopClick (click)="selectFolder(f.node)">
<i class="fa-li fa fa-caret-right"></i> {{f.node.name}}</a>
<a href="#" class="text-muted ml-auto show-active" appStopClick (click)="editFolder(f.node)"
title="{{'editFolder' | i18n}}" *ngIf="f.node.id">
<i class="fa fa-pencil fa-fw"></i>
</a>
</div>
<ul class="fa-ul card-ul carets" *ngIf="f.children.length">
<ng-container *ngTemplateOutlet="recursiveFolders; context:{ $implicit: f.children }"></ng-container>
</ul>
</li>
</ng-template>
<ng-container *ngTemplateOutlet="recursiveFolders; context:{ $implicit: nestedFolders }"></ng-container>
</ul>
</ng-container>
<ng-container *ngIf="showCollections && collections && collections.length">
<h3>{{'collections' | i18n}}</h3>
<ul class="fa-ul card-ul carets">
<li *ngFor="let c of collections" [ngClass]="{active: c.id === selectedCollectionId}">
<a href="#" appStopClick (click)="selectCollection(c)">
<i class="fa-li fa fa-caret-right"></i> {{c.name}}</a>
</li>
<ng-template #recursiveCollections let-collections>
<li *ngFor="let c of collections" [ngClass]="{active: c.node.id === selectedCollectionId}">
<a href="#" appStopClick (click)="selectCollection(c.node)">
<i class="fa-li fa fa-caret-right"></i> {{c.node.name}}</a>
<ul class="fa-ul card-ul carets" *ngIf="c.children.length">
<ng-container *ngTemplateOutlet="recursiveCollections; context:{ $implicit: c.children }"></ng-container>
</ul>
</li>
</ng-template>
<ng-container *ngTemplateOutlet="recursiveCollections; context:{ $implicit: nestedCollections }"></ng-container>
</ul>
</ng-container>
</ng-container>

View File

@@ -39,10 +39,10 @@
<tbody>
<tr *ngFor="let c of collections; let i = index" (click)="check(c)">
<td class="table-list-checkbox">
<input type="checkbox" [(ngModel)]="c.checked" name="Collection[{{i}}].Checked">
<input type="checkbox" [(ngModel)]="c.checked" name="Collection[{{i}}].Checked" appStopProp>
</td>
<td>
<span appStopProp>{{c.name}}</span>
{{c.name}}
</td>
</tr>
</tbody>

View File

@@ -1,94 +1,33 @@
import {
Component,
EventEmitter,
Input,
OnDestroy,
OnInit,
Output,
} from '@angular/core';
import { ToasterService } from 'angular2-toaster';
import { Angulartics2 } from 'angulartics2';
import { CipherService } from 'jslib/abstractions/cipher.service';
import { CollectionService } from 'jslib/abstractions/collection.service';
import { I18nService } from 'jslib/abstractions/i18n.service';
import { PlatformUtilsService } from 'jslib/abstractions/platformUtils.service';
import { UserService } from 'jslib/abstractions/user.service';
import { Organization } from 'jslib/models/domain/organization';
import { CipherView } from 'jslib/models/view/cipherView';
import { CollectionView } from 'jslib/models/view/collectionView';
import { ShareComponent as BaseShareComponent } from 'jslib/angular/components/share.component';
@Component({
selector: 'app-vault-share',
templateUrl: 'share.component.html',
})
export class ShareComponent implements OnInit, OnDestroy {
@Input() cipherId: string;
@Input() organizationId: string;
@Output() onSharedCipher = new EventEmitter();
formPromise: Promise<any>;
cipher: CipherView;
collections: CollectionView[] = [];
organizations: Organization[] = [];
private writeableCollections: CollectionView[] = [];
constructor(private collectionService: CollectionService, private analytics: Angulartics2,
private toasterService: ToasterService, private i18nService: I18nService,
private userService: UserService, private cipherService: CipherService) { }
async ngOnInit() {
const cipherDomain = await this.cipherService.get(this.cipherId);
this.cipher = await cipherDomain.decrypt();
const allCollections = await this.collectionService.getAllDecrypted();
this.writeableCollections = allCollections.filter((c) => !c.readOnly);
this.organizations = await this.userService.getAllOrganizations();
if (this.organizationId == null && this.organizations.length > 0) {
this.organizationId = this.organizations[0].id;
}
this.filterCollections();
export class ShareComponent extends BaseShareComponent implements OnDestroy {
constructor(collectionService: CollectionService, platformUtilsService: PlatformUtilsService,
i18nService: I18nService, userService: UserService,
cipherService: CipherService) {
super(collectionService, platformUtilsService, i18nService, userService, cipherService);
}
ngOnDestroy() {
this.selectAll(false);
}
filterCollections() {
this.selectAll(false);
if (this.organizationId == null || this.writeableCollections.length === 0) {
this.collections = [];
} else {
this.collections = this.writeableCollections.filter((c) => c.organizationId === this.organizationId);
}
}
async submit() {
const cipherDomain = await this.cipherService.get(this.cipherId);
const cipherView = await cipherDomain.decrypt();
const attachmentPromises: Array<Promise<any>> = [];
if (cipherView.attachments != null) {
for (const attachment of cipherView.attachments) {
const promise = this.cipherService.shareAttachmentWithServer(attachment,
cipherView.id, this.organizationId);
attachmentPromises.push(promise);
}
}
const checkedCollectionIds = this.collections.filter((c) => (c as any).checked).map((c) => c.id);
try {
this.formPromise = Promise.all(attachmentPromises).then(async () => {
await this.cipherService.shareWithServer(cipherView, this.organizationId, checkedCollectionIds);
this.onSharedCipher.emit();
this.analytics.eventTrack.next({ action: 'Shared Cipher' });
this.toasterService.popAsync('success', null, this.i18nService.t('sharedItem'));
});
await this.formPromise;
} catch { }
}
check(c: CollectionView, select?: boolean) {
(c as any).checked = select == null ? !(c as any).checked : select;
}
@@ -97,15 +36,4 @@ export class ShareComponent implements OnInit, OnDestroy {
const collections = select ? this.collections : this.writeableCollections;
collections.forEach((c) => this.check(c, select));
}
get canSave() {
if (this.collections != null) {
for (let i = 0; i < this.collections.length; i++) {
if ((this.collections[i] as any).checked) {
return true;
}
}
}
return false;
}
}

View File

@@ -187,7 +187,7 @@ export class VaultComponent implements OnInit, OnDestroy {
}
async filterCollection(collectionId: string) {
this.ciphersComponent.showAddNew = false;
this.ciphersComponent.showAddNew = true;
this.groupingsComponent.searchPlaceholder = this.i18nService.t('searchCollection');
await this.ciphersComponent.load((c) => c.collectionIds != null && c.collectionIds.indexOf(collectionId) > -1);
this.clearFilters();
@@ -327,6 +327,13 @@ export class VaultComponent implements OnInit, OnDestroy {
const component = this.editCipher(null);
component.type = this.type;
component.folderId = this.folderId === 'none' ? null : this.folderId;
if (this.collectionId != null) {
const collection = this.groupingsComponent.collections.filter((c) => c.id === this.collectionId);
if (collection.length > 0) {
component.organizationId = collection[0].organizationId;
component.collectionIds = [this.collectionId];
}
}
}
editCipher(cipher: CipherView) {

View File

@@ -37,6 +37,9 @@
"password": {
"message": "Heslo"
},
"passphrase": {
"message": "Passphrase"
},
"notes": {
"message": "Poznámky"
},
@@ -616,7 +619,7 @@
}
},
"enterVerificationCodeApp": {
"message": "Zadejte 6-místný kód z ověřovací aplikace."
"message": "Zadejte 6místný kód z ověřovací aplikace."
},
"enterVerificationCodeEmail": {
"message": "Zadejte 6místný kód z e-mailu, který byl zaslán na $EMAIL$.",
@@ -795,6 +798,12 @@
"length": {
"message": "Délka"
},
"numWords": {
"message": "Number of Words"
},
"wordSeparator": {
"message": "Word Separator"
},
"passwordHistory": {
"message": "Historie hesel"
},
@@ -889,7 +898,7 @@
"message": "Nebezpečná zóna"
},
"dangerZoneDesc": {
"message": "Opatrně tyto akce se nedají vrátit!"
"message": "Opatrně. Tyto akce se nedají vrátit!"
},
"deauthorizeSessions": {
"message": "Zrušit autorizaci relací"
@@ -906,9 +915,15 @@
"purgeVault": {
"message": "Vymazat celý trezor"
},
"purgedOrganizationVault": {
"message": "Purged organization vault."
},
"purgeVaultDesc": {
"message": "Pokračujte níže ke smazání všech položek a složek ve vašem trezoru. Položky sdílené s organizací nebudou smazány."
},
"purgeOrgVaultDesc": {
"message": "Proceed below to delete all items in the organization's vault."
},
"purgeVaultWarning": {
"message": "Vymazání trezoru je trvalé. Tuto akci nelze vrátit zpět."
},
@@ -1002,7 +1017,7 @@
"message": "Doménová pravidla"
},
"domainRulesDesc": {
"message": "Pokud máte stejné přihlašovací údaje napříč různými doménami, můžete je označit jako \"ekvivalentní\". Bitwarden za vás již vytvořil seznam globálních domén."
"message": "Pokud máte stejné přihlašovací údaje napříč různými doménami, můžete je označit jako ekvivalentní. Bitwarden za vás již vytvořil seznam globálních domén."
},
"globalEqDomains": {
"message": "Globální ekvivalentní domény"
@@ -1023,7 +1038,7 @@
"message": "Přidat vlastní doménu"
},
"newCustomDomainDesc": {
"message": "Zadejte seznam domén oddělených čárkou. Povolené jsou pouze \"základní\" domény a proto nezadávejte subdomény. Například zadejte \"bitwarden.com\" místo \"vault.bitwarden.com\". Můžete také zadat \"androidapp:\/\/package.name\" pokud chcete přiřadit Android aplikaci k ostatním webovým doménám."
"message": "Zadejte seznam domén oddělených čárkou. Povolené jsou pouze základní domény. Nezadávejte subdomény. Například zadejte bitwarden.com“, nikoliv „vault.bitwarden.com. Můžete také zadat androidapp:\/\/package.name pokud chcete přiřadit Android aplikaci k ostatním webovým doménám."
},
"customDomainX": {
"message": "Vlastní doména $INDEX$",
@@ -1121,7 +1136,7 @@
"message": "Klíč"
},
"twoStepAuthenticatorEnterCode": {
"message": "Zadejte 6-místný kód z ověřovací aplikace"
"message": "Zadejte 6místný kód z ověřovací aplikace"
},
"twoStepAuthenticatorReaddDesc": {
"message": "V případě potřeby přidání do jiného zařízení, je níže zobrazen QR kód (nebo klíč) vyžadovaný ověřovací aplikací."
@@ -1165,6 +1180,15 @@
}
}
},
"u2fkeyX": {
"message": "U2F Key $INDEX$",
"placeholders": {
"index": {
"content": "$1",
"example": "2"
}
}
},
"nfcSupport": {
"message": "Podpora NFC"
},
@@ -1199,7 +1223,7 @@
"message": "Zadejte e-mail, na který si přejete dostávat ověřovací kódy"
},
"twoFactorEmailEnterCode": {
"message": "Zadejte 6-místný kód zaslaný na e-mail"
"message": "Zadejte 6místný kód zaslaný na e-mail"
},
"sendEmail": {
"message": "Odeslat e-mail"
@@ -1207,12 +1231,27 @@
"twoFactorU2fAdd": {
"message": "Přidání bezpečnostního klíče FIDO U2F k vašemu účtu"
},
"twoFactorU2fPlugIn": {
"message": "Připojte bezpečnostní klíč do portu USB počítače."
"removeU2fConfirmation": {
"message": "Are you sure you want to remove this security key?"
},
"readKey": {
"message": "Read Key"
},
"keyCompromised": {
"message": "Key is compromised."
},
"twoFactorU2fGiveName": {
"message": "Give the security key a friendly name to identify it."
},
"twoFactorU2fPlugInReadKey": {
"message": "Plug the security key into your computer's USB port and click the \"Read Key\" button."
},
"twoFactorU2fTouchButton": {
"message": "Pokud má bezpečnostní klíč tlačítko, zmáčkněte jej."
},
"twoFactorU2fSaveForm": {
"message": "Save the form."
},
"twoFactorU2fWarning": {
"message": "Z důvodu omezení různých platforem, nemůže být FIDO U2F použit ve všech aplikacích Bitwarden. Měli byste povolit jiný způsob dvoufázového přihlášení pro případy, kdy nelze FIDO U2F použít. Podporované platformy:"
},
@@ -1222,11 +1261,11 @@
"twoFactorU2fWaiting": {
"message": "Čeká se na stisknutí tlačítka na bezpečnostním klíči"
},
"twoFactorU2fClickEnable": {
"message": "Klepněte níže na tlačítko \"Povolit\" pro povolení tohoto bezpečnostního klíče pro dvoufázové přihlášení."
"twoFactorU2fClickSave": {
"message": "Click the \"Save\" button below to enable this security key for two-step login."
},
"twoFactorU2fProblemReading": {
"message": "Došlo k potížím při čtení bezpečnostního klíče."
"twoFactorU2fProblemReadingTryAgain": {
"message": "There was a problem reading the security key. Try again."
},
"twoFactorRecoveryYourCode": {
"message": "Váše kód pro obnovení dvoufázového přihlášení"
@@ -1245,7 +1284,7 @@
"message": "Hlášení o úniku dat"
},
"breachDesc": {
"message": "\"Únik\" je incident, při kterém se podařilo hackerům nelegální cestou získat údaje z webových stránek a následně je také zveřejnit. Zkontrolujte prosím kompromitovaná data (e-mailové adresy, hesla, kreditní karty, ...) a proveďte příslušná opatření (např. změňte heslo)."
"message": "Únik je incident, při kterém se podařilo hackerům nelegální cestou získat údaje z webových stránek a následně je také zveřejnit. Zkontrolujte prosím kompromitovaná data (e-mailové adresy, hesla, kreditní karty, ) a proveďte příslušná opatření (např. změňte heslo)."
},
"breachCheckUsernameEmail": {
"message": "Zkontrolujte všechna uživatelská jména nebo e-mailové adresy, které používáte."
@@ -1346,8 +1385,8 @@
"additionalStorageGbDesc": {
"message": "# dalších GB"
},
"additionalStorageDesc": {
"message": "Vybraný plán obsahuje $SIZE$ šifrovaného uložiště. Můžete si přikoupit další prostor za $PRICE$ za každý další 1 GB ročně.",
"additionalStorageIntervalDesc": {
"message": "Your plan comes with $SIZE$ of encrypted file storage. You can add additional storage for $PRICE$ per GB \/$INTERVAL$.",
"placeholders": {
"size": {
"content": "$1",
@@ -1356,6 +1395,10 @@
"price": {
"content": "$2",
"example": "$4.00"
},
"interval": {
"content": "$3",
"example": "'month' or 'year'"
}
}
},
@@ -1379,7 +1422,7 @@
"message": "Částka bude stržena okamžitě a poté opakovaně každý rok. Plán můžete kdykoli zrušit."
},
"paymentChargedWithTrial": {
"message": "Vybraný plán obsahuje bezplatnou 7denní zkušební dobu. Částka z vašeho účtu nebude stržena, dokud tato zkušební doba neuplyne. Stržení platby a fakturace bude následně probíhat opakovaně každý $ INTERVAL $. Plán můžete kdykoli zrušit.",
"message": "Vybraný plán obsahuje bezplatnou 7denní zkušební dobu. Částka z vašeho účtu nebude stržena, dokud tato zkušební doba neuplyne. Stržení platby a fakturace bude následně probíhat opakovaně každý $INTERVAL$. Plán můžete kdykoli zrušit.",
"placeholders": {
"interval": {
"content": "$1",
@@ -1457,7 +1500,7 @@
"message": "Odebrat uložiště"
},
"subscriptionStorage": {
"message": "Your subscription has a total of $MAX_STORAGE$ GB of encrypted file storage. You are currently using $USED_STORAGE$.",
"message": "Vaše předplatné zahrnuje celkem $MAX_STORAGE$ GB místa v šifrovaném úložišti. Aktuálně používáte $USED_STORAGE$.",
"placeholders": {
"max_storage": {
"content": "$1",
@@ -1689,7 +1732,7 @@
"message": "Create unlimited collections"
},
"gbEncryptedFileStorage": {
"message": "$SIZE$ encrypted file storage",
"message": "$SIZE$ šifrovaného úložiště",
"placeholders": {
"size": {
"content": "$1",
@@ -1719,7 +1762,7 @@
"message": "Přednostní zákaznická podpora"
},
"xDayFreeTrial": {
"message": "$COUNT$ day free trial, cancel anytime",
"message": "$COUNT$denní zkušební verze, možno kdykoliv zrušit",
"placeholders": {
"count": {
"content": "$1",
@@ -1871,6 +1914,12 @@
"userDesc": {
"message": "A regular user with access to your organization's collections."
},
"manager": {
"message": "Manager"
},
"managerDesc": {
"message": "Managers can access and manage assigned collections in your organization."
},
"all": {
"message": "Vše"
},
@@ -1910,8 +1959,8 @@
"changedPassword": {
"message": "Heslo účtu bylo změněno"
},
"enabled2fa": {
"message": "Dvoufázové přihlášení bylo povoleno"
"enabledUpdated2fa": {
"message": "Enabled\/updated two-step login."
},
"disabled2fa": {
"message": "Dvoufázové přihlášení bylo zakázáno"
@@ -2118,7 +2167,7 @@
"message": "Invited user(s)."
},
"resendInvitation": {
"message": "Resend Invitation"
"message": "Znovu poslat pozvánku"
},
"hasBeenReinvited": {
"message": "$USER$ has been reinvited.",
@@ -2274,7 +2323,7 @@
"message": "Bank account has been verified."
},
"bankAccount": {
"message": "Bank Account"
"message": "Bankovní účet"
},
"amountX": {
"message": "Amount $COUNT$",
@@ -2287,17 +2336,17 @@
}
},
"routingNumber": {
"message": "Routing Number",
"message": "Směrovací číslo",
"description": "Bank account routing number"
},
"accountNumber": {
"message": "Account Number"
"message": "Číslo účtu"
},
"accountHolderName": {
"message": "Account Holder Name"
"message": "Jméno majitele účtu"
},
"bankAccountType": {
"message": "Account Type"
"message": "Typ účtu"
},
"bankAccountTypeCompany": {
"message": "Company (Business)"
@@ -2306,7 +2355,7 @@
"message": "Individual (Personal)"
},
"enterInstallationId": {
"message": "Enter your installation id"
"message": "Zadejte ID instalace"
},
"addSeats": {
"message": "Add Seats",
@@ -2350,7 +2399,7 @@
"message": "Key Updated"
},
"updateKeyTitle": {
"message": "Update Key"
"message": "Aktualizovat klíč"
},
"updateEncryptionKey": {
"message": "Aktualizovat šifrovací klíč"
@@ -2444,5 +2493,17 @@
},
"licenseIsExpired": {
"message": "Licence vypršela."
},
"updatedUsers": {
"message": "Updated users"
},
"selected": {
"message": "Selected"
},
"ownership": {
"message": "Ownership"
},
"whoOwnsThisItem": {
"message": "Who owns this item?"
}
}

View File

@@ -37,6 +37,9 @@
"password": {
"message": "Adgangskode"
},
"passphrase": {
"message": "Adgangssætning"
},
"notes": {
"message": "Noter"
},
@@ -680,7 +683,7 @@
"message": "YubiKey OTP sikkerhedsnøgle"
},
"yubiKeyDesc": {
"message": "Brug en YubiKey til at få adgang til din konto. Virker med YubiKey 4, 4 Nano, 4C og NEO enheder."
"message": "Brug en YubiKey til at få adgang til din konto. Virker med YubiKey 4 serien, 5 serien og NEO enheder."
},
"duoDesc": {
"message": "Bekræft med Duo Security ved hjælp af Duo Mobile app, SMS, telefonopkald eller U2F sikkerhedsnøgle.",
@@ -795,6 +798,12 @@
"length": {
"message": "Længde"
},
"numWords": {
"message": "Antal ord"
},
"wordSeparator": {
"message": "Ordseparator"
},
"passwordHistory": {
"message": "Adgangskodehistorik"
},
@@ -906,9 +915,15 @@
"purgeVault": {
"message": "Tøm boks"
},
"purgedOrganizationVault": {
"message": "Organisationsboks tømt."
},
"purgeVaultDesc": {
"message": "Fortsæt nedenfor for at slette alle elementer og mapper i din boks. Elementer, der tilhører en organisation du deler med, slettes ikke."
},
"purgeOrgVaultDesc": {
"message": "Fortsæt nedenfor for at slette alle elementer i organisationens boks."
},
"purgeVaultWarning": {
"message": "Tømning af din boks er permanent. Det kan ikke fortrydes."
},
@@ -1136,7 +1151,7 @@
"message": "Tilføj en ny YubiKey til din konto"
},
"twoFactorYubikeyPlugIn": {
"message": "Sæt YubiKey'en (NEO eller 4-serien) i din computers USB-port."
"message": "Sæt YubiKey'en i din computers USB-port."
},
"twoFactorYubikeySelectKey": {
"message": "Vælg det første tomme YubiKey-indtastningsfelt nedenfor."
@@ -1165,6 +1180,15 @@
}
}
},
"u2fkeyX": {
"message": "U2F nøgle $INDEX$",
"placeholders": {
"index": {
"content": "$1",
"example": "2"
}
}
},
"nfcSupport": {
"message": "NFC understøttelse"
},
@@ -1207,12 +1231,27 @@
"twoFactorU2fAdd": {
"message": "Tilføj en FIDO U2F sikkerhedsnøgle til din konto"
},
"twoFactorU2fPlugIn": {
"message": "Sæt sikkerhedsnøglen i computerens USB-port."
"removeU2fConfirmation": {
"message": "Er du sikker på, at du vil fjerne denne sikkerhedsnøgle?"
},
"readKey": {
"message": "Læs nøgle"
},
"keyCompromised": {
"message": "Nøglen er kompromitteret."
},
"twoFactorU2fGiveName": {
"message": "Giv sikkerhedsnøglen et brugervenligt navn til at identificere den."
},
"twoFactorU2fPlugInReadKey": {
"message": "Sæt sikkerhedsnøglen i computerens USB-port, og klik på knappen \"Læs nøgle\"."
},
"twoFactorU2fTouchButton": {
"message": "Hvis sikkerhedsnøglen har en knap, skal du trykke på den."
},
"twoFactorU2fSaveForm": {
"message": "Gem formularen."
},
"twoFactorU2fWarning": {
"message": "På grund af platformbegrænsninger kan FIDO U2F ikke bruges på alle Bitwarden-applikationer. Du bør aktivere en anden to-trins login udbyder, så du kan få adgang til din konto, når FIDO U2F ikke kan benyttes. Understøttede platforme:"
},
@@ -1222,11 +1261,11 @@
"twoFactorU2fWaiting": {
"message": "Venter på at du trykker på knappen på din sikkerhedsnøgle"
},
"twoFactorU2fClickEnable": {
"message": "Klik på knappen \"Aktivér\" nedenfor for at benytte denne sikkerhedsnøgle til to-trins-login."
"twoFactorU2fClickSave": {
"message": "Klik på \"Gem\" knappen nedenfor for at aktivere denne sikkerhedsnøgle til to-trins login."
},
"twoFactorU2fProblemReading": {
"message": "Der opstod et problem med at læse sikkerhedsnøglen."
"twoFactorU2fProblemReadingTryAgain": {
"message": "Der opstod et problem med at læse sikkerhedsnøglen. Prøv igen."
},
"twoFactorRecoveryYourCode": {
"message": "Din Bitwarden to-trins-login gendannelseskode"
@@ -1346,8 +1385,8 @@
"additionalStorageGbDesc": {
"message": "# af ekstra GB"
},
"additionalStorageDesc": {
"message": "Dit abonnement indeholder $SIZE$ krypteret fillagring. Du kan tilføje ekstra lagerplads til $PRICE$ per GB \/ år.",
"additionalStorageIntervalDesc": {
"message": "Dit abonnement indeholder $SIZE$ krypteret fillagring. Du kan tilføje ekstra lagerplads til $PRICE$ per GB \/ $INTERVAL$.",
"placeholders": {
"size": {
"content": "$1",
@@ -1356,6 +1395,10 @@
"price": {
"content": "$2",
"example": "$4.00"
},
"interval": {
"content": "$3",
"example": "'month' or 'year'"
}
}
},
@@ -1869,7 +1912,13 @@
"message": "Bruger"
},
"userDesc": {
"message": "En almindelig bruger med adgang til organisationens samlinger."
"message": "En almindelig bruger med adgang til tildelte samlinger i din organisation."
},
"manager": {
"message": "Administrator"
},
"managerDesc": {
"message": "Administratorer kan få adgang til og håndtere tildelte samlinger i din organisation."
},
"all": {
"message": "Alle"
@@ -1910,8 +1959,8 @@
"changedPassword": {
"message": "Ændrede konto kodeord."
},
"enabled2fa": {
"message": "Aktiverede to-trins-login."
"enabledUpdated2fa": {
"message": "Aktiverede\/opdaterede to-trins login."
},
"disabled2fa": {
"message": "Deaktiverede to-trins-login."
@@ -2444,5 +2493,17 @@
},
"licenseIsExpired": {
"message": "Licensen er udløbet."
},
"updatedUsers": {
"message": "Opdaterede brugere"
},
"selected": {
"message": "Valgt"
},
"ownership": {
"message": "Ejerskab"
},
"whoOwnsThisItem": {
"message": "Hvem ejer dette element?"
}
}

View File

@@ -37,6 +37,9 @@
"password": {
"message": "Passwort"
},
"passphrase": {
"message": "Passphrase"
},
"notes": {
"message": "Notizen"
},
@@ -795,6 +798,12 @@
"length": {
"message": "Länge"
},
"numWords": {
"message": "Anzahl der Wörter"
},
"wordSeparator": {
"message": "Trennungszeichen"
},
"passwordHistory": {
"message": "Kennwort-Historie"
},
@@ -906,9 +915,15 @@
"purgeVault": {
"message": "Tresor leeren"
},
"purgedOrganizationVault": {
"message": "Gelöschter Organisations-Tresor."
},
"purgeVaultDesc": {
"message": "Gehen Sie wie folgt vor, um alle Einträge und Ordner in Ihrem Tresor zu löschen. Einträge, die zu einer Organisation gehören, die Sie mit anderen teilen, werden nicht gelöscht."
},
"purgeOrgVaultDesc": {
"message": "Bitte bestätige, dass du alle Inhalte dieses Tresores löschen möchtest."
},
"purgeVaultWarning": {
"message": "Die Leerung des Tresor ist permanent. Sie kann nicht rückgängig gemacht werden."
},
@@ -1165,6 +1180,15 @@
}
}
},
"u2fkeyX": {
"message": "U2F Schlüssel $INDEX$",
"placeholders": {
"index": {
"content": "$1",
"example": "2"
}
}
},
"nfcSupport": {
"message": "NFC-Unterstützung"
},
@@ -1207,12 +1231,27 @@
"twoFactorU2fAdd": {
"message": "Fügen Sie Ihrem Konto einen FIDO U2F-Sicherheitsschlüssel hinzu"
},
"twoFactorU2fPlugIn": {
"message": "Stecken Sie den Sicherheitsschlüssel in USB-Port Ihres Computers."
"removeU2fConfirmation": {
"message": "Sind Sie sich sicher, dass Sie diesen U2F-Schlüssel entfernen möchten?"
},
"readKey": {
"message": "Schlüssel erfassen"
},
"keyCompromised": {
"message": "Dieser Schlüssel ist kompromitiert."
},
"twoFactorU2fGiveName": {
"message": "Vergeben Sie einen Namen um den Schlüssel zu unterscheiden."
},
"twoFactorU2fPlugInReadKey": {
"message": "Stecken Sie den Schlüssel in den Computer und drücken Sie den \"Schlüssel erfassen\" Knopf."
},
"twoFactorU2fTouchButton": {
"message": "Wenn der Sicherheitsschlüssel eine Taste hat, drücken Sie die."
},
"twoFactorU2fSaveForm": {
"message": "Formular absenden."
},
"twoFactorU2fWarning": {
"message": "Aufgrund von Plattformbeschränkungen kann FIDO U2F nicht mit allen Bitwarden-Anwendungen verwendet werden. Sie sollten einen anderen Zwei-Faktor-Authentifizierungsanbieter aktivieren, damit Sie auf Ihr Konto zugreifen können, wenn FIDO U2F nicht verwendet werden kann. Unterstützte Plattformen sind:"
},
@@ -1222,11 +1261,11 @@
"twoFactorU2fWaiting": {
"message": "Es wird darauf gewartet, dass Sie die Taste Ihres Sicherheitsschlüssels betätigen"
},
"twoFactorU2fClickEnable": {
"message": "Klicken Sie unten auf \"Aktivieren\", um diesen Sicherheitsschlüssel für die Zwei-Faktor-Anmeldung zu verwenden."
"twoFactorU2fClickSave": {
"message": "Speichern drücken um den Sicherheitsschlüssel für die Zwei-Schritte Anmeldung zu aktivieren."
},
"twoFactorU2fProblemReading": {
"message": "Es gab ein Problem beim Lesen des Sicherheitsschlüssels."
"twoFactorU2fProblemReadingTryAgain": {
"message": "Es gab ein Problem beim lesen des Sicherheitsschlüssels, bitte erneut versuchen."
},
"twoFactorRecoveryYourCode": {
"message": "Ihr Wiederherstellungsschlüssel für die Zwei-Faktor-Anmeldung in Bitwarden"
@@ -1346,8 +1385,8 @@
"additionalStorageGbDesc": {
"message": "# zusätzliche GB"
},
"additionalStorageDesc": {
"message": "Ihr Abo beinhaltet einen $SIZE$ großen verschlüsselten Datenspeicher. Sie können zusätzlichen Speicher für $PRICE$ pro GB \/ Jahr dazubuchen.",
"additionalStorageIntervalDesc": {
"message": "Your plan comes with $SIZE$ of encrypted file storage. You can add additional storage for $PRICE$ per GB \/$INTERVAL$.",
"placeholders": {
"size": {
"content": "$1",
@@ -1356,6 +1395,10 @@
"price": {
"content": "$2",
"example": "$4.00"
},
"interval": {
"content": "$3",
"example": "'month' or 'year'"
}
}
},
@@ -1871,6 +1914,12 @@
"userDesc": {
"message": "Ein normaler Benutzer mit Zugriff auf die Sammlungen Ihrer Organisation."
},
"manager": {
"message": "Moderator"
},
"managerDesc": {
"message": "Moderatoren können auf alle Einträge in Ihrer Organisation zugreifen und diese verwalten."
},
"all": {
"message": "Alle"
},
@@ -1910,8 +1959,8 @@
"changedPassword": {
"message": "Benutzerpasswort geändert."
},
"enabled2fa": {
"message": "Zwei-Faktor-Anmeldung aktiviert."
"enabledUpdated2fa": {
"message": "Zwei-Schritte Anmeldung aktiviert \/ aktualisiert."
},
"disabled2fa": {
"message": "Zwei-Faktor-Anmeldung deaktiviert."
@@ -2444,5 +2493,17 @@
},
"licenseIsExpired": {
"message": "Lizenz ist abgelaufen."
},
"updatedUsers": {
"message": "Aktualisierte Benutzer"
},
"selected": {
"message": "Ausgewählt"
},
"ownership": {
"message": "Besitzer"
},
"whoOwnsThisItem": {
"message": "Wem gehört dieser Eintrag?"
}
}

View File

@@ -37,6 +37,9 @@
"password": {
"message": "Password"
},
"passphrase": {
"message": "Passphrase"
},
"notes": {
"message": "Notes"
},
@@ -683,7 +686,7 @@
"message": "YubiKey OTP Security Key"
},
"yubiKeyDesc": {
"message": "Use a YubiKey to access your account. Works with YubiKey 4, 4 Nano, 4C, and NEO devices."
"message": "Use a YubiKey to access your account. Works with YubiKey 4 series, 5 series, and NEO devices."
},
"duoDesc": {
"message": "Verify with Duo Security using the Duo Mobile app, SMS, phone call, or U2F security key.",
@@ -798,6 +801,12 @@
"length": {
"message": "Length"
},
"numWords": {
"message": "Number of Words"
},
"wordSeparator": {
"message": "Word Separator"
},
"passwordHistory": {
"message": "Password History"
},
@@ -909,9 +918,15 @@
"purgeVault": {
"message": "Purge Vault"
},
"purgedOrganizationVault": {
"message": "Purged organization vault."
},
"purgeVaultDesc": {
"message": "Proceed below to delete all items and folders in your vault. Items that belong to an organization that you share with will not be deleted."
},
"purgeOrgVaultDesc": {
"message": "Proceed below to delete all items in the organization's vault."
},
"purgeVaultWarning": {
"message": "Purging your vault is permanent. It cannot be undone."
},
@@ -1139,7 +1154,7 @@
"message": "Add a new YubiKey to your account"
},
"twoFactorYubikeyPlugIn": {
"message": "Plug the YubiKey (NEO or 4 series) into your computer's USB port."
"message": "Plug the YubiKey into your computer's USB port."
},
"twoFactorYubikeySelectKey": {
"message": "Select the first empty YubiKey input field below."
@@ -1168,6 +1183,15 @@
}
}
},
"u2fkeyX": {
"message": "U2F Key $INDEX$",
"placeholders": {
"index": {
"content": "$1",
"example": "2"
}
}
},
"nfcSupport": {
"message": "NFC Support"
},
@@ -1210,12 +1234,27 @@
"twoFactorU2fAdd": {
"message": "Add a FIDO U2F security key to your account"
},
"twoFactorU2fPlugIn": {
"message": "Plug the security key into your computer's USB port."
"removeU2fConfirmation": {
"message": "Are you sure you want to remove this security key?"
},
"readKey": {
"message": "Read Key"
},
"keyCompromised": {
"message": "Key is compromised."
},
"twoFactorU2fGiveName": {
"message": "Give the security key a friendly name to identify it."
},
"twoFactorU2fPlugInReadKey": {
"message": "Plug the security key into your computer's USB port and click the \"Read Key\" button."
},
"twoFactorU2fTouchButton": {
"message": "If the security key has a button, touch it."
},
"twoFactorU2fSaveForm": {
"message": "Save the form."
},
"twoFactorU2fWarning": {
"message": "Due to platform limitations, FIDO U2F cannot be used on all Bitwarden applications. You should enable another two-step login provider so that you can access your account when FIDO U2F cannot be used. Supported platforms:"
},
@@ -1225,11 +1264,11 @@
"twoFactorU2fWaiting": {
"message": "Waiting for you to touch the button on your security key"
},
"twoFactorU2fClickEnable": {
"message": "Click the \"Enable\" button below to enable this security key for two-step login."
"twoFactorU2fClickSave": {
"message": "Click the \"Save\" button below to enable this security key for two-step login."
},
"twoFactorU2fProblemReading": {
"message": "There was a problem reading the security key."
"twoFactorU2fProblemReadingTryAgain": {
"message": "There was a problem reading the security key. Try again."
},
"twoFactorRecoveryYourCode": {
"message": "Your Bitwarden two-step login recovery code"
@@ -1349,8 +1388,8 @@
"additionalStorageGbDesc": {
"message": "# of additional GB"
},
"additionalStorageDesc": {
"message": "Your plan comes with $SIZE$ of encrypted file storage. You can add additional storage for $PRICE$ per GB /year.",
"additionalStorageIntervalDesc": {
"message": "Your plan comes with $SIZE$ of encrypted file storage. You can add additional storage for $PRICE$ per GB /$INTERVAL$.",
"placeholders": {
"size": {
"content": "$1",
@@ -1359,6 +1398,10 @@
"price": {
"content": "$2",
"example": "$4.00"
},
"interval": {
"content": "$3",
"example": "'month' or 'year'"
}
}
},
@@ -1866,13 +1909,19 @@
"message": "Admin"
},
"adminDesc": {
"message": " Admins can access and manage all items, collections and users in your organization."
"message": "Admins can access and manage all items, collections and users in your organization."
},
"user": {
"message": "User"
},
"userDesc": {
"message": "A regular user with access to your organization's collections."
"message": "A regular user with access to assigned collections in your organization."
},
"manager": {
"message": "Manager"
},
"managerDesc": {
"message": "Managers can access and manage assigned collections in your organization."
},
"all": {
"message": "All"
@@ -1913,8 +1962,8 @@
"changedPassword": {
"message": "Changed account password."
},
"enabled2fa": {
"message": "Enabled two-step login."
"enabledUpdated2fa": {
"message": "Enabled/updated two-step login."
},
"disabled2fa": {
"message": "Disabled two-step login."
@@ -2447,5 +2496,17 @@
},
"licenseIsExpired": {
"message": "License is expired."
},
"updatedUsers": {
"message": "Updated users"
},
"selected": {
"message": "Selected"
},
"ownership": {
"message": "Ownership"
},
"whoOwnsThisItem": {
"message": "Who owns this item?"
}
}

View File

@@ -37,6 +37,9 @@
"password": {
"message": "Contraseña"
},
"passphrase": {
"message": "Frase de contraseña"
},
"notes": {
"message": "Notas"
},
@@ -795,6 +798,12 @@
"length": {
"message": "Longitud"
},
"numWords": {
"message": "Número de palabras"
},
"wordSeparator": {
"message": "Separador de palabras"
},
"passwordHistory": {
"message": "Historial de contraseñas"
},
@@ -906,9 +915,15 @@
"purgeVault": {
"message": "Purgar caja fuerte"
},
"purgedOrganizationVault": {
"message": "Caja fuerte de organización purgada."
},
"purgeVaultDesc": {
"message": "Proceder eliminará todos los elementos y carpetas de tu caja fuerte. Los elementos que petenezcan a una organización con la que compartes contenido, no serán eliminados."
},
"purgeOrgVaultDesc": {
"message": "Proceda a continuación para eliminar todos los elementos de la caja fuerte de la organización."
},
"purgeVaultWarning": {
"message": "Purgar tu caja fuerte es permanente. No se puede deshacer."
},
@@ -1165,6 +1180,15 @@
}
}
},
"u2fkeyX": {
"message": "Llave U2F $INDEX$",
"placeholders": {
"index": {
"content": "$1",
"example": "2"
}
}
},
"nfcSupport": {
"message": "Soporte NFC"
},
@@ -1207,12 +1231,27 @@
"twoFactorU2fAdd": {
"message": "Agregar una clave de seguridad U2F FIDO a su cuenta"
},
"twoFactorU2fPlugIn": {
"message": "Conecte la llave de seguridad al puerto USB de su computadora."
"removeU2fConfirmation": {
"message": "¿Estás seguro de que quieres eliminar esta clave de seguridad?"
},
"readKey": {
"message": "Leer llave"
},
"keyCompromised": {
"message": "La clave está comprometida."
},
"twoFactorU2fGiveName": {
"message": "Asigne un nombre descriptivo a la llave de seguridad."
},
"twoFactorU2fPlugInReadKey": {
"message": "Conecte la llave de seguridad al puerto USB de su ordenador y haga clic en el botón \"Leer llave\"."
},
"twoFactorU2fTouchButton": {
"message": "Si la clave de seguridad tiene un botón, tócalo."
},
"twoFactorU2fSaveForm": {
"message": "Guarda el formulario."
},
"twoFactorU2fWarning": {
"message": "Debido a limitaciones de la plataforma, FIDO U2F no puede ser usado en todas las aplicaciones de Bitwarden. Debería habilitar otro proveedor de inicio de sesión de dos pasos, para que pueda acceder a su cuenta cuando FIDO U2F no pueda ser utilizado. Plataformas soportadas:"
},
@@ -1222,11 +1261,11 @@
"twoFactorU2fWaiting": {
"message": "Esperando que toque el botón de su clave de seguridad"
},
"twoFactorU2fClickEnable": {
"message": "Haga clic en el botón \"Habilitar\" a continuación para habilitar esta clave de seguridad para el inicio de sesión en dos pasos."
"twoFactorU2fClickSave": {
"message": "Haga clic en el botón \"guardar\" para habilitar esta clave de seguridad para el inicio de sesión de dos pasos."
},
"twoFactorU2fProblemReading": {
"message": "Hubo un problema leyendo la clave de seguridad."
"twoFactorU2fProblemReadingTryAgain": {
"message": "Hubo un problema al leer la clave de seguridad. Inténtalo de nuevo."
},
"twoFactorRecoveryYourCode": {
"message": "Su código de recuperación de inicio de sesión de dos pasos Bitwarden"
@@ -1346,8 +1385,8 @@
"additionalStorageGbDesc": {
"message": "# de GB adicional"
},
"additionalStorageDesc": {
"message": "Su plan viene con $SIZE$ de almacenamiento de archivos cifrados. Puede agregar almacenamiento adicional por $PRICE$ por GB\/año.",
"additionalStorageIntervalDesc": {
"message": "Su plan viene con $SIZE$ de almacenamiento de archivos cifrados. Puede agregar almacenamiento adicional por $PRICE$ por GB\/$INTERVAL$.",
"placeholders": {
"size": {
"content": "$1",
@@ -1356,6 +1395,10 @@
"price": {
"content": "$2",
"example": "$4.00"
},
"interval": {
"content": "$3",
"example": "'month' or 'year'"
}
}
},
@@ -1863,7 +1906,7 @@
"message": "Administrador"
},
"adminDesc": {
"message": "Los administradores pueden acceder y gestionar todos los elementos, coleccionefs y usuarios de la organización."
"message": "Los administradores pueden acceder y gestionar todos los elementos, colecciones y usuarios de la organización."
},
"user": {
"message": "Usuario"
@@ -1871,6 +1914,12 @@
"userDesc": {
"message": "Un usuario regular con acceso a las colecciones de su organización."
},
"manager": {
"message": "Gestor"
},
"managerDesc": {
"message": "Los gestores pueden acceder y gestionar colecciones asignadas en tu organización."
},
"all": {
"message": "Todo"
},
@@ -1910,8 +1959,8 @@
"changedPassword": {
"message": "Contraseña de la cuenta cambiada."
},
"enabled2fa": {
"message": "Autenticación en dos pasos habilitada."
"enabledUpdated2fa": {
"message": "Autenticación en dos pasos habilitado\/actualizado."
},
"disabled2fa": {
"message": "Autenticación en dos pasos deshabilitada."
@@ -2444,5 +2493,17 @@
},
"licenseIsExpired": {
"message": "Licencia expirada."
},
"updatedUsers": {
"message": "Usuarios actualizados"
},
"selected": {
"message": "Seleccionado"
},
"ownership": {
"message": "Propiedad"
},
"whoOwnsThisItem": {
"message": "¿Quién posee este elemento?"
}
}

View File

@@ -37,6 +37,9 @@
"password": {
"message": "Parool"
},
"passphrase": {
"message": "Paroolifraas"
},
"notes": {
"message": "Märkmed"
},
@@ -795,6 +798,12 @@
"length": {
"message": "Pikkus"
},
"numWords": {
"message": "Sõnade arv"
},
"wordSeparator": {
"message": "Sõna eraldaja"
},
"passwordHistory": {
"message": "Paroolide ajalugu"
},
@@ -906,9 +915,15 @@
"purgeVault": {
"message": "Likvideeri Hoidla"
},
"purgedOrganizationVault": {
"message": "Organisatsiooni hoidla on likvideeritud."
},
"purgeVaultDesc": {
"message": "Jätkates kustutatakse kõik hoidlas olevad kirjed ja kaustad. Andmed, mis kuuluvad organisatsioonile, jäävad puutumata."
},
"purgeOrgVaultDesc": {
"message": "Jätka allpool, et kõik organisatsiooni kirjed hoidlast likvideerida."
},
"purgeVaultWarning": {
"message": "Hoidla likvideerimine on alatine. Seda ei saa tagasi võtta."
},
@@ -1165,6 +1180,15 @@
}
}
},
"u2fkeyX": {
"message": "U2F võtme $INDEX$",
"placeholders": {
"index": {
"content": "$1",
"example": "2"
}
}
},
"nfcSupport": {
"message": "NFS tugi"
},
@@ -1207,12 +1231,27 @@
"twoFactorU2fAdd": {
"message": "Lisa oma kontole FIDO U2F turvavõti"
},
"twoFactorU2fPlugIn": {
"message": "Sisesta turvavõti oma arvuti USB pessa."
"removeU2fConfirmation": {
"message": "Oled kindel, et soovid selle turvavõtme eemaldada?"
},
"readKey": {
"message": "Loe võtit"
},
"keyCompromised": {
"message": "Võti on ohus."
},
"twoFactorU2fGiveName": {
"message": "Anna turvavõtmele lihtne nimi, et seda oleks lihtsam ära tunda."
},
"twoFactorU2fPlugInReadKey": {
"message": "Sisesta turvavõti arvuti USB pessa ning kliki \"Loe võtit\" nupul."
},
"twoFactorU2fTouchButton": {
"message": "Kui turvavõtmel on nupp, siis vajuta seda."
},
"twoFactorU2fSaveForm": {
"message": "Salvesta vorm."
},
"twoFactorU2fWarning": {
"message": "Mõnede platvormi piirangute tõttu ei saa FIDO U2F-i kõikide Bitwardeni rakendustega kasutada. Võiksid kaaluda teise kaheastmelise kinnitamise aktiveerimist olukordadeks, kus FIDO U2F-i ei saa kasutada. Toetatud platvormid:"
},
@@ -1222,11 +1261,11 @@
"twoFactorU2fWaiting": {
"message": "Ootame, kuni puudutad turvavõtmel olevat nuppu"
},
"twoFactorU2fClickEnable": {
"message": "Kliki all olevale \"Luba\" nupule, et kaheastmeline kinnitamine läbi turvavõtme sisse lülitada."
"twoFactorU2fClickSave": {
"message": "Kliki all olevale \"Luba\" nupule, et kaheastmeline kinnitamine läbi selle turvavõtme sisse lülitada."
},
"twoFactorU2fProblemReading": {
"message": "Turvavõtme lugemisel tekkis tõrge."
"twoFactorU2fProblemReadingTryAgain": {
"message": "Turvavõtme lugemisel tekkis tõrge. Proovi uuesti."
},
"twoFactorRecoveryYourCode": {
"message": "Bitwardeni kaheastmelise logimise varukood"
@@ -1346,8 +1385,8 @@
"additionalStorageGbDesc": {
"message": "# lisa GB"
},
"additionalStorageDesc": {
"message": "Sul on kasutada $SIZE$ krüpteeritud failiruumi Soovi korral saad osta lisaruumi hinnaga $PRICE$ GB \/aastas.",
"additionalStorageIntervalDesc": {
"message": "Sul on kasutada $SIZE$ krüpteeritud failiruumi Soovi korral saad osta lisaruumi hinnaga $PRICE$ GB \/$INTERVAL$.",
"placeholders": {
"size": {
"content": "$1",
@@ -1356,6 +1395,10 @@
"price": {
"content": "$2",
"example": "$4.00"
},
"interval": {
"content": "$3",
"example": "'month' or 'year'"
}
}
},
@@ -1871,6 +1914,12 @@
"userDesc": {
"message": "Tavaline kasutaja, kel on ligipääsu organisatsiooni kirjetele."
},
"manager": {
"message": "Administraator"
},
"managerDesc": {
"message": "Administraatorid pääsevad ligi ja saavad hallata organisatsiooni poolt määratud kollektsioone."
},
"all": {
"message": "Kõik"
},
@@ -1910,8 +1959,8 @@
"changedPassword": {
"message": "Muuda konto parooli."
},
"enabled2fa": {
"message": "Lülita kaheastmeline kinnitamine sisse."
"enabledUpdated2fa": {
"message": "Lülitasime sisse\/uuendasime kaheastmelist kinnitamist."
},
"disabled2fa": {
"message": "Keela kaheastmeline kinnitamine."
@@ -2444,5 +2493,17 @@
},
"licenseIsExpired": {
"message": "Litsents on aegunud."
},
"updatedUsers": {
"message": "Kasutajad on uuendatud"
},
"selected": {
"message": "Valitud"
},
"ownership": {
"message": "Omanik"
},
"whoOwnsThisItem": {
"message": "Kes seda üksust omab?"
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -37,6 +37,9 @@
"password": {
"message": "Mot de passe"
},
"passphrase": {
"message": "Phrase de passe"
},
"notes": {
"message": "Notes"
},
@@ -680,7 +683,7 @@
"message": "Clé de sécurité YubiKey OTP"
},
"yubiKeyDesc": {
"message": "Utiliser une YubiKey pour accéder à votre compte. Fonctionne avec les appareils YubiKey 4, 4 Nano, 4C et NEO."
"message": "Utiliser une YubiKey pour accéder à votre compte. Fonctionne avec les appareils YubiKey série 4, série 5 et NEO."
},
"duoDesc": {
"message": "Vérifier avec Duo Security via l'application Duo Mobile, un SMS, un appel vocal ou une clé de sécurité U2F.",
@@ -795,6 +798,12 @@
"length": {
"message": "Longueur"
},
"numWords": {
"message": "Nombre de mots"
},
"wordSeparator": {
"message": "Séparateur de mots"
},
"passwordHistory": {
"message": "Historique des mots de passe"
},
@@ -906,9 +915,15 @@
"purgeVault": {
"message": "Effacer le coffre"
},
"purgedOrganizationVault": {
"message": "Le coffre de l'organisation a été effacé."
},
"purgeVaultDesc": {
"message": "Poursuivez ci-dessous pour supprimer tous les éléments et dossiers de votre coffre. Les éléments qui appartiennent à une organisation avec laquelle vous partagez ne seront pas supprimés."
},
"purgeOrgVaultDesc": {
"message": "Poursuivez ci-dessous pour supprimer tous les éléments du coffre de votre organisation."
},
"purgeVaultWarning": {
"message": "L'effacement votre coffre est définitif. Cette action ne peut pas être annulée."
},
@@ -1136,7 +1151,7 @@
"message": "Ajouter une nouvelle YubiKey à votre compte"
},
"twoFactorYubikeyPlugIn": {
"message": "Branchez la YubiKey (NEO ou série 4) dans un port USB de votre ordinateur."
"message": "Branchez la YubiKey dans un port USB de votre ordinateur."
},
"twoFactorYubikeySelectKey": {
"message": "Sélectionnez le premier champ YubiKey libre ci-dessous."
@@ -1165,6 +1180,15 @@
}
}
},
"u2fkeyX": {
"message": "Clé U2F $INDEX$",
"placeholders": {
"index": {
"content": "$1",
"example": "2"
}
}
},
"nfcSupport": {
"message": "Support du NFC"
},
@@ -1207,12 +1231,27 @@
"twoFactorU2fAdd": {
"message": "Ajouter une clé de sécurité FIDO U2F à votre compte"
},
"twoFactorU2fPlugIn": {
"message": "Branchez la clé de sécurité sur un port USB de votre ordinateur."
"removeU2fConfirmation": {
"message": "Êtes-vous certain de vouloir retirer cette clé de sécurité ?"
},
"readKey": {
"message": "Lire la clé"
},
"keyCompromised": {
"message": "La clé est compromise."
},
"twoFactorU2fGiveName": {
"message": "Donnez un nom convivial à la clé de sécurité pour l'identifier."
},
"twoFactorU2fPlugInReadKey": {
"message": "Branchez la clé de sécurité sur un port USB de votre ordinateur et cliquez sur le bouton \"Lire la clé\"."
},
"twoFactorU2fTouchButton": {
"message": "Si la clé de sécurité a un bouton, touchez-le."
},
"twoFactorU2fSaveForm": {
"message": "Sauvegarder le contenu du formulaire."
},
"twoFactorU2fWarning": {
"message": "En raison de limitations de plateforme, FIDO U2F n'est pas utilisable sur toutes les applications Bitwarden. Vous devriez activer un autre fournisseur de connexion en deux étapes afin de pouvoir accéder à votre compte lorsque vous ne pouvez pas utiliser FIDO U2F. Plateformes prises en charge :"
},
@@ -1222,11 +1261,11 @@
"twoFactorU2fWaiting": {
"message": "Attente de l'appui sur le bouton de votre clé de sécurité"
},
"twoFactorU2fClickEnable": {
"message": "Cliquez sur le bouton « Activer » ci-dessous pour activer cette clé de sécurité pour une connexion en deux étapes."
"twoFactorU2fClickSave": {
"message": "Cliquez sur le bouton « Enregistrer » ci-dessous pour activer cette clé de sécurité pour une connexion en deux étapes."
},
"twoFactorU2fProblemReading": {
"message": "Un problème est survenu lors de la lecture de la clé de sécurité."
"twoFactorU2fProblemReadingTryAgain": {
"message": "Un problème est survenu lors de la lecture de la clé de sécurité. Veuillez réessayer."
},
"twoFactorRecoveryYourCode": {
"message": "Votre code de récupération de connexion en deux étapes Bitwarden"
@@ -1346,8 +1385,8 @@
"additionalStorageGbDesc": {
"message": "# Go additionnels"
},
"additionalStorageDesc": {
"message": "Votre offre comprend $SIZE$ de stockage de fichiers chiffrés. Vous pouvez ajouter du stockage supplémentaire pour $PRICE$ par Go\/an.",
"additionalStorageIntervalDesc": {
"message": "Votre offre comprend $SIZE$ de stockage de fichiers chiffrés. Vous pouvez ajouter du stockage supplémentaire pour $PRICE$ par Go\/$INTERVAL$.",
"placeholders": {
"size": {
"content": "$1",
@@ -1356,6 +1395,10 @@
"price": {
"content": "$2",
"example": "$4.00"
},
"interval": {
"content": "$3",
"example": "'month' or 'year'"
}
}
},
@@ -1871,6 +1914,12 @@
"userDesc": {
"message": "Un utilisateur normal avec accès aux collections de votre organisation."
},
"manager": {
"message": "Gestionnaire"
},
"managerDesc": {
"message": "Les gestionnaires peuvent voir et gérer les collections de votre organisation qui leur ont été assignées."
},
"all": {
"message": "Tous"
},
@@ -1910,8 +1959,8 @@
"changedPassword": {
"message": "Mot de passe changé."
},
"enabled2fa": {
"message": "Connexion en deux étapes activée."
"enabledUpdated2fa": {
"message": "Connexion en deux étapes activée\/mise à jour."
},
"disabled2fa": {
"message": "Connexion en deux étapes désactivée."
@@ -2444,5 +2493,17 @@
},
"licenseIsExpired": {
"message": "La licence a expiré."
},
"updatedUsers": {
"message": "Utilisateurs mis à jour"
},
"selected": {
"message": "Sélectionné(s)"
},
"ownership": {
"message": "Propriété"
},
"whoOwnsThisItem": {
"message": "À qui appartient cet élément?"
}
}

View File

@@ -37,6 +37,9 @@
"password": {
"message": "Jelszó"
},
"passphrase": {
"message": "Kulcsszó"
},
"notes": {
"message": "Jegyzetek"
},
@@ -164,48 +167,48 @@
"message": "Nincs kijelölve"
},
"noneFolder": {
"message": "No Folder",
"message": "Nincs mappa",
"description": "This is the folder for uncategorized items"
},
"addFolder": {
"message": "Add Folder"
"message": "Mappa hozzáadása"
},
"editFolder": {
"message": "Edit Folder"
"message": "Mappa szerkesztése"
},
"baseDomain": {
"message": "Base domain"
"message": "Alap domain"
},
"host": {
"message": "Host",
"message": "Hoszt",
"description": "A URL's host value. For example, the host of https:\/\/sub.domain.com:443 is 'sub.domain.com:443'."
},
"exact": {
"message": "Exact"
"message": "Pontos"
},
"startsWith": {
"message": "Starts with"
"message": "Ezzel kezdődik"
},
"regEx": {
"message": "Regular expression",
"message": "Reguláris kifejezés",
"description": "A programming term, also known as 'RegEx'."
},
"matchDetection": {
"message": "Match Detection",
"message": "Találatfelismerés",
"description": "URI match detection for auto-fill."
},
"defaultMatchDetection": {
"message": "Default match detection",
"message": "Alapértelmezett találatfelismerés",
"description": "Default URI match detection for auto-fill."
},
"never": {
"message": "Never"
"message": "Soha"
},
"toggleVisibility": {
"message": "Toggle Visibility"
"message": "Láthatóság váltása"
},
"generatePassword": {
"message": "Generate Password"
"message": "Jelszó generálása"
},
"checkPassword": {
"message": "Check if password has been exposed."
@@ -795,6 +798,12 @@
"length": {
"message": "Length"
},
"numWords": {
"message": "Number of Words"
},
"wordSeparator": {
"message": "Word Separator"
},
"passwordHistory": {
"message": "Password History"
},
@@ -906,9 +915,15 @@
"purgeVault": {
"message": "Purge Vault"
},
"purgedOrganizationVault": {
"message": "Purged organization vault."
},
"purgeVaultDesc": {
"message": "Proceed below to delete all items and folders in your vault. Items that belong to an organization that you share with will not be deleted."
},
"purgeOrgVaultDesc": {
"message": "Proceed below to delete all items in the organization's vault."
},
"purgeVaultWarning": {
"message": "Purging your vault is permanent. It cannot be undone."
},
@@ -1165,6 +1180,15 @@
}
}
},
"u2fkeyX": {
"message": "U2F Key $INDEX$",
"placeholders": {
"index": {
"content": "$1",
"example": "2"
}
}
},
"nfcSupport": {
"message": "NFC Support"
},
@@ -1207,12 +1231,27 @@
"twoFactorU2fAdd": {
"message": "Add a FIDO U2F security key to your account"
},
"twoFactorU2fPlugIn": {
"message": "Plug the security key into your computer's USB port."
"removeU2fConfirmation": {
"message": "Are you sure you want to remove this security key?"
},
"readKey": {
"message": "Read Key"
},
"keyCompromised": {
"message": "Key is compromised."
},
"twoFactorU2fGiveName": {
"message": "Give the security key a friendly name to identify it."
},
"twoFactorU2fPlugInReadKey": {
"message": "Plug the security key into your computer's USB port and click the \"Read Key\" button."
},
"twoFactorU2fTouchButton": {
"message": "If the security key has a button, touch it."
},
"twoFactorU2fSaveForm": {
"message": "Save the form."
},
"twoFactorU2fWarning": {
"message": "Due to platform limitations, FIDO U2F cannot be used on all Bitwarden applications. You should enable another two-step login provider so that you can access your account when FIDO U2F cannot be used. Supported platforms:"
},
@@ -1222,11 +1261,11 @@
"twoFactorU2fWaiting": {
"message": "Waiting for you to touch the button on your security key"
},
"twoFactorU2fClickEnable": {
"message": "Click the \"Enable\" button below to enable this security key for two-step login."
"twoFactorU2fClickSave": {
"message": "Click the \"Save\" button below to enable this security key for two-step login."
},
"twoFactorU2fProblemReading": {
"message": "There was a problem reading the security key."
"twoFactorU2fProblemReadingTryAgain": {
"message": "There was a problem reading the security key. Try again."
},
"twoFactorRecoveryYourCode": {
"message": "Your Bitwarden two-step login recovery code"
@@ -1346,8 +1385,8 @@
"additionalStorageGbDesc": {
"message": "# of additional GB"
},
"additionalStorageDesc": {
"message": "Your plan comes with $SIZE$ of encrypted file storage. You can add additional storage for $PRICE$ per GB \/year.",
"additionalStorageIntervalDesc": {
"message": "Your plan comes with $SIZE$ of encrypted file storage. You can add additional storage for $PRICE$ per GB \/$INTERVAL$.",
"placeholders": {
"size": {
"content": "$1",
@@ -1356,6 +1395,10 @@
"price": {
"content": "$2",
"example": "$4.00"
},
"interval": {
"content": "$3",
"example": "'month' or 'year'"
}
}
},
@@ -1871,6 +1914,12 @@
"userDesc": {
"message": "A regular user with access to your organization's collections."
},
"manager": {
"message": "Manager"
},
"managerDesc": {
"message": "Managers can access and manage assigned collections in your organization."
},
"all": {
"message": "All"
},
@@ -1910,8 +1959,8 @@
"changedPassword": {
"message": "Changed account password."
},
"enabled2fa": {
"message": "Enabled two-step login."
"enabledUpdated2fa": {
"message": "Enabled\/updated two-step login."
},
"disabled2fa": {
"message": "Disabled two-step login."
@@ -2444,5 +2493,17 @@
},
"licenseIsExpired": {
"message": "License is expired."
},
"updatedUsers": {
"message": "Updated users"
},
"selected": {
"message": "Selected"
},
"ownership": {
"message": "Ownership"
},
"whoOwnsThisItem": {
"message": "Who owns this item?"
}
}

View File

@@ -37,6 +37,9 @@
"password": {
"message": "Password"
},
"passphrase": {
"message": "Passphrase"
},
"notes": {
"message": "Note"
},
@@ -795,6 +798,12 @@
"length": {
"message": "Lunghezza"
},
"numWords": {
"message": "Number of Words"
},
"wordSeparator": {
"message": "Word Separator"
},
"passwordHistory": {
"message": "Cronologia delle password"
},
@@ -906,9 +915,15 @@
"purgeVault": {
"message": "Svuota Cassaforte"
},
"purgedOrganizationVault": {
"message": "Purged organization vault."
},
"purgeVaultDesc": {
"message": "Procedi in basso per eliminare tutti gli elementi e le cartelle nel Vault. Gli elementi che appartengono a un'organizzazione con cui condividi non verranno eliminati."
},
"purgeOrgVaultDesc": {
"message": "Proceed below to delete all items in the organization's vault."
},
"purgeVaultWarning": {
"message": "Svuotare la tua cassaforte è permanente. Questa azione non è reversibile."
},
@@ -1165,6 +1180,15 @@
}
}
},
"u2fkeyX": {
"message": "U2F Key $INDEX$",
"placeholders": {
"index": {
"content": "$1",
"example": "2"
}
}
},
"nfcSupport": {
"message": "Supporto per NFC"
},
@@ -1207,12 +1231,27 @@
"twoFactorU2fAdd": {
"message": "Aggiungi una chiave di sicurezza di FIDO U2F al tuo account"
},
"twoFactorU2fPlugIn": {
"message": "Inserire la chiave di protezione nella porta USB del tuo computer."
"removeU2fConfirmation": {
"message": "Are you sure you want to remove this security key?"
},
"readKey": {
"message": "Read Key"
},
"keyCompromised": {
"message": "Key is compromised."
},
"twoFactorU2fGiveName": {
"message": "Give the security key a friendly name to identify it."
},
"twoFactorU2fPlugInReadKey": {
"message": "Plug the security key into your computer's USB port and click the \"Read Key\" button."
},
"twoFactorU2fTouchButton": {
"message": "Se la chiave di protezione dispone di un pulsante, premilo."
},
"twoFactorU2fSaveForm": {
"message": "Save the form."
},
"twoFactorU2fWarning": {
"message": "A causa di limitazioni della piattaforma, FIDO U2F non può essere utilizzato su tutte le applicazioni Bitwarden. Si consiglia di abilitare un altro metodo di verifica in due passaggi in modo da poter accedere al tuo account anche dove FIDO U2F non può essere usato. Piattaforme supportate:"
},
@@ -1222,11 +1261,11 @@
"twoFactorU2fWaiting": {
"message": "In attesa che venga premuto il pulsante della tua chiave di sicurezza"
},
"twoFactorU2fClickEnable": {
"message": "Fai click sul pulsante \"Abilita\" per abilitare questa chiave di sicurezza per la verifica in due passaggi."
"twoFactorU2fClickSave": {
"message": "Click the \"Save\" button below to enable this security key for two-step login."
},
"twoFactorU2fProblemReading": {
"message": "Si è verificato un problema durante la lettura della chiave di sicurezza."
"twoFactorU2fProblemReadingTryAgain": {
"message": "There was a problem reading the security key. Try again."
},
"twoFactorRecoveryYourCode": {
"message": "Il tuo codice di recupero Bitwarden per la verifica in due passaggi"
@@ -1346,8 +1385,8 @@
"additionalStorageGbDesc": {
"message": "# di GB aggiuntivi"
},
"additionalStorageDesc": {
"message": "Il piano è dotato di $SIZE$ di archiviazione crittografato. È possibile aggiungere spazio di archiviazione aggiuntivo per $PRICE$ per GB\/anno.",
"additionalStorageIntervalDesc": {
"message": "Your plan comes with $SIZE$ of encrypted file storage. You can add additional storage for $PRICE$ per GB \/$INTERVAL$.",
"placeholders": {
"size": {
"content": "$1",
@@ -1356,6 +1395,10 @@
"price": {
"content": "$2",
"example": "$4.00"
},
"interval": {
"content": "$3",
"example": "'month' or 'year'"
}
}
},
@@ -1871,6 +1914,12 @@
"userDesc": {
"message": "User normale con accesso alle collezioni della tua organizzazione."
},
"manager": {
"message": "Manager"
},
"managerDesc": {
"message": "Managers can access and manage assigned collections in your organization."
},
"all": {
"message": "Tutti"
},
@@ -1910,8 +1959,8 @@
"changedPassword": {
"message": "Password dell'account modificata."
},
"enabled2fa": {
"message": "Verifica in due passaggi abilitata."
"enabledUpdated2fa": {
"message": "Verifica in due passaggi abilitata\/aggiornata."
},
"disabled2fa": {
"message": "Verifica in due passaggi disabilitata."
@@ -2444,5 +2493,17 @@
},
"licenseIsExpired": {
"message": "La licenza è scaduta."
},
"updatedUsers": {
"message": "Updated users"
},
"selected": {
"message": "Selected"
},
"ownership": {
"message": "Ownership"
},
"whoOwnsThisItem": {
"message": "Who owns this item?"
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -37,6 +37,9 @@
"password": {
"message": "비밀번호"
},
"passphrase": {
"message": "Passphrase"
},
"notes": {
"message": "메모"
},
@@ -487,25 +490,25 @@
"message": "Log Out"
},
"ok": {
"message": "Ok"
"message": "확인"
},
"yes": {
"message": "Yes"
"message": ""
},
"no": {
"message": "No"
"message": "아니오"
},
"loginOrCreateNewAccount": {
"message": "Log in or create a new account to access your secure vault."
"message": "안전 보관함에 접근하려면 로그인하거나 새 계정을 만드세요."
},
"createAccount": {
"message": "Create Account"
"message": "계정 만들기"
},
"logIn": {
"message": "Log In"
"message": "로그인"
},
"submit": {
"message": "Submit"
"message": "보내기"
},
"emailAddressDesc": {
"message": "You'll use your email address to log in."
@@ -517,82 +520,82 @@
"message": "What should we call you?"
},
"masterPass": {
"message": "Master Password"
"message": "마스터 비밀번호"
},
"masterPassDesc": {
"message": "The master password is the password you use to access your vault. It is very important that you do not forget your master password. There is no way to recover the password in the event that you forget it."
"message": "마스터 비밀번호는 보관함을 열 때 필요한 비밀번호입니다. 절대 마스터 비밀번호를 잊어버리지 마세요. 잊어버리면 복구할 수 있는 방법이 없습니다."
},
"masterPassHintDesc": {
"message": "A master password hint can help you remember your password if you forget it."
"message": "마스터 비밀번호 힌트는 마스터 비밀번호를 잊었을 때 도움이 될 수 있습니다."
},
"reTypeMasterPass": {
"message": "Re-type Master Password"
"message": "마스터 비밀번호 다시 입력"
},
"masterPassHint": {
"message": "Master Password Hint (optional)"
"message": "마스터 비밀번호 힌트 (선택)"
},
"masterPassHintLabel": {
"message": "Master Password Hint"
"message": "마스터 비밀번호 힌트"
},
"settings": {
"message": "Settings"
"message": "설정"
},
"passwordHint": {
"message": "Password Hint"
"message": "비밀번호 힌트"
},
"enterEmailToGetHint": {
"message": "Enter your account email address to receive your master password hint."
"message": "마스터 비밀번호 힌트를 받으려면 계정의 이메일 주소를 입력하세요."
},
"getMasterPasswordHint": {
"message": "Get master password hint"
"message": "마스터 비밀번호 힌트 얻기"
},
"emailRequired": {
"message": "Email address is required."
"message": "이메일은 반드시 입력해야 합니다."
},
"invalidEmail": {
"message": "Invalid email address."
"message": "잘못된 이메일 주소입니다."
},
"masterPassRequired": {
"message": "Master password is required."
"message": "마스터 비밀번호는 반드시 입력해야 합니다."
},
"masterPassLength": {
"message": "Master password must be at least 8 characters long."
"message": "마스터 비밀번호는 최소 8자 이상이어야 합니다."
},
"masterPassDoesntMatch": {
"message": "Master password confirmation does not match."
"message": "마스터 비밀번호 확인과 마스터 비밀번호가 일치하지 않습니다."
},
"newAccountCreated": {
"message": "Your new account has been created! You may now log in."
"message": "계정 생성이 완료되었습니다! 이제 로그인하실 수 있습니다."
},
"masterPassSent": {
"message": "We've sent you an email with your master password hint."
"message": "마스터 비밀번호 힌트가 담긴 이메일을 보냈습니다."
},
"unexpectedError": {
"message": "An unexpected error has occurred."
"message": "예기치 못한 오류가 발생했습니다."
},
"emailAddress": {
"message": "Email Address"
"message": "이메일 주소"
},
"yourVaultIsLocked": {
"message": "Your vault is locked. Verify your master password to continue."
"message": "보관함이 잠겨 있습니다. 마스터 비밀번호를 입력하여 계속하세요."
},
"unlock": {
"message": "Unlock"
"message": "잠금 해제"
},
"invalidMasterPassword": {
"message": "Invalid master password"
"message": "잘못된 마스터 비밀번호"
},
"lockNow": {
"message": "Lock Now"
"message": "지금 잠그기"
},
"noItemsInList": {
"message": "There are no items to list."
"message": "항목이 없습니다."
},
"noCollectionsInList": {
"message": "There are no collections to list."
},
"noGroupsInList": {
"message": "There are no groups to list."
"message": "그룹이 없습니다."
},
"noUsersInList": {
"message": "There are no users to list."
@@ -601,7 +604,7 @@
"message": "There are no events to list."
},
"newOrganization": {
"message": "New Organization"
"message": "새 조직"
},
"noOrganizationsList": {
"message": "You do not belong to any organizations. Organizations allow you to securely share items with other users."
@@ -616,10 +619,10 @@
}
},
"enterVerificationCodeApp": {
"message": "Enter the 6 digit verification code from your authenticator app."
"message": "인증 앱에서 6자리 인증 코드를 입력하세요."
},
"enterVerificationCodeEmail": {
"message": "Enter the 6 digit verification code that was emailed to $EMAIL$.",
"message": "$EMAIL$ 주소로 전송된 6자리 인증 코드를 입력하세요.",
"placeholders": {
"email": {
"content": "$1",
@@ -628,7 +631,7 @@
}
},
"verificationCodeEmailSent": {
"message": "Verification email sent to $EMAIL$.",
"message": "$EMAIL$ 주소로 인증 이메일을 보냈습니다.",
"placeholders": {
"email": {
"content": "$1",
@@ -637,13 +640,13 @@
}
},
"rememberMe": {
"message": "Remember me"
"message": "기억하기"
},
"sendVerificationCodeEmailAgain": {
"message": "Send verification code email again"
"message": "인증 코드 이메일 다시 보내기"
},
"useAnotherTwoStepMethod": {
"message": "Use another two-step login method"
"message": "다른 2단계 인증 사용"
},
"insertYubiKey": {
"message": "YubiKey를 컴퓨터의 USB 포트에 삽입하고 버튼을 누르세요."
@@ -795,6 +798,12 @@
"length": {
"message": "길이"
},
"numWords": {
"message": "Number of Words"
},
"wordSeparator": {
"message": "Word Separator"
},
"passwordHistory": {
"message": "비밀번호 변경 기록"
},
@@ -906,9 +915,15 @@
"purgeVault": {
"message": "Purge Vault"
},
"purgedOrganizationVault": {
"message": "Purged organization vault."
},
"purgeVaultDesc": {
"message": "Proceed below to delete all items and folders in your vault. Items that belong to an organization that you share with will not be deleted."
},
"purgeOrgVaultDesc": {
"message": "Proceed below to delete all items in the organization's vault."
},
"purgeVaultWarning": {
"message": "Purging your vault is permanent. It cannot be undone."
},
@@ -1165,6 +1180,15 @@
}
}
},
"u2fkeyX": {
"message": "U2F Key $INDEX$",
"placeholders": {
"index": {
"content": "$1",
"example": "2"
}
}
},
"nfcSupport": {
"message": "NFC 지원"
},
@@ -1207,12 +1231,27 @@
"twoFactorU2fAdd": {
"message": "Add a FIDO U2F security key to your account"
},
"twoFactorU2fPlugIn": {
"message": "Plug the security key into your computer's USB port."
"removeU2fConfirmation": {
"message": "Are you sure you want to remove this security key?"
},
"readKey": {
"message": "Read Key"
},
"keyCompromised": {
"message": "Key is compromised."
},
"twoFactorU2fGiveName": {
"message": "Give the security key a friendly name to identify it."
},
"twoFactorU2fPlugInReadKey": {
"message": "Plug the security key into your computer's USB port and click the \"Read Key\" button."
},
"twoFactorU2fTouchButton": {
"message": "If the security key has a button, touch it."
},
"twoFactorU2fSaveForm": {
"message": "Save the form."
},
"twoFactorU2fWarning": {
"message": "Due to platform limitations, FIDO U2F cannot be used on all Bitwarden applications. You should enable another two-step login provider so that you can access your account when FIDO U2F cannot be used. Supported platforms:"
},
@@ -1222,11 +1261,11 @@
"twoFactorU2fWaiting": {
"message": "Waiting for you to touch the button on your security key"
},
"twoFactorU2fClickEnable": {
"message": "Click the \"Enable\" button below to enable this security key for two-step login."
"twoFactorU2fClickSave": {
"message": "Click the \"Save\" button below to enable this security key for two-step login."
},
"twoFactorU2fProblemReading": {
"message": "There was a problem reading the security key."
"twoFactorU2fProblemReadingTryAgain": {
"message": "There was a problem reading the security key. Try again."
},
"twoFactorRecoveryYourCode": {
"message": "Your Bitwarden two-step login recovery code"
@@ -1346,8 +1385,8 @@
"additionalStorageGbDesc": {
"message": "# of additional GB"
},
"additionalStorageDesc": {
"message": "Your plan comes with $SIZE$ of encrypted file storage. You can add additional storage for $PRICE$ per GB \/year.",
"additionalStorageIntervalDesc": {
"message": "Your plan comes with $SIZE$ of encrypted file storage. You can add additional storage for $PRICE$ per GB \/$INTERVAL$.",
"placeholders": {
"size": {
"content": "$1",
@@ -1356,6 +1395,10 @@
"price": {
"content": "$2",
"example": "$4.00"
},
"interval": {
"content": "$3",
"example": "'month' or 'year'"
}
}
},
@@ -1871,6 +1914,12 @@
"userDesc": {
"message": "A regular user with access to your organization's collections."
},
"manager": {
"message": "Manager"
},
"managerDesc": {
"message": "Managers can access and manage assigned collections in your organization."
},
"all": {
"message": "All"
},
@@ -1910,8 +1959,8 @@
"changedPassword": {
"message": "Changed account password."
},
"enabled2fa": {
"message": "Enabled two-step login."
"enabledUpdated2fa": {
"message": "Enabled\/updated two-step login."
},
"disabled2fa": {
"message": "Disabled two-step login."
@@ -2444,5 +2493,17 @@
},
"licenseIsExpired": {
"message": "라이선스가 만료되었습니다."
},
"updatedUsers": {
"message": "Updated users"
},
"selected": {
"message": "Selected"
},
"ownership": {
"message": "Ownership"
},
"whoOwnsThisItem": {
"message": "Who owns this item?"
}
}

View File

@@ -37,6 +37,9 @@
"password": {
"message": "Passord"
},
"passphrase": {
"message": "Passfrase"
},
"notes": {
"message": "Notater"
},
@@ -795,6 +798,12 @@
"length": {
"message": "Lengde"
},
"numWords": {
"message": "Antall ord"
},
"wordSeparator": {
"message": "Ordadskiller"
},
"passwordHistory": {
"message": "Passordhistorikk"
},
@@ -853,16 +862,16 @@
"message": "Bekreft det nye superpassordet"
},
"encKeySettings": {
"message": "Encryption Key Settings"
"message": "Innstillinger for krypteringsnøkkel"
},
"kdfAlgorithm": {
"message": "KDF Algorithm"
"message": "KDF-algoritme"
},
"kdfIterations": {
"message": "KDF Iterations"
"message": "KDF-gjentakelser"
},
"kdfIterationsDesc": {
"message": "Higher KDF iterations can help protect your master password from being brute forced by an attacker. We recommend a value of $VALUE$ or more.",
"message": "Flere KDF-gjentakelser kan hjelpe til med å beskytte superpassordet fra å bli tvunget inn i av en angriper. Vi anbefaler en verdi på $VALUE$ eller mer.",
"placeholders": {
"value": {
"content": "$1",
@@ -871,7 +880,7 @@
}
},
"kdfIterationsWarning": {
"message": "Setting your KDF iterations too high could result in poor performance when logging into (and unlocking) Bitwarden on devices with slower CPUs. We recommend that you increase the value in increments of $INCREMENT$ and then test all of your devices.",
"message": "Å velge for mange KDF-gjentakelser kan føre til dårlig ytelse når du logger inn på (og låser opp) Bitwarden på enheter med tregere CPUer. Vi anbefaler at du øker verdien trinnvis, $INCREMENT$ om gangen, og å så teste det på alle dine enheter.",
"placeholders": {
"increment": {
"content": "$1",
@@ -880,10 +889,10 @@
}
},
"changeKdf": {
"message": "Change KDF"
"message": "Endre KDF"
},
"encKeySettingsChanged": {
"message": "Encryption Key Settings Changed"
"message": "Krypteringsnøkkelinnstillingene har blitt endret"
},
"dangerZone": {
"message": "Faresone"
@@ -906,9 +915,15 @@
"purgeVault": {
"message": "Tøm hvelvet"
},
"purgedOrganizationVault": {
"message": "Tømte organisasjonens hvelv."
},
"purgeVaultDesc": {
"message": "Fortsett nedenfor for å slette alle gjenstander og mapper i ditt hvelv. Gjenstander som tilhører en organisasjon som du deler med, vil ikke bli slettet."
},
"purgeOrgVaultDesc": {
"message": "Fortsett nedenfor for å slette alle gjenstandene i organisasjonens hvelv."
},
"purgeVaultWarning": {
"message": "Å tømme hvelvet ditt er permanent. Det kan ikke bli angret på."
},
@@ -1076,10 +1091,10 @@
"message": "Et Premium-medlemskap er påkrevd for å bruke denne funksjonen."
},
"youHavePremiumAccess": {
"message": "You have premium access"
"message": "Du har Premium-tilgang"
},
"alreadyPremiumFromOrg": {
"message": "You already have access to premium features because of an organization you are a member of."
"message": "Du har allerede tilgang til Premium-funksjoner takket være en organisasjon som du er medlem av."
},
"manage": {
"message": "Behandle"
@@ -1165,6 +1180,15 @@
}
}
},
"u2fkeyX": {
"message": "U2F-nøkkel $INDEX$",
"placeholders": {
"index": {
"content": "$1",
"example": "2"
}
}
},
"nfcSupport": {
"message": "NFC-støtte"
},
@@ -1207,12 +1231,27 @@
"twoFactorU2fAdd": {
"message": "Legg til en FIDO U2F-sikkerhetsnøkkel til din konto"
},
"twoFactorU2fPlugIn": {
"message": "Sett inn sikkerhetsnøkkelen i din datamaskins USB-port."
"removeU2fConfirmation": {
"message": "Er du sikker på at du vil fjerne denne sikkerhetsnøkkelen?"
},
"readKey": {
"message": "Les nøkkel"
},
"keyCompromised": {
"message": "Nøkkelen er kompromittert."
},
"twoFactorU2fGiveName": {
"message": "Gi sikkerhetsnøkkelen et vennlig navn for å identifisere den."
},
"twoFactorU2fPlugInReadKey": {
"message": "Sett sikkerhetsnøkkelen inn i din datamaskins USB-port, og klikk på «Les nøkkel»-knappen."
},
"twoFactorU2fTouchButton": {
"message": "Dersom sikkerhetsnøkkelen har en knapp, trykk på den."
},
"twoFactorU2fSaveForm": {
"message": "Lagre skjemaet."
},
"twoFactorU2fWarning": {
"message": "På grunn av plattformbegrensninger, kan FIDO U2F ikke bli brukt på alle Bitwarden-apper. Du burde skru på en annen 2-trinnsinnloggingsleverandør, sånn at du kan få tilgang til din konto når FIDO U2F ikke kan bli brukt. Støttede plattformer:"
},
@@ -1222,11 +1261,11 @@
"twoFactorU2fWaiting": {
"message": "Venter på at du skal trykke på knappen på din sikkerhetsnøkkel"
},
"twoFactorU2fClickEnable": {
"message": "Klikk på «Aktiver»-knappen nedenfor for å aktivere denne sikkerhetsnøkkelen for 2-trinnsinnlogging."
"twoFactorU2fClickSave": {
"message": "Klikk på «Lagre»-knappen nedenfor for å aktivere denne sikkerhetsnøkkelen for 2-trinnsinnlogging."
},
"twoFactorU2fProblemReading": {
"message": "Det var et problem med å lese sikkerhetsnøkkelen."
"twoFactorU2fProblemReadingTryAgain": {
"message": "Det oppsto et problem med å lese sikkerhetsnøkkelen. Prøv igjen."
},
"twoFactorRecoveryYourCode": {
"message": "Din 2-trinnsinnloggingsgjenopprettingskode for Bitwarden"
@@ -1301,7 +1340,7 @@
"message": "En feil oppstod under forsøk på å laste inn rapporten. Prøv igjen."
},
"billingAndLicensing": {
"message": "Regninger og lisensiering"
"message": "Fakturering og lisensiering"
},
"goPremium": {
"message": "Oppgrader til Premium",
@@ -1346,8 +1385,8 @@
"additionalStorageGbDesc": {
"message": "Antall ekstra GB"
},
"additionalStorageDesc": {
"message": "Din funksjonsplan kommer med $SIZE$ kryptert fillagring. Du kan legge til ytterligere lagring for $PRICE$ per GB per år.",
"additionalStorageIntervalDesc": {
"message": "Din funksjonsplan kommer med $SIZE$ kryptert fillagring. Du kan legge til ytterligere lagring for $PRICE$ per GB per $INTERVAL$.",
"placeholders": {
"size": {
"content": "$1",
@@ -1356,6 +1395,10 @@
"price": {
"content": "$2",
"example": "$4.00"
},
"interval": {
"content": "$3",
"example": "'month' or 'year'"
}
}
},
@@ -1701,7 +1744,7 @@
"message": "Lokal betjening (valgfritt)"
},
"usersGetPremium": {
"message": "Users get access to premium membership features"
"message": "Brukerne får tilgang til Premium-medlemskapsfunksjoner"
},
"controlAccessWithGroups": {
"message": "Kontroller brukertilgang med grupper"
@@ -1863,13 +1906,19 @@
"message": "Admin"
},
"adminDesc": {
"message": " Administratorer kan få tilgang til og behandle alle gjenstander, samlinger og brukere i din organisasjon."
"message": "Administratorer kan få tilgang til og behandle alle gjenstander, samlinger og brukere i din organisasjon."
},
"user": {
"message": "Bruker"
},
"userDesc": {
"message": "En vanlig bruker med tilgang til din organisasjons samlinger."
"message": "En vanlig bruker med tilgang til tilegnede samlinger i din organisasjon."
},
"manager": {
"message": "Behandler"
},
"managerDesc": {
"message": "Behandlere har tilgang til og kan behandle tilegnede samlinger i din organisasjon."
},
"all": {
"message": "Alle"
@@ -1910,8 +1959,8 @@
"changedPassword": {
"message": "Endret kontopassordet."
},
"enabled2fa": {
"message": "Skrudde på 2-trinnsinnlogging."
"enabledUpdated2fa": {
"message": "Aktiverte\/oppdaterte 2-trinnsinnloggingen."
},
"disabled2fa": {
"message": "Skrudde av 2-trinnsinnlogging."
@@ -2142,10 +2191,10 @@
}
},
"confirmUsers": {
"message": "Confirm Users"
"message": "Bekreft brukere"
},
"usersNeedConfirmed": {
"message": "You have users that have accepted their invitation, but still need to be confirmed. Users will not have access to the organization until they are confirmed."
"message": "Du har brukere som har akseptert sin invitasjon, men som fortsatt må bekreftes. Brukere vil ikke ha tilgang til organisasjonen før de er bekreftet."
},
"startDate": {
"message": "Startdato"
@@ -2444,5 +2493,17 @@
},
"licenseIsExpired": {
"message": "Lisensen har utløpt."
},
"updatedUsers": {
"message": "Oppdaterte brukere"
},
"selected": {
"message": "Valgt"
},
"ownership": {
"message": "Eierskap"
},
"whoOwnsThisItem": {
"message": "Hvem eier dette elementet?"
}
}

View File

@@ -37,6 +37,9 @@
"password": {
"message": "Wachtwoord"
},
"passphrase": {
"message": "Wachtwoordzin"
},
"notes": {
"message": "Notities"
},
@@ -795,6 +798,12 @@
"length": {
"message": "Lengte"
},
"numWords": {
"message": "Aantal woorden"
},
"wordSeparator": {
"message": "Woordscheidingsteken"
},
"passwordHistory": {
"message": "Wachtwoordgeschiedenis"
},
@@ -906,9 +915,15 @@
"purgeVault": {
"message": "Kluis legen"
},
"purgedOrganizationVault": {
"message": "Organisatie kluis leeg gemaakt."
},
"purgeVaultDesc": {
"message": "Ga hieronder verder om alle items en mappen in uw kluis te verwijderen. Items die behoren tot een organisatie waarmee u deelt, worden niet verwijderd."
},
"purgeOrgVaultDesc": {
"message": "Ga hieronder verder om alle objecten in de organisatie kluis te verwijderen."
},
"purgeVaultWarning": {
"message": "Het leegmaken van uw webkluis is permanent en kan niet ongedaan worden gemaakt."
},
@@ -1165,6 +1180,15 @@
}
}
},
"u2fkeyX": {
"message": "U2F sleutel $INDEX$",
"placeholders": {
"index": {
"content": "$1",
"example": "2"
}
}
},
"nfcSupport": {
"message": "NFC ondersteuning"
},
@@ -1207,12 +1231,27 @@
"twoFactorU2fAdd": {
"message": "Voeg een FIDO U2F beveiligingssleutel toe aan uw account"
},
"twoFactorU2fPlugIn": {
"message": "Stop de beveiligingssleutel in een USB-poort van uw computer."
"removeU2fConfirmation": {
"message": "Weet je zeker dat je deze beveiligingssleutel wilt verwijderen?"
},
"readKey": {
"message": "Lees sleutel"
},
"keyCompromised": {
"message": "Sleutel is gecompromitteerd."
},
"twoFactorU2fGiveName": {
"message": "Geef de beveiligingssleutel een beschrijvende naam om deze te identificeren."
},
"twoFactorU2fPlugInReadKey": {
"message": "Stop de beveiligingssleutel in een USB-poort van uw computer en klik op de \"Lees sleutel\" knop."
},
"twoFactorU2fTouchButton": {
"message": "Druk op de knop van de beveiligingssleutel wanneer deze aanwezig is."
},
"twoFactorU2fSaveForm": {
"message": "Sla het formulier op."
},
"twoFactorU2fWarning": {
"message": "Vanwege platformbeperkingen kunnen FIDO U2F niet in alle Bitwarden applicaties gebruikt worden. U zou een andere tweestapsaanmeldingsaanbieder moeten instellen voor wanneer FIDO U2F niet gebruikt kunnen worden. De volgende platformen worden ondersteunt:"
},
@@ -1222,11 +1261,11 @@
"twoFactorU2fWaiting": {
"message": "Wachten tot u op de knop van de beveiligingssleutel hebt gedrukt"
},
"twoFactorU2fClickEnable": {
"message": "Klik op de \"Activeren\"-knop hier beneden om deze beveiligingssleutel te activeren als tweestapsaanmeldingsmethode."
"twoFactorU2fClickSave": {
"message": "Klik op de \"Opslaan\"-knop hier beneden om deze beveiligingssleutel te activeren als tweestapsaanmeldingsmethode."
},
"twoFactorU2fProblemReading": {
"message": "Er was een probleem met het lezen van de beveiligingssleutel."
"twoFactorU2fProblemReadingTryAgain": {
"message": "Er was een probleem met het lezen van de beveiligingssleutel. Probeer het nogmaals."
},
"twoFactorRecoveryYourCode": {
"message": "Uw Bitwarden tweestapsaanmelding herstelcode"
@@ -1346,8 +1385,8 @@
"additionalStorageGbDesc": {
"message": "# extra GB"
},
"additionalStorageDesc": {
"message": "Uw abonnement omvat $SIZE$ gecodeerde bestandsopslag. U kunt extra opslagruimte toevoegen voor $PRICE$ per GB \/ jaar.",
"additionalStorageIntervalDesc": {
"message": "Uw abonnement omvat $SIZE$ gecodeerde bestandsopslag. U kunt extra opslagruimte toevoegen voor $PRICE$ per GB \/$INTERVAL$.",
"placeholders": {
"size": {
"content": "$1",
@@ -1356,6 +1395,10 @@
"price": {
"content": "$2",
"example": "$4.00"
},
"interval": {
"content": "$3",
"example": "'month' or 'year'"
}
}
},
@@ -1800,22 +1843,22 @@
"message": "Deze groep kan alle items inzien en bewerken."
},
"groupAccessSelectedCollections": {
"message": "Deze groep heeft alleen toegang tot de geselecteerde collecties."
"message": "Deze groep heeft alleen toegang tot de geselecteerde verzamelingen."
},
"readOnly": {
"message": "Alleen-lezen"
},
"newCollection": {
"message": "Nieuwe Collectie"
"message": "Nieuwe verzameling"
},
"addCollection": {
"message": "Collectie toevoegen"
"message": "Verzameling toevoegen"
},
"editCollection": {
"message": "Collectie bewerken"
"message": "Verzameling bewerken"
},
"deleteCollectionConfirmation": {
"message": "Weet u zeker dat u deze collectie wilt verwijderen?"
"message": "Weet u zeker dat u deze verzameling wilt verwijderen?"
},
"editUser": {
"message": "Gebruiker bewerken"
@@ -1839,7 +1882,7 @@
"message": "Deze gebruiker kan alle items inzien en bewerken."
},
"userAccessSelectedCollections": {
"message": "Deze gebruiker heeft alleen toegang tot de geselecteerde collecties."
"message": "Deze gebruiker heeft alleen toegang tot de geselecteerde verzamelingen."
},
"search": {
"message": "Zoeken"
@@ -1863,13 +1906,19 @@
"message": "Administrator"
},
"adminDesc": {
"message": "Administrators hebben toegang tot alle items, collecties en gebruikers binnen uw organisatie en kunnen deze ook beheren."
"message": "Administrators hebben toegang tot alle items, verzamelingen en gebruikers binnen uw organisatie en kunnen deze ook beheren."
},
"user": {
"message": "Gebruiker"
},
"userDesc": {
"message": "Een normale gebruiker met toegang tot de collecties van uw organisatie."
"message": "Een normale gebruiker met toegang tot de verzamelingen van uw organisatie."
},
"manager": {
"message": "Beheerder"
},
"managerDesc": {
"message": "Beheerders hebben toegang tot toegewezen collecties binnen uw organisatie en kunnen deze ook beheren."
},
"all": {
"message": "Alles"
@@ -1910,8 +1959,8 @@
"changedPassword": {
"message": "Veranderd account wachtwoord."
},
"enabled2fa": {
"message": "Tweestapsaanmelden geactiveerd."
"enabledUpdated2fa": {
"message": "Tweestapsaanmelden geactiveerd\/bijgewerkt."
},
"disabled2fa": {
"message": "Tweestapsaanmelden uitgeschakeld."
@@ -2444,5 +2493,17 @@
},
"licenseIsExpired": {
"message": "De licentie is verlopen."
},
"updatedUsers": {
"message": "Bijgewerkte gebruikers"
},
"selected": {
"message": "Geselecteerde"
},
"ownership": {
"message": "Eigendom"
},
"whoOwnsThisItem": {
"message": "Wie is eigenaar van dit object?"
}
}

View File

@@ -37,6 +37,9 @@
"password": {
"message": "Hasło"
},
"passphrase": {
"message": "Hasło"
},
"notes": {
"message": "Notatki"
},
@@ -680,7 +683,7 @@
"message": "Klucz bezpieczeństwa YubiKey OTP"
},
"yubiKeyDesc": {
"message": "Użyj YubiKey jako metody dostępu do konta. Działa z YubiKey 4, 4 Nano, 4C i urządzeniami NEO."
"message": "Użyj YubiKey jako metody dostępu do konta. Działa z YubiKey serii 4, serii 5 i urządzeniami NEO."
},
"duoDesc": {
"message": "Weryfikacja z użyciem Duo Security poprzez aplikację Duo Mobile, SMS, połączenie telefoniczne lub klucz bezpieczeństwa U2F.",
@@ -795,6 +798,12 @@
"length": {
"message": "Długość"
},
"numWords": {
"message": "Liczba słów"
},
"wordSeparator": {
"message": "Separator wyrazów"
},
"passwordHistory": {
"message": "Historia haseł"
},
@@ -906,9 +915,15 @@
"purgeVault": {
"message": "Wyczyść sejf"
},
"purgedOrganizationVault": {
"message": "Wyczyszczono sejf organizacji."
},
"purgeVaultDesc": {
"message": "Kontynuuj poniżej, aby usunąć wszystkie elementy i foldery z Twojego sejfu. Elementy udostępnione organizacji nie zostaną usunięte."
},
"purgeOrgVaultDesc": {
"message": "Przejdź poniżej, aby usunąć wszystkie elementy z sejfu organizacji."
},
"purgeVaultWarning": {
"message": "Wykasowanie sejfu jest nieodwracalne. Akcja ta nie może być cofnięta."
},
@@ -1136,7 +1151,7 @@
"message": "Dodaj nowy klucz YubiKey do swojego konta"
},
"twoFactorYubikeyPlugIn": {
"message": "Włóż klucz YubiKey (NEO lub seria 4) do portu USB swojego komputera."
"message": "Włóż YubiKey do portu USB swojego komputera."
},
"twoFactorYubikeySelectKey": {
"message": "Wybierz poniżej pierwsze wolne pole YubiKey."
@@ -1165,6 +1180,15 @@
}
}
},
"u2fkeyX": {
"message": "Klucz U2F $INDEX$",
"placeholders": {
"index": {
"content": "$1",
"example": "2"
}
}
},
"nfcSupport": {
"message": "Obsługa NFC"
},
@@ -1207,12 +1231,27 @@
"twoFactorU2fAdd": {
"message": "Dodaj klucz bezpieczeństwa FIDO U2F do swojego konta"
},
"twoFactorU2fPlugIn": {
"message": "Włóż klucz bezpieczeństwa do portu USB swojego komputera."
"removeU2fConfirmation": {
"message": "Czy jesteś pewien, że chcesz usunąć ten klucz bezpieczeństwa?"
},
"readKey": {
"message": "Odczytaj klucz"
},
"keyCompromised": {
"message": "Klucz został złamany."
},
"twoFactorU2fGiveName": {
"message": "Nadaj kluczowi bezpieczeństwa przyjazną nazwę, aby go łatwiej zidentyfikować."
},
"twoFactorU2fPlugInReadKey": {
"message": "Podłącz klucz bezpieczeństwa do portu USB komputera i kliknij przycisk \"Odczytaj klucz\"."
},
"twoFactorU2fTouchButton": {
"message": "Jeśli klucz bezpieczeństwa posiada przycisk, dotknij go."
},
"twoFactorU2fSaveForm": {
"message": "Zapisz formularz."
},
"twoFactorU2fWarning": {
"message": "Z powodu ograniczeń platformy, FIDO U2F nie może być używany we wszystkich aplikacjach Bitwarden. Powinieneś włączyć inną metodę dwustopniowego logowania, tak abyś miał dostęp do swojego konta w sytuacji, gdy FIDO U2F nie będzie mógł zostać użyty. Wspierane platformy:"
},
@@ -1222,11 +1261,11 @@
"twoFactorU2fWaiting": {
"message": "Oczekiwanie na dotknięcie klucza bezpieczeństwa"
},
"twoFactorU2fClickEnable": {
"message": "Kliknij przycisk \"Włącz\" poniżej, aby aktywować dwustopniowe logowanie za pomocą tego klucza bezpieczeństwa."
"twoFactorU2fClickSave": {
"message": "Kliknij przycisk \"Zapisz\" poniżej, aby włączyć ten klucz bezpieczeństwa dla dwustopniowego logowania."
},
"twoFactorU2fProblemReading": {
"message": "Wystąpił problem z odczytem klucza bezpieczeństwa."
"twoFactorU2fProblemReadingTryAgain": {
"message": "Wystąpił problem z odczytem klucza bezpieczeństwa. Spróbuj ponownie."
},
"twoFactorRecoveryYourCode": {
"message": "Twój kod odzyskiwania Bitwarden"
@@ -1346,8 +1385,8 @@
"additionalStorageGbDesc": {
"message": "# dodatkowych GB"
},
"additionalStorageDesc": {
"message": "Twój plan zawiera $SIZE$ szyfrowanej przestrzeni. Możesz zwiększyć rozmiar dostępnej przestrzeni za $PRICE$ za GB\/rok.",
"additionalStorageIntervalDesc": {
"message": "Twój plan zawiera $SIZE$ szyfrowanej przestrzeni. Możesz zwiększyć rozmiar dostępnej przestrzeni za $PRICE$ za GB\/$INTERVAL$.",
"placeholders": {
"size": {
"content": "$1",
@@ -1356,6 +1395,10 @@
"price": {
"content": "$2",
"example": "$4.00"
},
"interval": {
"content": "$3",
"example": "'month' or 'year'"
}
}
},
@@ -1863,7 +1906,7 @@
"message": "Administrator"
},
"adminDesc": {
"message": " Administratorzy mają dostęp do wszystkich elementów, kolekcji i użytkowników w Twojej organizacji."
"message": "Administratorzy mają dostęp do wszystkich elementów, kolekcji i użytkowników w Twojej organizacji."
},
"user": {
"message": "Użytkownik"
@@ -1871,6 +1914,12 @@
"userDesc": {
"message": "Zwykły użytkownik, posiadający dostęp do kolekcji w Twojej organizacji."
},
"manager": {
"message": "Menedżer"
},
"managerDesc": {
"message": "Menedżerowie mają dostęp i mogą zarządzać kolekcjami przypisanymi do Twojej organizacji."
},
"all": {
"message": "Wszystkie"
},
@@ -1910,8 +1959,8 @@
"changedPassword": {
"message": "Zmieniono hasło do konta."
},
"enabled2fa": {
"message": "Włączono dwustopniowe logowanie."
"enabledUpdated2fa": {
"message": "Włączono\/zaktualizowano dwustopniowe logowanie."
},
"disabled2fa": {
"message": "Wyłączono dwustopniowe logowanie."
@@ -2444,5 +2493,17 @@
},
"licenseIsExpired": {
"message": "Licencja wygasła."
},
"updatedUsers": {
"message": "Zaktualizowano użytkowników"
},
"selected": {
"message": "Wybrano"
},
"ownership": {
"message": "Własność"
},
"whoOwnsThisItem": {
"message": "Kto jest właścicielem tego elementu?"
}
}

View File

@@ -10,7 +10,7 @@
}
},
"whatTypeOfItem": {
"message": "Que item é esse?"
"message": "Que tipo de item é este?"
},
"name": {
"message": "Nome"
@@ -37,6 +37,9 @@
"password": {
"message": "Senha"
},
"passphrase": {
"message": "Frase Secreta"
},
"notes": {
"message": "Notas"
},
@@ -680,7 +683,7 @@
"message": "Chave de Segurança YubiKey OTP"
},
"yubiKeyDesc": {
"message": "Utilize um YubiKey para acessar a sua conta. Funciona com YubiKey 4, 4 Nano, 4C, e dispositivos NEO."
"message": "Utilize uma YubiKey para acessar a sua conta. Funciona com YubiKey 4, 4 Nano, 4C, e dispositivos NEO."
},
"duoDesc": {
"message": "Verifique com o Duo Security utilizando o aplicativo Duo Mobile, SMS, chamada telefônica, ou chave de segurança U2F.",
@@ -795,6 +798,12 @@
"length": {
"message": "Comprimento"
},
"numWords": {
"message": "Número de Palavras"
},
"wordSeparator": {
"message": "Separador de Palavra"
},
"passwordHistory": {
"message": "Histórico de Senha"
},
@@ -906,9 +915,15 @@
"purgeVault": {
"message": "Limpar o Cofre"
},
"purgedOrganizationVault": {
"message": "Cofre da organização limpado."
},
"purgeVaultDesc": {
"message": "Continue abaixo para excluir todos os itens e pastas do seu cofre. Itens que pertencem a uma organização com a qual você compartilha não serão excluídos."
},
"purgeOrgVaultDesc": {
"message": "Continue abaixo para excluir todos os itens no cofre da organização."
},
"purgeVaultWarning": {
"message": "Limpando o seu cofre permanentemente. Não pode ser desfeito."
},
@@ -1136,7 +1151,7 @@
"message": "Adicionar um novo Yubikey à sua conta"
},
"twoFactorYubikeyPlugIn": {
"message": "Conecte o YubiKey (NEO ou 4 series) a porta USB do seu computador."
"message": "Conecte o YubiKey na porta USB do seu computador."
},
"twoFactorYubikeySelectKey": {
"message": "Selecione o YubiKey no primeiro campo vazio abaixo."
@@ -1165,6 +1180,15 @@
}
}
},
"u2fkeyX": {
"message": "Chave U2F $INDEX$",
"placeholders": {
"index": {
"content": "$1",
"example": "2"
}
}
},
"nfcSupport": {
"message": "Suporte a NFC"
},
@@ -1207,12 +1231,27 @@
"twoFactorU2fAdd": {
"message": "Adicione uma chave de segurança FIDO U2F à sua conta"
},
"twoFactorU2fPlugIn": {
"message": "Conecte a chave de segurança na porta USB do seu computador."
"removeU2fConfirmation": {
"message": "Tem certeza que deseja remover esta chave de segurança?"
},
"readKey": {
"message": "Chave de Leitura"
},
"keyCompromised": {
"message": "A chave está comprometida."
},
"twoFactorU2fGiveName": {
"message": "Dê à chave de segurança um nome amigável para identificá-la."
},
"twoFactorU2fPlugInReadKey": {
"message": "Conecte a chave de segurança na porta USB do seu computador e clique no botão \"Ler Chave\"."
},
"twoFactorU2fTouchButton": {
"message": "Se a chave de segurança tiver um botão, toque nele."
},
"twoFactorU2fSaveForm": {
"message": "Salvar o formulário."
},
"twoFactorU2fWarning": {
"message": "Devido as limitações da plataforma, o FIDO U2F não pode ser usado em todas os aplicativos do Bitwarden. Você deve habilitar outro provedor de login em duas etapas para poder acessar sua conta quando o FIDO U2F não puder ser usado. Plataformas Suportadas:"
},
@@ -1222,11 +1261,11 @@
"twoFactorU2fWaiting": {
"message": "Esperando que você toque no botão da sua chave de segurança"
},
"twoFactorU2fClickEnable": {
"message": "Clique no botão \"Habilitar\" abaixo para ativar essa chave de segurança para o login em duas etapas."
"twoFactorU2fClickSave": {
"message": "Clique no botão \"Salvar\" abaixo para ativar essa chave de segurança para o login em duas etapas."
},
"twoFactorU2fProblemReading": {
"message": "Houve um problema ao ler a chave de segurança."
"twoFactorU2fProblemReadingTryAgain": {
"message": "Houve um problema ao ler a chave de segurança. Tente novamente."
},
"twoFactorRecoveryYourCode": {
"message": "Seu código de recuperação de login em duas etapas do Bitwarden"
@@ -1346,8 +1385,8 @@
"additionalStorageGbDesc": {
"message": "# de GB adicional"
},
"additionalStorageDesc": {
"message": "Seu plano vem com $SIZE$ de armazenamento de arquivos criptografados. Você pode adicionar armazenamento adicional por US $PRICE$ por GB \/ano.",
"additionalStorageIntervalDesc": {
"message": "Seu plano tem $SIZE$ de amazenamento criptografado de arquivos. Você pode adicionar armazenamento adicional por $PRICE$ por GB\/$INTERVAL$.",
"placeholders": {
"size": {
"content": "$1",
@@ -1356,6 +1395,10 @@
"price": {
"content": "$2",
"example": "$4.00"
},
"interval": {
"content": "$3",
"example": "'month' or 'year'"
}
}
},
@@ -1845,10 +1888,10 @@
"message": "Pesquisar"
},
"invited": {
"message": "Convindado"
"message": "Convidado"
},
"accepted": {
"message": "Aceitado"
"message": "Aceito"
},
"confirmed": {
"message": "Confirmado"
@@ -1871,6 +1914,12 @@
"userDesc": {
"message": "Um usuário comum com acesso às coleções da sua organização."
},
"manager": {
"message": "Gerente"
},
"managerDesc": {
"message": "Os gerentes podem acessar e gerenciar coleções atribuídas em sua organização."
},
"all": {
"message": "Todos"
},
@@ -1910,8 +1959,8 @@
"changedPassword": {
"message": "Senha da conta alterada."
},
"enabled2fa": {
"message": "Habilitar login em duas etapas."
"enabledUpdated2fa": {
"message": "Habilitado\/atualizado o login em duas etapas."
},
"disabled2fa": {
"message": "Desabilitar login em duas etapas."
@@ -2444,5 +2493,17 @@
},
"licenseIsExpired": {
"message": "A licença está expirada."
},
"updatedUsers": {
"message": "Usuários atualizados"
},
"selected": {
"message": "Selecionado"
},
"ownership": {
"message": "Propriedade"
},
"whoOwnsThisItem": {
"message": "Quem possui este item?"
}
}

View File

@@ -37,6 +37,9 @@
"password": {
"message": "Palavra-passe"
},
"passphrase": {
"message": "Frase-passe"
},
"notes": {
"message": "Notas"
},
@@ -795,6 +798,12 @@
"length": {
"message": "Comprimento"
},
"numWords": {
"message": "Número de palavras"
},
"wordSeparator": {
"message": "Separador de palavras"
},
"passwordHistory": {
"message": "Histórico de Palavras-passe"
},
@@ -906,9 +915,15 @@
"purgeVault": {
"message": "Esvaziar Cofre"
},
"purgedOrganizationVault": {
"message": "Cofre da organização purgado."
},
"purgeVaultDesc": {
"message": "Continue para eliminar todos os itens e pastas do seu cofre. Itens que pertençam a uma organização que partilhe não serão eliminados."
},
"purgeOrgVaultDesc": {
"message": "Proceda abaixo para apagar todos os itens no cofre da organização."
},
"purgeVaultWarning": {
"message": "Esvaziar o seu cofre é permanente. Não pode ser desfeito."
},
@@ -1165,6 +1180,15 @@
}
}
},
"u2fkeyX": {
"message": "Chave $INDEX$ U2F",
"placeholders": {
"index": {
"content": "$1",
"example": "2"
}
}
},
"nfcSupport": {
"message": "Suporte NFC"
},
@@ -1207,12 +1231,27 @@
"twoFactorU2fAdd": {
"message": "Adicione à sua conta uma chave de segurança FIDO U2F"
},
"twoFactorU2fPlugIn": {
"message": "Ligue a chave de segurança numa porta USB do seu computador."
"removeU2fConfirmation": {
"message": "Tem a certeza de que pretende remover esta chave de segurança?"
},
"readKey": {
"message": "Ler chave"
},
"keyCompromised": {
"message": "A chave foi comprometida."
},
"twoFactorU2fGiveName": {
"message": "Dê à chave de segurança um nome amigável para a identificar."
},
"twoFactorU2fPlugInReadKey": {
"message": "Coloque a chave de segurança na porta USB do seu computador e clique no botão \"Ler chave\"."
},
"twoFactorU2fTouchButton": {
"message": "Se a chave de segurança tiver um botão, toque-lhe."
},
"twoFactorU2fSaveForm": {
"message": "Guarde o formulário."
},
"twoFactorU2fWarning": {
"message": "Devido a limitações da plataforma, FIDO U2F não pode ser utilizado em todas as aplicações Bitwarden. Deverá escolher outro método de início de sessão em dois passos quando FIDO U2F não puder ser utilizado. Plataformas suportadas:"
},
@@ -1222,11 +1261,11 @@
"twoFactorU2fWaiting": {
"message": "A aguardar que toque no botão da sua chave de segurança"
},
"twoFactorU2fClickEnable": {
"message": "Clique no botão \"Ativar\" abaixo para ativar esta chave de segurança para o início de sessão em dois passos."
"twoFactorU2fClickSave": {
"message": "Clique no botão \"Guardar\" abaixo para ativar esta chave de segurança para o início de sessão em dois passos."
},
"twoFactorU2fProblemReading": {
"message": "Ocorreu um problema ao ler a chave de segurança."
"twoFactorU2fProblemReadingTryAgain": {
"message": "Ocorreu um problema ao ler a chave de segurança. Tente novamente."
},
"twoFactorRecoveryYourCode": {
"message": "O seu código Bitwarden de recuperação de início de sessão em dois passos"
@@ -1346,8 +1385,8 @@
"additionalStorageGbDesc": {
"message": "# de GB adicionais"
},
"additionalStorageDesc": {
"message": "O seu plano inclui $SIZE$ de armazenamento encriptado. Pode adicionar mais armazenamento por $PRICE$ por GB \/ano.",
"additionalStorageIntervalDesc": {
"message": "O seu plano vem com $SIZE$ de armazenamento encriptado de ficheiros. Pode adicionar armazenamento adicional por $PRICE$ por GB \/$INTERVAL$.",
"placeholders": {
"size": {
"content": "$1",
@@ -1356,6 +1395,10 @@
"price": {
"content": "$2",
"example": "$4.00"
},
"interval": {
"content": "$3",
"example": "'month' or 'year'"
}
}
},
@@ -1863,7 +1906,7 @@
"message": "Admin"
},
"adminDesc": {
"message": " Administradores podem aceder e gerir todos os itens, coleções e utilizadores na sua organização."
"message": " Admins podem aceder e gerir todos os itens, coleções e utilizadores na sua organização."
},
"user": {
"message": "Utilizador"
@@ -1871,6 +1914,12 @@
"userDesc": {
"message": "Um utilizador normal com acesso às coleções da sua organização."
},
"manager": {
"message": "Gestor"
},
"managerDesc": {
"message": "Os gestores podem aceder e gerir coleções atribuídas na sua organização."
},
"all": {
"message": "Todos"
},
@@ -1910,8 +1959,8 @@
"changedPassword": {
"message": "Palavra-passe da conta alterada."
},
"enabled2fa": {
"message": "Início de sessão em dois passos ativado."
"enabledUpdated2fa": {
"message": "Início de sessão em dois passos ativado\/atualizado."
},
"disabled2fa": {
"message": "Início de sessão em dois passos desativado."
@@ -2444,5 +2493,17 @@
},
"licenseIsExpired": {
"message": "A licença está expirada."
},
"updatedUsers": {
"message": "Utilizadores atualizados"
},
"selected": {
"message": "Selecionado"
},
"ownership": {
"message": "Propriedade"
},
"whoOwnsThisItem": {
"message": "Quem é dono deste item?"
}
}

View File

@@ -10,7 +10,7 @@
}
},
"whatTypeOfItem": {
"message": "Какой это тип элемента?"
"message": "Выберите тип элемента"
},
"name": {
"message": "Имя"
@@ -37,6 +37,9 @@
"password": {
"message": "Пароль"
},
"passphrase": {
"message": "Парольная фраза"
},
"notes": {
"message": "Заметки"
},
@@ -680,7 +683,7 @@
"message": "Ключ безопасности YubiKey OTP"
},
"yubiKeyDesc": {
"message": "Используйте YubiKey для доступа к вашей учетной записи. Работает с устройствами YubiKey 4, 4 Nano, 4C и NEO."
"message": "Используйте YubiKey для доступа к вашей учетной записи. Работает с устройствами YubiKey 4 серии, 5 серии и NEO."
},
"duoDesc": {
"message": "Подтвердите при помощи Duo Security, используя приложение Duo Mobile, SMS, телефонный звонок или ключ безопасности U2F.",
@@ -795,6 +798,12 @@
"length": {
"message": "Длина"
},
"numWords": {
"message": "Количество слов"
},
"wordSeparator": {
"message": "Разделитель слов"
},
"passwordHistory": {
"message": "История паролей"
},
@@ -826,7 +835,7 @@
}
},
"loggedOutWarning": {
"message": "При продолжении ваша сессия будет завершена и вам будет предложено авторизоваться повторно. Сеансы на других устройствах могут продолжать оставаться активными в течение одного часа."
"message": "В случае продолжения, ваша сессия будет завершена и вам будет предложено авторизоваться повторно. Сеансы на других устройствах могут продолжать оставаться активными в течение одного часа."
},
"emailChanged": {
"message": "Email изменен"
@@ -871,7 +880,7 @@
}
},
"kdfIterationsWarning": {
"message": "Установка слишком высоких итераций KDF может привести к снижению производительности при входе (и разблокировке) на устройствах с медленными процессорами. Мы рекомендуем увеличить значение с шагом $INCREMENT$, а затем проверить все ваши устройства.",
"message": "Установка слишком высоких итераций KDF может привести к снижению производительности при входе (и разблокировке) на устройствах с медленными процессорами. Мы рекомендуем увеличивать значение с шагом $INCREMENT$, а затем проверить все ваши устройства.",
"placeholders": {
"increment": {
"content": "$1",
@@ -889,16 +898,16 @@
"message": "Опасная зона"
},
"dangerZoneDesc": {
"message": "Осторожно, эти действия не обратимы!"
"message": "Будьте внимательны - эти действия не обратимы!"
},
"deauthorizeSessions": {
"message": "Деавторизовать сессии"
},
"deauthorizeSessionsDesc": {
"message": "Беспокоитесь о том, что ваша учетная запись авторизована на другом устройстве? Чтобы деавторизовать все ранее использовавшиеся компьютеры или устройства, выполните действие ниже. Этот шаг безопасности рекомендуется, если вы ранее использовали общедоступный компьютер или случайно сохранили свой пароль на устройстве, которое вам не принадлежит. Это действие также очистит все ранее сохраненные сессии двухфакторного входа."
"message": "Беспокоитесь о том, что ваша учетная запись авторизована на другом устройстве? Чтобы деавторизовать все ранее использовавшиеся компьютеры или устройства, выполните действие ниже. Этот шаг безопасности рекомендуется, если вы ранее использовали общедоступный компьютер или случайно сохранили свой пароль на устройстве, которое вам не принадлежит. Это действие также очистит все ранее сохраненные сессии двухфакторной аутентификации."
},
"deauthorizeSessionsWarning": {
"message": "При продолжении ваша сессия также будет завершена и вам будет предложено авторизоваться повторно. Также вам будет предложено повторно авторизоваться при помощи двухфакторной аутентификации, если она включена. Сеансы на других устройствах могут продолжать оставаться активными в течение одного часа."
"message": "В случае продолжения, ваша сессия будет завершена и вам будет предложено авторизоваться повторно. При активированной двухфакторной аутентификации вам потребуется ввести код. Сеансы на других устройствах могут продолжать оставаться активными в течение одного часа."
},
"sessionsDeauthorized": {
"message": "Все сессии деавторизованы"
@@ -906,9 +915,15 @@
"purgeVault": {
"message": "Очистить хранилище"
},
"purgedOrganizationVault": {
"message": "Хранилище организации очищено."
},
"purgeVaultDesc": {
"message": "Чтобы удалить все элементы и папки из вашего хранилища, выполните действие ниже. Элементы, принадлежащие организации, с которой вы делитесь, удалены не будут."
},
"purgeOrgVaultDesc": {
"message": "Для удаления всех элементов в хранилище организации выполните следующие действия."
},
"purgeVaultWarning": {
"message": "Очистка вашего хранилища необратима. Ее нельзя отменить."
},
@@ -993,7 +1008,7 @@
"description": "'Gravatar' is the name of a service. See www.gravatar.com"
},
"enableGravatarsDesc": {
"message": "Использовать аватары, загруженные с gravatar.com."
"message": "Использовать аватары с сайта gravatar.com."
},
"default": {
"message": "По умолчанию"
@@ -1112,7 +1127,7 @@
"message": "Устройства Windows"
},
"twoStepAuthenticatorAppsRecommended": {
"message": "Эти приложения рекомендуется, однако, другие приложения-аутентификаторы также будут работать."
"message": "Эти приложения рекомендуются, однако, другие приложения-аутентификаторы также будут работать."
},
"twoStepAuthenticatorScanCode": {
"message": "Сосканируйте этот QR-код вашим приложением-аутентификатором"
@@ -1136,7 +1151,7 @@
"message": "Добавить новый YubiKey в свою учетную запись"
},
"twoFactorYubikeyPlugIn": {
"message": "Подключите YubiKey (NEO или 4 серии) к USB-порту вашего компьютера."
"message": "Подключите YubiKey к USB-порту вашего компьютера."
},
"twoFactorYubikeySelectKey": {
"message": "Выберите первое пустое поле ввода YubiKey ниже."
@@ -1165,6 +1180,15 @@
}
}
},
"u2fkeyX": {
"message": "Ключ U2F $INDEX$",
"placeholders": {
"index": {
"content": "$1",
"example": "2"
}
}
},
"nfcSupport": {
"message": "Поддержка NFC"
},
@@ -1175,7 +1199,7 @@
"message": "Если один из ваших YubiKey поддерживает NFC (например, YubiKey NEO), то при обнаружении доступности NFC на мобильном устройстве вам будет выдан запрос."
},
"yubikeysUpdated": {
"message": "YubiKeys обновлены"
"message": "YubiKey обновлены"
},
"disableAllKeys": {
"message": "Отключить все ключи"
@@ -1207,12 +1231,27 @@
"twoFactorU2fAdd": {
"message": "Добавьте в свою учетную запись ключ безопасности FIDO U2F"
},
"twoFactorU2fPlugIn": {
"message": "Подключите ключ безопасности к USB-порту вашего компьютера."
"removeU2fConfirmation": {
"message": "Вы действительно хотите удалить этот ключ безопасности?"
},
"readKey": {
"message": "Чтение ключа"
},
"keyCompromised": {
"message": "Ключ скомпрометирован."
},
"twoFactorU2fGiveName": {
"message": "Присвойте ключу безопасности понятное имя для его идентификации."
},
"twoFactorU2fPlugInReadKey": {
"message": "Подключите ключ безопасности к USB-порту компьютера и нажмите кнопку 'Чтение ключа'."
},
"twoFactorU2fTouchButton": {
"message": "Если у ключа безопасности есть кнопка, нажмите ее."
},
"twoFactorU2fSaveForm": {
"message": "Сохранить форму."
},
"twoFactorU2fWarning": {
"message": "Из-за ограничений платформы FIDO U2F нельзя использовать во всех приложениях Bitwarden. Вы должны включить другого провайдера двухфакторной аутентификации, чтобы иметь возможность получить доступ к своей учетной записи, когда FIDO U2F не может быть использован. Поддерживаемые платформы:"
},
@@ -1222,14 +1261,14 @@
"twoFactorU2fWaiting": {
"message": "В ожидании нажатия кнопки на ключе безопасности"
},
"twoFactorU2fClickEnable": {
"message": "Нажмите кнопку \"Включить\" ниже, чтобы активировать этот ключ безопасности для двухфакторной аутентификации."
"twoFactorU2fClickSave": {
"message": "Нажмите кнопку 'Сохранить' ниже, чтобы включить этот ключ безопасности для двухфакторной аутентификации."
},
"twoFactorU2fProblemReading": {
"message": "Не удалось прочитать ключ безопасности."
"twoFactorU2fProblemReadingTryAgain": {
"message": "Не удалось прочитать ключ безопасности. Попробуйте снова."
},
"twoFactorRecoveryYourCode": {
"message": "Ваш код восстановления двухфакторной ауентификации Bitwarden"
"message": "Ваш код восстановления двухфакторной аутентификации Bitwarden"
},
"twoFactorRecoveryNoCode": {
"message": "Вы еще не включили ни одного провайдера двухфакторной аутентификации. После включения, вы можете вернуться сюда и проверить ваш код восстановления."
@@ -1346,8 +1385,8 @@
"additionalStorageGbDesc": {
"message": "# дополнительных ГБ"
},
"additionalStorageDesc": {
"message": "В ваш план включено $SIZE$ зашифрованного файлового хранилища. Вы можете добавить дополнительное место по $PRICE$ за ГБ \/год.",
"additionalStorageIntervalDesc": {
"message": "В ваш план включено $SIZE$ зашифрованного файлового хранилища. Вы можете добавить дополнительное место по $PRICE$ за ГБ \/$INTERVAL$.",
"placeholders": {
"size": {
"content": "$1",
@@ -1356,6 +1395,10 @@
"price": {
"content": "$2",
"example": "$4.00"
},
"interval": {
"content": "$3",
"example": "'month' or 'year'"
}
}
},
@@ -1863,13 +1906,19 @@
"message": "Администратор"
},
"adminDesc": {
"message": " Администраторы могут получать доступ и управлять всеми элементами, коллекциями и пользователями в вашей организации."
"message": "Администраторы могут получать доступ и управлять всеми элементами, коллекциями и пользователями вашей организации."
},
"user": {
"message": "Пользователь"
},
"userDesc": {
"message": "Обычный пользователь с доступом к коллекциям вашей организации."
"message": "Обычный пользователь с доступом к назначенным коллекциям вашей организации."
},
"manager": {
"message": "Менеджер"
},
"managerDesc": {
"message": "Менеджеры могут получать доступ и управлять назначенными коллекциями вашей организации."
},
"all": {
"message": "Все"
@@ -1910,8 +1959,8 @@
"changedPassword": {
"message": "Изменен пароль учетной записи."
},
"enabled2fa": {
"message": "Двухфакторная аутентификация включена."
"enabledUpdated2fa": {
"message": "Двухфакторная аутентификация включена\/обновлена."
},
"disabled2fa": {
"message": "Двухфакторная аутентификация выключена."
@@ -2444,5 +2493,17 @@
},
"licenseIsExpired": {
"message": "Срок действия лицензии истек."
},
"updatedUsers": {
"message": "Пользователи обновлены"
},
"selected": {
"message": "Выбрано"
},
"ownership": {
"message": "Владелец"
},
"whoOwnsThisItem": {
"message": "Кому принадлежит этот элемент?"
}
}

View File

@@ -37,6 +37,9 @@
"password": {
"message": "Heslo"
},
"passphrase": {
"message": "Heslo"
},
"notes": {
"message": "Poznámky"
},
@@ -795,6 +798,12 @@
"length": {
"message": "Dĺžka"
},
"numWords": {
"message": "Počet slov"
},
"wordSeparator": {
"message": "Oddeľovač slov"
},
"passwordHistory": {
"message": "História hesla"
},
@@ -906,9 +915,15 @@
"purgeVault": {
"message": "Vyprázdniť trezor"
},
"purgedOrganizationVault": {
"message": "Trezor organizácie bol vyprázdnený."
},
"purgeVaultDesc": {
"message": "Pokračujte ďalej ak chcete odstrániť všetky položky a priečinky vo vašom trezore. Položky, ktoré ste zdieľali a patria k organizácii sa neodstránia."
},
"purgeOrgVaultDesc": {
"message": "Pokračujte ďalej ak chcete odstrániť všetky položky v trezore organizácie."
},
"purgeVaultWarning": {
"message": "Vyprázdnenie trezoru je trvalé. Operáciu nie je možné vrátiť späť."
},
@@ -1136,7 +1151,7 @@
"message": "Do svojho účtu pridať nový YubiKey"
},
"twoFactorYubikeyPlugIn": {
"message": "Zapojte YubiKey (NEO alebo 4 séria) do USB portu počítača."
"message": "Zapojte YubiKey do USB portu počítača."
},
"twoFactorYubikeySelectKey": {
"message": "Nižšie zvoľte prvé voľné pole na vašom YubiKey."
@@ -1165,6 +1180,15 @@
}
}
},
"u2fkeyX": {
"message": "U2F kľúč $INDEX$",
"placeholders": {
"index": {
"content": "$1",
"example": "2"
}
}
},
"nfcSupport": {
"message": "Podpora NFC"
},
@@ -1207,12 +1231,27 @@
"twoFactorU2fAdd": {
"message": "Pridajte FIDO U2F kľúč k vášmu účtu"
},
"twoFactorU2fPlugIn": {
"message": "Pripojte bezpečnostný kľúč k vášmu počítaču prostredníctvom USB portu."
"removeU2fConfirmation": {
"message": "Naozaj chcete odstrániť tento bezpečnostný kľúč?"
},
"readKey": {
"message": "Načítať kľúč"
},
"keyCompromised": {
"message": "Kľúč bol kompromitovaný."
},
"twoFactorU2fGiveName": {
"message": "Zadajte názov pre bezpečnostný kľúč pre jeho ľahšiu identifikáciu."
},
"twoFactorU2fPlugInReadKey": {
"message": "Zasuňte bezpečnostný kľúč do USB portu na vašom počítači a kliknite na tlačidlo \"Načítať kľúč\"."
},
"twoFactorU2fTouchButton": {
"message": "Ak má kľúč na sebe tlačidlo, stlačte ho."
},
"twoFactorU2fSaveForm": {
"message": "Ulož formulár."
},
"twoFactorU2fWarning": {
"message": "Vzhľadom na obmedzenia platform, FIDO U2F nemôže byť použitý vo všetkých Bitwarden aplikáciách. Mali by ste povoliť inú formu dvojitého overenia, aby ste sa mohli prihlásiť k svojmu účtu ak nie je možné použiť FIDO U2F. Podporované platformy:"
},
@@ -1222,11 +1261,11 @@
"twoFactorU2fWaiting": {
"message": "Čaká sa na stlačenie tlačidla na vašom kľúči"
},
"twoFactorU2fClickEnable": {
"message": "Kliknite na tlačidlo \"Povoliť\" pre povolenie bezpečnostného kľúča pre dvojstupňové prihlásenie."
"twoFactorU2fClickSave": {
"message": "Kliknite na tlačidlo \"Uložiť\" pre povolenie bezpečnostného kľúča pre dvojstupňové prihlásenie."
},
"twoFactorU2fProblemReading": {
"message": "Problém pri čítaní bezpečnostného kľúča."
"twoFactorU2fProblemReadingTryAgain": {
"message": "Problém pri čítaní bezpečnostného kľúča. Skúste to znova."
},
"twoFactorRecoveryYourCode": {
"message": "Váš Bitwarden záchranný kód pre dvojstupňové overovanie"
@@ -1346,8 +1385,8 @@
"additionalStorageGbDesc": {
"message": "množstvo dodatočných GB"
},
"additionalStorageDesc": {
"message": "Váš plán zahŕňa $SIZE$ šifrovaného úložiska. Môžete si dokúpiť dodatočné miesto za $PRICE$\/GB ročne.",
"additionalStorageIntervalDesc": {
"message": "Your plan comes with $SIZE$ of encrypted file storage. You can add additional storage for $PRICE$ per GB \/$INTERVAL$.",
"placeholders": {
"size": {
"content": "$1",
@@ -1356,6 +1395,10 @@
"price": {
"content": "$2",
"example": "$4.00"
},
"interval": {
"content": "$3",
"example": "'month' or 'year'"
}
}
},
@@ -1871,6 +1914,12 @@
"userDesc": {
"message": "Obyčajný používateľ s prístupom k zbierkam organizácie."
},
"manager": {
"message": "Manager"
},
"managerDesc": {
"message": "Managers can access and manage assigned collections in your organization."
},
"all": {
"message": "Všetky"
},
@@ -1910,8 +1959,8 @@
"changedPassword": {
"message": "Zmenené heslo k účtu."
},
"enabled2fa": {
"message": "Dvojstupňové prihlasovanie zapnuté."
"enabledUpdated2fa": {
"message": "Dvojstupňové prihlasovanie zapnuté\/aktualizované."
},
"disabled2fa": {
"message": "Dvojstupňové prihlasovanie vypnuté."
@@ -2444,5 +2493,17 @@
},
"licenseIsExpired": {
"message": "Licencia vypršala."
},
"updatedUsers": {
"message": "Updated users"
},
"selected": {
"message": "Selected"
},
"ownership": {
"message": "Ownership"
},
"whoOwnsThisItem": {
"message": "Who owns this item?"
}
}

View File

@@ -37,6 +37,9 @@
"password": {
"message": "Lösenord"
},
"passphrase": {
"message": "Passphrase"
},
"notes": {
"message": "Anteckningar"
},
@@ -718,7 +721,7 @@
"message": "Välj en organisation som du vill dela dessa objekt med. Delning överför ägandet av objekten till organisationen. Du kommer inte längre att vara direkt ägare till dessa objekt när de har delats."
},
"collectionsDesc": {
"message": "Edit the collections that this item is being shared with. Only organization users with access to these collections will be able to see this item."
"message": "Redigera de samlingar som detta objekt delas med. Endast användare i organisationer som har tillgång till dessa samlingar kommer kunna se objektet."
},
"deleteSelectedItemsDesc": {
"message": "Du har valt $COUNT$ objekt att ta bort. Är du säker på att du vill ta bort alla dessa objekt?",
@@ -795,6 +798,12 @@
"length": {
"message": "Längd"
},
"numWords": {
"message": "Number of Words"
},
"wordSeparator": {
"message": "Word Separator"
},
"passwordHistory": {
"message": "Lösenordshistorik"
},
@@ -853,13 +862,13 @@
"message": "Bekräfta nytt huvudlösenord"
},
"encKeySettings": {
"message": "Encryption Key Settings"
"message": "Inställningar för krypteringsnyckel"
},
"kdfAlgorithm": {
"message": "KDF Algorithm"
"message": "KDF algoritm"
},
"kdfIterations": {
"message": "KDF Iterations"
"message": "KDF iterationer"
},
"kdfIterationsDesc": {
"message": "Higher KDF iterations can help protect your master password from being brute forced by an attacker. We recommend a value of $VALUE$ or more.",
@@ -880,10 +889,10 @@
}
},
"changeKdf": {
"message": "Change KDF"
"message": "Ändra KDF"
},
"encKeySettingsChanged": {
"message": "Encryption Key Settings Changed"
"message": "Inställningar för krypteringsnyckel ändrade"
},
"dangerZone": {
"message": "Högrisksområde"
@@ -906,9 +915,15 @@
"purgeVault": {
"message": "Rensa valv"
},
"purgedOrganizationVault": {
"message": "Purged organization vault."
},
"purgeVaultDesc": {
"message": "Proceed below to delete all items and folders in your vault. Items that belong to an organization that you share with will not be deleted."
},
"purgeOrgVaultDesc": {
"message": "Proceed below to delete all items in the organization's vault."
},
"purgeVaultWarning": {
"message": "Rensning av ditt valv är permanent. Det går inte att ångra."
},
@@ -1076,7 +1091,7 @@
"message": "Ett premium-medlemskap krävs för att använda den här funktionen."
},
"youHavePremiumAccess": {
"message": "You have premium access"
"message": "Du har premiumtillgång"
},
"alreadyPremiumFromOrg": {
"message": "You already have access to premium features because of an organization you are a member of."
@@ -1165,6 +1180,15 @@
}
}
},
"u2fkeyX": {
"message": "U2F Key $INDEX$",
"placeholders": {
"index": {
"content": "$1",
"example": "2"
}
}
},
"nfcSupport": {
"message": "NFC-stöd"
},
@@ -1207,12 +1231,27 @@
"twoFactorU2fAdd": {
"message": "Lägg till en FIDO U2F säkerhetsnyckel till ditt konto"
},
"twoFactorU2fPlugIn": {
"message": "Sätt in säkerhetsnyckeln till din dators USB-port."
"removeU2fConfirmation": {
"message": "Are you sure you want to remove this security key?"
},
"readKey": {
"message": "Read Key"
},
"keyCompromised": {
"message": "Key is compromised."
},
"twoFactorU2fGiveName": {
"message": "Give the security key a friendly name to identify it."
},
"twoFactorU2fPlugInReadKey": {
"message": "Plug the security key into your computer's USB port and click the \"Read Key\" button."
},
"twoFactorU2fTouchButton": {
"message": "Om säkerhetsnyckeln har en knapp, tryck på den."
},
"twoFactorU2fSaveForm": {
"message": "Save the form."
},
"twoFactorU2fWarning": {
"message": "Due to platform limitations, FIDO U2F cannot be used on all Bitwarden applications. You should enable another two-step login provider so that you can access your account when FIDO U2F cannot be used. Supported platforms:"
},
@@ -1222,11 +1261,11 @@
"twoFactorU2fWaiting": {
"message": "Waiting for you to touch the button on your security key"
},
"twoFactorU2fClickEnable": {
"message": "Click the \"Enable\" button below to enable this security key for two-step login."
"twoFactorU2fClickSave": {
"message": "Click the \"Save\" button below to enable this security key for two-step login."
},
"twoFactorU2fProblemReading": {
"message": "There was a problem reading the security key."
"twoFactorU2fProblemReadingTryAgain": {
"message": "There was a problem reading the security key. Try again."
},
"twoFactorRecoveryYourCode": {
"message": "Your Bitwarden two-step login recovery code"
@@ -1346,8 +1385,8 @@
"additionalStorageGbDesc": {
"message": "# ytterligare GB"
},
"additionalStorageDesc": {
"message": "Your plan comes with $SIZE$ of encrypted file storage. You can add additional storage for $PRICE$ per GB \/year.",
"additionalStorageIntervalDesc": {
"message": "Your plan comes with $SIZE$ of encrypted file storage. You can add additional storage for $PRICE$ per GB \/$INTERVAL$.",
"placeholders": {
"size": {
"content": "$1",
@@ -1356,6 +1395,10 @@
"price": {
"content": "$2",
"example": "$4.00"
},
"interval": {
"content": "$3",
"example": "'month' or 'year'"
}
}
},
@@ -1871,6 +1914,12 @@
"userDesc": {
"message": "En vanlig användare med tillgång till organisationens samlingar."
},
"manager": {
"message": "Manager"
},
"managerDesc": {
"message": "Managers can access and manage assigned collections in your organization."
},
"all": {
"message": "Alla"
},
@@ -1910,8 +1959,8 @@
"changedPassword": {
"message": "Ändrade kontolösenord."
},
"enabled2fa": {
"message": "Aktiverade tvåstegsverifiering."
"enabledUpdated2fa": {
"message": "Enabled\/updated two-step login."
},
"disabled2fa": {
"message": "Inaktiverade tvåstegsverifiering."
@@ -2444,5 +2493,17 @@
},
"licenseIsExpired": {
"message": "Licensen har upphört att gälla."
},
"updatedUsers": {
"message": "Updated users"
},
"selected": {
"message": "Selected"
},
"ownership": {
"message": "Ownership"
},
"whoOwnsThisItem": {
"message": "Who owns this item?"
}
}

2509
src/locales/tr/messages.json Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -37,6 +37,9 @@
"password": {
"message": "密码"
},
"passphrase": {
"message": "密码"
},
"notes": {
"message": "笔记"
},
@@ -795,6 +798,12 @@
"length": {
"message": "长度"
},
"numWords": {
"message": "字数"
},
"wordSeparator": {
"message": "单词分隔符"
},
"passwordHistory": {
"message": "密码历史记录"
},
@@ -906,9 +915,15 @@
"purgeVault": {
"message": "清空密码库"
},
"purgedOrganizationVault": {
"message": "已清除的组织密码库。"
},
"purgeVaultDesc": {
"message": "接下来的操作会删除密码库中的所有项目和文件夹。属于组织的共享项目将不会被删除。"
},
"purgeOrgVaultDesc": {
"message": "继续执行以下操作以删除组织的密码库中的所有项目。"
},
"purgeVaultWarning": {
"message": "清空密码库是永久性的。不能被撤消。"
},
@@ -1165,6 +1180,15 @@
}
}
},
"u2fkeyX": {
"message": "YubiKey $INDEX$",
"placeholders": {
"index": {
"content": "$1",
"example": "2"
}
}
},
"nfcSupport": {
"message": "NFC 支持"
},
@@ -1207,12 +1231,27 @@
"twoFactorU2fAdd": {
"message": "在您的帐户中添加 FIDO U2F 安全密钥"
},
"twoFactorU2fPlugIn": {
"message": "将安全密钥插入电脑的 USB 端口。"
"removeU2fConfirmation": {
"message": "您确认要删除这个安全密钥?"
},
"readKey": {
"message": "读取密钥"
},
"keyCompromised": {
"message": "密钥被破坏。"
},
"twoFactorU2fGiveName": {
"message": "给安全密钥一个友好的名称来标识它。"
},
"twoFactorU2fPlugInReadKey": {
"message": "将安全密钥插入计算机的 USB 端口, 然后单击 \"读取密钥\" 按钮。"
},
"twoFactorU2fTouchButton": {
"message": "如果安全密钥有按钮,请按下它。"
},
"twoFactorU2fSaveForm": {
"message": "保存表单。"
},
"twoFactorU2fWarning": {
"message": "由于平台限制FIDO U2F 无法用于所有的 Bitwarden 应用。 您应启用另一个两步登录验证应用,以便在 FIDO U2F 不可用时,也能访问您的帐户。 支持的平台:"
},
@@ -1222,11 +1261,11 @@
"twoFactorU2fWaiting": {
"message": "请您按下安全密钥上的按钮"
},
"twoFactorU2fClickEnable": {
"message": "单击下面的 \"启用\" 按钮,启用此安全密钥进行两步登录。"
"twoFactorU2fClickSave": {
"message": "单击下面的 \"保存\" 按钮,启用此安全密钥进行两步登录。"
},
"twoFactorU2fProblemReading": {
"message": "读取安全密钥时出现问题。"
"twoFactorU2fProblemReadingTryAgain": {
"message": "读取安全密钥时出现问题,请再试一次。"
},
"twoFactorRecoveryYourCode": {
"message": "您的 Bitwarden 两步登录恢复代码"
@@ -1346,8 +1385,8 @@
"additionalStorageGbDesc": {
"message": "# GB 附加存储"
},
"additionalStorageDesc": {
"message": "您的计划附带 $SIZE$ 的加密存储空间。您也可以用 $PRICE$ 每 GB 每购买附加存储。",
"additionalStorageIntervalDesc": {
"message": "您的计划附带 $SIZE$ 的加密存储空间。您也可以用 $PRICE$ 每 GB 每 $INTERVAL$ 购买附加存储。",
"placeholders": {
"size": {
"content": "$1",
@@ -1356,6 +1395,10 @@
"price": {
"content": "$2",
"example": "$4.00"
},
"interval": {
"content": "$3",
"example": "'month' or 'year'"
}
}
},
@@ -1871,6 +1914,12 @@
"userDesc": {
"message": "具有对组织集合的访问权限的普通用户。"
},
"manager": {
"message": "管理员"
},
"managerDesc": {
"message": "经理可以访问和管理组织中分配的集合。"
},
"all": {
"message": "全部"
},
@@ -1910,8 +1959,8 @@
"changedPassword": {
"message": "已更改帐户密码。"
},
"enabled2fa": {
"message": "已启用两步登录。"
"enabledUpdated2fa": {
"message": "已启用\/更新两步登录。"
},
"disabled2fa": {
"message": "已禁用两步登录。"
@@ -2444,5 +2493,17 @@
},
"licenseIsExpired": {
"message": "授权已过期"
},
"updatedUsers": {
"message": "更新用户"
},
"selected": {
"message": "已选择"
},
"ownership": {
"message": "所有权"
},
"whoOwnsThisItem": {
"message": "谁拥有这个项目?"
}
}

View File

@@ -37,6 +37,9 @@
"password": {
"message": "密碼"
},
"passphrase": {
"message": "密碼"
},
"notes": {
"message": "筆記"
},
@@ -718,10 +721,10 @@
"message": "選擇您希望分享這些項目的組織。這些項目的擁有權將會轉移到組織。一經分享,您將不再是這些項目的直接擁有者。"
},
"collectionsDesc": {
"message": "Edit the collections that this item is being shared with. Only organization users with access to these collections will be able to see this item."
"message": "編輯與此項目共享的集合。只有具有這些集合存取權限的組織使用者才能夠看到此項目。"
},
"deleteSelectedItemsDesc": {
"message": "You have selected $COUNT$ item(s) to delete. Are you sure you want to delete all of these items?",
"message": "您已經選取了 $COUNT$ 個項目要刪除。確定要刪除這些項目嗎 ?",
"placeholders": {
"count": {
"content": "$1",
@@ -730,7 +733,7 @@
}
},
"moveSelectedItemsDesc": {
"message": "Choose a folder that you would like to move the $COUNT$ selected item(s) to.",
"message": "選擇要將這 $COUNT$ 個項目移動到的資料夾。",
"placeholders": {
"count": {
"content": "$1",
@@ -739,7 +742,7 @@
}
},
"shareSelectedItemsDesc": {
"message": "You have selected $COUNT$ item(s). $SHAREABLE_COUNT$ items are sharable, $NONSHAREABLE_COUNT$ are not. Items with attachments must be shared individually.",
"message": "您已經選取了 $COUNT$ 個項目。 $SHAREABLE_COUNT$ 個項目是可以分享的,$NONSHAREABLE_COUNT$ 則否。有附件的的項目必須單獨分享。",
"placeholders": {
"count": {
"content": "$1",
@@ -795,6 +798,12 @@
"length": {
"message": "長度"
},
"numWords": {
"message": "字數"
},
"wordSeparator": {
"message": "文字分隔字元"
},
"passwordHistory": {
"message": "密碼歷史記錄"
},
@@ -826,7 +835,7 @@
}
},
"loggedOutWarning": {
"message": "Proceeding will log you out of your current session, requiring you to log back in. Active sessions on other devices may continue to remain active for up to one hour."
"message": "接下來會登出目前的登入階段,並要求您重新登入。其他裝置上的登入階段最多會保持一個小時。"
},
"emailChanged": {
"message": "電子郵件已更改"
@@ -835,7 +844,7 @@
"message": "請重新登入。"
},
"logBackInOthersToo": {
"message": "Please log back in. If you are using other Bitwarden applications log out and back in to those as well."
"message": "請重新登入。如果您還在使用其他 Bitwarden 應用,也請登出並重新登入。"
},
"changeMasterPassword": {
"message": "變更主密碼"
@@ -853,16 +862,16 @@
"message": "確認新密碼"
},
"encKeySettings": {
"message": "Encryption Key Settings"
"message": "加密金鑰設定"
},
"kdfAlgorithm": {
"message": "KDF Algorithm"
"message": "KDF 演算法"
},
"kdfIterations": {
"message": "KDF Iterations"
"message": "KDF 疊代"
},
"kdfIterationsDesc": {
"message": "Higher KDF iterations can help protect your master password from being brute forced by an attacker. We recommend a value of $VALUE$ or more.",
"message": "較高的 KDF 遞代次數可以避免您的主密碼遭到暴力破解。我們建議 $VALUE$ 或更高。",
"placeholders": {
"value": {
"content": "$1",
@@ -871,7 +880,7 @@
}
},
"kdfIterationsWarning": {
"message": "Setting your KDF iterations too high could result in poor performance when logging into (and unlocking) Bitwarden on devices with slower CPUs. We recommend that you increase the value in increments of $INCREMENT$ and then test all of your devices.",
"message": "把 KDF 遞代次數設太高會導致在 CPU 較慢的裝置上登入 ( 與解鎖 ) Bitwarden 時降低效能。我們建議您增加 $INCREMENT$ 的值並且在所有的裝置上測試。",
"placeholders": {
"increment": {
"content": "$1",
@@ -880,10 +889,10 @@
}
},
"changeKdf": {
"message": "Change KDF"
"message": "變更 KDF"
},
"encKeySettingsChanged": {
"message": "Encryption Key Settings Changed"
"message": "加密金鑰設定已變更"
},
"dangerZone": {
"message": "危險區域"
@@ -892,23 +901,29 @@
"message": "小心, 這些動作是無法挽回的!"
},
"deauthorizeSessions": {
"message": "Deauthorize Sessions"
"message": "取消登入階段"
},
"deauthorizeSessionsDesc": {
"message": "Concerned your account is logged in on another device? Proceed below to deauthorize all computers or devices that you have previously used. This security step is recommended if you previously used a public PC or accidentally saved your password on a device that isn't yours. This step will also clear all previously remembered two-step login sessions."
},
"deauthorizeSessionsWarning": {
"message": "Proceeding will also log you out of your current session, requiring you to log back in. You will also be prompted for two-step login again, if enabled. Active sessions on other devices may continue to remain active for up to one hour."
"message": "接下來會登出目前的登入階段,並要求您重新登入。若您有設定兩步驟登入,也需重新驗證。其他裝置上的登入階段最多會保持一個小時。"
},
"sessionsDeauthorized": {
"message": "All Sessions Deauthorized"
"message": "已取消所有登入階段授權"
},
"purgeVault": {
"message": "清除密碼庫"
},
"purgedOrganizationVault": {
"message": "Purged organization vault."
},
"purgeVaultDesc": {
"message": "Proceed below to delete all items and folders in your vault. Items that belong to an organization that you share with will not be deleted."
},
"purgeOrgVaultDesc": {
"message": "Proceed below to delete all items in the organization's vault."
},
"purgeVaultWarning": {
"message": "清除密碼庫是永久性的。將無法被還原。"
},
@@ -928,7 +943,7 @@
"message": "帳戶已刪除"
},
"accountDeletedDesc": {
"message": "Your account has been closed and all associated data has been deleted."
"message": "您的帳戶已經關閉, 所有關聯資料已經被刪除。"
},
"myAccount": {
"message": "我的帳戶"
@@ -940,10 +955,10 @@
"message": "匯入資料"
},
"importSuccess": {
"message": "Data has been successfully imported into your vault."
"message": "資料已經成功匯入到您的密碼庫中。"
},
"importFormatError": {
"message": "Data is not formatted correctly. Please check your import file and try again."
"message": "資料格式不正確。請檢查您匯入檔, 再重新匯入。"
},
"importNothingError": {
"message": "沒有匯入任何內容。"
@@ -1023,7 +1038,7 @@
"message": "新增自訂網域"
},
"newCustomDomainDesc": {
"message": "Enter a list of domains separated by commas. Only \"base\" domains are allowed. Do not enter subdomains. For example, enter \"google.com\" instead of \"www.google.com\". You can also enter \"androidapp:\/\/package.name\" to associate an android app with other website domains."
"message": "輸入不同域名時以逗號分隔.只允許輸入域名。不能輸入子網域名稱。例如, 輸入 \"google. com\" 而不是 \"www.google.com\"。您還可以輸入 \"androidapp:\/\/package.name\" 將 android 應用程式 與其他網站域相關聯。"
},
"customDomainX": {
"message": "自訂網域 $INDEX$",
@@ -1076,7 +1091,7 @@
"message": "進階會員才可使用此功能。"
},
"youHavePremiumAccess": {
"message": "You have premium access"
"message": "您有進階版存取權限"
},
"alreadyPremiumFromOrg": {
"message": "You already have access to premium features because of an organization you are a member of."
@@ -1088,19 +1103,19 @@
"message": "停用"
},
"twoStepLoginProviderEnabled": {
"message": "This two-step login provider is enabled on your account."
"message": "您的帳戶已經啟用兩步驗證"
},
"twoStepLoginAuthDesc": {
"message": "Enter your master password to modify two-step login settings."
"message": "輸入您的主密碼以改兩步驗證設定"
},
"twoStepAuthenticatorDesc": {
"message": "Follow these steps to set up two-step login with an authenticator app:"
},
"twoStepAuthenticatorDownloadApp": {
"message": "Download a two-step authenticator app"
"message": "下載兩步驗證APP"
},
"twoStepAuthenticatorNeedApp": {
"message": "Need a two-step authenticator app? Download one of the following"
"message": "需要兩步驗證器APP下載以下應用之一"
},
"iosDevices": {
"message": "iOS 裝置"
@@ -1115,7 +1130,7 @@
"message": "These apps are recommended, however, other authenticator apps will also work."
},
"twoStepAuthenticatorScanCode": {
"message": "Scan this QR code with your authenticator app"
"message": "使用您的兩步驟APP驗證掃描此QR-Code"
},
"key": {
"message": "金鑰"
@@ -1165,6 +1180,15 @@
}
}
},
"u2fkeyX": {
"message": "U2F Key $INDEX$",
"placeholders": {
"index": {
"content": "$1",
"example": "2"
}
}
},
"nfcSupport": {
"message": "NFC 支援"
},
@@ -1207,12 +1231,27 @@
"twoFactorU2fAdd": {
"message": "在您的帳戶中新增 FIDO U2F 安全金鑰"
},
"twoFactorU2fPlugIn": {
"message": "將安全金鑰插入電腦的 USB 連接埠。"
"removeU2fConfirmation": {
"message": "Are you sure you want to remove this security key?"
},
"readKey": {
"message": "讀取金鑰"
},
"keyCompromised": {
"message": "Key is compromised."
},
"twoFactorU2fGiveName": {
"message": "Give the security key a friendly name to identify it."
},
"twoFactorU2fPlugInReadKey": {
"message": "Plug the security key into your computer's USB port and click the \"Read Key\" button."
},
"twoFactorU2fTouchButton": {
"message": "If the security key has a button, touch it."
},
"twoFactorU2fSaveForm": {
"message": "儲存表單"
},
"twoFactorU2fWarning": {
"message": "Due to platform limitations, FIDO U2F cannot be used on all Bitwarden applications. You should enable another two-step login provider so that you can access your account when FIDO U2F cannot be used. Supported platforms:"
},
@@ -1222,11 +1261,11 @@
"twoFactorU2fWaiting": {
"message": "Waiting for you to touch the button on your security key"
},
"twoFactorU2fClickEnable": {
"message": "Click the \"Enable\" button below to enable this security key for two-step login."
"twoFactorU2fClickSave": {
"message": "Click the \"Save\" button below to enable this security key for two-step login."
},
"twoFactorU2fProblemReading": {
"message": "讀取安全金鑰時發生問題。"
"twoFactorU2fProblemReadingTryAgain": {
"message": "There was a problem reading the security key. Try again."
},
"twoFactorRecoveryYourCode": {
"message": "您的 Bitwarden 兩步登入復原碼"
@@ -1346,8 +1385,8 @@
"additionalStorageGbDesc": {
"message": "# of additional GB"
},
"additionalStorageDesc": {
"message": "Your plan comes with $SIZE$ of encrypted file storage. You can add additional storage for $PRICE$ per GB \/year.",
"additionalStorageIntervalDesc": {
"message": "Your plan comes with $SIZE$ of encrypted file storage. You can add additional storage for $PRICE$ per GB \/$INTERVAL$.",
"placeholders": {
"size": {
"content": "$1",
@@ -1356,6 +1395,10 @@
"price": {
"content": "$2",
"example": "$4.00"
},
"interval": {
"content": "$3",
"example": "'month' or 'year'"
}
}
},
@@ -1430,7 +1473,7 @@
"message": "狀態"
},
"nextCharge": {
"message": "Next Charge"
"message": "下一次收費"
},
"details": {
"message": "詳細資料"
@@ -1576,7 +1619,7 @@
"message": "使用者"
},
"userSeats": {
"message": "User Seats"
"message": "使用者數量"
},
"additionalUserSeats": {
"message": "Additional User Seats"
@@ -1871,6 +1914,12 @@
"userDesc": {
"message": "A regular user with access to your organization's collections."
},
"manager": {
"message": "Manager"
},
"managerDesc": {
"message": "Managers can access and manage assigned collections in your organization."
},
"all": {
"message": "全部"
},
@@ -1910,8 +1959,8 @@
"changedPassword": {
"message": "Changed account password."
},
"enabled2fa": {
"message": "Enabled two-step login."
"enabledUpdated2fa": {
"message": "Enabled\/updated two-step login."
},
"disabled2fa": {
"message": "Disabled two-step login."
@@ -2229,7 +2278,7 @@
"message": "Deleting the organization is permanent. It cannot be undone."
},
"organizationDeleted": {
"message": "Organization Deleted"
"message": "已刪除組織"
},
"organizationDeletedDesc": {
"message": "The organization and all associated data has been deleted."
@@ -2444,5 +2493,17 @@
},
"licenseIsExpired": {
"message": "授權已過期。"
},
"updatedUsers": {
"message": "更新使用者"
},
"selected": {
"message": "已選擇"
},
"ownership": {
"message": "所有權"
},
"whoOwnsThisItem": {
"message": "誰擁有這個項目?"
}
}

View File

@@ -207,6 +207,12 @@ input, select, textarea {
left: -24px;
}
}
ul {
&.carets {
margin-left: 0.85em;
}
}
}
.modal-dialog {
@@ -419,13 +425,13 @@ app-vault-groupings, app-org-vault-groupings {
}
li.active {
.show-active {
> .show-active, > div .show-active {
display: inline;
}
}
li.active {
a:first-child {
> a:first-child, > div a:first-child {
font-weight: bold;
color: theme-color("primary");
}

View File

@@ -4,6 +4,7 @@ import { SweetAlert } from 'sweetalert/typings/core';
import { DeviceType } from 'jslib/enums/deviceType';
import { I18nService } from 'jslib/abstractions/i18n.service';
import { MessagingService } from 'jslib/abstractions/messaging.service';
import { PlatformUtilsService } from 'jslib/abstractions/platformUtils.service';
import { Utils } from 'jslib/misc/utils';
@@ -16,7 +17,7 @@ export class WebPlatformUtilsService implements PlatformUtilsService {
private browserCache: DeviceType = null;
constructor(private i18nService: I18nService) { }
constructor(private i18nService: I18nService, private messagingService: MessagingService) { }
getDevice(): DeviceType {
if (this.browserCache != null) {
@@ -85,10 +86,6 @@ export class WebPlatformUtilsService implements PlatformUtilsService {
return 'UA-81915606-3';
}
getDomain(uriString: string): string {
return Utils.getHostname(uriString);
}
isViewOpen(): boolean {
return false;
}
@@ -110,6 +107,33 @@ export class WebPlatformUtilsService implements PlatformUtilsService {
saveFile(win: Window, blobData: any, blobOptions: any, fileName: string): void {
let blob: Blob = null;
let type: string = null;
const fileNameLower = fileName.toLowerCase();
let doDownload = true;
if (fileNameLower.endsWith('.pdf')) {
type = 'application/pdf';
doDownload = false;
} else if (fileNameLower.endsWith('.xlsx')) {
type = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';
} else if (fileNameLower.endsWith('.docx')) {
type = 'application/vnd.openxmlformats-officedocument.wordprocessingml.document';
} else if (fileNameLower.endsWith('.pptx')) {
type = 'application/vnd.openxmlformats-officedocument.presentationml.presentation';
} else if (fileNameLower.endsWith('.csv')) {
type = 'text/csv';
} else if (fileNameLower.endsWith('.png')) {
type = 'image/png';
} else if (fileNameLower.endsWith('.jpg') || fileNameLower.endsWith('.jpeg')) {
type = 'image/jpeg';
} else if (fileNameLower.endsWith('.gif')) {
type = 'image/gif';
}
if (type != null) {
blobOptions = blobOptions || {};
if (blobOptions.type == null) {
blobOptions.type = type;
}
}
if (blobOptions != null && !this.isIE()) {
blob = new Blob([blobData], blobOptions);
} else {
@@ -119,8 +143,12 @@ export class WebPlatformUtilsService implements PlatformUtilsService {
navigator.msSaveBlob(blob, fileName);
} else {
const a = win.document.createElement('a');
if (doDownload) {
a.download = fileName;
} else {
a.target = '_blank';
}
a.href = win.URL.createObjectURL(blob);
a.download = fileName;
a.style.position = 'fixed';
win.document.body.appendChild(a);
a.click();
@@ -143,8 +171,14 @@ export class WebPlatformUtilsService implements PlatformUtilsService {
return true;
}
showToast(type: 'error' | 'success' | 'warning' | 'info', title: string, text: string, global?: any): void {
throw new Error('showToast not implemented');
showToast(type: 'error' | 'success' | 'warning' | 'info', title: string, text: string | string[],
options?: any): void {
this.messagingService.send('showToast', {
text: text,
title: title,
type: type,
options: options,
});
}
async showDialog(text: string, title?: string, confirmText?: string, cancelText?: string, type?: string) {
@@ -199,6 +233,14 @@ export class WebPlatformUtilsService implements PlatformUtilsService {
return confirmed;
}
eventTrack(action: string, label?: string, options?: any) {
this.messagingService.send('analyticsEventTrack', {
action: action,
label: label,
options: options,
});
}
isDev(): boolean {
return process.env.ENV === 'development';
}

View File

@@ -16,18 +16,15 @@
"types": [],
"baseUrl": ".",
"paths": {
"tldjs": [
"jslib/src/misc/tldjs.noop"
],
"jslib/*": [
"jslib/src/*"
],
"@angular/*": [
"node_modules/@angular/*"
],
"angular2-toaster": [
"node_modules/angular2-toaster"
],
"angulartics2": [
"node_modules/angulartics2"
],
"node": [
"node_modules/@types/node"
]

View File

@@ -91,6 +91,7 @@ const plugins = [
chunks: ['connectors/u2f'],
}),
new CopyWebpackPlugin([
{ from: './src/.nojekyll' },
{ from: './src/manifest.json' },
{ from: './src/favicon.ico' },
{ from: './src/browserconfig.xml' },
@@ -130,22 +131,21 @@ if (ENV === 'production') {
});
}
// ref: https://webpack.js.org/configuration/dev-server/#devserver
let certSuffix = fs.existsSync('dev-server.local.pem') ? '.local' : '.shared';
const serve = {
const devServer = {
https: {
key: fs.readFileSync('dev-server' + certSuffix + '.pem'),
cert: fs.readFileSync('dev-server' + certSuffix + '.pem'),
},
// host: '192.168.1.9',
// host client has issues. ref: https://github.com/webpack-contrib/webpack-serve/issues/233
// hotClient: false,
hot: false,
};
const config = {
mode: ENV,
devtool: 'source-map',
serve: serve,
devServer: devServer,
entry: {
'app/polyfills': './src/app/polyfills.ts',
'app/main': './src/app/main.ts',
@@ -172,6 +172,7 @@ const config = {
extensions: ['.ts', '.js'],
alias: {
jslib: path.join(__dirname, 'jslib/src'),
tldjs: path.join(__dirname, 'jslib/src/misc/tldjs.noop'),
},
symlinks: false,
modules: [path.resolve('node_modules')],