i18n support.

This commit is contained in:
Dave Page 2015-02-25 17:06:00 +00:00
parent 39089cca21
commit 83cbe87040
28 changed files with 704 additions and 108 deletions

1
.gitignore vendored
View File

@ -3,6 +3,7 @@
*.pyo *.pyo
*.o *.o
*.psp *.psp
_build
build-* build-*
.DS_Store .DS_Store
runtime/.qmake.cache runtime/.qmake.cache

View File

@ -75,6 +75,7 @@ learn how pgAdmin works, and how to develop improvements and new features.
coding-standards coding-standards
code-overview code-overview
submitting-patches submitting-patches
translations
******* *******
Website Website

134
docs/en_US/translations.rst Normal file
View File

@ -0,0 +1,134 @@
Translations
============
pgAdmin supports multiple languages using the `Flask-Babel
<https://pythonhosted.org/Flask-Babel/>`_ Python module. A list of supported
languages is included in the **web/config.py** configuration file and must be
updated whenever langauges are added or removed.
Translation Marking
-------------------
Strings can be marked for translation in either Python code (using **gettext()**)
or Jinja templates (using **_()**). Here are some examples that show how this
is achieved.
Python::
errormsg = gettext('No server group name was specified')
Jinja:
.. code-block:: html
<input type="submit" value="{{ _('Change Password') }}">
.. code-block:: html
<title>{{ _('%(appname)s Password Change', appname=config.APP_NAME) }}</title>
.. code-block:: javascript
var alert = alertify.prompt(
'{{ _('Add a server group') }}',
'{{ _('Enter a name for the new server group') }}',
''
...
)
Updating and Merging
--------------------
Whenever new strings are added to the application, the template catalogues
(**web/pgadmin/messages.pot**) must be updated and the existing catalogues
merged with the updated template and compiled. This can be achieved using the
following command from the **web** directory, in the Python virtual environment
used for pgAdmin:
.. code-block:: bash
(pgadmin4)piranha:web dpage$ pybabel extract -F babel.cfg -o pgadmin/messages.pot pgadmin
For example:
.. code-block:: bash
(pgadmin4)piranha:web dpage$ pybabel extract -F babel.cfg -o pgadmin/messages.pot pgadmin
extracting messages from pgadmin/__init__.py
extracting messages from pgadmin/about/__init__.py
extracting messages from pgadmin/about/hooks.py
extracting messages from pgadmin/about/views.py
extracting messages from pgadmin/about/templates/about/index.html (extensions="jinja2.ext.autoescape,jinja2.ext.with_")
extracting messages from pgadmin/browser/__init__.py
extracting messages from pgadmin/browser/hooks.py
extracting messages from pgadmin/browser/views.py
extracting messages from pgadmin/browser/nodes/CollectionNode.py
extracting messages from pgadmin/browser/nodes/ObjectNode.py
extracting messages from pgadmin/browser/nodes/__init__.py
extracting messages from pgadmin/browser/nodes/server_groups/__init__.py
extracting messages from pgadmin/browser/nodes/server_groups/hooks.py
extracting messages from pgadmin/browser/nodes/server_groups/views.py
extracting messages from pgadmin/browser/templates/browser/body.html (extensions="jinja2.ext.autoescape,jinja2.ext.with_")
extracting messages from pgadmin/browser/templates/browser/index.html (extensions="jinja2.ext.autoescape,jinja2.ext.with_")
extracting messages from pgadmin/browser/templates/browser/messages.html (extensions="jinja2.ext.autoescape,jinja2.ext.with_")
extracting messages from pgadmin/help/__init__.py
extracting messages from pgadmin/help/hooks.py
extracting messages from pgadmin/help/views.py
extracting messages from pgadmin/redirects/__init__.py
extracting messages from pgadmin/redirects/views.py
extracting messages from pgadmin/settings/__init__.py
extracting messages from pgadmin/settings/hooks.py
extracting messages from pgadmin/settings/settings_model.py
extracting messages from pgadmin/settings/views.py
extracting messages from pgadmin/templates/base.html (extensions="jinja2.ext.autoescape,jinja2.ext.with_")
extracting messages from pgadmin/templates/security/change_password.html (extensions="jinja2.ext.autoescape,jinja2.ext.with_")
extracting messages from pgadmin/templates/security/fields.html (extensions="jinja2.ext.autoescape,jinja2.ext.with_")
extracting messages from pgadmin/templates/security/forgot_password.html (extensions="jinja2.ext.autoescape,jinja2.ext.with_")
extracting messages from pgadmin/templates/security/login_user.html (extensions="jinja2.ext.autoescape,jinja2.ext.with_")
extracting messages from pgadmin/templates/security/messages.html (extensions="jinja2.ext.autoescape,jinja2.ext.with_")
extracting messages from pgadmin/templates/security/panel.html (extensions="jinja2.ext.autoescape,jinja2.ext.with_")
extracting messages from pgadmin/templates/security/reset_password.html (extensions="jinja2.ext.autoescape,jinja2.ext.with_")
extracting messages from pgadmin/templates/security/watermark.html (extensions="jinja2.ext.autoescape,jinja2.ext.with_")
extracting messages from pgadmin/test/__init__.py
extracting messages from pgadmin/test/hooks.py
extracting messages from pgadmin/test/views.py
extracting messages from pgadmin/utils/__init__.py
extracting messages from pgadmin/utils/views.py
writing PO template file to pgadmin/messages.pot
Once the template has been updated, it needs to be merged into the existing
message catalogues, for example:
.. code-block:: bash
(pgadmin4)piranha:web dpage$ pybabel update -i pgadmin/messages.pot -d pgadmin/translations
updating catalog 'pgadmin/translations/fr/LC_MESSAGES/messages.po' based on 'pgadmin/messages.pot'
Finally, the message catalogues can be compiled for use:
.. code-block:: bash
(pgadmin4)piranha:web dpage$ pybabel compile -d pgadmin/translations
compiling catalog 'pgadmin/translations/fr/LC_MESSAGES/messages.po' to 'pgadmin/translations/fr/LC_MESSAGES/messages.mo'
Adding a new Language
---------------------
Adding a new language is simple. First, add the language name and identifier to
**web/config.py**::
# Languages we support in the UI
LANGUAGES = {
'en': 'English',
'fr': 'Français'
}
Then, create the new message catalogue from the **web** directory in the source
tree, in the Python virtual environment used for pgAdmin:
.. code-block:: bash
(pgadmin4)piranha:web dpage$ pybabel init -i pgadmin/messages.pot -d pgadmin/translations -l fr
This will initialise a new catalogue for a French translation.

View File

