Implement i18n. Add translations. Fixes #36

This commit is contained in:
brantje 2017-04-25 18:10:06 +02:00
Родитель b9f87e6aad
Коммит dfdbd1b417
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 5FF1D117F918687F
16 изменённых файлов: 601 добавлений и 133 удалений

354
_locales/en/messages.json Normal file
Просмотреть файл

@ -0,0 +1,354 @@
{
"extName": {
"message": "Passman",
"description": "Extension name"
},
"extDescription": {
"message": "Passman - The password manager for NextCloud / ownCloud",
"description": "Extension description"
},
"search_for": {
"message": "Search for",
"description": "No credentials found for the current site"
},
"no_credentials_found_for_site": {
"message": "No credentials found for this site!",
"description": "No credentials found for the current site"
},
"no_credentials_found": {
"message": "No credentials found",
"description": "No credentials found for the current site"
},
"invalid_response_from_server": {
"message": "Invalid response from server: [$STATUS_CODE] $STATUS_TEXT$",
"description": "No credentials found for the current site",
"placeholders": {
"statux_code": {
"content": "$1",
"example": "404"
},
"status_text": {
"content": "$2",
"example": "Page not found"
}
}
},
"please_enter_nextcloud_credentials": {
"message": "Please enter your Nextcloud / ownCloud credentials",
"description": "No credentials found for the current site"
},
"save_site": {
"message": "Save site",
"description": "Save site"
},
"password_generator": {
"message": "Password generator",
"description": "Password generator"
},
"generate_password": {
"message": "Generate password",
"description": "Generate password"
},
"toggle_visibility": {
"message": "Toggle visibility",
"description": "Toggle visibility"
},
"toggle_advanced": {
"message": "Toggle advanced options",
"description": "Toggle advanced options"
},
"use_generated_password": {
"message": "Use password",
"description": "Use password"
},
"add_hint": {
"message": "After creating you can edit the credential in passman",
"description": "After creating you can edit the credential in passman"
},
"pw_length": {
"message": "Password length",
"description": "Password length"
},
"min_digits": {
"message": "Minimum amount of digits",
"description": "Minimum amount of digits"
},
"avoid_ambiguous": {
"message": "Avoid ambiguous characters",
"description": "Avoid ambiguous characters"
},
"require_every_character_type": {
"message": "Require every character type",
"description": "Require every character type"
},
"add_account": {
"message": "Add account",
"description": "Add account"
},
"accounts": {
"message": "Accounts",
"description": "Accounts"
},
"label": {
"message": "Label",
"description": "Label"
},
"username": {
"message": "Username",
"description": "Username"
},
"password": {
"message": "Password",
"description": "Password"
},
"detected_new_login": {
"message": "Detected new login",
"description": "Detected new login"
},
"detected_changed_login": {
"message": "Detected changed password for user",
"description": "Detected changed password for user"
},
"browser_action_title_login": {
"message": "Passman - $CREDENTIAL_AMOUNT$ $PLURAL$ found for this page",
"description": "String for the browser action",
"placeholders": {
"credential_amount": {
"content": "$1",
"example": "4"
},
"plural": {
"content": "$2",
"example": "credentials"
}
}
},
"browser_action_title_locked": {
"message": "Passman - Locked",
"description": "Browser extension title when the extension is locked"
},
"detected_changed_url": {
"message": "Detected changed url for",
"description": "Detected changed url for"
},
"credential": {
"message": "credential",
"description": "credential"
},
"credentials": {
"message": "credentials",
"description": "credentials"
},
"refresh_timer": {
"message": "Refresh timer [s]",
"description": "Password"
},
"ignore_protocol": {
"message": "Ignore protocol for site matching",
"description": "Ignore protocol for site matching"
},
"ignore_subdomain": {
"message": "Ignore subdomain for site matching",
"description": "Password"
},
"ignore_port": {
"message": "Ignore port for site matching",
"description": "Ignore port for site matching"
},
"disable_autofill": {
"message": "Disable auto fill of forms",
"description": "Disable auto fill of forms"
},
"disable_password_picker": {
"message": "Disable password picker",
"description": "Disable password picker"
},
"enable_debug": {
"message": "Enable debug mode",
"description": "Enable debug mode"
},
"save": {
"message": "Save",
"description": "Save"
},
"saving": {
"message": "Saving",
"description": "Saving"
},
"credential_saved": {
"message": "Credential saved",
"description": "saved, lowercase"
},
"credential_updated": {
"message": "Credential updated",
"description": "updated, lowercase"
},
"site_ignored": {
"message": "Site ignored",
"description": "Site ignored"
},
"update": {
"message": "Update",
"description": "Update"
},
"ignore_site": {
"message": "Ignore site",
"description": "Update"
},
"btn_save_site": {
"message": "Save credentials for this site",
"description": "Save credentials for this site"
},
"btn_search": {
"message": "Search credentials",
"description": "Search credentials"
},
"welcome_to_passman": {
"message": "Welcome to the Passman extension",
"description": "Intro at the setup"
},
"server_url": {
"message": "Server url",
"description": "Server url"
},
"begin": {
"message": "Let's begin!",
"description": "Let's begin!"
},
"intro_text": {
"message": "This wizard will guide you trough the steps to set up the extension and keep all your passwords synchronized!",
"description": "Cancel"
},
"cancel": {
"message": "Cancel",
"description": "Cancel"
},
"default_vault": {
"message": "Default vault",
"description": "Default vault"
},
"default_vault_desc": {
"message": "Select the vault that you want to keep in sync with the browser.",
"description": "Select the vault that you want to keep in sync with the browser."
},
"enter_master_password": {
"message": "Enter master password",
"description": "Enter master password"
},
"enter_master_password_desc": {
"message": "This password will be used to encrypt all your settings in the extension. You can choose to save this password locally, but it will be saved in PLAIN TEXT.",
"description": "Enter master password"
},
"remember_master_password": {
"message": "Remember master password",
"description": "Remember master password"
},
"done": {
"message": "Done",
"description": "Done"
},
"finish": {
"message": "Finish",
"description": "Finish"
},
"done_donate": {
"message": "If you like, don't forget to donate",
"description": "If you like, don't forget to donate"
},
"close": {
"message": "Close",
"description": "Close"
},
"prev": {
"message": "Prev",
"description": "Previous"
},
"next": {
"message": "Next",
"description": "Next"
},
"nextcloud_settings": {
"message": "Nextcloud / ownCloud server settings",
"description": "Nextcloud / ownCloud server settings"
},
"select_default_vault": {
"message": "Select default vault",
"description": "Select default vault"
},
"ignored_sites": {
"message": "Ignored sites",
"description": "Ignored sites"
},
"no_sites_ignored": {
"message": "No sites ignored",
"description": "No sites ignored"
},
"vault_password": {
"message": "Vault password",
"description": "Vault password"
},
"invalid_vault_password": {
"message": "Invalid vault key!",
"description": "Vault password"
},
"empty_master_key": {
"message": "Empty master key!",
"description": "Empty master key!"
},
"one_time_password": {
"message": "OTP",
"description": "One time password"
},
"settings": {
"message": "settings",
"description": "Settings"
},
"search": {
"message": "Search",
"description": "Search"
},
"refresh_credential_list": {
"message": "Refresh credential list",
"description": "Refresh the credential list"
},
"donate_button_title": {
"message": "Developent depends on donations, please support us",
"description": "Title on the paypal button"
},
"lock_extension": {
"message": "Lock extension",
"description": "Title on the paypal button"
},
"extension_locked": {
"message": "Passman extension is locked, unlock to use it",
"description": "When the extension is locked, and requires an unlock to use it"
},
"invalid_master_password": {
"message": "Invalid master password!",
"description": "When the master password is invalid"
},
"unlock": {
"message": "Unlock",
"description": "Unlock"
},
"credentials_found_for_site": {
"message": "We've found $AMOUNT$ credentials for this site",
"description": "Amount of the credentials found for the current site",
"placeholders": {
"amount": {
"content": "$1",
"example": "2"
}
}
},
"credentials_in_db": {
"message": "$AMOUNT$ credentials in the database",
"description": "Amount of the credentials in the database",
"placeholders": {
"amount": {
"content": "$1",
"example": "2"
}
}
}
}

