String globalization

This commit is contained in:
Antelle 2015-12-17 21:25:25 +03:00
parent dd2f94978e
commit e0f5f49255
43 changed files with 633 additions and 310 deletions

View File

@ -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
});

View File

@ -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],

View File

@ -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;
}

View File

@ -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) {

View File

@ -4,3 +4,4 @@ require('./cmp');
require('./ifeq');
require('./ifneq');
require('./ifemptyoreq');
require('./res');

View File

@ -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;
});

View File

@ -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([

View File

@ -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;
}

View File

@ -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() : '';
}
};

View File

@ -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 &rarr; Z',
searchZA: 'Z &rarr; A',
searchON: 'Old &rarr; New',
searchNO: 'New &rarr; 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;

View File

@ -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 {

View File

@ -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) {

View File

@ -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();

View File

@ -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));

View File

@ -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>';
},

View File

@ -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);

View File

@ -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 &rarr; Z' },
{ value: '-title', icon: 'sort-alpha-desc', text: 'Title Z &rarr; A' },
{ value: 'website', icon: 'sort-alpha-asc', text: 'Website A &rarr; Z' },
{ value: '-website', icon: 'sort-alpha-desc', text: 'Website Z &rarr; A' },
{ value: 'user', icon: 'sort-alpha-asc', text: 'User A &rarr; Z' },
{ value: '-user', icon: 'sort-alpha-desc', text: 'User Z &rarr; A' },
{ value: 'created', icon: 'sort-numeric-asc', text: 'Created Old &rarr; New' },
{ value: '-created', icon: 'sort-numeric-desc', text: 'Created New &rarr; Old' },
{ value: 'updated', icon: 'sort-numeric-asc', text: 'Updated Old &rarr; New' },
{ value: '-updated', icon: 'sort-numeric-desc', text: 'Updated New &rarr; 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' }
];

View File

@ -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');

View File

@ -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: '',

View File

@ -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') {

View File

@ -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;
}
},

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>
</div>
<div class="details__history-body">
<div class="details__field">
<div class="details__field-label">Title</div>
<div class="details__field-value"><i class="fa fa-key yellow-color"></i> Agent forum</div>
<div class="details__field-label">{{Res 'title'}}</div>
<div class="details__field-value"><i class="fa fa-key"></i> </div>
</div>
</div>
<div class="details__history-buttons">
<button class="details__history-button details__history-button-revert btn-silent">Revert to state</button>
<button class="details__history-button details__history-button-delete btn-error">Delete state</button>
<button class="details__history-button details__history-button-discard btn-error">Discard changes</button>
<button class="details__history-button details__history-button-revert btn-silent">{{res 'detHistoryRevert'}}</button>
<button class="details__history-button details__history-button-delete btn-error">{{res 'detHistoryDel'}}</button>
<button class="details__history-button details__history-button-discard btn-error">{{res 'detHistoryDiscard'}}</button>
</div>
</div>

View File

