mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2025-02-25 18:55:22 -06:00
Add i18n capabilities for custom templates.
For example: The Sphinx reference documentation in doc directory provides sphinx.pot file from ``doc/_templates/*.html`` by ``make gettext``.
This commit is contained in:
parent
7b443298a3
commit
2c409959ac
4
CHANGES
4
CHANGES
@ -1,6 +1,10 @@
|
|||||||
Release 1.2 (in development)
|
Release 1.2 (in development)
|
||||||
============================
|
============================
|
||||||
|
|
||||||
|
* Add i18n capabilities for custom templates.
|
||||||
|
For example: The Sphinx reference documentation in doc directory provides
|
||||||
|
sphinx.pot file from ``doc/_templates/*.html`` by ``make gettext``.
|
||||||
|
|
||||||
* PR#123, #1106: Add epub_use_index configuration value.
|
* PR#123, #1106: Add epub_use_index configuration value.
|
||||||
If provided, it will be used instead of html_use_index for epub builder.
|
If provided, it will be used instead of html_use_index for epub builder.
|
||||||
|
|
||||||
|
86
doc/_templates/index.html
vendored
86
doc/_templates/index.html
vendored
@ -1,87 +1,87 @@
|
|||||||
{% extends "layout.html" %}
|
{% extends "layout.html" %}
|
||||||
{% set title = 'Overview' %}
|
{% set title = _('Overview') %}
|
||||||
{% block body %}
|
{% block body %}
|
||||||
<h1>Welcome</h1>
|
<h1>{{ _('Welcome') }}</h1>
|
||||||
|
|
||||||
<div class="quotebar">
|
<div class="quotebar">
|
||||||
<p><em>What users say:</em></p>
|
<p><em>{%trans%}What users say:{%endtrans%}</em></p>
|
||||||
<p>“Cheers for a great tool that actually makes programmers <b>want</b>
|
<p>{%trans%}“Cheers for a great tool that actually makes programmers <b>want</b>
|
||||||
to write documentation!”</p>
|
to write documentation!”{%endtrans%}</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<p>
|
<p>{%trans%}
|
||||||
Sphinx is a tool that makes it easy to create intelligent and beautiful
|
Sphinx is a tool that makes it easy to create intelligent and beautiful
|
||||||
documentation, written by Georg Brandl and licensed under the BSD license.</p>
|
documentation, written by Georg Brandl and licensed under the BSD license.{%endtrans%}</p>
|
||||||
<p>It was originally created for <a href="http://docs.python.org/">the
|
<p>{%trans%}It was originally created for <a href="http://docs.python.org/">the
|
||||||
new Python documentation</a>, and it has excellent facilities for the
|
new Python documentation</a>, and it has excellent facilities for the
|
||||||
documentation of Python projects, but C/C++ is already supported as well,
|
documentation of Python projects, but C/C++ is already supported as well,
|
||||||
and it is planned to add special support for other languages as well. Of
|
and it is planned to add special support for other languages as well. Of
|
||||||
course, this site is also created from reStructuredText sources using
|
course, this site is also created from reStructuredText sources using
|
||||||
Sphinx! The following features should be highlighted:
|
Sphinx! The following features should be highlighted:{%endtrans%}
|
||||||
</p>
|
</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li><b>Output formats:</b> HTML (including Windows HTML Help), LaTeX (for
|
<li>{%trans%}<b>Output formats:</b> HTML (including Windows HTML Help), LaTeX (for
|
||||||
printable PDF versions), Texinfo, manual pages, plain text</li>
|
printable PDF versions), Texinfo, manual pages, plain text{%endtrans%}</li>
|
||||||
<li><b>Extensive cross-references:</b> semantic markup and automatic links
|
<li>{%trans%}<b>Extensive cross-references:</b> semantic markup and automatic links
|
||||||
for functions, classes, citations, glossary terms and similar pieces of
|
for functions, classes, citations, glossary terms and similar pieces of
|
||||||
information</li>
|
information{%endtrans%}</li>
|
||||||
<li><b>Hierarchical structure:</b> easy definition of a document tree, with
|
<li>{%trans%}<b>Hierarchical structure:</b> easy definition of a document tree, with
|
||||||
automatic links to siblings, parents and children</li>
|
automatic links to siblings, parents and children{%endtrans%}</li>
|
||||||
<li><b>Automatic indices:</b> general index as well as a language-specific
|
<li>{%trans%}<b>Automatic indices:</b> general index as well as a language-specific
|
||||||
module indices</li>
|
module indices{%endtrans%}</li>
|
||||||
<li><b>Code handling:</b> automatic highlighting using the <a
|
<li>{%trans%}<b>Code handling:</b> automatic highlighting using the <a
|
||||||
href="http://pygments.org">Pygments</a> highlighter</li>
|
href="http://pygments.org">Pygments</a> highlighter{%endtrans%}</li>
|
||||||
<li><b>Extensions:</b> automatic testing of code snippets, inclusion of
|
<li>{%trans path=pathto('extensions')%}<b>Extensions:</b> automatic testing of code snippets, inclusion of
|
||||||
docstrings from Python modules (API docs), and
|
docstrings from Python modules (API docs), and
|
||||||
<a href="{{ pathto('extensions') }}#builtin-sphinx-extensions">more</a></li>
|
<a href="{{ path }}#builtin-sphinx-extensions">more</a>{%endtrans%}</li>
|
||||||
</ul>
|
</ul>
|
||||||
<p>
|
<p>{%trans%}
|
||||||
Sphinx uses <a href="http://docutils.sf.net/rst.html">reStructuredText</a>
|
Sphinx uses <a href="http://docutils.sf.net/rst.html">reStructuredText</a>
|
||||||
as its markup language, and many of its strengths come from the power and
|
as its markup language, and many of its strengths come from the power and
|
||||||
straightforwardness of reStructuredText and its parsing and translating
|
straightforwardness of reStructuredText and its parsing and translating
|
||||||
suite, the <a href="http://docutils.sf.net/">Docutils</a>.
|
suite, the <a href="http://docutils.sf.net/">Docutils</a>.{%endtrans%}
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h2 style="margin-bottom: 0">Documentation</h2>
|
<h2 style="margin-bottom: 0">{%trans%}Documentation{%endtrans%}</h2>
|
||||||
|
|
||||||
<table class="contentstable" align="center" style="margin-left: 30px"><tr>
|
<table class="contentstable" align="center" style="margin-left: 30px"><tr>
|
||||||
<td width="50%">
|
<td width="50%">
|
||||||
<p class="biglink"><a class="biglink" href="{{ pathto("tutorial") }}">First steps with Sphinx</a><br/>
|
<p class="biglink"><a class="biglink" href="{{ pathto("tutorial") }}">{%trans%}First steps with Sphinx{%endtrans%}</a><br/>
|
||||||
<span class="linkdescr">overview of basic tasks</span></p>
|
<span class="linkdescr">{%trans%}overview of basic tasks{%endtrans%}</span></p>
|
||||||
<p class="biglink"><a class="biglink" href="{{ pathto("contents") }}">Contents</a><br/>
|
<p class="biglink"><a class="biglink" href="{{ pathto("contents") }}">{%trans%}Contents{%endtrans%}</a><br/>
|
||||||
<span class="linkdescr">for a complete overview</span></p>
|
<span class="linkdescr">{%trans%}for a complete overview{%endtrans%}</span></p>
|
||||||
</td><td width="50%">
|
</td><td width="50%">
|
||||||
<p class="biglink"><a class="biglink" href="{{ pathto("search") }}">Search page</a><br/>
|
<p class="biglink"><a class="biglink" href="{{ pathto("search") }}">{%trans%}Search page{%endtrans%}</a><br/>
|
||||||
<span class="linkdescr">search the documentation</span></p>
|
<span class="linkdescr">{%trans%}search the documentation{%endtrans%}</span></p>
|
||||||
<p class="biglink"><a class="biglink" href="{{ pathto("genindex") }}">General Index</a><br/>
|
<p class="biglink"><a class="biglink" href="{{ pathto("genindex") }}">{%trans%}General Index{%endtrans%}</a><br/>
|
||||||
<span class="linkdescr">all functions, classes, terms</span></p>
|
<span class="linkdescr">{%trans%}all functions, classes, terms{%endtrans%}</span></p>
|
||||||
</td></tr>
|
</td></tr>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
<p>
|
<p>{%trans%}
|
||||||
You can also download PDF versions of the Sphinx documentation:
|
You can also download PDF versions of the Sphinx documentation:
|
||||||
a <a href="http://sphinx-doc.org/sphinx.pdf">version</a> generated from
|
a <a href="http://sphinx-doc.org/sphinx.pdf">version</a> generated from
|
||||||
the LaTeX Sphinx produces, and
|
the LaTeX Sphinx produces, and
|
||||||
a <a href="http://sphinx-doc.org/sphinx-rst2pdf.pdf">version</a> generated
|
a <a href="http://sphinx-doc.org/sphinx-rst2pdf.pdf">version</a> generated
|
||||||
by rst2pdf.
|
by rst2pdf.{%endtrans%}
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h2>Examples</h2>
|
<h2>{%trans%}Examples{%endtrans%}</h2>
|
||||||
<p>Links to documentation generated with Sphinx can be found on the
|
<p>{%trans path=pathto("examples")%}Links to documentation generated with Sphinx can be found on the
|
||||||
<a href="{{ pathto("examples") }}">Projects using Sphinx</a> page.
|
<a href="{{ path }}">Projects using Sphinx</a> page.{%endtrans%}
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>{%trans%}
|
||||||
For examples of how Sphinx source files look, use the “Show
|
For examples of how Sphinx source files look, use the “Show
|
||||||
source” links on all pages of the documentation apart from this
|
source” links on all pages of the documentation apart from this
|
||||||
welcome page.
|
welcome page.{%endtrans%}
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p>You may also be interested in the very nice
|
<p>{%trans%}You may also be interested in the very nice
|
||||||
<a href="http://matplotlib.sourceforge.net/sampledoc/">tutorial</a> on how to
|
<a href="http://matplotlib.sourceforge.net/sampledoc/">tutorial</a> on how to
|
||||||
create a customized documentation using Sphinx written by the matplotlib
|
create a customized documentation using Sphinx written by the matplotlib
|
||||||
developers.</p>
|
developers.{%endtrans%}</p>
|
||||||
|
|
||||||
<p>There is a <a href="http://sphinx-users.jp/doc10/">Japanese translation</a>
|
<p>{%trans%}There is a <a href="http://sphinx-users.jp/doc10/">Japanese translation</a>
|
||||||
of this documentation, thanks to Yoshiki Shibukawa.</p>
|
of this documentation, thanks to Yoshiki Shibukawa.{%endtrans%}</p>
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
30
doc/_templates/indexsidebar.html
vendored
30
doc/_templates/indexsidebar.html
vendored
@ -1,32 +1,32 @@
|
|||||||
<p class="logo">A <a href="http://pocoo.org/">
|
<p class="logo">A <a href="http://pocoo.org/">
|
||||||
<img src="{{ pathto("_static/pocoo.png", 1) }}" /></a> project</a></p>
|
<img src="{{ pathto("_static/pocoo.png", 1) }}" /></a> {%trans%}project{%endtrans%}</a></p>
|
||||||
|
|
||||||
<h3>Download</h3>
|
<h3>Download</h3>
|
||||||
{% if version.endswith('(hg)') %}
|
{% if version.endswith('(hg)') %}
|
||||||
<p>This documentation is for version <b>{{ version }}</b>, which is
|
<p>{%trans%}This documentation is for version <b>{{ version }}</b>, which is
|
||||||
not released yet.</p>
|
not released yet.{%endtrans%}</p>
|
||||||
<p>You can use it from the
|
<p>{%trans%}You can use it from the
|
||||||
<a href="http://bitbucket.org/birkenfeld/sphinx/">Mercurial repo</a> or look for
|
<a href="http://bitbucket.org/birkenfeld/sphinx/">Mercurial repo</a> or look for
|
||||||
released versions in the <a href="http://pypi.python.org/pypi/Sphinx">Python
|
released versions in the <a href="http://pypi.python.org/pypi/Sphinx">Python
|
||||||
Package Index</a>.</p>
|
Package Index</a>.{%endtrans%}</p>
|
||||||
{% else %}
|
{% else %}
|
||||||
<p>Current version: <b>{{ version }}</b></p>
|
<p>{%trans%}Current version: <b>{{ version }}</b>{%endtrans%}</p>
|
||||||
<p>Get Sphinx from the <a href="http://pypi.python.org/pypi/Sphinx">Python Package
|
<p>{%trans%}Get Sphinx from the <a href="http://pypi.python.org/pypi/Sphinx">Python Package
|
||||||
Index</a>, or install it with:</p>
|
Index</a>, or install it with:{%endtrans%}</p>
|
||||||
<pre>easy_install -U Sphinx</pre>
|
<pre>easy_install -U Sphinx</pre>
|
||||||
<p>Latest <a href="http://sphinx-doc.org/latest/">development version docs</a>
|
<p>{%trans%}Latest <a href="http://sphinx-doc.org/latest/">development version docs</a>
|
||||||
are also available.</p>
|
are also available.{%endtrans%}</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
<h3>Questions? Suggestions?</h3>
|
<h3>{%trans%}Questions? Suggestions?{%endtrans%}</h3>
|
||||||
|
|
||||||
<p>Join the <a href="http://groups.google.com/group/sphinx-users">Google group</a>:</p>
|
<p>{%trans%}Join the <a href="http://groups.google.com/group/sphinx-users">Google group</a>:{%endtrans%}</p>
|
||||||
<form action="http://groups.google.com/group/sphinx-users/boxsubscribe"
|
<form action="http://groups.google.com/group/sphinx-users/boxsubscribe"
|
||||||
style="padding-left: 0.5em">
|
style="padding-left: 0.5em">
|
||||||
<input type="text" name="email" value="your@email" style="font-size: 90%; width: 120px"
|
<input type="text" name="email" value="your@email" style="font-size: 90%; width: 120px"
|
||||||
onfocus="$(this).val('');"/>
|
onfocus="$(this).val('');"/>
|
||||||
<input type="submit" name="sub" value="Subscribe" style="font-size: 90%; width: 70px"/>
|
<input type="submit" name="sub" value="Subscribe" style="font-size: 90%; width: 70px"/>
|
||||||
</form>
|
</form>
|
||||||
<p>or come to the <tt>#pocoo</tt> channel on FreeNode.</p>
|
<p>{%trans%}or come to the <tt>#pocoo</tt> channel on FreeNode.{%endtrans%}</p>
|
||||||
<p>You can also open an issue at the
|
<p>{%trans%}You can also open an issue at the
|
||||||
<a href="http://www.bitbucket.org/birkenfeld/sphinx/issues/">tracker</a>.</p>
|
<a href="http://www.bitbucket.org/birkenfeld/sphinx/issues/">tracker</a>.{%endtrans%}</p>
|
||||||
|
@ -9,16 +9,17 @@
|
|||||||
:license: BSD, see LICENSE for details.
|
:license: BSD, see LICENSE for details.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from os import path
|
from os import path, walk
|
||||||
from codecs import open
|
from codecs import open
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
|
from uuid import uuid4
|
||||||
|
|
||||||
from sphinx.builders import Builder
|
from sphinx.builders import Builder
|
||||||
from sphinx.util import split_index_msg
|
from sphinx.util import split_index_msg
|
||||||
from sphinx.util.nodes import extract_messages, traverse_translatable_index
|
from sphinx.util.nodes import extract_messages, traverse_translatable_index
|
||||||
from sphinx.util.osutil import safe_relpath, ensuredir, find_catalog
|
from sphinx.util.osutil import safe_relpath, ensuredir, find_catalog, SEP
|
||||||
from sphinx.util.console import darkgreen
|
from sphinx.util.console import darkgreen, purple, bold
|
||||||
from sphinx.locale import pairindextypes
|
from sphinx.locale import pairindextypes
|
||||||
|
|
||||||
POHEADER = ur"""
|
POHEADER = ur"""
|
||||||
@ -57,6 +58,17 @@ class Catalog(object):
|
|||||||
self.metadata[msg].append((origin.source, origin.line, origin.uid))
|
self.metadata[msg].append((origin.source, origin.line, origin.uid))
|
||||||
|
|
||||||
|
|
||||||
|
class MsgOrigin(object):
|
||||||
|
"""
|
||||||
|
Origin holder for Catalog message origin.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, source, line):
|
||||||
|
self.source = source
|
||||||
|
self.line = line
|
||||||
|
self.uid = uuid4().hex
|
||||||
|
|
||||||
|
|
||||||
class I18nBuilder(Builder):
|
class I18nBuilder(Builder):
|
||||||
"""
|
"""
|
||||||
General i18n builder.
|
General i18n builder.
|
||||||
@ -101,6 +113,43 @@ class MessageCatalogBuilder(I18nBuilder):
|
|||||||
"""
|
"""
|
||||||
name = 'gettext'
|
name = 'gettext'
|
||||||
|
|
||||||
|
def init(self):
|
||||||
|
I18nBuilder.init(self)
|
||||||
|
self.create_template_bridge()
|
||||||
|
self.templates.init(self)
|
||||||
|
|
||||||
|
def _collect_templates(self):
|
||||||
|
template_files = set()
|
||||||
|
for template_path in self.config.templates_path:
|
||||||
|
tmpl_abs_path = path.join(self.app.srcdir, template_path)
|
||||||
|
for dirpath, dirs, files in walk(tmpl_abs_path):
|
||||||
|
for fn in files:
|
||||||
|
if fn.endswith('.html'):
|
||||||
|
filename = path.join(dirpath, fn)
|
||||||
|
filename = filename.replace(path.sep, SEP)
|
||||||
|
template_files.add(filename)
|
||||||
|
return template_files
|
||||||
|
|
||||||
|
def _extract_from_template(self):
|
||||||
|
files = self._collect_templates()
|
||||||
|
self.info(bold('building [%s]: ' % self.name), nonl=1)
|
||||||
|
self.info('targets for %d template files' % len(files))
|
||||||
|
|
||||||
|
extract_translations = self.templates.environment.extract_translations
|
||||||
|
|
||||||
|
for template in self.status_iterator(files,
|
||||||
|
'reading templates... ', purple, len(files)):
|
||||||
|
#catalog = self.catalogs[template]
|
||||||
|
catalog = self.catalogs['sphinx']
|
||||||
|
context = open(template, 'rt').read() #TODO: encoding
|
||||||
|
for line, meth, msg in extract_translations(context):
|
||||||
|
origin = MsgOrigin(template, line)
|
||||||
|
catalog.add(msg, origin)
|
||||||
|
|
||||||
|
def build(self, docnames, summary=None, method='update'):
|
||||||
|
self._extract_from_template()
|
||||||
|
I18nBuilder.build(self, docnames, summary, method)
|
||||||
|
|
||||||
def finish(self):
|
def finish(self):
|
||||||
I18nBuilder.finish(self)
|
I18nBuilder.finish(self)
|
||||||
data = dict(
|
data = dict(
|
||||||
@ -136,7 +185,8 @@ class MessageCatalogBuilder(I18nBuilder):
|
|||||||
|
|
||||||
# message contains *one* line of text ready for translation
|
# message contains *one* line of text ready for translation
|
||||||
message = message.replace(u'\\', ur'\\'). \
|
message = message.replace(u'\\', ur'\\'). \
|
||||||
replace(u'"', ur'\"')
|
replace(u'"', ur'\"'). \
|
||||||
|
replace(u'\n', u'\\n"\n"')
|
||||||
pofile.write(u'msgid "%s"\nmsgstr ""\n\n' % message)
|
pofile.write(u'msgid "%s"\nmsgstr ""\n\n' % message)
|
||||||
|
|
||||||
finally:
|
finally:
|
||||||
|
5
tests/roots/test-intl/_templates/index.html
Normal file
5
tests/roots/test-intl/_templates/index.html
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
{% extends "layout.html" %}
|
||||||
|
{% block body %}
|
||||||
|
<h1>{{ _('Welcome') }}</h1>
|
||||||
|
<p>{%trans%}Sphinx {{ version }}{%endtrans%}</p>
|
||||||
|
{% endblock %}
|
@ -5,3 +5,6 @@ import sys, os
|
|||||||
project = 'Sphinx intl <Tests>'
|
project = 'Sphinx intl <Tests>'
|
||||||
source_suffix = '.txt'
|
source_suffix = '.txt'
|
||||||
keep_warnings = True
|
keep_warnings = True
|
||||||
|
templates_path = ['_templates']
|
||||||
|
html_additional_pages = {'index': 'index.html'}
|
||||||
|
release = version = '2013.120'
|
||||||
|
23
tests/roots/test-intl/sphinx.po
Normal file
23
tests/roots/test-intl/sphinx.po
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
# SOME DESCRIPTIVE TITLE.
|
||||||
|
# Copyright (C) 2012, foof
|
||||||
|
# This file is distributed under the same license as the foo package.
|
||||||
|
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||||
|
#
|
||||||
|
#, fuzzy
|
||||||
|
msgid ""
|
||||||
|
msgstr ""
|
||||||
|
"Project-Id-Version: sphinx 1.0\n"
|
||||||
|
"Report-Msgid-Bugs-To: \n"
|
||||||
|
"POT-Creation-Date: 2012-11-22 08:28\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"
|
||||||
|
|
||||||
|
msgid "Welcome"
|
||||||
|
msgstr "WELCOME"
|
||||||
|
|
||||||
|
msgid "Sphinx %(version)s"
|
||||||
|
msgstr "SPHINX %(version)s"
|
@ -137,3 +137,15 @@ def test_gettext_index_entries(app):
|
|||||||
|
|
||||||
# unexpected msgid existent
|
# unexpected msgid existent
|
||||||
assert msgids == []
|
assert msgids == []
|
||||||
|
|
||||||
|
|
||||||
|
@with_app(buildername='gettext',
|
||||||
|
srcdir=(test_roots / 'test-intl'),
|
||||||
|
doctreedir=(test_roots / 'test-intl' / '_build' / 'doctree'))
|
||||||
|
def test_gettext_template(app):
|
||||||
|
app.builder.build_all()
|
||||||
|
assert (app.outdir / 'sphinx.pot').isfile()
|
||||||
|
|
||||||
|
result = (app.outdir / 'sphinx.pot').text(encoding='utf-8')
|
||||||
|
assert "Welcome" in result
|
||||||
|
assert "Sphinx %(version)s" in result
|
||||||
|
@ -420,3 +420,11 @@ def test_i18n_docfields_html(app):
|
|||||||
app.builder.build(['docfields'])
|
app.builder.build(['docfields'])
|
||||||
result = (app.outdir / 'docfields.html').text(encoding='utf-8')
|
result = (app.outdir / 'docfields.html').text(encoding='utf-8')
|
||||||
# expect no error by build
|
# expect no error by build
|
||||||
|
|
||||||
|
|
||||||
|
@with_intl_app(buildername='html')
|
||||||
|
def test_gettext_template(app):
|
||||||
|
app.builder.build_all()
|
||||||
|
result = (app.outdir / 'index.html').text(encoding='utf-8')
|
||||||
|
assert "WELCOME" in result
|
||||||
|
assert "SPHINX 2013.120" in result
|
||||||
|
Loading…
Reference in New Issue
Block a user