Просмотреть файл

@ -14,6 +14,7 @@
<script src="/js/lib/API/tabs.js"></script>
<script src="/js/lib/API/cookies.js"></script>
<script src="/js/lib/API/extension.js"></script>
<script src="/js/lib/API/i18n.js"></script>
<script src="/js/lib/otp.js"></script>
<script src="/js/lib/font-awesome.js"></script>
<script src="/js/vendor/sjcl/sjcl.js"></script>
@ -39,6 +40,7 @@
<script src="/js/ui/popup/controllers/setup.js"></script>
<script src="/js/ui/popup/directives/otp.js"></script>
<script src="/js/ui/popup/directives/ngenter.js"></script>
<script src="/js/ui/popup/filters/translate.js"></script>
<link href="/css/vendor/bootstrap.css" media="all" rel="stylesheet" />

Просмотреть файл

@ -1,32 +1,32 @@
<div class="col-xs-12 nopadding pwcontainer">
<div ng-show="found_credentials.length === 0">
No credentials found for this site!
{{ 'no_credentials_found_for_site' | translate}}
</div>
<div ng-show="found_credentials.length > 0">We've found {{found_credentials.length}} credentials for this site</div>
<div ng-show="found_credentials.length > 0">{{'credentials_found_for_site' | translate:found_credentials.length.toString() }}</div>
<div class="credential" ng-repeat="credential in found_credentials">
<div class="col-xs-7 nopadding">{{credential.label}}<br/>
<small>{{credential.username}}</small>
</div>
<div class="col-xs-5 OTP" ng-if="credential.otp.secret">
OTP: <div otp-generator secret="credential.otp.secret"></div>
{{'one_time_password' | translate}}: <div otp-generator secret="credential.otp.secret"></div>
</div>
</div>
</div>
<div class="footer">
<div style="position: absolute; left: 50%; margin-top: -7px; width: 110px;">
<div style="position: relative; left: -50%; margin-top: 1.3em;">
<small>{{credential_amount}} {{ credential_amount === 1 ? 'credential' : 'credentials'}} in the database</small>
<small>{{'credentials_in_db' | translate:credential_amount}}</small>
</div>
</div>
<div class="bottomBtn" ng-click="goto_settings()" title="Settings"><i class="fa fa-gears"></i></div>
<div class="bottomBtn" ng-click="goto_search()" title="Search"><i class="fa fa-search"></i></div>
<div class="bottomBtn" ng-click="refresh()" title="Refresh credential list"><i class="fa fa-refresh" ng-class="{'fa-spin': refreshing_credentials}"></i></div>
<div class="bottomBtn" ng-click="goto_settings()" title="{{'settings' | translate}}"><i class="fa fa-gears"></i></div>
<div class="bottomBtn" ng-click="goto_search()" title="{{'search' | translate}}"><i class="fa fa-search"></i></div>
<div class="bottomBtn" ng-click="refresh()" title="{{'refresh_credential_list' | translate}}"><i class="fa fa-refresh" ng-class="{'fa-spin': refreshing_credentials}"></i></div>
<div class="bottomBtn pull-right"> <a href="https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=6YS8F97PETVU2" target="_blank"
title="Developent depends on donations, please support us"
title="{{'donate_button_title' | translate}}"
rel="nofollow noopener noreferrer"
><i class="fa fa-paypal"></i> </a>
</div>
<div class="bottomBtn pull-right" ng-click="lockExtension()" title="Lock extension"><i class="fa fa-lock"></i></div>
<div class="bottomBtn pull-right" ng-click="lockExtension()" title="{{'lock_extension' | translate}}"><i class="fa fa-lock"></i></div>
<!-- <a class="btn btn-success pull-right"
>Donate</a> -->
</div>