@ -1,9 +1,9 @@
<div class="details">
<div class="details__back-button">
<i class="fa fa-chevron-left"></i> back to list
<i class="fa fa-chevron-left"></i> {{res 'detBackToList'}}
</div>
<div class="details__header">
<i class="details__header-color fa fa-bookmark-o" title="Change icon color">
<i class="details__header-color fa fa-bookmark-o" title="{{res 'detSetIconColor'}}">
<span class="details__colors-popup">
<span class="details__colors-popup-item yellow-color fa fa-bookmark-o" data-color="yellow"></span>
<span class="details__colors-popup-item green-color fa fa-bookmark-o" data-color="green"></span>
@ -15,9 +15,9 @@
</i>
<h1 class="details__header-title">{{#if title}}{{title}}{{else}}(no title){{/if}}</h1>
{{#if customIcon}}
<div class="details__header-icon details__header-icon--icon" style="background-image: url({{{customIcon}}})" title="Change icon"></div>
<div class="details__header-icon details__header-icon--icon" style="background-image: url({{{customIcon}}})" title="{{res 'detSetIcon'}}"></div>
{{else}}
<i class="details__header-icon fa fa-{{icon}}" title="Change icon"></i>
<i class="details__header-icon fa fa-{{icon}}" title="{{res 'detSetIcon'}}"></i>
{{/if}}
</div>
<div class="details__body">
@ -31,22 +31,22 @@
</div>
<div class="details__buttons">
{{#if deleted~}}
<i class="details__buttons-trash-del fa fa-minus-circle" title="Delete permanently"></i>
<i class="details__buttons-trash-del fa fa-minus-circle" title="{{res 'detDelEntryPerm'}}"></i>
{{~else~}}
<i class="details__buttons-trash fa fa-trash-o" title="Delete"></i>
<i class="details__buttons-trash fa fa-trash-o" title="{{res 'detDelEntry'}}"></i>
{{~/if~}}
<div class="details__attachments">
{{#each attachments as |attachment ix|}}
<div class="details__attachment" data-id="{{ix}}"><i class="fa fa-{{attachment.icon}}"></i> {{attachment.title}}</div>
{{else}}
<div class="details__attachment-add">
<span class="details__attachment-add-title">drag attachments here</span> <i class="fa fa-paperclip"></i>
<span class="details__attachment-add-title">{{res 'detDropAttachments'}}</span> <i class="fa fa-paperclip"></i>
</div>
{{/each}}
</div>
</div>
<div class="details__dropzone">
<i class="fa fa-paperclip muted-color details__dropzone-icon"></i>
<h1 class="muted-color details__dropzone-header">drop attachments here</h1>
<h1 class="muted-color details__dropzone-header">{{res 'detDropAttachments'}}</h1>
</div>
</div>

View File

@ -12,7 +12,7 @@
{{~/if}}
</div>
{{/each}}
<div class="footer__db footer__db--dimmed footer__db--expanded footer__db-open"><i class="fa fa-plus"></i> Open / New</div>
<div class="footer__db footer__db--dimmed footer__db--expanded footer__db-open"><i class="fa fa-plus"></i> {{res 'footerOpen'}}</div>
<div class="footer__btn footer__btn-help"><i class="fa fa-question"></i></div>
<div class="footer__btn footer__btn-settings">
{{#if updateAvailable}}

View File

@ -1,5 +1,5 @@
<div class="gen">
<div>Length: <span class="gen__length-range-val">{{opt.length}}</span></div>
<div>{{res 'genLen'}}: <span class="gen__length-range-val">{{opt.length}}</span></div>
<input type="range" class="gen__length-range" value="13" min="0" max="25" />
<div>
<div class="gen__check"><input type="checkbox" id="gen__check-upper"
@ -17,6 +17,6 @@
<div class="gen__check"><input type="checkbox" id="gen__check-ambiguous"
data-id="ambiguous" {{#if opt.ambiguous}}checked{{/if}}><label for="gen__check-ambiguous">0Oo</label></div>
</div>
<div class="gen__result">password</div>
<div class="gen__result"></div>
<div class="gen__btn-wrap"><button class="gen__btn-ok">{{btnTitle}}</button></div>
</div>

View File

@ -1,21 +1,21 @@
<div class="grp">
<div class="grp__back-button">
return to app <i class="fa fa-external-link-square"></i>
{{res 'retToApp'}} <i class="fa fa-external-link-square"></i>
</div>
<div class="scroller">
<h1>Group</h1>
<h1>{{res 'grpTitle'}}</h1>
<div class="grp__field">
<label for="grp__field-title">Name:</label>
<label for="grp__field-title">{{Res 'name'}}:</label>
<input type="text" class="input-base" id="grp__field-title" value="{{title}}" size="50" maxlength="1024"
required {{#if readonly}}readonly{{/if}} />
</div>
{{#unless readonly}}
<div>
<input type="checkbox" class="input-base" id="grp__check-search" {{#if enableSearching}}checked{{/if}} />
<label for="grp__check-search">Enable searching entries in this group</label>
<label for="grp__check-search">{{res 'grpSearch'}}</label>
</div>
{{/unless}}
<label>Icon:</label>
<label>{{Res 'icon'}}:</label>
{{#if customIcon}}
<img src="{{{customIcon}}}" class="grp__icon grp__icon--image" />
{{else}}

View File

@ -8,12 +8,12 @@
<input type="file" class="icon-select__file-input hide-by-pos" accept="image/*" />
{{#if canDownloadFavicon}}
<span class="icon-select__icon icon-select__icon-btn icon-select__icon-download"
data-val="special" data-special="download" title="Download and use website favicon">
data-val="special" data-special="download" title="{{res 'iconFavTitle'}}">
<i class="fa fa-cloud-download"></i>
</span>
{{/if}}
<span class="icon-select__icon icon-select__icon-btn icon-select__icon-select"
data-val="special" data-special="select" title="Select custom icon">
data-val="special" data-special="select" title="{{res 'iconSelCustom'}}">
<i class="fa fa-ellipsis-h"></i>
</span>
{{#each customIcons as |icon ci|}}

View File

@ -1,7 +1,7 @@
<div class="empty-block muted-color">
<div class="empty-block__icon"><i class="fa fa-key"></i></div>
<h1 class="empty-block__title">Empty</h1>
<h1 class="empty-block__title">{{res 'listEmptyTitle'}}</h1>
<p class="empty-block__text">
add with <i class="fa fa-plus"></i> button above
{{#res 'listEmptyAdd'}} <i class="fa fa-plus"></i>{{/res}}
</p>
</div>
</div>

View File

@ -4,5 +4,5 @@
{{~else~}}
<i class="fa fa-{{icon}} {{#if color}}{{color}}-color{{/if}} list__item-icon"></i>
{{~/if}}
<span class="list__item-title">{{#if title}}{{title}}{{else}}(no title){{/if}}</span><span class="list__item-descr thin">{{description}}</span>
<span class="list__item-title">{{#if title}}{{title}}{{else}}({{res 'noTitle'}}){{/if}}</span><span class="list__item-descr thin">{{description}}</span>
</div>

View File

@ -6,7 +6,7 @@
<i class="fa fa-{{icon}} {{#if color}}{{color}}-color{{/if}} list__item-icon"></i>
{{~/if~}}
</td>
<td>{{#if title}}{{title}}{{else}}(no title){{/if}}</td>
<td>{{#if title}}{{title}}{{else}}({{res 'noTitle'}}){{/if}}</td>
<td>{{user}}</td>
<td>{{url}}</td>
<td>{{tags}}</td>

View File

@ -7,11 +7,11 @@
<input type="text" class="list__search-field input-padding-right" autocomplete="off">
<i class="list__search-icon-search fa fa-search"></i>
</div>
<div class="list__search-btn-new">
<i class="fa fa-plus" title="Add new"></i>
<div class="list__search-btn-new" title="{{res 'searchAddNew'}}">
<i class="fa fa-plus"></i>
</div>
<div class="list__search-btn-sort">
<i class="fa fa-sort-alpha-asc" title="Sort"></i>
<div class="list__search-btn-sort" title="{{res 'searchSort'}}">
<i class="fa fa-sort-alpha-asc"></i>
</div>
</div>
</div>

View File

@ -2,11 +2,11 @@
<thead>
<tr>
<th></th>
<th>Title</th>
<th>User</th>
<th>Website</th>
<th>Tags</th>
<th>Notes</th>
<th>{{Res 'title'}}</th>
<th>{{Res 'user'}}</th>
<th>{{Res 'website'}}</th>
<th>{{Res 'tags'}}</th>
<th>{{Res 'notes'}}</th>
</tr>
</thead>
<tbody>

View File

@ -10,7 +10,7 @@
{{~else~}}
<i class="menu__item-icon fa {{#if icon}}fa-{{icon}}{{else}}menu__item-icon--no-icon{{/if}}"></i>
{{~/if}}
<span class="menu__item-title">{{#if title}}{{title}}{{else}}(no title){{/if}}</span>
<span class="menu__item-title">{{#if title}}{{title}}{{else}}({{res 'noTitle'}}){{/if}}</span>
{{#if options}}
<div class="menu__item-options">
{{#each options.models as |opt|}}
@ -20,6 +20,6 @@
</div>
{{/if}}
{{#if editable}}<i class="menu__item-edit fa fa-cog"></i>{{/if}}
{{#ifeq filterKey 'trash'}}<i class="menu__item-empty-trash fa fa-minus-circle" title="Empty Trash"></i>{{/ifeq}}
{{#ifeq filterKey 'trash'}}<i class="menu__item-empty-trash fa fa-minus-circle" title="{{res 'menuEmptyTrash'}}"></i>{{/ifeq}}
</div>
</div>

View File

@ -3,11 +3,11 @@
<div class="open__icons">
<div class="open__icon open__icon-open">
<i class="fa fa-lock open__icon-i"></i>
<div class="open__icon-text">Open</div>
<div class="open__icon-text">{{res 'openOpen'}}</div>
</div>
<div class="open__icon open__icon-new">
<i class="fa fa-plus open__icon-i"></i>
<div class="open__icon-text">New</div>
<div class="open__icon-text">{{res 'openNew'}}</div>
</div>
<div class="open__icon open__icon-dropbox">
<i class="fa fa-dropbox open__icon-i"></i>
@ -15,12 +15,12 @@
</div>
<div class="open__icon open__icon-demo">
<i class="fa fa-magic open__icon-i"></i>
<div class="open__icon-text">Demo</div>
<div class="open__icon-text">{{res 'openDemo'}}</div>
</div>
</div>
<div class="open__pass-area">
<div class="open__pass-warn-wrap">
<div class="open__pass-warning muted-color invisible"><i class="fa fa-exclamation-triangle"></i> Caps Lock is on</div>
<div class="open__pass-warning muted-color invisible"><i class="fa fa-exclamation-triangle"></i> {{res 'openCaps'}}</div>
</div>
<div class="open__pass-field-wrap">
<input class="open__pass-input" type="password" size="30" autocomplete="off" maxlength="128"
@ -31,8 +31,8 @@
<div class="open__settings">
<div class="open__settings-key-file hide">
<i class="fa fa-key open__settings-key-file-icon"></i>
<span class="open__settings-key-file-name">key file</span>
<span class="open__settings-key-file-dropbox"> (from dropbox)</span>
<span class="open__settings-key-file-name">{{res 'openKeyFile'}}</span>
<span class="open__settings-key-file-dropbox"> {{res 'openKeyFileDropbox'}}</span>
</div>
</div>
<div class="open__last">
@ -47,6 +47,6 @@
</div>
<div class="open__dropzone">
<i class="fa fa-lock muted-color open__dropzone-icon"></i>
<h1 class="muted-color open__dropzone-header">drop files here</h1>
<h1 class="muted-color open__dropzone-header">{{res 'openDropHere'}}</h1>
</div>
</div>

View File

@ -1,9 +1,9 @@
<div>
<h1><i class="fa fa-info"></i> About KeeWeb v{{version}}</h1>
<p>This is an open-source app created by <a href="http://antelle.net" target="_blank">Antelle</a> and licensed under
<a href="{{licenseLink}}" target="_blank">MIT</a>.
The source code and issues are <a href="{{repoLink}}" target="_blank">on GitHub <i class="fa fa-github-alt"></i></a>.</p>
<p>This app is built with these awesome tools:</p>
<h1><i class="fa fa-info"></i> {{res 'setAboutTitle'}} KeeWeb v{{version}}</h1>
<p>{{#res 'setAboutFirst'}}<a href="http://antelle.net" target="_blank">Antelle</a>{{/res~}}
{{~#res 'setAboutSecond'}}<a href="{{licenseLink}}" target="_blank">MIT</a>{{/res}}
{{#res 'setAboutSource'}}<a href="{{repoLink}}" target="_blank">GitHub <i class="fa fa-github-alt"></i></a>{{/res}}</p>
<p>{{res 'setAboutBuilt'}}:</p>
<h3>Libraries</h3>
<ul>
<li><a href="http://electron.atom.io/" target="_blank">electron</a><span class="muted-color">, cross-platform desktop apps framework</span></li>
@ -54,8 +54,8 @@
and CSS toolkit</span></li>
</ul>
<h2>License</h2>
<p>The app itself and all included components which are not in public domain are licensed under MIT license:</p>
<h2>{{res 'setAboutLic'}}</h2>
<p>{{res 'setAboutLicComment'}}:</p>
<p></p>
<p>Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the Software without restriction, including without limitation

View File

@ -1,59 +1,59 @@
<div>
<h1><i class="fa fa-lock"></i> {{name}}</h1>
{{#if storage}}
{{#ifeq storage 'file'}}<p>File path: {{path}}</p>{{/ifeq}}
{{#ifeq storage 'dropbox'}}<p>This file is opened from Dropbox.</p>{{/ifeq}}
{{#ifeq storage 'file'}}<p>{{res 'setFilePath'}}: {{path}}</p>{{/ifeq}}
{{#ifeq storage 'dropbox'}}<p>{{#res 'setFileStorage'}}Dropbox{{/res}}</p>{{/ifeq}}
{{else}}
<p>This file is stored in internal app storage.</p>
<p>{{res 'setFileIntl'}}.</p>
{{#unless supportFiles}}
<p>Want to work seamlessly with local files? <a href="{{desktopLink}}" target="_blank">Download a desktop app</a></p>
<p>{{res 'setFileLocalHint'}} <a href="{{desktopLink}}" target="_blank">{{res 'setFileDownloadApp'}}</a></p>
{{/unless}}
{{/if}}
<div class="settings__file-buttons">
{{#ifemptyoreq storage 'file'}}<button class="settings__file-button-save-default">Save</button>{{/ifemptyoreq}}
{{#ifemptyoreq storage 'file'}}<button class="settings__file-button-save-default">{{res 'setFileSave'}}</button>{{/ifemptyoreq}}
<button class="settings__file-button-save-dropbox {{#ifneq storage 'dropbox'}}btn-silent{{/ifneq}}"
{{#if syncing}}disabled{{/if}}>Sync with Dropbox</button>
{{#ifneq storage 'file'}}<button class="settings__file-button-save-file btn-silent">Save to file</button>{{/ifneq}}
<button class="settings__file-button-export-xml btn-silent">Export to XML</button>
<button class="settings__file-button-close btn-silent">Close</button>
{{#if syncing}}disabled{{/if}}>{{#res 'setFileSyncWith'}}Dropbox{{/res}}</button>
{{#ifneq storage 'file'}}<button class="settings__file-button-save-file btn-silent">{{res 'setFileSaveFile'}}</button>{{/ifneq}}
<button class="settings__file-button-export-xml btn-silent">{{res 'setFileExportXml'}}</button>
<button class="settings__file-button-close btn-silent">{{res 'setFileClose'}}</button>
</div>
{{#if storage}}
<h2>Sync</h2>
<div>Last sync: {{#if syncDate}}{{syncDate}}{{else}}unknown{{/if}} {{#if syncing}}(sync in progress...){{/if}}</div>
{{#if syncError}}<div>Sync error: {{syncError}}</div>{{/if}}
<h2>{{res 'setFileSync'}}</h2>
<div>{{res 'setFileLastSync'}}: {{#if syncDate}}{{syncDate}}{{else}}{{res 'setFileLastSyncUnknown'}}{{/if}} {{#if syncing}}({{res 'setFileSyncInProgress'}}...){{/if}}</div>
{{#if syncError}}<div>{{res 'setFileSyncError'}}: {{syncError}}</div>{{/if}}
{{/if}}
<h2>Settings</h2>
<label for="settings__file-master-pass" class="settings__file-master-pass-label input-base">Master password:
<h2>{{res 'setFileSettings'}}</h2>
<label for="settings__file-master-pass" class="settings__file-master-pass-label input-base">{{res 'setFilePass'}}:
<span class="settings__file-master-pass-warning">
<i class="fa fa-warning"></i> password was changed; leave the field blank to use old password
<i class="fa fa-warning"></i> {{res 'setFilePassChanged'}}
</span>
</label>
<input type="password" class="settings__input input-base" id="settings__file-master-pass" value="{{password}}" />
<label for="settings__file-key-file">Key file:</label>
<label for="settings__file-key-file">{{res 'setFileKeyFile'}}:</label>
<select class="settings__select settings__select-no-margin input-base" id="settings__file-key-file"></select>
<a id="settings__file-file-select-link">Select a key file</a>
<a id="settings__file-file-select-link">{{res 'setFileSelKeyFile'}}</a>
<input type="file" id="settings__file-file-select" class="hide-by-pos" />
<h2>Names</h2>
<label for="settings__file-name">Name:</label>
<h2>{{res 'setFileNames'}}</h2>
<label for="settings__file-name">{{res 'setFileName'}}:</label>
<input type="text" class="settings__input input-base" id="settings__file-name" value="{{name}}" required />
<label for="settings__file-def-user">Default username:</label>
<label for="settings__file-def-user">{{res 'setFileDefUser'}}:</label>
<input type="text" class="settings__input input-base" id="settings__file-def-user" value="{{defaultUser}}" />
<h2>History</h2>
<h2>{{res 'setFileHistory'}}</h2>
<div>
<input type="checkbox" class="settings__input input-base" id="settings__file-trash" {{#if recycleBinEnabled}}checked{{/if}}> />
<label for="settings__file-trash">Enable trash</label>
<input type="checkbox" class="settings__input input-base" id="settings__file-trash" {{#if recycleBinEnabled}}checked{{/if}} />
<label for="settings__file-trash">{{res 'setFileEnableTrash'}}</label>
</div>
<label for="settings__file-hist-len">History length, keep last records per entry:</label>
<label for="settings__file-hist-len">{{res 'setFileHistLen'}}:</label>
<input type="text" pattern="\d+" required class="settings__input input-base" id="settings__file-hist-len" value="{{historyMaxItems}}" />
<label for="settings__file-hist-size">History size, total MB per file:</label>
<label for="settings__file-hist-size">{{res 'resFileHistSize'}}:</label>
<input type="text" pattern="\d+" required class="settings__input input-base" id="settings__file-hist-size" value="{{historyMaxSize}}" />
<h2>Advanced</h2>
<label for="settings__file-key-rounds">Key encryption rounds:</label>
<h2>{{res 'setFileAdvanced'}}</h2>
<label for="settings__file-key-rounds">{{res 'setFileRounds'}}:</label>
<input type="text" pattern="\d+" required class="settings__input input-base" id="settings__file-key-rounds" value="{{keyEncryptionRounds}}" />
</div>

View File

@ -1,45 +1,44 @@
<div>
<h1><i class="fa fa-cog"></i> General Settings</h1>
<h1><i class="fa fa-cog"></i> {{res 'setGenTitle'}}</h1>
{{#if updateWaitingReload}}
<h2 class="action-color">Update</h2>
<div>New app version was released and downloaded. <a href="{{releaseNotesLink}}" target="_blank">View release notes</a></div>
<h2 class="action-color">{{res 'setGenUpdate'}}</h2>
<div>{{res 'setGenNewVersion'}}. <a href="{{releaseNotesLink}}" target="_blank">{{res 'setGenReleaseNotes'}}</a></div>
<div class="settings__general-update-buttons">
<button class="settings__general-restart-btn">Reload to update</button>
<button class="settings__general-restart-btn">{{res 'setGenReloadToUpdate'}}</button>
</div>
{{else if updateManual}}
<h2 class="action-color">Update</h2>
<div>New version has been released. It will check for updates and install them automatically
but auto-upgrading from your version is impossible.</div>
<h2 class="action-color">{{res 'setGenUpdate'}}</h2>
<div>{{res 'setGenUpdateManual'}}</div>
<div class="settings__general-update-buttons">
<button class="settings__general-download-update-btn">Download update</button>
<button class="settings__general-download-update-btn">{{res 'setGenDownloadUpdate'}}</button>
</div>
{{/if}}
{{#if showUpdateBlock}}
<h2>Update</h2>
<h2>{{res 'setGenUpdate'}}</h2>
<div>
<select class="settings__general-auto-update settings__select input-base">
<option value="install" {{#ifeq autoUpdate 'install'}}selected{{/ifeq}}>Download and install automatically</option>
<option value="check" {{#ifeq autoUpdate 'check'}}selected{{/ifeq}}>Check but don't install</option>
<option value="" {{#unless autoUpdate}}selected{{/unless}}>Never check for updates</option>
<option value="install" {{#ifeq autoUpdate 'install'}}selected{{/ifeq}}>{{res 'setGenUpdateAuto'}}</option>
<option value="check" {{#ifeq autoUpdate 'check'}}selected{{/ifeq}}>{{res 'setGenUpdateCheck'}}</option>
<option value="" {{#unless autoUpdate}}selected{{/unless}}>{{res 'setGenNoUpdate'}}</option>
</select>
<div>{{updateInfo}}</div>
<a href="{{releaseNotesLink}}" target="_blank">View release notes</a>
<a href="{{releaseNotesLink}}" target="_blank">{{res 'setGenReleaseNotes'}}</a>
</div>
<div class="settings__general-update-buttons">
{{#if updateInProgress}}
<button class="settings__general-update-btn btn-silent" disabled>Checking for updates</button>
<button class="settings__general-update-btn btn-silent" disabled>{{res 'setGenUpdateChecking'}}</button>
{{else}}
<button class="settings__general-update-btn btn-silent">Check for updates</button>
<button class="settings__general-update-btn btn-silent">{{res 'setGenCheckUpdate'}}</button>
{{/if}}
{{#if updateReady}}<button class="settings__general-restart-btn">Restart to update</button>{{/if}}
{{#if updateFound}}<button class="settings__general-update-found-btn">Download update and restart</button>{{/if}}
{{#if updateReady}}<button class="settings__general-restart-btn">{{res 'setGenRestartToUpdate'}}</button>{{/if}}
{{#if updateFound}}<button class="settings__general-update-found-btn">{{res 'setGenDownloadAndRestart'}}</button>{{/if}}
</div>
{{/if}}
<h2>Appearance</h2>
<h2>{{res 'setGenAppearance'}}</h2>
<div>
<label for="settings__general-theme">Theme:</label>
<label for="settings__general-theme">{{res 'setGenTheme'}}:</label>
<select class="settings__general-theme settings__select input-base" id="settings__general-theme">
{{#each themes as |name key|}}
<option value="{{key}}" {{#ifeq key activeTheme}}selected{{/ifeq}}>{{name}}</option>
@ -48,44 +47,44 @@
</div>
<div>
<input type="checkbox" class="settings__input input-base settings__general-expand" id="settings__general-expand" {{#if expandGroups}}checked{{/if}} />
<label for="settings__general-expand">Show entries from all subgroups</label>
<label for="settings__general-expand">{{res 'setGenShowSubgroups'}}</label>
</div>
{{#if canSetTableView}}
<div>
<input type="checkbox" class="settings__input input-base settings__general-table-view" id="settings__general-table-view" {{#if tableView}}checked{{/if}} />
<label for="settings__general-table-view">Entries list table view</label>
<label for="settings__general-table-view">{{res 'setGenTableView'}}</label>
</div>
{{/if}}
<div>
<input type="checkbox" class="settings__input input-base settings__general-colorful-icons" id="settings__general-colorful-icons" {{#if colorfulIcons}}checked{{/if}} />
<label for="settings__general-colorful-icons">Colorful custom icons in list</label>
<label for="settings__general-colorful-icons">{{res 'setGenColorfulIcons'}}</label>
</div>
<h2>Function</h2>
<div>
<input type="checkbox" class="settings__input input-base settings__general-auto-save" id="settings__general-auto-save"
{{#if autoSave}}checked{{/if}} />
<label for="settings__general-auto-save">Automatically save and sync</label>
<label for="settings__general-auto-save">{{res 'setGenAutoSync'}}</label>
</div>
<div>
<label for="settings__general-idle-minutes">Auto-lock if the app is inactive:</label>
<label for="settings__general-idle-minutes">{{res 'setGenLockInactive'}}:</label>
<select class="settings__general-idle-minutes settings__select input-base" id="settings__general-idle-minutes">
<option value="0" {{#cmp idleMinutes 0 '<='}}selected{{/cmp}}>Don't auto-lock</option>
<option value="5" {{#ifeq idleMinutes 5}}selected{{/ifeq}}>In 5 minutes</option>
<option value="10" {{#ifeq idleMinutes 10}}selected{{/ifeq}}>In 10 minutes</option>
<option value="15" {{#ifeq idleMinutes 15}}selected{{/ifeq}}>In 15 minutes</option>
<option value="60" {{#ifeq idleMinutes 60}}selected{{/ifeq}}>In an hour</option>
<option value="0" {{#cmp idleMinutes 0 '<='}}selected{{/cmp}}>{{res 'setGenNoAutoLock'}}</option>
<option value="5" {{#ifeq idleMinutes 5}}selected{{/ifeq}}>{{#res 'setGenLockMinutes'}}5{{/res}}</option>
<option value="10" {{#ifeq idleMinutes 10}}selected{{/ifeq}}>{{#res 'setGenLockMinutes'}}10{{/res}}</option>
<option value="15" {{#ifeq idleMinutes 15}}selected{{/ifeq}}>{{#res 'setGenLockMinutes'}}15{{/res}}</option>
<option value="60" {{#ifeq idleMinutes 60}}selected{{/ifeq}}>{{res 'setGenLockHour'}}</option>
</select>
</div>
{{#if canClearClipboard}}
<div>
<label for="settings__general-clipboard">Clear clipboard after copy:</label>
<label for="settings__general-clipboard">{{res 'setGenClearClip'}}:</label>
<select class="settings__general-clipboard settings__select input-base" id="settings__general-clipboard">
<option value="0" {{#unless clipboardSeconds}}selected{{/unless}}>Don't clear</option>
<option value="5" {{#ifeq clipboardSeconds 5}}selected{{/ifeq}}>In 5 seconds</option>
<option value="10" {{#ifeq clipboardSeconds 10}}selected{{/ifeq}}>In 10 seconds</option>
<option value="15" {{#ifeq clipboardSeconds 15}}selected{{/ifeq}}>In 15 seconds</option>
<option value="60" {{#ifeq clipboardSeconds 60}}selected{{/ifeq}}>In a minute</option>
<option value="0" {{#unless clipboardSeconds}}selected{{/unless}}>{{res 'setGenNoClear'}}</option>
<option value="5" {{#ifeq clipboardSeconds 5}}selected{{/ifeq}}>{{#res 'setGenClearSeconds'}}5{{/res}}</option>
<option value="10" {{#ifeq clipboardSeconds 10}}selected{{/ifeq}}>{{#res 'setGenClearSeconds'}}10{{/res}}</option>
<option value="15" {{#ifeq clipboardSeconds 15}}selected{{/ifeq}}>{{#res 'setGenClearSeconds'}}15{{/res}}</option>
<option value="60" {{#ifeq clipboardSeconds 60}}selected{{/ifeq}}>{{res 'setGenClearMinute'}}</option>
</select>
</div>
{{/if}}
@ -93,17 +92,17 @@
<div>
<input type="checkbox" class="settings__input input-base settings__general-minimize" id="settings__general-minimize"
{{#if minimizeOnClose}}checked{{/if}} />
<label for="settings__general-minimize">Minimize app instead of close</label>
<label for="settings__general-minimize">{{res 'setGenMinInstead'}}</label>
</div>
<div>
<input type="checkbox" class="settings__input input-base settings__general-lock-on-minimize" id="settings__general-lock-on-minimize"
{{#if lockOnMinimize}}checked{{/if}} />
<label for="settings__general-lock-on-minimize">Auto-lock on minimize</label>
<label for="settings__general-lock-on-minimize">{{res 'setGenLockMinimize'}}</label>
</div>
{{/if}}
{{#if devTools}}
<h2>Advanced</h2>
<a class="settings__general-dev-tools-link">Show dev tools</a>
<h2>{{res 'setGenAdvanced'}}</h2>
<a class="settings__general-dev-tools-link">{{res 'setGenDevTools'}}</a>
{{/if}}
</div>

View File

@ -1,23 +1,20 @@
<div>
<h1><i class="fa fa-question"></i> Help</h1>
<h2>File Format</h2>
<p>This is a port of <a href="http://keepass.info/" target="_blank">KeePass</a> 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.
<h1><i class="fa fa-question"></i> {{res 'setHelpTitle'}}</h1>
<h2>{{res 'setHelpFormat'}}</h2>
<p>{{#res 'setHelpFormatBody'}}<a href="http://keepass.info/" target="_blank">KeePass</a>{{/res}}</p>
<h2>{{res 'setHelpProblems'}}</h2>
<p>{{#res 'setHelpProblems1'}}<a href="{{issueLink}}" target="_blank">{{res 'setHelpOpenIssue'}}</a>{{/res}}
{{#res 'setHelpProblems2'}}<a href="http://antelle.net/" target="_blank">{{res 'setHelpContactLink'}}</a>{{/res}}.
</p>
<h2>Problems?</h2>
<p>If something goes wrong, please, <a href="{{issueLink}}" target="_blank">open an issue on GitHub</a>
or <a href="http://antelle.net/" target="_blank">contact a developer</a> directly.
</p>
<p>App information:</p>
<p>{{res 'setHelpAppInfo'}}:</p>
<pre class="settings__pre input-base">{{appInfo}}</pre>
<h2>Other platforms</h2>
<h2>{{res 'setHelpOtherPlatforms'}}</h2>
<ul>
<li>
<i class="fa fa-windows"></i>
<i class="fa fa-apple"></i>
<i class="fa fa-linux"></i>
<a href="{{desktopLink}}" target="_blank">Desktop apps</a>
<a href="{{desktopLink}}" target="_blank">{{res 'setHelpDesktopApps'}}</a>
</li>
<li>
<i class="fa fa-chrome"></i>
@ -25,9 +22,9 @@
<i class="fa fa-opera"></i>
<i class="fa fa-compass"></i>
<i class="fa fa-internet-explorer"></i>
<a href="{{webAppLink}}" target="_blank">Web app</a>
<a href="{{webAppLink}}" target="_blank">{{res 'setHelpWebApp'}}</a>
</li>
</ul>
<h2>Updates <i class="fa fa-twitter"></i></h2>
<p>App twitter: <a href="https://twitter.com/kee_web" target="_blank">kee_web</a></p>
<h2>{{res 'setHelpUpdates'}} <i class="fa fa-twitter"></i></h2>
<p>{{res 'setHelpTwitter'}}: <a href="https://twitter.com/kee_web" target="_blank">kee_web</a></p>
</div>

View File

@ -1,16 +1,16 @@
<div>
<h1><i class="fa fa-keyboard-o"></i> Shortcuts</h1>
<div><span class="shortcut">{{{cmd}}}A</span> or <span class="shortcut">{{{alt}}}A</span> show all items</div>
<div><span class="shortcut">{{{alt}}}C</span> show items with colors</div>
<div><span class="shortcut">{{{alt}}}D</span> go to trash</div>
<div><span class="shortcut">{{{cmd}}}F</span> search, or just start typing</div>
<div><span class="shortcut">esc</span> clear search</div>
<div><span class="shortcut">⏎</span> go to entry</div>
<div><span class="shortcut">{{{cmd}}}C</span> copy password or selected field</div>
<div><span class="shortcut">&uarr;</span> go to previous item</div>
<div><span class="shortcut">&darr;</span> go to next item</div>
<div><span class="shortcut">{{{alt}}}N</span> create entry</div>
<div><span class="shortcut">{{{cmd}}}O</span> open / new</div>
<div><span class="shortcut">{{{cmd}}}S</span> save</div>
<div><span class="shortcut">{{{cmd}}}G</span> generate</div>
<h1><i class="fa fa-keyboard-o"></i> {{res 'setShTitle'}}</h1>
<div><span class="shortcut">{{{cmd}}}A</span> {{res 'or'}} <span class="shortcut">{{{alt}}}A</span> {{res 'setShShowAll'}}</div>
<div><span class="shortcut">{{{alt}}}C</span> {{res 'setShColors'}}</div>
<div><span class="shortcut">{{{alt}}}D</span> {{res 'setShTrash'}}</div>
<div><span class="shortcut">{{{cmd}}}F</span> {{res 'setShFind'}}</div>
<div><span class="shortcut">esc</span> {{res 'setShClearSearch'}}</div>
<div><span class="shortcut">⏎</span> {{res 'setShEntry'}}</div>
<div><span class="shortcut">{{{cmd}}}C</span> {{res 'setShCopy'}}</div>
<div><span class="shortcut">&uarr;</span> {{res 'setShPrev'}}</div>
<div><span class="shortcut">&darr;</span> {{res 'setShNext'}}</div>
<div><span class="shortcut">{{{alt}}}N</span> {{res 'setShCreateEntry'}}</div>
<div><span class="shortcut">{{{cmd}}}O</span> {{res 'setShOpen'}}</div>
<div><span class="shortcut">{{{cmd}}}S</span> {{res 'setShSave'}}</div>
<div><span class="shortcut">{{{cmd}}}G</span> {{res 'setShGen'}}</div>
</div>

View File

@ -1,6 +1,6 @@
<div class="settings">
<div class="settings__back-button">
<i class="fa fa-chevron-left settings__back-button-pre"></i> return to app <i class="fa fa-external-link-square settings__back-button-post"></i>
<i class="fa fa-chevron-left settings__back-button-pre"></i> {{res 'retToApp'}} <i class="fa fa-external-link-square settings__back-button-post"></i>
</div>
<div class="scroller">
</div>