mirror of
https://github.com/webrecorder/pywb.git
synced 2025-03-14 15:53:28 +01:00
Localization Support (#647)
* add localization utilities: - add locmanager to support extract, update, remove, list using pybabel - add po2csv/csv2po conversion with translate-utils - docs: add localization.rst to manual! * add language switch header (via header.html) to all pages if more than one locale is present. * localization: wrap more text strings in templates in existing templates * docs: - document `wb-manager i18n` commands - mention `<html lang>` setting - include csv example - add info about adding localizable text in templates * add localization to CHANGES
This commit is contained in:
parent
0eedd1502f
commit
12fcc87962
11
CHANGES.rst
11
CHANGES.rst
@ -7,6 +7,17 @@ Documentation Updates:
|
|||||||
|
|
||||||
* `New ACL header configuration <https://pywb.readthedocs.io/en/latest/manual/usage.html#config-acl-header>`_
|
* `New ACL header configuration <https://pywb.readthedocs.io/en/latest/manual/usage.html#config-acl-header>`_
|
||||||
|
|
||||||
|
* `Locaalization / Multi-lingual Support Guide <https://pywb.readthedocs.io/en/latest/manual/localization.html>`_
|
||||||
|
|
||||||
|
|
||||||
|
Localization Improvements: (`#647 <https://github.com/webrecorder/pywb/pull/647>`_)
|
||||||
|
|
||||||
|
* Support for extracting, updating, listing and removing localizable commands via ``wb-manager i18n`` command.
|
||||||
|
|
||||||
|
* UI: Add language switch header to all UI templates.
|
||||||
|
|
||||||
|
* Mark localizable strings in translatable in existing templates.
|
||||||
|
|
||||||
|
|
||||||
Access Control Improvements:
|
Access Control Improvements:
|
||||||
|
|
||||||
|
2
babel.ini
Normal file
2
babel.ini
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
[jinja2: pywb/templates/**.html]
|
||||||
|
extensions=jinja2.ext.i18n,jinja2.ext.autoescape,jinja2.ext.with_
|
@ -17,3 +17,6 @@ enable_memento: true
|
|||||||
# Replay content in an iframe
|
# Replay content in an iframe
|
||||||
framed_replay: true
|
framed_replay: true
|
||||||
|
|
||||||
|
locales:
|
||||||
|
- en
|
||||||
|
- es
|
||||||
|
@ -18,6 +18,7 @@ A subset of features provides the basic functionality of a "Wayback Machine".
|
|||||||
manual/configuring
|
manual/configuring
|
||||||
manual/access-control
|
manual/access-control
|
||||||
manual/ui-customization
|
manual/ui-customization
|
||||||
|
manual/localization
|
||||||
manual/architecture
|
manual/architecture
|
||||||
manual/apis
|
manual/apis
|
||||||
manual/owb-transition
|
manual/owb-transition
|
||||||
|
147
docs/manual/localization.rst
Normal file
147
docs/manual/localization.rst
Normal file
@ -0,0 +1,147 @@
|
|||||||
|
.. _localizaation:
|
||||||
|
|
||||||
|
Localization / Multi-lingual Support
|
||||||
|
------------------------------------
|
||||||
|
|
||||||
|
pywb supports configuring different language locales and loading different language translations, and dynamically switching languages.
|
||||||
|
|
||||||
|
pywb can extract all text from templates and generate CSV files for translation and convert them back into a binary format used for localization/internationalization.
|
||||||
|
|
||||||
|
(pywb uses the `Babel library <http://babel.pocoo.org/en/latest/>`_ which extends the `standard Python i18n system <https://docs.python.org/3/library/gettext.html>`_)
|
||||||
|
|
||||||
|
Locales to use are configured in the ``config.yaml``.
|
||||||
|
|
||||||
|
The command-line ``wb-manager`` utility provides a way to manages locales for translation, including generatin extracted text, update translated text.
|
||||||
|
|
||||||
|
Adding a Locale and Extracting Text
|
||||||
|
===================================
|
||||||
|
|
||||||
|
To add a new locale for translation and automatically extract all text that needs to be translated, run::
|
||||||
|
|
||||||
|
wb-manager i18n extract <loc>
|
||||||
|
|
||||||
|
The ``<loc>`` can be one or more supported two-letter locales or CLDR language codes. To list available codes, you can run ``pybabel --list-locales``.
|
||||||
|
|
||||||
|
Localization data is placed in the ``i18n`` directory, and translatable strings can be found in ``i18n/translations/<locale>/LC_MESSAGES/messages.csv``
|
||||||
|
|
||||||
|
Each CSV file looks as follows, listing source string and an empty string for the translated version::
|
||||||
|
|
||||||
|
"location","source","target"
|
||||||
|
"pywb/templates/banner.html:6","Live on",""
|
||||||
|
"pywb/templates/banner.html:8","Calendar icon",""
|
||||||
|
"pywb/templates/banner.html:9 pywb/templates/query.html:45","View All Captures",""
|
||||||
|
"pywb/templates/banner.html:10 pywb/templates/header.html:4","Language:",""
|
||||||
|
"pywb/templates/banner.html:11","Loading...",""
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
This CSV can then be passed to translators to translate the text.
|
||||||
|
|
||||||
|
(The extraction parameters arae configured to load data from ``pywb/templates/*.html`` in ``babel.ini``)
|
||||||
|
|
||||||
|
|
||||||
|
For example, the following will generate translation strings for ``es`` and ``pt`` locales::
|
||||||
|
|
||||||
|
wb-manager i18n extract es pt
|
||||||
|
|
||||||
|
|
||||||
|
The translatable text can then be found in ``i18n/translations/es/LC_MESSAGES/messages.csv`` and ``i18n/translations/pt/LC_MESSAGES/messages.csv``.
|
||||||
|
|
||||||
|
|
||||||
|
The CSV files should be updated with a translation for each string in the target column.
|
||||||
|
|
||||||
|
The extract commannd add any new strings without overwriting existing translations, so it is safe to run multiple times.
|
||||||
|
|
||||||
|
|
||||||
|
Updating Locale Catalog
|
||||||
|
=======================
|
||||||
|
|
||||||
|
Once the text has been translated, and the CSV files updated, simply run::
|
||||||
|
|
||||||
|
wb-manager i18n update <loc>
|
||||||
|
|
||||||
|
This will parse the CSVs and compile the translated string tables for use with pywb.
|
||||||
|
|
||||||
|
|
||||||
|
Specifying locales in pywb
|
||||||
|
==========================
|
||||||
|
|
||||||
|
To enable the locales in pywb, add one or more locales can be added to the ``locales`` key in ``config.yaml``, ex::
|
||||||
|
|
||||||
|
locales:
|
||||||
|
- en
|
||||||
|
- es
|
||||||
|
|
||||||
|
Single Language Default Locale
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
pywb can be configured with a default, single-language locale, by setting the ``default_locale`` property in ``config.yaml``::
|
||||||
|
|
||||||
|
|
||||||
|
default_locale: es
|
||||||
|
locales:
|
||||||
|
- es
|
||||||
|
|
||||||
|
|
||||||
|
With this configuration, pywb will automatically use the ``es`` locale for all text strings in pywb pages.
|
||||||
|
|
||||||
|
pywb will also set the ``<html lang="es">`` so that the browser will recognize the correct locale.
|
||||||
|
|
||||||
|
|
||||||
|
Mutli-language Translations
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
If more than one locale is specified, pywb will automatically show a language switching UI at the top of collection and search pages, with an option
|
||||||
|
for each locale listed. To include English as an option, it should also be added as a locale (and no strings translated). For example::
|
||||||
|
|
||||||
|
locales:
|
||||||
|
- en
|
||||||
|
- es
|
||||||
|
- pt
|
||||||
|
|
||||||
|
will configure pywb to show a language switch option on all pages.
|
||||||
|
|
||||||
|
|
||||||
|
Localized Collection Paths
|
||||||
|
==========================
|
||||||
|
|
||||||
|
When localization is enabled, pywb supports the locale prefix for accessing each collection with a localized language:
|
||||||
|
If pywb has a collection ``my-web-archive``, then:
|
||||||
|
|
||||||
|
* ``/my-web-archive/`` - loads UI with default language (set via ``default_locale``)
|
||||||
|
* ``/en/my-web-archive/`` - loads UI with ``en`` locale
|
||||||
|
* ``/es/my-web-archive/`` - loads UI with ``es`` locale
|
||||||
|
* ``/pt/my-web-archive/`` - loads UI with ``pt`` locale
|
||||||
|
|
||||||
|
The language switch options work by changing the locale prefix for the same page.
|
||||||
|
|
||||||
|
Listing and Removing Locales
|
||||||
|
============================
|
||||||
|
|
||||||
|
To list the locales that have previously been added, you can also run ``wb-manager i18n list``.
|
||||||
|
|
||||||
|
To disable a locale from being used in pywb, simply remove it from the ``locales`` key in ``config.yaml``
|
||||||
|
|
||||||
|
To remove data for a locale permanently, you can run: ``wb-manager i18n remove <loc>``. This will remove the locale directory on disk.
|
||||||
|
|
||||||
|
To remove all localization data, you can manually delete the ``i18n`` directory.
|
||||||
|
|
||||||
|
|
||||||
|
UI Templates: Adding Localizable Text
|
||||||
|
=====================================
|
||||||
|
|
||||||
|
Text that can be translated, localizable text, can be marked as such directly in the UI templates:
|
||||||
|
|
||||||
|
1. By wrapping the text in ``{% trans %}``/``{% endtrans %}`` tags. For example::
|
||||||
|
|
||||||
|
{% trans %}Collection {{ coll }} Search Page{% endtrans %}
|
||||||
|
|
||||||
|
2. Short-hand by calling a special ``_()`` function, which can be used in attributes or more dynamically. For example::
|
||||||
|
|
||||||
|
... title="{{ _('Enter a URL to search for') }}">
|
||||||
|
|
||||||
|
|
||||||
|
These methods can be used in all UI templates and are supported by the Jinja2 templating system.
|
||||||
|
|
||||||
|
See :ref:`ui-customizations` for a list of all available UI templates.
|
||||||
|
|
@ -5,3 +5,4 @@ uwsgi
|
|||||||
ujson
|
ujson
|
||||||
pysocks
|
pysocks
|
||||||
lxml
|
lxml
|
||||||
|
translate_toolkit
|
||||||
|
@ -72,7 +72,8 @@ class RewriterApp(object):
|
|||||||
|
|
||||||
self.jinja_env.init_loc(self.config.get('locales_root_dir'),
|
self.jinja_env.init_loc(self.config.get('locales_root_dir'),
|
||||||
self.config.get('locales'),
|
self.config.get('locales'),
|
||||||
self.loc_map)
|
self.loc_map,
|
||||||
|
self.config.get('default_locale'))
|
||||||
|
|
||||||
self.redirect_to_exact = config.get('redirect_to_exact')
|
self.redirect_to_exact = config.get('redirect_to_exact')
|
||||||
|
|
||||||
|
109
pywb/manager/locmanager.py
Normal file
109
pywb/manager/locmanager.py
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
import os
|
||||||
|
import os.path
|
||||||
|
import shutil
|
||||||
|
|
||||||
|
from babel.messages.frontend import CommandLineInterface
|
||||||
|
|
||||||
|
from translate.convert.po2csv import main as po2csv
|
||||||
|
from translate.convert.csv2po import main as csv2po
|
||||||
|
|
||||||
|
|
||||||
|
ROOT_DIR = 'i18n'
|
||||||
|
|
||||||
|
TRANSLATIONS = os.path.join(ROOT_DIR, 'translations')
|
||||||
|
|
||||||
|
MESSAGES = os.path.join(ROOT_DIR, 'messages.pot')
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
class LocManager:
|
||||||
|
def process(self, r):
|
||||||
|
if r.name == 'list':
|
||||||
|
r.loc_func(self)
|
||||||
|
elif r.name == 'remove':
|
||||||
|
r.loc_func(self, r.locale)
|
||||||
|
else:
|
||||||
|
r.loc_func(self, r.locale, r.no_csv)
|
||||||
|
|
||||||
|
def extract_loc(self, locale, no_csv):
|
||||||
|
self.extract_text()
|
||||||
|
|
||||||
|
for loc in locale:
|
||||||
|
loc_dir = os.path.join(TRANSLATIONS, loc)
|
||||||
|
if os.path.isdir(loc_dir):
|
||||||
|
self.update_catalog(loc)
|
||||||
|
else:
|
||||||
|
os.makedirs(loc_dir)
|
||||||
|
self.init_catalog(loc)
|
||||||
|
|
||||||
|
if not no_csv:
|
||||||
|
base = os.path.join(TRANSLATIONS, loc, 'LC_MESSAGES')
|
||||||
|
po = os.path.join(base, 'messages.po')
|
||||||
|
csv = os.path.join(base, 'messages.csv')
|
||||||
|
po2csv([po, csv])
|
||||||
|
|
||||||
|
def update_loc(self, locale, no_csv):
|
||||||
|
for loc in locale:
|
||||||
|
if not no_csv:
|
||||||
|
loc_dir = os.path.join(TRANSLATIONS, loc)
|
||||||
|
base = os.path.join(TRANSLATIONS, loc, 'LC_MESSAGES')
|
||||||
|
po = os.path.join(base, 'messages.po')
|
||||||
|
csv = os.path.join(base, 'messages.csv')
|
||||||
|
|
||||||
|
if os.path.isfile(csv):
|
||||||
|
csv2po([csv, po])
|
||||||
|
|
||||||
|
self.compile_catalog()
|
||||||
|
|
||||||
|
def remove_loc(self, locale):
|
||||||
|
for loc in locale:
|
||||||
|
loc_dir = os.path.join(TRANSLATIONS, loc)
|
||||||
|
if not os.path.isdir(loc_dir):
|
||||||
|
print('Locale "{0}" does not exist'.format(loc))
|
||||||
|
return
|
||||||
|
|
||||||
|
shutil.rmtree(loc_dir)
|
||||||
|
print('Removed locale "{0}"'.format(loc))
|
||||||
|
|
||||||
|
def list_loc(self):
|
||||||
|
print('Current locales:')
|
||||||
|
print('\n'.join(' - ' + x for x in os.listdir(TRANSLATIONS)))
|
||||||
|
print('')
|
||||||
|
|
||||||
|
def extract_text(self):
|
||||||
|
os.makedirs(ROOT_DIR, exist_ok=True)
|
||||||
|
|
||||||
|
CommandLineInterface().run(['pybabel', 'extract', '-F', 'babel.ini', '-k', '_ _Q gettext ngettext', '-o', MESSAGES, './', '--omit-header'])
|
||||||
|
|
||||||
|
def init_catalog(self, loc):
|
||||||
|
CommandLineInterface().run(['pybabel', 'init', '-l', loc, '-i', MESSAGES, '-d', TRANSLATIONS])
|
||||||
|
|
||||||
|
def update_catalog(self, loc):
|
||||||
|
CommandLineInterface().run(['pybabel', 'update', '-l', loc, '-i', MESSAGES, '-d', TRANSLATIONS, '--previous'])
|
||||||
|
|
||||||
|
def compile_catalog(self):
|
||||||
|
CommandLineInterface().run(['pybabel', 'compile', '-d', TRANSLATIONS])
|
||||||
|
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def init_parser(cls, parser):
|
||||||
|
"""Initializes an argument parser for acl commands
|
||||||
|
|
||||||
|
:param argparse.ArgumentParser parser: The parser to be initialized
|
||||||
|
:rtype: None
|
||||||
|
"""
|
||||||
|
subparsers = parser.add_subparsers(dest='op')
|
||||||
|
subparsers.required = True
|
||||||
|
|
||||||
|
def command(name, func):
|
||||||
|
op = subparsers.add_parser(name)
|
||||||
|
if name != 'list':
|
||||||
|
op.add_argument('locale', nargs='+')
|
||||||
|
if name != 'remove':
|
||||||
|
op.add_argument('--no-csv', action='store_true')
|
||||||
|
|
||||||
|
op.set_defaults(loc_func=func, name=name)
|
||||||
|
|
||||||
|
command('extract', cls.extract_loc)
|
||||||
|
command('update', cls.update_loc)
|
||||||
|
command('remove', cls.remove_loc)
|
||||||
|
command('list', cls.list_loc)
|
@ -441,6 +441,17 @@ Create manage file based web archive collections
|
|||||||
ACLManager.init_parser(acl)
|
ACLManager.init_parser(acl)
|
||||||
acl.set_defaults(func=do_acl)
|
acl.set_defaults(func=do_acl)
|
||||||
|
|
||||||
|
# LOC
|
||||||
|
from pywb.manager.locmanager import LocManager
|
||||||
|
def do_loc(r):
|
||||||
|
loc = LocManager()
|
||||||
|
loc.process(r)
|
||||||
|
|
||||||
|
loc_help = 'Generate strings for i18n/localization'
|
||||||
|
loc = subparsers.add_parser('i18n', help=loc_help)
|
||||||
|
LocManager.init_parser(loc)
|
||||||
|
loc.set_defaults(func=do_loc)
|
||||||
|
|
||||||
# Parse
|
# Parse
|
||||||
r = parser.parse_args(args=args)
|
r = parser.parse_args(args=args)
|
||||||
r.func(r)
|
r.func(r)
|
||||||
|
@ -98,6 +98,8 @@ class JinjaEnv(object):
|
|||||||
assets_env.resolver = PkgResResolver()
|
assets_env.resolver = PkgResResolver()
|
||||||
jinja_env.assets_environment = assets_env
|
jinja_env.assets_environment = assets_env
|
||||||
|
|
||||||
|
self.default_locale = ''
|
||||||
|
|
||||||
def _make_loaders(self, paths, packages):
|
def _make_loaders(self, paths, packages):
|
||||||
"""Initialize the template loaders based on the supplied paths and packages.
|
"""Initialize the template loaders based on the supplied paths and packages.
|
||||||
|
|
||||||
@ -117,16 +119,19 @@ class JinjaEnv(object):
|
|||||||
|
|
||||||
return loaders
|
return loaders
|
||||||
|
|
||||||
def init_loc(self, locales_root_dir, locales, loc_map):
|
def init_loc(self, locales_root_dir, locales, loc_map, default_locale):
|
||||||
locales = locales or []
|
locales = locales or []
|
||||||
|
locales_root_dir = locales_root_dir or os.path.join('i18n', 'translations')
|
||||||
|
default_locale = default_locale or 'en'
|
||||||
|
self.default_locale = default_locale
|
||||||
|
|
||||||
if locales_root_dir:
|
if locales_root_dir:
|
||||||
for loc in locales:
|
for loc in locales:
|
||||||
loc_map[loc] = Translations.load(locales_root_dir, [loc, 'en'])
|
loc_map[loc] = Translations.load(locales_root_dir, [loc, default_locale])
|
||||||
#jinja_env.jinja_env.install_gettext_translations(translations)
|
#jinja_env.jinja_env.install_gettext_translations(translations)
|
||||||
|
|
||||||
def get_translate(context):
|
def get_translate(context):
|
||||||
loc = context.get('env', {}).get('pywb_lang')
|
loc = context.get('env', {}).get('pywb_lang', default_locale)
|
||||||
return loc_map.get(loc)
|
return loc_map.get(loc)
|
||||||
|
|
||||||
def override_func(jinja_env, name):
|
def override_func(jinja_env, name):
|
||||||
@ -160,6 +165,7 @@ class JinjaEnv(object):
|
|||||||
|
|
||||||
self.jinja_env.globals['locales'] = list(loc_map.keys())
|
self.jinja_env.globals['locales'] = list(loc_map.keys())
|
||||||
self.jinja_env.globals['_Q'] = quote_gettext
|
self.jinja_env.globals['_Q'] = quote_gettext
|
||||||
|
self.jinja_env.globals['default_locale'] = default_locale
|
||||||
|
|
||||||
@contextfunction
|
@contextfunction
|
||||||
def switch_locale(context, locale):
|
def switch_locale(context, locale):
|
||||||
|
@ -182,7 +182,7 @@ This file is part of pywb, https://github.com/webrecorder/pywb
|
|||||||
ancillaryLinks.appendChild(calendarLink);
|
ancillaryLinks.appendChild(calendarLink);
|
||||||
this.calendarLink = calendarLink;
|
this.calendarLink = calendarLink;
|
||||||
|
|
||||||
if (typeof window.banner_info.locales !== "undefined" && window.banner_info.locales.length) {
|
if (typeof window.banner_info.locales !== "undefined" && window.banner_info.locales.length > 1) {
|
||||||
var locales = window.banner_info.locales;
|
var locales = window.banner_info.locales;
|
||||||
var languages = document.createElement("div");
|
var languages = document.createElement("div");
|
||||||
|
|
||||||
@ -317,4 +317,4 @@ This file is part of pywb, https://github.com/webrecorder/pywb
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
})();
|
})();
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
<!-- jquery and bootstrap dependencies query view -->
|
<!-- jquery and bootstrap dependencies query view -->
|
||||||
<link rel="stylesheet" href="{{ static_prefix }}/css/bootstrap.min.css"/>
|
<link rel="stylesheet" href="{{ static_prefix }}/css/bootstrap.min.css"/>
|
||||||
<link rel="stylesheet" href="{{ static_prefix }}/css/font-awesome.min.css">
|
<link rel="stylesheet" href="{{ static_prefix }}/css/font-awesome.min.css">
|
||||||
|
<link rel="stylesheet" href="{{ static_prefix }}/css/base.css">
|
||||||
|
|
||||||
<script src="{{ static_prefix }}/js/jquery-latest.min.js"></script>
|
<script src="{{ static_prefix }}/js/jquery-latest.min.js"></script>
|
||||||
<script src="{{ static_prefix }}/js/bootstrap.min.js"></script>
|
<script src="{{ static_prefix }}/js/bootstrap.min.js"></script>
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
{% extends "base.html" %}
|
{% extends "base.html" %}
|
||||||
{% block title %}Pywb Error{% endblock %}
|
{% block title %}{{ _('Pywb Error') }}{% endblock %}
|
||||||
{% block body %}
|
{% block body %}
|
||||||
<div class="container text-danger">
|
<div class="container text-danger">
|
||||||
<div class="row justify-content-center">
|
<div class="row justify-content-center">
|
||||||
@ -8,22 +8,22 @@
|
|||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-12 text-center">
|
<div class="col-12 text-center">
|
||||||
{% if err_status == 451 %}
|
{% if err_status == 451 %}
|
||||||
<p class="lead">Access Blocked to {{ err_msg }}</p>
|
<p class="lead">{% trans %}Access Blocked to {{ err_msg }}{% endtrans %}</p>
|
||||||
|
|
||||||
{% elif err_status == 404 and err_details == 'coll_not_found' %}
|
{% elif err_status == 404 and err_details == 'coll_not_found' %}
|
||||||
<p>Collection not found: <b>{{ err_msg }}</b></p>
|
<p>{% trans %}Collection not found: <b>{{ err_msg }}{% endtrans %}</b></p>
|
||||||
|
|
||||||
<p><a href="/">See list of valid collections</a></p>
|
<p><a href="/">{{ _('See list of valid collections') }}</a></p>
|
||||||
|
|
||||||
{% elif err_status == 404 and err_details == 'static_file_not_found' %}
|
{% elif err_status == 404 and err_details == 'static_file_not_found' %}
|
||||||
<p>Static file not found: <b>{{ err_msg }}</b></p>
|
<p>{% trans %}Static file not found: <b>{{ err_msg }}{% endtrans %}</b></p>
|
||||||
|
|
||||||
{% else %}
|
{% else %}
|
||||||
|
|
||||||
<p class="lead">{{ err_msg }}</p>
|
<p class="lead">{{ err_msg }}</p>
|
||||||
|
|
||||||
{% if err_details %}
|
{% if err_details %}
|
||||||
<p class="lead">Error Details:</p>
|
<p class="lead">{% trans %}Error Details:{% endtrans %}</p>
|
||||||
<pre>{{ err_details }}</pre>
|
<pre>{{ err_details }}</pre>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<h2 class="display-2">{{ _('Pywb Wayback Machine') }}</h2>
|
<h2 class="display-2">{{ _('Pywb Wayback Machine') }}</h2>
|
||||||
<p class="lead">This archive contains the following collections:</p>
|
<p class="lead">{{ _('This archive contains the following collections:') }}</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<ul>
|
<ul>
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{% extends "base.html" %}
|
{% extends "base.html" %}
|
||||||
|
|
||||||
{% block title %}URL Not Found{% endblock %}
|
{% block title %}{{ _('URL Not Found') }}{% endblock %}
|
||||||
|
|
||||||
{% block body %}
|
{% block body %}
|
||||||
<div class="container">
|
<div class="container">
|
||||||
@ -13,7 +13,7 @@
|
|||||||
{% if wbrequest and wbrequest.env.pywb_proxy_magic and url %}
|
{% if wbrequest and wbrequest.env.pywb_proxy_magic and url %}
|
||||||
<p>
|
<p>
|
||||||
<a href="//select.{{ wbrequest and wbrequest.env.pywb_proxy_magic }}/{{ url }}">
|
<a href="//select.{{ wbrequest and wbrequest.env.pywb_proxy_magic }}/{{ url }}">
|
||||||
Try Different Collection
|
{{ _('Try Different Collection') }}
|
||||||
</a>
|
</a>
|
||||||
</p>
|
</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
@ -13,7 +13,7 @@ window.wb_prefix = "{{ wb_prefix }}";
|
|||||||
<div class="container-fluid">
|
<div class="container-fluid">
|
||||||
<div class="row justify-content-center">
|
<div class="row justify-content-center">
|
||||||
<h4 class="display-4">
|
<h4 class="display-4">
|
||||||
Collection {{ coll }} Search Page
|
{% trans %}Collection {{ coll }} Search Page{% endtrans %}
|
||||||
</h4>
|
</h4>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -27,9 +27,9 @@ window.wb_prefix = "{{ wb_prefix }}";
|
|||||||
</label>
|
</label>
|
||||||
<input aria-label="url" aria-required="true" class="form-control form-control-lg" id="search-url"
|
<input aria-label="url" aria-required="true" class="form-control form-control-lg" id="search-url"
|
||||||
name="search" placeholder="Enter a URL to search for"
|
name="search" placeholder="Enter a URL to search for"
|
||||||
title="Enter a URL to search for" type="search" required/>
|
title="{{ _('Enter a URL to search for') }}" type="search" required/>
|
||||||
<div class="invalid-feedback">
|
<div class="invalid-feedback">
|
||||||
Please enter a URL
|
{% trans %}'Please enter a URL{% endtrans %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -37,7 +37,7 @@ window.wb_prefix = "{{ wb_prefix }}";
|
|||||||
<div class="col-5">
|
<div class="col-5">
|
||||||
<div class="custom-control custom-checkbox custom-control">
|
<div class="custom-control custom-checkbox custom-control">
|
||||||
<input type="checkbox" class="custom-control-input" id="open-results-new-window">
|
<input type="checkbox" class="custom-control-input" id="open-results-new-window">
|
||||||
<label class="custom-control-label" for="open-results-new-window">Open results in new window</label>
|
<label class="custom-control-label" for="open-results-new-window">{{ _('Open results in new window') }}</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-7">
|
<div class="col-7">
|
||||||
@ -47,51 +47,51 @@ window.wb_prefix = "{{ wb_prefix }}";
|
|||||||
<button class="btn btn-outline-info float-right mr-3" type="button" role="button"
|
<button class="btn btn-outline-info float-right mr-3" type="button" role="button"
|
||||||
data-toggle="collapse" data-target="#advancedOptions"
|
data-toggle="collapse" data-target="#advancedOptions"
|
||||||
aria-expanded="false" aria-controls="advancedOptions" aria-label="Advanced Search Options">
|
aria-expanded="false" aria-controls="advancedOptions" aria-label="Advanced Search Options">
|
||||||
Advanced Search Options
|
{{ _('Advanced Search Options') }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="collapse mt-3" id="advancedOptions">
|
<div class="collapse mt-3" id="advancedOptions">
|
||||||
<div class="form-group form-row">
|
<div class="form-group form-row">
|
||||||
<label for="match-type-select" class="col-sm-2 col-form-label" aria-label="Match Type">
|
<label for="match-type-select" class="col-sm-2 col-form-label" aria-label="Match Type">
|
||||||
Match Type:
|
{{ _('Match Type:') }}
|
||||||
</label>
|
</label>
|
||||||
<select id="match-type-select" class="form-control form-control col-sm-6">
|
<select id="match-type-select" class="form-control form-control col-sm-6">
|
||||||
<option value=""></option>
|
<option value=""></option>
|
||||||
<option value="prefix">Prefix</option>
|
<option value="prefix">{% trans %}Prefix{% endtrans %}</option>
|
||||||
<option value="host">Host</option>
|
<option value="host">{% trans %}Host{% endtrans %}</option>
|
||||||
<option value="domain">Domain</option>
|
<option value="domain">{% trans %}Domain{% endtrans %}</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<p style="cursor: help;">
|
<p style="cursor: help;">
|
||||||
<span data-toggle="tooltip" data-placement="right"
|
<span data-toggle="tooltip" data-placement="right"
|
||||||
title="Restricts the results to the given date/time range (inclusive)">
|
title="Restricts the results to the given date/time range (inclusive)">
|
||||||
Date/Time Range
|
{{ _('Date/Time Range') }}
|
||||||
</span>
|
</span>
|
||||||
</p>
|
</p>
|
||||||
<div class="form-row">
|
<div class="form-row">
|
||||||
<div class="col-6">
|
<div class="col-6">
|
||||||
<label class="sr-only" for="dt-from" aria-label="Date/Time Range From">From:</label>
|
<label class="sr-only" for="dt-from" aria-label="Date/Time Range From">{% trans %}From:{% endtrans %}</label>
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<div class="input-group-prepend">
|
<div class="input-group-prepend">
|
||||||
<div class="input-group-text">From:</div>
|
<div class="input-group-text">{% trans %}From:{% endtrans %}</div>
|
||||||
</div>
|
</div>
|
||||||
<input id="dt-from" type="number" name="date-range-from" class="form-control"
|
<input id="dt-from" type="number" name="date-range-from" class="form-control"
|
||||||
pattern="^\d{4,14}$">
|
pattern="^\d{4,14}$">
|
||||||
<div class="invalid-feedback" id="dt-from-bad">
|
<div class="invalid-feedback" id="dt-from-bad">
|
||||||
Please enter a valid <b>From</b> timestamp. Timestamps may be 4 <= ts <=14 digits
|
{% trans %}Please enter a valid <b>From</b> timestamp. Timestamps may be 4 <= ts <=14 digits{% endtrans %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-6">
|
<div class="col-6">
|
||||||
<label class="sr-only" for="dt-to" aria-label="Date/Time Range To">To:</label>
|
<label class="sr-only" for="dt-to" aria-label="Date/Time Range To">{% trans %}To:{% endtrans %}</label>
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<div class="input-group-prepend">
|
<div class="input-group-prepend">
|
||||||
<div class="input-group-text">To:</div>
|
<div class="input-group-text">{% trans %}To:{% endtrans %}</div>
|
||||||
</div>
|
</div>
|
||||||
<input id="dt-to" type="number" name="date-range-to" class="form-control" pattern="^\d{4,14}$">
|
<input id="dt-to" type="number" name="date-range-to" class="form-control" pattern="^\d{4,14}$">
|
||||||
<div class="invalid-feedback" id="dt-to-bad">
|
<div class="invalid-feedback" id="dt-to-bad">
|
||||||
Please enter a valid <b>To</b> timestamp. Timestamps may be 4 <= ts <=14 digits
|
{% trans %}Please enter a valid <b>To</b> timestamp. Timestamps may be 4 <= ts <=14 digits{% endtrans %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -99,41 +99,41 @@ window.wb_prefix = "{{ wb_prefix }}";
|
|||||||
<div class="form-group mt-3">
|
<div class="form-group mt-3">
|
||||||
<div class="form-row">
|
<div class="form-row">
|
||||||
<div class="col-6">
|
<div class="col-6">
|
||||||
<p>Filtering</p>
|
<p>{% trans %}Filtering{% endtrans %}</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-6">
|
<div class="col-6">
|
||||||
<button id="clear-filters" class="btn btn-outline-warning float-right" type="button">
|
<button id="clear-filters" class="btn btn-outline-warning float-right" type="button">
|
||||||
Clear Filters
|
{% trans %}Clear Filters{% endtrans %}
|
||||||
</button>
|
</button>
|
||||||
<button id="add-filter" class="btn btn-outline-secondary float-right mr-2" type="button">
|
<button id="add-filter" class="btn btn-outline-secondary float-right mr-2" type="button">
|
||||||
Add Filter
|
{% trans %}Add Filter{% endtrans %}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-row">
|
<div class="form-row">
|
||||||
<div class="col-6">
|
<div class="col-6">
|
||||||
<div class="row pb-1">
|
<div class="row pb-1">
|
||||||
<label for="filter-by" class="col-form-label col-3">By:</label>
|
<label for="filter-by" class="col-form-label col-3">{% trans %}By:{% endtrans %}</label>
|
||||||
<select id="filter-by" class="form-control col-7">
|
<select id="filter-by" class="form-control col-7">
|
||||||
<option value="" selected></option>
|
<option value="" selected></option>
|
||||||
<option value="mime">Mime Type</option>
|
<option value="mime">{% trans %}Mime Type{% endtrans %}</option>
|
||||||
<option value="status">Status</option>
|
<option value="status">{% trans %}Status{% endtrans %}</option>
|
||||||
<option value="url">URL</option>
|
<option value="url">{% trans %}URL{% endtrans %}</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div class="row pb-1">
|
<div class="row pb-1">
|
||||||
<label for="filter-modifier" class="col-form-label col-3">How:</label>
|
<label for="filter-modifier" class="col-form-label col-3">{% trans %}How:{% endtrans %}</label>
|
||||||
<select id="filter-modifier" class="form-control col-7">
|
<select id="filter-modifier" class="form-control col-7">
|
||||||
<option value="=">Contains</option>
|
<option value="=">{% trans %}Contains{% endtrans %}</option>
|
||||||
<option value="==">Matches Exactly</option>
|
<option value="==">{% trans %}Matches Exactly{% endtrans %}</option>
|
||||||
<option value="=~">Matches Regex</option>
|
<option value="=~">{% trans %}Matches Regex{% endtrans %}</option>
|
||||||
<option value="=!">Does Not Contains</option>
|
<option value="=!">{% trans %}Does Not Contain{% endtrans %}</option>
|
||||||
<option value="=!=">Is Not</option>
|
<option value="=!=">{% trans %}Is Not{% endtrans %}</option>
|
||||||
<option value="=!~">Does Not Begins With</option>
|
<option value="=!~">{% trans %}Does Not Begins With{% endtrans %}</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<label for="filter-expression" class="col-form-label col-3">Expr:</label>
|
<label for="filter-expression" class="col-form-label col-3">{% trans %}Expr:{% endtrans %}</label>
|
||||||
<input type="text" id="filter-expression" class="form-control col-7"
|
<input type="text" id="filter-expression" class="form-control col-7"
|
||||||
placeholder="Enter an expression to filter by"
|
placeholder="Enter an expression to filter by"
|
||||||
>
|
>
|
||||||
@ -141,7 +141,7 @@ window.wb_prefix = "{{ wb_prefix }}";
|
|||||||
</div>
|
</div>
|
||||||
<div class="col-6">
|
<div class="col-6">
|
||||||
<ul id="filter-list" class="filter-list">
|
<ul id="filter-list" class="filter-list">
|
||||||
<li id="filtering-nothing">No Filter</li>
|
<li id="filtering-nothing">{% trans %}No Filter{% endtrans %}</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -151,7 +151,7 @@ window.wb_prefix = "{{ wb_prefix }}";
|
|||||||
</div>
|
</div>
|
||||||
{% if metadata %}
|
{% if metadata %}
|
||||||
<div class="container mt-4 justify-content-center">
|
<div class="container mt-4 justify-content-center">
|
||||||
<p class="lead">Collection Metadata</p>
|
<p class="lead">{{ _('Collection Metadata') }}</p>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-4 pr-1">
|
<div class="col-4 pr-1">
|
||||||
<div class="list-group" id="collection-metadata" role="tablist">
|
<div class="list-group" id="collection-metadata" role="tablist">
|
||||||
|
Loading…
x
Reference in New Issue
Block a user