Просмотреть файл

@ -1,15 +1,15 @@
<div class="content unlock" style="max-height: 160px; overflow: auto">
<div>
<i class="fa fa-lock fa-5x"></i>
<div>Passman extension is locked, unlock to use it:</div>
<div>{{'extension_locked'| translate}}:</div>
<input class="form-control" type="password" ng-enter="apply_settings()" ng-model="master_password" />
<label><input type="checkbox" ng-model="master_password_remember">Remember master password</label>
</div>
<div ng-show="inValidPassword" class="error">
Invalid master password!
{{'invalid_master_password' | translate}}
</div>
<button class="btn btn-default stepNext" ng-click="apply_settings()" ng-disabled="saving"><i ng-show="saving" ng-class="{'fa-spinner fa-spin': saving}" class="fa"></i><i ng-show="!saving" class="fa fa-unlock"></i> Unlock</button>
<button class="btn btn-default stepNext" ng-click="apply_settings()" ng-disabled="saving"><i ng-show="saving" ng-class="{'fa-spinner fa-spin': saving}" class="fa"></i><i ng-show="!saving" class="fa fa-unlock"></i> {{'unlock' | translate}}</button>
</div>

Просмотреть файл

@ -1,9 +1,9 @@
<div class="col-xs-12 nopadding pwcontainer">
<div class="searchContainer">
<input type="text" ng-model="searchText" placeholder="Search for..." class="form-control" ng-enter="search()"/>
<input type="text" ng-model="searchText" placeholder="{{'search_for' | translate }}..." class="form-control" ng-enter="search()"/>
</div>
<div ng-show="found_credentials.length === 0">
No credentials found!
{{'no_credentials_found' | translate}}
</div>
@ -13,7 +13,7 @@
<small>{{credential.email}}</small>
</div>
<div class="col-xs-5 OTP" ng-if="credential.otp.secret">
OTP: <div otp-generator secret="credential.otp.secret"></div>
{{'one_time_password' | translate}}: <div otp-generator secret="credential.otp.secret"></div>
</div>
</div>
</div>
@ -21,18 +21,18 @@
<div class="footer">
<div style="position: absolute; left: 50%; margin-top: -7px; width: 110px;">
<div style="position: relative; left: -50%; margin-top: 1.3em;">
<small>{{credential_amount}} {{ credential_amount === 1 ? 'credential' : 'credentials'}} in the database</small>
<small>{{'credentials_in_db' | translate:credential_amount}}</small>
</div>
</div>
<div class="bottomBtn" ng-click="goto_settings()" title="Settings"><i class="fa fa-gears"></i></div>
<div class="bottomBtn" ng-click="goto_search()" title="Refresh credential list"><i class="fa fa-search"></i></div>
<div class="bottomBtn" ng-click="refresh()" title="Refresh credential list"><i class="fa fa-refresh" ng-class="{'fa-spin': refreshing_credentials}"></i></div>
<div class="bottomBtn" ng-click="goto_settings()" title="{{'settings' | translate}}"><i class="fa fa-gears"></i></div>
<div class="bottomBtn" ng-click="goto_search()" title="{{'search' | translate}}"><i class="fa fa-search"></i></div>
<div class="bottomBtn" ng-click="refresh()" title="{{'refresh_credential_list' | translate}}"><i class="fa fa-refresh" ng-class="{'fa-spin': refreshing_credentials}"></i></div>
<div class="bottomBtn pull-right"> <a href="https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=6YS8F97PETVU2" target="_blank"
title="Developent depends on donations, please support us"
title="{{'donate_button_title' | translate}}"
rel="nofollow noopener noreferrer"
><i class="fa fa-paypal"></i> </a>
</div>
<div class="bottomBtn pull-right" ng-click="lockExtension()" title="Lock extension"><i class="fa fa-lock"></i></div>
<div class="bottomBtn pull-right" ng-click="lockExtension()" title="{{'lock_extension' | translate}}"><i class="fa fa-lock"></i></div>
<!-- <a class="btn btn-success pull-right"
>Donate</a> -->
</div>

Просмотреть файл

