diff --git a/bookwyrm/importers/importer.py b/bookwyrm/importers/importer.py index 203db0343..d5f1449ca 100644 --- a/bookwyrm/importers/importer.py +++ b/bookwyrm/importers/importer.py @@ -2,6 +2,8 @@ import csv import logging +from django.utils import timezone + from bookwyrm import models from bookwyrm.models import ImportJob, ImportItem from bookwyrm.tasks import app @@ -100,7 +102,10 @@ def handle_imported_book(source, user, item, include_reviews, privacy): # shelve the book if it hasn't been shelved already if item.shelf and not existing_shelf: desired_shelf = models.Shelf.objects.get(identifier=item.shelf, user=user) - models.ShelfBook.objects.create(book=item.book, shelf=desired_shelf, user=user) + shelved_date = item.date_added or timezone.now() + models.ShelfBook.objects.create( + book=item.book, shelf=desired_shelf, user=user, shelved_date=shelved_date + ) for read in item.reads: # check for an existing readthrough with the same dates diff --git a/bookwyrm/migrations/0078_add_shelved_date.py b/bookwyrm/migrations/0078_add_shelved_date.py new file mode 100644 index 000000000..b8a95ab17 --- /dev/null +++ b/bookwyrm/migrations/0078_add_shelved_date.py @@ -0,0 +1,34 @@ +# Generated by Django 3.2.4 on 2021-07-03 08:25 + +from django.db import migrations, models +import django.utils.timezone + + +def copy_created_date(app_registry, schema_editor): + db_alias = schema_editor.connection.alias + ShelfBook = app_registry.get_model("bookwyrm", "ShelfBook") + ShelfBook.objects.all().update(shelved_date=models.F("created_date")) + + +def do_nothing(app_registry, schema_editor): + pass + + +class Migration(migrations.Migration): + + dependencies = [ + ("bookwyrm", "0077_auto_20210623_2155"), + ] + + operations = [ + migrations.AlterModelOptions( + name="shelfbook", + options={"ordering": ("-shelved_date",)}, + ), + migrations.AddField( + model_name="shelfbook", + name="shelved_date", + field=models.DateTimeField(default=django.utils.timezone.now), + ), + migrations.RunPython(copy_created_date, reverse_code=do_nothing), + ] diff --git a/bookwyrm/models/activitypub_mixin.py b/bookwyrm/models/activitypub_mixin.py index 83b4c0abe..729d9cba0 100644 --- a/bookwyrm/models/activitypub_mixin.py +++ b/bookwyrm/models/activitypub_mixin.py @@ -30,6 +30,7 @@ logger = logging.getLogger(__name__) PropertyField = namedtuple("PropertyField", ("set_activity_from_field")) +# pylint: disable=invalid-name def set_activity_from_property_field(activity, obj, field): """assign a model property value to the activity json""" activity[field[1]] = getattr(obj, field[0]) @@ -318,7 +319,9 @@ class OrderedCollectionPageMixin(ObjectMixin): remote_id = remote_id or self.remote_id if page: - return to_ordered_collection_page(queryset, remote_id, **kwargs) + if isinstance(page, list) and len(page) > 0: + page = page[0] + return to_ordered_collection_page(queryset, remote_id, page=page, **kwargs) if collection_only or not hasattr(self, "activity_serializer"): serializer = activitypub.OrderedCollection diff --git a/bookwyrm/models/shelf.py b/bookwyrm/models/shelf.py index 4110ae8dc..c4e907d27 100644 --- a/bookwyrm/models/shelf.py +++ b/bookwyrm/models/shelf.py @@ -1,6 +1,7 @@ """ puttin' books on shelves """ import re from django.db import models +from django.utils import timezone from bookwyrm import activitypub from .activitypub_mixin import CollectionItemMixin, OrderedCollectionMixin @@ -69,6 +70,7 @@ class ShelfBook(CollectionItemMixin, BookWyrmModel): "Edition", on_delete=models.PROTECT, activitypub_field="book" ) shelf = models.ForeignKey("Shelf", on_delete=models.PROTECT) + shelved_date = models.DateTimeField(default=timezone.now) user = fields.ForeignKey( "User", on_delete=models.PROTECT, activitypub_field="actor" ) @@ -86,4 +88,4 @@ class ShelfBook(CollectionItemMixin, BookWyrmModel): you can't put a book on shelf twice""" unique_together = ("book", "shelf") - ordering = ("-created_date",) + ordering = ("-shelved_date", "-created_date", "-updated_date") diff --git a/bookwyrm/static/css/bookwyrm.css b/bookwyrm/static/css/bookwyrm.css index 3db25d1fe..d10fb9b7e 100644 --- a/bookwyrm/static/css/bookwyrm.css +++ b/bookwyrm/static/css/bookwyrm.css @@ -72,6 +72,14 @@ body { flex-grow: 1; } +.preserve-whitespace p { + white-space: pre-wrap !important; +} + +.display-inline p { + display: inline !important; +} + /** Shelving ******************************************************************************/ diff --git a/bookwyrm/templates/book/book.html b/bookwyrm/templates/book/book.html index f93420d31..c5dab1097 100644 --- a/bookwyrm/templates/book/book.html +++ b/bookwyrm/templates/book/book.html @@ -52,7 +52,7 @@ {% if user_authenticated and can_edit_book %}
- + {% trans "Edit Book" %} @@ -214,24 +214,24 @@ diff --git a/bookwyrm/templates/directory/user_card.html b/bookwyrm/templates/directory/user_card.html index c52c1f7ae..b7941826b 100644 --- a/bookwyrm/templates/directory/user_card.html +++ b/bookwyrm/templates/directory/user_card.html @@ -18,7 +18,7 @@
-
+
{% if user.summary %} {{ user.summary|to_markdown|safe|truncatechars_html:40 }} {% else %} {% endif %} diff --git a/bookwyrm/templates/feed/direct_messages.html b/bookwyrm/templates/feed/direct_messages.html index 0b7dcd79c..30a50cd0a 100644 --- a/bookwyrm/templates/feed/direct_messages.html +++ b/bookwyrm/templates/feed/direct_messages.html @@ -1,4 +1,4 @@ -{% extends 'feed/feed_layout.html' %} +{% extends 'feed/layout.html' %} {% load i18n %} {% block panel %} diff --git a/bookwyrm/templates/feed/feed.html b/bookwyrm/templates/feed/feed.html index 21e71ae18..44ee5c930 100644 --- a/bookwyrm/templates/feed/feed.html +++ b/bookwyrm/templates/feed/feed.html @@ -1,4 +1,4 @@ -{% extends 'feed/feed_layout.html' %} +{% extends 'feed/layout.html' %} {% load i18n %} {% block panel %} diff --git a/bookwyrm/templates/feed/feed_layout.html b/bookwyrm/templates/feed/feed_layout.html deleted file mode 100644 index 07784418a..000000000 --- a/bookwyrm/templates/feed/feed_layout.html +++ /dev/null @@ -1,108 +0,0 @@ -{% extends 'layout.html' %} -{% load i18n %} -{% load static %} - -{% block title %}{% trans "Updates" %}{% endblock %} - -{% block content %} -
- {% if user.is_authenticated %} -
-

{% trans "Your books" %}

- {% if not suggested_books %} -

{% trans "There are no books here right now! Try searching for a book to get started" %}

- {% else %} - {% with active_book=request.GET.book %} -
-
-
    - {% for shelf in suggested_books %} - {% if shelf.books %} - {% with shelf_counter=forloop.counter %} -
  • -

    - {% if shelf.identifier == 'to-read' %}{% trans "To Read" %} - {% elif shelf.identifier == 'reading' %}{% trans "Currently Reading" %} - {% elif shelf.identifier == 'read' %}{% trans "Read" %} - {% else %}{{ shelf.name }}{% endif %} -

    -
    - -
    -
  • - {% endwith %} - {% endif %} - {% endfor %} -
-
- {% for shelf in suggested_books %} - {% with shelf_counter=forloop.counter %} - {% for book in shelf.books %} -
- -
-
-
-

{% include 'snippets/book_titleby.html' with book=book %}

- {% include 'snippets/shelve_button/shelve_button.html' with book=book %} -
-
-
- {% trans "Close" as button_text %} - {% include 'snippets/toggle/toggle_button.html' with label=button_text controls_text="book" controls_uid=book.id class="delete" nonbutton=True pressed=True %} -
-
-
- {% include 'snippets/create_status.html' with book=book %} -
-
- {% endfor %} - {% endwith %} - {% endfor %} -
- {% endwith %} - {% endif %} - - {% if goal %} -
-
-

{% blocktrans with yar=goal.year %}{{ year }} Reading Goal{% endblocktrans %}

- {% include 'snippets/goal_progress.html' with goal=goal %} -
-
- {% endif %} -
- {% endif %} - -
- {% block panel %}{% endblock %} - - {% if activities %} - {% include 'snippets/pagination.html' with page=activities path=path anchor="#feed" %} - {% endif %} -
-
-{% endblock %} - -{% block scripts %} - -{% endblock %} diff --git a/bookwyrm/templates/feed/layout.html b/bookwyrm/templates/feed/layout.html new file mode 100644 index 000000000..57305df72 --- /dev/null +++ b/bookwyrm/templates/feed/layout.html @@ -0,0 +1,109 @@ +{% extends 'layout.html' %} +{% load i18n %} + +{% block title %}{% trans "Updates" %}{% endblock %} + +{% block content %} +
+ {% if user.is_authenticated %} +
+
+

{% trans "Your books" %}

+ {% if not suggested_books %} +

{% trans "There are no books here right now! Try searching for a book to get started" %}

+ {% else %} + {% with active_book=request.GET.book %} +
+
+
    + {% for shelf in suggested_books %} + {% if shelf.books %} + {% with shelf_counter=forloop.counter %} +
  • +

    + {% if shelf.identifier == 'to-read' %}{% trans "To Read" %} + {% elif shelf.identifier == 'reading' %}{% trans "Currently Reading" %} + {% elif shelf.identifier == 'read' %}{% trans "Read" %} + {% else %}{{ shelf.name }}{% endif %} +

    +
    + +
    +
  • + {% endwith %} + {% endif %} + {% endfor %} +
+
+ {% for shelf in suggested_books %} + {% with shelf_counter=forloop.counter %} + {% for book in shelf.books %} +
+ +
+
+
+

{% include 'snippets/book_titleby.html' with book=book %}

+ {% include 'snippets/shelve_button/shelve_button.html' with book=book %} +
+
+
+ {% trans "Close" as button_text %} + {% include 'snippets/toggle/toggle_button.html' with label=button_text controls_text="book" controls_uid=book.id class="delete" nonbutton=True pressed=True %} +
+
+
+ {% include 'snippets/create_status.html' with book=book %} +
+
+ {% endfor %} + {% endwith %} + {% endfor %} +
+ {% endwith %} + {% endif %} +
+ + {% if goal %} +
+
+

{% blocktrans with yar=goal.year %}{{ year }} Reading Goal{% endblocktrans %}