@ -1,4 +1,6 @@
Babel==1.3
Flask==0.10.1 Flask==0.10.1
Flask-Babel==0.9
Flask-Gravatar==0.4.1 Flask-Gravatar==0.4.1
Flask-Login==0.2.11 Flask-Login==0.2.11
Flask-Mail==0.9.1 Flask-Mail==0.9.1
@ -19,5 +21,7 @@ html5lib==1.0b3
itsdangerous==0.24 itsdangerous==0.24
passlib==1.6.2 passlib==1.6.2
psycopg2==2.5.2 psycopg2==2.5.2
pytz==2014.10
six==1.9.0 six==1.9.0
speaklater==1.3
wsgiref==0.1.2 wsgiref==0.1.2

3
web/babel.cfg Normal file
View File

@ -0,0 +1,3 @@
[python: **.py]
[jinja2: **/templates/**.html]
extensions=jinja2.ext.autoescape,jinja2.ext.with_

View File

@ -1,3 +1,5 @@
# -*- coding: utf-8 -*-
########################################################################## ##########################################################################
# #
# pgAdmin 4 - PostgreSQL Tools # pgAdmin 4 - PostgreSQL Tools
@ -34,6 +36,12 @@ APP_COPYRIGHT = 'Copyright 2014 - 2015, The pgAdmin Development Team'
# Path to the online help. # Path to the online help.
HELP_PATH = '../../../docs/en_US/_build/html/' HELP_PATH = '../../../docs/en_US/_build/html/'
# Languages we support in the UI
LANGUAGES = {
'en': 'English',
'fr': 'Français'
}
# DO NOT CHANGE! # DO NOT CHANGE!
# The application version string, constructed from the components # The application version string, constructed from the components
APP_VERSION = '%s.%s.%s-%s' % (APP_MAJOR, APP_MINOR, APP_REVISION, APP_SUFFIX) APP_VERSION = '%s.%s.%s-%s' % (APP_MAJOR, APP_MINOR, APP_REVISION, APP_SUFFIX)

View File

@ -10,7 +10,8 @@
"""The main pgAdmin module. This handles the application initialisation tasks, """The main pgAdmin module. This handles the application initialisation tasks,
such as setup of logging, dynamic loading of modules etc.""" such as setup of logging, dynamic loading of modules etc."""
from flask import Flask, abort from flask import Flask, abort, request
from flask.ext.babel import Babel
from flask.ext.sqlalchemy import SQLAlchemy from flask.ext.sqlalchemy import SQLAlchemy
from flask.ext.security import Security, SQLAlchemyUserDatastore, login_required from flask.ext.security import Security, SQLAlchemyUserDatastore, login_required
from flask_security.utils import login_user from flask_security.utils import login_user
@ -66,6 +67,23 @@ def create_app(app_name=config.APP_NAME):
app.logger.info('Starting %s v%s...', config.APP_NAME, config.APP_VERSION) app.logger.info('Starting %s v%s...', config.APP_NAME, config.APP_VERSION)
app.logger.info('################################################################################') app.logger.info('################################################################################')
##########################################################################
# Setup i18n
##########################################################################
# Initialise i18n
babel = Babel(app)
app.logger.debug('Available translations: %s' % babel.list_translations())
@babel.localeselector
def get_locale():
"""Get the best language for the user."""
language = request.accept_languages.best_match(config.LANGUAGES.keys())
app.logger.info('Using language: %s', language)
return language
########################################################################## ##########################################################################
# Setup authentication # Setup authentication
########################################################################## ##########################################################################

View File

@ -1,25 +1,25 @@
<div class="row"> <div class="row">
<div class="col-sm-3"><b>Version</b></div> <div class="col-sm-3"><b>{{ _('Version') }}</b></div>
<div class="col-sm-9">{{ config.APP_VERSION }}</div> <div class="col-sm-9">{{ config.APP_VERSION }}</div>
</div> </div>
<div class="row"> <div class="row">
<div class="col-sm-3"><b>Copyright</b></div> <div class="col-sm-3"><b>{{ _('Copyright') }}</b></div>
<div class="col-sm-9">{{ config.APP_COPYRIGHT }}</div> <div class="col-sm-9">{{ config.APP_COPYRIGHT }}</div>
</div> </div>
<div class="row"> <div class="row">
<div class="col-sm-3"><b>Python Version</b></div> <div class="col-sm-3"><b>{{ _('Python Version') }}</b></div>
<div class="col-sm-9">{{ info.python_version }}</div> <div class="col-sm-9">{{ info.python_version }}</div>
</div> </div>
<div class="row"> <div class="row">
<div class="col-sm-3"><b>Flask Version</b></div> <div class="col-sm-3"><b>{{ _('Flask Version') }}</b></div>
<div class="col-sm-9">{{ info.flask_version }}</div> <div class="col-sm-9">{{ info.flask_version }}</div>
</div> </div>
<div class="row"> <div class="row">
<div class="col-sm-3"><b>Application Mode</b></div> <div class="col-sm-3"><b>{{ _('Application Mode') }}</b></div>
<div class="col-sm-9">{{ info.app_mode }}</div> <div class="col-sm-9">{{ info.app_mode }}</div>
</div> </div>
<div class="row"> <div class="row">
<div class="col-sm-3"><b>Current User</b></div> <div class="col-sm-3"><b>{{ _('Current User') }}</b></div>
<div class="col-sm-9">{{ info.current_user }}</div> <div class="col-sm-9">{{ info.current_user }}</div>
</div> </div>

View File

@ -11,6 +11,7 @@
MODULE_NAME = 'about' MODULE_NAME = 'about'
from flask import Blueprint, Response, current_app, render_template, __version__ from flask import Blueprint, Response, current_app, render_template, __version__
from flask.ext.babel import gettext
from flask.ext.security import current_user, login_required from flask.ext.security import current_user, login_required
import sys import sys
@ -31,9 +32,9 @@ def index():
info['python_version'] = sys.version info['python_version'] = sys.version
info['flask_version'] = __version__ info['flask_version'] = __version__
if config.SERVER_MODE == True: if config.SERVER_MODE == True:
info['app_mode'] = 'Server' info['app_mode'] = gettext('Server')
else: else:
info['app_mode'] = 'Desktop' info['app_mode'] = gettext('Desktop')
info['current_user'] = current_user.email info['current_user'] = current_user.email
return render_template(MODULE_NAME + '/index.html', info=info) return render_template(MODULE_NAME + '/index.html', info=info)

View File

@ -10,6 +10,7 @@
"""Integration hooks for server groups.""" """Integration hooks for server groups."""
from flask import render_template, url_for from flask import render_template, url_for
from flask.ext.babel import gettext
from flask.ext.security import current_user from flask.ext.security import current_user
from pgadmin.settings.settings_model import db, ServerGroup from pgadmin.settings.settings_model import db, ServerGroup
@ -30,17 +31,17 @@ def get_file_menu_items():
"""Return a (set) of dicts of file menu items, with name, priority, URL, """Return a (set) of dicts of file menu items, with name, priority, URL,
target and onclick code.""" target and onclick code."""
return [ return [
{'name': 'mnu_add_server_group', 'label': 'Add a server group...', 'priority': 10, 'url': '#', 'onclick': 'add_server_group()'}, {'name': 'mnu_add_server_group', 'label': gettext('Add a server group...'), 'priority': 10, 'url': '#', 'onclick': 'add_server_group()'},
{'name': 'mnu_delete_server_group', 'label': 'Delete server group', 'priority': 20, 'url': '#', 'onclick': 'delete_server_group()'}, {'name': 'mnu_delete_server_group', 'label': gettext('Delete server group'), 'priority': 20, 'url': '#', 'onclick': 'delete_server_group()'},
{'name': 'mnu_rename_server_group', 'label': 'Rename server group...', 'priority': 30, 'url': '#', 'onclick': 'rename_server_group()'} {'name': 'mnu_rename_server_group', 'label': gettext('Rename server group...'), 'priority': 30, 'url': '#', 'onclick': 'rename_server_group()'}
] ]
def get_context_menu_items(): def get_context_menu_items():
"""Return a (set) of dicts of content menu items with name, label, priority and JS""" """Return a (set) of dicts of content menu items with name, label, priority and JS"""
return [ return [
{'name': 'delete', 'label': 'Delete server group', 'priority': 100, 'onclick': 'delete_server_group(item);'}, {'name': 'delete', 'label': gettext('Delete server group'), 'priority': 100, 'onclick': 'delete_server_group(item);'},
{'name': 'rename', 'label': 'Rename server group...', 'priority': 200, 'onclick': 'rename_server_group(item);'} {'name': 'rename', 'label': gettext('Rename server group...'), 'priority': 200, 'onclick': 'rename_server_group(item);'}
] ]

View File

@ -1,8 +1,8 @@
// Add a server group // Add a server group
function add_server_group() { function add_server_group() {
var alert = alertify.prompt( var alert = alertify.prompt(
'Add a server group', '{{ _('Add a server group') }}',
'Enter a name for the new server group', '{{ _('Enter a name for the new server group') }}',
'', '',
function(evt, value) { function(evt, value) {
$.post("{{ url_for('NODE-server-group.add') }}", { name: value }) $.post("{{ url_for('NODE-server-group.add') }}", { name: value })
@ -34,8 +34,8 @@ function add_server_group() {
// Delete a server group // Delete a server group
function delete_server_group(item) { function delete_server_group(item) {
alertify.confirm( alertify.confirm(
'Delete server group?', '{{ _('Delete server group?') }}',
'Are you sure you wish to delete the server group "{0}"?'.replace('{0}', tree.getLabel(item)), '{{ _('Are you sure you wish to delete the server group "{0}"?') }}'.replace('{0}', tree.getLabel(item)),
function() { function() {
var id = tree.getId(item) var id = tree.getId(item)
$.post("{{ url_for('NODE-server-group.delete') }}", { id: id }) $.post("{{ url_for('NODE-server-group.delete') }}", { id: id })
@ -62,8 +62,8 @@ function delete_server_group(item) {
// Rename a server group // Rename a server group
function rename_server_group(item) { function rename_server_group(item) {
alertify.prompt( alertify.prompt(
'Rename server group', '{{ _('Rename server group') }}',
'Enter a new name for the server group', '{{ _('Enter a new name for the server group') }}',
tree.getLabel(item), tree.getLabel(item),
function(evt, value) { function(evt, value) {
var id = tree.getId(item) var id = tree.getId(item)

View File

@ -15,6 +15,7 @@ NODE_PATH = '/browser/' + NODE_NAME
import traceback import traceback
from flask import Blueprint, Response, current_app, request from flask import Blueprint, Response, current_app, request
from flask.ext.babel import gettext
from flask.ext.security import current_user, login_required from flask.ext.security import current_user, login_required
from utils.ajax import make_json_response from utils.ajax import make_json_response
@ -44,7 +45,7 @@ def add():
else: else:
success = 0 success = 0
errormsg = "No server group name was specified" errormsg = gettext('No server group name was specified')
if success == 1: if success == 1:
data['id'] = servergroup.id data['id'] = servergroup.id
@ -69,7 +70,7 @@ def delete():
if servergroup is None: if servergroup is None:
success = 0 success = 0
errormsg = 'The specified server group could not be found.' errormsg = gettext('The specified server group could not be found.')
else: else:
try: try:
db.session.delete(servergroup) db.session.delete(servergroup)
@ -80,7 +81,7 @@ def delete():
else: else:
success = 0 success = 0
errormsg = "No server group was specified." errormsg = gettext('No server group was specified.')
return make_json_response(success=success, return make_json_response(success=success,
errormsg=errormsg, errormsg=errormsg,
@ -100,7 +101,7 @@ def rename():
if servergroup is None: if servergroup is None:
success = 0 success = 0
errormsg = 'The specified server group could not be found.' errormsg = gettext('The specified server group could not be found.')
else: else:
try: try:
servergroup.name = request.form['name'] servergroup.name = request.form['name']
@ -111,9 +112,10 @@ def rename():
else: else:
success = 0 success = 0
errormsg = "No server group was specified." errormsg = gettext('No server group was specified.')
return make_json_response(success=success, return make_json_response(success=success,
errormsg=errormsg, errormsg=errormsg,
info=traceback.format_exc(), info=traceback.format_exc(),
result=request.form) result=request.form)

View File

@ -1,39 +0,0 @@
function report_error(message, info) {
text = '<div class="panel-group" id="accordion" role="tablist" aria-multiselectable="true">\
<div class="panel panel-default">\
<div class="panel-heading" role="tab" id="headingOne">\
<h4 class="panel-title">\
<a data-toggle="collapse" data-parent="#accordion" href="#collapseOne" aria-expanded="true" aria-controls="collapseOne">\
Error message\
</a>\
</h4>\
</div>\
<div id="collapseOne" class="panel-collapse collapse in" role="tabpanel" aria-labelledby="headingOne">\
<div class="panel-body" style="overflow: scroll;">' + message + '</div>\
</div>\
</div>'
if (info != '') {
text += '<div class="panel panel-default">\
<div class="panel-heading" role="tab" id="headingTwo">\
<h4 class="panel-title">\
<a class="collapsed" data-toggle="collapse" data-parent="#accordion" href="#collapseTwo" aria-expanded="false" aria-controls="collapseTwo">\
Additional info\
</a>\
</h4>\
</div>\
<div id="collapseTwo" class="panel-collapse collapse" role="tabpanel" aria-labelledby="headingTwo">\
<div class="panel-body" style="overflow: scroll;">' + info + '</div>\
</div>\
</div>\
</div>'
}
text += '</div>'
alertify.alert(
'An error has occurred',
text
)
}

View File

@ -8,11 +8,11 @@
<div class="pane ui-layout-center browser-inner-pane" id="outer-center"> <div class="pane ui-layout-center browser-inner-pane" id="outer-center">
<div class="pane ui-layout-center browser-center-pane" id="inner-center"> <div class="pane ui-layout-center browser-center-pane" id="inner-center">
<ul class="nav nav-tabs browser-tab-bar" role="tablist"> <ul class="nav nav-tabs browser-tab-bar" role="tablist">
<li role="presentation" class="active"><a href="#dashboard" aria-controls="home" role="tab" data-toggle="tab">Dashboard</a></li> <li role="presentation" class="active"><a href="#dashboard" aria-controls="home" role="tab" data-toggle="tab">{{ _('Dashboard') }}</a></li>
<li role="presentation"><a href="#properties" aria-controls="profile" role="tab" data-toggle="tab">Properties</a></li> <li role="presentation"><a href="#properties" aria-controls="profile" role="tab" data-toggle="tab">{{ _('Properties') }}</a></li>
<li role="presentation"><a href="#statistics" aria-controls="messages" role="tab" data-toggle="tab">Statistics</a></li> <li role="presentation"><a href="#statistics" aria-controls="messages" role="tab" data-toggle="tab">{{ _('Statistics') }}</a></li>
<li role="presentation"><a href="#dependencies" aria-controls="settings" role="tab" data-toggle="tab">Dependencies</a></li> <li role="presentation"><a href="#dependencies" aria-controls="settings" role="tab" data-toggle="tab">{{ _('Dependencies') }}</a></li>
<li role="presentation"><a href="#dependents" aria-controls="settings" role="tab" data-toggle="tab">Dependents</a></li> <li role="presentation"><a href="#dependents" aria-controls="settings" role="tab" data-toggle="tab">{{ _('Dependents') }}</a></li>
</ul> </ul>
<div class="tab-content browser-tab-panes"> <div class="tab-content browser-tab-panes">
<div role="tabpanel" class="tab-pane browser-tab-pane active" id="dashboard"> <div role="tabpanel" class="tab-pane browser-tab-pane active" id="dashboard">

View File

@ -5,7 +5,7 @@
<div class="container-fluid"> <div class="container-fluid">
<div class="navbar-header"> <div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar-menu"> <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar-menu">
<span class="sr-only">Toggle navigation</span> <span class="sr-only">{{ _('Toggle navigation') }}</span>
<span class="icon-bar"></span> <span class="icon-bar"></span>
<span class="icon-bar"></span> <span class="icon-bar"></span>
<span class="icon-bar"></span> <span class="icon-bar"></span>
@ -18,35 +18,35 @@
<ul class="nav navbar-nav"> <ul class="nav navbar-nav">
{% if file_items is defined and file_items|count > 0 %}<li class="dropdown"> {% if file_items is defined and file_items|count > 0 %}<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false">File <span class="caret"></span></a> <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false">{{ _('File') }} <span class="caret"></span></a>
<ul class="dropdown-menu" role="menu">{% for file_item in file_items %} <ul class="dropdown-menu" role="menu">{% for file_item in file_items %}
<li><a id="{{ file_item.name }}" href="{{ file_item.url }}" target="{{ file_item.target }}" onclick="{{ file_item.onclick|safe }}">{{ file_item.label }}</a></li>{% endfor %} <li><a id="{{ file_item.name }}" href="{{ file_item.url }}" target="{{ file_item.target }}" onclick="{{ file_item.onclick|safe }}">{{ file_item.label }}</a></li>{% endfor %}
</ul> </ul>
</li>{% endif %} </li>{% endif %}
{% if edit_items is defined and edit_items|count > 0 %}<li class="dropdown"> {% if edit_items is defined and edit_items|count > 0 %}<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false">Edit <span class="caret"></span></a> <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false">{{ _('Edit') }} <span class="caret"></span></a>
<ul class="dropdown-menu" role="menu">{% for edit_item in edit_items %} <ul class="dropdown-menu" role="menu">{% for edit_item in edit_items %}
<li><a id="{{ edit_item.name }}" href="{{ edit_item.url }}" target="{{ edit_item.target }}" onclick="{{ edit_item.onclick|safe }}">{{ edit_item.label }}</a></li>{% endfor %} <li><a id="{{ edit_item.name }}" href="{{ edit_item.url }}" target="{{ edit_item.target }}" onclick="{{ edit_item.onclick|safe }}">{{ edit_item.label }}</a></li>{% endfor %}
</ul> </ul>
</li>{% endif %} </li>{% endif %}
{% if tools_items is defined and tools_items|count > 0 %}<li class="dropdown"> {% if tools_items is defined and tools_items|count > 0 %}<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false">Tools <span class="caret"></span></a> <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false">{{ _('Tools') }} <span class="caret"></span></a>
<ul class="dropdown-menu" role="menu">{% for tools_item in tools_items %} <ul class="dropdown-menu" role="menu">{% for tools_item in tools_items %}
<li><a id="{{ tools_item.name }}" href="{{ tools_item.url }}" target="{{ tools_item.target }}" onclick="{{ tools_item.onclick|safe }}">{{ tools_item.label }}</a></li>{% endfor %} <li><a id="{{ tools_item.name }}" href="{{ tools_item.url }}" target="{{ tools_item.target }}" onclick="{{ tools_item.onclick|safe }}">{{ tools_item.label }}</a></li>{% endfor %}
</ul> </ul>
</li>{% endif %} </li>{% endif %}
{% if management_items is defined and management_items|count > 0 %}<li class="dropdown"> {% if management_items is defined and management_items|count > 0 %}<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false">Management <span class="caret"></span></a> <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false">{{ _('Management') }} <span class="caret"></span></a>
<ul class="dropdown-menu" role="menu">{% for management_item in management_items %} <ul class="dropdown-menu" role="menu">{% for management_item in management_items %}
<li><a id="{{ management_item.name }}" href="{{ management_item.url }}" target="{{ management_item.target }}" onclick="{{ management_item.onclick|safe }}">{{ management_item.label }}</a></li>{% endfor %} <li><a id="{{ management_item.name }}" href="{{ management_item.url }}" target="{{ management_item.target }}" onclick="{{ management_item.onclick|safe }}">{{ management_item.label }}</a></li>{% endfor %}
</ul> </ul>
</li>{% endif %} </li>{% endif %}
{% if help_items is defined and help_items|count > 0 %}<li class="dropdown"> {% if help_items is defined and help_items|count > 0 %}<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false">Help <span class="caret"></span></a> <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false">{{ _('Help') }} <span class="caret"></span></a>
<ul class="dropdown-menu" role="menu">{% for help_item in help_items %} <ul class="dropdown-menu" role="menu">{% for help_item in help_items %}
<li><a id="{{ help_item.name }}" href="{{ help_item.url }}" target="{{ help_item.target }}" onclick="{{ help_item.onclick|safe }}">{{ help_item.label }}</a></li>{% endfor %} <li><a id="{{ help_item.name }}" href="{{ help_item.url }}" target="{{ help_item.target }}" onclick="{{ help_item.onclick|safe }}">{{ help_item.label }}</a></li>{% endfor %}
</ul> </ul>
@ -58,9 +58,9 @@
<li class="dropdown"> <li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false"><img src="{{ username | gravatar }}" width="18" height="18" alt="Gravatar image for {{ username }}"> {{ username }} <span class="caret"></span></a> <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false"><img src="{{ username | gravatar }}" width="18" height="18" alt="Gravatar image for {{ username }}"> {{ username }} <span class="caret"></span></a>
<ul class="dropdown-menu"> <ul class="dropdown-menu">
<li><a href="{{ url_for('security.change_password') }}">Change Password</a></li> <li><a href="{{ url_for('security.change_password') }}">{{ _('Change Password') }}</a></li>
<li class="divider"></li> <li class="divider"></li>
<li><a href="{{ url_for('security.logout') }}">Logout</a></li> <li><a href="{{ url_for('security.logout') }}">{{ _('Logout') }}</a></li>
</ul> </ul>
</li> </li>
</ul> </ul>

View File

@ -35,25 +35,25 @@ $(document).ready(function(){
center__maskContents: true, center__maskContents: true,
center__onresize: "storeLayout", center__onresize: "storeLayout",
south__maskContents: true, south__maskContents: true,
south__size: {{ layout_settings.sql_size }}, south__size: {{ layout_settings.sql_size }},
south__initClosed: {{ layout_settings.sql_closed }}, south__initClosed: {{ layout_settings.sql_closed }},
south__spacing_closed: 22, south__spacing_closed: 22,
south__togglerLength_closed: 140, south__togglerLength_closed: 140,
south__togglerAlign_closed: "right", south__togglerAlign_closed: "right",
south__togglerContent_closed: 'SQL Pane', south__togglerContent_closed: "{{ _('SQL Pane') }}",
south__togglerTip_closed: "Open & Pin SQL Pane", south__togglerTip_closed: "{{ _('Open & Pin SQL Pane') }}",
south__sliderTip: "Slide Open SQL Pane", south__sliderTip: "{{ _('Slide Open SQL Pane') }}",
south__slideTrigger_open: "mouseover", south__slideTrigger_open: "mouseover",
}], }],
west__maskContents: true, west__maskContents: true,
west__size: {{ layout_settings.browser_size }}, west__size: {{ layout_settings.browser_size }},
west__initClosed: {{ layout_settings.browser_closed }}, west__initClosed: {{ layout_settings.browser_closed }},
west__spacing_closed: 22, west__spacing_closed: 22,
west__togglerLength_closed: 140, west__togglerLength_closed: 140,
west__togglerAlign_closed: "top", west__togglerAlign_closed: "top",
west__togglerContent_closed: 'B<br />r<br />o<br />w<br />s<br />e<br />r', west__togglerContent_closed: "{{ _('B<br />r<br />o<br />w<br />s<br />e<br />r') }}",
west__togglerTip_closed: "Open & Pin Browser", west__togglerTip_closed: "{{ _('Open & Pin Browser') }}",
west__sliderTip: "Slide Open Browser", west__sliderTip: "{{ _('Slide Open Browser') }}",
west__slideTrigger_open: "mouseover", west__slideTrigger_open: "mouseover",
}; };
@ -126,3 +126,43 @@ $(document).ready(function(){
} }
}); });
}); });
function report_error(message, info) {
text = '<div class="panel-group" id="accordion" role="tablist" aria-multiselectable="true">\
<div class="panel panel-default">\
<div class="panel-heading" role="tab" id="headingOne">\
<h4 class="panel-title">\
<a data-toggle="collapse" data-parent="#accordion" href="#collapseOne" aria-expanded="true" aria-controls="collapseOne">\
{{ _('Error message') }}\
</a>\
</h4>\
</div>\
<div id="collapseOne" class="panel-collapse collapse in" role="tabpanel" aria-labelledby="headingOne">\
<div class="panel-body" style="overflow: scroll;">' + message + '</div>\
</div>\
</div>'
if (info != '') {
text += '<div class="panel panel-default">\
<div class="panel-heading" role="tab" id="headingTwo">\
<h4 class="panel-title">\
<a class="collapsed" data-toggle="collapse" data-parent="#accordion" href="#collapseTwo" aria-expanded="false" aria-controls="collapseTwo">\
{{ _('Additional info') }}\
</a>\
</h4>\
</div>\
<div id="collapseTwo" class="panel-collapse collapse" role="tabpanel" aria-labelledby="headingTwo">\
<div class="panel-body" style="overflow: scroll;">' + info + '</div>\
</div>\
</div>\
</div>'
}
text += '</div>'
alertify.alert(
'{{ _('An error has occurred') }}',
text
)
}

View File

@ -59,7 +59,6 @@ def index():
# Add browser scripts # Add browser scripts
scripts.append(url_for('static', filename='js/codemirror/codemirror.js')) scripts.append(url_for('static', filename='js/codemirror/codemirror.js'))
scripts.append(url_for('static', filename='js/codemirror/mode/sql.js')) scripts.append(url_for('static', filename='js/codemirror/mode/sql.js'))
scripts.append(url_for('browser.static', filename='js/utils.js'))
scripts.append(url_for('browser.static', filename='js/aciTree/jquery.aciPlugin.min.js')) scripts.append(url_for('browser.static', filename='js/aciTree/jquery.aciPlugin.min.js'))
scripts.append(url_for('browser.static', filename='js/aciTree/jquery.aciTree.dom.js')) scripts.append(url_for('browser.static', filename='js/aciTree/jquery.aciTree.dom.js'))
scripts.append(url_for('browser.static', filename='js/aciTree/jquery.aciTree.min.js')) scripts.append(url_for('browser.static', filename='js/aciTree/jquery.aciTree.min.js'))

View File

@ -10,6 +10,7 @@
"""Browser integration functions for the Help module.""" """Browser integration functions for the Help module."""
from flask import url_for from flask import url_for
from flask.ext.babel import gettext
import config import config
@ -17,7 +18,7 @@ def get_help_menu_items():
"""Return a (set) of dicts of help menu items, with name, priority, URL, """Return a (set) of dicts of help menu items, with name, priority, URL,
target and onclick code.""" target and onclick code."""
return [{'name': 'mnu_contents', return [{'name': 'mnu_contents',
'label': 'Contents', 'label': gettext('Contents'),
'priority': 1, 'priority': 1,
'target': '_new', 'target': '_new',
'url': url_for('help.static', filename='index.html') }] 'url': url_for('help.static', filename='index.html') }]

210
web/pgadmin/messages.pot Normal file
View File

@ -0,0 +1,210 @@
# Translations template for PROJECT.
# Copyright (C) 2015 ORGANIZATION
# This file is distributed under the same license as the PROJECT project.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2015.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2015-02-25 16:30+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 1.3\n"
#: pgadmin/about/views.py:35
msgid "Server"
msgstr ""
#: pgadmin/about/views.py:37
msgid "Desktop"
msgstr ""
#: pgadmin/about/templates/about/index.html:2
msgid "Version"
msgstr ""
#: pgadmin/about/templates/about/index.html:6
msgid "Copyright"
msgstr ""
#: pgadmin/about/templates/about/index.html:10
msgid "Python Version"
msgstr ""
#: pgadmin/about/templates/about/index.html:14
msgid "Flask Version"
msgstr ""
#: pgadmin/about/templates/about/index.html:18
msgid "Application Mode"
msgstr ""
#: pgadmin/about/templates/about/index.html:22
msgid "Current User"
msgstr ""
#: pgadmin/browser/nodes/server_groups/hooks.py:34
msgid "Add a server group..."
msgstr ""
#: pgadmin/browser/nodes/server_groups/hooks.py:35
#: pgadmin/browser/nodes/server_groups/hooks.py:43
msgid "Delete server group"
msgstr ""
#: pgadmin/browser/nodes/server_groups/hooks.py:36
#: pgadmin/browser/nodes/server_groups/hooks.py:44
msgid "Rename server group..."
msgstr ""
#: pgadmin/browser/nodes/server_groups/views.py:48
msgid "No server group name was specified"
msgstr ""
#: pgadmin/browser/nodes/server_groups/views.py:73
#: pgadmin/browser/nodes/server_groups/views.py:104
msgid "The specified server group could not be found."
msgstr ""
#: pgadmin/browser/nodes/server_groups/views.py:84
msgid "No server group was specified."
msgstr ""
#: pgadmin/browser/nodes/server_groups/views.py:115
msgid "No server group was specified."
msgstr ""
#: pgadmin/browser/templates/browser/body.html:11
msgid "Dashboard"
msgstr ""
#: pgadmin/browser/templates/browser/body.html:12
msgid "Properties"
msgstr ""
#: pgadmin/browser/templates/browser/body.html:13
msgid "Statistics"
msgstr ""
#: pgadmin/browser/templates/browser/body.html:14
msgid "Dependencies"
msgstr ""
#: pgadmin/browser/templates/browser/body.html:15
msgid "Dependents"
msgstr ""
#: pgadmin/browser/templates/browser/index.html:8
msgid "Toggle navigation"
msgstr ""
#: pgadmin/browser/templates/browser/index.html:21
msgid "File"
msgstr ""
#: pgadmin/browser/templates/browser/index.html:28
msgid "Edit"
msgstr ""
#: pgadmin/browser/templates/browser/index.html:35
msgid "Tools"
msgstr ""
#: pgadmin/browser/templates/browser/index.html:42
msgid "Management"
msgstr ""
#: pgadmin/browser/templates/browser/index.html:49
msgid "Help"
msgstr ""
#: pgadmin/browser/templates/browser/index.html:61
#: pgadmin/templates/security/change_password.html:10
msgid "Change Password"
msgstr ""
#: pgadmin/browser/templates/browser/index.html:63
msgid "Logout"
msgstr ""
#: pgadmin/help/hooks.py:21
msgid "Contents"
msgstr ""
#: pgadmin/templates/security/change_password.html:2
#, python-format
msgid "%(appname)s Password Change"
msgstr ""
#: pgadmin/templates/security/forgot_password.html:2
#, python-format
msgid "Recover %(appname)s Password"
msgstr ""
#: pgadmin/templates/security/forgot_password.html:4
msgid ""
"Enter the email address for the user account you wish to recover the "
"password for:"
msgstr ""
#: pgadmin/templates/security/forgot_password.html:9
msgid "Recover Password"
msgstr ""
#: pgadmin/templates/security/login_user.html:2
#, python-format
msgid "%(appname)s Login"
msgstr ""
#: pgadmin/templates/security/login_user.html:9
msgid "Login"
msgstr ""
#: pgadmin/templates/security/login_user.html:12
#, python-format
msgid "Forgotten your <a href=\"%(url)s\">password</a>?"
msgstr ""
#: pgadmin/templates/security/messages.html:6
msgid "Close"
msgstr ""
#: pgadmin/templates/security/reset_password.html:2
#, python-format
msgid "%(appname)s Password Reset"
msgstr ""
#: pgadmin/templates/security/reset_password.html:9
msgid "Reset Password"
msgstr ""
#: pgadmin/test/hooks.py:19
msgid "Generated Test HTML"
msgstr ""
#: pgadmin/test/hooks.py:20
msgid "Test Alert"
msgstr ""
#: pgadmin/test/hooks.py:21
msgid "Test Confirm"
msgstr ""
#: pgadmin/test/hooks.py:22
msgid "Test Dialog"
msgstr ""
#: pgadmin/test/hooks.py:23
msgid "Test Prompt"
msgstr ""
#: pgadmin/test/hooks.py:24
msgid "Test Notifier"
msgstr ""

View File

@ -1,5 +1,5 @@
{% extends "security/panel.html" %} {% extends "security/panel.html" %}
{% block panel_title %}pgAdmin Password Change{% endblock %} {% block panel_title %}{{ _('%(appname)s Password Change', appname=config.APP_NAME) }}{% endblock %}
{% block panel_body %} {% block panel_body %}
<form action="{{ url_for_security('change_password') }}" method="POST" name="change_password_form"> <form action="{{ url_for_security('change_password') }}" method="POST" name="change_password_form">
{{ change_password_form.hidden_tag() }} {{ change_password_form.hidden_tag() }}
@ -7,7 +7,7 @@
{{ render_field_with_errors(change_password_form.password, "password") }} {{ render_field_with_errors(change_password_form.password, "password") }}
{{ render_field_with_errors(change_password_form.new_password, "password") }} {{ render_field_with_errors(change_password_form.new_password, "password") }}
{{ render_field_with_errors(change_password_form.new_password_confirm, "password") }} {{ render_field_with_errors(change_password_form.new_password_confirm, "password") }}
<input class="btn btn-lg btn-success btn-block" type="submit" value="Change Password"> <input class="btn btn-lg btn-success btn-block" type="submit" value="{{ _('Change Password') }}">
</fieldset> </fieldset>
</form> </form>
{% endblock %} {% endblock %}

View File

@ -1,12 +1,12 @@
{% extends "security/panel.html" %} {% extends "security/panel.html" %}
{% block panel_title %}Recover pgAdmin Password{% endblock %} {% block panel_title %}{{ _('Recover %(appname)s Password', appname=config.APP_NAME) }}{% endblock %}
{% block panel_body %} {% block panel_body %}
<p>Enter the email address for the user account you wish to recover the password for:</p> <p>{{ _('Enter the email address for the user account you wish to recover the password for:') }}</p>
<form action="{{ url_for_security('forgot_password') }}" method="POST" name="forgot_password_form"> <form action="{{ url_for_security('forgot_password') }}" method="POST" name="forgot_password_form">
{{ forgot_password_form.hidden_tag() }} {{ forgot_password_form.hidden_tag() }}
<fieldset> <fieldset>
{{ render_field_with_errors(forgot_password_form.email, "text") }} {{ render_field_with_errors(forgot_password_form.email, "text") }}
<input class="btn btn-lg btn-success btn-block" type="submit" value="Recover Password"> <input class="btn btn-lg btn-success btn-block" type="submit" value="{{ _('Recover Password') }}">
</fieldset> </fieldset>
</form> </form>
{% endblock %} {% endblock %}

View File

@ -1,13 +1,13 @@
{% extends "security/panel.html" %} {% extends "security/panel.html" %}
{% block panel_title %}pgAdmin Login{% endblock %} {% block panel_title %}{{ _('%(appname)s Login', appname=config.APP_NAME) }}{% endblock %}
{% block panel_body %} {% block panel_body %}
<form action="{{ url_for_security('login') }}" method="POST" name="login_user_form"> <form action="{{ url_for_security('login') }}" method="POST" name="login_user_form">
{{ login_user_form.hidden_tag() }} {{ login_user_form.hidden_tag() }}
<fieldset> <fieldset>
{{ render_field_with_errors(login_user_form.email, "text") }} {{ render_field_with_errors(login_user_form.email, "text") }}
{{ render_field_with_errors(login_user_form.password, "password") }} {{ render_field_with_errors(login_user_form.password, "password") }}
<input class="btn btn-lg btn-success btn-block" type="submit" value="Login"> <input class="btn btn-lg btn-success btn-block" type="submit" value="{{ _('Login') }}">
</fieldset> </fieldset>
</form> </form>
<span class="help-block">Forgotten your <a href="{{ url_for('security.forgot_password') }}">password</a>?</span> <span class="help-block">{{ _('Forgotten your <a href="%(url)s">password</a>?', url=url_for('security.forgot_password')) }}</span>
{% endblock %} {% endblock %}

View File

@ -3,7 +3,7 @@
<div style="position: fixed; top: 20px; right: 20px; width: 400px; z-index: 9999"> <div style="position: fixed; top: 20px; right: 20px; width: 400px; z-index: 9999">
{% for category, message in messages %} {% for category, message in messages %}
<div class="alert alert-{{ category }} alert-dismissible" role="alert"> <div class="alert alert-{{ category }} alert-dismissible" role="alert">
<button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">&times;</span></button> <button type="button" class="close" data-dismiss="alert" aria-label="{{ _('Close') }}"><span aria-hidden="true">&times;</span></button>
{{ message }} {{ message }}
</div> </div>
{% endfor %} {% endfor %}

View File

@ -1,12 +1,12 @@
{% extends "security/panel.html" %} {% extends "security/panel.html" %}
{% block panel_title %}pgAdmin Password Reset{% endblock %} {% block panel_title %}{{ _('%(appname)s Password Reset', appname=config.APP_NAME) }}{% endblock %}
{% block panel_body %} {% block panel_body %}
<form action="{{ url_for_security('reset_password', token=reset_password_token) }}" method="POST" name="reset_password_form"> <form action="{{ url_for_security('reset_password', token=reset_password_token) }}" method="POST" name="reset_password_form">
{{ reset_password_form.hidden_tag() }} {{ reset_password_form.hidden_tag() }}
<fieldset> <fieldset>
{{ render_field_with_errors(reset_password_form.password, "password") }} {{ render_field_with_errors(reset_password_form.password, "password") }}
{{ render_field_with_errors(reset_password_form.password_confirm, "password") }} {{ render_field_with_errors(reset_password_form.password_confirm, "password") }}
<input class="btn btn-lg btn-success btn-block" type="submit" value="Reset Password"> <input class="btn btn-lg btn-success btn-block" type="submit" value="{{ _('Reset Password') }}">
</fieldset> </fieldset>
</form> </form>
{% endblock %} {% endblock %}

View File

@ -1,5 +1,5 @@
{% block watermark %} {% block watermark %}
<div style="position: fixed; bottom: 0; right: 0;"> <div style="position: fixed; bottom: 0; right: 0;">
<img src="{{ url_for('static', filename='img/logo-right-256.png') }}" alt="pgAdmin 4"> <img src="{{ url_for('static', filename='img/logo-right-256.png') }}" alt="{{ config.APP_NAME }}">
</div> </div>
{% endblock %} {% endblock %}

View File

@ -10,17 +10,18 @@
"""Browser integration functions for the Test module.""" """Browser integration functions for the Test module."""
from flask import render_template, url_for from flask import render_template, url_for
from flask.ext.babel import gettext
def get_file_menu_items(): def get_file_menu_items():
"""Return a (set) of dicts of file menu items, with name, priority, URL, """Return a (set) of dicts of file menu items, with name, priority, URL,
target and onclick code.""" target and onclick code."""
return [ return [
{'name': 'mnu_generate_test_html', 'label': 'Generated Test HTML', 'priority': 100, 'url': url_for('test.generated')}, {'name': 'mnu_generate_test_html', 'label': gettext('Generated Test HTML'), 'priority': 100, 'url': url_for('test.generated')},
{'name': 'mnu_test_alert', 'label': 'Test Alert', 'priority': 200, 'url': '#', 'onclick': 'test_alert()'}, {'name': 'mnu_test_alert', 'label': gettext('Test Alert'), 'priority': 200, 'url': '#', 'onclick': 'test_alert()'},
{'name': 'mnu_test_confirm', 'label': 'Test Confirm', 'priority': 300, 'url': '#', 'onclick': 'test_confirm()'}, {'name': 'mnu_test_confirm', 'label': gettext('Test Confirm'), 'priority': 300, 'url': '#', 'onclick': 'test_confirm()'},
{'name': 'mnu_test_dialog', 'label': 'Test Dialog', 'priority': 400, 'url': '#', 'onclick': 'test_dialog()'}, {'name': 'mnu_test_dialog', 'label': gettext('Test Dialog'), 'priority': 400, 'url': '#', 'onclick': 'test_dialog()'},
{'name': 'mnu_test_prompt', 'label': 'Test Prompt', 'priority': 500, 'url': '#', 'onclick': 'test_prompt()'}, {'name': 'mnu_test_prompt', 'label': gettext('Test Prompt'), 'priority': 500, 'url': '#', 'onclick': 'test_prompt()'},
{'name': 'mnu_test_notifier', 'label': 'Test Notifier', 'priority': 600, 'url': '#', 'onclick': 'test_notifier()'}, {'name': 'mnu_test_notifier', 'label': gettext('Test Notifier'), 'priority': 600, 'url': '#', 'onclick': 'test_notifier()'},
] ]
def get_scripts(): def get_scripts():

Binary file not shown.

View File

@ -0,0 +1,211 @@
# French translations for PROJECT.
# Copyright (C) 2015 ORGANIZATION
# This file is distributed under the same license as the PROJECT project.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2015.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2015-02-25 16:30+0000\n"
"PO-Revision-Date: 2015-02-25 17:04+0000\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: fr <LL@li.org>\n"
"Plural-Forms: nplurals=2; plural=(n > 1)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 1.3\n"
#: pgadmin/about/views.py:35
msgid "Server"
msgstr ""
#: pgadmin/about/views.py:37
msgid "Desktop"
msgstr ""
#: pgadmin/about/templates/about/index.html:2
msgid "Version"
msgstr ""
#: pgadmin/about/templates/about/index.html:6
msgid "Copyright"
msgstr ""
#: pgadmin/about/templates/about/index.html:10
msgid "Python Version"
msgstr ""
#: pgadmin/about/templates/about/index.html:14
msgid "Flask Version"
msgstr ""
#: pgadmin/about/templates/about/index.html:18
msgid "Application Mode"
msgstr ""
#: pgadmin/about/templates/about/index.html:22
msgid "Current User"
msgstr ""
#: pgadmin/browser/nodes/server_groups/hooks.py:34
msgid "Add a server group..."
msgstr ""
#: pgadmin/browser/nodes/server_groups/hooks.py:35
#: pgadmin/browser/nodes/server_groups/hooks.py:43
msgid "Delete server group"
msgstr ""
#: pgadmin/browser/nodes/server_groups/hooks.py:36
#: pgadmin/browser/nodes/server_groups/hooks.py:44
msgid "Rename server group..."
msgstr ""
#: pgadmin/browser/nodes/server_groups/views.py:48
msgid "No server group name was specified"
msgstr ""
#: pgadmin/browser/nodes/server_groups/views.py:73
#: pgadmin/browser/nodes/server_groups/views.py:104
msgid "The specified server group could not be found."
msgstr ""
#: pgadmin/browser/nodes/server_groups/views.py:84
msgid "No server group was specified."
msgstr ""
#: pgadmin/browser/nodes/server_groups/views.py:115
msgid "No server group was specified."
msgstr ""
#: pgadmin/browser/templates/browser/body.html:11
msgid "Dashboard"
msgstr ""
#: pgadmin/browser/templates/browser/body.html:12
msgid "Properties"
msgstr ""
#: pgadmin/browser/templates/browser/body.html:13
msgid "Statistics"
msgstr ""
#: pgadmin/browser/templates/browser/body.html:14
msgid "Dependencies"
msgstr ""
#: pgadmin/browser/templates/browser/body.html:15
msgid "Dependents"
msgstr ""
#: pgadmin/browser/templates/browser/index.html:8
msgid "Toggle navigation"
msgstr ""
#: pgadmin/browser/templates/browser/index.html:21
msgid "File"
msgstr ""
#: pgadmin/browser/templates/browser/index.html:28
msgid "Edit"
msgstr ""
#: pgadmin/browser/templates/browser/index.html:35
msgid "Tools"
msgstr ""
#: pgadmin/browser/templates/browser/index.html:42
msgid "Management"
msgstr ""
#: pgadmin/browser/templates/browser/index.html:49
msgid "Help"
msgstr ""
#: pgadmin/browser/templates/browser/index.html:61
#: pgadmin/templates/security/change_password.html:10
msgid "Change Password"
msgstr ""
#: pgadmin/browser/templates/browser/index.html:63
msgid "Logout"
msgstr ""
#: pgadmin/help/hooks.py:21
msgid "Contents"
msgstr ""
#: pgadmin/templates/security/change_password.html:2
#, python-format
msgid "%(appname)s Password Change"
msgstr ""
#: pgadmin/templates/security/forgot_password.html:2
#, python-format
msgid "Recover %(appname)s Password"
msgstr ""
#: pgadmin/templates/security/forgot_password.html:4
msgid ""
"Enter the email address for the user account you wish to recover the "
"password for:"
msgstr ""
#: pgadmin/templates/security/forgot_password.html:9
msgid "Recover Password"
msgstr ""
#: pgadmin/templates/security/login_user.html:2
#, python-format
msgid "%(appname)s Login"
msgstr ""
#: pgadmin/templates/security/login_user.html:9
msgid "Login"
msgstr ""
#: pgadmin/templates/security/login_user.html:12
#, python-format
msgid "Forgotten your <a href=\"%(url)s\">password</a>?"
msgstr ""
#: pgadmin/templates/security/messages.html:6
msgid "Close"
msgstr ""
#: pgadmin/templates/security/reset_password.html:2
#, python-format
msgid "%(appname)s Password Reset"
msgstr ""
#: pgadmin/templates/security/reset_password.html:9
msgid "Reset Password"
msgstr ""
#: pgadmin/test/hooks.py:19
msgid "Generated Test HTML"
msgstr ""
#: pgadmin/test/hooks.py:20
msgid "Test Alert"
msgstr ""
#: pgadmin/test/hooks.py:21
msgid "Test Confirm"
msgstr ""
#: pgadmin/test/hooks.py:22
msgid "Test Dialog"
msgstr ""
#: pgadmin/test/hooks.py:23
msgid "Test Prompt"
msgstr ""
#: pgadmin/test/hooks.py:24
msgid "Test Notifier"
msgstr ""