@ -1,12 +1,12 @@
<h4 align="center">Please enter your nextCloud credentials:</h4>
<h4 align="center">{{'please_enter_nextcloud_credentials' | translate}}:</h4>
<div class="alerts alert alert-warning" ng-if="errors.length > 0">
<li ng-repeat="error in errors">{{error}}</li>
</div>
<div style="max-height: 350px; overflow-y: auto;">
<table class="table">
<tr>
<td><small>Host:</small></td>
<td><small>{{'server_url' | translate}}:</small></td>
<td><input type="url" class="form-control input-sm" id="host" ng-model="settings.nextcloud_host" required ng-debounce="1000">
</td>
</tr>
@ -14,20 +14,20 @@
<td colspan=2><div class="alert alert-warning">Warning: This connection is insecure!</div></td>
</tr>
<tr>
<td><small>Username:</small></td>
<td><small>{{'username' | translate}}:</small></td>
<td><input type="text" class="form-control input-sm" id="user" ng-model="settings.nextcloud_username" required ng-debounce="1000"></td>
</tr>
<tr>
<td><small>Password:</small></td>
<td><small>{{'password' | translate}}:</small></td>
<td><input type="password" class="form-control input-sm" id="password" ng-model="settings.nextcloud_password" required ng-debounce="1000"></td>
</tr>
<tr class="login-req" ng-if="vaults">
<td><small>Select default vault:</small></td>
<td><small>{{'select_default_vault' | translate}}:</small></td>
<td><select id="defaultVault" class="form-control input-sm" ng-model="settings.default_vault" required ng-options="vault.name for vault in vaults track by vault.guid"></select></td>
</tr>
<tr class="login-req" id="vaultPassRow" ng-show="settings.default_vault != ''">
<td valign="top">
<small>Vault password</small>
<small>{{'vault_password' | translate}}</small>
</td>
<td>
<input type="password" class="form-control input-sm" id="vaultPassword" ng-model="settings.vault_password">
@ -35,43 +35,43 @@
</tr>
<tr class="login-req">
<td valign="top">
<small>Ignored sites</small>
<small>{{'ignored_sites' | translate}}</small>
</td>
<td>
<ul class="ignored_sites">
<li ng-repeat="site in settings.ignored_sites">{{site}} <i class="fa fa-trash pull-right" ng-click="removeSite(site)"></i></li>
<li ng-show="settings.ignored_sites.length === 0">No sites ignored</li>
<li ng-show="settings.ignored_sites.length === 0">{{'no_sites_ignored' | translate}}</li>
<li><input type="text" class="form-control" placeholder="Enter a domain or tld" ng-model="ignoreSite" ng-enter="addSite(ignoreSite)"> </li>
</ul>
</td>
</tr>
<tr>
<td><small>Refresh timer [s]:</small></td>
<td><small>{{'refresh_timer' | translate}}:</small></td>
<td><input type="text" id="timer" class="form-control input-sm" ng-model="settings.refreshTime"></td>
</tr>
<tr>
<td colspan=2><input type="checkbox" id="ignoreProtocol" ng-model="settings.ignoreProtocol"><small>Ignore protocol for site matching</small></td>
<td colspan=2><input type="checkbox" id="ignoreProtocol" ng-model="settings.ignoreProtocol"><small>{{'ignore_protocol' | translate}}</small></td>
</tr>
<tr>
<td colspan=2><input type="checkbox" id="ignoreSubdomain" ng-model="settings.ignoreSubdomain"><small>Ignore subdomain for site matching</small></td>
<td colspan=2><input type="checkbox" id="ignoreSubdomain" ng-model="settings.ignoreSubdomain"><small>{{'ignore_subdomain' | translate}}</small></td>
</tr>
<tr>
<td colspan=2><input type="checkbox" id="ignorePath" ng-model="settings.ignorePort"><small>Ignore port for site matching</small></td>
<td colspan=2><input type="checkbox" id="ignorePath" ng-model="settings.ignorePort"><small>{{'ignore_port' | translate}}</small></td>
</tr>
<tr>
<td colspan=2><input type="checkbox" id="disableAutoFill" ng-model="settings.disableAutoFill"><small>Disable auto fill of forms</small></td>
<td colspan=2><input type="checkbox" id="disableAutoFill" ng-model="settings.disableAutoFill"><small>{{'disable_autofill' | translate}}</small></td>
</tr>
<tr>
<td colspan=2><input type="checkbox" id="disablePasswordPicker" ng-model="settings.disablePasswordPicker"><small>Disable password picker</small></td>
<td colspan=2><input type="checkbox" id="disablePasswordPicker" ng-model="settings.disablePasswordPicker"><small>{{'disable_password_picker' | translate}}</small></td>
</tr>
<tr>
<td colspan=2><input type="checkbox" id="enableDebug" ng-model="settings.debug"><small>Enable debug mode</small></td>
<td colspan=2><input type="checkbox" id="enableDebug" ng-model="settings.debug"><small>{{'enable_debug' | translate}}</small></td>
</tr>
<tr>
<td colspan=2 align="center">
<button class="btn btn-success" ng-click="saveSettings()" ng-disabled="saving"><i ng-show="saving" ng-class="{'fa-spinner fa-spin': saving}" class="fa"></i> Save</button>
<button class="btn btn-warning" ng-click="cancel()">Cancel</button>
<button class="btn btn-success" ng-click="saveSettings()" ng-disabled="saving"><i ng-show="saving" ng-class="{'fa-spinner fa-spin': saving}" class="fa"></i> {{'save' | translate}}</button>
<button class="btn btn-warning" ng-click="cancel()">{{'cancel' | translate}}</button>
</td>
</tr>
</table>

Просмотреть файл

