Merge branch 'main' into remote-follow

This commit is contained in:
Mouse Reeve 2021-12-02 12:16:56 -08:00 committed by GitHub
commit 4b55ffe99e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
24 changed files with 409 additions and 114 deletions

View file

@ -296,7 +296,7 @@ class ManyToManyField(ActivitypubFieldMixin, models.ManyToManyField):
super().__init__(*args, **kwargs)
def set_field_from_activity(self, instance, data, overwrite=True):
"""helper function for assinging a value to the field"""
"""helper function for assigning a value to the field"""
if not overwrite and getattr(instance, self.name).exists():
return False
@ -398,7 +398,11 @@ class ImageField(ActivitypubFieldMixin, models.ImageField):
if formatted is None or formatted is MISSING:
return False
if not overwrite and hasattr(instance, self.name):
if (
not overwrite
and hasattr(instance, self.name)
and getattr(instance, self.name)
):
return False
getattr(instance, self.name).save(*formatted, save=save)

View file

@ -45,6 +45,13 @@ let BookWyrm = new class {
'change',
this.disableIfTooLarge.bind(this)
));
document.querySelectorAll('[data-duplicate]')
.forEach(node => node.addEventListener(
'click',
this.duplicateInput.bind(this)
))
}
/**
@ -383,4 +390,24 @@ let BookWyrm = new class {
"left=100,top=100,width=430,height=600"
);
}
duplicateInput (event ) {
const trigger = event.currentTarget;
const input_id = trigger.dataset['duplicate']
const orig = document.getElementById(input_id);
const parent = orig.parentNode;
const new_count = parent.querySelectorAll("input").length + 1
let input = orig.cloneNode();
input.id += ("-" + (new_count))
input.value = ""
let label = parent.querySelector("label").cloneNode();
label.setAttribute("for", input.id)
parent.appendChild(label)
parent.appendChild(input)
}
}();

View file

@ -187,6 +187,7 @@ let StatusCache = new class {
.forEach(item => BookWyrm.addRemoveClass(item, "is-hidden", false));
// Remove existing disabled states
button.querySelectorAll("[data-shelf-dropdown-identifier] button")
.forEach(item => item.disabled = false);

View file

@ -153,12 +153,21 @@
{# user's relationship to the book #}
<div class="block">
{% if user_shelfbooks.count > 0 %}
<h2 class="title is-5">
{% trans "You have shelved this edition in:" %}
</h2>
<ul>
{% for shelf in user_shelfbooks %}
<p>
{% blocktrans with path=shelf.shelf.local_path shelf_name=shelf.shelf.name %}This edition is on your <a href="{{ path }}">{{ shelf_name }}</a> shelf.{% endblocktrans %}
{% include 'snippets/shelf_selector.html' with current=shelf.shelf %}
</p>
<li class="box">
{% blocktrans with path=shelf.shelf.local_path shelf_name=shelf.shelf.name %}<a href="{{ path }}">{{ shelf_name }}</a>{% endblocktrans %}
<div class="mb-3">
{% include 'snippets/shelf_selector.html' with shelf=shelf.shelf class="is-small" readthrough=readthrough %}
</div>
</li>
{% endfor %}
</ul>
{% endif %}
{% for shelf in other_edition_shelves %}
<p>
{% blocktrans with book_path=shelf.book.local_path shelf_path=shelf.shelf.local_path shelf_name=shelf.shelf.name %}A <a href="{{ book_path }}">different edition</a> of this book is on your <a href="{{ shelf_path }}">{{ shelf_name }}</a> shelf.{% endblocktrans %}

View file

@ -119,10 +119,16 @@
</fieldset>
{% endif %}
<div class="field">
<label class="label" for="id_add_author">{% trans "Add Authors:" %}</label>
<input class="input" type="text" name="add_author" id="id_add_author" placeholder="{% trans 'John Doe, Jane Smith' %}" value="{{ add_author }}" {% if confirm_mode %}readonly{% endif %}>
<span class="help">{% trans "Separate multiple values with commas." %}</span>
<label class="label">{% trans "Add Authors:" %}</label>
{% for author in add_author %}
<label class="label is-sr-only" for="id_add_author{% if not forloop.first %}-{{forloop.counter}}{% endif %}">{% trans "Add Author" %}</label>
<input class="input" type="text" name="add_author" id="id_add_author{% if not forloop.first %}-{{forloop.counter}}{% endif %}" placeholder="{% trans 'Jane Doe' %}" value="{{ author }}" {% if confirm_mode %}readonly{% endif %}>
{% empty %}
<label class="label is-sr-only" for="id_add_author">{% trans "Add Author" %}</label>
<input class="input" type="text" name="add_author" id="id_add_author" placeholder="{% trans 'Jane Doe' %}" value="{{ author }}" {% if confirm_mode %}readonly{% endif %}>
{% endfor %}
</div>
<span class="help"><button class="button is-small" type="button" data-duplicate="id_add_author" id="another_author_field">{% trans "Add Another Author" %}</button></span>
</div>
</section>
</div>

View file

@ -9,10 +9,11 @@ Finish "<em>{{ book_title }}</em>"
{% endblock %}
{% block modal-form-open %}
<form name="finish-reading" action="{% url 'reading-status' 'finish' book.id %}" method="post" class="submit-status">
<form name="finish-reading" action="{% url 'reading-status' 'finish' book.id %}" method="post" {% if not refresh %}class="submit-status"{% endif %}>
{% csrf_token %}
<input type="hidden" name="id" value="{{ readthrough.id }}">
<input type="hidden" name="reading_status" value="read">
<input type="hidden" name="shelf" value="{{ move_from }}">
{% endblock %}
{% block reading-dates %}

View file

@ -9,8 +9,9 @@ Start "<em>{{ book_title }}</em>"
{% endblock %}
{% block modal-form-open %}
<form name="start-reading" action="{% url 'reading-status' 'start' book.id %}" method="post" class="submit-status">
<form name="start-reading" action="{% url 'reading-status' 'start' book.id %}" method="post" {% if not refresh %}class="submit-status"{% endif %}>
<input type="hidden" name="reading_status" value="reading">
<input type="hidden" name="shelf" value="{{ move_from }}">
{% csrf_token %}
{% endblock %}

View file

@ -9,8 +9,9 @@ Want to Read "<em>{{ book_title }}</em>"
{% endblock %}
{% block modal-form-open %}
<form name="shelve" action="{% url 'reading-status' 'want' book.id %}" method="post" class="submit-status">
<form name="shelve" action="{% url 'reading-status' 'want' book.id %}" method="post" {% if not refresh %}class="submit-status"{% endif %}>
<input type="hidden" name="reading_status" value="to-read">
<input type="hidden" name="shelf" value="{{ move_from }}">
{% csrf_token %}
{% endblock %}

View file

@ -1,29 +1,88 @@
{% extends 'components/dropdown.html' %}
{% load i18n %}
{% load bookwyrm_tags %}
{% load utilities %}
{% block dropdown-trigger %}
<span>{% trans "Move book" %}</span>
<span class="icon icon-arrow-down" aria-hidden="true"></span>
{% endblock %}
{% block dropdown-list %}
{% with book.id|uuid as uuid %}
{% active_shelf book as active_shelf %}
{% latest_read_through book request.user as readthrough %}
{% for shelf in user_shelves %}
{% if shelf.editable %}
<li role="menuitem" class="dropdown-item p-0">
<form name="shelve" action="/shelve/" method="post">
{% csrf_token %}
<input type="hidden" name="book" value="{{ book.id }}">
<input type="hidden" name="change-shelf-from" value="{{ current.identifier }}">
<input type="hidden" name="shelf" value="{{ shelf.identifier }}">
<button class="button is-fullwidth is-small shelf-option is-radiusless is-white" type="submit" {% if shelf.identifier == current.identifier %}disabled{% endif %}><span>{{ shelf.name }}</span></button>
<button class="button is-fullwidth is-small shelf-option is-radiusless is-white" type="submit" {% if shelf in book.shelf_set.all %} disabled {% endif %}><span>{{ shelf.name }}</span></button>
</form>
</li>
{% else%}
{% comparison_bool shelf.identifier active_shelf.shelf.identifier as is_current %}
{% with button_class="is-fullwidth is-small shelf-option is-radiusless is-white" %}
<li role="menuitem" class="dropdown-item p-0">
{% if shelf.identifier == 'reading' %}
{% trans "Start reading" as button_text %}
{% url 'reading-status' 'start' book.id as fallback_url %}
{% include 'snippets/toggle/toggle_button.html' with class=button_class text=button_text controls_text="start_reading" controls_uid=uuid focus="modal_title_start_reading" disabled=is_current fallback_url=fallback_url %}
{% elif shelf.identifier == 'read' %}
{% trans "Read" as button_text %}
{% url 'reading-status' 'finish' book.id as fallback_url %}
{% include 'snippets/toggle/toggle_button.html' with class=button_class text=button_text controls_text="finish_reading" controls_uid=uuid focus="modal_title_finish_reading" disabled=is_current fallback_url=fallback_url %}
{% elif shelf.identifier == 'to-read' %}
{% trans "Want to read" as button_text %}
{% url 'reading-status' 'want' book.id as fallback_url %}
{% include 'snippets/toggle/toggle_button.html' with class=button_class text=button_text controls_text="want_to_read" controls_uid=uuid focus="modal_title_want_to_read" disabled=is_current fallback_url=fallback_url %}
{% endif %}
</li>
{% endwith %}
{% endif %}
{% endfor %}
<li class="navbar-divider" role="separator"></li>
{% if shelf.identifier == 'all' %}
{% for shelved_in in book.shelves.all %}
<li class="navbar-divider m-0" role="separator" ></li>
<li role="menuitem" class="dropdown-item p-0">
<form name="shelve" action="/unshelve/" method="post">
{% csrf_token %}
<input type="hidden" name="book" value="{{ book.id }}">
<input type="hidden" name="shelf" value="{{ current.id }}">
<button class="button is-fullwidth is-small is-radiusless is-danger is-light" type="submit">{% trans "Remove" %}</button>
<input type="hidden" name="shelf" value="{{ shelved_in.id }}">
<button class="button is-fullwidth is-small is-radiusless is-danger is-light" type="submit">{% trans "Remove from" %} {{ shelved_in.name }}</button>
</form>
</li>
{% endfor %}
{% else %}
<li class="navbar-divider" role="separator" ></li>
<li role="menuitem" class="dropdown-item p-0">
<form name="shelve" action="/unshelve/" method="post">
{% csrf_token %}
<input type="hidden" name="book" value="{{ book.id }}">
<input type="hidden" name="shelf" value="{{ shelf.id }}">
<button class="button is-fullwidth is-small is-radiusless is-danger is-light" type="submit">{% trans "Remove from" %} {{ shelf.name }}</button>
</form>
</li>
{% endif %}
{% include 'snippets/reading_modals/want_to_read_modal.html' with book=active_shelf.book controls_text="want_to_read" controls_uid=uuid move_from=current.id refresh=True %}
{% include 'snippets/reading_modals/start_reading_modal.html' with book=active_shelf.book controls_text="start_reading" controls_uid=uuid move_from=current.id refresh=True %}
{% include 'snippets/reading_modals/finish_reading_modal.html' with book=active_shelf.book controls_text="finish_reading" controls_uid=uuid move_from=current.id readthrough=readthrough refresh=True %}
{% endwith %}
{% endblock %}

View file

@ -32,7 +32,7 @@
{% elif shelf.editable %}
<form name="shelve" action="/shelve/" method="post">
<form name="shelve" action="/shelve/" method="post" autocomplete="off">
{% csrf_token %}
<input type="hidden" name="book" value="{{ active_shelf.book.id }}">
<button class="button {{ class }}" name="shelf" type="submit" value="{{ shelf.identifier }}" {% if shelf in book.shelf_set.all %} disabled {% endif %}>

View file

@ -1,6 +1,6 @@
{% load utilities %}
{% if fallback_url %}
<form name="fallback_form_{{ 0|uuid }}" method="GET" action="{{ fallback_url }}">
<form name="fallback_form_{{ 0|uuid }}" method="GET" action="{{ fallback_url }}" autocomplete="off">
{% endif %}
<button
{% if not fallback_url %}

View file

@ -77,7 +77,12 @@ def related_status(notification):
def active_shelf(context, book):
"""check what shelf a user has a book on, if any"""
if hasattr(book, "current_shelves"):
return book.current_shelves[0] if len(book.current_shelves) else {"book": book}
read_shelves = [
s
for s in book.current_shelves
if s.shelf.identifier in models.Shelf.READ_STATUS_IDENTIFIERS
]
return read_shelves[0] if len(read_shelves) else {"book": book}
shelf = (
models.ShelfBook.objects.filter(

View file

@ -19,7 +19,7 @@ from django.utils import timezone
from bookwyrm import activitypub
from bookwyrm.activitypub.base_activity import ActivityObject
from bookwyrm.models import fields, User, Status
from bookwyrm.models import fields, User, Status, Edition
from bookwyrm.models.base_model import BookWyrmModel
from bookwyrm.models.activitypub_mixin import ActivitypubMixin
from bookwyrm.settings import DOMAIN
@ -215,7 +215,7 @@ class ModelFields(TestCase):
"rat", "rat@rat.rat", "ratword", local=True, localname="rat"
)
public = "https://www.w3.org/ns/activitystreams#Public"
followers = "%s/followers" % user.remote_id
followers = f"{user.remote_id}/followers"
instance = fields.PrivacyField()
instance.name = "privacy_field"
@ -409,11 +409,10 @@ class ModelFields(TestCase):
"""loadin' a list of items from Links"""
# TODO
@responses.activate
@patch("bookwyrm.models.activitypub_mixin.ObjectMixin.broadcast")
@patch("bookwyrm.suggested_users.remove_user_task.delay")
def test_image_field(self, *_):
"""storing images"""
def test_image_field_to_activity(self, *_):
"""serialize an image field to activitypub"""
user = User.objects.create_user(
"mouse", "mouse@mouse.mouse", "mouseword", local=True, localname="mouse"
)
@ -437,16 +436,155 @@ class ModelFields(TestCase):
self.assertEqual(output.name, "")
self.assertEqual(output.type, "Document")
@responses.activate
def test_image_field_from_activity(self, *_):
"""load an image from activitypub"""
image_file = pathlib.Path(__file__).parent.joinpath(
"../../static/images/default_avi.jpg"
)
image = Image.open(image_file)
output = BytesIO()
image.save(output, format=image.format)
instance = fields.ImageField()
responses.add(
responses.GET,
"http://www.example.com/image.jpg",
body=user.avatar.file.read(),
body=image.tobytes(),
status=200,
)
loaded_image = instance.field_from_activity("http://www.example.com/image.jpg")
self.assertIsInstance(loaded_image, list)
self.assertIsInstance(loaded_image[1], ContentFile)
@responses.activate
def test_image_field_set_field_from_activity(self, *_):
"""update a model instance from an activitypub object"""
image_file = pathlib.Path(__file__).parent.joinpath(
"../../static/images/default_avi.jpg"
)
image = Image.open(image_file)
output = BytesIO()
image.save(output, format=image.format)
instance = fields.ImageField(activitypub_field="cover", name="cover")
responses.add(
responses.GET,
"http://www.example.com/image.jpg",
body=image.tobytes(),
status=200,
)
book = Edition.objects.create(title="hello")
MockActivity = namedtuple("MockActivity", ("cover"))
mock_activity = MockActivity("http://www.example.com/image.jpg")
instance.set_field_from_activity(book, mock_activity)
self.assertIsNotNone(book.cover.name)
self.assertEqual(book.cover.size, 43200)
@responses.activate
def test_image_field_set_field_from_activity_no_overwrite_no_cover(self, *_):
"""update a model instance from an activitypub object"""
image_file = pathlib.Path(__file__).parent.joinpath(
"../../static/images/default_avi.jpg"
)
image = Image.open(image_file)
output = BytesIO()
image.save(output, format=image.format)
instance = fields.ImageField(activitypub_field="cover", name="cover")
responses.add(
responses.GET,
"http://www.example.com/image.jpg",
body=image.tobytes(),
status=200,
)
book = Edition.objects.create(title="hello")
MockActivity = namedtuple("MockActivity", ("cover"))
mock_activity = MockActivity("http://www.example.com/image.jpg")
instance.set_field_from_activity(book, mock_activity, overwrite=False)
self.assertIsNotNone(book.cover.name)
self.assertEqual(book.cover.size, 43200)
@responses.activate
def test_image_field_set_field_from_activity_no_overwrite_with_cover(self, *_):
"""update a model instance from an activitypub object"""
image_file = pathlib.Path(__file__).parent.joinpath(
"../../static/images/default_avi.jpg"
)
image = Image.open(image_file)
output = BytesIO()
image.save(output, format=image.format)
another_image_file = pathlib.Path(__file__).parent.joinpath(
"../../static/images/logo.png"
)
another_image = Image.open(another_image_file)
another_output = BytesIO()
another_image.save(another_output, format=another_image.format)
instance = fields.ImageField(activitypub_field="cover", name="cover")
responses.add(
responses.GET,
"http://www.example.com/image.jpg",
body=another_image.tobytes(),
status=200,
)
book = Edition.objects.create(title="hello")
book.cover.save("test.jpg", ContentFile(output.getvalue()))
self.assertEqual(book.cover.size, 2136)
MockActivity = namedtuple("MockActivity", ("cover"))
mock_activity = MockActivity("http://www.example.com/image.jpg")
instance.set_field_from_activity(book, mock_activity, overwrite=False)
# same cover as before
self.assertEqual(book.cover.size, 2136)
@responses.activate
def test_image_field_set_field_from_activity_with_overwrite_with_cover(self, *_):
"""update a model instance from an activitypub object"""
image_file = pathlib.Path(__file__).parent.joinpath(
"../../static/images/default_avi.jpg"
)
image = Image.open(image_file)
output = BytesIO()
image.save(output, format=image.format)
book = Edition.objects.create(title="hello")
book.cover.save("test.jpg", ContentFile(output.getvalue()))
self.assertEqual(book.cover.size, 2136)
another_image_file = pathlib.Path(__file__).parent.joinpath(
"../../static/images/logo.png"
)
another_image = Image.open(another_image_file)
another_output = BytesIO()
another_image.save(another_output, format=another_image.format)
instance = fields.ImageField(activitypub_field="cover", name="cover")
responses.add(
responses.GET,
"http://www.example.com/image.jpg",
body=another_image.tobytes(),
status=200,
)
MockActivity = namedtuple("MockActivity", ("cover"))
mock_activity = MockActivity("http://www.example.com/image.jpg")
instance.set_field_from_activity(book, mock_activity, overwrite=True)
# new cover
self.assertIsNotNone(book.cover.name)
self.assertEqual(book.cover.size, 376800)
def test_datetime_field(self, *_):
"""this one is pretty simple, it just has to use isoformat"""
instance = fields.DateTimeField()

View file

@ -51,13 +51,14 @@ class EditBook(View):
if not form.is_valid():
return TemplateResponse(request, "book/edit/edit_book.html", data)
add_author = request.POST.get("add_author")
# we're adding an author through a free text field
# filter out empty author fields
add_author = [author for author in request.POST.getlist("add_author") if author]
if add_author:
data["add_author"] = add_author
data["author_matches"] = []
data["isni_matches"] = []
for author in add_author.split(","):
for author in add_author:
if not author:
continue
# check for existing authors

View file

@ -9,6 +9,7 @@ from django.views import View
from django.views.decorators.http import require_POST
from bookwyrm import models
from bookwyrm.views.shelf.shelf_actions import unshelve
from .status import CreateStatus
from .helpers import get_edition, handle_reading_status, is_api_request
from .helpers import load_date_in_user_tz_as_utc
@ -16,6 +17,7 @@ from .helpers import load_date_in_user_tz_as_utc
@method_decorator(login_required, name="dispatch")
# pylint: disable=no-self-use
# pylint: disable=too-many-return-statements
class ReadingStatus(View):
"""consider reading a book"""
@ -89,8 +91,21 @@ class ReadingStatus(View):
privacy = request.POST.get("privacy")
handle_reading_status(request.user, desired_shelf, book, privacy)
# if the request includes a "shelf" value we are using the 'move' button
if bool(request.POST.get("shelf")):
# unshelve the existing shelf
this_shelf = request.POST.get("shelf")
if (
bool(current_status_shelfbook)
and int(this_shelf) != int(current_status_shelfbook.shelf.id)
and current_status_shelfbook.shelf.identifier
!= desired_shelf.identifier
):
return unshelve(request, book_id=book_id)
if is_api_request(request):
return HttpResponse()
return redirect(referer)

View file

@ -91,13 +91,13 @@ def shelve(request):
@login_required
@require_POST
def unshelve(request):
def unshelve(request, book_id=False):
"""remove a book from a user's shelf"""
book = get_object_or_404(models.Edition, id=request.POST.get("book"))
identity = book_id if book_id else request.POST.get("book")
book = get_object_or_404(models.Edition, id=identity)
shelf_book = get_object_or_404(
models.ShelfBook, book=book, shelf__id=request.POST["shelf"]
)
shelf_book.raise_not_deletable(request.user)
shelf_book.delete()
return redirect(request.headers.get("Referer", "/"))

View file

@ -54,6 +54,7 @@ class CreateStatus(View):
data = {"book": book}
return TemplateResponse(request, "compose.html", data)
# pylint: disable=too-many-branches
def post(self, request, status_type, existing_status_id=None):
"""create status of whatever type"""
created = not existing_status_id
@ -117,11 +118,12 @@ class CreateStatus(View):
status.save(created=created)
# update a readthorugh, if needed
try:
edit_readthrough(request)
except Http404:
pass
# update a readthrough, if needed
if bool(request.POST.get("id")):
try:
edit_readthrough(request)
except Http404:
pass
if is_api_request(request):
return HttpResponse()

1
bw-dev
View file

@ -118,6 +118,7 @@ case "$CMD" in
git checkout l10n_main locale/pt_BR
git checkout l10n_main locale/zh_Hans
git checkout l10n_main locale/zh_Hant
runweb django-admin makemessages --no-wrap --ignore=venv -l en_US $@
runweb django-admin compilemessages --ignore venv
;;
build)

View file

@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: 0.0.1\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-11-17 18:03+0000\n"
"POT-Creation-Date: 2021-12-02 18:24+0000\n"
"PO-Revision-Date: 2021-02-28 17:19-0800\n"
"Last-Translator: Mouse Reeve <mousereeve@riseup.net>\n"
"Language-Team: English <LL@li.org>\n"
@ -227,47 +227,51 @@ msgstr ""
msgid "Something went wrong! Sorry about that."
msgstr ""
#: bookwyrm/templates/author/author.html:17
#: bookwyrm/templates/author/author.html:18
#: bookwyrm/templates/author/author.html:19
msgid "Edit Author"
msgstr ""
#: bookwyrm/templates/author/author.html:34
#: bookwyrm/templates/author/author.html:35
#: bookwyrm/templates/author/edit_author.html:43
msgid "Aliases:"
msgstr ""
#: bookwyrm/templates/author/author.html:45
#: bookwyrm/templates/author/author.html:46
msgid "Born:"
msgstr ""
#: bookwyrm/templates/author/author.html:52
#: bookwyrm/templates/author/author.html:53
msgid "Died:"
msgstr ""
#: bookwyrm/templates/author/author.html:61
#: bookwyrm/templates/author/author.html:62
msgid "Wikipedia"
msgstr ""
#: bookwyrm/templates/author/author.html:69
#: bookwyrm/templates/author/author.html:70
msgid "View ISNI record"
msgstr ""
#: bookwyrm/templates/author/author.html:78
#: bookwyrm/templates/book/book.html:94
msgid "View on OpenLibrary"
msgstr ""
#: bookwyrm/templates/author/author.html:77
#: bookwyrm/templates/author/author.html:86
#: bookwyrm/templates/book/book.html:97
msgid "View on Inventaire"
msgstr ""
#: bookwyrm/templates/author/author.html:85
#: bookwyrm/templates/author/author.html:94
msgid "View on LibraryThing"
msgstr ""
#: bookwyrm/templates/author/author.html:93
#: bookwyrm/templates/author/author.html:102
msgid "View on Goodreads"
msgstr ""
#: bookwyrm/templates/author/author.html:108
#: bookwyrm/templates/author/author.html:117
#, python-format
msgid "Books by %(name)s"
msgstr ""
@ -277,17 +281,17 @@ msgid "Edit Author:"
msgstr ""
#: bookwyrm/templates/author/edit_author.html:13
#: bookwyrm/templates/book/edit/edit_book.html:18
#: bookwyrm/templates/book/edit/edit_book.html:19
msgid "Added:"
msgstr ""
#: bookwyrm/templates/author/edit_author.html:14
#: bookwyrm/templates/book/edit/edit_book.html:21
#: bookwyrm/templates/book/edit/edit_book.html:22
msgid "Updated:"
msgstr ""
#: bookwyrm/templates/author/edit_author.html:16
#: bookwyrm/templates/book/edit/edit_book.html:25
#: bookwyrm/templates/book/edit/edit_book.html:26
msgid "Last edited by:"
msgstr ""
@ -347,7 +351,7 @@ msgstr ""
#: bookwyrm/templates/author/edit_author.html:118
#: bookwyrm/templates/book/book.html:140
#: bookwyrm/templates/book/edit/edit_book.html:110
#: bookwyrm/templates/book/edit/edit_book.html:121
#: bookwyrm/templates/book/readthrough.html:76
#: bookwyrm/templates/groups/form.html:24
#: bookwyrm/templates/lists/bookmark_button.html:15
@ -366,8 +370,8 @@ msgstr ""
#: bookwyrm/templates/author/edit_author.html:119
#: bookwyrm/templates/book/book.html:141 bookwyrm/templates/book/book.html:190
#: bookwyrm/templates/book/cover_modal.html:32
#: bookwyrm/templates/book/edit/edit_book.html:112
#: bookwyrm/templates/book/edit/edit_book.html:115
#: bookwyrm/templates/book/edit/edit_book.html:123
#: bookwyrm/templates/book/edit/edit_book.html:126
#: bookwyrm/templates/book/readthrough.html:77
#: bookwyrm/templates/groups/delete_group_modal.html:17
#: bookwyrm/templates/lists/delete_list_modal.html:17
@ -513,56 +517,60 @@ msgstr ""
msgid "Load cover from url:"
msgstr ""
#: bookwyrm/templates/book/edit/edit_book.html:5
#: bookwyrm/templates/book/edit/edit_book.html:11
#: bookwyrm/templates/book/edit/edit_book.html:6
#: bookwyrm/templates/book/edit/edit_book.html:12
#, python-format
msgid "Edit \"%(book_title)s\""
msgstr ""
#: bookwyrm/templates/book/edit/edit_book.html:5
#: bookwyrm/templates/book/edit/edit_book.html:13
#: bookwyrm/templates/book/edit/edit_book.html:6
#: bookwyrm/templates/book/edit/edit_book.html:14
msgid "Add Book"
msgstr ""
#: bookwyrm/templates/book/edit/edit_book.html:47
#: bookwyrm/templates/book/edit/edit_book.html:48
msgid "Confirm Book Info"
msgstr ""
#: bookwyrm/templates/book/edit/edit_book.html:55
#: bookwyrm/templates/book/edit/edit_book.html:56
#, python-format
msgid "Is \"%(name)s\" an existing author?"
msgid "Is \"%(name)s\" one of these authors?"
msgstr ""
#: bookwyrm/templates/book/edit/edit_book.html:64
#, python-format
msgid "Author of <em>%(book_title)s</em>"
#: bookwyrm/templates/book/edit/edit_book.html:67
#: bookwyrm/templates/book/edit/edit_book.html:69
msgid "Author of "
msgstr ""
#: bookwyrm/templates/book/edit/edit_book.html:68
#: bookwyrm/templates/book/edit/edit_book.html:69
msgid "Find more information at isni.org"
msgstr ""
#: bookwyrm/templates/book/edit/edit_book.html:79
msgid "This is a new author"
msgstr ""
#: bookwyrm/templates/book/edit/edit_book.html:75
#: bookwyrm/templates/book/edit/edit_book.html:86
#, python-format
msgid "Creating a new author: %(name)s"
msgstr ""
#: bookwyrm/templates/book/edit/edit_book.html:82
#: bookwyrm/templates/book/edit/edit_book.html:93
msgid "Is this an edition of an existing work?"
msgstr ""
#: bookwyrm/templates/book/edit/edit_book.html:90
#: bookwyrm/templates/book/edit/edit_book.html:101
msgid "This is a new work"
msgstr ""
#: bookwyrm/templates/book/edit/edit_book.html:97
#: bookwyrm/templates/book/edit/edit_book.html:108
#: bookwyrm/templates/groups/members.html:16
#: bookwyrm/templates/landing/password_reset.html:30
#: bookwyrm/templates/snippets/remove_from_group_button.html:16
msgid "Confirm"
msgstr ""
#: bookwyrm/templates/book/edit/edit_book.html:99
#: bookwyrm/templates/book/edit/edit_book.html:110
#: bookwyrm/templates/feed/status.html:9
msgid "Back"
msgstr ""
@ -1016,6 +1024,22 @@ msgstr ""
msgid "Learn more about %(site_name)s:"
msgstr ""
#: bookwyrm/templates/email/moderation_report/html_content.html:6
#: bookwyrm/templates/email/moderation_report/text_content.html:5
#, python-format
msgid "@%(reporter)s has flagged behavior by @%(reportee)s for moderation. "
msgstr ""
#: bookwyrm/templates/email/moderation_report/html_content.html:9
#: bookwyrm/templates/email/moderation_report/text_content.html:7
msgid "View report"
msgstr ""
#: bookwyrm/templates/email/moderation_report/subject.html:2
#, python-format
msgid "New report for %(site_name)s"
msgstr ""
#: bookwyrm/templates/email/password_reset/html_content.html:6
#: bookwyrm/templates/email/password_reset/text_content.html:4
#, python-format
@ -3842,7 +3866,7 @@ msgstr ""
msgid "File exceeds maximum size: 10MB"
msgstr ""
#: bookwyrm/templatetags/utilities.py:31
#: bookwyrm/templatetags/utilities.py:34
#, python-format
msgid "%(title)s: %(subtitle)s"
msgstr ""

Binary file not shown.

Binary file not shown.

View file

@ -3,7 +3,7 @@ msgstr ""
"Project-Id-Version: bookwyrm\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-11-17 18:03+0000\n"
"PO-Revision-Date: 2021-11-22 08:50\n"
"PO-Revision-Date: 2021-11-29 13:53\n"
"Last-Translator: Mouse Reeve <mousereeve@riseup.net>\n"
"Language-Team: Lithuanian\n"
"Language: lt\n"
@ -591,7 +591,7 @@ msgstr "Kalbos:"
#: bookwyrm/templates/book/edit/edit_book_form.html:74
msgid "Publication"
msgstr "Paskelbimas"
msgstr "Leidimas"
#: bookwyrm/templates/book/edit/edit_book_form.html:77
msgid "Publisher:"
@ -930,7 +930,7 @@ msgstr "<a href=\"%(user_path)s\">%(username)s</a> įvertino <a href=\"%(book_pa
#: bookwyrm/templates/discover/card-header.html:27
#, python-format
msgid "<a href=\"%(user_path)s\">%(username)s</a> reviewed <a href=\"%(book_path)s\">%(book_title)s</a>"
msgstr "<a href=\"%(user_path)s\">%(username)s</a> peržiūrėjo <a href=\"%(book_path)s\">%(book_title)s</a>"
msgstr "<a href=\"%(user_path)s\">%(username)s</a> apžvelgė <a href=\"%(book_path)s\">%(book_title)s</a>"
#: bookwyrm/templates/discover/card-header.html:31
#, python-format
@ -1114,7 +1114,7 @@ msgstr "Šiuo metu skaitoma"
#: bookwyrm/templates/snippets/shelve_button/shelve_button_options.html:12
#: bookwyrm/templates/user/books_header.html:8
msgid "Read"
msgstr "Perskaičiau"
msgstr "Perskaityta"
#: bookwyrm/templates/feed/suggested_users.html:5
#: bookwyrm/templates/get_started/users.html:6
@ -1483,7 +1483,7 @@ msgstr "Lentyna"
#: bookwyrm/templates/import/manual_review.html:13
#: bookwyrm/templates/snippets/create_status.html:17
msgid "Review"
msgstr "Peržiūra"
msgstr "Apžvalga"
#: bookwyrm/templates/import/import_status.html:119
msgid "Book"
@ -1513,7 +1513,7 @@ msgstr "Importuota"
#: bookwyrm/templates/import/import_status.html:182
msgid "Needs manual review"
msgstr "Reikalingas manualus atsiliepimas"
msgstr "Reikalinga peržvelgti"
#: bookwyrm/templates/import/import_status.html:195
msgid "Retry"
@ -2416,7 +2416,7 @@ msgstr[3] "%(display_count)s prašymai pakviesti"
#: bookwyrm/templates/settings/dashboard/dashboard.html:65
msgid "Instance Activity"
msgstr "Pavyzdinė veikla"
msgstr "Serverio statistika"
#: bookwyrm/templates/settings/dashboard/dashboard.html:83
msgid "Interval:"
@ -2436,7 +2436,7 @@ msgstr "Naudotojo prisijungimo veikla"
#: bookwyrm/templates/settings/dashboard/dashboard.html:112
msgid "Status activity"
msgstr "Būsenos veikla"
msgstr "Būsenos"
#: bookwyrm/templates/settings/dashboard/dashboard.html:118
msgid "Works created"
@ -3467,7 +3467,7 @@ msgstr "%(percent)s%% baigta!"
#: bookwyrm/templates/snippets/goal_progress.html:12
#, python-format
msgid "You've read <a href=\"%(path)s\">%(read_count)s of %(goal_count)s books</a>."
msgstr "Perskaitėte <a href=\"%(path)s\">%(read_count)s iš %(goal_count)s knygų</a>."
msgstr "Perskaityta <a href=\"%(path)s\">%(read_count)s iš %(goal_count)s knygų</a>."
#: bookwyrm/templates/snippets/goal_progress.html:14
#, python-format
@ -3504,7 +3504,7 @@ msgstr "Viešas"
#: bookwyrm/templates/snippets/privacy_select.html:14
#: bookwyrm/templates/snippets/privacy_select_no_followers.html:14
msgid "Unlisted"
msgstr "Nėra sąraše"
msgstr "Slaptas"
#: bookwyrm/templates/snippets/privacy-icons.html:12
msgid "Followers-only"
@ -3873,7 +3873,7 @@ msgstr[3] "%(mutuals_display)s sekėjai, kuriuos sekate jūs"
#: bookwyrm/templates/user/user_preview.html:38
msgid "No followers you follow"
msgstr "Jūs nieko nesekate"
msgstr "Jūs kartu nieko nesekate"
#: bookwyrm/templates/widgets/clearable_file_input_with_warning.html:28
msgid "File exceeds maximum size: 10MB"

Binary file not shown.

View file

@ -3,7 +3,7 @@ msgstr ""
"Project-Id-Version: bookwyrm\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-11-17 18:03+0000\n"
"PO-Revision-Date: 2021-11-17 18:42\n"
"PO-Revision-Date: 2021-11-29 09:31\n"
"Last-Translator: Mouse Reeve <mousereeve@riseup.net>\n"
"Language-Team: Chinese Simplified\n"
"Language: zh\n"
@ -184,7 +184,7 @@ msgstr "Español西班牙语"
#: bookwyrm/settings.py:168
msgid "Galego (Galician)"
msgstr ""
msgstr "Galego加利西亚语"
#: bookwyrm/settings.py:169
msgid "Français (French)"
@ -192,11 +192,11 @@ msgstr "Français法语"
#: bookwyrm/settings.py:170
msgid "Lietuvių (Lithuanian)"
msgstr ""
msgstr "Lietuvių立陶宛语"
#: bookwyrm/settings.py:171
msgid "Português - Brasil (Brazilian Portuguese)"
msgstr "葡萄牙语-巴西(巴西的葡语)"
msgstr "Português - Brasil巴西葡萄牙语)"
#: bookwyrm/settings.py:172
msgid "简体中文 (Simplified Chinese)"
@ -906,12 +906,12 @@ msgstr "<a href=\"%(user_path)s\">%(username)s</a> 想要阅读 <a href=\"%(book
#: bookwyrm/templates/discover/card-header.html:13
#, python-format
msgid "<a href=\"%(user_path)s\">%(username)s</a> finished reading <a href=\"%(book_path)s\">%(book_title)s</a>"
msgstr ""
msgstr "<a href=\"%(user_path)s\">%(username)s</a> 完成了 <a href=\"%(book_path)s\">%(book_title)s</a> 的阅读"
#: bookwyrm/templates/discover/card-header.html:18
#, python-format
msgid "<a href=\"%(user_path)s\">%(username)s</a> started reading <a href=\"%(book_path)s\">%(book_title)s</a>"
msgstr ""
msgstr "<a href=\"%(user_path)s\">%(username)s</a> 开始阅读 <a href=\"%(book_path)s\">%(book_title)s</a>"
#: bookwyrm/templates/discover/card-header.html:23
#, python-format
@ -1395,11 +1395,11 @@ msgstr "导入状态"
#: bookwyrm/templates/import/import_status.html:13
#: bookwyrm/templates/import/import_status.html:27
msgid "Retry Status"
msgstr ""
msgstr "重试状态"
#: bookwyrm/templates/import/import_status.html:22
msgid "Imports"
msgstr ""
msgstr "导入"
#: bookwyrm/templates/import/import_status.html:39
msgid "Import started:"
@ -1407,36 +1407,36 @@ msgstr "导入开始:"
#: bookwyrm/templates/import/import_status.html:48
msgid "In progress"
msgstr ""
msgstr "正在进行"
#: bookwyrm/templates/import/import_status.html:50
msgid "Refresh"
msgstr ""
msgstr "刷新"
#: bookwyrm/templates/import/import_status.html:71
#, python-format
msgid "%(display_counter)s item needs manual approval."
msgid_plural "%(display_counter)s items need manual approval."
msgstr[0] ""
msgstr[0] "%(display_counter)s 项需要手动批准。"
#: bookwyrm/templates/import/import_status.html:76
#: bookwyrm/templates/import/manual_review.html:8
msgid "Review items"
msgstr ""
msgstr "审阅项目"
#: bookwyrm/templates/import/import_status.html:82
#, python-format
msgid "%(display_counter)s item failed to import."
msgid_plural "%(display_counter)s items failed to import."
msgstr[0] ""
msgstr[0] "%(display_counter)s 项导入失败。"
#: bookwyrm/templates/import/import_status.html:88
msgid "View and troubleshoot failed items"
msgstr ""
msgstr "查看并排查失败项目"
#: bookwyrm/templates/import/import_status.html:100
msgid "Row"
msgstr ""
msgstr ""
#: bookwyrm/templates/import/import_status.html:103
#: bookwyrm/templates/shelf/shelf.html:141
@ -1446,7 +1446,7 @@ msgstr "标题"
#: bookwyrm/templates/import/import_status.html:106
msgid "ISBN"
msgstr ""
msgstr "ISBN"
#: bookwyrm/templates/import/import_status.html:109
#: bookwyrm/templates/shelf/shelf.html:142
@ -1456,7 +1456,7 @@ msgstr "作者"
#: bookwyrm/templates/import/import_status.html:112
msgid "Shelf"
msgstr ""
msgstr "书架"
#: bookwyrm/templates/import/import_status.html:115
#: bookwyrm/templates/import/manual_review.html:13
@ -1480,11 +1480,11 @@ msgstr "状态"
#: bookwyrm/templates/import/import_status.html:130
msgid "Import preview unavailable."
msgstr ""
msgstr "导入预览不可用。"
#: bookwyrm/templates/import/import_status.html:162
msgid "View imported review"
msgstr ""
msgstr "查看已导入的书评"
#: bookwyrm/templates/import/import_status.html:176
msgid "Imported"
@ -1492,28 +1492,28 @@ msgstr "已导入"
#: bookwyrm/templates/import/import_status.html:182
msgid "Needs manual review"
msgstr ""
msgstr "需要手动批准"
#: bookwyrm/templates/import/import_status.html:195
msgid "Retry"
msgstr ""
msgstr "重试"
#: bookwyrm/templates/import/import_status.html:213
msgid "This import is in an old format that is no longer supported. If you would like to troubleshoot missing items from this import, click the button below to update the import format."
msgstr ""
msgstr "此导入所用格式已不再受支持。 如果您想要在此次导入中排查缺失的项目,请点击下面的按钮来更新导入格式。"
#: bookwyrm/templates/import/import_status.html:215
msgid "Update import"
msgstr ""
msgstr "更新导入"
#: bookwyrm/templates/import/manual_review.html:5
#: bookwyrm/templates/import/troubleshoot.html:4
msgid "Import Troubleshooting"
msgstr ""
msgstr "导入排查"
#: bookwyrm/templates/import/manual_review.html:21
msgid "Approving a suggestion will permanently add the suggested book to your shelves and associate your reading dates, reviews, and ratings with that book."
msgstr ""
msgstr "批准建议后,被提议的书将会永久添加到您的书架上并与您的阅读日期、书评、评分联系起来。"
#: bookwyrm/templates/import/manual_review.html:58
#: bookwyrm/templates/lists/curate.html:57
@ -1522,7 +1522,7 @@ msgstr "批准"
#: bookwyrm/templates/import/manual_review.html:66
msgid "Reject"
msgstr ""
msgstr "驳回"
#: bookwyrm/templates/import/tooltip.html:6
msgid "You can download your Goodreads data from the <a href=\"https://www.goodreads.com/review/import\" target=\"_blank\" rel=\"noopener\">Import/Export page</a> of your Goodreads account."
@ -1530,31 +1530,31 @@ msgstr "您可以从 <a href=\"https://www.goodreads.com/review/import\" target=
#: bookwyrm/templates/import/troubleshoot.html:7
msgid "Failed items"
msgstr ""
msgstr "失败项目"
#: bookwyrm/templates/import/troubleshoot.html:12
msgid "Troubleshooting"
msgstr ""
msgstr "问题排查"
#: bookwyrm/templates/import/troubleshoot.html:20
msgid "Re-trying an import can fix missing items in cases such as:"
msgstr ""
msgstr "重试导入可以修复以下情况的缺失项目:"
#: bookwyrm/templates/import/troubleshoot.html:23
msgid "The book has been added to the instance since this import"
msgstr ""
msgstr "在此次导入后该书已被添加到本实例"
#: bookwyrm/templates/import/troubleshoot.html:24
msgid "A transient error or timeout caused the external data source to be unavailable."
msgstr ""
msgstr "暂时的错误或超时导致外部数据源不可用。"
#: bookwyrm/templates/import/troubleshoot.html:25
msgid "BookWyrm has been updated since this import with a bug fix"
msgstr ""
msgstr "在此次导入后BookWyrm 已经更新并修复了漏洞"
#: bookwyrm/templates/import/troubleshoot.html:28
msgid "Contact your admin or <a href='https://github.com/bookwyrm-social/bookwyrm/issues'>open an issue</a> if you are seeing unexpected failed items."
msgstr ""
msgstr "如果您看到意外失败的项目,请联系您的管理员或 <a href='https://github.com/bookwyrm-social/bookwyrm/issues'>发起一个 issue</a>。"
#: bookwyrm/templates/landing/about.html:7 bookwyrm/templates/layout.html:230
#, python-format