+ {% include 'snippets/goal_progress.html' with goal=goal %} +
+
+ {% endif %} +
+ {% endif %} + +
+ {% block panel %}{% endblock %} + + {% if activities %} + {% include 'snippets/pagination.html' with page=activities path=path anchor="#feed" %} + {% endif %} +
+
+{% endblock %} + +{% block scripts %} + +{% endblock %} diff --git a/bookwyrm/templates/feed/status.html b/bookwyrm/templates/feed/status.html index 9f90f355d..903ca7907 100644 --- a/bookwyrm/templates/feed/status.html +++ b/bookwyrm/templates/feed/status.html @@ -1,4 +1,4 @@ -{% extends 'feed/feed_layout.html' %} +{% extends 'feed/layout.html' %} {% load i18n %} {% block panel %} diff --git a/bookwyrm/templates/user/layout.html b/bookwyrm/templates/user/layout.html index 69455806e..41d52812f 100644 --- a/bookwyrm/templates/user/layout.html +++ b/bookwyrm/templates/user/layout.html @@ -28,8 +28,10 @@
{% if user.summary %} -
+ {% spaceless %} +
{{ user.summary|to_markdown|safe }} + {% endspaceless %}
{% endif %}
diff --git a/bookwyrm/templates/user/shelf/shelf.html b/bookwyrm/templates/user/shelf/shelf.html index 2163db8cb..01a7eee9e 100644 --- a/bookwyrm/templates/user/shelf/shelf.html +++ b/bookwyrm/templates/user/shelf/shelf.html @@ -103,7 +103,7 @@ {% include 'snippets/authors.html' %} - {{ book.created_date|naturalday }} + {{ book.shelved_date|naturalday }} {% latest_read_through book user as read_through %} diff --git a/bookwyrm/tests/importers/test_goodreads_import.py b/bookwyrm/tests/importers/test_goodreads_import.py index 81f47e6f5..499444748 100644 --- a/bookwyrm/tests/importers/test_goodreads_import.py +++ b/bookwyrm/tests/importers/test_goodreads_import.py @@ -3,6 +3,8 @@ from collections import namedtuple import csv import pathlib from unittest.mock import patch +import datetime +import pytz from django.test import TestCase import responses @@ -13,6 +15,10 @@ from bookwyrm.importers.importer import import_data, handle_imported_book from bookwyrm.settings import DOMAIN +def make_date(*args): + return datetime.datetime(*args, tzinfo=pytz.UTC) + + class GoodreadsImport(TestCase): """importing from goodreads csv""" @@ -130,22 +136,25 @@ class GoodreadsImport(TestCase): shelf.refresh_from_db() self.assertEqual(shelf.books.first(), self.book) + self.assertEqual( + shelf.shelfbook_set.first().shelved_date, make_date(2020, 10, 21) + ) readthrough = models.ReadThrough.objects.get(user=self.user) self.assertEqual(readthrough.book, self.book) - # I can't remember how to create dates and I don't want to look it up. - self.assertEqual(readthrough.start_date.year, 2020) - self.assertEqual(readthrough.start_date.month, 10) - self.assertEqual(readthrough.start_date.day, 21) - self.assertEqual(readthrough.finish_date.year, 2020) - self.assertEqual(readthrough.finish_date.month, 10) - self.assertEqual(readthrough.finish_date.day, 25) + self.assertEqual(readthrough.start_date, make_date(2020, 10, 21)) + self.assertEqual(readthrough.finish_date, make_date(2020, 10, 25)) def test_handle_imported_book_already_shelved(self): """goodreads import added a book, this adds related connections""" with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): shelf = self.user.shelf_set.filter(identifier="to-read").first() - models.ShelfBook.objects.create(shelf=shelf, user=self.user, book=self.book) + models.ShelfBook.objects.create( + shelf=shelf, + user=self.user, + book=self.book, + shelved_date=make_date(2020, 2, 2), + ) import_job = models.ImportJob.objects.create(user=self.user) datafile = pathlib.Path(__file__).parent.joinpath("../data/goodreads.csv") @@ -164,15 +173,15 @@ class GoodreadsImport(TestCase): shelf.refresh_from_db() self.assertEqual(shelf.books.first(), self.book) + self.assertEqual( + shelf.shelfbook_set.first().shelved_date, make_date(2020, 2, 2) + ) self.assertIsNone(self.user.shelf_set.get(identifier="read").books.first()) + readthrough = models.ReadThrough.objects.get(user=self.user) self.assertEqual(readthrough.book, self.book) - self.assertEqual(readthrough.start_date.year, 2020) - self.assertEqual(readthrough.start_date.month, 10) - self.assertEqual(readthrough.start_date.day, 21) - self.assertEqual(readthrough.finish_date.year, 2020) - self.assertEqual(readthrough.finish_date.month, 10) - self.assertEqual(readthrough.finish_date.day, 25) + self.assertEqual(readthrough.start_date, make_date(2020, 10, 21)) + self.assertEqual(readthrough.finish_date, make_date(2020, 10, 25)) def test_handle_import_twice(self): """re-importing books""" @@ -198,16 +207,14 @@ class GoodreadsImport(TestCase): shelf.refresh_from_db() self.assertEqual(shelf.books.first(), self.book) + self.assertEqual( + shelf.shelfbook_set.first().shelved_date, make_date(2020, 10, 21) + ) readthrough = models.ReadThrough.objects.get(user=self.user) self.assertEqual(readthrough.book, self.book) - # I can't remember how to create dates and I don't want to look it up. - self.assertEqual(readthrough.start_date.year, 2020) - self.assertEqual(readthrough.start_date.month, 10) - self.assertEqual(readthrough.start_date.day, 21) - self.assertEqual(readthrough.finish_date.year, 2020) - self.assertEqual(readthrough.finish_date.month, 10) - self.assertEqual(readthrough.finish_date.day, 25) + self.assertEqual(readthrough.start_date, make_date(2020, 10, 21)) + self.assertEqual(readthrough.finish_date, make_date(2020, 10, 25)) @patch("bookwyrm.activitystreams.ActivityStream.add_status") def test_handle_imported_book_review(self, _): @@ -229,9 +236,7 @@ class GoodreadsImport(TestCase): review = models.Review.objects.get(book=self.book, user=self.user) self.assertEqual(review.content, "mixed feelings") self.assertEqual(review.rating, 2) - self.assertEqual(review.published_date.year, 2019) - self.assertEqual(review.published_date.month, 7) - self.assertEqual(review.published_date.day, 8) + self.assertEqual(review.published_date, make_date(2019, 7, 8)) self.assertEqual(review.privacy, "unlisted") @patch("bookwyrm.activitystreams.ActivityStream.add_status") @@ -256,9 +261,7 @@ class GoodreadsImport(TestCase): review = models.ReviewRating.objects.get(book=self.book, user=self.user) self.assertIsInstance(review, models.ReviewRating) self.assertEqual(review.rating, 2) - self.assertEqual(review.published_date.year, 2019) - self.assertEqual(review.published_date.month, 7) - self.assertEqual(review.published_date.day, 8) + self.assertEqual(review.published_date, make_date(2019, 7, 8)) self.assertEqual(review.privacy, "unlisted") def test_handle_imported_book_reviews_disabled(self): diff --git a/bookwyrm/tests/importers/test_librarything_import.py b/bookwyrm/tests/importers/test_librarything_import.py index 8e299d567..1e4911b40 100644 --- a/bookwyrm/tests/importers/test_librarything_import.py +++ b/bookwyrm/tests/importers/test_librarything_import.py @@ -2,6 +2,8 @@ import csv import pathlib from unittest.mock import patch +import datetime +import pytz from django.test import TestCase import responses @@ -12,6 +14,10 @@ from bookwyrm.importers.importer import import_data, handle_imported_book from bookwyrm.settings import DOMAIN +def make_date(*args): + return datetime.datetime(*args, tzinfo=pytz.UTC) + + class LibrarythingImport(TestCase): """importing from librarything tsv""" @@ -125,13 +131,8 @@ class LibrarythingImport(TestCase): readthrough = models.ReadThrough.objects.get(user=self.user) self.assertEqual(readthrough.book, self.book) - # I can't remember how to create dates and I don't want to look it up. - self.assertEqual(readthrough.start_date.year, 2007) - self.assertEqual(readthrough.start_date.month, 4) - self.assertEqual(readthrough.start_date.day, 16) - self.assertEqual(readthrough.finish_date.year, 2007) - self.assertEqual(readthrough.finish_date.month, 5) - self.assertEqual(readthrough.finish_date.day, 8) + self.assertEqual(readthrough.start_date, make_date(2007, 4, 16)) + self.assertEqual(readthrough.finish_date, make_date(2007, 5, 8)) def test_handle_imported_book_already_shelved(self): """librarything import added a book, this adds related connections""" @@ -160,14 +161,11 @@ class LibrarythingImport(TestCase): shelf.refresh_from_db() self.assertEqual(shelf.books.first(), self.book) self.assertIsNone(self.user.shelf_set.get(identifier="read").books.first()) + readthrough = models.ReadThrough.objects.get(user=self.user) self.assertEqual(readthrough.book, self.book) - self.assertEqual(readthrough.start_date.year, 2007) - self.assertEqual(readthrough.start_date.month, 4) - self.assertEqual(readthrough.start_date.day, 16) - self.assertEqual(readthrough.finish_date.year, 2007) - self.assertEqual(readthrough.finish_date.month, 5) - self.assertEqual(readthrough.finish_date.day, 8) + self.assertEqual(readthrough.start_date, make_date(2007, 4, 16)) + self.assertEqual(readthrough.finish_date, make_date(2007, 5, 8)) def test_handle_import_twice(self): """re-importing books""" @@ -198,13 +196,8 @@ class LibrarythingImport(TestCase): readthrough = models.ReadThrough.objects.get(user=self.user) self.assertEqual(readthrough.book, self.book) - # I can't remember how to create dates and I don't want to look it up. - self.assertEqual(readthrough.start_date.year, 2007) - self.assertEqual(readthrough.start_date.month, 4) - self.assertEqual(readthrough.start_date.day, 16) - self.assertEqual(readthrough.finish_date.year, 2007) - self.assertEqual(readthrough.finish_date.month, 5) - self.assertEqual(readthrough.finish_date.day, 8) + self.assertEqual(readthrough.start_date, make_date(2007, 4, 16)) + self.assertEqual(readthrough.finish_date, make_date(2007, 5, 8)) @patch("bookwyrm.activitystreams.ActivityStream.add_status") def test_handle_imported_book_review(self, _): @@ -226,9 +219,7 @@ class LibrarythingImport(TestCase): review = models.Review.objects.get(book=self.book, user=self.user) self.assertEqual(review.content, "chef d'oeuvre") self.assertEqual(review.rating, 5) - self.assertEqual(review.published_date.year, 2007) - self.assertEqual(review.published_date.month, 5) - self.assertEqual(review.published_date.day, 8) + self.assertEqual(review.published_date, make_date(2007, 5, 8)) self.assertEqual(review.privacy, "unlisted") def test_handle_imported_book_reviews_disabled(self): diff --git a/bookwyrm/tests/models/test_activitypub_mixin.py b/bookwyrm/tests/models/test_activitypub_mixin.py index 020d031d2..553ca5469 100644 --- a/bookwyrm/tests/models/test_activitypub_mixin.py +++ b/bookwyrm/tests/models/test_activitypub_mixin.py @@ -9,11 +9,19 @@ from django.test import TestCase from bookwyrm.activitypub.base_activity import ActivityObject from bookwyrm import models from bookwyrm.models import base_model -from bookwyrm.models.activitypub_mixin import ActivitypubMixin -from bookwyrm.models.activitypub_mixin import ActivityMixin, ObjectMixin +from bookwyrm.models.activitypub_mixin import ( + ActivitypubMixin, + ActivityMixin, + ObjectMixin, + OrderedCollectionMixin, + to_ordered_collection_page, +) +from bookwyrm.settings import PAGE_LENGTH +# pylint: disable=invalid-name @patch("bookwyrm.activitystreams.ActivityStream.add_status") +@patch("bookwyrm.preview_images.generate_user_preview_image_task.delay") class ActivitypubMixins(TestCase): """functionality shared across models""" @@ -45,8 +53,7 @@ class ActivitypubMixins(TestCase): "published": "2020-12-04T17:52:22.623807+00:00", } - # ActivitypubMixin - def test_to_activity(self, _): + def test_to_activity(self, *_): """model to ActivityPub json""" @dataclass(init=False) @@ -67,7 +74,7 @@ class ActivitypubMixins(TestCase): self.assertEqual(activity["id"], "https://www.example.com/test") self.assertEqual(activity["type"], "Test") - def test_find_existing_by_remote_id(self, _): + def test_find_existing_by_remote_id(self, *_): """attempt to match a remote id to an object in the db""" # uses a different remote id scheme # this isn't really part of this test directly but it's helpful to state @@ -101,7 +108,7 @@ class ActivitypubMixins(TestCase): # test subclass match result = models.Status.find_existing_by_remote_id("https://comment.net") - def test_find_existing(self, _): + def test_find_existing(self, *_): """match a blob of data to a model""" with patch("bookwyrm.preview_images.generate_edition_preview_image_task.delay"): book = models.Edition.objects.create( @@ -112,7 +119,7 @@ class ActivitypubMixins(TestCase): result = models.Edition.find_existing({"openlibraryKey": "OL1234"}) self.assertEqual(result, book) - def test_get_recipients_public_object(self, _): + def test_get_recipients_public_object(self, *_): """determines the recipients for an object's broadcast""" MockSelf = namedtuple("Self", ("privacy")) mock_self = MockSelf("public") @@ -120,7 +127,7 @@ class ActivitypubMixins(TestCase): self.assertEqual(len(recipients), 1) self.assertEqual(recipients[0], self.remote_user.inbox) - def test_get_recipients_public_user_object_no_followers(self, _): + def test_get_recipients_public_user_object_no_followers(self, *_): """determines the recipients for a user's object broadcast""" MockSelf = namedtuple("Self", ("privacy", "user")) mock_self = MockSelf("public", self.local_user) @@ -128,7 +135,7 @@ class ActivitypubMixins(TestCase): recipients = ActivitypubMixin.get_recipients(mock_self) self.assertEqual(len(recipients), 0) - def test_get_recipients_public_user_object(self, _): + def test_get_recipients_public_user_object(self, *_): """determines the recipients for a user's object broadcast""" MockSelf = namedtuple("Self", ("privacy", "user")) mock_self = MockSelf("public", self.local_user) @@ -138,22 +145,21 @@ class ActivitypubMixins(TestCase): self.assertEqual(len(recipients), 1) self.assertEqual(recipients[0], self.remote_user.inbox) - def test_get_recipients_public_user_object_with_mention(self, _): + def test_get_recipients_public_user_object_with_mention(self, *_): """determines the recipients for a user's object broadcast""" MockSelf = namedtuple("Self", ("privacy", "user")) mock_self = MockSelf("public", self.local_user) self.local_user.followers.add(self.remote_user) - with patch("bookwyrm.preview_images.generate_user_preview_image_task.delay"): - with patch("bookwyrm.models.user.set_remote_server.delay"): - another_remote_user = models.User.objects.create_user( - "nutria", - "nutria@nutria.com", - "nutriaword", - local=False, - remote_id="https://example.com/users/nutria", - inbox="https://example.com/users/nutria/inbox", - outbox="https://example.com/users/nutria/outbox", - ) + with patch("bookwyrm.models.user.set_remote_server.delay"): + another_remote_user = models.User.objects.create_user( + "nutria", + "nutria@nutria.com", + "nutriaword", + local=False, + remote_id="https://example.com/users/nutria", + inbox="https://example.com/users/nutria/inbox", + outbox="https://example.com/users/nutria/outbox", + ) MockSelf = namedtuple("Self", ("privacy", "user", "recipients")) mock_self = MockSelf("public", self.local_user, [another_remote_user]) @@ -162,22 +168,21 @@ class ActivitypubMixins(TestCase): self.assertTrue(another_remote_user.inbox in recipients) self.assertTrue(self.remote_user.inbox in recipients) - def test_get_recipients_direct(self, _): + def test_get_recipients_direct(self, *_): """determines the recipients for a user's object broadcast""" MockSelf = namedtuple("Self", ("privacy", "user")) mock_self = MockSelf("public", self.local_user) self.local_user.followers.add(self.remote_user) - with patch("bookwyrm.preview_images.generate_user_preview_image_task.delay"): - with patch("bookwyrm.models.user.set_remote_server.delay"): - another_remote_user = models.User.objects.create_user( - "nutria", - "nutria@nutria.com", - "nutriaword", - local=False, - remote_id="https://example.com/users/nutria", - inbox="https://example.com/users/nutria/inbox", - outbox="https://example.com/users/nutria/outbox", - ) + with patch("bookwyrm.models.user.set_remote_server.delay"): + another_remote_user = models.User.objects.create_user( + "nutria", + "nutria@nutria.com", + "nutriaword", + local=False, + remote_id="https://example.com/users/nutria", + inbox="https://example.com/users/nutria/inbox", + outbox="https://example.com/users/nutria/outbox", + ) MockSelf = namedtuple("Self", ("privacy", "user", "recipients")) mock_self = MockSelf("direct", self.local_user, [another_remote_user]) @@ -185,22 +190,21 @@ class ActivitypubMixins(TestCase): self.assertEqual(len(recipients), 1) self.assertEqual(recipients[0], another_remote_user.inbox) - def test_get_recipients_combine_inboxes(self, _): + def test_get_recipients_combine_inboxes(self, *_): """should combine users with the same shared_inbox""" self.remote_user.shared_inbox = "http://example.com/inbox" self.remote_user.save(broadcast=False) - with patch("bookwyrm.preview_images.generate_user_preview_image_task.delay"): - with patch("bookwyrm.models.user.set_remote_server.delay"): - another_remote_user = models.User.objects.create_user( - "nutria", - "nutria@nutria.com", - "nutriaword", - local=False, - remote_id="https://example.com/users/nutria", - inbox="https://example.com/users/nutria/inbox", - shared_inbox="http://example.com/inbox", - outbox="https://example.com/users/nutria/outbox", - ) + with patch("bookwyrm.models.user.set_remote_server.delay"): + another_remote_user = models.User.objects.create_user( + "nutria", + "nutria@nutria.com", + "nutriaword", + local=False, + remote_id="https://example.com/users/nutria", + inbox="https://example.com/users/nutria/inbox", + shared_inbox="http://example.com/inbox", + outbox="https://example.com/users/nutria/outbox", + ) MockSelf = namedtuple("Self", ("privacy", "user")) mock_self = MockSelf("public", self.local_user) self.local_user.followers.add(self.remote_user) @@ -210,20 +214,19 @@ class ActivitypubMixins(TestCase): self.assertEqual(len(recipients), 1) self.assertEqual(recipients[0], "http://example.com/inbox") - def test_get_recipients_software(self, _): + def test_get_recipients_software(self, *_): """should differentiate between bookwyrm and other remote users""" - with patch("bookwyrm.preview_images.generate_user_preview_image_task.delay"): - with patch("bookwyrm.models.user.set_remote_server.delay"): - another_remote_user = models.User.objects.create_user( - "nutria", - "nutria@nutria.com", - "nutriaword", - local=False, - remote_id="https://example.com/users/nutria", - inbox="https://example.com/users/nutria/inbox", - outbox="https://example.com/users/nutria/outbox", - bookwyrm_user=False, - ) + with patch("bookwyrm.models.user.set_remote_server.delay"): + another_remote_user = models.User.objects.create_user( + "nutria", + "nutria@nutria.com", + "nutriaword", + local=False, + remote_id="https://example.com/users/nutria", + inbox="https://example.com/users/nutria/inbox", + outbox="https://example.com/users/nutria/outbox", + bookwyrm_user=False, + ) MockSelf = namedtuple("Self", ("privacy", "user")) mock_self = MockSelf("public", self.local_user) self.local_user.followers.add(self.remote_user) @@ -241,7 +244,7 @@ class ActivitypubMixins(TestCase): self.assertEqual(recipients[0], another_remote_user.inbox) # ObjectMixin - def test_object_save_create(self, _): + def test_object_save_create(self, *_): """should save uneventufully when broadcast is disabled""" class Success(Exception): @@ -272,7 +275,7 @@ class ActivitypubMixins(TestCase): ObjectModel(user=self.local_user).save(broadcast=False) ObjectModel(user=None).save() - def test_object_save_update(self, _): + def test_object_save_update(self, *_): """should save uneventufully when broadcast is disabled""" class Success(Exception): @@ -298,7 +301,7 @@ class ActivitypubMixins(TestCase): with self.assertRaises(Success): UpdateObjectModel(id=1, last_edited_by=self.local_user).save() - def test_object_save_delete(self, _): + def test_object_save_delete(self, *_): """should create delete activities when objects are deleted by flag""" class ActivitySuccess(Exception): @@ -320,7 +323,7 @@ class ActivitypubMixins(TestCase): with self.assertRaises(ActivitySuccess): DeletableObjectModel(id=1, user=self.local_user, deleted=True).save() - def test_to_delete_activity(self, _): + def test_to_delete_activity(self, *_): """wrapper for Delete activity""" MockSelf = namedtuple("Self", ("remote_id", "to_activity")) mock_self = MockSelf( @@ -335,7 +338,7 @@ class ActivitypubMixins(TestCase): activity["cc"], ["https://www.w3.org/ns/activitystreams#Public"] ) - def test_to_update_activity(self, _): + def test_to_update_activity(self, *_): """ditto above but for Update""" MockSelf = namedtuple("Self", ("remote_id", "to_activity")) mock_self = MockSelf( @@ -352,8 +355,7 @@ class ActivitypubMixins(TestCase): ) self.assertIsInstance(activity["object"], dict) - # Activity mixin - def test_to_undo_activity(self, _): + def test_to_undo_activity(self, *_): """and again, for Undo""" MockSelf = namedtuple("Self", ("remote_id", "to_activity", "user")) mock_self = MockSelf( @@ -366,3 +368,59 @@ class ActivitypubMixins(TestCase): self.assertEqual(activity["actor"], self.local_user.remote_id) self.assertEqual(activity["type"], "Undo") self.assertIsInstance(activity["object"], dict) + + @patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay") + def test_to_ordered_collection_page(self, *_): + """make sure the paged results of an ordered collection work""" + self.assertEqual(PAGE_LENGTH, 15) + for number in range(0, 2 * PAGE_LENGTH): + models.Status.objects.create( + user=self.local_user, + content="test status {:d}".format(number), + ) + page_1 = to_ordered_collection_page( + models.Status.objects.all(), "http://fish.com/", page=1 + ) + self.assertEqual(page_1.partOf, "http://fish.com/") + self.assertEqual(page_1.id, "http://fish.com/?page=1") + self.assertEqual(page_1.next, "http://fish.com/?page=2") + self.assertEqual(page_1.orderedItems[0]["content"], "test status 29") + self.assertEqual(page_1.orderedItems[1]["content"], "test status 28") + + page_2 = to_ordered_collection_page( + models.Status.objects.all(), "http://fish.com/", page=2 + ) + self.assertEqual(page_2.partOf, "http://fish.com/") + self.assertEqual(page_2.id, "http://fish.com/?page=2") + self.assertEqual(page_2.orderedItems[0]["content"], "test status 14") + self.assertEqual(page_2.orderedItems[-1]["content"], "test status 0") + + @patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay") + def test_to_ordered_collection(self, *_): + """convert a queryset into an ordered collection object""" + self.assertEqual(PAGE_LENGTH, 15) + + for number in range(0, 2 * PAGE_LENGTH): + models.Status.objects.create( + user=self.local_user, + content="test status {:d}".format(number), + ) + + MockSelf = namedtuple("Self", ("remote_id")) + mock_self = MockSelf("") + + collection = OrderedCollectionMixin.to_ordered_collection( + mock_self, models.Status.objects.all(), remote_id="http://fish.com/" + ) + + self.assertEqual(collection.totalItems, 30) + self.assertEqual(collection.first, "http://fish.com/?page=1") + self.assertEqual(collection.last, "http://fish.com/?page=2") + + page_2 = OrderedCollectionMixin.to_ordered_collection( + mock_self, models.Status.objects.all(), remote_id="http://fish.com/", page=2 + ) + self.assertEqual(page_2.partOf, "http://fish.com/") + self.assertEqual(page_2.id, "http://fish.com/?page=2") + self.assertEqual(page_2.orderedItems[0]["content"], "test status 14") + self.assertEqual(page_2.orderedItems[-1]["content"], "test status 0") diff --git a/bookwyrm/tests/views/test_status.py b/bookwyrm/tests/views/test_status.py index 9253e6e57..68ddcd289 100644 --- a/bookwyrm/tests/views/test_status.py +++ b/bookwyrm/tests/views/test_status.py @@ -8,6 +8,7 @@ from bookwyrm import forms, models, views from bookwyrm.settings import DOMAIN +# pylint: disable=invalid-name @patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay") class StatusViews(TestCase): """viewing and creating statuses""" @@ -318,6 +319,15 @@ class StatusViews(TestCase): '