@ -3,51 +3,50 @@
</div>
<steps on-finish="finished()" class="setup">
<step class="step1" name="welcome">
<h2>Welcome to the Passman extension</h2>
This wizard will guide you trough the steps to set up the extension and keep all your passwords synchronized!
<button class="btn btn-default stepNext" ng-click="next()">Let's begin!</button>
<h2>{{'welcome_to_passman' | translate}}</h2>
{{'intro_text' | translate}}
<button class="btn btn-default stepNext" ng-click="next()">{{'begin' | translate}}</button>
</step>
<step class="step2" name="server">
<h2>Nextcloud server settings</h2>
<label>Server url</label>
<h2>{{'nextcloud_settings' | translate}}</h2>
<label>{{'server_url' | translate}}</label>
<input type="text" ng-model="settings.nextcloud_host"/>
<label>Username</label>
<label>{{'username' | translate}}</label>
<input type="text" ng-model="settings.nextcloud_username"/>
<label>Password</label>
<label>{{'password' | translate}}</label>
<input type="password" ng-model="settings.nextcloud_password" />
<button step-previous class="btn btn-default">Prev</button>
<button step-previous class="btn btn-default">{{'prev' | translate}}</button>
<button class="btn btn-default stepNext" ng-click="next()" ng-disabled="saving"><i ng-show="saving" ng-class="{'fa-spinner fa-spin': saving}" class="fa"></i> Next</button>
<button class="btn btn-default stepNext" ng-click="next()" ng-disabled="saving"><i ng-show="saving" ng-class="{'fa-spinner fa-spin': saving}" class="fa"></i> {{'next' | translate}}</button>
</step>
<step class="step3" name="vault">
<h2>Default vault</h2>
<label>Select the vault that you want to keep in sync with the browser.</label>
<h2>{{'default_vault' | translate}}</h2>
<label>{{'default_vault_desc' | translate}}</label>
<select id="defaultVault" class="form-control input-sm" ng-model="settings.default_vault" required ng-options="vault.name for vault in vaults track by vault.guid">
</select>
<label>Vault password</label>
<label>{{'vault_password' | translate}}</label>
<input type="password" ng-change="" ng-model="settings.vault_password" />
<button step-previous class="btn btn-default">Prev</button>
<button class="btn btn-default stepNext" ng-click="next()" ng-disabled="saving"><i ng-show="saving" ng-class="{'fa-spinner fa-spin': saving}" class="fa"></i> Next</button>
<button step-previous class="btn btn-default">{{'prev' | translate}}</button>
<button class="btn btn-default stepNext" ng-click="next()" ng-disabled="saving"><i ng-show="saving" ng-class="{'fa-spinner fa-spin': saving}" class="fa"></i> {{'next' | translate}}</button>
</step>
<step class="step4" name="master">
<h2>Enter master password</h2>
<div>This password will be used to encrypt all your settings in the extension. <br />
You can choose to save this password locally, but it will be saved in PLAIN TEXT.</div>
<h2>{{'enter_master_password' | translate}}</h2>
<div>{{'enter_master_password_desc' | translate}}</div>
<br />
<input type="password" ng-model="settings.master_password" />
<br />
<label><input type="checkbox" ng-model="settings.master_password_remember">Remember master password</label>
<label><input type="checkbox" ng-model="settings.master_password_remember">{{'remember_master_password' | translate}}</label>
<button step-previous class="btn btn-default">Prev</button>
<button class="btn btn-default stepNext" ng-click="next()" ng-disabled="saving"><i ng-show="saving" ng-class="{'fa-spinner fa-spin': saving}" class="fa"></i> Next</button>
<button step-previous class="btn btn-default">{{'prev' | translate}}</button>
<button class="btn btn-default stepNext" ng-click="next()" ng-disabled="saving"><i ng-show="saving" ng-class="{'fa-spinner fa-spin': saving}" class="fa"></i> {{'next' | translate}}</button>
</step>
<step class="step5" name="last">
<h1>Done!</h1>
<div>If you like, don't forget to donate</div>
<h1>{{'done' | translate}}!</h1>
<div>{{'done_donate' | translate}}</div>
<button step-finish class="btn btn-default" ng-disabled="saving"><i ng-show="saving" ng-class="{'fa-spinner fa-spin': saving}" class="fa"></i> Finish</button>
<button step-finish class="btn btn-default" ng-disabled="saving"><i ng-show="saving" ng-class="{'fa-spinner fa-spin': saving}" class="fa"></i> {{'finish' | translate}}</button>
</step>
</steps>

Просмотреть файл

