Commit d14d8fa5 authored by Benjamin "Ziirish" SANS's avatar Benjamin "Ziirish" SANS
Browse files

add: support translations

parent b27a836b
Pipeline #742 failed with stage
in 2 minutes and 50 seconds
*.pyc
*.swp
*.mo
burpui-dev.cfg*
burpui/RELEASE
devel.sh
......
[python: **.py]
encoding = utf-8
[jinja2: **/templates/**.html]
encoding = utf-8
extensions=jinja2.ext.autoescape,jinja2.ext.with_
[jinja2: **/templates/**.js]
encoding = utf-8
extensions=jinja2.ext.autoescape,jinja2.ext.with_
......@@ -179,6 +179,7 @@ def init(conf=None, verbose=0, logfile=None, gunicorn=True, unittest=False, debu
:returns: A :class:`burpui.server.BUIServer` object
"""
from flask import g
from flask_login import LoginManager
from flask_bower import Bower
from .utils import basic_login_from_request, ReverseProxied, lookup_file
......@@ -186,6 +187,7 @@ def init(conf=None, verbose=0, logfile=None, gunicorn=True, unittest=False, debu
from .routes import view
from .api import api, apibp
from .ext.cache import cache
from .ext.i18n import babel, get_locale
logger = logging.getLogger('burp-ui')
......@@ -261,6 +263,9 @@ def init(conf=None, verbose=0, logfile=None, gunicorn=True, unittest=False, debu
app.gunicorn = gunicorn
app.config['CFG'] = None
# Some config
# FIXME: strange behavior when bundling errors
# app.config['BUNDLE_ERRORS'] = True
......@@ -391,6 +396,10 @@ def init(conf=None, verbose=0, logfile=None, gunicorn=True, unittest=False, debu
force_scheduling_now()
except:
pass
# Initialize i18n
babel.init_app(app)
# Create SQLAlchemy if enabled
create_db(app)
......@@ -447,6 +456,10 @@ def init(conf=None, verbose=0, logfile=None, gunicorn=True, unittest=False, debu
if app.auth != 'none':
return basic_login_from_request(request, app)
@app.before_request
def before_request():
g.locale = get_locale()
return app
......
# -*- coding: utf8 -*-
"""
.. module:: burpui.ext.i18n
:platform: Unix
:synopsis: Burp-UI external Internationalization module.
.. moduleauthor:: Ziirish <hi+burpui@ziirish.me>
"""
from flask import request
from flask_babel import Babel
from flask_login import current_user
from ..config import config
babel = Babel()
LANGUAGES = {
'en': 'English',
'fr': 'Français',
}
config['LANGUAGES'] = LANGUAGES
@babel.localeselector
def get_locale():
locale = None
if current_user and not current_user.is_anonymous:
locale = getattr(current_user, 'language', None)
return locale or request.accept_languages.best_match(config['LANGUAGES'].keys())
......@@ -7,12 +7,15 @@
.. moduleauthor:: Ziirish <hi+burpui@ziirish.me>
"""
from .ext.i18n import LANGUAGES, get_locale
from flask_wtf import Form
from wtforms import TextField, PasswordField, BooleanField, validators
from flask_babel import gettext
from wtforms import TextField, PasswordField, BooleanField, SelectField, validators
class LoginForm(Form):
username = TextField('Username', [validators.Length(min=2, max=25)])
password = PasswordField('Password', [validators.Required()])
remember = BooleanField('Remember me', [validators.Optional()])
username = TextField(gettext('Username'), [validators.Length(min=2, max=25)])
password = PasswordField(gettext('Password'), [validators.Required()])
language = SelectField(gettext('Language'), choices=LANGUAGES.items(), default=get_locale)
remember = BooleanField(gettext('Remember me'), [validators.Optional()])
......@@ -44,6 +44,25 @@ def create_manager():
manager, migrate, app = create_manager()
@manager.command
def init_translation(language):
os.system('pybabel extract -F babel.cfg -k lazy_gettext -o messages.pot burpui')
os.system('pybabel init -i messages.pot -d burpui/translations -l {}'.format(language))
os.unlink('messages.pot')
@manager.command
def update_translation():
os.system('pybabel extract -F babel.cfg -k lazy_gettext -o messages.pot burpui')
os.system('pybabel update -i messages.pot -d burpui/translations')
os.unlink('messages.pot')
@manager.command
def compile_translation():
os.system('pybabel compile -f -d burpui/translations')
@manager.command
def create_user(name, backend='BASIC', password=None, ask=False):
print('[*] Adding \'{}\' user...'.format(name))
......
......@@ -49,6 +49,7 @@ class UserHandler(BUIuser):
sess = session._get_current_object()
self.active = False
self.authenticated = sess.get('authenticated', False)
self.language = sess.get('language', None)
self.backends = backends
self.back = None
self.name = name
......@@ -83,6 +84,7 @@ class UserHandler(BUIuser):
self.authenticated = self.real.login(passwd)
sess = session._get_current_object()
sess['authenticated'] = self.authenticated
sess['language'] = self.language
return self.authenticated
def get_id(self):
......
......@@ -12,12 +12,14 @@ import math
from flask import request, render_template, redirect, url_for, abort, \
flash, Blueprint as FlaskBlueprint, session, current_app
from flask_login import login_user, login_required, logout_user, current_user
from flask_babel import gettext
from .server import BUIServer # noqa
from ._compat import quote
from .forms import LoginForm
from .exceptions import BUIserverException
from .utils import human_readable as _hr
from .ext.i18n import LANGUAGES, get_locale
class Blueprint(FlaskBlueprint):
......@@ -164,14 +166,14 @@ def live_monitor(server=None, name=None):
bui.cli.is_one_backup_running()
if bui.standalone:
if not bui.cli.running:
flash('Sorry, there are no running backups', 'warning')
flash(gettext('Sorry, there are no running backups'), 'warning')
return redirect(url_for('.home'))
else:
run = False
for a in bui.cli.servers:
run = run or (a in bui.cli.running and bui.cli.running[a])
if not run:
flash('Sorry, there are no running backups', 'warning')
flash(gettext('Sorry, there are no running backups'), 'warning')
return redirect(url_for('.home'))
return render_template(
......@@ -191,7 +193,9 @@ def edit_server_initiated_restore(server=None, name=None):
to = None
if not data or not data['found']:
flash(
'Sorry, there are no restore file found for this client',
gettext(
'Sorry, there are no restore file found for this client'
),
'warning'
)
return redirect(url_for('.home'))
......@@ -227,8 +231,11 @@ def client_browse(server=None, name=None, backup=None, encrypted=None,
edit = bui.cli.is_server_restore(to, server)
if not edit or not edit['found']:
flash(
'Sorry, there are no restore file found for this client',
gettext(
'Sorry, there are no restore file found for this client'
),
'warning'
)
edit = None
else:
......@@ -369,14 +376,16 @@ def servers_report():
@view.route('/login', methods=['POST', 'GET'])
def login():
form = LoginForm(request.form)
if form.validate_on_submit():
user = bui.uhandler.user(form.username.data)
user.language = form.language.data
if user.is_active and user.login(form.password.data):
login_user(user, remember=form.remember.data)
flash('Logged in successfully', 'success')
flash(gettext('Logged in successfully'), 'success')
return redirect(request.args.get("next") or url_for('.home'))
else:
flash('Wrong username or password', 'danger')
flash(gettext('Wrong username or password'), 'danger')
return render_template('login.html', form=form, login=True)
......@@ -386,6 +395,8 @@ def logout():
sess = session._get_current_object()
if 'authenticated' in sess:
sess.pop('authenticated')
if 'language' in sess:
sess.pop('language')
logout_user()
return redirect(url_for('.home'))
......
......@@ -5,12 +5,12 @@
{% include "small_topbar.html" %}
<ul class="breadcrumb" style="margin-bottom: 5px;">
{% if server -%}
<li><a href="{{ url_for('view.home') }}">Home</a></li>
<li><a href="{{ url_for('view.home') }}">{{ _('Home') }}</a></li>
<li><a href="{{ url_for('view.clients', server=server) }}">{{ server }} clients</a></li>
<li class="active">{{ cname }} overview</li>
<li class="active">{{ cname }} {{ _('overview') }}</li>
{% else -%}
<li><a href="{{ url_for('view.home') }}">Home</a></li>
<li class="active">{{ cname }} overview</li>
<li><a href="{{ url_for('view.home') }}">{{ _('Home') }}</a></li>
<li class="active">{{ cname }} {{ _('overview') }}</li>
{% endif -%}
</ul>
<br />
......@@ -18,22 +18,22 @@
<h2 class="sub-header">Backups</h2>
<p>
Toggle column: <a class="toggle-vis" data-column="0" href="#">Number</a> -
Toggle column: <a class="toggle-vis" data-column="0" href="#">{{ _('Number') }}</a> -
<a class="toggle-vis" data-column="1" href="#">Date</a> -
<a class="toggle-vis" data-column="2" href="#">Bytes received</a> -
<a class="toggle-vis" data-column="3" href="#">Estimated size</a> -
<a class="toggle-vis" data-column="4" href="#">Deletable</a> -
<a class="toggle-vis" data-column="2" href="#">{{ _('Bytes received') }}</a> -
<a class="toggle-vis" data-column="3" href="#">{{ _('Estimated size') }}</a> -
<a class="toggle-vis" data-column="4" href="#">{{ _('Deletable') }}</a> -
<a class="toggle-vis" data-column="5" href="#">Status</a>
</p>
<div class="table-responsive">
<table class="table table-striped table-hover nowrap" id="table-client" width="100%">
<thead>
<tr>
<th>Number</th>
<th>{{ _('Number') }}</th>
<th class="desktop">Date</th>
<th class="desktop">Bytes received</th>
<th class="desktop">Estimated size</th>
<th class="desktop">Deletable</th>
<th class="desktop">{{ _('Bytes received') }}</th>
<th class="desktop">{{ _('Estimated size') }}</th>
<th class="desktop">{{ _('Deletable') }}</th>
<th class="desktop">Status</th>
</tr>
</thead>
......@@ -41,24 +41,24 @@
</tbody>
</table>
<div class="alert alert-dismissable alert-danger" id="client-alert" style="display: none;">
<span class="glyphicon glyphicon-exclamation-sign"></span> <strong>Sorry!</strong> There are no backups for this client.
<span class="glyphicon glyphicon-exclamation-sign"></span> <strong>{{ _('Sorry!') }}</strong> {{ _('There are no backups for this client.') }}
</div>
</div>
<br />
<div id="left" class="form-inline col-md-6">
<div class="edit-restore" style="display: none;">
<a href="{{ url_for('view.edit_server_initiated_restore', name=cname, server=server) }}" id="btn-edit-restore" class="btn btn-info"><span class="glyphicon glyphicon-pencil"></span>&nbsp;Edit restore</a>
<a href="{{ url_for('view.edit_server_initiated_restore', name=cname, server=server) }}" id="btn-edit-restore" class="btn btn-info"><span class="glyphicon glyphicon-pencil"></span>&nbsp;{{ _('Edit restore') }}</a>
</div>
<div class="scheduled-backup">
<button type="button" id="btn-schedule-backup" class="btn btn-info"><span class="glyphicon glyphicon-time"></span>&nbsp;Schedule backup</button>
<button type="button" id="btn-schedule-backup" class="btn btn-info"><span class="glyphicon glyphicon-time"></span>&nbsp;{{ _('Schedule backup') }}</button>
</div>
</div>
<div id="right" class="form-inline col-md-6 text-right">
<div class="edit-restore" style="display: none;">
<button type="button" id="btn-cancel-restore" class="btn btn-danger"><span class="glyphicon glyphicon-trash"></span>&nbsp;Cancel restore</button>
<button type="button" id="btn-cancel-restore" class="btn btn-danger"><span class="glyphicon glyphicon-trash"></span>&nbsp;{{ _('Cancel restore') }}</button>
</div>
<div class="cancel-backup" style="display: none;">
<button type="button" id="btn-cancel-backup" class="btn btn-danger"><span class="glyphicon glyphicon-trash"></span>&nbsp;Cancel backup</button>
<button type="button" id="btn-cancel-backup" class="btn btn-danger"><span class="glyphicon glyphicon-trash"></span>&nbsp;{{ _('Cancel backup') }}</button>
</div>
</div>
</div>
......
......@@ -5,10 +5,10 @@
{% include "small_topbar.html" %}
<ul class="breadcrumb" style="margin-bottom: 5px;">
{% if server -%}
<li><a href="{{ url_for('view.home') }}">Home</a></li>
<li><a href="{{ url_for('view.home') }}">{{ _('Home') }}</a></li>
<li class="active">{{ server }} clients</li>
{% else -%}
<li class="active">Home</li>
<li class="active">{{ _('Home') }}</li>
{% endif -%}
</ul>
<br />
......@@ -19,9 +19,9 @@
<table class="table table-striped table-hover nowrap" id="table-clients" width="100%">
<thead>
<tr>
<th>Name</th>
<th class="desktop">State</th>
<th class="desktop">Last Backup</th>
<th>{{ _('Name') }}</th>
<th class="desktop">{{ _('State') }}</th>
<th class="desktop">{{ _('Last Backup') }}</th>
</tr>
</thead>
<tbody>
......
......@@ -56,6 +56,12 @@
<!-- Typeadhead + Bloodhound Javascript
================================================== -->
<script src="{{ url_for('bower.static', filename='typeahead.js/dist/typeahead.bundle.min.js') }}"></script>
<!-- MomentJS
================================================== -->
<script src="{{ url_for('bower.static', filename='moment/min/moment.min.js') }}"></script>
{% if g.locale != 'en' -%}
<script src="{{ url_for('bower.static', filename='moment/locale/{}.js'.format(g.locale)) }}"></script>
{% endif -%}
{% if report -%}
<!-- d3 + nvd3 Javascript
================================================== -->
......@@ -101,7 +107,6 @@
================================================== -->
<script src="{{ url_for('bower.static', filename='angular-bootstrap/ui-bootstrap.min.js') }}"></script>
<script src="{{ url_for('bower.static', filename='angular-bootstrap/ui-bootstrap-tpls.min.js') }}"></script>
<script src="{{ url_for('bower.static', filename='moment/min/moment.min.js') }}"></script>
<script src="{{ url_for('bower.static', filename='angular-ui-calendar/src/calendar.js') }}"></script>
<script src="{{ url_for('bower.static', filename='fullcalendar/dist/fullcalendar.min.js') }}"></script>
<script src="{{ url_for('bower.static', filename='fullcalendar/dist/gcal.js') }}"></script>
......
......@@ -4,9 +4,10 @@
{% include "notifications.html" %}
<div class="main">
<div class="wrapper">
{% call macros.render_form(form, action_text='Login', class_='form-signin', btn_class='btn btn-info btn-lg btn-primary btn-block') %}
{{ macros.render_field(form.username, placeholder='Username', type='text', autofocus=true) }}
{{ macros.render_field(form.password, placeholder='Password', type='password') }}
{% call macros.render_form(form, action_text=_('Login'), class_='form-signin', btn_class='btn btn-info btn-lg btn-primary btn-block') %}
{{ macros.render_field(form.username, placeholder=_('Username'), type='text', autofocus=true) }}
{{ macros.render_field(form.password, placeholder=_('Password'), type='password') }}
{{ macros.render_field(form.language) }}
{{ macros.render_checkbox_field(form.remember) }}
{% endcall %}
</div>
......
......@@ -11,7 +11,6 @@
{% macro render_field(field, label_visible=true) -%}
<div class="form-group {% if field.errors %}has-error{% endif %} {{ kwargs.pop('class_', '') }}">
{# {% if (field.id != 'csrf_token' or field.type != 'HiddenField' or field.type !='CSRFTokenField') and label_visible %} #}
{% if (field.id != 'csrf_token' or field.type != 'HiddenField') %}
<label for="{{ field.id }}" class="control-label">{{ field.label }}</label>
{% endif %}
......@@ -88,6 +87,8 @@
{{ render_checkbox_field(f) }}
{% elif f.type == 'RadioField' %}
{{ render_radio_field(f) }}
{% elif f.type == 'SelectField' %}
{{ render_select_field(f) }}
{% else %}
{{ render_field(f) }}
{% endif %}
......
......@@ -4,16 +4,16 @@
<div class="col-sm-9 col-sm-offset-3 col-md-10 col-md-offset-2 main">
{% include "small_topbar.html" %}
<ul class="breadcrumb" style="margin-bottom: 5px;">
<li class="active">Home</li>
<li class="active">{{ _('Home') }}</li>
</ul>
<br />
<h1 class="page-header">Servers</h1>
<h1 class="page-header">{{ _('Servers') }}</h1>
<div class="table-responsive">
<table class="table table-striped table-hover nowrap" id="table-servers" width="100%">
<thead>
<tr>
<th>Name</th>
<th>{{ _('Name') }}</th>
<th class="desktop">Clients</th>
<th class="desktop">Status</th>
</tr>
......
......@@ -11,32 +11,32 @@
<ul class="nav nav-sidebar">
<li {% if overview %}class="active"{% endif %}>
{% if backup %}
<a href="{{ url_for('view.client_browse', name=cname, server=server, backup=nbackup) }}"><span class="glyphicon glyphicon-th"></span>&nbsp;Overview</a>
<a href="{{ url_for('view.client_browse', name=cname, server=server, backup=nbackup) }}"><span class="glyphicon glyphicon-th"></span>&nbsp;{{ _('Overview') }}</a>
{% elif client %}
<a href="{{ url_for('view.client', name=cname, server=server) }}"><span class="glyphicon glyphicon-th"></span>&nbsp;Overview</a>
<a href="{{ url_for('view.client', name=cname, server=server) }}"><span class="glyphicon glyphicon-th"></span>&nbsp;{{ _('Overview') }}</a>
{% elif clients %}
<a href="{{ url_for('view.clients', server=server) }}"><span class="glyphicon glyphicon-th"></span>&nbsp;Overview</a>
<a href="{{ url_for('view.clients', server=server) }}"><span class="glyphicon glyphicon-th"></span>&nbsp;{{ _('Overview') }}</a>
{% elif servers %}
<a href="{{ url_for('view.servers') }}"><span class="glyphicon glyphicon-th"></span>&nbsp;Overview</a>
<a href="{{ url_for('view.servers') }}"><span class="glyphicon glyphicon-th"></span>&nbsp;{{ _('Overview') }}</a>
{% else %}
<a href="#"><span class="glyphicon glyphicon-th"></span>&nbsp;Overview</a>
<a href="#"><span class="glyphicon glyphicon-th"></span>&nbsp;{{ _('Overview') }}</a>
{% endif %}
</li>
<li {% if report %}class="active"{% endif %}>
{% if client and not backup %}
<a href="{{ url_for('view.client_report', name=cname, server=server) }}"><span class="glyphicon glyphicon-stats"></span>&nbsp;Reports</a>
<a href="{{ url_for('view.client_report', name=cname, server=server) }}"><span class="glyphicon glyphicon-stats"></span>&nbsp;{{ _('Reports') }}</a>
{% elif clients %}
<a href="{{ url_for('view.clients_report', server=server) }}"><span class="glyphicon glyphicon-stats"></span>&nbsp;Reports</a>
<a href="{{ url_for('view.clients_report', server=server) }}"><span class="glyphicon glyphicon-stats"></span>&nbsp;{{ _('Reports') }}</a>
{% elif backup %}
<a href="{{ url_for('view.backup_report', name=cname, backup=nbackup, server=server) }}"><span class="glyphicon glyphicon-stats"></span>&nbsp;Reports</a>
<a href="{{ url_for('view.backup_report', name=cname, backup=nbackup, server=server) }}"><span class="glyphicon glyphicon-stats"></span>&nbsp;{{ _('Reports') }}</a>
{% elif servers %}
<a href="{{ url_for('view.servers_report') }}"><span class="glyphicon glyphicon-stats"></span>&nbsp;Reports</a>
<a href="{{ url_for('view.servers_report') }}"><span class="glyphicon glyphicon-stats"></span>&nbsp;{{ _('Reports') }}</a>
{% else %}
<a href="#"><span class="glyphicon glyphicon-stats"></span>&nbsp;Reports</a>
<a href="#"><span class="glyphicon glyphicon-stats"></span>&nbsp;{{ _('Reports') }}</a>
{% endif %}
</li>
<li {% if calendar %} class="active"{% endif %}>
<a href="{{ url_for('view.calendar', client=cname, server=server) }}"><span class="glyphicon glyphicon-calendar"></span>&nbsp;Calendar</a>
<a href="{{ url_for('view.calendar', client=cname, server=server) }}"><span class="glyphicon glyphicon-calendar"></span>&nbsp;{{ _('Calendar') }}</a>
</li>
</ul>
{% endif -%}
......
<div id="navbar-config">
<h4>Configuration navigation</h4>
<h4>{{ _('Configuration navigation') }}</h4>
<ul class="nav nav-sidebar bui-scrollspy">
<li class="active" data-target="#boolean"><a href="#boolean">Booleans</a></li>
<li data-target="#string"><a href="#string">Strings</a></li>
......@@ -8,7 +8,7 @@
<li data-target="#multi"><a href="#multi">Multi</a></li>
<li data-target="#includes_source"><a href="#includes_source">Includes</a></li>
</ul>
<h4>Client to configure</h4>
<h4>{{ _('Client to configure') }}</h4>
<ul class="nav nav-sidebar" ng-cloak>
{% raw %}
<li>
......@@ -28,7 +28,7 @@
<li>
<form action="{{ url_for('api.new_client', server=server) }}" method="POST" ng-submit="createClient($event)">
<div class="input-group">
<input class="form-control" type="text" name="newclient" id="newclient" placeholder="Create new client">
<input class="form-control" type="text" name="newclient" id="newclient" placeholder="{{ _('Create new client') }}">
<span class="input-group-btn">
<button class="btn btn-success" type="submit"><span class="glyphicon glyphicon-plus"></span></button>
</span>
......
......@@ -2,7 +2,7 @@
<div class="container-fluid">
<div class="navbar-header">
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
<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>
......@@ -13,14 +13,14 @@
<ul class="nav navbar-nav navbar-right">
<li class="detail {% if about %}active{% endif %}">
<a href="{{ url_for('view.about') }}">
<span class="glyphicon glyphicon-question-sign"></span><span class="dtl">&nbsp;About</span>
<span class="glyphicon glyphicon-question-sign"></span><span class="dtl">&nbsp;{{ _('About') }}</span>
</a>
</li>
{% if not login -%}
{% if not config.STANDALONE -%}
<li class="detail {% if not server and not about %}active{% endif %}">
<a href="{{ url_for('view.home') }}">
<span class="glyphicon glyphicon-hdd"></span><span class="dtl">&nbsp;Servers</span>
<span class="glyphicon glyphicon-hdd"></span><span class="dtl">&nbsp;{{ _('Servers') }}</span>
</a>
</li>
{% if server -%}
......@@ -40,32 +40,32 @@
{% if config.STANDALONE or server -%}
<li class="detail {% if settings %}active{% endif %}">
<a href="{{ url_for('view.settings', server=server) }}">
<span class="glyphicon glyphicon-wrench"></span><span class="dtl">&nbsp;Settings</span>
<span class="glyphicon glyphicon-wrench"></span><span class="dtl">&nbsp;{{ _('Settings') }}</span>
</a>
</li>
{% endif -%}
<li class="detail {% if live %}active{% endif %}">
<a href="{{ url_for('view.live_monitor', server=server) }}">
<span id="toblink" class="glyphicon glyphicon-screenshot"></span><span class="dtl">&nbsp;Live monitor</span>
<span id="toblink" class="glyphicon glyphicon-screenshot"></span><span class="dtl">&nbsp;{{ _('Live monitor') }}</span>
</a>
</li>
{% if current_user and current_user.is_authenticated -%}
<li class="detail">
<a href="{{ url_for('view.logout') }}">
<span class="glyphicon glyphicon-log-out"></span><span class="dtl">&nbsp;Logout<small>({{ current_user.get_id() }})</small></span>
<span class="glyphicon glyphicon-log-out"></span><span class="dtl">&nbsp;{{ _('Logout') }}<small>({{ current_user.get_id() }})</small></span>
</a>
</li>
{% endif -%}
<li {% if live %}ng-click="refresh"{% endif %}>
<a id="refresh" href="#">
<span class="glyphicon glyphicon-refresh"></span><span class="hidden-md hidden-lg">&nbsp;Refresh</span>
<span class="glyphicon glyphicon-refresh"></span><span class="hidden-md hidden-lg">&nbsp;{{ _('Refresh') }}</span>
</a>
</li>
{% endif -%}
</ul>
{% if not login -%}
<form class="navbar-form navbar-right" id="search">
<input type="text" class="form-control" id="input-client" placeholder="Search client..." autocomplete="off">
<input type="text" class="form-control" id="input-client" placeholder="{{ _('Search client...') }}" autocomplete="off">
</form>
{% endif -%}
</div>
......
# French translations for Burp-UI.
# Copyright (C) 2016 ORGANIZATION
# This file is distributed under the same license as the PROJECT project.
# Ziirish <hi+burpui@ziirish.me>, 2016.
#
msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2016-08-25 17:17+0200\n"
"PO-Revision-Date: 2016-08-25 15:19+0200\n"
"Last-Translator: Ziirish <hi+burpui@ziirish.me>\n"
"Language: fr\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 2.3.4\n"
#: burpui/forms.py:18 burpui/templates/login.html:8
msgid "Username"
msgstr "Utilisateur"
#: burpui/forms.py:19 burpui/templates/login.html:9
msgid "Password"
msgstr "Mot de passe"
#: burpui/forms.py:20
msgid "Language"
msgstr "Langue"
#: burpui/forms.py:21
msgid "Remember me"
msgstr "Rester connecté"
#: burpui/routes.py:169 burpui/routes.py:176
msgid "Sorry, there are no running backups"
msgstr "Désolé, il n'y a pas de backups"
#: burpui/routes.py:196 burpui/routes.py:234
msgid "Sorry, there are no restore file found for this client"
msgstr "Désolé, il n'y a pas restoration prévue pour ce client"
#: burpui/routes.py:385
msgid "Logged in successfully"
msgstr "Connecté avec succès"
#: burpui/routes.py:388
msgid "Wrong username or password"
msgstr "Mauvais nom d'utilisateur ou mot de passe"
#: burpui/templates/client.html:8 burpui/templates/client.html:12
#: burpui/templates/clients.html:8 burpui/templates/clients.html:11
#: burpui/templates/servers.html:7
msgid "Home"