hi and fish.com ' "is rad

", ) + def test_to_markdown_detect_url(self, _): + """this is mostly handled in other places, but nonetheless""" + text = "http://fish.com/@hello#okay" + result = views.status.to_markdown(text) + self.assertEqual( + result, + '

fish.com/@hello#okay

', + ) + def test_to_markdown_link(self, _): """this is mostly handled in other places, but nonetheless""" text = "[hi](http://fish.com) is rad" diff --git a/bookwyrm/urls.py b/bookwyrm/urls.py index ea73f0388..7eccfd651 100644 --- a/bookwyrm/urls.py +++ b/bookwyrm/urls.py @@ -295,7 +295,7 @@ urlpatterns = [ views.Book.as_view(), name="book-user-statuses", ), - re_path(r"%s/edit/?$" % BOOK_PATH, views.EditBook.as_view()), + re_path(r"%s/edit/?$" % BOOK_PATH, views.EditBook.as_view(), name="edit-book"), re_path(r"%s/confirm/?$" % BOOK_PATH, views.ConfirmEditBook.as_view()), re_path(r"^create-book/?$", views.EditBook.as_view(), name="create-book"), re_path(r"^create-book/confirm?$", views.ConfirmEditBook.as_view()), diff --git a/bookwyrm/views/books.py b/bookwyrm/views/books.py index a4b9f312a..492d0cac4 100644 --- a/bookwyrm/views/books.py +++ b/bookwyrm/views/books.py @@ -59,7 +59,7 @@ class Book(View): queryset = book.comment_set else: queryset = book.quotation_set - queryset = queryset.filter(user=request.user) + queryset = queryset.filter(user=request.user, deleted=False) else: queryset = reviews.exclude(Q(content__isnull=True) | Q(content="")) queryset = queryset.select_related("user") @@ -102,10 +102,11 @@ class Book(View): book__parent_work=book.parent_work, ).select_related("shelf", "book") + filters = {"user": request.user, "deleted": False} data["user_statuses"] = { - "review_count": book.review_set.filter(user=request.user).count(), - "comment_count": book.comment_set.filter(user=request.user).count(), - "quotation_count": book.quotation_set.filter(user=request.user).count(), + "review_count": book.review_set.filter(**filters).count(), + "comment_count": book.comment_set.filter(**filters).count(), + "quotation_count": book.quotation_set.filter(**filters).count(), } return TemplateResponse(request, "book/book.html", data) diff --git a/bookwyrm/views/shelf.py b/bookwyrm/views/shelf.py index 540975094..e9ad074d1 100644 --- a/bookwyrm/views/shelf.py +++ b/bookwyrm/views/shelf.py @@ -2,7 +2,7 @@ from collections import namedtuple from django.db import IntegrityError -from django.db.models import OuterRef, Subquery +from django.db.models import OuterRef, Subquery, F from django.contrib.auth.decorators import login_required from django.core.paginator import Paginator from django.http import HttpResponseBadRequest, HttpResponseNotFound @@ -69,7 +69,8 @@ class Shelf(View): reviews = privacy_filter(request.user, reviews) books = books.annotate( - rating=Subquery(reviews.values("rating")[:1]) + rating=Subquery(reviews.values("rating")[:1]), + shelved_date=F("shelfbook__shelved_date"), ).prefetch_related("authors") paginated = Paginator( diff --git a/bookwyrm/views/status.py b/bookwyrm/views/status.py index 61b6dc7aa..651021b63 100644 --- a/bookwyrm/views/status.py +++ b/bookwyrm/views/status.py @@ -150,7 +150,7 @@ def find_mentions(content): def format_links(content): """detect and format links""" return re.sub( - r'([^(href=")]|^|\()(https?:\/\/(%s([\w\.\-_\/+&\?=:;,])*))' % regex.DOMAIN, + r'([^(href=")]|^|\()(https?:\/\/(%s([\w\.\-_\/+&\?=:;,@#])*))' % regex.DOMAIN, r'\g<1>\g<3>', content, ) diff --git a/bw-dev b/bw-dev index 71c144a3b..3accd10b6 100755 --- a/bw-dev +++ b/bw-dev @@ -50,7 +50,9 @@ function awscommand { } CMD=$1 -shift +if [ -n "$CMD" ]; then + shift +fi # show commands as they're executed set -x @@ -67,9 +69,12 @@ case "$CMD" in ;; resetdb) clean - docker-compose up --build -d + # Start just the DB so no one else is using it + docker-compose up --build -d db execdb dropdb -U ${POSTGRES_USER} ${POSTGRES_DB} execdb createdb -U ${POSTGRES_USER} ${POSTGRES_DB} + # Now start up web so we can run the migrations + docker-compose up --build -d web initdb clean ;; @@ -134,27 +139,38 @@ case "$CMD" in --endpoint-url ${AWS_S3_ENDPOINT_URL}\ --cors-configuration file:///bw/$@" ;; + runweb) + runweb "$@" + ;; + rundb) + rundb "$@" + ;; *) - set +x + set +x # No need to echo echo echo "Unrecognised command. Try:" - echo " build" - echo " clean" - echo " up" + echo " up [container]" + echo " run" echo " initdb" echo " resetdb" - echo " makemigrations" - echo " migrate" + echo " makemigrations [migration]" + echo " migrate [migration]" echo " bash" echo " shell" echo " dbshell" echo " restart_celery" - echo " test" - echo " pytest" - echo " test_report" + echo " test [path]" + echo " pytest [path]" + echo " collectstatic" + echo " makemessages [locale]" + echo " compilemessages [locale]" + echo " build" + echo " clean" echo " black" echo " populate_streams" - echo " generate_preview_images" + echo " generate_preview_images [--all]" echo " copy_media_to_s3" - echo " set_cors_to_s3" + echo " set_cors_to_s3 [cors file]" + echo " runweb [command]" + echo " rundb [command]" ;; esac diff --git a/locale/zh_Hans/LC_MESSAGES/django.mo b/locale/zh_Hans/LC_MESSAGES/django.mo index 78797d29e..3450c353a 100644 Binary files a/locale/zh_Hans/LC_MESSAGES/django.mo and b/locale/zh_Hans/LC_MESSAGES/django.mo differ diff --git a/locale/zh_Hans/LC_MESSAGES/django.po b/locale/zh_Hans/LC_MESSAGES/django.po index c40e716c8..a52542b0a 100644 --- a/locale/zh_Hans/LC_MESSAGES/django.po +++ b/locale/zh_Hans/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: 0.1.1\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-06-06 20:52+0000\n" +"POT-Creation-Date: 2021-07-30 09:07+0000\n" "PO-Revision-Date: 2021-03-20 00:56+0000\n" "Last-Translator: Kana \n" "Language-Team: Mouse Reeve \n" @@ -18,99 +18,103 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0;\n" -#: bookwyrm/forms.py:224 +#: bookwyrm/forms.py:231 msgid "A user with this email already exists." msgstr "已经存在使用该邮箱的用户。" -#: bookwyrm/forms.py:238 +#: bookwyrm/forms.py:245 msgid "One Day" msgstr "一天" -#: bookwyrm/forms.py:239 +#: bookwyrm/forms.py:246 msgid "One Week" msgstr "一周" -#: bookwyrm/forms.py:240 +#: bookwyrm/forms.py:247 msgid "One Month" msgstr "一个月" -#: bookwyrm/forms.py:241 +#: bookwyrm/forms.py:248 msgid "Does Not Expire" msgstr "永不失效" -#: bookwyrm/forms.py:246 +#: bookwyrm/forms.py:253 #, python-format msgid "%(count)d uses" msgstr "%(count)d 次使用" -#: bookwyrm/forms.py:249 +#: bookwyrm/forms.py:256 msgid "Unlimited" msgstr "不受限" -#: bookwyrm/forms.py:299 +#: bookwyrm/forms.py:306 msgid "List Order" msgstr "列表顺序" -#: bookwyrm/forms.py:300 +#: bookwyrm/forms.py:307 msgid "Book Title" msgstr "书名" -#: bookwyrm/forms.py:301 bookwyrm/templates/snippets/create_status_form.html:34 +#: bookwyrm/forms.py:308 bookwyrm/templates/snippets/create_status_form.html:34 #: bookwyrm/templates/user/shelf/shelf.html:85 #: bookwyrm/templates/user/shelf/shelf.html:116 msgid "Rating" msgstr "评价" -#: bookwyrm/forms.py:303 bookwyrm/templates/lists/list.html:107 +#: bookwyrm/forms.py:310 bookwyrm/templates/lists/list.html:107 msgid "Sort By" msgstr "排序方式" -#: bookwyrm/forms.py:307 +#: bookwyrm/forms.py:314 msgid "Ascending" msgstr "升序" -#: bookwyrm/forms.py:308 +#: bookwyrm/forms.py:315 msgid "Descending" msgstr "降序" -#: bookwyrm/models/fields.py:25 +#: bookwyrm/models/fields.py:26 #, python-format msgid "%(value)s is not a valid remote_id" msgstr "%(value)s 不是有效的 remote_id" -#: bookwyrm/models/fields.py:34 bookwyrm/models/fields.py:43 +#: bookwyrm/models/fields.py:35 bookwyrm/models/fields.py:44 #, python-format msgid "%(value)s is not a valid username" msgstr "%(value)s 不是有效的用户名" -#: bookwyrm/models/fields.py:166 bookwyrm/templates/layout.html:152 +#: bookwyrm/models/fields.py:167 bookwyrm/templates/layout.html:157 msgid "username" msgstr "用户名" -#: bookwyrm/models/fields.py:171 +#: bookwyrm/models/fields.py:172 msgid "A user with that username already exists." msgstr "已经存在使用该用户名的用户。" -#: bookwyrm/settings.py:156 +#: bookwyrm/settings.py:164 msgid "English" msgstr "English(英语)" -#: bookwyrm/settings.py:157 +#: bookwyrm/settings.py:165 msgid "German" msgstr "Deutsch(德语)" -#: bookwyrm/settings.py:158 +#: bookwyrm/settings.py:166 msgid "Spanish" msgstr "Español(西班牙语)" -#: bookwyrm/settings.py:159 +#: bookwyrm/settings.py:167 msgid "French" msgstr "Français(法语)" -#: bookwyrm/settings.py:160 +#: bookwyrm/settings.py:168 msgid "Simplified Chinese" msgstr "简体中文" +#: bookwyrm/settings.py:169 +msgid "Traditional Chinese" +msgstr "繁體中文(繁体中文)" + #: bookwyrm/templates/404.html:4 bookwyrm/templates/404.html:8 msgid "Not Found" msgstr "未找到" @@ -136,34 +140,42 @@ msgstr "某些东西出错了!对不起啦。" msgid "Edit Author" msgstr "编辑作者" -#: bookwyrm/templates/author/author.html:32 -#: bookwyrm/templates/author/edit_author.html:38 +#: bookwyrm/templates/author/author.html:34 +#: bookwyrm/templates/author/edit_author.html:41 msgid "Aliases:" msgstr "别名:" -#: bookwyrm/templates/author/author.html:38 +#: bookwyrm/templates/author/author.html:45 msgid "Born:" msgstr "出生:" -#: bookwyrm/templates/author/author.html:44 +#: bookwyrm/templates/author/author.html:52 msgid "Died:" msgstr "逝世:" -#: bookwyrm/templates/author/author.html:51 +#: bookwyrm/templates/author/author.html:61 msgid "Wikipedia" msgstr "维基百科" -#: bookwyrm/templates/author/author.html:55 -#: bookwyrm/templates/book/book.html:78 +#: bookwyrm/templates/author/author.html:69 +#: bookwyrm/templates/book/book.html:90 msgid "View on OpenLibrary" msgstr "在 OpenLibrary 查看" -#: bookwyrm/templates/author/author.html:60 -#: bookwyrm/templates/book/book.html:81 +#: bookwyrm/templates/author/author.html:77 +#: bookwyrm/templates/book/book.html:93 msgid "View on Inventaire" msgstr "在 Inventaire 查看" -#: bookwyrm/templates/author/author.html:74 +#: bookwyrm/templates/author/author.html:85 +msgid "View on LibraryThing" +msgstr "在 LibraryThing 查看" + +#: bookwyrm/templates/author/author.html:93 +msgid "View on Goodreads" +msgstr "在 Goodreads 查看" + +#: bookwyrm/templates/author/author.html:108 #, python-format msgid "Books by %(name)s" msgstr "%(name)s 所著的书" @@ -173,212 +185,212 @@ msgid "Edit Author:" msgstr "编辑作者:" #: bookwyrm/templates/author/edit_author.html:13 -#: bookwyrm/templates/book/edit_book.html:18 +#: bookwyrm/templates/book/edit_book.html:19 msgid "Added:" msgstr "添加了:" #: bookwyrm/templates/author/edit_author.html:14 -#: bookwyrm/templates/book/edit_book.html:19 +#: bookwyrm/templates/book/edit_book.html:24 msgid "Updated:" msgstr "更新了:" #: bookwyrm/templates/author/edit_author.html:15 -#: bookwyrm/templates/book/edit_book.html:20 +#: bookwyrm/templates/book/edit_book.html:30 msgid "Last edited by:" msgstr "最后编辑人:" #: bookwyrm/templates/author/edit_author.html:31 -#: bookwyrm/templates/book/edit_book.html:90 +#: bookwyrm/templates/book/edit_book.html:117 msgid "Metadata" msgstr "元数据" -#: bookwyrm/templates/author/edit_author.html:32 +#: bookwyrm/templates/author/edit_author.html:33 #: bookwyrm/templates/lists/form.html:8 #: bookwyrm/templates/user/shelf/create_shelf_form.html:13 #: bookwyrm/templates/user/shelf/edit_shelf_form.html:14 msgid "Name:" msgstr "名称:" -#: bookwyrm/templates/author/edit_author.html:40 -#: bookwyrm/templates/book/edit_book.html:132 -#: bookwyrm/templates/book/edit_book.html:141 -#: bookwyrm/templates/book/edit_book.html:178 +#: bookwyrm/templates/author/edit_author.html:43 +#: bookwyrm/templates/book/edit_book.html:162 +#: bookwyrm/templates/book/edit_book.html:171 +#: bookwyrm/templates/book/edit_book.html:214 msgid "Separate multiple values with commas." msgstr "请用英文逗号(,)分隔多个值。" -#: bookwyrm/templates/author/edit_author.html:46 +#: bookwyrm/templates/author/edit_author.html:50 msgid "Bio:" msgstr "简介:" -#: bookwyrm/templates/author/edit_author.html:51 +#: bookwyrm/templates/author/edit_author.html:57 msgid "Wikipedia link:" msgstr "维基百科链接:" -#: bookwyrm/templates/author/edit_author.html:57 +#: bookwyrm/templates/author/edit_author.html:63 msgid "Birth date:" msgstr "出生日期:" -#: bookwyrm/templates/author/edit_author.html:65 +#: bookwyrm/templates/author/edit_author.html:71 msgid "Death date:" msgstr "死亡日期:" -#: bookwyrm/templates/author/edit_author.html:73 +#: bookwyrm/templates/author/edit_author.html:79 msgid "Author Identifiers" msgstr "作者标识号:" -#: bookwyrm/templates/author/edit_author.html:74 +#: bookwyrm/templates/author/edit_author.html:81 msgid "Openlibrary key:" msgstr "Openlibrary key:" -#: bookwyrm/templates/author/edit_author.html:79 -#: bookwyrm/templates/book/edit_book.html:243 +#: bookwyrm/templates/author/edit_author.html:89 +#: bookwyrm/templates/book/edit_book.html:293 msgid "Inventaire ID:" msgstr "Inventaire ID:" -#: bookwyrm/templates/author/edit_author.html:84 +#: bookwyrm/templates/author/edit_author.html:97 msgid "Librarything key:" msgstr "Librarything key:" -#: bookwyrm/templates/author/edit_author.html:89 +#: bookwyrm/templates/author/edit_author.html:105 msgid "Goodreads key:" msgstr "Goodreads key:" -#: bookwyrm/templates/author/edit_author.html:98 -#: bookwyrm/templates/book/book.html:124 -#: bookwyrm/templates/book/edit_book.html:263 +#: bookwyrm/templates/author/edit_author.html:116 +#: bookwyrm/templates/book/book.html:136 +#: bookwyrm/templates/book/edit_book.html:321 +#: bookwyrm/templates/book/readthrough.html:77 #: bookwyrm/templates/lists/form.html:42 #: bookwyrm/templates/preferences/edit_user.html:70 #: bookwyrm/templates/settings/announcement_form.html:69 #: bookwyrm/templates/settings/edit_server.html:68 #: bookwyrm/templates/settings/federated_server.html:98 -#: bookwyrm/templates/settings/site.html:97 -#: bookwyrm/templates/snippets/readthrough.html:77 +#: bookwyrm/templates/settings/site.html:101 #: bookwyrm/templates/snippets/shelve_button/finish_reading_modal.html:42 #: bookwyrm/templates/snippets/shelve_button/progress_update_modal.html:42 -#: bookwyrm/templates/snippets/shelve_button/start_reading_modal.html:34 -#: bookwyrm/templates/user_admin/user_moderation_actions.html:38 +#: bookwyrm/templates/snippets/shelve_button/start_reading_modal.html:36 +#: bookwyrm/templates/user_admin/user_moderation_actions.html:45 msgid "Save" msgstr "保存" -#: bookwyrm/templates/author/edit_author.html:99 -#: bookwyrm/templates/book/book.html:125 bookwyrm/templates/book/book.html:174 +#: bookwyrm/templates/author/edit_author.html:117 +#: bookwyrm/templates/book/book.html:137 bookwyrm/templates/book/book.html:186 #: bookwyrm/templates/book/cover_modal.html:32 -#: bookwyrm/templates/book/edit_book.html:264 +#: bookwyrm/templates/book/edit_book.html:322 +#: bookwyrm/templates/book/readthrough.html:78 #: bookwyrm/templates/moderation/report_modal.html:34 #: bookwyrm/templates/settings/federated_server.html:99 #: bookwyrm/templates/snippets/delete_readthrough_modal.html:17 #: bookwyrm/templates/snippets/goal_form.html:32 -#: bookwyrm/templates/snippets/readthrough.html:78 #: bookwyrm/templates/snippets/shelve_button/finish_reading_modal.html:43 #: bookwyrm/templates/snippets/shelve_button/progress_update_modal.html:43 -#: bookwyrm/templates/snippets/shelve_button/start_reading_modal.html:35 +#: bookwyrm/templates/snippets/shelve_button/start_reading_modal.html:37 #: bookwyrm/templates/snippets/shelve_button/want_to_read_modal.html:28 msgid "Cancel" msgstr "取消" -#: bookwyrm/templates/book/book.html:31 +#: bookwyrm/templates/book/book.html:43 #: bookwyrm/templates/discover/large-book.html:25 #: bookwyrm/templates/discover/small-book.html:19 msgid "by" msgstr "作者" -#: bookwyrm/templates/book/book.html:39 bookwyrm/templates/book/book.html:40 +#: bookwyrm/templates/book/book.html:51 bookwyrm/templates/book/book.html:52 msgid "Edit Book" msgstr "编辑书目" -#: bookwyrm/templates/book/book.html:57 +#: bookwyrm/templates/book/book.html:69 #: bookwyrm/templates/book/cover_modal.html:5 msgid "Add cover" msgstr "添加封面" -#: bookwyrm/templates/book/book.html:61 +#: bookwyrm/templates/book/book.html:73 msgid "Failed to load cover" msgstr "加载封面失败" -#: bookwyrm/templates/book/book.html:101 +#: bookwyrm/templates/book/book.html:113 #, python-format msgid "(%(review_count)s review)" msgid_plural "(%(review_count)s reviews)" msgstr[0] "(%(review_count)s 则书评)" -#: bookwyrm/templates/book/book.html:113 +#: bookwyrm/templates/book/book.html:125 msgid "Add Description" msgstr "添加描述" -#: bookwyrm/templates/book/book.html:120 -#: bookwyrm/templates/book/edit_book.html:108 +#: bookwyrm/templates/book/book.html:132 +#: bookwyrm/templates/book/edit_book.html:136 #: bookwyrm/templates/lists/form.html:12 msgid "Description:" msgstr "描述:" -#: bookwyrm/templates/book/book.html:134 +#: bookwyrm/templates/book/book.html:146 #, python-format msgid "%(count)s editions" msgstr "%(count)s 个版本" -#: bookwyrm/templates/book/book.html:142 +#: bookwyrm/templates/book/book.html:154 #, python-format msgid "This edition is on your %(shelf_name)s shelf." msgstr "此版本在你的 %(shelf_name)s 书架上。" -#: bookwyrm/templates/book/book.html:148 +#: bookwyrm/templates/book/book.html:160 #, python-format msgid "A different edition of this book is on your %(shelf_name)s shelf." msgstr "本书的 另一个版本 在你的 %(shelf_name)s 书架上。" -#: bookwyrm/templates/book/book.html:159 +#: bookwyrm/templates/book/book.html:171 msgid "Your reading activity" msgstr "你的阅读活动" -#: bookwyrm/templates/book/book.html:162 +#: bookwyrm/templates/book/book.html:174 msgid "Add read dates" msgstr "添加阅读日期" -#: bookwyrm/templates/book/book.html:171 +#: bookwyrm/templates/book/book.html:183 msgid "Create" msgstr "创建" -#: bookwyrm/templates/book/book.html:181 +#: bookwyrm/templates/book/book.html:193 msgid "You don't have any reading activity for this book." msgstr "你还没有任何这本书的阅读活动。" -#: bookwyrm/templates/book/book.html:200 +#: bookwyrm/templates/book/book.html:212 msgid "Reviews" msgstr "书评" -#: bookwyrm/templates/book/book.html:205 +#: bookwyrm/templates/book/book.html:217 msgid "Your reviews" msgstr "你的书评" -#: bookwyrm/templates/book/book.html:211 +#: bookwyrm/templates/book/book.html:223 msgid "Your comments" msgstr "你的评论" -#: bookwyrm/templates/book/book.html:217 +#: bookwyrm/templates/book/book.html:229 msgid "Your quotes" msgstr "你的引用" -#: bookwyrm/templates/book/book.html:253 +#: bookwyrm/templates/book/book.html:265 msgid "Subjects" msgstr "主题" -#: bookwyrm/templates/book/book.html:265 +#: bookwyrm/templates/book/book.html:277 msgid "Places" msgstr "地点" -#: bookwyrm/templates/book/book.html:276 bookwyrm/templates/layout.html:61 +#: bookwyrm/templates/book/book.html:288 bookwyrm/templates/layout.html:66 #: bookwyrm/templates/lists/lists.html:5 bookwyrm/templates/lists/lists.html:12 #: bookwyrm/templates/search/layout.html:25 #: bookwyrm/templates/search/layout.html:50 -#: bookwyrm/templates/user/layout.html:68 +#: bookwyrm/templates/user/layout.html:73 msgid "Lists" msgstr "列表" -#: bookwyrm/templates/book/book.html:287 +#: bookwyrm/templates/book/book.html:299 msgid "Add to list" msgstr "添加到列表" -#: bookwyrm/templates/book/book.html:297 +#: bookwyrm/templates/book/book.html:309 #: bookwyrm/templates/book/cover_modal.html:31 #: bookwyrm/templates/lists/list.html:179 msgid "Add" @@ -389,22 +401,22 @@ msgid "ISBN:" msgstr "ISBN:" #: bookwyrm/templates/book/book_identifiers.html:14 -#: bookwyrm/templates/book/edit_book.html:248 +#: bookwyrm/templates/book/edit_book.html:301 msgid "OCLC Number:" msgstr "OCLC 号:" #: bookwyrm/templates/book/book_identifiers.html:21 -#: bookwyrm/templates/book/edit_book.html:253 +#: bookwyrm/templates/book/edit_book.html:309 msgid "ASIN:" msgstr "ASIN:" #: bookwyrm/templates/book/cover_modal.html:17 -#: bookwyrm/templates/book/edit_book.html:192 +#: bookwyrm/templates/book/edit_book.html:229 msgid "Upload cover:" msgstr "上传封面:" #: bookwyrm/templates/book/cover_modal.html:23 -#: bookwyrm/templates/book/edit_book.html:198 +#: bookwyrm/templates/book/edit_book.html:235 msgid "Load cover from url:" msgstr "从网址加载封面:" @@ -419,127 +431,132 @@ msgstr "编辑 \"%(book_title)s\"" msgid "Add Book" msgstr "添加书目" -#: bookwyrm/templates/book/edit_book.html:40 +#: bookwyrm/templates/book/edit_book.html:54 msgid "Confirm Book Info" msgstr "确认书目信息" -#: bookwyrm/templates/book/edit_book.html:47 +#: bookwyrm/templates/book/edit_book.html:62 #, python-format msgid "Is \"%(name)s\" an existing author?" msgstr "\"%(name)s\" 是已存在的作者吗?" -#: bookwyrm/templates/book/edit_book.html:52 +#: bookwyrm/templates/book/edit_book.html:71 #, python-format msgid "Author of %(book_title)s" msgstr "%(book_title)s 的作者" -#: bookwyrm/templates/book/edit_book.html:55 +#: bookwyrm/templates/book/edit_book.html:75 msgid "This is a new author" msgstr "这是一位新的作者" -#: bookwyrm/templates/book/edit_book.html:61 +#: bookwyrm/templates/book/edit_book.html:82 #, python-format msgid "Creating a new author: %(name)s" msgstr "正在创建新的作者: %(name)s" -#: bookwyrm/templates/book/edit_book.html:67 +#: bookwyrm/templates/book/edit_book.html:89 msgid "Is this an edition of an existing work?" msgstr "这是已存在的作品的一个版本吗?" -#: bookwyrm/templates/book/edit_book.html:71 +#: bookwyrm/templates/book/edit_book.html:97 msgid "This is a new work" msgstr "这是一个新的作品。" -#: bookwyrm/templates/book/edit_book.html:77 +#: bookwyrm/templates/book/edit_book.html:104 #: bookwyrm/templates/password_reset.html:30 msgid "Confirm" msgstr "确认" -#: bookwyrm/templates/book/edit_book.html:79 +#: bookwyrm/templates/book/edit_book.html:106 #: bookwyrm/templates/feed/status.html:8 msgid "Back" msgstr "返回" -#: bookwyrm/templates/book/edit_book.html:93 +#: bookwyrm/templates/book/edit_book.html:120 msgid "Title:" msgstr "标题:" -#: bookwyrm/templates/book/edit_book.html:101 +#: bookwyrm/templates/book/edit_book.html:128 msgid "Subtitle:" msgstr "副标题:" -#: bookwyrm/templates/book/edit_book.html:114 +#: bookwyrm/templates/book/edit_book.html:144 msgid "Series:" msgstr "系列:" -#: bookwyrm/templates/book/edit_book.html:122 +#: bookwyrm/templates/book/edit_book.html:152 msgid "Series number:" msgstr "系列编号:" -#: bookwyrm/templates/book/edit_book.html:130 +#: bookwyrm/templates/book/edit_book.html:160 msgid "Languages:" msgstr "语言:" -#: bookwyrm/templates/book/edit_book.html:139 +#: bookwyrm/templates/book/edit_book.html:169 msgid "Publisher:" msgstr "出版社:" -#: bookwyrm/templates/book/edit_book.html:148 +#: bookwyrm/templates/book/edit_book.html:178 msgid "First published date:" msgstr "初版时间:" -#: bookwyrm/templates/book/edit_book.html:156 +#: bookwyrm/templates/book/edit_book.html:186 msgid "Published date:" msgstr "出版时间:" -#: bookwyrm/templates/book/edit_book.html:165 +#: bookwyrm/templates/book/edit_book.html:195 msgid "Authors" msgstr "作者" -#: bookwyrm/templates/book/edit_book.html:171 +#: bookwyrm/templates/book/edit_book.html:202 #, python-format -msgid "Remove %(name)s" -msgstr "移除 %(name)s" +msgid "Remove %(name)s" +msgstr "移除 %(name)s" -#: bookwyrm/templates/book/edit_book.html:176 +#: bookwyrm/templates/book/edit_book.html:205 +#, python-format +msgid "Author page for %(name)s" +msgstr "%(name)s 的作者页面" + +#: bookwyrm/templates/book/edit_book.html:212 msgid "Add Authors:" msgstr "添加作者:" -#: bookwyrm/templates/book/edit_book.html:177 +#: bookwyrm/templates/book/edit_book.html:213 msgid "John Doe, Jane Smith" msgstr "张三, 李四" -#: bookwyrm/templates/book/edit_book.html:183 +#: bookwyrm/templates/book/edit_book.html:220 #: bookwyrm/templates/user/shelf/shelf.html:78 msgid "Cover" msgstr "封面" -#: bookwyrm/templates/book/edit_book.html:211 +#: bookwyrm/templates/book/edit_book.html:248 msgid "Physical Properties" msgstr "实体性质" -#: bookwyrm/templates/book/edit_book.html:212 +#: bookwyrm/templates/book/edit_book.html:250 #: bookwyrm/templates/book/format_filter.html:5 msgid "Format:" msgstr "格式:" -#: bookwyrm/templates/book/edit_book.html:220 +#: bookwyrm/templates/book/edit_book.html:258 msgid "Pages:" msgstr "页数:" -#: bookwyrm/templates/book/edit_book.html:227 +#: bookwyrm/templates/book/edit_book.html:267 msgid "Book Identifiers" msgstr "书目标识号" -#: bookwyrm/templates/book/edit_book.html:228 +#: bookwyrm/templates/book/edit_book.html:269 msgid "ISBN 13:" msgstr "ISBN 13:" -#: bookwyrm/templates/book/edit_book.html:233 +#: bookwyrm/templates/book/edit_book.html:277 msgid "ISBN 10:" msgstr "ISBN 10:" -#: bookwyrm/templates/book/edit_book.html:238 +#: bookwyrm/templates/book/edit_book.html:285 msgid "Openlibrary ID:" msgstr "Openlibrary ID:" @@ -585,7 +602,7 @@ msgstr "%(languages)s 语言" #: bookwyrm/templates/book/publisher_info.html:64 #, python-format msgid "Published %(date)s by %(publisher)s." -msgstr "在 %(date)s 由 %(publisher)s 出版。" +msgstr "%(publisher)s 于 %(date)s 出版。" #: bookwyrm/templates/book/publisher_info.html:66 #, python-format @@ -595,15 +612,44 @@ msgstr "于 %(date)s 出版" #: bookwyrm/templates/book/publisher_info.html:68 #, python-format msgid "Published by %(publisher)s." -msgstr "由 %(publisher)s 出版。" +msgstr "%(publisher)s 出版。" #: bookwyrm/templates/book/rating.html:13 msgid "rated it" msgstr "评价了" +#: bookwyrm/templates/book/readthrough.html:8 +msgid "Progress Updates:" +msgstr "进度更新:" + +#: bookwyrm/templates/book/readthrough.html:14 +msgid "finished" +msgstr "已完成" + +#: bookwyrm/templates/book/readthrough.html:25 +msgid "Show all updates" +msgstr "显示所有更新" + +#: bookwyrm/templates/book/readthrough.html:41 +msgid "Delete this progress update" +msgstr "删除此进度更新" + +#: bookwyrm/templates/book/readthrough.html:52 +msgid "started" +msgstr "已开始" + +#: bookwyrm/templates/book/readthrough.html:59 +#: bookwyrm/templates/book/readthrough.html:73 +msgid "Edit read dates" +msgstr "编辑阅读日期" + +#: bookwyrm/templates/book/readthrough.html:63 +msgid "Delete these read dates" +msgstr "删除这些阅读日期" + #: bookwyrm/templates/components/inline_form.html:8 #: bookwyrm/templates/components/modal.html:11 -#: bookwyrm/templates/feed/feed_layout.html:69 +#: bookwyrm/templates/feed/layout.html:70 #: bookwyrm/templates/get_started/layout.html:19 #: bookwyrm/templates/get_started/layout.html:52 #: bookwyrm/templates/search/book.html:32 @@ -629,7 +675,7 @@ msgstr "跨站社区" #: bookwyrm/templates/directory/directory.html:4 #: bookwyrm/templates/directory/directory.html:9 -#: bookwyrm/templates/layout.html:64 +#: bookwyrm/templates/layout.html:69 msgid "Directory" msgstr "目录" @@ -738,7 +784,7 @@ msgstr "本实例不开放。" #: bookwyrm/templates/discover/landing_layout.html:57 msgid "Thank you! Your request has been received." -msgstr "谢谢你!我们已经受到了你的请求。" +msgstr "谢谢你!我们已经收到了你的请求。" #: bookwyrm/templates/discover/landing_layout.html:60 msgid "Request an Invitation" @@ -828,7 +874,7 @@ msgid "Direct Messages with %(username)s" msgstr "与 %(username)s 私信" #: bookwyrm/templates/feed/direct_messages.html:10 -#: bookwyrm/templates/layout.html:92 +#: bookwyrm/templates/layout.html:97 msgid "Direct Messages" msgstr "私信" @@ -872,43 +918,43 @@ msgstr "加载 0 条未读状态" #: bookwyrm/templates/feed/feed.html:47 msgid "There aren't any activities right now! Try following a user to get started" -msgstr "现在还没有任何活动!尝试着从关注一个用户开始吧" +msgstr "现在还没有任何活动!尝试从关注一个用户开始吧" #: bookwyrm/templates/feed/feed.html:55 #: bookwyrm/templates/get_started/users.html:6 msgid "Who to follow" msgstr "可以关注的人" -#: bookwyrm/templates/feed/feed_layout.html:4 +#: bookwyrm/templates/feed/layout.html:4 msgid "Updates" msgstr "更新" -#: bookwyrm/templates/feed/feed_layout.html:10 +#: bookwyrm/templates/feed/layout.html:11 #: bookwyrm/templates/user/shelf/books_header.html:3 msgid "Your books" msgstr "你的书目" -#: bookwyrm/templates/feed/feed_layout.html:12 +#: bookwyrm/templates/feed/layout.html:13 msgid "There are no books here right now! Try searching for a book to get started" msgstr "现在这里还没有任何书目!尝试着从搜索某本书开始吧" -#: bookwyrm/templates/feed/feed_layout.html:23 +#: bookwyrm/templates/feed/layout.html:24 #: bookwyrm/templates/user/shelf/shelf.html:29 msgid "To Read" msgstr "想读" -#: bookwyrm/templates/feed/feed_layout.html:24 +#: bookwyrm/templates/feed/layout.html:25 #: bookwyrm/templates/user/shelf/shelf.html:29 msgid "Currently Reading" msgstr "在读" -#: bookwyrm/templates/feed/feed_layout.html:25 -#: bookwyrm/templates/snippets/shelve_button/shelve_button_options.html:13 +#: bookwyrm/templates/feed/layout.html:26 +#: bookwyrm/templates/snippets/shelve_button/shelve_button_options.html:16 #: bookwyrm/templates/user/shelf/shelf.html:29 msgid "Read" msgstr "读过" -#: bookwyrm/templates/feed/feed_layout.html:87 bookwyrm/templates/goal.html:26 +#: bookwyrm/templates/feed/layout.html:89 bookwyrm/templates/goal.html:26 #: bookwyrm/templates/snippets/goal_card.html:6 #, python-format msgid "%(year)s Reading Goal" @@ -955,7 +1001,7 @@ msgstr "你可以在开始使用 %(site_name)s 后添加书目。" #: bookwyrm/templates/get_started/books.html:17 #: bookwyrm/templates/get_started/users.html:18 #: bookwyrm/templates/get_started/users.html:19 -#: bookwyrm/templates/layout.html:37 bookwyrm/templates/layout.html:38 +#: bookwyrm/templates/layout.html:42 bookwyrm/templates/layout.html:43 #: bookwyrm/templates/lists/list.html:139 #: bookwyrm/templates/search/layout.html:4 #: bookwyrm/templates/search/layout.html:9 @@ -1122,65 +1168,78 @@ msgstr "无最近的导入" msgid "Import Status" msgstr "导入状态" -#: bookwyrm/templates/import_status.html:12 +#: bookwyrm/templates/import_status.html:10 +msgid "Back to imports" +msgstr "回到导入" + +#: bookwyrm/templates/import_status.html:14 msgid "Import started:" msgstr "导入开始:" -#: bookwyrm/templates/import_status.html:16 +#: bookwyrm/templates/import_status.html:19 msgid "Import completed:" msgstr "导入完成:" -#: bookwyrm/templates/import_status.html:19 +#: bookwyrm/templates/import_status.html:24 msgid "TASK FAILED" msgstr "任务失败" -#: bookwyrm/templates/import_status.html:25 +#: bookwyrm/templates/import_status.html:31 msgid "Import still in progress." msgstr "还在导入中。" -#: bookwyrm/templates/import_status.html:27 +#: bookwyrm/templates/import_status.html:33 msgid "(Hit reload to update!)" msgstr "(按下重新加载来更新!)" -#: bookwyrm/templates/import_status.html:34 +#: bookwyrm/templates/import_status.html:40 msgid "Failed to load" msgstr "加载失败" -#: bookwyrm/templates/import_status.html:43 +#: bookwyrm/templates/import_status.html:49 #, python-format msgid "Jump to the bottom of the list to select the %(failed_count)s items which failed to import." msgstr "跳转至列表底部来选取 %(failed_count)s 个导入失败的项目。" -#: bookwyrm/templates/import_status.html:78 +#: bookwyrm/templates/import_status.html:61 +#, python-format +msgid "Line %(index)s: %(title)s by %(author)s" +msgstr "第 %(index)s 行: %(author)s 所著的 %(title)s" + +#: bookwyrm/templates/import_status.html:81 msgid "Select all" msgstr "全选" -#: bookwyrm/templates/import_status.html:81 +#: bookwyrm/templates/import_status.html:84 msgid "Retry items" msgstr "重试项目" -#: bookwyrm/templates/import_status.html:107 +#: bookwyrm/templates/import_status.html:111 msgid "Successfully imported" msgstr "成功导入了" -#: bookwyrm/templates/import_status.html:111 +#: bookwyrm/templates/import_status.html:113 +msgid "Import Progress" +msgstr "导入进度" + +#: bookwyrm/templates/import_status.html:118 msgid "Book" msgstr "书目" -#: bookwyrm/templates/import_status.html:114 +#: bookwyrm/templates/import_status.html:121 #: bookwyrm/templates/snippets/create_status_form.html:13 #: bookwyrm/templates/user/shelf/shelf.html:79 #: bookwyrm/templates/user/shelf/shelf.html:99 msgid "Title" msgstr "标题" -#: bookwyrm/templates/import_status.html:117 +#: bookwyrm/templates/import_status.html:124 #: bookwyrm/templates/user/shelf/shelf.html:80 #: bookwyrm/templates/user/shelf/shelf.html:102 msgid "Author" msgstr "作者" -#: bookwyrm/templates/import_status.html:140 +#: bookwyrm/templates/import_status.html:147 msgid "Imported" msgstr "已导入" @@ -1210,27 +1269,27 @@ msgstr "\"%(query)s\" 的搜索结果" msgid "Matching Books" msgstr "匹配的书目" -#: bookwyrm/templates/layout.html:33 +#: bookwyrm/templates/layout.html:38 msgid "Search for a book or user" msgstr "搜索书目或用户" -#: bookwyrm/templates/layout.html:47 bookwyrm/templates/layout.html:48 +#: bookwyrm/templates/layout.html:52 bookwyrm/templates/layout.html:53 msgid "Main navigation menu" msgstr "主导航菜单" -#: bookwyrm/templates/layout.html:58 +#: bookwyrm/templates/layout.html:63 msgid "Feed" msgstr "动态" -#: bookwyrm/templates/layout.html:87 +#: bookwyrm/templates/layout.html:92 msgid "Your Books" msgstr "你的书目" -#: bookwyrm/templates/layout.html:97 +#: bookwyrm/templates/layout.html:102 msgid "Settings" msgstr "设置" -#: bookwyrm/templates/layout.html:106 +#: bookwyrm/templates/layout.html:111 #: bookwyrm/templates/settings/admin_layout.html:31 #: bookwyrm/templates/settings/manage_invite_requests.html:15 #: bookwyrm/templates/settings/manage_invites.html:3 @@ -1238,61 +1297,61 @@ msgstr "设置" msgid "Invites" msgstr "邀请" -#: bookwyrm/templates/layout.html:113 +#: bookwyrm/templates/layout.html:118 msgid "Admin" msgstr "管理员" -#: bookwyrm/templates/layout.html:120 +#: bookwyrm/templates/layout.html:125 msgid "Log out" msgstr "登出" -#: bookwyrm/templates/layout.html:128 bookwyrm/templates/layout.html:129 +#: bookwyrm/templates/layout.html:133 bookwyrm/templates/layout.html:134 #: bookwyrm/templates/notifications.html:6 #: bookwyrm/templates/notifications.html:11 msgid "Notifications" msgstr "通知" -#: bookwyrm/templates/layout.html:151 bookwyrm/templates/layout.html:155 +#: bookwyrm/templates/layout.html:156 bookwyrm/templates/layout.html:160 #: bookwyrm/templates/login.html:17 #: bookwyrm/templates/snippets/register_form.html:4 msgid "Username:" msgstr "用户名:" -#: bookwyrm/templates/layout.html:156 +#: bookwyrm/templates/layout.html:161 msgid "password" msgstr "密码" -#: bookwyrm/templates/layout.html:157 bookwyrm/templates/login.html:36 +#: bookwyrm/templates/layout.html:162 bookwyrm/templates/login.html:36 msgid "Forgot your password?" msgstr "忘记了密码?" -#: bookwyrm/templates/layout.html:160 bookwyrm/templates/login.html:10 +#: bookwyrm/templates/layout.html:165 bookwyrm/templates/login.html:10 #: bookwyrm/templates/login.html:33 msgid "Log in" msgstr "登录" -#: bookwyrm/templates/layout.html:168 +#: bookwyrm/templates/layout.html:173 msgid "Join" msgstr "加入" -#: bookwyrm/templates/layout.html:206 +#: bookwyrm/templates/layout.html:211 msgid "About this instance" msgstr "关于本实例" -#: bookwyrm/templates/layout.html:210 +#: bookwyrm/templates/layout.html:215 msgid "Contact site admin" msgstr "联系站点管理员" -#: bookwyrm/templates/layout.html:214 +#: bookwyrm/templates/layout.html:219 msgid "Documentation" msgstr "文档:" -#: bookwyrm/templates/layout.html:221 +#: bookwyrm/templates/layout.html:226 #, python-format msgid "Support %(site_name)s on %(support_title)s" msgstr "在 %(support_title)s 上支持 %(site_name)s" -#: bookwyrm/templates/layout.html:225 +#: bookwyrm/templates/layout.html:230 msgid "BookWyrm's source code is freely available. You can contribute or report issues on GitHub." msgstr "BookWyrm 是开源软件。你可以在 GitHub 贡献或报告问题。" @@ -1454,7 +1513,7 @@ msgstr "联系管理员以取得邀请" #: bookwyrm/templates/login.html:63 msgid "More about this site" -msgstr "关于本站点的更多" +msgstr "更多关于本站点的信息" #: bookwyrm/templates/moderation/report.html:5 #: bookwyrm/templates/moderation/report.html:6 @@ -1670,6 +1729,7 @@ msgstr "你什么也没错过!" #: bookwyrm/templates/password_reset.html:23 #: bookwyrm/templates/preferences/change_password.html:18 +#: bookwyrm/templates/preferences/delete_user.html:20 msgid "Confirm password:" msgstr "确认密码:" @@ -1683,7 +1743,7 @@ msgstr "重设密码" #: bookwyrm/templates/preferences/blocks.html:4 #: bookwyrm/templates/preferences/blocks.html:7 -#: bookwyrm/templates/preferences/preferences_layout.html:26 +#: bookwyrm/templates/preferences/layout.html:30 msgid "Blocked Users" msgstr "屏蔽的用户" @@ -1694,7 +1754,7 @@ msgstr "当前没有被屏蔽的用户。" #: bookwyrm/templates/preferences/change_password.html:4 #: bookwyrm/templates/preferences/change_password.html:7 #: bookwyrm/templates/preferences/change_password.html:21 -#: bookwyrm/templates/preferences/preferences_layout.html:19 +#: bookwyrm/templates/preferences/layout.html:19 msgid "Change Password" msgstr "更改密码" @@ -1702,6 +1762,21 @@ msgstr "更改密码" msgid "New password:" msgstr "新密码:" +#: bookwyrm/templates/preferences/delete_user.html:4 +#: bookwyrm/templates/preferences/delete_user.html:7 +#: bookwyrm/templates/preferences/delete_user.html:26 +#: bookwyrm/templates/preferences/layout.html:23 +msgid "Delete Account" +msgstr "删除帐号" + +#: bookwyrm/templates/preferences/delete_user.html:12 +msgid "Permanently delete account" +msgstr "永久删除帐号" + +#: bookwyrm/templates/preferences/delete_user.html:14 +msgid "Deleting your account cannot be undone. The username will not be available to register in the future." +msgstr "删除帐号的操作将无法被撤销。对应用户名也无法被再次注册。" + #: bookwyrm/templates/preferences/edit_user.html:4 #: bookwyrm/templates/preferences/edit_user.html:7 msgid "Edit Profile" @@ -1720,18 +1795,33 @@ msgstr "你的帐号会显示在 目录 中,并可能 msgid "Preferred Timezone: " msgstr "偏好的时区:" -#: bookwyrm/templates/preferences/preferences_layout.html:11 +#: bookwyrm/templates/preferences/layout.html:11 msgid "Account" msgstr "帐号" -#: bookwyrm/templates/preferences/preferences_layout.html:15 +#: bookwyrm/templates/preferences/layout.html:15 msgid "Profile" msgstr "个人资料" -#: bookwyrm/templates/preferences/preferences_layout.html:22 +#: bookwyrm/templates/preferences/layout.html:26 msgid "Relationships" msgstr "关系" +#: bookwyrm/templates/reading_progress/finish.html:5 +#, python-format +msgid "Finish \"%(book_title)s\"" +msgstr "完成 \"%(book_title)s\"" + +#: bookwyrm/templates/reading_progress/start.html:5 +#, python-format +msgid "Start \"%(book_title)s\"" +msgstr "开始 \"%(book_title)s\"" + +#: bookwyrm/templates/reading_progress/want.html:5 +#, python-format +msgid "Want to Read \"%(book_title)s\"" +msgstr "想要阅读 \"%(book_title)s\"" + #: bookwyrm/templates/rss/title.html:5 #: bookwyrm/templates/snippets/status/status_header.html:35 msgid "rated" @@ -1774,7 +1864,7 @@ msgstr "搜索类型" #: bookwyrm/templates/search/layout.html:21 #: bookwyrm/templates/search/layout.html:42 -#: bookwyrm/templates/user/layout.html:74 +#: bookwyrm/templates/user/layout.html:79 msgid "Books" msgstr "书目" @@ -1979,7 +2069,7 @@ msgid "Details" msgstr "详细" #: bookwyrm/templates/settings/federated_server.html:39 -#: bookwyrm/templates/user/layout.html:56 +#: bookwyrm/templates/user/layout.html:61 msgid "Activity" msgstr "活动" @@ -2018,7 +2108,7 @@ msgid "Edit" msgstr "编辑" #: bookwyrm/templates/settings/federated_server.html:105 -#: bookwyrm/templates/user_admin/user_moderation_actions.html:3 +#: bookwyrm/templates/user_admin/user_moderation_actions.html:8 msgid "Actions" msgstr "动作" @@ -2224,15 +2314,15 @@ msgstr "管理员邮件:" msgid "Additional info:" msgstr "附加信息:" -#: bookwyrm/templates/settings/site.html:83 -msgid "Allow registration:" -msgstr "允许注册:" - -#: bookwyrm/templates/settings/site.html:87 -msgid "Allow invite requests:" -msgstr "允许请求邀请:" +#: bookwyrm/templates/settings/site.html:85 +msgid "Allow registration" +msgstr "允许注册" #: bookwyrm/templates/settings/site.html:91 +msgid "Allow invite requests" +msgstr "允许请求邀请" + +#: bookwyrm/templates/settings/site.html:95 msgid "Registration closed text:" msgstr "注册关闭文字:" @@ -2442,7 +2532,7 @@ msgstr "目标隐私:" #: bookwyrm/templates/snippets/goal_form.html:26 #: bookwyrm/templates/snippets/shelve_button/finish_reading_modal.html:37 -#: bookwyrm/templates/snippets/shelve_button/start_reading_modal.html:29 +#: bookwyrm/templates/snippets/shelve_button/start_reading_modal.html:31 #: bookwyrm/templates/snippets/shelve_button/want_to_read_modal.html:20 msgid "Post to feed" msgstr "发布到消息流中" @@ -2518,38 +2608,9 @@ msgstr "留下评价" msgid "Rate" msgstr "评价" -#: bookwyrm/templates/snippets/readthrough.html:8 -msgid "Progress Updates:" -msgstr "进度更新:" - -#: bookwyrm/templates/snippets/readthrough.html:14 -msgid "finished" -msgstr "已完成" - -#: bookwyrm/templates/snippets/readthrough.html:25 -msgid "Show all updates" -msgstr "显示所有更新" - -#: bookwyrm/templates/snippets/readthrough.html:41 -msgid "Delete this progress update" -msgstr "删除此进度更新" - -#: bookwyrm/templates/snippets/readthrough.html:52 -msgid "started" -msgstr "已开始" - -#: bookwyrm/templates/snippets/readthrough.html:59 -#: bookwyrm/templates/snippets/readthrough.html:73 -msgid "Edit read dates" -msgstr "编辑阅读日期" - -#: bookwyrm/templates/snippets/readthrough.html:63 -msgid "Delete these read dates" -msgstr "删除这些阅读日期" - #: bookwyrm/templates/snippets/readthrough_form.html:7 #: bookwyrm/templates/snippets/shelve_button/finish_reading_modal.html:19 -#: bookwyrm/templates/snippets/shelve_button/start_reading_modal.html:17 +#: bookwyrm/templates/snippets/shelve_button/start_reading_modal.html:19 msgid "Started reading" msgstr "已开始阅读" @@ -2584,7 +2645,7 @@ msgid "Finish \"%(book_title)s\"" msgstr "完成 \"%(book_title)s\"" #: bookwyrm/templates/snippets/shelve_button/progress_update_modal.html:5 -#: bookwyrm/templates/snippets/shelve_button/shelve_button_options.html:36 +#: bookwyrm/templates/snippets/shelve_button/shelve_button_options.html:45 msgid "Update progress" msgstr "更新进度" @@ -2592,20 +2653,20 @@ msgstr "更新进度" msgid "More shelves" msgstr "更多书架" -#: bookwyrm/templates/snippets/shelve_button/shelve_button_options.html:10 +#: bookwyrm/templates/snippets/shelve_button/shelve_button_options.html:11 msgid "Start reading" msgstr "开始阅读" -#: bookwyrm/templates/snippets/shelve_button/shelve_button_options.html:15 +#: bookwyrm/templates/snippets/shelve_button/shelve_button_options.html:19 msgid "Finish reading" msgstr "完成阅读" -#: bookwyrm/templates/snippets/shelve_button/shelve_button_options.html:18 +#: bookwyrm/templates/snippets/shelve_button/shelve_button_options.html:25 #: bookwyrm/templates/snippets/shelve_button/want_to_read_modal.html:26 msgid "Want to read" msgstr "想要阅读" -#: bookwyrm/templates/snippets/shelve_button/shelve_button_options.html:47 +#: bookwyrm/templates/snippets/shelve_button/shelve_button_options.html:57 #, python-format msgid "Remove from %(name)s" msgstr "从 %(name)s 移除" @@ -2669,7 +2730,7 @@ msgstr "删除并重新起草" #: bookwyrm/templates/snippets/status/status_options.html:35 #: bookwyrm/templates/snippets/user_options.html:13 -#: bookwyrm/templates/user_admin/user_moderation_actions.html:6 +#: bookwyrm/templates/user_admin/user_moderation_actions.html:12 msgid "Send direct message" msgstr "发送私信" @@ -2685,15 +2746,15 @@ msgstr "升序排序" msgid "Sorted descending" msgstr "降序排序" -#: bookwyrm/templates/user/layout.html:13 bookwyrm/templates/user/user.html:10 +#: bookwyrm/templates/user/layout.html:18 bookwyrm/templates/user/user.html:10 msgid "User Profile" msgstr "用户个人资料" -#: bookwyrm/templates/user/layout.html:37 +#: bookwyrm/templates/user/layout.html:42 msgid "Follow Requests" msgstr "关注请求" -#: bookwyrm/templates/user/layout.html:62 +#: bookwyrm/templates/user/layout.html:67 msgid "Reading Goal" msgstr "阅读目标" @@ -2876,22 +2937,31 @@ msgstr "实例详情" msgid "View instance" msgstr "查看实例" -#: bookwyrm/templates/user_admin/user_moderation_actions.html:11 +#: bookwyrm/templates/user_admin/user_moderation_actions.html:5 +msgid "Permanently deleted" +msgstr "已永久删除" + +#: bookwyrm/templates/user_admin/user_moderation_actions.html:17 msgid "Suspend user" msgstr "停用用户" -#: bookwyrm/templates/user_admin/user_moderation_actions.html:13 +#: bookwyrm/templates/user_admin/user_moderation_actions.html:19 msgid "Un-suspend user" msgstr "取消停用用户" -#: bookwyrm/templates/user_admin/user_moderation_actions.html:21 +#: bookwyrm/templates/user_admin/user_moderation_actions.html:28 msgid "Access level:" msgstr "访问级别:" -#: bookwyrm/templates/widgets/clearable_file_input_with_warning.html:3 +#: bookwyrm/templates/widgets/clearable_file_input_with_warning.html:22 msgid "File exceeds maximum size: 10MB" msgstr "文件超过了最大大小: 10MB" +#: bookwyrm/templatetags/utilities.py:30 +#, python-format +msgid "%(title)s: %(subtitle)s" +msgstr "%(title)s:%(subtitle)s" + #: bookwyrm/views/import_data.py:67 msgid "Not a valid csv file" msgstr "不是有效的 csv 文件" @@ -2904,4 +2974,3 @@ msgstr "没有找到使用该邮箱的用户。" #, python-format msgid "A password reset link sent to %s" msgstr "密码重置连接已发送给 %s" - diff --git a/locale/zh_Hant/LC_MESSAGES/django.mo b/locale/zh_Hant/LC_MESSAGES/django.mo new file mode 100644 index 000000000..8c8b5af88 Binary files /dev/null and b/locale/zh_Hant/LC_MESSAGES/django.mo differ