@ -9,6 +9,7 @@
<script src="/js/lib/API/runtime.js"></script>
<script src="/js/lib/API/cookies.js"></script>
<script src="/js/lib/API/extension.js"></script>
<script src="/js/lib/API/i18n.js"></script>
<script src="/js/lib/otp.js"></script>
<script src="/js/lib/font-awesome.js"></script>
<script src="/js/vendor/sjcl/sjcl.js"></script>
@ -21,81 +22,81 @@
<body>
<div id="password_picker">
<div class="tabs">
<div class="tab add"><span class="fa fa-plus" aria-hidden="true" title="Add account"></span></div>
<div class="tab list active"><span class="fa fa-list" aria-hidden="true" title="Accounts"></span></div>
<div class="tab search"><span class="fa fa-search" aria-hidden="true" title="Accounts"></span></div>
<div class="tab generate"><span class="fa fa-refresh" aria-hidden="true" title="Password generator"></span>
<div class="tab add"><span class="fa fa-plus" aria-hidden="true" t="[add_account, title]"></span></div>
<div class="tab list active"><span class="fa fa-list" aria-hidden="true" t="[accounts, title]"></span></div>
<div class="tab search"><span class="fa fa-search" aria-hidden="true" t="[search, title]"></span></div>
<div class="tab generate"><span class="fa fa-refresh" aria-hidden="true" t="[password_generator, title]"></span>
</div>
<div class="tab close pull-right"><span class="fa fa-times" aria-hidden="true" title="Close"></span></div>
<div class="tab close pull-right"><span class="fa fa-times" aria-hidden="true" t="[close, title]" title="Close"></span></div>
</div>
<div class="tab-content">
<div class="tab-add-content" style="display: none">
<h2>Save site</h2>
<h2 t="save_site"></h2>
<div class="rrow">
<label>Label</label>
<label t="label"></label>
<input type="text" class="input" name="savepw-label" id="savepw-label"/>
</div>
<div class="rrow">
<label>Username</label>
<label t="username"></label>
<input type="text" class="input" name="savepw-username" id="savepw-username"/>
</div>
<div class="rrow">
<label>Password</label>
<label t="password"></label>
<input type="password" class="input" name="savepw-password" id="savepw-password"/>
<span class="niceInputButtons">
<div class="cell renewpw_newac" ng-click="generatePasswordStart()" title="Generate password">
<div class="cell renewpw_newac" ng-click="generatePasswordStart()" t="[generate_password, title]">
<i class="fa fa-refresh"></i>
</div>
<div class="cell togglePw" title="Toggle visibility">
<div class="cell togglePw" t="[toggle_visibility, placeholder]">
<i class="fa fa-eye-slash"></i>
</div>
</span>
</div>
<small>After creating you can edit the credential in passman</small>
<small t="add_hint"></small>
<hr/>
<button class="btn btn-success" id="savepw-save">Save</button>
<button class="btn" id="savepw-cancel">Cancel</button>
<button class="btn btn-success" id="savepw-save" t="save"></button>
<button class="btn" id="savepw-cancel" t="cancel"></button>
</div>
<div class="tab-list-content">
<div class="no-credentials">
<div class="btn btn-secondary save">Save credentials for this site</div>
<div class="btn btn-secondary save" t="btn_save_site"></div>
<div class="clearfix"></div>
<div class="btn btn-secondary search">Search credentials</div>
<div class="btn btn-secondary search" t="btn_search"></div>
<div class="clearfix"></div>
<div class="btn btn-secondary gen">Generate password</div>
<div class="btn btn-secondary gen" t="generate_password"></div>
</div>
</div>
<div class="tab-search-content" style="display: none">
<input type="text" class="input" id="password_search" placeholder="Search for...">
<input type="text" class="input" id="password_search" t="[search_for, placeholder]" >
<div id="searchResults">
</div>
</div>
<div class="tab-generate-content" style="display: none">
<h2>Generate password</h2>
<h2 t="generate_password"></h2>
<div class="pw-gen">
<div class="input-group">
<input type="password" id="generated_password"/>
<span class="niceInputButtons">
<div class="cell renewpw" title="Generate password">
<div class="cell renewpw" t="[generate_password, title]">
<i class="fa fa-refresh"></i>
</div>
<div class="cell togglePwVis" title="Toggle visibility">
<div class="cell togglePwVis" t="[toggle_visibility, placeholder]">
<i class="fa fa-eye-slash"></i>
</div>
</span>
</div>
<div class="btn btn-secondary usepwd">Use password</div>
<div class="btn btn-secondary usepwd" t="use_generated_password"></div>
</div>
<div class="password_settings">
<div>
<span href="#" class="adv_opt"><i class="fa fa-angle-right"></i> Toggle advanced options</span>
<span href="#" class="adv_opt"><i class="fa fa-angle-right"></i> <span t="toggle_advanced"></span></span>
</div>
<div class="pw-setting-advanced" style="display: none">
<form name="advancedSettings">
<label class="pw-len">
<span class="label">Password length</span><br/>
<span class="label" t="pw_length"></span><br/>
<input type="number" name="length" min="1"/>
</label>
@ -122,19 +123,19 @@
<span class="label sm">1$%@#</span>
</label>
<label class="pull-left digits clearfix">
<span class="label">Minimum amount of digits</span><br/>
<span class="label" t="min_digits"></span><br/>
<input type="number" name="minimumDigitCount" min="0"/>
</label>
<label class="pull-left clearfix">
<input type="checkbox" id="ambig"
name="avoidAmbiguousCharacters"/>
<span class="label sm">Avoid ambiguous characters</span>
<span class="label sm" t="avoid_ambiguous"></span>
</label>
<label class="pull-left clearfix">
<input type="checkbox" name="requireEveryCharType"
id="reqevery"/>
<span class="label sm">Require every character type</span>
<span class="label sm" t="require_every_character_type"></span>
</label>
</form>
</div>

Просмотреть файл

