mirror of https://github.com/keeweb/keeweb
String globalization
parent
dd2f94978e
commit
e0f5f49255
|
@ -7,7 +7,8 @@ var AppModel = require('./models/app-model'),
|
|||
Alerts = require('./comp/alerts'),
|
||||
DropboxLink = require('./comp/dropbox-link'),
|
||||
Updater = require('./comp/updater'),
|
||||
ThemeChanger = require('./util/theme-changer');
|
||||
ThemeChanger = require('./util/theme-changer'),
|
||||
Locale = require('./util/locale');
|
||||
|
||||
$(function() {
|
||||
if (location.href.indexOf('state=') >= 0) {
|
||||
|
@ -24,14 +25,10 @@ $(function() {
|
|||
ThemeChanger.setTheme(appModel.settings.get('theme'));
|
||||
}
|
||||
if (['https:', 'file:', 'app:'].indexOf(location.protocol) < 0 && !localStorage.disableSecurityCheck) {
|
||||
Alerts.error({ header: 'Not Secure!', icon: 'user-secret', esc: false, enter: false, click: false,
|
||||
body: 'You have loaded this app with insecure connection. ' +
|
||||
'Someone may be watching you and stealing your passwords. ' +
|
||||
'We strongly advice you to stop, unless you clearly understand what you\'re doing.' +
|
||||
'<br/><br/>' +
|
||||
'Yes, your database is encrypted but no one can guarantee that the app has not been modified on the way to you.',
|
||||
Alerts.error({ header: Locale.appSecWarn, icon: 'user-secret', esc: false, enter: false, click: false,
|
||||
body: Locale.appSecWarnBody1 + '<br/><br/>' + Locale.appSecWarnBody2,
|
||||
buttons: [
|
||||
{ result: '', title: 'I understand the risks, continue', error: true }
|
||||
{ result: '', title: Locale.appSecWarnBtn, error: true }
|
||||
],
|
||||
complete: showApp
|
||||
});
|
||||
|
|
|
@ -1,14 +1,15 @@
|
|||
'use strict';
|
||||
|
||||
var ModalView = require('../views/modal-view');
|
||||
var ModalView = require('../views/modal-view'),
|
||||
Locale = require('../util/locale');
|
||||
|
||||
var Alerts = {
|
||||
alertDisplayed: false,
|
||||
|
||||
buttons: {
|
||||
ok: {result: 'yes', title: 'OK'},
|
||||
yes: {result: 'yes', title: 'Yes'},
|
||||
no: {result: '', title: 'No'}
|
||||
ok: {result: 'yes', title: Locale.alertOk},
|
||||
yes: {result: 'yes', title: Locale.alertYes},
|
||||
no: {result: '', title: Locale.alertNo}
|
||||
},
|
||||
|
||||
alert: function(config) {
|
||||
|
@ -31,7 +32,7 @@ var Alerts = {
|
|||
|
||||
notImplemented: function() {
|
||||
this.alert({
|
||||
header: 'Not Implemented',
|
||||
header: Locale.notImplemented,
|
||||
body: '',
|
||||
icon: 'exclamation-triangle',
|
||||
buttons: [this.buttons.ok],
|
||||
|
|
|
@ -4,6 +4,7 @@ var Dropbox = require('dropbox'),
|
|||
Alerts = require('./alerts'),
|
||||
Launcher = require('./launcher'),
|
||||
Logger = require('../util/logger'),
|
||||
Locale = require('../util/locale'),
|
||||
Links = require('../const/links');
|
||||
|
||||
var logger = new Logger('dropbox');
|
||||
|
@ -134,9 +135,9 @@ var DropboxLink = {
|
|||
if (!isValidKey()) {
|
||||
Alerts.error({
|
||||
icon: 'dropbox',
|
||||
header: 'Dropbox not configured',
|
||||
body: 'So, you are using KeeWeb on your own server? Good!<br/>' +
|
||||
'<a href="' + Links.SelfHostedDropbox + '" target="blank">Some configuration</a> is required to make Dropbox work, it\'s just 3 steps away.'
|
||||
header: Locale.dropboxNotConfigured,
|
||||
body: Locale.dropboxNotConfiguredBody1 + '<br/>' + Locale.dropboxNotConfiguredBody2.replace('{}',
|
||||
'<a href="' + Links.SelfHostedDropbox + '" target="blank">' + Locale.dropboxNotConfiguredLink + '</a>')
|
||||
});
|
||||
return complete(DropboxCustomErrors.BadKey);
|
||||
}
|
||||
|
@ -166,9 +167,9 @@ var DropboxLink = {
|
|||
if (!Alerts.alertDisplayed) {
|
||||
Alerts.yesno({
|
||||
icon: 'dropbox',
|
||||
header: 'Dropbox Login',
|
||||
body: 'To continue, you have to sign in to Dropbox.',
|
||||
buttons: [{result: 'yes', title: 'Sign In'}, {result: '', title: 'Cancel'}],
|
||||
header: Locale.dropboxLogin,
|
||||
body: Locale.dropboxLoginBody,
|
||||
buttons: [{result: 'yes', title: Locale.alertSignIn}, {result: '', title: Locale.alertCancel}],
|
||||
success: (function () {
|
||||
this.authenticate(function (err) { callback(!err); });
|
||||
}).bind(this),
|
||||
|
@ -181,42 +182,42 @@ var DropboxLink = {
|
|||
break;
|
||||
case Dropbox.ApiError.NOT_FOUND:
|
||||
alertCallback({
|
||||
header: 'Dropbox Sync Error',
|
||||
body: 'The file was not found. Has it been removed from another computer?'
|
||||
header: Locale.dropboxSyncError,
|
||||
body: Locale.dropboxNotFoundBody
|
||||
});
|
||||
break;
|
||||
case Dropbox.ApiError.OVER_QUOTA:
|
||||
alertCallback({
|
||||
header: 'Dropbox Full',
|
||||
body: 'Your Dropbox is full, there\'s no space left anymore.'
|
||||
header: Locale.dropboxFull,
|
||||
body: Locale.dropboxFullBody
|
||||
});
|
||||
break;
|
||||
case Dropbox.ApiError.RATE_LIMITED:
|
||||
alertCallback({
|
||||
header: 'Dropbox Sync Error',
|
||||
body: 'Too many requests to Dropbox have been made by this app. Please, try again later.'
|
||||
header: Locale.dropboxSyncError,
|
||||
body: Locale.dropboxRateLimitedBody
|
||||
});
|
||||
break;
|
||||
case Dropbox.ApiError.NETWORK_ERROR:
|
||||
alertCallback({
|
||||
header: 'Dropbox Sync Network Error',
|
||||
body: 'Network error occured during Dropbox sync. Please, check your connection and try again.'
|
||||
header: Locale.dropboxNetError,
|
||||
body: Locale.dropboxNetErrorBody
|
||||
});
|
||||
break;
|
||||
case Dropbox.ApiError.INVALID_PARAM:
|
||||
case Dropbox.ApiError.OAUTH_ERROR:
|
||||
case Dropbox.ApiError.INVALID_METHOD:
|
||||
alertCallback({
|
||||
header: 'Dropbox Sync Error',
|
||||
body: 'Something went wrong during Dropbox sync. Please, try again later. Error code: ' + err.status
|
||||
header: Locale.dropboxSyncError,
|
||||
body: Locale.dropboxErrorBody + err.status
|
||||
});
|
||||
break;
|
||||
case Dropbox.ApiError.CONFLICT:
|
||||
break;
|
||||
default:
|
||||
alertCallback({
|
||||
header: 'Dropbox Sync Error',
|
||||
body: 'Something went wrong during Dropbox sync. Please, try again later. Error: ' + err
|
||||
header: Locale.dropboxSyncError,
|
||||
body: Locale.dropboxErrorRepeatBody + err
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
'use strict';
|
||||
|
||||
var Backbone = require('backbone');
|
||||
var Backbone = require('backbone'),
|
||||
Locale = require('../util/locale');
|
||||
var Launcher;
|
||||
|
||||
if (window.process && window.process.versions && window.process.versions.electron) {
|
||||
|
@ -28,9 +29,9 @@ if (window.process && window.process.versions && window.process.versions.electro
|
|||
defaultPath = this.req('path').join(homePath, defaultPath);
|
||||
}
|
||||
this.remReq('dialog').showSaveDialog({
|
||||
title: 'Save Passwords Database',
|
||||
title: Locale.launcherSave,
|
||||
defaultPath: defaultPath,
|
||||
filters: [{ name: 'KeePass files', extensions: ['kdbx'] }]
|
||||
filters: [{ name: Locale.launcherFileFilter, extensions: ['kdbx'] }]
|
||||
}, cb);
|
||||
},
|
||||
getUserDataPath: function(fileName) {
|
||||
|
|
|
@ -4,3 +4,4 @@ require('./cmp');
|
|||
require('./ifeq');
|
||||
require('./ifneq');
|
||||
require('./ifemptyoreq');
|
||||
require('./res');
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
'use strict';
|
||||
|
||||
var Handlebars = require('hbs'),
|
||||
Locale = require('../util/locale');
|
||||
|
||||
Handlebars.registerHelper('res', function(key, options) {
|
||||
var value = Locale[key];
|
||||
if (value) {
|
||||
var ix = value.indexOf('{}');
|
||||
if (ix >= 0) {
|
||||
value = value.replace('{}', options.fn(this));
|
||||
}
|
||||
}
|
||||
return value;
|
||||
});
|
||||
|
||||
Handlebars.registerHelper('Res', function(key) {
|
||||
var value = Locale[key];
|
||||
if (value) {
|
||||
value = value[0].toUpperCase() + value.substr(1);
|
||||
}
|
||||
return value;
|
||||
});
|
|
@ -4,6 +4,7 @@ var Backbone = require('backbone'),
|
|||
MenuSectionCollection = require('../../collections/menu/menu-section-collection'),
|
||||
MenuSectionModel = require('./menu-section-model'),
|
||||
GroupsMenuModel = require('./groups-menu-model'),
|
||||
Locale = require('../../util/locale'),
|
||||
Keys = require('../../const/keys'),
|
||||
Colors = require('../../const/colors');
|
||||
|
||||
|
@ -16,17 +17,18 @@ var MenuModel = Backbone.Model.extend({
|
|||
|
||||
initialize: function() {
|
||||
this.menus = {};
|
||||
this.allItemsSection = new MenuSectionModel([{ title: 'All Items', icon: 'th-large', active: true, shortcut: Keys.DOM_VK_A, filterKey: '*' }]);
|
||||
this.allItemsSection = new MenuSectionModel([{ title: Locale.menuAllItems, icon: 'th-large', active: true,
|
||||
shortcut: Keys.DOM_VK_A, filterKey: '*' }]);
|
||||
this.groupsSection = new GroupsMenuModel();
|
||||
this.colorsSection = new MenuSectionModel([{ title: 'Colors', icon: 'bookmark', shortcut: Keys.DOM_VK_C, cls: 'menu__item-colors',
|
||||
filterKey: 'color', filterValue: true }]);
|
||||
this.colorsSection = new MenuSectionModel([{ title: Locale.menuColors, icon: 'bookmark', shortcut: Keys.DOM_VK_C,
|
||||
cls: 'menu__item-colors', filterKey: 'color', filterValue: true }]);
|
||||
this.colorsItem = this.colorsSection.get('items').models[0];
|
||||
var defTags = [{ title: 'Tags', icon: 'tags', defaultItem: true,
|
||||
disabled: { header: 'No tags', body: 'You can add new tags while editing fields, in tags section.', icon: 'tags' } }];
|
||||
var defTags = [{ title: Locale.menuTags, icon: 'tags', defaultItem: true,
|
||||
disabled: { header: Locale.menuAlertNoTags, body: Locale.menuAlertNoTagsBody, icon: 'tags' } }];
|
||||
this.tagsSection = new MenuSectionModel(defTags);
|
||||
this.tagsSection.set({ scrollable: true, drag: true });
|
||||
this.tagsSection.defaultItems = defTags;
|
||||
this.trashSection = new MenuSectionModel([{ title: 'Trash', icon: 'trash', shortcut: Keys.DOM_VK_D,
|
||||
this.trashSection = new MenuSectionModel([{ title: Locale.menuTrash, icon: 'trash', shortcut: Keys.DOM_VK_D,
|
||||
filterKey: 'trash', filterValue: true, drop: true }]);
|
||||
Colors.AllColors.forEach(function(color) { this.colorsSection.get('items').models[0]
|
||||
.addOption({ cls: 'fa ' + color + '-color', value: color, filterValue: color }); }, this);
|
||||
|
@ -38,10 +40,10 @@ var MenuModel = Backbone.Model.extend({
|
|||
this.trashSection
|
||||
]);
|
||||
|
||||
this.generalSection = new MenuSectionModel([{ title: 'General', icon: 'cog', page: 'general', active: true }]);
|
||||
this.shortcutsSection = new MenuSectionModel([{ title: 'Shortcuts', icon: 'keyboard-o', page: 'shortcuts' }]);
|
||||
this.aboutSection = new MenuSectionModel([{ title: 'About', icon: 'info', page: 'about' }]);
|
||||
this.helpSection = new MenuSectionModel([{ title: 'Help', icon: 'question', page: 'help' }]);
|
||||
this.generalSection = new MenuSectionModel([{ title: Locale.menuSetGeneral, icon: 'cog', page: 'general', active: true }]);
|
||||
this.shortcutsSection = new MenuSectionModel([{ title: Locale.menuSetShortcuts, icon: 'keyboard-o', page: 'shortcuts' }]);
|
||||
this.aboutSection = new MenuSectionModel([{ title: Locale.menuSetAbout, icon: 'info', page: 'about' }]);
|
||||
this.helpSection = new MenuSectionModel([{ title: Locale.menuSetHelp, icon: 'question', page: 'help' }]);
|
||||
this.filesSection = new MenuSectionModel();
|
||||
this.filesSection.set({ scrollable: true, grow: true });
|
||||
this.menus.settings = new MenuSectionCollection([
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
'use strict';
|
||||
|
||||
var Format = require('../util/format');
|
||||
var Format = require('../util/format'),
|
||||
Locale = require('../util/locale');
|
||||
|
||||
var EntryPresenter = function(descField, noColor, activeEntryId) {
|
||||
this.entry = null;
|
||||
|
@ -33,19 +34,19 @@ EntryPresenter.prototype = {
|
|||
get tags() { return this.entry ? this.entry.tags : false; },
|
||||
get description() {
|
||||
if (!this.entry) {
|
||||
return '[Group]';
|
||||
return '[' + Locale.listGroup + ']';
|
||||
}
|
||||
switch (this.descField) {
|
||||
case 'website':
|
||||
return this.url || '(no website)';
|
||||
return this.url || '(' + Locale.listNoWebsite + ')';
|
||||
case 'user':
|
||||
return this.user || '(no user)';
|
||||
return this.user || '(' + Locale.listNoUser + ')';
|
||||
case 'created':
|
||||
return this.created;
|
||||
case 'updated':
|
||||
return this.updated;
|
||||
case 'attachments':
|
||||
return this.entry.attachments.map(function(a) { return a.title; }).join(', ') || '(no attachments)';
|
||||
return this.entry.attachments.map(function(a) { return a.title; }).join(', ') || '(' + Locale.listNoAttachments + ')';
|
||||
default:
|
||||
return this.notes || this.url || this.user;
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@ var Format = {
|
|||
':' + this.pad(dt.getSeconds(), 2) : '';
|
||||
},
|
||||
dStr: function(dt) {
|
||||
return dt ? dt.getDate() + ' ' + Locale.MonthsShort[dt.getMonth()] + ' ' + dt.getFullYear() : '';
|
||||
return dt ? dt.getDate() + ' ' + Locale.monthsShort[dt.getMonth()] + ' ' + dt.getFullYear() : '';
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -1,10 +1,300 @@
|
|||
'use strict';
|
||||
|
||||
var Locale = {
|
||||
Months: ['January','February','March','April','May','June','July','August','September','October','November','December'],
|
||||
MonthsShort: ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'],
|
||||
Weekdays: ['Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday'],
|
||||
WeekdaysShort: ['Sun','Mon','Tue','Wed','Thu','Fri','Sat']
|
||||
months: ['January','February','March','April','May','June','July','August','September','October','November','December'],
|
||||
monthsShort: ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'],
|
||||
weekdays: ['Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday'],
|
||||
weekdaysShort: ['Sun','Mon','Tue','Wed','Thu','Fri','Sat'],
|
||||
|
||||
retToApp: 'return to app',
|
||||
name: 'name',
|
||||
icon: 'icon',
|
||||
title: 'title',
|
||||
user: 'user',
|
||||
website: 'website',
|
||||
tags: 'tags',
|
||||
notes: 'notes',
|
||||
noTitle: 'no title',
|
||||
or: 'or',
|
||||
notImplemented: 'Not Implemented',
|
||||
|
||||
menuAllItems: 'All Items',
|
||||
menuColors: 'Colors',
|
||||
menuTags: 'Tags',
|
||||
menuTrash: 'Trash',
|
||||
menuSetGeneral: 'General',
|
||||
menuSetShortcuts: 'Shortcuts',
|
||||
menuSetHelp: 'Help',
|
||||
menuSetAbout: 'About',
|
||||
menuAlertNoTags: 'No tags',
|
||||
menuAlertNoTagsBody: 'You can add new tags while editing fields, in tags section.',
|
||||
menuEmptyTrash: 'Empty Trash',
|
||||
menuEmptyTrashAlert: 'Empty Trash?',
|
||||
menuEmptyTrashAlertBody: 'You will not be able to put items back',
|
||||
|
||||
alertYes: 'Yes',
|
||||
alertNo: 'No',
|
||||
alertOk: 'OK',
|
||||
alertCancel: 'Cancel',
|
||||
alertSignIn: 'Sign In',
|
||||
alertCopy: 'Copy',
|
||||
alertClose: 'Close',
|
||||
|
||||
footerOpen: 'Open / New',
|
||||
|
||||
genLen: 'Length',
|
||||
grpTitle: 'Group',
|
||||
grpSearch: 'Enable searching entries in this group',
|
||||
|
||||
iconFavTitle: 'Download and use website favicon',
|
||||
iconSelCustom: 'Select custom icon',
|
||||
|
||||
listEmptyTitle: 'Empty',
|
||||
listEmptyAdd: 'add with {} button above',
|
||||
listGroup: 'Group',
|
||||
listNoWebsite: 'no website',
|
||||
listNoUser: 'no user',
|
||||
listNoAttachments: 'no attachments',
|
||||
|
||||
searchAddNew: 'Add New',
|
||||
searchSort: 'Sort',
|
||||
searchTitle: 'Title',
|
||||
searchWebsite: 'Website',
|
||||
searchUser: 'User',
|
||||
searchCreated: 'Created',
|
||||
searchUpdated: 'Updated',
|
||||
searchAttachments: 'Attachments',
|
||||
searchAZ: 'A → Z',
|
||||
searchZA: 'Z → A',
|
||||
searchON: 'Old → New',
|
||||
searchNO: 'New → Old',
|
||||
searchShiftClickOr: 'shift-click or',
|
||||
|
||||
openOpen: 'Open',
|
||||
openNew: 'New',
|
||||
openDemo: 'Demo',
|
||||
openCaps: 'Caps Lock is on',
|
||||
openKeyFile: 'key file',
|
||||
openKeyFileDropbox: '(from dropbox)',
|
||||
openDropHere: 'drop files here',
|
||||
openFailedRead: 'Failed to read file',
|
||||
openNothingFound: 'Nothing found',
|
||||
openNothingFoundBody: 'You have no files in your Dropbox which could be opened.',
|
||||
openNothingFoundBodyAppFolder: 'Files are searched inside app folder in your Dropbox.',
|
||||
openSelectFile: 'Select a file',
|
||||
openSelectFileBody: 'Select a file from your Dropbox which you would like to open',
|
||||
openPassFor: 'Password for',
|
||||
|
||||
detAttDownload: 'Shift-click attachment button to download or ',
|
||||
detAttDelToRemove: 'Delete to remove',
|
||||
detEmpty: 'Your passwords will be displayed here',
|
||||
detGroupRestore: 'To restore this group, please drag it to any group outside trash',
|
||||
detHistoryClickPoint: 'Click entry history timeline point to view state',
|
||||
detHistoryReturn: 'return to entry',
|
||||
detHistoryRevert: 'Revert to state',
|
||||
detHistoryDel: 'Delete state',
|
||||
detHistoryDiscard: 'Discard changes',
|
||||
detHistoryEmpty: 'empty',
|
||||
detHistoryModified: 'modified',
|
||||
detHistoryRec: 'record',
|
||||
detHistoryRecs: 'records',
|
||||
detHistoryVersion: 'Version',
|
||||
detHistorySaved: 'Saved',
|
||||
detHistoryTitle: 'Title',
|
||||
detHistoryNoTitle: 'no title',
|
||||
detHistoryCurState: 'current state',
|
||||
detHistoryCurUnsavedState: 'current unsaved state',
|
||||
detBackToList: 'back to list',
|
||||
detSetIconColor: 'Change icon color',
|
||||
detSetIcon: 'Change icon',
|
||||
detDropAttachments: 'drop attachments here',
|
||||
detDelEntry: 'Delete',
|
||||
detDelEntryPerm: 'Delete permanently',
|
||||
detUser: 'User',
|
||||
detPassword: 'Password',
|
||||
detWebsite: 'Website',
|
||||
detNotes: 'Notes',
|
||||
detTags: 'Tags',
|
||||
detExpires: 'Expires',
|
||||
detExpired: 'expired',
|
||||
detFile: 'File',
|
||||
detCreated: 'Created',
|
||||
detUpdated: 'Updated',
|
||||
detHistory: 'History',
|
||||
detNetField: 'New Field',
|
||||
detAddField: 'add field',
|
||||
detAttachments: 'Attachments',
|
||||
detDelFromTrash: 'Delete from trash?',
|
||||
detDelFromTrashBody: 'You will not be able to put it back.',
|
||||
detDelFromTrashBodyHint: 'To quickly remove all items from trash, click empty icon in Trash menu.',
|
||||
|
||||
appSecWarn: 'Not Secure!',
|
||||
appSecWarnBody1: 'You have loaded this app with insecure connection. ' +
|
||||
'Someone may be watching you and stealing your passwords. ' +
|
||||
'We strongly advice you to stop, unless you clearly understand what you\'re doing.',
|
||||
appSecWarnBody2: 'Yes, your database is encrypted but no one can guarantee that the app has not been modified on the way to you.',
|
||||
appSecWarnBtn: 'I understand the risks, continue',
|
||||
appUnsavedWarn: 'Unsaved changes!',
|
||||
appUnsavedWarnBody: 'You have unsaved files, all changes will be lost.',
|
||||
appExitBtn: 'Exit and discard unsaved changes',
|
||||
appDontExitBtn: 'Don\'t exit',
|
||||
appUnsavedCloseMsg: 'You have unsaved files, all changes will be lost.',
|
||||
appCannotLockAutoInit: 'The app cannot be locked because auto save is disabled.',
|
||||
appCannotLock: 'You have unsaved changes that will be lost. Continue?',
|
||||
appSaveChangesBtn: 'Save changes',
|
||||
appDiscardChangesBtn: 'Discard changes',
|
||||
appAutoSave: 'Save changes automatically',
|
||||
appSaveError: 'Save Error',
|
||||
appSaveErrorBody: 'Failed to auto-save file',
|
||||
appSaveErrorBodyMul: 'Failed to auto-save files:',
|
||||
|
||||
setGenTitle: 'General Settings',
|
||||
setGenUpdate: 'Update',
|
||||
setGenNewVersion: 'New app version was released and downloaded',
|
||||
setGenReleaseNotes: 'View release notes',
|
||||
setGenReloadTpUpdate: 'Reload to update',
|
||||
setGenUpdateManual: 'New version has been released. It will check for updates and install them automatically ' +
|
||||
'but auto-upgrading from your version is impossible.',
|
||||
setGenDownloadUpdate: 'Download update',
|
||||
setGenUpdateAuto: 'Download and install automatically',
|
||||
setGenUpdateCheck: 'Check but don\'t install',
|
||||
setGenNoUpdate: 'Never check for updates',
|
||||
setGenUpdateChecking: 'Checking for updates',
|
||||
setGenCheckUpdate: 'Check for updates',
|
||||
setGenErrorChecking: 'Error checking for updates',
|
||||
setGenLastCheckSuccess: 'Last successful check was at {}',
|
||||
setGenLastCheckVer: 'the latest version was {}',
|
||||
setGenCheckedAt: 'Checked at',
|
||||
setGenLatestVer: 'you are using the latest version',
|
||||
setGenNewVer: 'new version {} available, released at',
|
||||
setGenDownloadingUpdate: 'Downloading update...',
|
||||
setGenExtractingUpdate: 'Extracting update...',
|
||||
setGenCheckErr: 'There was an error downloading new version',
|
||||
setGenNeverChecked: 'Never checked for updates',
|
||||
setGenRestartToUpdate: 'Restart to update',
|
||||
setGenDownloadAndRestart: 'Download update and restart',
|
||||
setGenAppearance: 'Appearance',
|
||||
setGenTheme: 'Theme',
|
||||
setGenShowSubgroups: 'Show entries from all subgroups',
|
||||
setGenTableView: 'Entries list table view',
|
||||
setGenColorfulIcons: 'Colorful custom icons in list',
|
||||
setGenAutoSync: 'Automatically save and sync',
|
||||
setGenLockInactive: 'Auto-lock if the app is inactive',
|
||||
setGenNoAutoLock: 'Don\'t auto-lock',
|
||||
setGenLockMinutes: 'In {} minutes',
|
||||
setGenLockHour: 'In an hour',
|
||||
setGenClearClip: 'Clear clipboard after copy',
|
||||
setGenNoClear: 'Don\'t clear',
|
||||
setGenClearSeconds: 'In {} seconds',
|
||||
setGenClearMinute: 'In a minute',
|
||||
setGenMinInstead: 'Minimize app instead of close',
|
||||
setGenLockMinimize: 'Auto-lock on minimize',
|
||||
setGenAdvanced: 'Advanced',
|
||||
setGenDevTools: 'Show dev tools',
|
||||
|
||||
setFilePath: 'File path',
|
||||
setFileStorage: 'This file is opened from {}.',
|
||||
setFileIntl: 'This file is stored in internal app storage',
|
||||
setFileLocalHint: 'Want to work seamlessly with local files?',
|
||||
setFileDownloadApp: 'Download a desktop app',
|
||||
setFileSave: 'Save',
|
||||
setFileSyncWith: 'Sync with {}',
|
||||
setFileSaveFile: 'Save to file',
|
||||
setFileExportXml: 'Export to XML',
|
||||
setFileClose: 'Close',
|
||||
setFileSync: 'Sync',
|
||||
setFileLastSync: 'Last sync',
|
||||
setFileLastSyncUnknown: 'unknown',
|
||||
setFileSyncInProgress: 'sync in progress',
|
||||
setFileSyncError: 'Sync error',
|
||||
setFileSettings: 'Settings',
|
||||
setFilePass: 'Master password',
|
||||
setFilePassChanged: 'password was changed; leave the field blank to use old password',
|
||||
setFileKeyFile: 'Key file',
|
||||
setFileSelKeyFile: 'Select a key file',
|
||||
setFileNames: 'Names',
|
||||
setFileName: 'Name',
|
||||
setFileDefUser: 'Default username',
|
||||
setFileHistory: 'History',
|
||||
setFileEnableTrash: 'Enable trash',
|
||||
setFileHistLen: 'History length, keep last records per entry',
|
||||
resFileHistSize: 'History size, total MB per file',
|
||||
setFileAdvanced: 'Advanced',
|
||||
setFileRounds: 'Key encryption rounds',
|
||||
setFileUseKeyFile: 'Use key file',
|
||||
setFileUseGenKeyFile: 'Use generated key file',
|
||||
setFileUseOldKeyFile: 'Use old key file',
|
||||
setFileGenKeyFile: 'Generate new key file',
|
||||
setFileDontUseKeyFile: 'Don\'t use key file',
|
||||
setFileEmptyPass: 'Empty password',
|
||||
setFileEmptyPassBody: 'Saving database with empty password makes it completely unprotected. Do you really want to do it?',
|
||||
setFileSaveError: 'Save error',
|
||||
setFileSaveErrorBody: 'Error saving to file',
|
||||
setFileAlreadyExists: 'Already exists',
|
||||
setFileAlreadyExistsBody: 'File {} already exists in your Dropbox. Overwrite it?',
|
||||
setFileUnsaved: 'Unsaved changes',
|
||||
setFileUnsavedBody: 'There are unsaved changes in this file',
|
||||
setFileCloseNoSave: 'Close and lose changes',
|
||||
setFileDontClose: 'Don\t close',
|
||||
|
||||
setShTitle: 'Shortcuts',
|
||||
setShShowAll: 'show all items',
|
||||
setShColors: 'show items with colors',
|
||||
setShTrash: 'go to trash',
|
||||
setShFind: 'search, or just start typing',
|
||||
setShClearSearch: 'clear search',
|
||||
setShEntry: 'go to entry',
|
||||
setShCopy: 'copy password or selected field',
|
||||
setShPrev: 'go to previous item',
|
||||
setShNext: 'go to next item',
|
||||
setShCreateEntry: 'create entry',
|
||||
setShOpen: 'open / new',
|
||||
setShSave: 'save all files',
|
||||
setShGen: 'generate password',
|
||||
|
||||
setAboutTitle: 'About',
|
||||
setAboutBuilt: 'This app is built with these awesome tools',
|
||||
setAboutLic: 'License',
|
||||
setAboutLicComment: 'The app itself and all included components which are not in public domain are licensed under MIT license',
|
||||
setAboutFirst: 'This is an open-source app created by {}',
|
||||
setAboutSecond: ' and licensed under {}.',
|
||||
setAboutSource: 'The source code and issues are on {}.',
|
||||
|
||||
setHelpTitle: 'Help',
|
||||
setHelpFormat: 'File Format',
|
||||
setHelpFormatBody: 'This is a port of {} app built with web technologies. ' +
|
||||
'It understands files in KeePass format (kdbx). You can create such files (password databases) either in KeePass, ' +
|
||||
'or in this app. The file format is 100% compatible and should be understood by both apps.',
|
||||
setHelpProblems: 'Problems?',
|
||||
setHelpProblems1: 'If something goes wrong, please {} ',
|
||||
setHelpProblems2: 'or {}',
|
||||
setHelpOpenIssue: 'open an issue on GitHub',
|
||||
setHelpContactLink: 'contact a developer directly',
|
||||
setHelpAppInfo: 'App information',
|
||||
setHelpOtherPlatforms: 'Other platforms',
|
||||
setHelpDesktopApps: 'Desktop apps',
|
||||
setHelpWebApp: 'Web app',
|
||||
setHelpUpdates: 'Updates',
|
||||
setHelpTwitter: 'App twitter',
|
||||
|
||||
dropboxNotConfigured: 'Dropbox not configured',
|
||||
dropboxNotConfiguredBody1: 'So, you are using KeeWeb on your own server? Good!',
|
||||
dropboxNotConfiguredBody2: '{} is required to make Dropbox work, it\'s just 3 steps away.',
|
||||
dropboxNotConfiguredLink: 'Some configuration',
|
||||
dropboxLogin: 'Dropbox Login',
|
||||
dropboxLoginBody: 'To continue, you have to sign in to Dropbox.',
|
||||
dropboxSyncError: 'Dropbox Sync Error',
|
||||
dropboxNotFoundBody: 'The file was not found. Has it been removed from another computer?',
|
||||
dropboxFull: 'Dropbox Full',
|
||||
dropboxFullBody: 'Your Dropbox is full, there\'s no space left anymore.',
|
||||
dropboxRateLimitedBody: 'Too many requests to Dropbox have been made by this app. Please, try again later.',
|
||||
dropboxNetError: 'Dropbox Sync Network Error',
|
||||
dropboxNetErrorBody: 'Network error occured during Dropbox sync. Please, check your connection and try again.',
|
||||
dropboxErrorBody: 'Something went wrong during Dropbox sync. Please, try again later. Error code: ',
|
||||
dropboxErrorRepeatBody: 'Something went wrong during Dropbox sync. Please, try again later. Error: ',
|
||||
|
||||
launcherSave: 'Save Passwords Database',
|
||||
launcherFileFilter: 'KeePass files'
|
||||
};
|
||||
|
||||
module.exports = Locale;
|
||||
|
|
|
@ -17,6 +17,7 @@ var Backbone = require('backbone'),
|
|||
IdleTracker = require('../comp/idle-tracker'),
|
||||
Launcher = require('../comp/launcher'),
|
||||
ThemeChanger = require('../util/theme-changer'),
|
||||
Locale = require('../util/locale'),
|
||||
UpdateModel = require('../models/update-model');
|
||||
|
||||
var AppView = Backbone.View.extend({
|
||||
|
@ -224,9 +225,9 @@ var AppView = Backbone.View.extend({
|
|||
var that = this;
|
||||
that.exitAlertShown = true;
|
||||
Alerts.yesno({
|
||||
header: 'Unsaved changes!',
|
||||
body: 'You have unsaved files, all changes will be lost.',
|
||||
buttons: [{result: 'yes', title: 'Exit and discard unsaved changes'}, {result: '', title: 'Don\'t exit'}],
|
||||
header: Locale.appUnsavedWarn,
|
||||
body: Locale.appUnsavedWarnBody,
|
||||
buttons: [{result: 'yes', title: Locale.appExitBtn}, {result: '', title: Locale.appDontExitBtn}],
|
||||
success: function () {
|
||||
Launcher.exit();
|
||||
},
|
||||
|
@ -240,7 +241,7 @@ var AppView = Backbone.View.extend({
|
|||
}
|
||||
return Launcher.preventExit(e);
|
||||
}
|
||||
return 'You have unsaved files, all changes will be lost.';
|
||||
return Locale.appUnsavedCloseMsg;
|
||||
} else if (Launcher && !Launcher.exitRequested && !Launcher.restartPending &&
|
||||
Launcher.canMinimize() && this.model.settings.get('minimizeOnClose')) {
|
||||
Launcher.minimizeApp();
|
||||
|
@ -294,18 +295,17 @@ var AppView = Backbone.View.extend({
|
|||
if (this.model.settings.get('autoSave')) {
|
||||
this.saveAndLock(autoInit);
|
||||
} else {
|
||||
var message = autoInit ? 'The app cannot be locked because auto save is disabled.'
|
||||
: 'You have unsaved changes that will be lost. Continue?';
|
||||
var message = autoInit ? Locale.appCannotLockAutoInit : Locale.appCannotLock;
|
||||
Alerts.alert({
|
||||
icon: 'lock',
|
||||
header: 'Lock',
|
||||
body: message,
|
||||
buttons: [
|
||||
{ result: 'save', title: 'Save changes' },
|
||||
{ result: 'discard', title: 'Discard changes', error: true },
|
||||
{ result: '', title: 'Cancel' }
|
||||
{ result: 'save', title: Locale.appSaveChangesBtn },
|
||||
{ result: 'discard', title: Locale.appDiscardChangesBtn, error: true },
|
||||
{ result: '', title: Locale.alertCancel }
|
||||
],
|
||||
checkbox: 'Save changes automatically',
|
||||
checkbox: Locale.appAutoSave,
|
||||
success: function(result, autoSaveChecked) {
|
||||
if (result === 'save') {
|
||||
if (autoSaveChecked) {
|
||||
|
@ -344,9 +344,10 @@ var AppView = Backbone.View.extend({
|
|||
if (--pendingCallbacks === 0) {
|
||||
if (errorFiles.length && that.model.files.hasDirtyFiles()) {
|
||||
if (!Alerts.alertDisplayed) {
|
||||
var alertBody = errorFiles.length > 1 ? Locale.appSaveErrorBodyMul : Locale.appSaveErrorBody;
|
||||
Alerts.error({
|
||||
header: 'Save Error',
|
||||
body: 'Failed to auto-save file' + (errorFiles.length > 1 ? 's: ' : '') + ' ' + errorFiles.join(', ')
|
||||
header: Locale.appSaveError,
|
||||
body: alertBody + ' ' + errorFiles.join(', ')
|
||||
});
|
||||
}
|
||||
} else {
|
||||
|
|
|
@ -4,6 +4,7 @@ var Backbone = require('backbone'),
|
|||
KeyHandler = require('../../comp/key-handler'),
|
||||
Keys = require('../../const/keys'),
|
||||
Format = require('../../util/format'),
|
||||
Locale = require('../../util/locale'),
|
||||
Alerts = require('../../comp/alerts'),
|
||||
FieldViewReadOnly = require('../fields/field-view-read-only'),
|
||||
FieldViewReadOnlyRaw = require('../fields/field-view-read-only-raw');
|
||||
|
@ -82,25 +83,26 @@ var DetailsHistoryView = Backbone.View.extend({
|
|||
this.removeFieldViews();
|
||||
this.bodyEl.html('');
|
||||
var colorCls = this.record.color ? this.record.color + '-color' : '';
|
||||
this.fieldViews.push(new FieldViewReadOnly({ model: { name: 'Rev', title: 'Version', value: ix + 1 } }));
|
||||
this.fieldViews.push(new FieldViewReadOnly({ model: { name: 'Updated', title: 'Saved',
|
||||
this.fieldViews.push(new FieldViewReadOnly({ model: { name: 'Rev', title: Locale.detHistoryVersion, value: ix + 1 } }));
|
||||
this.fieldViews.push(new FieldViewReadOnly({ model: { name: 'Updated', title: Locale.detHistorySaved,
|
||||
value: Format.dtStr(this.record.updated) +
|
||||
(this.record.unsaved ? ' (current unsaved state)' : '') +
|
||||
((ix === this.history.length - 1 && !this.record.unsaved) ? ' (current state)' : '') } }));
|
||||
this.fieldViews.push(new FieldViewReadOnlyRaw({ model: { name: '$Title', title: 'Title',
|
||||
value: '<i class="fa fa-' + this.record.icon + ' ' + colorCls + '"></i> ' + _.escape(this.record.title) || '(no title)' } }));
|
||||
this.fieldViews.push(new FieldViewReadOnly({ model: { name: '$UserName', title: 'User', value: this.record.user } }));
|
||||
this.fieldViews.push(new FieldViewReadOnly({ model: { name: '$Password', title: 'Password', value: this.record.password } }));
|
||||
this.fieldViews.push(new FieldViewReadOnly({ model: { name: '$URL', title: 'Website', value: this.record.url } }));
|
||||
this.fieldViews.push(new FieldViewReadOnly({ model: { name: '$Notes', title: 'Notes', value: this.record.notes } }));
|
||||
this.fieldViews.push(new FieldViewReadOnly({ model: { name: 'Tags', title: 'Tags', value: this.record.tags.join(', ') } }));
|
||||
this.fieldViews.push(new FieldViewReadOnly({ model: { name: 'Expires', title: 'Expires',
|
||||
value: this.record.expires ? Format.dtStr(this.record.expires) : 'Never' } }));
|
||||
(this.record.unsaved ? ' (' + Locale.detHistoryCurUnsavedState + ')' : '') +
|
||||
((ix === this.history.length - 1 && !this.record.unsaved) ? ' (' + Locale.detHistoryCurState + ')' : '') } }));
|
||||
this.fieldViews.push(new FieldViewReadOnlyRaw({ model: { name: '$Title', title: Locale.detHistoryTitle,
|
||||
value: '<i class="fa fa-' + this.record.icon + ' ' + colorCls + '"></i> ' +
|
||||
_.escape(this.record.title) || '(' + Locale.detHistoryNoTitle + ')' } }));
|
||||
this.fieldViews.push(new FieldViewReadOnly({ model: { name: '$UserName', title: Locale.detUser, value: this.record.user } }));
|
||||
this.fieldViews.push(new FieldViewReadOnly({ model: { name: '$Password', title: Locale.detPassword, value: this.record.password } }));
|
||||
this.fieldViews.push(new FieldViewReadOnly({ model: { name: '$URL', title: Locale.detWebsite, value: this.record.url } }));
|
||||
this.fieldViews.push(new FieldViewReadOnly({ model: { name: '$Notes', title: Locale.detNotes, value: this.record.notes } }));
|
||||
this.fieldViews.push(new FieldViewReadOnly({ model: { name: 'Tags', title: Locale.detTags, value: this.record.tags.join(', ') } }));
|
||||
this.fieldViews.push(new FieldViewReadOnly({ model: { name: 'Expires', title: Locale.detExpires,
|
||||
value: this.record.expires ? Format.dtStr(this.record.expires) : '' } }));
|
||||
_.forEach(this.record.fields, function(value, field) {
|
||||
this.fieldViews.push(new FieldViewReadOnly({ model: { name: '$' + field, title: field, value: value } }));
|
||||
}, this);
|
||||
if (this.record.attachments.length) {
|
||||
this.fieldViews.push(new FieldViewReadOnly({ model: { name: 'Attachments', title: 'Attachments',
|
||||
this.fieldViews.push(new FieldViewReadOnly({ model: { name: 'Attachments', title: Locale.detAttachments,
|
||||
value: this.record.attachments.map(function(att) { return att.title; }).join(', ') } }));
|
||||
}
|
||||
this.fieldViews.forEach(function(fieldView) {
|
||||
|
|
|
@ -18,6 +18,7 @@ var Backbone = require('backbone'),
|
|||
Alerts = require('../../comp/alerts'),
|
||||
CopyPaste = require('../../comp/copy-paste'),
|
||||
Format = require('../../util/format'),
|
||||
Locale = require('../../util/locale'),
|
||||
FileSaver = require('filesaver'),
|
||||
baron = require('baron'),
|
||||
kdbxweb = require('kdbxweb');
|
||||
|
@ -105,31 +106,31 @@ var DetailsView = Backbone.View.extend({
|
|||
|
||||
addFieldViews: function() {
|
||||
var model = this.model;
|
||||
this.fieldViews.push(new FieldViewText({ model: { name: '$UserName', title: 'User',
|
||||
this.fieldViews.push(new FieldViewText({ model: { name: '$UserName', title: Locale.detUser,
|
||||
value: function() { return model.user; } } }));
|
||||
this.fieldViews.push(new FieldViewText({ model: { name: '$Password', title: 'Password', canGen: true,
|
||||
this.fieldViews.push(new FieldViewText({ model: { name: '$Password', title: Locale.detPassword, canGen: true,
|
||||
value: function() { return model.password; } } }));
|
||||
this.fieldViews.push(new FieldViewUrl({ model: { name: '$URL', title: 'Website',
|
||||
this.fieldViews.push(new FieldViewUrl({ model: { name: '$URL', title: Locale.detWebsite,
|
||||
value: function() { return model.url; } } }));
|
||||
this.fieldViews.push(new FieldViewText({ model: { name: '$Notes', title: 'Notes', multiline: 'true',
|
||||
this.fieldViews.push(new FieldViewText({ model: { name: '$Notes', title: Locale.detNotes, multiline: 'true',
|
||||
value: function() { return model.notes; } } }));
|
||||
this.fieldViews.push(new FieldViewTags({ model: { name: 'Tags', title: 'Tags', tags: this.appModel.tags,
|
||||
this.fieldViews.push(new FieldViewTags({ model: { name: 'Tags', title: Locale.detTags, tags: this.appModel.tags,
|
||||
value: function() { return model.tags; } } }));
|
||||
this.fieldViews.push(new FieldViewDate({ model: { name: 'Expires', title: 'Expires', lessThanNow: '(expired)',
|
||||
this.fieldViews.push(new FieldViewDate({ model: { name: 'Expires', title: Locale.detExpires, lessThanNow: '(' + Locale.detExpired + ')',
|
||||
value: function() { return model.expires; } } }));
|
||||
this.fieldViews.push(new FieldViewReadOnly({ model: { name: 'File', title: 'File',
|
||||
this.fieldViews.push(new FieldViewReadOnly({ model: { name: 'File', title: Locale.detFile,
|
||||
value: function() { return model.fileName; } } }));
|
||||
this.fieldViews.push(new FieldViewReadOnly({ model: { name: 'Created', title: 'Created',
|
||||
this.fieldViews.push(new FieldViewReadOnly({ model: { name: 'Created', title: Locale.detCreated,
|
||||
value: function() { return Format.dtStr(model.created); } } }));
|
||||
this.fieldViews.push(new FieldViewReadOnly({ model: { name: 'Updated', title: 'Updated',
|
||||
this.fieldViews.push(new FieldViewReadOnly({ model: { name: 'Updated', title: Locale.detUpdated,
|
||||
value: function() { return Format.dtStr(model.updated); } } }));
|
||||
this.fieldViews.push(new FieldViewHistory({ model: { name: 'History', title: 'History',
|
||||
this.fieldViews.push(new FieldViewHistory({ model: { name: 'History', title: Locale.detHistory,
|
||||
value: function() { return { length: model.historyLength, unsaved: model.unsaved }; } } }));
|
||||
_.forEach(model.fields, function(value, field) {
|
||||
this.fieldViews.push(new FieldViewCustom({ model: { name: '$' + field, title: field,
|
||||
value: function() { return model.fields[field]; } } }));
|
||||
}, this);
|
||||
var newFieldTitle = 'New Field';
|
||||
var newFieldTitle = Locale.detNetField;
|
||||
if (model.fields[newFieldTitle]) {
|
||||
for (var i = 1; ; i++) {
|
||||
var newFieldTitleVariant = newFieldTitle + i;
|
||||
|
@ -139,7 +140,7 @@ var DetailsView = Backbone.View.extend({
|
|||
}
|
||||
}
|
||||
}
|
||||
this.fieldViews.push(new FieldViewCustom({ model: { name: '', title: 'add field', newField: newFieldTitle,
|
||||
this.fieldViews.push(new FieldViewCustom({ model: { name: '', title: Locale.detAddField, newField: newFieldTitle,
|
||||
value: function() { return ''; } } }));
|
||||
|
||||
var fieldsMainEl = this.$el.find('.details__body-fields');
|
||||
|
@ -486,8 +487,8 @@ var DetailsView = Backbone.View.extend({
|
|||
|
||||
deleteFromTrash: function() {
|
||||
Alerts.yesno({
|
||||
header: 'Delete from trash?',
|
||||
body: 'You will not be able to put it back<p class="muted-color">To quickly remove all items from trash, click empty icon in Trash menu</p>',
|
||||
header: Locale.detDelFromTrash,
|
||||
body: Locale.detDelFromTrashBody + ' <p class="muted-color">' + Locale.detDelFromTrashBodyHint + '</p>',
|
||||
icon: 'minus-circle',
|
||||
success: (function() {
|
||||
this.model.deleteFromTrash();
|
||||
|
|
|
@ -30,9 +30,9 @@ var FieldViewDate = FieldViewText.extend({
|
|||
i18n: {
|
||||
previousMonth: '',
|
||||
nextMonth: '',
|
||||
months: Locale.Months,
|
||||
weekdays: Locale.Weekdays,
|
||||
weekdaysShort: Locale.WeekdaysShort
|
||||
months: Locale.months,
|
||||
weekdays: Locale.weekdays,
|
||||
weekdaysShort: Locale.weekdaysShort
|
||||
}
|
||||
});
|
||||
_.defer(this.picker.show.bind(this.picker));
|
||||
|
|
|
@ -1,15 +1,16 @@
|
|||
'use strict';
|
||||
|
||||
var FieldView = require('./field-view');
|
||||
var FieldView = require('./field-view'),
|
||||
Locale = require('../../util/locale');
|
||||
|
||||
var FieldViewHistory = FieldView.extend({
|
||||
renderValue: function(value) {
|
||||
if (!value.length) {
|
||||
return 'empty';
|
||||
return Locale.detHistoryEmpty;
|
||||
}
|
||||
var text = value.length + ' record' + (value.length % 10 === 1 ? '' : 's');
|
||||
var text = value.length + ' ' + (value.length % 10 === 1 ? Locale.detHistoryRec : Locale.detHistoryRecs);
|
||||
if (value.unsaved) {
|
||||
text += ' (modified)';
|
||||
text += ' (' + Locale.detHistoryModified + ')';
|
||||
}
|
||||
return '<a class="details__history-link">' + text + '</a>';
|
||||
},
|
||||
|
|
|
@ -2,7 +2,8 @@
|
|||
|
||||
var Backbone = require('backbone'),
|
||||
PasswordGenerator = require('../util/password-generator'),
|
||||
CopyPaste = require('../comp/copy-paste');
|
||||
CopyPaste = require('../comp/copy-paste'),
|
||||
Locale = require('../util/locale');
|
||||
|
||||
var DefaultGenOpts = {
|
||||
length: 16, upper: true, lower: true, digits: true, special: false, brackets: false, high: false, ambiguous: false
|
||||
|
@ -31,7 +32,7 @@ var GeneratorView = Backbone.View.extend({
|
|||
|
||||
render: function() {
|
||||
var canCopy = document.queryCommandSupported('copy');
|
||||
var btnTitle = this.model.copy ? canCopy ? 'Copy' : 'Close' : 'OK';
|
||||
var btnTitle = this.model.copy ? canCopy ? Locale.alertCopy : Locale.alertClose : Locale.alertOk;
|
||||
this.renderTemplate({ btnTitle: btnTitle, opt: this.gen });
|
||||
this.resultEl = this.$el.find('.gen__result');
|
||||
this.$el.css(this.model.pos);
|
||||
|
|
|
@ -4,7 +4,8 @@ var Backbone = require('backbone'),
|
|||
Keys = require('../const/keys'),
|
||||
KeyHandler = require('../comp/key-handler'),
|
||||
DropdownView = require('./dropdown-view'),
|
||||
FeatureDetector = require('../util/feature-detector');
|
||||
FeatureDetector = require('../util/feature-detector'),
|
||||
Locale = require('../util/locale');
|
||||
|
||||
var ListSearchView = Backbone.View.extend({
|
||||
template: require('templates/list-search.hbs'),
|
||||
|
@ -28,24 +29,24 @@ var ListSearchView = Backbone.View.extend({
|
|||
|
||||
initialize: function () {
|
||||
this.sortOptions = [
|
||||
{ value: 'title', icon: 'sort-alpha-asc', text: 'Title A → Z' },
|
||||
{ value: '-title', icon: 'sort-alpha-desc', text: 'Title Z → A' },
|
||||
{ value: 'website', icon: 'sort-alpha-asc', text: 'Website A → Z' },
|
||||
{ value: '-website', icon: 'sort-alpha-desc', text: 'Website Z → A' },
|
||||
{ value: 'user', icon: 'sort-alpha-asc', text: 'User A → Z' },
|
||||
{ value: '-user', icon: 'sort-alpha-desc', text: 'User Z → A' },
|
||||
{ value: 'created', icon: 'sort-numeric-asc', text: 'Created Old → New' },
|
||||
{ value: '-created', icon: 'sort-numeric-desc', text: 'Created New → Old' },
|
||||
{ value: 'updated', icon: 'sort-numeric-asc', text: 'Updated Old → New' },
|
||||
{ value: '-updated', icon: 'sort-numeric-desc', text: 'Updated New → Old' },
|
||||
{ value: '-attachments', icon: 'sort-amount-desc', text: 'Attachments' }
|
||||
{ value: 'title', icon: 'sort-alpha-asc', text: Locale.searchTitle + ' ' + Locale.searchAZ },
|
||||
{ value: '-title', icon: 'sort-alpha-desc', text: Locale.searchTitle + ' ' + Locale.searchZA },
|
||||
{ value: 'website', icon: 'sort-alpha-asc', text: Locale.searchWebsite + ' ' + Locale.searchAZ },
|
||||
{ value: '-website', icon: 'sort-alpha-desc', text: Locale.searchWebsite + ' ' + Locale.searchZA },
|
||||
{ value: 'user', icon: 'sort-alpha-asc', text: Locale.searchUser + ' ' + Locale.searchAZ },
|
||||
{ value: '-user', icon: 'sort-alpha-desc', text: Locale.searchUser + ' ' + Locale.searchZA },
|
||||
{ value: 'created', icon: 'sort-numeric-asc', text: Locale.searchCreated + ' ' + Locale.searchON },
|
||||
{ value: '-created', icon: 'sort-numeric-desc', text: Locale.searchCreated + ' ' + Locale.searchNO },
|
||||
{ value: 'updated', icon: 'sort-numeric-asc', text: Locale.searchUpdated + ' ' + Locale.searchON },
|
||||
{ value: '-updated', icon: 'sort-numeric-desc', text: Locale.searchUpdated + ' ' + Locale.searchNO },
|
||||
{ value: '-attachments', icon: 'sort-amount-desc', text: Locale.searchAttachments }
|
||||
];
|
||||
this.sortIcons = {};
|
||||
this.sortOptions.forEach(function(opt) {
|
||||
this.sortIcons[opt.value] = opt.icon;
|
||||
}, this);
|
||||
this.createOptions = [
|
||||
{ value: 'entry', icon: 'key', text: 'Entry <span class="muted-color">(shift-click or ' +
|
||||
{ value: 'entry', icon: 'key', text: 'Entry <span class="muted-color">(' + Locale.searchShiftClickOr + ' ' +
|
||||
FeatureDetector.altShortcutSymbol(true) + 'N)</span>' },
|
||||
{ value: 'group', icon: 'folder', text: 'Group' }
|
||||
];
|
||||
|
|
|
@ -4,7 +4,8 @@ var Backbone = require('backbone'),
|
|||
KeyHandler = require('../../comp/key-handler'),
|
||||
Keys = require('../../const/keys'),
|
||||
Alerts = require('../../comp/alerts'),
|
||||
DragDropInfo = require('../../comp/drag-drop-info');
|
||||
DragDropInfo = require('../../comp/drag-drop-info'),
|
||||
Locale = require('../../util/locale');
|
||||
|
||||
var MenuItemView = Backbone.View.extend({
|
||||
template: require('templates/menu/menu-item.hbs'),
|
||||
|
@ -162,8 +163,8 @@ var MenuItemView = Backbone.View.extend({
|
|||
emptyTrash: function(e) {
|
||||
e.stopPropagation();
|
||||
Alerts.yesno({
|
||||
header: 'Empty trash?',
|
||||
body: 'You will not be able to put items back',
|
||||
header: Locale.menuEmptyTrashAlert,
|
||||
body: Locale.menuEmptyTrashAlertBody,
|
||||
icon: 'minus-circle',
|
||||
success: function() {
|
||||
Backbone.trigger('empty-trash');
|
||||
|
|
|
@ -5,7 +5,8 @@ var Backbone = require('backbone'),
|
|||
Alerts = require('../comp/alerts'),
|
||||
SecureInput = require('../comp/secure-input'),
|
||||
DropboxLink = require('../comp/dropbox-link'),
|
||||
Logger = require('../util/logger');
|
||||
Logger = require('../util/logger'),
|
||||
Locale = require('../util/locale');
|
||||
|
||||
var logger = new Logger('open-view');
|
||||
|
||||
|
@ -113,7 +114,7 @@ var OpenView = Backbone.View.extend({
|
|||
}
|
||||
}).bind(this);
|
||||
reader.onerror = (function() {
|
||||
Alerts.error({ header: 'Failed to read file' });
|
||||
Alerts.error({ header: Locale.openFailedRead });
|
||||
if (complete) {
|
||||
complete(false);
|
||||
}
|
||||
|
@ -125,7 +126,7 @@ var OpenView = Backbone.View.extend({
|
|||
this.$el.addClass('open--file');
|
||||
this.$el.find('.open__settings-key-file').removeClass('hide');
|
||||
this.inputEl[0].removeAttribute('readonly');
|
||||
this.inputEl[0].setAttribute('placeholder', 'Password for ' + this.params.name);
|
||||
this.inputEl[0].setAttribute('placeholder', Locale.openPassFor + ' ' + this.params.name);
|
||||
this.inputEl.focus();
|
||||
},
|
||||
|
||||
|
@ -290,16 +291,15 @@ var OpenView = Backbone.View.extend({
|
|||
});
|
||||
if (!buttons.length) {
|
||||
Alerts.error({
|
||||
header: 'Nothing found',
|
||||
body: 'You have no files in your Dropbox which could be opened.' +
|
||||
(dirStat && dirStat.inAppFolder ? ' Files are searched inside app folder in your Dropbox.' : '')
|
||||
header: Locale.openNothingFound,
|
||||
body: Locale.openNothingFoundBody + (dirStat && dirStat.inAppFolder ? ' ' + Locale.openNothingFoundBodyAppFolder : '')
|
||||
});
|
||||
return;
|
||||
}
|
||||
buttons.push({ result: '', title: 'Cancel' });
|
||||
buttons.push({ result: '', title: Locale.alertCancel });
|
||||
Alerts.alert({
|
||||
header: 'Select a file',
|
||||
body: 'Select a file from your Dropbox which you would like to open',
|
||||
header: Locale.openSelectFile,
|
||||
body: Locale.openSelectFileBody,
|
||||
icon: 'dropbox',
|
||||
buttons: buttons,
|
||||
esc: '',
|
||||
|
|
|
@ -9,6 +9,7 @@ var Backbone = require('backbone'),
|
|||
Links = require('../../const/links'),
|
||||
DropboxLink = require('../../comp/dropbox-link'),
|
||||
Format = require('../../util/format'),
|
||||
Locale = require('../../util/locale'),
|
||||
kdbxweb = require('kdbxweb'),
|
||||
FileSaver = require('filesaver');
|
||||
|
||||
|
@ -72,14 +73,15 @@ var SettingsAboutView = Backbone.View.extend({
|
|||
var sel = this.$el.find('#settings__file-key-file');
|
||||
sel.html('');
|
||||
if (keyFileName && keyFileChanged) {
|
||||
var text = keyFileName !== 'Generated' ? 'Use key file ' + keyFileName : 'Use generated key file';
|
||||
var text = keyFileName !== 'Generated' ? Locale.setFileUseKeyFile + ' ' + keyFileName : Locale.setFileUseGenKeyFile;
|
||||
$('<option/>').val('ex').text(text).appendTo(sel);
|
||||
}
|
||||
if (oldKeyFileName) {
|
||||
$('<option/>').val('old').text('Use ' + (keyFileChanged ? 'old ' : '') + 'key file ' + oldKeyFileName).appendTo(sel);
|
||||
var useText = keyFileChanged ? Locale.setFileUseOldKeyFile : Locale.setFileUseKeyFile + ' ' + oldKeyFileName;
|
||||
$('<option/>').val('old').text(useText).appendTo(sel);
|
||||
}
|
||||
$('<option/>').val('gen').text('Generate new key file').appendTo(sel);
|
||||
$('<option/>').val('none').text('Don\'t use key file').appendTo(sel);
|
||||
$('<option/>').val('gen').text(Locale.setFileGenKeyFile).appendTo(sel);
|
||||
$('<option/>').val('none').text(Locale.setFileDontUseKeyFile).appendTo(sel);
|
||||
if (keyFileName && keyFileChanged) {
|
||||
sel.val('ex');
|
||||
} else if (!keyFileName) {
|
||||
|
@ -93,8 +95,8 @@ var SettingsAboutView = Backbone.View.extend({
|
|||
if (!this.model.get('passwordLength')) {
|
||||
var that = this;
|
||||
Alerts.yesno({
|
||||
header: 'Empty password',
|
||||
body: 'Saving database with empty password makes it completely unprotected. Do you really want to do it?',
|
||||
header: Locale.setFileEmptyPass,
|
||||
body: Locale.setFileEmptyPassBody,
|
||||
success: function() {
|
||||
continueCallback();
|
||||
},
|
||||
|
@ -149,8 +151,8 @@ var SettingsAboutView = Backbone.View.extend({
|
|||
Storage.file.save(path, data, function (err) {
|
||||
if (err) {
|
||||
Alerts.error({
|
||||
header: 'Save error',
|
||||
body: 'Error saving to file ' + path + ': \n' + err
|
||||
header: Locale.setFileSaveError,
|
||||
body: Locale.setFileSaveErrorBody + ' ' + path + ': \n' + err
|
||||
});
|
||||
}
|
||||
});
|
||||
|
@ -191,8 +193,8 @@ var SettingsAboutView = Backbone.View.extend({
|
|||
if (existingPath) {
|
||||
Alerts.yesno({
|
||||
icon: 'dropbox',
|
||||
header: 'Already exists',
|
||||
body: 'File ' + that.model.escape('name') + ' already exists in your Dropbox. Overwrite it?',
|
||||
header: Locale.setFileAlreadyExists,
|
||||
body: Locale.setFileAlreadyExistsBody.replace('{}', that.model.escape('name')),
|
||||
success: function() {
|
||||
that.model.set('syncing', true);
|
||||
DropboxLink.deleteFile(existingPath, function(err) {
|
||||
|
@ -215,12 +217,11 @@ var SettingsAboutView = Backbone.View.extend({
|
|||
if (this.model.get('modified')) {
|
||||
var that = this;
|
||||
Alerts.yesno({
|
||||
header: 'Unsaved changes',
|
||||
body: 'There are unsaved changes in this file',
|
||||
header: Locale.setFileUnsaved,
|
||||
body: Locale.setFileUnsavedBody,
|
||||
buttons: [
|
||||
//{result: 'save', title: 'Save and close'},
|
||||
{result: 'close', title: 'Close and lose changes', error: true},
|
||||
{result: '', title: 'Don\t close'}
|
||||
{result: 'close', title: Locale.setFileCloseNoSave, error: true},
|
||||
{result: '', title: Locale.setFileDontClose}
|
||||
],
|
||||
success: function(result) {
|
||||
if (result === 'close') {
|
||||
|
|
|
@ -8,6 +8,7 @@ var Backbone = require('backbone'),
|
|||
UpdateModel = require('../../models/update-model'),
|
||||
RuntimeInfo = require('../../comp/runtime-info'),
|
||||
FeatureDetector = require('../../util/feature-detector'),
|
||||
Locale = require('../../util/locale'),
|
||||
Links = require('../../const/links');
|
||||
|
||||
var SettingsGeneralView = Backbone.View.extend({
|
||||
|
@ -77,36 +78,36 @@ var SettingsGeneralView = Backbone.View.extend({
|
|||
getUpdateInfo: function() {
|
||||
switch (UpdateModel.instance.get('status')) {
|
||||
case 'checking':
|
||||
return 'Checking for updates...';
|
||||
return Locale.setGenUpdateChecking + '...';
|
||||
case 'error':
|
||||
var errMsg = 'Error checking for updates';
|
||||
var errMsg = Locale.setGenErrorChecking;
|
||||
if (UpdateModel.instance.get('lastError')) {
|
||||
errMsg += ': ' + UpdateModel.instance.get('lastError');
|
||||
}
|
||||
if (UpdateModel.instance.get('lastSuccessCheckDate')) {
|
||||
errMsg += '. Last successful check was at ' + Format.dtStr(UpdateModel.instance.get('lastSuccessCheckDate')) +
|
||||
': the latest version was ' + UpdateModel.instance.get('lastVersion');
|
||||
errMsg += '. ' + Locale.setGenLastCheckSuccess.replace('{}', Format.dtStr(UpdateModel.instance.get('lastSuccessCheckDate'))) +
|
||||
': ' + Locale.setGenLastCheckVer.replace('{}', UpdateModel.instance.get('lastVersion'));
|
||||
}
|
||||
return errMsg;
|
||||
case 'ok':
|
||||
var msg = 'Checked at ' + Format.dtStr(UpdateModel.instance.get('lastCheckDate')) + ': ';
|
||||
var msg = Locale.setGenCheckedAt + ' ' + Format.dtStr(UpdateModel.instance.get('lastCheckDate')) + ': ';
|
||||
if (RuntimeInfo.version === UpdateModel.instance.get('lastVersion')) {
|
||||
msg += 'you are using the latest version';
|
||||
msg += Locale.setGenLatestVer;
|
||||
} else {
|
||||
msg += 'new version ' + UpdateModel.instance.get('lastVersion') + ' available, released at ' +
|
||||
msg += Locale.setGenNewVer.replace('{}', UpdateModel.instance.get('lastVersion')) + ' ' +
|
||||
Format.dStr(UpdateModel.instance.get('lastVersionReleaseDate'));
|
||||
}
|
||||
switch (UpdateModel.instance.get('updateStatus')) {
|
||||
case 'downloading':
|
||||
return msg + '. Downloading update...';
|
||||
return msg + '. ' + Locale.setGenDownloadingUpdate;
|
||||
case 'extracting':
|
||||
return msg + '. Extracting update...';
|
||||
return msg + '. ' + Locale.setGenExtractingUpdate;
|
||||
case 'error':
|
||||
return msg + '. There was an error downloading new version';
|
||||
return msg + '. ' + Locale.setGenCheckErr;
|
||||
}
|
||||
return msg;
|
||||
default:
|
||||
return 'Never checked for updates';
|
||||
return Locale.setGenNeverChecked;
|
||||
}
|
||||
},
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<div class="details__attachment-preview">
|
||||
<div class="details__attachment-preview-data"></div>
|
||||
<i class="fa details__attachment-preview-icon"></i>
|
||||
<div class="details__attachment-preview-download-text">Shift-click attachment button to download
|
||||
or <span class="details__attachment-preview-download-text-shortcut"></span>Delete to remove</div>
|
||||
<div class="details__attachment-preview-download-text">{{res 'detAttDownload'}}
|
||||
<span class="details__attachment-preview-download-text-shortcut"></span>{{res 'detAttDelToRemove'}}</div>
|
||||
</div>
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
<div class="empty-block muted-color">
|
||||
<h1 class="empty-block__title">Your passwords will be displayed here</h1>
|
||||
</div>
|
||||
<h1 class="empty-block__title">{{res 'detEmpty'}}</h1>
|
||||
</div>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<div class="empty-block muted-color">
|
||||
<h1 class="empty-block__title">To restore this group, please drag it to any group outside trash</h1>
|
||||
<h1 class="empty-block__title">{{res 'detGroupRestore'}}</h1>
|
||||
<div class="empty-block__lower-btns">
|
||||
<i class="details__buttons-trash-del fa fa-minus-circle"></i>
|
||||
</div>
|
||||
|
|
|
@ -1,22 +1,22 @@
|
|||
<div class="details__history">
|
||||
<div class="details__history-desc muted-color">Click entry history timeline point to view state</div>
|
||||
<div class="details__history-desc muted-color">{{res 'detHistoryClickPoint'}}</div>
|
||||
<div class="details__history-top">
|
||||
<div class="details__history-timeline">
|
||||
<div class="details__history-timeline-axis"></div>
|
||||
<div class="details__history-arrow-prev"><i class="fa fa-long-arrow-left"></i></div>
|
||||
<div class="details__history-arrow-next"><i class="fa fa-long-arrow-right"></i></div>
|
||||
</div>
|
||||
<a class="details__history-close">return to entry <i class="fa fa-external-link-square"></i></a>
|
||||
<a class="details__history-close">{{res 'detHistoryReturn'}} <i class="fa fa-external-link-square"></i></a>
|
||||