String globalization

pull/91/head
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>