@ -3,8 +3,8 @@ var $j = jQuery.noConflict();
$j(document).ready(function () {
var _this = this;
Array.prototype.findUrl = function(match) {
return this.filter(function(item){
Array.prototype.findUrl = function (match) {
return this.filter(function (item) {
return typeof item === 'string' && item.indexOf(match) > -1;
});
};
@ -18,7 +18,7 @@ $j(document).ready(function () {
function enterLoginDetails(login) {
var username;
if(login.hasOwnProperty('username')){
if (login.hasOwnProperty('username')) {
username = (login.username.trim() !== '' ) ? login.username : login.email;
}
@ -44,7 +44,7 @@ $j(document).ready(function () {
var left = loginFieldPos.left;
var top = loginFieldPos.top;
var maxZ = Math.max.apply(null,
$j.map($j('body *'), function(e) {
$j.map($j('body *'), function (e) {
if ($j(e).css('position') !== 'static')
return parseInt($j(e).css('z-index')) || 1;
}));
@ -190,11 +190,13 @@ $j(document).ready(function () {
return;
}
if (data.hasOwnProperty('username') && data.hasOwnProperty('password') && data.hasOwnProperty('url')) {
var btnText = (data.guid === null) ? 'Save' : 'Update';
var save = API.i18n.getMessage('save');
var update = API.i18n.getMessage('update');
var btnText = (data.guid === null) ? save : update;
var buttons = [
{
text: 'Cancel',
text: API.i18n.getMessage('cancel'),
onClickFn: function () {
closeToolbar();
API.runtime.sendMessage(API.runtime.id, {method: "clearMined"});
@ -205,16 +207,16 @@ $j(document).ready(function () {
onClickFn: function () {
//closeToolbar();
API.runtime.sendMessage(API.runtime.id, {method: "saveMined"});
$j('#password-toolbar').find('.toolbar-text').text('Saving...');
$j('#password-toolbar').find('.toolbar-text').text(API.i18n.getMessage('saving')+'...');
$j('#password-toolbar').find('.passman-btn').hide();
}
},
{
text: 'Ignore site',
text: API.i18n.getMessage('ignore_site'),
onClickFn: function () {
//closeToolbar();
API.runtime.sendMessage(API.runtime.id, {method: "ignoreSite", args: window.location.href});
$j('#password-toolbar').find('.toolbar-text').text('Site ignored');
$j('#password-toolbar').find('.toolbar-text').text(API.i18n.getMessage('site_ignored'));
$j('#password-toolbar').find('.passman-btn').hide();
setTimeout(function () {
closeToolbar();
@ -235,8 +237,10 @@ $j(document).ready(function () {
return;
}
if ($j('#password-toolbar').is(':visible')) {
var action = (args.updated) ? 'updated' : 'saved';
$j('#password-toolbar').html('Credential ' + action + '!');
var saved = API.i18n.getMessage('credential_saved');
var updated = API.i18n.getMessage('credential_updated');
var action = (args.updated) ? updated : saved;
$j('#password-toolbar').html(action + '!');
setTimeout(function () {
closeToolbar();
}, 2500);
@ -251,7 +255,7 @@ $j(document).ready(function () {
});
}
function initForms(){
function initForms() {
API.runtime.sendMessage(API.runtime.id, {method: 'getRuntimeSettings'}).then(function (result) {
var disablePasswordPicker = result.disablePasswordPicker;
var url = window.location.href;
@ -264,7 +268,7 @@ $j(document).ready(function () {
}
//Password miner
/* jshint ignore:start */
if(!result.hasOwnProperty('ignored_sites') || result.ignored_sites.findUrl(url) !== -1 ) {
if (!result.hasOwnProperty('ignored_sites') || result.ignored_sites.findUrl(url) !== -1) {
$j(form).submit((function (loginFields) {
return function () {
formSubmitted(loginFields);
@ -303,6 +307,7 @@ $j(document).ready(function () {
document.execCommand('copy');
txtToCopy.parentNode.removeChild(txtToCopy);
}
_this.copyText = copyText;
function init() {

Просмотреть файл

@ -121,6 +121,7 @@ var background = (function () {
function getSetting(name) {
return _self.settings[name];
}
_self.getSetting = getSetting;
function saveSettings(settings, cb) {
@ -212,14 +213,14 @@ var background = (function () {
function minedForm(data, sender) {
var url = sender.url;
var existingLogins = getCredentialsByUrl(sender.url);
var title = "Detected new login:";
var title = API.i18n.getMessage('detected_new_login') + ':';
var minedMatchingID = null;
for (var j = 0; j < existingLogins.length; j++) {
var login = existingLogins[j];
if (login.username === data.username) {
if (login.password !== data.password) {
minedMatchingID = login.guid;
title = "Detected changed password for user:";
title = API.i18n.getMessage('detected_changed_login') + ':';
}
else {
//console.log('No changes detected');
@ -264,11 +265,11 @@ var background = (function () {
}
function ignoreSite(_url) {
if(!_self.settings.hasOwnProperty('ignored_sites')){
if (!_self.settings.hasOwnProperty('ignored_sites')) {
_self.settings.ignored_sites = [];
}
var site = processURL(_url, _self.settings.ignoreProtocol, _self.settings.ignoreSubdomain, _self.settings.ignorePath, _self.settings.ignorePort);
if(_self.settings.ignored_sites.indexOf(site) === -1){
if (_self.settings.ignored_sites.indexOf(site) === -1) {
_self.settings.ignored_sites.push(site);
saveSettings(_self.settings);
}
@ -298,7 +299,7 @@ var background = (function () {
var tab = tabs[0];
var data = login;
data.url = tab.url;
data.title = 'Detected changed url for: ';
data.title = API.i18n.getMessage('detected_changed_url') + ':';
API.tabs.sendMessage(tab.id, {
method: 'showUrlUpdateDoorhanger',
args: {data: data}
@ -313,6 +314,7 @@ var background = (function () {
saveMined({}, sender);
}
_self.updateCredentialUrl = updateCredentialUrl;
function saveMined(args, sender) {
@ -336,7 +338,7 @@ var background = (function () {
credential.url = sender.tab.url;
if (credential.guid !== null) {
PAPI.updateCredential(credential, _self.settings.vault_password, function (updatedCredential) {
if(credential_index){
if (credential_index) {
local_credentials[credential_index] = updatedCredential;
}
saveMinedCallback({credential: credential, updated: true, sender: sender});
@ -440,9 +442,10 @@ var background = (function () {
color: defaultColor,
tabId: tab.id
});
var plural = (credentialAmount === 1) ? 'credential' : 'credentials';
var plural = (credentialAmount === 1) ? API.i18n.getMessage('credential') : API.i18n.getMessage('credentials');
API.browserAction.setTitle({
title: 'Passman - ' + credentialAmount.toString() + ' ' + plural + ' found for this page',
title: API.i18n.getMessage('browser_action_title_login', [credentialAmount.toString(), plural.toString()]),
tabId: tab.id
});
}
@ -461,7 +464,7 @@ var background = (function () {
tabId: tab.id
});
API.browserAction.setTitle({
title: 'Passman - Locked',
title: API.i18n.getMessage('browser_action_title_locked'),
tabId: tab.id
});
}

37
js/lib/API/i18n.js Normal file
Просмотреть файл

@ -0,0 +1,37 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
/* global API */
API.i18n = {
getAcceptLanguages: function() {
if (API.promise) {
return API.api.i18n.getAcceptLanguages();
}
else {
return new C_Promise(function(){
API.extension.getAcceptLanguages((function(data){
this.call_then(data);
}).bind(this));
});
}
},
detectLanguage: function() {
if (API.promise) {
return API.api.i18n.detectLanguage();
}
else {
return new C_Promise(function(){
API.api.i18n.detectLanguage((function(isReliable, languages){
this.call_then(isReliable, languages);
}).bind(this));
});
}
},
getMessage: API.api.i18n.getMessage,
getUILanguage: API.api.i18n.getUILanguage
};

Просмотреть файл

@ -2,6 +2,23 @@ $(document).ready(function () {
var _this = this;
var storage = new API.Storage();
$('[t]').each(function () {
var string = $(this).attr('t');
var startChar = string[0];
var endChar = string[string.length - 1];
var attribute;
if(startChar === '[' && endChar === ']'){
var data = string.replace('[','').replace(']','').split(',');
attribute = data[1].trim();
string = data[0].trim();
}
var translated = API.i18n.getMessage(string);
if(attribute){
$(this).attr(attribute, translated);
} else {
$(this).text(translated);
}
});
function fillLogin(login) {
API.runtime.sendMessage(API.runtime.id, {
@ -195,12 +212,13 @@ $(document).ready(function () {
});
API.runtime.sendMessage(API.runtime.id, {method: "getActiveTab", args: {returnFn: "returnActiveTab"}});
function returnActiveTab(tab) {
API.runtime.sendMessage(API.runtime.id, {method: "getCredentialsByUrl", args: [tab.url]}).then(function (logins) {
API.runtime.sendMessage(API.runtime.id, {
method: "getCredentialsByUrl",
args: [tab.url]
}).then(function (logins) {
if (logins.length !== 0) {
picker.find('.tab-list-content').html('');
}
@ -224,6 +242,7 @@ $(document).ready(function () {
}
});
}
_this.returnActiveTab = returnActiveTab;
@ -247,9 +266,8 @@ $(document).ready(function () {
});
$('#password_search').keypress(function(e) {
if(e.which === 13) {
$('#password_search').keypress(function (e) {
if (e.which === 13) {
searchCredentials();
}
});
@ -262,13 +280,16 @@ $(document).ready(function () {
function searchCredentials() {
$('#searchResults').html('');
var searchText = $('#password_search').val();
if(searchText === ''){
var searchText = $('#password_search').val();
if (searchText === '') {
return;
}
API.runtime.sendMessage(API.runtime.id, {'method': 'searchCredential', args: searchText}).then(function (result) {
if(result.length === 0){
$('#searchResults').html('No results');
API.runtime.sendMessage(API.runtime.id, {
'method': 'searchCredential',
args: searchText
}).then(function (result) {
if (result.length === 0) {
$('#searchResults').html(API.i18n.getMessage('no_credentials_found'));
}
for (var i = 0; i < result.length; i++) {
var login = result[i];
@ -285,7 +306,10 @@ $(document).ready(function () {
//API.runtime.sendMessage(API.runtime.id, {method: 'getMasterPasswordSet'})
fillLogin(login);
//@TODO Ask to update the url of the login
API.runtime.sendMessage(API.runtime.id, {'method': 'updateCredentialUrlDoorhanger', args: login})
API.runtime.sendMessage(API.runtime.id, {
'method': 'updateCredentialUrlDoorhanger',
args: login
})
};
})(login));
/* jshint ignore:end*/

Просмотреть файл

@ -69,8 +69,8 @@
var save_btn = jQuery('#save'),
login_required = jQuery('.login-req');
if (vaults.hasOwnProperty('error')) {
console.log(vaults);
var errors = 'Invalid response from server: [' + vaults.result.status + '] ' + vaults.result.statusText;
var errors = API.i18n.getMessage('invalid_response_from_server', [vaults.result.status, vaults.result.statusText]);
$scope.errors.push(errors);
login_required.hide();
save_btn.hide();
@ -106,7 +106,7 @@
/** global: PAPI */
PAPI.decryptString(settings.default_vault.challenge_password, settings.vault_password);
} catch (e) {
$scope.errors.push('Invalid vault key!');
$scope.errors.push(API.i18n.getMessage('invalid_vault_password'));
return;
}

Просмотреть файл

@ -65,8 +65,8 @@
PAPI.password = $scope.settings.nextcloud_password;
PAPI.getVaults(function (vaults) {
if (vaults.hasOwnProperty('error')) {
console.log(vaults);
$scope.errors.push("Invalid server settings!");
var errors = API.i18n.getMessage('invalid_response_from_server', [vaults.result.status, vaults.result.statusText]);
$scope.errors.push(errors);
callback(false);
}
@ -83,7 +83,7 @@
callback(true);
}
catch (e) {
$scope.errors.push("Invalid vault key!");
$scope.errors.push(API.i18n.getMessage('invalid_vault_password'));
callback(false);
}
},
@ -91,7 +91,7 @@
if ($scope.settings.master_password.trim() !== '') {
callback(true);
} else {
$scope.errors.push("Empty master key!");
$scope.errors.push(API.i18n.getMessage('empty_master_key'));
callback(false);
}
}

Просмотреть файл

@ -0,0 +1,41 @@
/**
* Nextcloud - passman
*
* @copyright Copyright (c) 2016, Sander Brand (brantje@gmail.com)
* @copyright Copyright (c) 2016, Marcos Zuriaga Miguel (wolfi@wolfi.es)
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
(function () {
'use strict';
/**
* @ngdoc filter
* @name passmanApp.filter:as
* @function
* @description
* # as
* Filter in the passmanApp.
*/
angular.module('passmanExtension')
.filter("translate", function () {
return function (value, args) {
return API.i18n.getMessage(value,args);
};
});
}());

Просмотреть файл

@ -1,11 +1,11 @@
{
"name": "Passman",
"short_name": "Passman",
"name": "__MSG_extName__",
"short_name": "__MSG_extName__",
"version": "1.1.8",
"manifest_version": 2,
"author": "Sander Brand",
"description": "Passman - The password manager for NextCloud / ownCloud",
"default_locale": "en",
"description": "__MSG_extDescription__",
"homepage_url": "https://github.com/nextcloud/passman-chrome-extension",
"icons": {
"16": "/icons/icon16.png",
@ -26,6 +26,7 @@
"/js/lib/API/base.js",
"/js/lib/API/storage.js",
"/js/lib/API/runtime.js",
"/js/lib/API/i18n.js",
"/js/lib/API/notifications.js",
"/js/lib/API/cookies.js",
"/js/lib/API/browser_action.js",
@ -72,6 +73,7 @@
"/js/lib/API/runtime.js",
"/js/lib/API/extension.js",
"/js/lib/API/cookies.js",
"/js/lib/API/i18n.js",
"/js/vendor/jquery/jquery.js",
"/js/lib/jQuerytoObject.js",
"/js/lib/findForm.js",