Merge branch 'main' into production

This commit is contained in:
Mouse Reeve 2021-09-08 09:09:09 -07:00
commit 8f50f8758c
52 changed files with 1982 additions and 1155 deletions

View file

@ -213,7 +213,7 @@ class ActivityObject:
return data
@app.task
@app.task(queue="medium_priority")
@transaction.atomic
def set_related_field(
model_name, origin_model_name, related_field_name, related_remote_id, data

View file

@ -395,7 +395,7 @@ def remove_statuses_on_unshelve(sender, instance, *args, **kwargs):
# ---- TASKS
@app.task
@app.task(queue="low_priority")
def add_book_statuses_task(user_id, book_id):
"""add statuses related to a book on shelve"""
user = models.User.objects.get(id=user_id)
@ -403,7 +403,7 @@ def add_book_statuses_task(user_id, book_id):
BooksStream().add_book_statuses(user, book)
@app.task
@app.task(queue="low_priority")
def remove_book_statuses_task(user_id, book_id):
"""remove statuses about a book from a user's books feed"""
user = models.User.objects.get(id=user_id)
@ -411,7 +411,7 @@ def remove_book_statuses_task(user_id, book_id):
BooksStream().remove_book_statuses(user, book)
@app.task
@app.task(queue="medium_priority")
def populate_stream_task(stream, user_id):
"""background task for populating an empty activitystream"""
user = models.User.objects.get(id=user_id)
@ -419,7 +419,7 @@ def populate_stream_task(stream, user_id):
stream.populate_streams(user)
@app.task
@app.task(queue="medium_priority")
def remove_status_task(status_ids):
"""remove a status from any stream it might be in"""
# this can take an id or a list of ids
@ -432,7 +432,7 @@ def remove_status_task(status_ids):
stream.remove_object_from_related_stores(status)
@app.task
@app.task(queue="medium_priority")
def add_status_task(status_id, increment_unread=False):
"""remove a status from any stream it might be in"""
status = models.Status.objects.get(id=status_id)
@ -440,7 +440,7 @@ def add_status_task(status_id, increment_unread=False):
stream.add_status(status, increment_unread=increment_unread)
@app.task
@app.task(queue="medium_priority")
def remove_user_statuses_task(viewer_id, user_id, stream_list=None):
"""remove all statuses by a user from a viewer's stream"""
stream_list = [streams[s] for s in stream_list] if stream_list else streams.values()
@ -450,9 +450,9 @@ def remove_user_statuses_task(viewer_id, user_id, stream_list=None):
stream.remove_user_statuses(viewer, user)
@app.task
@app.task(queue="medium_priority")
def add_user_statuses_task(viewer_id, user_id, stream_list=None):
"""remove all statuses by a user from a viewer's stream"""
"""add all statuses by a user to a viewer's stream"""
stream_list = [streams[s] for s in stream_list] if stream_list else streams.values()
viewer = models.User.objects.get(id=viewer_id)
user = models.User.objects.get(id=user_id)
@ -460,7 +460,7 @@ def add_user_statuses_task(viewer_id, user_id, stream_list=None):
stream.add_user_statuses(viewer, user)
@app.task
@app.task(queue="medium_priority")
def handle_boost_task(boost_id):
"""remove the original post and other, earlier boosts"""
instance = models.Status.objects.get(id=boost_id)

View file

@ -119,7 +119,7 @@ def get_or_create_connector(remote_id):
return load_connector(connector_info)
@app.task
@app.task(queue="low_priority")
def load_more_data(connector_id, book_id):
"""background the work of getting all 10,000 editions of LoTR"""
connector_info = models.Connector.objects.get(id=connector_id)

View file

@ -64,7 +64,7 @@ def format_email(email_name, data):
return (subject, html_content, text_content)
@app.task
@app.task(queue="high_priority")
def send_email(recipient, subject, html_content, text_content):
"""use a task to send the email"""
email = EmailMultiAlternatives(

View file

@ -61,7 +61,7 @@ class Importer:
job.save()
@app.task
@app.task(queue="low_priority")
def import_data(source, job_id):
"""does the actual lookup work in a celery task"""
job = ImportJob.objects.get(id=job_id)

View file

@ -502,7 +502,7 @@ def unfurl_related_field(related_field, sort_field=None):
return related_field.remote_id
@app.task
@app.task(queue="medium_priority")
def broadcast_task(sender_id, activity, recipients):
"""the celery task for broadcast"""
user_model = apps.get_model("bookwyrm.User", require_ready=True)

View file

@ -174,6 +174,7 @@ class ImportItem(models.Model):
if start_date and start_date is not None and not self.date_read:
return [ReadThrough(start_date=start_date)]
if self.date_read:
start_date = start_date if start_date < self.date_read else None
return [
ReadThrough(
start_date=start_date,

View file

@ -274,30 +274,46 @@ class User(OrderedCollectionPageMixin, AbstractUser):
transaction.on_commit(lambda: set_remote_server.delay(self.id))
return
# populate fields for local users
link = site_link()
self.remote_id = f"{link}/user/{self.localname}"
self.followers_url = f"{self.remote_id}/followers"
self.inbox = f"{self.remote_id}/inbox"
self.shared_inbox = f"{link}/inbox"
self.outbox = f"{self.remote_id}/outbox"
with transaction.atomic():
# populate fields for local users
link = site_link()
self.remote_id = f"{link}/user/{self.localname}"
self.followers_url = f"{self.remote_id}/followers"
self.inbox = f"{self.remote_id}/inbox"
self.shared_inbox = f"{link}/inbox"
self.outbox = f"{self.remote_id}/outbox"
# an id needs to be set before we can proceed with related models
# an id needs to be set before we can proceed with related models
super().save(*args, **kwargs)
# make users editors by default
try:
self.groups.add(Group.objects.get(name="editor"))
except Group.DoesNotExist:
# this should only happen in tests
pass
# create keys and shelves for new local users
self.key_pair = KeyPair.objects.create(
remote_id=f"{self.remote_id}/#main-key"
)
self.save(broadcast=False, update_fields=["key_pair"])
self.create_shelves()
def delete(self, *args, **kwargs):
"""deactivate rather than delete a user"""
self.is_active = False
# skip the logic in this class's save()
super().save(*args, **kwargs)
# make users editors by default
try:
self.groups.add(Group.objects.get(name="editor"))
except Group.DoesNotExist:
# this should only happen in tests
pass
# create keys and shelves for new local users
self.key_pair = KeyPair.objects.create(
remote_id="%s/#main-key" % self.remote_id
)
self.save(broadcast=False, update_fields=["key_pair"])
@property
def local_path(self):
"""this model doesn't inherit bookwyrm model, so here we are"""
return "/user/%s" % (self.localname or self.username)
def create_shelves(self):
"""default shelves for a new user"""
shelves = [
{
"name": "To Read",
@ -321,17 +337,6 @@ class User(OrderedCollectionPageMixin, AbstractUser):
editable=False,
).save(broadcast=False)
def delete(self, *args, **kwargs):
"""deactivate rather than delete a user"""
self.is_active = False
# skip the logic in this class's save()
super().save(*args, **kwargs)
@property
def local_path(self):
"""this model doesn't inherit bookwyrm model, so here we are"""
return "/user/%s" % (self.localname or self.username)
class KeyPair(ActivitypubMixin, BookWyrmModel):
"""public and private keys for a user"""
@ -420,7 +425,7 @@ class AnnualGoal(BookWyrmModel):
}
@app.task
@app.task(queue="low_priority")
def set_remote_server(user_id):
"""figure out the user's remote server in the background"""
user = User.objects.get(id=user_id)
@ -459,7 +464,7 @@ def get_or_create_remote_server(domain):
return server
@app.task
@app.task(queue="low_priority")
def get_remote_reviews(outbox):
"""ingest reviews by a new remote bookwyrm user"""
outbox_page = outbox + "?page=true&type=Review"

View file

@ -352,7 +352,7 @@ def save_and_cleanup(image, instance=None):
# pylint: disable=invalid-name
@app.task
@app.task(queue="low_priority")
def generate_site_preview_image_task():
"""generate preview_image for the website"""
if not settings.ENABLE_PREVIEW_IMAGES:
@ -377,7 +377,7 @@ def generate_site_preview_image_task():
# pylint: disable=invalid-name
@app.task
@app.task(queue="low_priority")
def generate_edition_preview_image_task(book_id):
"""generate preview_image for a book"""
if not settings.ENABLE_PREVIEW_IMAGES:
@ -402,7 +402,7 @@ def generate_edition_preview_image_task(book_id):
save_and_cleanup(image, instance=book)
@app.task
@app.task(queue="low_priority")
def generate_user_preview_image_task(user_id):
"""generate preview_image for a book"""
if not settings.ENABLE_PREVIEW_IMAGES:

View file

@ -13,17 +13,6 @@ VERSION = "0.0.1"
PAGE_LENGTH = env("PAGE_LENGTH", 15)
DEFAULT_LANGUAGE = env("DEFAULT_LANGUAGE", "English")
# celery
CELERY_BROKER = "redis://:{}@redis_broker:{}/0".format(
requests.utils.quote(env("REDIS_BROKER_PASSWORD", "")), env("REDIS_BROKER_PORT")
)
CELERY_RESULT_BACKEND = "redis://:{}@redis_broker:{}/0".format(
requests.utils.quote(env("REDIS_BROKER_PASSWORD", "")), env("REDIS_BROKER_PORT")
)
CELERY_ACCEPT_CONTENT = ["application/json"]
CELERY_TASK_SERIALIZER = "json"
CELERY_RESULT_SERIALIZER = "json"
# email
EMAIL_BACKEND = env("EMAIL_BACKEND", "django.core.mail.backends.smtp.EmailBackend")
EMAIL_HOST = env("EMAIL_HOST")

View file

@ -96,7 +96,7 @@ body {
@see https://www.youtube.com/watch?v=9xXBYcWgCHA */
.shelf-option:disabled > *::after {
font-family: "icomoon"; /* stylelint-disable font-family-no-missing-generic-family-keyword */
content: "\e918";
content: "\e919"; /* icon-check */
margin-left: 0.5em;
}
@ -167,21 +167,21 @@ body {
/* All stars are visually filled by default. */
.form-rate-stars .icon::before {
content: '\e9d9';
content: '\e9d9'; /* icon-star-full */
}
/* Icons directly following inputs that follow the checked input are emptied. */
.form-rate-stars input:checked ~ input + .icon::before {
content: '\e9d7';
content: '\e9d7'; /* icon-star-empty */
}
/* When a label is hovered, repeat the fill-all-then-empty-following pattern. */
.form-rate-stars:hover .icon.icon::before {
content: '\e9d9';
content: '\e9d9'; /* icon-star-full */
}
.form-rate-stars .icon:hover ~ .icon::before {
content: '\e9d7';
content: '\e9d7'; /* icon-star-empty */
}
/** Book covers
@ -292,13 +292,13 @@ body {
}
.quote > blockquote::before {
content: "\e906";
content: "\e907"; /* icon-quote-open */
top: 0;
left: 0;
}
.quote > blockquote::after {
content: "\e905";
content: "\e906"; /* icon-quote-close */
right: 0;
}

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 32 KiB

After

Width:  |  Height:  |  Size: 33 KiB

View file

@ -1,10 +1,10 @@
@font-face {
font-family: 'icomoon';
src: url('../fonts/icomoon.eot?19nagi');
src: url('../fonts/icomoon.eot?19nagi#iefix') format('embedded-opentype'),
url('../fonts/icomoon.ttf?19nagi') format('truetype'),
url('../fonts/icomoon.woff?19nagi') format('woff'),
url('../fonts/icomoon.svg?19nagi#icomoon') format('svg');
src: url('../fonts/icomoon.eot?wjd7rd');
src: url('../fonts/icomoon.eot?wjd7rd#iefix') format('embedded-opentype'),
url('../fonts/icomoon.ttf?wjd7rd') format('truetype'),
url('../fonts/icomoon.woff?wjd7rd') format('woff'),
url('../fonts/icomoon.svg?wjd7rd#icomoon') format('svg');
font-weight: normal;
font-style: normal;
font-display: block;
@ -25,6 +25,90 @@
-moz-osx-font-smoothing: grayscale;
}
.icon-book:before {
content: "\e901";
}
.icon-envelope:before {
content: "\e902";
}
.icon-arrow-right:before {
content: "\e903";
}
.icon-bell:before {
content: "\e904";
}
.icon-x:before {
content: "\e905";
}
.icon-quote-close:before {
content: "\e906";
}
.icon-quote-open:before {
content: "\e907";
}
.icon-image:before {
content: "\e908";
}
.icon-pencil:before {
content: "\e909";
}
.icon-list:before {
content: "\e90a";
}
.icon-unlock:before {
content: "\e90b";
}
.icon-globe:before {
content: "\e90c";
}
.icon-lock:before {
content: "\e90d";
}
.icon-chain-broken:before {
content: "\e90e";
}
.icon-chain:before {
content: "\e90f";
}
.icon-comments:before {
content: "\e910";
}
.icon-comment:before {
content: "\e911";
}
.icon-boost:before {
content: "\e912";
}
.icon-arrow-left:before {
content: "\e913";
}
.icon-arrow-up:before {
content: "\e914";
}
.icon-arrow-down:before {
content: "\e915";
}
.icon-local:before {
content: "\e917";
}
.icon-dots-three:before {
content: "\e918";
}
.icon-check:before {
content: "\e919";
}
.icon-dots-three-vertical:before {
content: "\e91a";
}
.icon-bookmark:before {
content: "\e91b";
}
.icon-warning:before {
content: "\e91c";
}
.icon-rss:before {
content: "\e91d";
}
.icon-graphic-heart:before {
content: "\e91e";
}
@ -34,102 +118,6 @@
.icon-graphic-banknote:before {
content: "\e920";
}
.icon-warning:before {
content: "\e91b";
}
.icon-book:before {
content: "\e900";
}
.icon-bookmark:before {
content: "\e91a";
}
.icon-rss:before {
content: "\e91d";
}
.icon-envelope:before {
content: "\e901";
}
.icon-arrow-right:before {
content: "\e902";
}
.icon-bell:before {
content: "\e903";
}
.icon-x:before {
content: "\e904";
}
.icon-quote-close:before {
content: "\e905";
}
.icon-quote-open:before {
content: "\e906";
}
.icon-image:before {
content: "\e907";
}
.icon-pencil:before {
content: "\e908";
}
.icon-list:before {
content: "\e909";
}
.icon-unlock:before {
content: "\e90a";
}
.icon-unlisted:before {
content: "\e90a";
}
.icon-globe:before {
content: "\e90b";
}
.icon-public:before {
content: "\e90b";
}
.icon-lock:before {
content: "\e90c";
}
.icon-followers:before {
content: "\e90c";
}
.icon-chain-broken:before {
content: "\e90d";
}
.icon-chain:before {
content: "\e90e";
}
.icon-comments:before {
content: "\e90f";
}
.icon-comment:before {
content: "\e910";
}
.icon-boost:before {
content: "\e911";
}
.icon-arrow-left:before {
content: "\e912";
}
.icon-arrow-up:before {
content: "\e913";
}
.icon-arrow-down:before {
content: "\e914";
}
.icon-home:before {
content: "\e915";
}
.icon-local:before {
content: "\e916";
}
.icon-dots-three:before {
content: "\e917";
}
.icon-check:before {
content: "\e918";
}
.icon-dots-three-vertical:before {
content: "\e919";
}
.icon-search:before {
content: "\e986";
}
@ -148,3 +136,6 @@
.icon-plus:before {
content: "\ea0a";
}
.icon-question-circle:before {
content: "\e900";
}

View file

@ -194,27 +194,27 @@ def add_new_user(sender, instance, created, update_fields=None, **kwargs):
remove_user_task.delay(instance.id)
@app.task
@app.task(queue="low_priority")
def rerank_suggestions_task(user_id):
"""do the hard work in celery"""
suggested_users.rerank_user_suggestions(user_id)
@app.task
@app.task(queue="low_priority")
def rerank_user_task(user_id, update_only=False):
"""do the hard work in celery"""
user = models.User.objects.get(id=user_id)
suggested_users.rerank_obj(user, update_only=update_only)
@app.task
@app.task(queue="low_priority")
def remove_user_task(user_id):
"""do the hard work in celery"""
user = models.User.objects.get(id=user_id)
suggested_users.remove_object_from_related_stores(user)
@app.task
@app.task(queue="medium_priority")
def remove_suggestion_task(user_id, suggested_user_id):
"""remove a specific user from a specific user's suggestions"""
suggested_user = models.User.objects.get(id=suggested_user_id)

View file

@ -2,10 +2,10 @@
import os
from celery import Celery
from bookwyrm import settings
from celerywyrm import settings
# set the default Django settings module for the 'celery' program.
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "celerywyrm.settings")
app = Celery(
"tasks", broker=settings.CELERY_BROKER, backend=settings.CELERY_RESULT_BACKEND
"tasks", broker=settings.CELERY_BROKER_URL, backend=settings.CELERY_RESULT_BACKEND
)

View file

@ -0,0 +1,11 @@
{% load i18n %}
{% trans "Help" as button_text %}
{% include 'snippets/toggle/open_button.html' with text=button_text class="ml-3 is-rounded is-small is-white p-0 pb-1" icon="question-circle is-size-6" controls_text=controls_text controls_uid=controls_uid %}
<aside class="notification is-hidden transition-y is-pulled-left mb-2" id="{{ controls_text }}{% if controls_uid %}-{{ controls_uid }}{% endif %}">
{% trans "Close" as button_text %}
{% include 'snippets/toggle/close_button.html' with label=button_text class="delete" nonbutton=True controls_text=controls_text controls_uid=controls_uid %}
{% block tooltip_content %}{% endblock %}
</aside>

View file

@ -12,9 +12,14 @@
<div class="columns">
<div class="column is-half">
<label class="label" for="source">
{% trans "Data source:" %}
</label>
<div class="field">
<label class="label is-pulled-left" for="source">
{% trans "Data source:" %}
</label>
{% include 'import/tooltip.html' with controls_text="goodreads-tooltip" %}
</div>
<div class="select block">
<select name="source" id="source">
<option value="GoodReads" {% if current == 'GoodReads' %}selected{% endif %}>

View file

@ -40,7 +40,7 @@
<div class="block">
<h2 class="title is-4">{% trans "Failed to load" %}</h2>
{% if not job.retry %}
<form name="retry" action="/import/{{ job.id }}" method="post">
<form name="retry" action="/import/{{ job.id }}" method="post" class="box">
{% csrf_token %}
{% with failed_count=failed_items|length %}

View file

@ -0,0 +1,8 @@
{% extends 'components/tooltip.html' %}
{% load i18n %}
{% block tooltip_content %}
{% trans '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.' %}
{% endblock %}

View file

@ -0,0 +1 @@
from . import *

View file

@ -0,0 +1,145 @@
""" testing activitystreams """
from unittest.mock import patch
from django.test import TestCase
from bookwyrm import activitystreams, models
@patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay")
@patch("bookwyrm.activitystreams.add_status_task.delay")
@patch("bookwyrm.activitystreams.add_book_statuses_task.delay")
@patch("bookwyrm.suggested_users.rerank_suggestions_task.delay")
@patch("bookwyrm.activitystreams.populate_stream_task.delay")
class Activitystreams(TestCase):
"""using redis to build activity streams"""
def setUp(self):
"""use a test csv"""
with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"), patch(
"bookwyrm.activitystreams.populate_stream_task.delay"
):
self.local_user = models.User.objects.create_user(
"mouse", "mouse@mouse.mouse", "password", local=True, localname="mouse"
)
self.another_user = models.User.objects.create_user(
"nutria",
"nutria@nutria.nutria",
"password",
local=True,
localname="nutria",
)
with patch("bookwyrm.models.user.set_remote_server.delay"):
self.remote_user = models.User.objects.create_user(
"rat",
"rat@rat.com",
"ratword",
local=False,
remote_id="https://example.com/users/rat",
inbox="https://example.com/users/rat/inbox",
outbox="https://example.com/users/rat/outbox",
)
work = models.Work.objects.create(title="test work")
self.book = models.Edition.objects.create(title="test book", parent_work=work)
class TestStream(activitystreams.ActivityStream):
"""test stream, don't have to do anything here"""
key = "test"
self.test_stream = TestStream()
def test_activitystream_class_ids(self, *_):
"""the abstract base class for stream objects"""
self.assertEqual(
self.test_stream.stream_id(self.local_user),
"{}-test".format(self.local_user.id),
)
self.assertEqual(
self.test_stream.unread_id(self.local_user),
"{}-test-unread".format(self.local_user.id),
)
def test_abstractstream_get_audience(self, *_):
"""get a list of users that should see a status"""
status = models.Status.objects.create(
user=self.remote_user, content="hi", privacy="public"
)
users = self.test_stream.get_audience(status)
# remote users don't have feeds
self.assertFalse(self.remote_user in users)
self.assertTrue(self.local_user in users)
self.assertTrue(self.another_user in users)
def test_abstractstream_get_audience_direct(self, *_):
"""get a list of users that should see a status"""
status = models.Status.objects.create(
user=self.remote_user,
content="hi",
privacy="direct",
)
status.mention_users.add(self.local_user)
users = self.test_stream.get_audience(status)
self.assertEqual(users, [])
status = models.Comment.objects.create(
user=self.remote_user,
content="hi",
privacy="direct",
book=self.book,
)
status.mention_users.add(self.local_user)
users = self.test_stream.get_audience(status)
self.assertTrue(self.local_user in users)
self.assertFalse(self.another_user in users)
self.assertFalse(self.remote_user in users)
def test_abstractstream_get_audience_followers_remote_user(self, *_):
"""get a list of users that should see a status"""
status = models.Status.objects.create(
user=self.remote_user,
content="hi",
privacy="followers",
)
users = self.test_stream.get_audience(status)
self.assertFalse(users.exists())
def test_abstractstream_get_audience_followers_self(self, *_):
"""get a list of users that should see a status"""
status = models.Comment.objects.create(
user=self.local_user,
content="hi",
privacy="direct",
book=self.book,
)
users = self.test_stream.get_audience(status)
self.assertTrue(self.local_user in users)
self.assertFalse(self.another_user in users)
self.assertFalse(self.remote_user in users)
def test_abstractstream_get_audience_followers_with_mention(self, *_):
"""get a list of users that should see a status"""
status = models.Comment.objects.create(
user=self.remote_user,
content="hi",
privacy="direct",
book=self.book,
)
status.mention_users.add(self.local_user)
users = self.test_stream.get_audience(status)
self.assertTrue(self.local_user in users)
self.assertFalse(self.another_user in users)
self.assertFalse(self.remote_user in users)
def test_abstractstream_get_audience_followers_with_relationship(self, *_):
"""get a list of users that should see a status"""
self.remote_user.followers.add(self.local_user)
status = models.Comment.objects.create(
user=self.remote_user,
content="hi",
privacy="direct",
book=self.book,
)
users = self.test_stream.get_audience(status)
self.assertFalse(self.local_user in users)
self.assertFalse(self.another_user in users)
self.assertFalse(self.remote_user in users)

View file

@ -0,0 +1,54 @@
""" testing activitystreams """
from unittest.mock import patch
from django.test import TestCase
from bookwyrm import activitystreams, models
@patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay")
@patch("bookwyrm.activitystreams.add_status_task.delay")
@patch("bookwyrm.activitystreams.add_book_statuses_task.delay")
@patch("bookwyrm.suggested_users.rerank_suggestions_task.delay")
@patch("bookwyrm.activitystreams.populate_stream_task.delay")
class Activitystreams(TestCase):
"""using redis to build activity streams"""
def setUp(self):
"""use a test csv"""
with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"), patch(
"bookwyrm.activitystreams.populate_stream_task.delay"
):
self.local_user = models.User.objects.create_user(
"mouse", "mouse@mouse.mouse", "password", local=True, localname="mouse"
)
with patch("bookwyrm.models.user.set_remote_server.delay"):
self.remote_user = models.User.objects.create_user(
"rat",
"rat@rat.com",
"ratword",
local=False,
remote_id="https://example.com/users/rat",
inbox="https://example.com/users/rat/inbox",
outbox="https://example.com/users/rat/outbox",
)
work = models.Work.objects.create(title="test work")
self.book = models.Edition.objects.create(title="test book", parent_work=work)
def test_get_statuses_for_user_books(self, *_):
"""create a stream for a user"""
alt_book = models.Edition.objects.create(
title="hi", parent_work=self.book.parent_work
)
status = models.Status.objects.create(
user=self.local_user, content="hi", privacy="public"
)
status = models.Comment.objects.create(
user=self.remote_user, content="hi", privacy="public", book=alt_book
)
models.ShelfBook.objects.create(
user=self.local_user,
shelf=self.local_user.shelf_set.first(),
book=self.book,
)
# yes book, yes audience
result = activitystreams.BooksStream().get_statuses_for_user(self.local_user)
self.assertEqual(list(result), [status])

View file

@ -0,0 +1,67 @@
""" testing activitystreams """
from unittest.mock import patch
from django.test import TestCase
from bookwyrm import activitystreams, models
@patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay")
@patch("bookwyrm.activitystreams.add_status_task.delay")
@patch("bookwyrm.activitystreams.add_book_statuses_task.delay")
@patch("bookwyrm.suggested_users.rerank_suggestions_task.delay")
@patch("bookwyrm.activitystreams.populate_stream_task.delay")
class Activitystreams(TestCase):
"""using redis to build activity streams"""
def setUp(self):
"""use a test csv"""
with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"), patch(
"bookwyrm.activitystreams.populate_stream_task.delay"
):
self.local_user = models.User.objects.create_user(
"mouse", "mouse@mouse.mouse", "password", local=True, localname="mouse"
)
self.another_user = models.User.objects.create_user(
"nutria",
"nutria@nutria.nutria",
"password",
local=True,
localname="nutria",
)
with patch("bookwyrm.models.user.set_remote_server.delay"):
self.remote_user = models.User.objects.create_user(
"rat",
"rat@rat.com",
"ratword",
local=False,
remote_id="https://example.com/users/rat",
inbox="https://example.com/users/rat/inbox",
outbox="https://example.com/users/rat/outbox",
)
def test_homestream_get_audience(self, *_):
"""get a list of users that should see a status"""
status = models.Status.objects.create(
user=self.remote_user, content="hi", privacy="public"
)
users = activitystreams.HomeStream().get_audience(status)
self.assertFalse(users.exists())
def test_homestream_get_audience_with_mentions(self, *_):
"""get a list of users that should see a status"""
status = models.Status.objects.create(
user=self.remote_user, content="hi", privacy="public"
)
status.mention_users.add(self.local_user)
users = activitystreams.HomeStream().get_audience(status)
self.assertFalse(self.local_user in users)
self.assertFalse(self.another_user in users)
def test_homestream_get_audience_with_relationship(self, *_):
"""get a list of users that should see a status"""
self.remote_user.followers.add(self.local_user)
status = models.Status.objects.create(
user=self.remote_user, content="hi", privacy="public"
)
users = activitystreams.HomeStream().get_audience(status)
self.assertTrue(self.local_user in users)
self.assertFalse(self.another_user in users)

View file

@ -0,0 +1,139 @@
""" testing activitystreams """
from unittest.mock import patch
from django.test import TestCase
from bookwyrm import activitystreams, models
@patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay")
@patch("bookwyrm.activitystreams.add_status_task.delay")
@patch("bookwyrm.activitystreams.add_book_statuses_task.delay")
@patch("bookwyrm.suggested_users.rerank_suggestions_task.delay")
@patch("bookwyrm.activitystreams.populate_stream_task.delay")
class Activitystreams(TestCase):
"""using redis to build activity streams"""
def setUp(self):
"""use a test csv"""
with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"), patch(
"bookwyrm.activitystreams.populate_stream_task.delay"
):
self.local_user = models.User.objects.create_user(
"mouse", "mouse@mouse.mouse", "password", local=True, localname="mouse"
)
self.another_user = models.User.objects.create_user(
"nutria",
"nutria@nutria.nutria",
"password",
local=True,
localname="nutria",
)
with patch("bookwyrm.models.user.set_remote_server.delay"):
self.remote_user = models.User.objects.create_user(
"rat",
"rat@rat.com",
"ratword",
local=False,
remote_id="https://example.com/users/rat",
inbox="https://example.com/users/rat/inbox",
outbox="https://example.com/users/rat/outbox",
)
work = models.Work.objects.create(title="test work")
self.book = models.Edition.objects.create(title="test book", parent_work=work)
def test_localstream_get_audience_remote_status(self, *_):
"""get a list of users that should see a status"""
status = models.Status.objects.create(
user=self.remote_user, content="hi", privacy="public"
)
users = activitystreams.LocalStream().get_audience(status)
self.assertEqual(users, [])
def test_localstream_get_audience_local_status(self, *_):
"""get a list of users that should see a status"""
status = models.Status.objects.create(
user=self.local_user, content="hi", privacy="public"
)
users = activitystreams.LocalStream().get_audience(status)
self.assertTrue(self.local_user in users)
self.assertTrue(self.another_user in users)
def test_localstream_get_audience_unlisted(self, *_):
"""get a list of users that should see a status"""
status = models.Status.objects.create(
user=self.local_user, content="hi", privacy="unlisted"
)
users = activitystreams.LocalStream().get_audience(status)
self.assertEqual(users, [])
def test_localstream_get_audience_books_no_book(self, *_):
"""get a list of users that should see a status"""
status = models.Status.objects.create(
user=self.local_user, content="hi", privacy="public"
)
audience = activitystreams.BooksStream().get_audience(status)
# no books, no audience
self.assertEqual(audience, [])
def test_localstream_get_audience_books_mention_books(self, *_):
"""get a list of users that should see a status"""
status = models.Status.objects.create(
user=self.local_user, content="hi", privacy="public"
)
status.mention_books.add(self.book)
status.save(broadcast=False)
models.ShelfBook.objects.create(
user=self.local_user,
shelf=self.local_user.shelf_set.first(),
book=self.book,
)
# yes book, yes audience
audience = activitystreams.BooksStream().get_audience(status)
self.assertTrue(self.local_user in audience)
def test_localstream_get_audience_books_book_field(self, *_):
"""get a list of users that should see a status"""
status = models.Comment.objects.create(
user=self.local_user, content="hi", privacy="public", book=self.book
)
models.ShelfBook.objects.create(
user=self.local_user,
shelf=self.local_user.shelf_set.first(),
book=self.book,
)
# yes book, yes audience
audience = activitystreams.BooksStream().get_audience(status)
self.assertTrue(self.local_user in audience)
def test_localstream_get_audience_books_alternate_edition(self, *_):
"""get a list of users that should see a status"""
alt_book = models.Edition.objects.create(
title="hi", parent_work=self.book.parent_work
)
status = models.Comment.objects.create(
user=self.remote_user, content="hi", privacy="public", book=alt_book
)
models.ShelfBook.objects.create(
user=self.local_user,
shelf=self.local_user.shelf_set.first(),
book=self.book,
)
# yes book, yes audience
audience = activitystreams.BooksStream().get_audience(status)
self.assertTrue(self.local_user in audience)
def test_localstream_get_audience_books_non_public(self, *_):
"""get a list of users that should see a status"""
alt_book = models.Edition.objects.create(
title="hi", parent_work=self.book.parent_work
)
status = models.Comment.objects.create(
user=self.remote_user, content="hi", privacy="unlisted", book=alt_book
)
models.ShelfBook.objects.create(
user=self.local_user,
shelf=self.local_user.shelf_set.first(),
book=self.book,
)
# yes book, yes audience
audience = activitystreams.BooksStream().get_audience(status)
self.assertEqual(audience, [])

View file

@ -0,0 +1,68 @@
""" testing activitystreams """
from unittest.mock import patch
from django.test import TestCase
from bookwyrm import activitystreams, models
@patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay")
class ActivitystreamsSignals(TestCase):
"""using redis to build activity streams"""
def setUp(self):
"""use a test csv"""
with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"), patch(
"bookwyrm.activitystreams.populate_stream_task.delay"
):
self.local_user = models.User.objects.create_user(
"mouse", "mouse@mouse.mouse", "password", local=True, localname="mouse"
)
with patch("bookwyrm.models.user.set_remote_server.delay"):
self.remote_user = models.User.objects.create_user(
"rat",
"rat@rat.com",
"ratword",
local=False,
remote_id="https://example.com/users/rat",
inbox="https://example.com/users/rat/inbox",
outbox="https://example.com/users/rat/outbox",
)
work = models.Work.objects.create(title="test work")
self.book = models.Edition.objects.create(title="test book", parent_work=work)
def test_add_status_on_create_ignore(self, _):
"""a new statuses has entered"""
activitystreams.add_status_on_create(models.User, self.local_user, False)
def test_add_status_on_create_deleted(self, _):
"""a new statuses has entered"""
with patch("bookwyrm.activitystreams.remove_status_task.delay"):
status = models.Status.objects.create(
user=self.remote_user, content="hi", privacy="public", deleted=True
)
with patch("bookwyrm.activitystreams.remove_status_task.delay") as mock:
activitystreams.add_status_on_create(models.Status, status, False)
self.assertEqual(mock.call_count, 1)
args = mock.call_args[0]
self.assertEqual(args[0], status.id)
def test_add_status_on_create_created(self, _):
"""a new statuses has entered"""
status = models.Status.objects.create(
user=self.remote_user, content="hi", privacy="public"
)
with patch("bookwyrm.activitystreams.add_status_task.delay") as mock:
activitystreams.add_status_on_create_command(models.Status, status, False)
self.assertEqual(mock.call_count, 1)
args = mock.call_args[0]
self.assertEqual(args[0], status.id)
def test_populate_streams_on_account_create(self, _):
"""create streams for a user"""
with patch("bookwyrm.activitystreams.populate_stream_task.delay") as mock:
activitystreams.populate_streams_on_account_create(
models.User, self.local_user, True
)
self.assertEqual(mock.call_count, 3)
args = mock.call_args[0]
self.assertEqual(args[0], "books")
self.assertEqual(args[1], self.local_user.id)

View file

@ -0,0 +1,193 @@
""" testing activitystreams """
from unittest.mock import patch
from django.test import TestCase
from bookwyrm import activitystreams, models
class Activitystreams(TestCase):
"""using redis to build activity streams"""
def setUp(self):
"""use a test csv"""
with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"), patch(
"bookwyrm.activitystreams.populate_stream_task.delay"
):
self.local_user = models.User.objects.create_user(
"mouse", "mouse@mouse.mouse", "password", local=True, localname="mouse"
)
self.another_user = models.User.objects.create_user(
"nutria",
"nutria@nutria.nutria",
"password",
local=True,
localname="nutria",
)
work = models.Work.objects.create(title="test work")
self.book = models.Edition.objects.create(title="test book", parent_work=work)
with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"):
self.status = models.Status.objects.create(
content="hi", user=self.local_user
)
def test_add_book_statuses_task(self):
"""statuses related to a book"""
with patch("bookwyrm.activitystreams.BooksStream.add_book_statuses") as mock:
activitystreams.add_book_statuses_task(self.local_user.id, self.book.id)
self.assertTrue(mock.called)
args = mock.call_args[0]
self.assertEqual(args[0], self.local_user)
self.assertEqual(args[1], self.book)
def test_remove_book_statuses_task(self):
"""remove stauses related to a book"""
with patch("bookwyrm.activitystreams.BooksStream.remove_book_statuses") as mock:
activitystreams.remove_book_statuses_task(self.local_user.id, self.book.id)
self.assertTrue(mock.called)
args = mock.call_args[0]
self.assertEqual(args[0], self.local_user)
self.assertEqual(args[1], self.book)
def test_populate_stream_task(self):
"""populate a given stream"""
with patch("bookwyrm.activitystreams.BooksStream.populate_streams") as mock:
activitystreams.populate_stream_task("books", self.local_user.id)
self.assertTrue(mock.called)
args = mock.call_args[0]
self.assertEqual(args[0], self.local_user)
with patch("bookwyrm.activitystreams.HomeStream.populate_streams") as mock:
activitystreams.populate_stream_task("home", self.local_user.id)
self.assertTrue(mock.called)
args = mock.call_args[0]
self.assertEqual(args[0], self.local_user)
def test_remove_status_task(self):
"""remove a status from all streams"""
with patch(
"bookwyrm.activitystreams.ActivityStream.remove_object_from_related_stores"
) as mock:
activitystreams.remove_status_task(self.status.id)
self.assertEqual(mock.call_count, 3)
args = mock.call_args[0]
self.assertEqual(args[0], self.status)
def test_add_status_task(self):
"""add a status to all streams"""
with patch("bookwyrm.activitystreams.ActivityStream.add_status") as mock:
activitystreams.add_status_task(self.status.id)
self.assertEqual(mock.call_count, 3)
args = mock.call_args[0]
self.assertEqual(args[0], self.status)
def test_remove_user_statuses_task(self):
"""remove all statuses by a user from another users' feeds"""
with patch(
"bookwyrm.activitystreams.ActivityStream.remove_user_statuses"
) as mock:
activitystreams.remove_user_statuses_task(
self.local_user.id, self.another_user.id
)
self.assertEqual(mock.call_count, 3)
args = mock.call_args[0]
self.assertEqual(args[0], self.local_user)
self.assertEqual(args[1], self.another_user)
with patch("bookwyrm.activitystreams.HomeStream.remove_user_statuses") as mock:
activitystreams.remove_user_statuses_task(
self.local_user.id, self.another_user.id, stream_list=["home"]
)
self.assertEqual(mock.call_count, 1)
args = mock.call_args[0]
self.assertEqual(args[0], self.local_user)
self.assertEqual(args[1], self.another_user)
def test_add_user_statuses_task(self):
"""add a user's statuses to another users feeds"""
with patch("bookwyrm.activitystreams.ActivityStream.add_user_statuses") as mock:
activitystreams.add_user_statuses_task(
self.local_user.id, self.another_user.id
)
self.assertEqual(mock.call_count, 3)
args = mock.call_args[0]
self.assertEqual(args[0], self.local_user)
self.assertEqual(args[1], self.another_user)
with patch("bookwyrm.activitystreams.HomeStream.add_user_statuses") as mock:
activitystreams.add_user_statuses_task(
self.local_user.id, self.another_user.id, stream_list=["home"]
)
self.assertEqual(mock.call_count, 1)
args = mock.call_args[0]
self.assertEqual(args[0], self.local_user)
self.assertEqual(args[1], self.another_user)
@patch("bookwyrm.activitystreams.LocalStream.remove_object_from_related_stores")
@patch("bookwyrm.activitystreams.BooksStream.remove_object_from_related_stores")
@patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay")
def test_boost_to_another_timeline(self, *_):
"""add a boost and deduplicate the boosted status on the timeline"""
status = models.Status.objects.create(user=self.local_user, content="hi")
with patch("bookwyrm.activitystreams.handle_boost_task.delay"):
boost = models.Boost.objects.create(
boosted_status=status,
user=self.another_user,
)
with patch(
"bookwyrm.activitystreams.HomeStream.remove_object_from_related_stores"
) as mock:
activitystreams.handle_boost_task(boost.id)
self.assertTrue(mock.called)
call_args = mock.call_args
self.assertEqual(call_args[0][0], status)
self.assertEqual(
call_args[1]["stores"], ["{:d}-home".format(self.another_user.id)]
)
@patch("bookwyrm.activitystreams.LocalStream.remove_object_from_related_stores")
@patch("bookwyrm.activitystreams.BooksStream.remove_object_from_related_stores")
@patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay")
def test_boost_to_following_timeline(self, *_):
"""add a boost and deduplicate the boosted status on the timeline"""
self.local_user.following.add(self.another_user)
status = models.Status.objects.create(user=self.local_user, content="hi")
with patch("bookwyrm.activitystreams.handle_boost_task.delay"):
boost = models.Boost.objects.create(
boosted_status=status,
user=self.another_user,
)
with patch(
"bookwyrm.activitystreams.HomeStream.remove_object_from_related_stores"
) as mock:
activitystreams.handle_boost_task(boost.id)
self.assertTrue(mock.called)
call_args = mock.call_args
self.assertEqual(call_args[0][0], status)
self.assertTrue(
"{:d}-home".format(self.another_user.id) in call_args[1]["stores"]
)
self.assertTrue(
"{:d}-home".format(self.local_user.id) in call_args[1]["stores"]
)
@patch("bookwyrm.activitystreams.LocalStream.remove_object_from_related_stores")
@patch("bookwyrm.activitystreams.BooksStream.remove_object_from_related_stores")
@patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay")
def test_boost_to_same_timeline(self, *_):
"""add a boost and deduplicate the boosted status on the timeline"""
status = models.Status.objects.create(user=self.local_user, content="hi")
with patch("bookwyrm.activitystreams.handle_boost_task.delay"):
boost = models.Boost.objects.create(
boosted_status=status,
user=self.local_user,
)
with patch(
"bookwyrm.activitystreams.HomeStream.remove_object_from_related_stores"
) as mock:
activitystreams.handle_boost_task(boost.id)
self.assertTrue(mock.called)
call_args = mock.call_args
self.assertEqual(call_args[0][0], status)
self.assertEqual(
call_args[1]["stores"], ["{:d}-home".format(self.local_user.id)]
)

View file

@ -1,360 +0,0 @@
""" testing activitystreams """
from unittest.mock import patch
from django.test import TestCase
from bookwyrm import activitystreams, models
@patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay")
@patch("bookwyrm.activitystreams.add_status_task.delay")
@patch("bookwyrm.activitystreams.add_book_statuses_task.delay")
@patch("bookwyrm.suggested_users.rerank_suggestions_task.delay")
@patch("bookwyrm.activitystreams.populate_stream_task.delay")
# pylint: disable=too-many-public-methods
class Activitystreams(TestCase):
"""using redis to build activity streams"""
def setUp(self):
"""use a test csv"""
with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"), patch(
"bookwyrm.activitystreams.populate_stream_task.delay"
):
self.local_user = models.User.objects.create_user(
"mouse", "mouse@mouse.mouse", "password", local=True, localname="mouse"
)
self.another_user = models.User.objects.create_user(
"nutria",
"nutria@nutria.nutria",
"password",
local=True,
localname="nutria",
)
with patch("bookwyrm.models.user.set_remote_server.delay"):
self.remote_user = models.User.objects.create_user(
"rat",
"rat@rat.com",
"ratword",
local=False,
remote_id="https://example.com/users/rat",
inbox="https://example.com/users/rat/inbox",
outbox="https://example.com/users/rat/outbox",
)
work = models.Work.objects.create(title="test work")
self.book = models.Edition.objects.create(title="test book", parent_work=work)
class TestStream(activitystreams.ActivityStream):
"""test stream, don't have to do anything here"""
key = "test"
self.test_stream = TestStream()
def test_activitystream_class_ids(self, *_):
"""the abstract base class for stream objects"""
self.assertEqual(
self.test_stream.stream_id(self.local_user),
"{}-test".format(self.local_user.id),
)
self.assertEqual(
self.test_stream.unread_id(self.local_user),
"{}-test-unread".format(self.local_user.id),
)
def test_abstractstream_get_audience(self, *_):
"""get a list of users that should see a status"""
status = models.Status.objects.create(
user=self.remote_user, content="hi", privacy="public"
)
users = self.test_stream.get_audience(status)
# remote users don't have feeds
self.assertFalse(self.remote_user in users)
self.assertTrue(self.local_user in users)
self.assertTrue(self.another_user in users)
def test_abstractstream_get_audience_direct(self, *_):
"""get a list of users that should see a status"""
status = models.Status.objects.create(
user=self.remote_user,
content="hi",
privacy="direct",
)
status.mention_users.add(self.local_user)
users = self.test_stream.get_audience(status)
self.assertEqual(users, [])
status = models.Comment.objects.create(
user=self.remote_user,
content="hi",
privacy="direct",
book=self.book,
)
status.mention_users.add(self.local_user)
users = self.test_stream.get_audience(status)
self.assertTrue(self.local_user in users)
self.assertFalse(self.another_user in users)
self.assertFalse(self.remote_user in users)
def test_abstractstream_get_audience_followers_remote_user(self, *_):
"""get a list of users that should see a status"""
status = models.Status.objects.create(
user=self.remote_user,
content="hi",
privacy="followers",
)
users = self.test_stream.get_audience(status)
self.assertFalse(users.exists())
def test_abstractstream_get_audience_followers_self(self, *_):
"""get a list of users that should see a status"""
status = models.Comment.objects.create(
user=self.local_user,
content="hi",
privacy="direct",
book=self.book,
)
users = self.test_stream.get_audience(status)
self.assertTrue(self.local_user in users)
self.assertFalse(self.another_user in users)
self.assertFalse(self.remote_user in users)
def test_abstractstream_get_audience_followers_with_mention(self, *_):
"""get a list of users that should see a status"""
status = models.Comment.objects.create(
user=self.remote_user,
content="hi",
privacy="direct",
book=self.book,
)
status.mention_users.add(self.local_user)
users = self.test_stream.get_audience(status)
self.assertTrue(self.local_user in users)
self.assertFalse(self.another_user in users)
self.assertFalse(self.remote_user in users)
def test_abstractstream_get_audience_followers_with_relationship(self, *_):
"""get a list of users that should see a status"""
self.remote_user.followers.add(self.local_user)
status = models.Comment.objects.create(
user=self.remote_user,
content="hi",
privacy="direct",
book=self.book,
)
users = self.test_stream.get_audience(status)
self.assertFalse(self.local_user in users)
self.assertFalse(self.another_user in users)
self.assertFalse(self.remote_user in users)
def test_homestream_get_audience(self, *_):
"""get a list of users that should see a status"""
status = models.Status.objects.create(
user=self.remote_user, content="hi", privacy="public"
)
users = activitystreams.HomeStream().get_audience(status)
self.assertFalse(users.exists())
def test_homestream_get_audience_with_mentions(self, *_):
"""get a list of users that should see a status"""
status = models.Status.objects.create(
user=self.remote_user, content="hi", privacy="public"
)
status.mention_users.add(self.local_user)
users = activitystreams.HomeStream().get_audience(status)
self.assertFalse(self.local_user in users)
self.assertFalse(self.another_user in users)
def test_homestream_get_audience_with_relationship(self, *_):
"""get a list of users that should see a status"""
self.remote_user.followers.add(self.local_user)
status = models.Status.objects.create(
user=self.remote_user, content="hi", privacy="public"
)
users = activitystreams.HomeStream().get_audience(status)
self.assertTrue(self.local_user in users)
self.assertFalse(self.another_user in users)
def test_localstream_get_audience_remote_status(self, *_):
"""get a list of users that should see a status"""
status = models.Status.objects.create(
user=self.remote_user, content="hi", privacy="public"
)
users = activitystreams.LocalStream().get_audience(status)
self.assertEqual(users, [])
def test_localstream_get_audience_local_status(self, *_):
"""get a list of users that should see a status"""
status = models.Status.objects.create(
user=self.local_user, content="hi", privacy="public"
)
users = activitystreams.LocalStream().get_audience(status)
self.assertTrue(self.local_user in users)
self.assertTrue(self.another_user in users)
def test_localstream_get_audience_unlisted(self, *_):
"""get a list of users that should see a status"""
status = models.Status.objects.create(
user=self.local_user, content="hi", privacy="unlisted"
)
users = activitystreams.LocalStream().get_audience(status)
self.assertEqual(users, [])
def test_localstream_get_audience_books_no_book(self, *_):
"""get a list of users that should see a status"""
status = models.Status.objects.create(
user=self.local_user, content="hi", privacy="public"
)
audience = activitystreams.BooksStream().get_audience(status)
# no books, no audience
self.assertEqual(audience, [])
def test_localstream_get_audience_books_mention_books(self, *_):
"""get a list of users that should see a status"""
status = models.Status.objects.create(
user=self.local_user, content="hi", privacy="public"
)
status.mention_books.add(self.book)
status.save(broadcast=False)
models.ShelfBook.objects.create(
user=self.local_user,
shelf=self.local_user.shelf_set.first(),
book=self.book,
)
# yes book, yes audience
audience = activitystreams.BooksStream().get_audience(status)
self.assertTrue(self.local_user in audience)
def test_localstream_get_audience_books_book_field(self, *_):
"""get a list of users that should see a status"""
status = models.Comment.objects.create(
user=self.local_user, content="hi", privacy="public", book=self.book
)
models.ShelfBook.objects.create(
user=self.local_user,
shelf=self.local_user.shelf_set.first(),
book=self.book,
)
# yes book, yes audience
audience = activitystreams.BooksStream().get_audience(status)
self.assertTrue(self.local_user in audience)
def test_localstream_get_audience_books_alternate_edition(self, *_):
"""get a list of users that should see a status"""
alt_book = models.Edition.objects.create(
title="hi", parent_work=self.book.parent_work
)
status = models.Comment.objects.create(
user=self.remote_user, content="hi", privacy="public", book=alt_book
)
models.ShelfBook.objects.create(
user=self.local_user,
shelf=self.local_user.shelf_set.first(),
book=self.book,
)
# yes book, yes audience
audience = activitystreams.BooksStream().get_audience(status)
self.assertTrue(self.local_user in audience)
def test_localstream_get_audience_books_non_public(self, *_):
"""get a list of users that should see a status"""
alt_book = models.Edition.objects.create(
title="hi", parent_work=self.book.parent_work
)
status = models.Comment.objects.create(
user=self.remote_user, content="hi", privacy="unlisted", book=alt_book
)
models.ShelfBook.objects.create(
user=self.local_user,
shelf=self.local_user.shelf_set.first(),
book=self.book,
)
# yes book, yes audience
audience = activitystreams.BooksStream().get_audience(status)
self.assertEqual(audience, [])
def test_get_statuses_for_user_books(self, *_):
"""create a stream for a user"""
alt_book = models.Edition.objects.create(
title="hi", parent_work=self.book.parent_work
)
status = models.Status.objects.create(
user=self.local_user, content="hi", privacy="public"
)
status = models.Comment.objects.create(
user=self.remote_user, content="hi", privacy="public", book=alt_book
)
models.ShelfBook.objects.create(
user=self.local_user,
shelf=self.local_user.shelf_set.first(),
book=self.book,
)
# yes book, yes audience
result = activitystreams.BooksStream().get_statuses_for_user(self.local_user)
self.assertEqual(list(result), [status])
@patch("bookwyrm.activitystreams.LocalStream.remove_object_from_related_stores")
@patch("bookwyrm.activitystreams.BooksStream.remove_object_from_related_stores")
def test_boost_to_another_timeline(self, *_):
"""add a boost and deduplicate the boosted status on the timeline"""
status = models.Status.objects.create(user=self.local_user, content="hi")
with patch("bookwyrm.activitystreams.handle_boost_task.delay"):
boost = models.Boost.objects.create(
boosted_status=status,
user=self.another_user,
)
with patch(
"bookwyrm.activitystreams.HomeStream.remove_object_from_related_stores"
) as mock:
activitystreams.handle_boost_task(boost.id)
self.assertTrue(mock.called)
call_args = mock.call_args
self.assertEqual(call_args[0][0], status)
self.assertEqual(
call_args[1]["stores"], ["{:d}-home".format(self.another_user.id)]
)
@patch("bookwyrm.activitystreams.LocalStream.remove_object_from_related_stores")
@patch("bookwyrm.activitystreams.BooksStream.remove_object_from_related_stores")
def test_boost_to_following_timeline(self, *_):
"""add a boost and deduplicate the boosted status on the timeline"""
self.local_user.following.add(self.another_user)
status = models.Status.objects.create(user=self.local_user, content="hi")
with patch("bookwyrm.activitystreams.handle_boost_task.delay"):
boost = models.Boost.objects.create(
boosted_status=status,
user=self.another_user,
)
with patch(
"bookwyrm.activitystreams.HomeStream.remove_object_from_related_stores"
) as mock:
activitystreams.handle_boost_task(boost.id)
self.assertTrue(mock.called)
call_args = mock.call_args
self.assertEqual(call_args[0][0], status)
self.assertTrue(
"{:d}-home".format(self.another_user.id) in call_args[1]["stores"]
)
self.assertTrue(
"{:d}-home".format(self.local_user.id) in call_args[1]["stores"]
)
@patch("bookwyrm.activitystreams.LocalStream.remove_object_from_related_stores")
@patch("bookwyrm.activitystreams.BooksStream.remove_object_from_related_stores")
def test_boost_to_same_timeline(self, *_):
"""add a boost and deduplicate the boosted status on the timeline"""
status = models.Status.objects.create(user=self.local_user, content="hi")
with patch("bookwyrm.activitystreams.handle_boost_task.delay"):
boost = models.Boost.objects.create(
boosted_status=status,
user=self.local_user,
)
with patch(
"bookwyrm.activitystreams.HomeStream.remove_object_from_related_stores"
) as mock:
activitystreams.handle_boost_task(boost.id)
self.assertTrue(mock.called)
call_args = mock.call_args
self.assertEqual(call_args[0][0], status)
self.assertEqual(
call_args[1]["stores"], ["{:d}-home".format(self.local_user.id)]
)

View file

@ -0,0 +1,110 @@
""" test for app action functionality """
from unittest.mock import patch
from django.contrib.auth.models import AnonymousUser
from django.template.response import TemplateResponse
from django.test import TestCase
from django.test.client import RequestFactory
from bookwyrm import forms, models, views
# pylint: disable=too-many-public-methods
@patch("bookwyrm.suggested_users.rerank_suggestions_task.delay")
@patch("bookwyrm.activitystreams.populate_stream_task.delay")
class LoginViews(TestCase):
"""login and password management"""
def setUp(self):
"""we need basic test data and mocks"""
self.factory = RequestFactory()
with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"), patch(
"bookwyrm.activitystreams.populate_stream_task.delay"
):
self.local_user = models.User.objects.create_user(
"mouse@your.domain.here",
"mouse@mouse.com",
"password",
local=True,
localname="mouse",
)
self.anonymous_user = AnonymousUser
self.anonymous_user.is_authenticated = False
models.SiteSettings.objects.create(id=1, require_confirm_email=False)
def test_login_get(self, *_):
"""there are so many views, this just makes sure it LOADS"""
login = views.Login.as_view()
request = self.factory.get("")
request.user = self.anonymous_user
result = login(request)
self.assertIsInstance(result, TemplateResponse)
result.render()
self.assertEqual(result.status_code, 200)
request.user = self.local_user
result = login(request)
self.assertEqual(result.url, "/")
self.assertEqual(result.status_code, 302)
def test_login_post_localname(self, *_):
"""there are so many views, this just makes sure it LOADS"""
view = views.Login.as_view()
form = forms.LoginForm()
form.data["localname"] = "mouse@mouse.com"
form.data["password"] = "password"
request = self.factory.post("", form.data)
request.user = self.anonymous_user
with patch("bookwyrm.views.login.login"):
result = view(request)
self.assertEqual(result.url, "/")
self.assertEqual(result.status_code, 302)
def test_login_post_username(self, *_):
"""there are so many views, this just makes sure it LOADS"""
view = views.Login.as_view()
form = forms.LoginForm()
form.data["localname"] = "mouse@your.domain.here"
form.data["password"] = "password"
request = self.factory.post("", form.data)
request.user = self.anonymous_user
with patch("bookwyrm.views.login.login"):
result = view(request)
self.assertEqual(result.url, "/")
self.assertEqual(result.status_code, 302)
def test_login_post_email(self, *_):
"""there are so many views, this just makes sure it LOADS"""
view = views.Login.as_view()
form = forms.LoginForm()
form.data["localname"] = "mouse"
form.data["password"] = "password"
request = self.factory.post("", form.data)
request.user = self.anonymous_user
with patch("bookwyrm.views.login.login"):
result = view(request)
self.assertEqual(result.url, "/")
self.assertEqual(result.status_code, 302)
def test_login_post_invalid_credentials(self, *_):
"""there are so many views, this just makes sure it LOADS"""
view = views.Login.as_view()
form = forms.LoginForm()
form.data["localname"] = "mouse"
form.data["password"] = "passsword1"
request = self.factory.post("", form.data)
request.user = self.anonymous_user
with patch("bookwyrm.views.login.login"):
result = view(request)
result.render()
self.assertEqual(result.status_code, 200)
self.assertEqual(
result.context_data["login_form"].non_field_errors,
"Username or password are incorrect",
)

View file

@ -8,14 +8,14 @@ from django.template.response import TemplateResponse
from django.test import TestCase
from django.test.client import RequestFactory
from bookwyrm import forms, models, views
from bookwyrm import models, views
from bookwyrm.settings import DOMAIN
# pylint: disable=too-many-public-methods
@patch("bookwyrm.suggested_users.rerank_suggestions_task.delay")
@patch("bookwyrm.activitystreams.populate_stream_task.delay")
class AuthenticationViews(TestCase):
class RegisterViews(TestCase):
"""login and password management"""
def setUp(self):
@ -38,82 +38,6 @@ class AuthenticationViews(TestCase):
id=1, require_confirm_email=False
)
def test_login_get(self, *_):
"""there are so many views, this just makes sure it LOADS"""
login = views.Login.as_view()
request = self.factory.get("")
request.user = self.anonymous_user
result = login(request)
self.assertIsInstance(result, TemplateResponse)
result.render()
self.assertEqual(result.status_code, 200)
request.user = self.local_user
result = login(request)
self.assertEqual(result.url, "/")
self.assertEqual(result.status_code, 302)
def test_login_post_localname(self, *_):
"""there are so many views, this just makes sure it LOADS"""
view = views.Login.as_view()
form = forms.LoginForm()
form.data["localname"] = "mouse@mouse.com"
form.data["password"] = "password"
request = self.factory.post("", form.data)
request.user = self.anonymous_user
with patch("bookwyrm.views.authentication.login"):
result = view(request)
self.assertEqual(result.url, "/")
self.assertEqual(result.status_code, 302)
def test_login_post_username(self, *_):
"""there are so many views, this just makes sure it LOADS"""
view = views.Login.as_view()
form = forms.LoginForm()
form.data["localname"] = "mouse@your.domain.here"
form.data["password"] = "password"
request = self.factory.post("", form.data)
request.user = self.anonymous_user
with patch("bookwyrm.views.authentication.login"):
result = view(request)
self.assertEqual(result.url, "/")
self.assertEqual(result.status_code, 302)
def test_login_post_email(self, *_):
"""there are so many views, this just makes sure it LOADS"""
view = views.Login.as_view()
form = forms.LoginForm()
form.data["localname"] = "mouse"
form.data["password"] = "password"
request = self.factory.post("", form.data)
request.user = self.anonymous_user
with patch("bookwyrm.views.authentication.login"):
result = view(request)
self.assertEqual(result.url, "/")
self.assertEqual(result.status_code, 302)
def test_login_post_invalid_credentials(self, *_):
"""there are so many views, this just makes sure it LOADS"""
view = views.Login.as_view()
form = forms.LoginForm()
form.data["localname"] = "mouse"
form.data["password"] = "passsword1"
request = self.factory.post("", form.data)
request.user = self.anonymous_user
with patch("bookwyrm.views.authentication.login"):
result = view(request)
result.render()
self.assertEqual(result.status_code, 200)
self.assertEqual(
result.context_data["login_form"].non_field_errors,
"Username or password are incorrect",
)
def test_register(self, *_):
"""create a user"""
view = views.Register.as_view()
@ -126,7 +50,7 @@ class AuthenticationViews(TestCase):
"email": "aa@bb.cccc",
},
)
with patch("bookwyrm.views.authentication.login"):
with patch("bookwyrm.views.register.login"):
response = view(request)
self.assertEqual(models.User.objects.count(), 2)
self.assertEqual(response.status_code, 302)
@ -151,7 +75,7 @@ class AuthenticationViews(TestCase):
"email": "aa@bb.cccc",
},
)
with patch("bookwyrm.views.authentication.login"):
with patch("bookwyrm.views.register.login"):
response = view(request)
self.assertEqual(response.status_code, 302)
nutria = models.User.objects.get(localname="nutria")
@ -169,7 +93,7 @@ class AuthenticationViews(TestCase):
"register/",
{"localname": "nutria ", "password": "mouseword", "email": "aa@bb.ccc"},
)
with patch("bookwyrm.views.authentication.login"):
with patch("bookwyrm.views.register.login"):
response = view(request)
self.assertEqual(models.User.objects.count(), 2)
self.assertEqual(response.status_code, 302)
@ -248,7 +172,7 @@ class AuthenticationViews(TestCase):
"invite_code": "testcode",
},
)
with patch("bookwyrm.views.authentication.login"):
with patch("bookwyrm.views.register.login"):
response = view(request)
self.assertEqual(models.User.objects.count(), 2)
self.assertEqual(response.status_code, 302)

View file

@ -1,7 +1,5 @@
""" make sure all our nice views are available """
from .announcements import Announcements, Announcement, delete_announcement
from .authentication import Login, Register, Logout
from .authentication import ConfirmEmail, ConfirmEmailCode, resend_link
from .author import Author, EditAuthor
from .block import Block, unblock
from .books import Book, EditBook, ConfirmEditBook
@ -28,11 +26,13 @@ from .landing import About, Home, Landing
from .list import Lists, SavedLists, List, Curate, UserLists
from .list import save_list, unsave_list
from .list import delete_list
from .login import Login, Logout
from .notifications import Notifications
from .outbox import Outbox
from .reading import edit_readthrough, create_readthrough
from .reading import delete_readthrough, delete_progressupdate
from .reading import ReadingStatus
from .register import Register, ConfirmEmail, ConfirmEmailCode, resend_link
from .reports import Report, Reports, make_report, resolve_report, suspend_user
from .rss_feed import RssFeed
from .password import PasswordResetRequest, PasswordReset, ChangePassword

View file

@ -28,7 +28,7 @@ class Import(View):
"""load import page"""
return TemplateResponse(
request,
"import.html",
"import/import.html",
{
"import_form": forms.ImportForm(),
"jobs": models.ImportJob.objects.filter(user=request.user).order_by(
@ -94,7 +94,7 @@ class ImportStatus(View):
items = [i for i in items if not i.fail_reason]
return TemplateResponse(
request,
"import_status.html",
"import/import_status.html",
{"job": job, "items": items, "failed_items": failed_items, "task": task},
)

View file

@ -93,7 +93,7 @@ def is_blocked_activity(activity_json):
return models.FederatedServer.is_blocked(actor)
@app.task
@app.task(queue="medium_priority")
def activity_task(activity_json):
"""do something with this json we think is legit"""
# lets see if the activitypub module can make sense of this json

View file

@ -83,7 +83,7 @@ class Invite(View):
}
return TemplateResponse(request, "invite.html", data)
# post handling is in views.authentication.Register
# post handling is in views.register.Register
class ManageInviteRequests(View):

83
bookwyrm/views/login.py Normal file
View file

@ -0,0 +1,83 @@
""" class views for login/register views """
from django.contrib.auth import authenticate, login, logout
from django.contrib.auth.decorators import login_required
from django.shortcuts import redirect
from django.template.response import TemplateResponse
from django.utils import timezone
from django.utils.decorators import method_decorator
from django.utils.translation import gettext_lazy as _
from django.views import View
from django.views.decorators.csrf import csrf_exempt
from django.views.decorators.debug import sensitive_variables, sensitive_post_parameters
from bookwyrm import forms, models
from bookwyrm.settings import DOMAIN
# pylint: disable=no-self-use
@method_decorator(csrf_exempt, name="dispatch")
class Login(View):
"""authenticate an existing user"""
def get(self, request, confirmed=None):
"""login page"""
if request.user.is_authenticated:
return redirect("/")
# send user to the login page
data = {
"show_confirmed_email": confirmed,
"login_form": forms.LoginForm(),
"register_form": forms.RegisterForm(),
}
return TemplateResponse(request, "login.html", data)
@sensitive_variables("password")
@method_decorator(sensitive_post_parameters("password"))
def post(self, request):
"""authentication action"""
if request.user.is_authenticated:
return redirect("/")
login_form = forms.LoginForm(request.POST)
localname = login_form.data["localname"]
if "@" in localname: # looks like an email address to me
try:
username = models.User.objects.get(email=localname).username
except models.User.DoesNotExist: # maybe it's a full username?
username = localname
else:
username = "%s@%s" % (localname, DOMAIN)
password = login_form.data["password"]
# perform authentication
user = authenticate(request, username=username, password=password)
if user is not None:
# successful login
login(request, user)
user.last_active_date = timezone.now()
user.save(broadcast=False, update_fields=["last_active_date"])
if request.POST.get("first_login"):
return redirect("get-started-profile")
return redirect(request.GET.get("next", "/"))
# maybe the user is pending email confirmation
if models.User.objects.filter(
username=username, is_active=False, deactivation_reason="pending"
).exists():
return redirect("confirm-email")
# login errors
login_form.non_field_errors = _("Username or password are incorrect")
register_form = forms.RegisterForm()
data = {"login_form": login_form, "register_form": register_form}
return TemplateResponse(request, "login.html", data)
@method_decorator(login_required, name="dispatch")
class Logout(View):
"""log out"""
def get(self, request):
"""done with this place! outa here!"""
logout(request)
return redirect("/")

View file

@ -1,90 +1,23 @@
""" class views for login/register views """
from django.contrib.auth import authenticate, login, logout
from django.contrib.auth.decorators import login_required
from django.contrib.auth import login
from django.core.exceptions import PermissionDenied
from django.shortcuts import get_object_or_404, redirect
from django.template.response import TemplateResponse
from django.utils import timezone
from django.utils.decorators import method_decorator
from django.utils.translation import gettext_lazy as _
from django.views.decorators.csrf import csrf_exempt
from django.views.decorators.http import require_POST
from django.views import View
from django.views.decorators.http import require_POST
from django.views.decorators.debug import sensitive_variables, sensitive_post_parameters
from bookwyrm import emailing, forms, models
from bookwyrm.settings import DOMAIN
# pylint: disable=no-self-use
@method_decorator(csrf_exempt, name="dispatch")
class Login(View):
"""authenticate an existing user"""
def get(self, request, confirmed=None):
"""login page"""
if request.user.is_authenticated:
return redirect("/")
# send user to the login page
data = {
"show_confirmed_email": confirmed,
"login_form": forms.LoginForm(),
"register_form": forms.RegisterForm(),
}
return TemplateResponse(request, "login.html", data)
def post(self, request):
"""authentication action"""
if request.user.is_authenticated:
return redirect("/")
login_form = forms.LoginForm(request.POST)
localname = login_form.data["localname"]
if "@" in localname: # looks like an email address to me
try:
username = models.User.objects.get(email=localname).username
except models.User.DoesNotExist: # maybe it's a full username?
username = localname
else:
username = "%s@%s" % (localname, DOMAIN)
password = login_form.data["password"]
# perform authentication
user = authenticate(request, username=username, password=password)
if user is not None:
# successful login
login(request, user)
user.last_active_date = timezone.now()
user.save(broadcast=False, update_fields=["last_active_date"])
if request.POST.get("first_login"):
return redirect("get-started-profile")
return redirect(request.GET.get("next", "/"))
# maybe the user is pending email confirmation
if models.User.objects.filter(
username=username, is_active=False, deactivation_reason="pending"
).exists():
return redirect("confirm-email")
# login errors
login_form.non_field_errors = _("Username or password are incorrect")
register_form = forms.RegisterForm()
data = {"login_form": login_form, "register_form": register_form}
return TemplateResponse(request, "login.html", data)
@method_decorator(login_required, name="dispatch")
class Logout(View):
"""log out"""
def get(self, request):
"""done with this place! outa here!"""
logout(request)
return redirect("/")
class Register(View):
"""register a user"""
@sensitive_variables("password")
@method_decorator(sensitive_post_parameters("password"))
def post(self, request):
"""join the server"""
settings = models.SiteSettings.get()

View file

@ -3,7 +3,7 @@ from __future__ import absolute_import, unicode_literals
import os
from celery import Celery
from . import settings
from . import settings # pylint: disable=unused-import
# set the default Django settings module for the 'celery' program.
@ -19,13 +19,3 @@ app.config_from_object("django.conf:settings", namespace="CELERY")
# Load task modules from all registered Django app configs.
app.autodiscover_tasks()
app.autodiscover_tasks(["bookwyrm"], related_name="activitypub.base_activity")
app.autodiscover_tasks(["bookwyrm"], related_name="activitystreams")
app.autodiscover_tasks(["bookwyrm"], related_name="broadcast")
app.autodiscover_tasks(["bookwyrm"], related_name="connectors.abstract_connector")
app.autodiscover_tasks(["bookwyrm"], related_name="emailing")
app.autodiscover_tasks(["bookwyrm"], related_name="goodreads_import")
app.autodiscover_tasks(["bookwyrm"], related_name="preview_images")
app.autodiscover_tasks(["bookwyrm"], related_name="models.user")
app.autodiscover_tasks(["bookwyrm"], related_name="suggested_users")
app.autodiscover_tasks(["bookwyrm"], related_name="views.inbox")

View file

@ -1,9 +1,20 @@
""" bookwyrm settings and configuration """
# pylint: disable=wildcard-import
# pylint: disable=unused-wildcard-import
from bookwyrm.settings import *
CELERY_BROKER_URL = CELERY_BROKER
CELERY_BROKER_URL = "redis://:{}@redis_broker:{}/0".format(
requests.utils.quote(env("REDIS_BROKER_PASSWORD", "")), env("REDIS_BROKER_PORT")
)
CELERY_RESULT_BACKEND = "redis://:{}@redis_broker:{}/0".format(
requests.utils.quote(env("REDIS_BROKER_PASSWORD", "")), env("REDIS_BROKER_PORT")
)
CELERY_DEFAULT_QUEUE = "low_priority"
CELERY_ACCEPT_CONTENT = ["json"]
CELERY_TASK_SERIALIZER = "json"
CELERY_RESULT_SERIALIZER = "json"
FLOWER_PORT = env("FLOWER_PORT")
INSTALLED_APPS = INSTALLED_APPS + [

View file

@ -79,7 +79,7 @@ services:
build: .
networks:
- main
command: celery -A celerywyrm worker -l info
command: celery -A celerywyrm worker -l info -Q high_priority,medium_priority,low_priority
volumes:
- .:/app
- static_volume:/app/static

Binary file not shown.

View file

@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: 0.0.1\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-09-05 23:09+0000\n"
"POT-Creation-Date: 2021-09-08 15:57+0000\n"
"PO-Revision-Date: 2021-03-02 17:19-0800\n"
"Last-Translator: Mouse Reeve <mousereeve@riseup.net>\n"
"Language-Team: English <LL@li.org>\n"
@ -102,21 +102,21 @@ msgstr "Username"
msgid "A user with that username already exists."
msgstr "Dieser Benutzename ist bereits vergeben."
#: bookwyrm/settings.py:124
#: bookwyrm/settings.py:113
msgid "Home Timeline"
msgstr ""
#: bookwyrm/settings.py:124
#: bookwyrm/settings.py:113
msgid "Home"
msgstr ""
#: bookwyrm/settings.py:125
#: bookwyrm/settings.py:114
#, fuzzy
#| msgid "Title"
msgid "Books Timeline"
msgstr "Titel"
#: bookwyrm/settings.py:125 bookwyrm/templates/search/layout.html:21
#: bookwyrm/settings.py:114 bookwyrm/templates/search/layout.html:21
#: bookwyrm/templates/search/layout.html:42
#: bookwyrm/templates/user/layout.html:81
#, fuzzy
@ -124,27 +124,27 @@ msgstr "Titel"
msgid "Books"
msgstr "Buch"
#: bookwyrm/settings.py:171
#: bookwyrm/settings.py:160
msgid "English"
msgstr "Englisch"
#: bookwyrm/settings.py:172
#: bookwyrm/settings.py:161
msgid "German"
msgstr "Deutsch"
#: bookwyrm/settings.py:173
#: bookwyrm/settings.py:162
msgid "Spanish"
msgstr "Spanisch"
#: bookwyrm/settings.py:174
#: bookwyrm/settings.py:163
msgid "French"
msgstr "Französisch"
#: bookwyrm/settings.py:175
#: bookwyrm/settings.py:164
msgid "Simplified Chinese"
msgstr "Vereinfachtes Chinesisch"
#: bookwyrm/settings.py:176
#: bookwyrm/settings.py:165
msgid "Traditional Chinese"
msgstr ""
@ -303,7 +303,7 @@ msgstr ""
#: bookwyrm/templates/book/edit_book.html:328
#: bookwyrm/templates/book/readthrough.html:76
#: bookwyrm/templates/lists/bookmark_button.html:15
#: bookwyrm/templates/lists/form.html:42
#: bookwyrm/templates/lists/form.html:44
#: bookwyrm/templates/preferences/edit_user.html:78
#: bookwyrm/templates/settings/announcement_form.html:69
#: bookwyrm/templates/settings/edit_server.html:68
@ -320,6 +320,7 @@ msgstr "Speichern"
#: bookwyrm/templates/book/cover_modal.html:32
#: bookwyrm/templates/book/edit_book.html:329
#: bookwyrm/templates/book/readthrough.html:77
#: bookwyrm/templates/lists/delete_list_modal.html:17
#: bookwyrm/templates/moderation/report_modal.html:34
#: bookwyrm/templates/settings/federated_server.html:99
#: bookwyrm/templates/snippets/delete_readthrough_modal.html:17
@ -731,6 +732,7 @@ msgstr "Suchergebnisse"
#: bookwyrm/templates/components/inline_form.html:8
#: bookwyrm/templates/components/modal.html:11
#: bookwyrm/templates/components/tooltip.html:7
#: bookwyrm/templates/feed/layout.html:71
#: bookwyrm/templates/get_started/layout.html:20
#: bookwyrm/templates/get_started/layout.html:53
@ -739,6 +741,10 @@ msgstr "Suchergebnisse"
msgid "Close"
msgstr "Schließen"
#: bookwyrm/templates/components/tooltip.html:3
msgid "Help"
msgstr ""
#: bookwyrm/templates/compose.html:5 bookwyrm/templates/compose.html:8
#, fuzzy
#| msgid "Boost status"
@ -766,6 +772,7 @@ msgid "Sorry! We couldn't find that code."
msgstr ""
#: bookwyrm/templates/confirm_email/confirm_email.html:19
#: bookwyrm/templates/user_admin/user_info.html:100
#, fuzzy
#| msgid "Confirm password:"
msgid "Confirmation code:"
@ -1283,125 +1290,130 @@ msgstr "Deine Bücher %(year)s"
msgid "%(username)s's %(year)s Books"
msgstr "%(username)ss %(year)s Bücher"
#: bookwyrm/templates/import.html:5 bookwyrm/templates/import.html:9
#: bookwyrm/templates/import/import.html:5
#: bookwyrm/templates/import/import.html:9
#: bookwyrm/templates/user/shelf/shelf.html:57
msgid "Import Books"
msgstr "Bücher importieren"
#: bookwyrm/templates/import.html:16
#: bookwyrm/templates/import/import.html:18
#, fuzzy
#| msgid "Data source"
msgid "Data source:"
msgstr "Datenquelle"
#: bookwyrm/templates/import.html:32
#: bookwyrm/templates/import/import.html:37
msgid "Data file:"
msgstr ""
#: bookwyrm/templates/import.html:40
#: bookwyrm/templates/import/import.html:45
msgid "Include reviews"
msgstr "Bewertungen importieren"
#: bookwyrm/templates/import.html:45
#: bookwyrm/templates/import/import.html:50
msgid "Privacy setting for imported reviews:"
msgstr "Datenschutzeinstellung für importierte Bewertungen"
#: bookwyrm/templates/import.html:51
#: bookwyrm/templates/import/import.html:56
#: bookwyrm/templates/settings/server_blocklist.html:64
msgid "Import"
msgstr "Importieren"
#: bookwyrm/templates/import.html:56
#: bookwyrm/templates/import/import.html:61
msgid "Recent Imports"
msgstr "Aktuelle Importe"
#: bookwyrm/templates/import.html:58
#: bookwyrm/templates/import/import.html:63
msgid "No recent imports"
msgstr "Keine aktuellen Importe"
#: bookwyrm/templates/import_status.html:6
#: bookwyrm/templates/import_status.html:10
#: bookwyrm/templates/import/import_status.html:6
#: bookwyrm/templates/import/import_status.html:10
msgid "Import Status"
msgstr "Importstatus"
#: bookwyrm/templates/import_status.html:11
#: bookwyrm/templates/import/import_status.html:11
#, fuzzy
#| msgid "Back to reports"
msgid "Back to imports"
msgstr "Zurück zu den Meldungen"
#: bookwyrm/templates/import_status.html:15
#: bookwyrm/templates/import/import_status.html:15
msgid "Import started:"
msgstr "Import gestartet:"
#: bookwyrm/templates/import_status.html:20
#: bookwyrm/templates/import/import_status.html:20
msgid "Import completed:"
msgstr "Import abgeschlossen:"
#: bookwyrm/templates/import_status.html:24
#: bookwyrm/templates/import/import_status.html:24
msgid "TASK FAILED"
msgstr "AUFGABE GESCHEITERT"
#: bookwyrm/templates/import_status.html:32
#: bookwyrm/templates/import/import_status.html:32
msgid "Import still in progress."
msgstr "Import läuft noch."
#: bookwyrm/templates/import_status.html:34
#: bookwyrm/templates/import/import_status.html:34
msgid "(Hit reload to update!)"
msgstr "(Aktualisiere für ein Update!)"
#: bookwyrm/templates/import_status.html:41
#: bookwyrm/templates/import/import_status.html:41
msgid "Failed to load"
msgstr "Laden fehlgeschlagen"
#: bookwyrm/templates/import_status.html:50
#: bookwyrm/templates/import/import_status.html:50
#, python-format
msgid "Jump to the bottom of the list to select the %(failed_count)s items which failed to import."
msgstr "Zum Ende der Liste springen, um die %(failed_count)s Einträge, deren Import fehlschlug, auszuwählen."
#: bookwyrm/templates/import_status.html:62
#: bookwyrm/templates/import/import_status.html:62
#, python-format
msgid "Line %(index)s: <strong>%(title)s</strong> by %(author)s"
msgstr ""
#: bookwyrm/templates/import_status.html:82
#: bookwyrm/templates/import/import_status.html:82
msgid "Select all"
msgstr "Alle auswählen"
#: bookwyrm/templates/import_status.html:85
#: bookwyrm/templates/import/import_status.html:85
msgid "Retry items"
msgstr "Punkte erneut versuchen"
#: bookwyrm/templates/import_status.html:112
#: bookwyrm/templates/import/import_status.html:112
msgid "Successfully imported"
msgstr "Erfolgreich importiert"
#: bookwyrm/templates/import_status.html:114
#: bookwyrm/templates/import/import_status.html:114
#, fuzzy
#| msgid "Import still in progress."
msgid "Import Progress"
msgstr "Import läuft noch."
#: bookwyrm/templates/import_status.html:119
#: bookwyrm/templates/import/import_status.html:119
msgid "Book"
msgstr "Buch"
#: bookwyrm/templates/import_status.html:122
#: bookwyrm/templates/import/import_status.html:122
#: bookwyrm/templates/user/shelf/shelf.html:111
#: bookwyrm/templates/user/shelf/shelf.html:131
msgid "Title"
msgstr "Titel"
#: bookwyrm/templates/import_status.html:125
#: bookwyrm/templates/import/import_status.html:125
#: bookwyrm/templates/user/shelf/shelf.html:112
#: bookwyrm/templates/user/shelf/shelf.html:134
msgid "Author"
msgstr "Autor*in"
#: bookwyrm/templates/import_status.html:148
#: bookwyrm/templates/import/import_status.html:148
msgid "Imported"
msgstr "Importiert"
#: 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."
msgstr ""
#: bookwyrm/templates/invite.html:4 bookwyrm/templates/invite.html:12
#: bookwyrm/templates/login.html:51
msgid "Create an Account"
@ -1605,8 +1617,27 @@ msgstr "Bestätigen"
msgid "Discard"
msgstr "Ablehnen"
#: bookwyrm/templates/lists/delete_list_modal.html:4
#, fuzzy
#| msgid "Delete this progress update"
msgid "Delete this list?"
msgstr "Dieses Fortschrittsupdate löschen"
#: bookwyrm/templates/lists/delete_list_modal.html:7
#, fuzzy
#| msgid "This shelf is empty."
msgid "This action cannot be un-done"
msgstr "Dieses Regal ist leer."
#: bookwyrm/templates/lists/delete_list_modal.html:15
#: bookwyrm/templates/settings/announcement.html:20
#: bookwyrm/templates/snippets/delete_readthrough_modal.html:15
#: bookwyrm/templates/snippets/follow_request_buttons.html:12
msgid "Delete"
msgstr "Löschen"
#: bookwyrm/templates/lists/edit_form.html:5
#: bookwyrm/templates/lists/list_layout.html:16
#: bookwyrm/templates/lists/layout.html:16
msgid "Edit List"
msgstr "Liste bearbeiten"
@ -1641,6 +1672,12 @@ msgstr "Offen"
msgid "Anyone can add books to this list"
msgstr "Alle können Bücher hinzufügen"
#: bookwyrm/templates/lists/form.html:49
#, fuzzy
#| msgid "Delete status"
msgid "Delete list"
msgstr "Post löschen"
#: bookwyrm/templates/lists/list.html:20
msgid "You successfully suggested a book for this list!"
msgstr ""
@ -2070,6 +2107,7 @@ msgid "Account"
msgstr ""
#: bookwyrm/templates/preferences/layout.html:15
#: bookwyrm/templates/user_admin/user_info.html:7
msgid "Profile"
msgstr "Profil"
@ -2208,12 +2246,6 @@ msgstr "Zurück zu den Meldungen"
msgid "Edit Announcement"
msgstr "Ankündigungen"
#: bookwyrm/templates/settings/announcement.html:20
#: bookwyrm/templates/snippets/delete_readthrough_modal.html:15
#: bookwyrm/templates/snippets/follow_request_buttons.html:12
msgid "Delete"
msgstr "Löschen"
#: bookwyrm/templates/settings/announcement.html:35
msgid "Visible:"
msgstr ""
@ -2280,6 +2312,7 @@ msgstr "Lesedaten bearbeiten"
#: bookwyrm/templates/settings/manage_invite_requests.html:44
#: bookwyrm/templates/settings/status_filter.html:5
#: bookwyrm/templates/user_admin/user_admin.html:34
#: bookwyrm/templates/user_admin/user_info.html:20
msgid "Status"
msgstr ""
@ -2329,7 +2362,7 @@ msgstr "Instanzname"
#: bookwyrm/templates/settings/edit_server.html:37
#: bookwyrm/templates/settings/federated_server.html:31
#: bookwyrm/templates/user_admin/user_info.html:34
#: bookwyrm/templates/user_admin/user_info.html:125
#, fuzzy
#| msgid "Import Status"
msgid "Status:"
@ -2348,13 +2381,13 @@ msgstr "Blockierte Nutzer*innen"
#: bookwyrm/templates/settings/edit_server.html:48
#: bookwyrm/templates/settings/federated_server.html:23
#: bookwyrm/templates/user_admin/user_info.html:26
#: bookwyrm/templates/user_admin/user_info.html:117
msgid "Software:"
msgstr ""
#: bookwyrm/templates/settings/edit_server.html:55
#: bookwyrm/templates/settings/federated_server.html:27
#: bookwyrm/templates/user_admin/user_info.html:30
#: bookwyrm/templates/user_admin/user_info.html:121
#, fuzzy
#| msgid "Description:"
msgid "Version:"
@ -2383,6 +2416,7 @@ msgid "View all"
msgstr ""
#: bookwyrm/templates/settings/federated_server.html:50
#: bookwyrm/templates/user_admin/user_info.html:59
#, fuzzy
#| msgid "Recent Imports"
msgid "Reports:"
@ -2407,7 +2441,7 @@ msgid "Blocked by us:"
msgstr "Blockierte Nutzer*innen"
#: bookwyrm/templates/settings/federated_server.html:82
#: bookwyrm/templates/user_admin/user_info.html:39
#: bookwyrm/templates/user_admin/user_info.html:130
msgid "Notes"
msgstr ""
@ -3428,7 +3462,7 @@ msgstr[1] "folgt dir"
msgid "No followers you follow"
msgstr "folgt dir"
#: bookwyrm/templates/user_admin/user.html:9
#: bookwyrm/templates/user_admin/user.html:8
#, fuzzy
#| msgid "Back to reports"
msgid "Back to users"
@ -3464,37 +3498,89 @@ msgid "Remote instance"
msgstr "Instanzname"
#: bookwyrm/templates/user_admin/user_admin.html:47
#: bookwyrm/templates/user_admin/user_info.html:24
#, fuzzy
#| msgid "Activity"
msgid "Active"
msgstr "Aktivität"
#: bookwyrm/templates/user_admin/user_admin.html:47
#: bookwyrm/templates/user_admin/user_info.html:28
msgid "Inactive"
msgstr ""
#: bookwyrm/templates/user_admin/user_admin.html:52
#: bookwyrm/templates/user_admin/user_info.html:49
#: bookwyrm/templates/user_admin/user_info.html:140
msgid "Not set"
msgstr ""
#: bookwyrm/templates/user_admin/user_info.html:5
msgid "User details"
msgstr ""
#: bookwyrm/templates/user_admin/user_info.html:14
#: bookwyrm/templates/user_admin/user_info.html:16
#, fuzzy
#| msgid "User Profile"
msgid "View user profile"
msgstr "Benutzerprofil"
#: bookwyrm/templates/user_admin/user_info.html:20
#: bookwyrm/templates/user_admin/user_info.html:36
msgid "Local"
msgstr "Lokal"
#: bookwyrm/templates/user_admin/user_info.html:38
#, fuzzy
#| msgid "Remove"
msgid "Remote"
msgstr "Entfernen"
#: bookwyrm/templates/user_admin/user_info.html:47
msgid "User details"
msgstr ""
#: bookwyrm/templates/user_admin/user_info.html:52
#, fuzzy
#| msgid "Email address:"
msgid "Email:"
msgstr "E-Mail Adresse"
#: bookwyrm/templates/user_admin/user_info.html:64
msgid "(View reports)"
msgstr ""
#: bookwyrm/templates/user_admin/user_info.html:72
#, fuzzy
#| msgid "Blocked Users"
msgid "Blocked by count:"
msgstr "Blockierte Nutzer*innen"
#: bookwyrm/templates/user_admin/user_info.html:77
#, fuzzy
#| msgid "Birth date:"
msgid "Last active date:"
msgstr "Geburtsdatum:"
#: bookwyrm/templates/user_admin/user_info.html:82
#, fuzzy
#| msgid "Manually approve followers:"
msgid "Manually approved followers:"
msgstr "Folgende manuell bestätigen"
#: bookwyrm/templates/user_admin/user_info.html:87
#, fuzzy
#| msgid "Discard"
msgid "Discoverable:"
msgstr "Ablehnen"
#: bookwyrm/templates/user_admin/user_info.html:93
#, fuzzy
#| msgid "Deactivate user"
msgid "Deactivation reason:"
msgstr "Nutzer:in deaktivieren"
#: bookwyrm/templates/user_admin/user_info.html:111
#, fuzzy
#| msgid "Instance Settings"
msgid "Instance details"
msgstr "Instanzeinstellungen"
#: bookwyrm/templates/user_admin/user_info.html:46
#: bookwyrm/templates/user_admin/user_info.html:137
msgid "View instance"
msgstr ""
@ -3523,16 +3609,16 @@ msgstr ""
msgid "%(title)s: %(subtitle)s"
msgstr ""
#: bookwyrm/views/authentication.py:69
msgid "Username or password are incorrect"
msgstr ""
#: bookwyrm/views/import_data.py:67
#, fuzzy
#| msgid "Email address:"
msgid "Not a valid csv file"
msgstr "E-Mail Adresse"
#: bookwyrm/views/login.py:70
msgid "Username or password are incorrect"
msgstr ""
#: bookwyrm/views/password.py:32
#, fuzzy
#| msgid "A user with that username already exists."
@ -3560,9 +3646,6 @@ msgstr ""
#~ msgid "Federated Timeline"
#~ msgstr "Föderierende Server"
#~ msgid "Local"
#~ msgstr "Lokal"
#, fuzzy
#~| msgid "Lists: %(username)s"
#~ msgid "Reports: <small>%(server_name)s</small>"
@ -3639,11 +3722,6 @@ msgstr ""
#~ msgid "Value %(value)r is not a valid choice."
#~ msgstr "%(value)s ist keine gültige remote_id"
#, fuzzy
#~| msgid "This shelf is empty."
#~ msgid "This field cannot be null."
#~ msgstr "Dieses Regal ist leer."
#, fuzzy
#~| msgid "A user with that username already exists."
#~ msgid "%(model_name)s with this %(field_label)s already exists."
@ -3873,9 +3951,6 @@ msgstr ""
#~ msgid "by %(author)s"
#~ msgstr "von %(author)s"
#~ msgid "Deactivate user"
#~ msgstr "Nutzer:in deaktivieren"
#~ msgid "Reactivate user"
#~ msgstr "Nutzer:in reaktivieren"

View file

@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: 0.0.1\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-09-05 23:09+0000\n"
"POT-Creation-Date: 2021-09-08 15:57+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"
@ -92,45 +92,45 @@ msgstr ""
msgid "A user with that username already exists."
msgstr ""
#: bookwyrm/settings.py:124
#: bookwyrm/settings.py:113
msgid "Home Timeline"
msgstr ""
#: bookwyrm/settings.py:124
#: bookwyrm/settings.py:113
msgid "Home"
msgstr ""
#: bookwyrm/settings.py:125
#: bookwyrm/settings.py:114
msgid "Books Timeline"
msgstr ""
#: bookwyrm/settings.py:125 bookwyrm/templates/search/layout.html:21
#: bookwyrm/settings.py:114 bookwyrm/templates/search/layout.html:21
#: bookwyrm/templates/search/layout.html:42
#: bookwyrm/templates/user/layout.html:81
msgid "Books"
msgstr ""
#: bookwyrm/settings.py:171
#: bookwyrm/settings.py:160
msgid "English"
msgstr ""
#: bookwyrm/settings.py:172
#: bookwyrm/settings.py:161
msgid "German"
msgstr ""
#: bookwyrm/settings.py:173
#: bookwyrm/settings.py:162
msgid "Spanish"
msgstr ""
#: bookwyrm/settings.py:174
#: bookwyrm/settings.py:163
msgid "French"
msgstr ""
#: bookwyrm/settings.py:175
#: bookwyrm/settings.py:164
msgid "Simplified Chinese"
msgstr ""
#: bookwyrm/settings.py:176
#: bookwyrm/settings.py:165
msgid "Traditional Chinese"
msgstr ""
@ -279,7 +279,7 @@ msgstr ""
#: bookwyrm/templates/book/edit_book.html:328
#: bookwyrm/templates/book/readthrough.html:76
#: bookwyrm/templates/lists/bookmark_button.html:15
#: bookwyrm/templates/lists/form.html:42
#: bookwyrm/templates/lists/form.html:44
#: bookwyrm/templates/preferences/edit_user.html:78
#: bookwyrm/templates/settings/announcement_form.html:69
#: bookwyrm/templates/settings/edit_server.html:68
@ -296,6 +296,7 @@ msgstr ""
#: bookwyrm/templates/book/cover_modal.html:32
#: bookwyrm/templates/book/edit_book.html:329
#: bookwyrm/templates/book/readthrough.html:77
#: bookwyrm/templates/lists/delete_list_modal.html:17
#: bookwyrm/templates/moderation/report_modal.html:34
#: bookwyrm/templates/settings/federated_server.html:99
#: bookwyrm/templates/snippets/delete_readthrough_modal.html:17
@ -672,6 +673,7 @@ msgstr ""
#: bookwyrm/templates/components/inline_form.html:8
#: bookwyrm/templates/components/modal.html:11
#: bookwyrm/templates/components/tooltip.html:7
#: bookwyrm/templates/feed/layout.html:71
#: bookwyrm/templates/get_started/layout.html:20
#: bookwyrm/templates/get_started/layout.html:53
@ -680,6 +682,10 @@ msgstr ""
msgid "Close"
msgstr ""
#: bookwyrm/templates/components/tooltip.html:3
msgid "Help"
msgstr ""
#: bookwyrm/templates/compose.html:5 bookwyrm/templates/compose.html:8
msgid "Compose status"
msgstr ""
@ -701,6 +707,7 @@ msgid "Sorry! We couldn't find that code."
msgstr ""
#: bookwyrm/templates/confirm_email/confirm_email.html:19
#: bookwyrm/templates/user_admin/user_info.html:100
msgid "Confirmation code:"
msgstr ""
@ -1168,119 +1175,124 @@ msgstr ""
msgid "%(username)s's %(year)s Books"
msgstr ""
#: bookwyrm/templates/import.html:5 bookwyrm/templates/import.html:9
#: bookwyrm/templates/import/import.html:5
#: bookwyrm/templates/import/import.html:9
#: bookwyrm/templates/user/shelf/shelf.html:57
msgid "Import Books"
msgstr ""
#: bookwyrm/templates/import.html:16
#: bookwyrm/templates/import/import.html:18
msgid "Data source:"
msgstr ""
#: bookwyrm/templates/import.html:32
#: bookwyrm/templates/import/import.html:37
msgid "Data file:"
msgstr ""
#: bookwyrm/templates/import.html:40
#: bookwyrm/templates/import/import.html:45
msgid "Include reviews"
msgstr ""
#: bookwyrm/templates/import.html:45
#: bookwyrm/templates/import/import.html:50
msgid "Privacy setting for imported reviews:"
msgstr ""
#: bookwyrm/templates/import.html:51
#: bookwyrm/templates/import/import.html:56
#: bookwyrm/templates/settings/server_blocklist.html:64
msgid "Import"
msgstr ""
#: bookwyrm/templates/import.html:56
#: bookwyrm/templates/import/import.html:61
msgid "Recent Imports"
msgstr ""
#: bookwyrm/templates/import.html:58
#: bookwyrm/templates/import/import.html:63
msgid "No recent imports"
msgstr ""
#: bookwyrm/templates/import_status.html:6
#: bookwyrm/templates/import_status.html:10
#: bookwyrm/templates/import/import_status.html:6
#: bookwyrm/templates/import/import_status.html:10
msgid "Import Status"
msgstr ""
#: bookwyrm/templates/import_status.html:11
#: bookwyrm/templates/import/import_status.html:11
msgid "Back to imports"
msgstr ""
#: bookwyrm/templates/import_status.html:15
#: bookwyrm/templates/import/import_status.html:15
msgid "Import started:"
msgstr ""
#: bookwyrm/templates/import_status.html:20
#: bookwyrm/templates/import/import_status.html:20
msgid "Import completed:"
msgstr ""
#: bookwyrm/templates/import_status.html:24
#: bookwyrm/templates/import/import_status.html:24
msgid "TASK FAILED"
msgstr ""
#: bookwyrm/templates/import_status.html:32
#: bookwyrm/templates/import/import_status.html:32
msgid "Import still in progress."
msgstr ""
#: bookwyrm/templates/import_status.html:34
#: bookwyrm/templates/import/import_status.html:34
msgid "(Hit reload to update!)"
msgstr ""
#: bookwyrm/templates/import_status.html:41
#: bookwyrm/templates/import/import_status.html:41
msgid "Failed to load"
msgstr ""
#: bookwyrm/templates/import_status.html:50
#: bookwyrm/templates/import/import_status.html:50
#, python-format
msgid "Jump to the bottom of the list to select the %(failed_count)s items which failed to import."
msgstr ""
#: bookwyrm/templates/import_status.html:62
#: bookwyrm/templates/import/import_status.html:62
#, python-format
msgid "Line %(index)s: <strong>%(title)s</strong> by %(author)s"
msgstr ""
#: bookwyrm/templates/import_status.html:82
#: bookwyrm/templates/import/import_status.html:82
msgid "Select all"
msgstr ""
#: bookwyrm/templates/import_status.html:85
#: bookwyrm/templates/import/import_status.html:85
msgid "Retry items"
msgstr ""
#: bookwyrm/templates/import_status.html:112
#: bookwyrm/templates/import/import_status.html:112
msgid "Successfully imported"
msgstr ""
#: bookwyrm/templates/import_status.html:114
#: bookwyrm/templates/import/import_status.html:114
msgid "Import Progress"
msgstr ""
#: bookwyrm/templates/import_status.html:119
#: bookwyrm/templates/import/import_status.html:119
msgid "Book"
msgstr ""
#: bookwyrm/templates/import_status.html:122
#: bookwyrm/templates/import/import_status.html:122
#: bookwyrm/templates/user/shelf/shelf.html:111
#: bookwyrm/templates/user/shelf/shelf.html:131
msgid "Title"
msgstr ""
#: bookwyrm/templates/import_status.html:125
#: bookwyrm/templates/import/import_status.html:125
#: bookwyrm/templates/user/shelf/shelf.html:112
#: bookwyrm/templates/user/shelf/shelf.html:134
msgid "Author"
msgstr ""
#: bookwyrm/templates/import_status.html:148
#: bookwyrm/templates/import/import_status.html:148
msgid "Imported"
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."
msgstr ""
#: bookwyrm/templates/invite.html:4 bookwyrm/templates/invite.html:12
#: bookwyrm/templates/login.html:51
msgid "Create an Account"
@ -1476,8 +1488,23 @@ msgstr ""
msgid "Discard"
msgstr ""
#: bookwyrm/templates/lists/delete_list_modal.html:4
msgid "Delete this list?"
msgstr ""
#: bookwyrm/templates/lists/delete_list_modal.html:7
msgid "This action cannot be un-done"
msgstr ""
#: bookwyrm/templates/lists/delete_list_modal.html:15
#: bookwyrm/templates/settings/announcement.html:20
#: bookwyrm/templates/snippets/delete_readthrough_modal.html:15
#: bookwyrm/templates/snippets/follow_request_buttons.html:12
msgid "Delete"
msgstr ""
#: bookwyrm/templates/lists/edit_form.html:5
#: bookwyrm/templates/lists/list_layout.html:16
#: bookwyrm/templates/lists/layout.html:16
msgid "Edit List"
msgstr ""
@ -1512,6 +1539,10 @@ msgstr ""
msgid "Anyone can add books to this list"
msgstr ""
#: bookwyrm/templates/lists/form.html:49
msgid "Delete list"
msgstr ""
#: bookwyrm/templates/lists/list.html:20
msgid "You successfully suggested a book for this list!"
msgstr ""
@ -1901,6 +1932,7 @@ msgid "Account"
msgstr ""
#: bookwyrm/templates/preferences/layout.html:15
#: bookwyrm/templates/user_admin/user_info.html:7
msgid "Profile"
msgstr ""
@ -2021,12 +2053,6 @@ msgstr ""
msgid "Edit Announcement"
msgstr ""
#: bookwyrm/templates/settings/announcement.html:20
#: bookwyrm/templates/snippets/delete_readthrough_modal.html:15
#: bookwyrm/templates/snippets/follow_request_buttons.html:12
msgid "Delete"
msgstr ""
#: bookwyrm/templates/settings/announcement.html:35
msgid "Visible:"
msgstr ""
@ -2077,6 +2103,7 @@ msgstr ""
#: bookwyrm/templates/settings/manage_invite_requests.html:44
#: bookwyrm/templates/settings/status_filter.html:5
#: bookwyrm/templates/user_admin/user_admin.html:34
#: bookwyrm/templates/user_admin/user_info.html:20
msgid "Status"
msgstr ""
@ -2114,7 +2141,7 @@ msgstr ""
#: bookwyrm/templates/settings/edit_server.html:37
#: bookwyrm/templates/settings/federated_server.html:31
#: bookwyrm/templates/user_admin/user_info.html:34
#: bookwyrm/templates/user_admin/user_info.html:125
msgid "Status:"
msgstr ""
@ -2129,13 +2156,13 @@ msgstr ""
#: bookwyrm/templates/settings/edit_server.html:48
#: bookwyrm/templates/settings/federated_server.html:23
#: bookwyrm/templates/user_admin/user_info.html:26
#: bookwyrm/templates/user_admin/user_info.html:117
msgid "Software:"
msgstr ""
#: bookwyrm/templates/settings/edit_server.html:55
#: bookwyrm/templates/settings/federated_server.html:27
#: bookwyrm/templates/user_admin/user_info.html:30
#: bookwyrm/templates/user_admin/user_info.html:121
msgid "Version:"
msgstr ""
@ -2162,6 +2189,7 @@ msgid "View all"
msgstr ""
#: bookwyrm/templates/settings/federated_server.html:50
#: bookwyrm/templates/user_admin/user_info.html:59
msgid "Reports:"
msgstr ""
@ -2178,7 +2206,7 @@ msgid "Blocked by us:"
msgstr ""
#: bookwyrm/templates/settings/federated_server.html:82
#: bookwyrm/templates/user_admin/user_info.html:39
#: bookwyrm/templates/user_admin/user_info.html:130
msgid "Notes"
msgstr ""
@ -3100,7 +3128,7 @@ msgstr[1] ""
msgid "No followers you follow"
msgstr ""
#: bookwyrm/templates/user_admin/user.html:9
#: bookwyrm/templates/user_admin/user.html:8
msgid "Back to users"
msgstr ""
@ -3127,31 +3155,69 @@ msgid "Remote instance"
msgstr ""
#: bookwyrm/templates/user_admin/user_admin.html:47
#: bookwyrm/templates/user_admin/user_info.html:24
msgid "Active"
msgstr ""
#: bookwyrm/templates/user_admin/user_admin.html:47
#: bookwyrm/templates/user_admin/user_info.html:28
msgid "Inactive"
msgstr ""
#: bookwyrm/templates/user_admin/user_admin.html:52
#: bookwyrm/templates/user_admin/user_info.html:49
#: bookwyrm/templates/user_admin/user_info.html:140
msgid "Not set"
msgstr ""
#: bookwyrm/templates/user_admin/user_info.html:5
msgid "User details"
msgstr ""
#: bookwyrm/templates/user_admin/user_info.html:14
#: bookwyrm/templates/user_admin/user_info.html:16
msgid "View user profile"
msgstr ""
#: bookwyrm/templates/user_admin/user_info.html:20
#: bookwyrm/templates/user_admin/user_info.html:36
msgid "Local"
msgstr ""
#: bookwyrm/templates/user_admin/user_info.html:38
msgid "Remote"
msgstr ""
#: bookwyrm/templates/user_admin/user_info.html:47
msgid "User details"
msgstr ""
#: bookwyrm/templates/user_admin/user_info.html:52
msgid "Email:"
msgstr ""
#: bookwyrm/templates/user_admin/user_info.html:64
msgid "(View reports)"
msgstr ""
#: bookwyrm/templates/user_admin/user_info.html:72
msgid "Blocked by count:"
msgstr ""
#: bookwyrm/templates/user_admin/user_info.html:77
msgid "Last active date:"
msgstr ""
#: bookwyrm/templates/user_admin/user_info.html:82
msgid "Manually approved followers:"
msgstr ""
#: bookwyrm/templates/user_admin/user_info.html:87
msgid "Discoverable:"
msgstr ""
#: bookwyrm/templates/user_admin/user_info.html:93
msgid "Deactivation reason:"
msgstr ""
#: bookwyrm/templates/user_admin/user_info.html:111
msgid "Instance details"
msgstr ""
#: bookwyrm/templates/user_admin/user_info.html:46
#: bookwyrm/templates/user_admin/user_info.html:137
msgid "View instance"
msgstr ""
@ -3180,14 +3246,14 @@ msgstr ""
msgid "%(title)s: %(subtitle)s"
msgstr ""
#: bookwyrm/views/authentication.py:69
msgid "Username or password are incorrect"
msgstr ""
#: bookwyrm/views/import_data.py:67
msgid "Not a valid csv file"
msgstr ""
#: bookwyrm/views/login.py:70
msgid "Username or password are incorrect"
msgstr ""
#: bookwyrm/views/password.py:32
msgid "No user with that email address was found."
msgstr ""

Binary file not shown.

View file

@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: 0.0.1\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-09-05 23:09+0000\n"
"POT-Creation-Date: 2021-09-08 15:57+0000\n"
"PO-Revision-Date: 2021-03-19 11:49+0800\n"
"Last-Translator: Reese Porter <reesedporter@gmail.com>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@ -92,45 +92,45 @@ msgstr "nombre de usuario"
msgid "A user with that username already exists."
msgstr "Ya existe un usuario con ese nombre."
#: bookwyrm/settings.py:124
#: bookwyrm/settings.py:113
msgid "Home Timeline"
msgstr "Línea temporal de hogar"
#: bookwyrm/settings.py:124
#: bookwyrm/settings.py:113
msgid "Home"
msgstr "Hogar"
#: bookwyrm/settings.py:125
#: bookwyrm/settings.py:114
msgid "Books Timeline"
msgstr "Línea temporal de libros"
#: bookwyrm/settings.py:125 bookwyrm/templates/search/layout.html:21
#: bookwyrm/settings.py:114 bookwyrm/templates/search/layout.html:21
#: bookwyrm/templates/search/layout.html:42
#: bookwyrm/templates/user/layout.html:81
msgid "Books"
msgstr "Libros"
#: bookwyrm/settings.py:171
#: bookwyrm/settings.py:160
msgid "English"
msgstr "Inglés"
#: bookwyrm/settings.py:172
#: bookwyrm/settings.py:161
msgid "German"
msgstr "Aléman"
#: bookwyrm/settings.py:173
#: bookwyrm/settings.py:162
msgid "Spanish"
msgstr "Español"
#: bookwyrm/settings.py:174
#: bookwyrm/settings.py:163
msgid "French"
msgstr "Francés"
#: bookwyrm/settings.py:175
#: bookwyrm/settings.py:164
msgid "Simplified Chinese"
msgstr "Chino simplificado"
#: bookwyrm/settings.py:176
#: bookwyrm/settings.py:165
msgid "Traditional Chinese"
msgstr "Chino tradicional"
@ -279,7 +279,7 @@ msgstr "Clave Goodreads:"
#: bookwyrm/templates/book/edit_book.html:328
#: bookwyrm/templates/book/readthrough.html:76
#: bookwyrm/templates/lists/bookmark_button.html:15
#: bookwyrm/templates/lists/form.html:42
#: bookwyrm/templates/lists/form.html:44
#: bookwyrm/templates/preferences/edit_user.html:78
#: bookwyrm/templates/settings/announcement_form.html:69
#: bookwyrm/templates/settings/edit_server.html:68
@ -296,6 +296,7 @@ msgstr "Guardar"
#: bookwyrm/templates/book/cover_modal.html:32
#: bookwyrm/templates/book/edit_book.html:329
#: bookwyrm/templates/book/readthrough.html:77
#: bookwyrm/templates/lists/delete_list_modal.html:17
#: bookwyrm/templates/moderation/report_modal.html:34
#: bookwyrm/templates/settings/federated_server.html:99
#: bookwyrm/templates/snippets/delete_readthrough_modal.html:17
@ -672,6 +673,7 @@ msgstr "Buscar ediciones"
#: bookwyrm/templates/components/inline_form.html:8
#: bookwyrm/templates/components/modal.html:11
#: bookwyrm/templates/components/tooltip.html:7
#: bookwyrm/templates/feed/layout.html:71
#: bookwyrm/templates/get_started/layout.html:20
#: bookwyrm/templates/get_started/layout.html:53
@ -680,6 +682,10 @@ msgstr "Buscar ediciones"
msgid "Close"
msgstr "Cerrar"
#: bookwyrm/templates/components/tooltip.html:3
msgid "Help"
msgstr ""
#: bookwyrm/templates/compose.html:5 bookwyrm/templates/compose.html:8
msgid "Compose status"
msgstr "Componer status"
@ -701,6 +707,7 @@ msgid "Sorry! We couldn't find that code."
msgstr "Sentimos que no pudimos encontrar ese código"
#: bookwyrm/templates/confirm_email/confirm_email.html:19
#: bookwyrm/templates/user_admin/user_info.html:100
msgid "Confirmation code:"
msgstr "Código de confirmación:"
@ -1168,119 +1175,124 @@ msgstr "Tus libros de %(year)s"
msgid "%(username)s's %(year)s Books"
msgstr "Los libros de %(username)s para %(year)s"
#: bookwyrm/templates/import.html:5 bookwyrm/templates/import.html:9
#: bookwyrm/templates/import/import.html:5
#: bookwyrm/templates/import/import.html:9
#: bookwyrm/templates/user/shelf/shelf.html:57
msgid "Import Books"
msgstr "Importar libros"
#: bookwyrm/templates/import.html:16
#: bookwyrm/templates/import/import.html:18
msgid "Data source:"
msgstr "Fuente de datos:"
#: bookwyrm/templates/import.html:32
#: bookwyrm/templates/import/import.html:37
msgid "Data file:"
msgstr "Archivo de datos:"
#: bookwyrm/templates/import.html:40
#: bookwyrm/templates/import/import.html:45
msgid "Include reviews"
msgstr "Incluir reseñas"
#: bookwyrm/templates/import.html:45
#: bookwyrm/templates/import/import.html:50
msgid "Privacy setting for imported reviews:"
msgstr "Configuración de privacidad para las reseñas importadas:"
#: bookwyrm/templates/import.html:51
#: bookwyrm/templates/import/import.html:56
#: bookwyrm/templates/settings/server_blocklist.html:64
msgid "Import"
msgstr "Importar"
#: bookwyrm/templates/import.html:56
#: bookwyrm/templates/import/import.html:61
msgid "Recent Imports"
msgstr "Importaciones recientes"
#: bookwyrm/templates/import.html:58
#: bookwyrm/templates/import/import.html:63
msgid "No recent imports"
msgstr "No hay ninguna importación reciente"
#: bookwyrm/templates/import_status.html:6
#: bookwyrm/templates/import_status.html:10
#: bookwyrm/templates/import/import_status.html:6
#: bookwyrm/templates/import/import_status.html:10
msgid "Import Status"
msgstr "Status de importación"
#: bookwyrm/templates/import_status.html:11
#: bookwyrm/templates/import/import_status.html:11
msgid "Back to imports"
msgstr "Volver a las importaciones"
#: bookwyrm/templates/import_status.html:15
#: bookwyrm/templates/import/import_status.html:15
msgid "Import started:"
msgstr "Importación ha empezado:"
#: bookwyrm/templates/import_status.html:20
#: bookwyrm/templates/import/import_status.html:20
msgid "Import completed:"
msgstr "Importación ha terminado:"
#: bookwyrm/templates/import_status.html:24
#: bookwyrm/templates/import/import_status.html:24
msgid "TASK FAILED"
msgstr "TAREA FALLÓ"
#: bookwyrm/templates/import_status.html:32
#: bookwyrm/templates/import/import_status.html:32
msgid "Import still in progress."
msgstr "Importación todavia en progreso"
#: bookwyrm/templates/import_status.html:34
#: bookwyrm/templates/import/import_status.html:34
msgid "(Hit reload to update!)"
msgstr "(¡Refresca para actualizar!)"
#: bookwyrm/templates/import_status.html:41
#: bookwyrm/templates/import/import_status.html:41
msgid "Failed to load"
msgstr "No se pudo cargar"
#: bookwyrm/templates/import_status.html:50
#: bookwyrm/templates/import/import_status.html:50
#, python-format
msgid "Jump to the bottom of the list to select the %(failed_count)s items which failed to import."
msgstr "Saltar al final de la lista para seleccionar los %(failed_count)s artículos que no se pudieron importar."
#: bookwyrm/templates/import_status.html:62
#: bookwyrm/templates/import/import_status.html:62
#, python-format
msgid "Line %(index)s: <strong>%(title)s</strong> by %(author)s"
msgstr "Renglón %(index)s: <strong>%(title)s</strong> por %(author)s"
#: bookwyrm/templates/import_status.html:82
#: bookwyrm/templates/import/import_status.html:82
msgid "Select all"
msgstr "Seleccionar todo"
#: bookwyrm/templates/import_status.html:85
#: bookwyrm/templates/import/import_status.html:85
msgid "Retry items"
msgstr "Reintentar ítems"
#: bookwyrm/templates/import_status.html:112
#: bookwyrm/templates/import/import_status.html:112
msgid "Successfully imported"
msgstr "Importado exitosamente"
#: bookwyrm/templates/import_status.html:114
#: bookwyrm/templates/import/import_status.html:114
msgid "Import Progress"
msgstr "Progreso de importación"
#: bookwyrm/templates/import_status.html:119
#: bookwyrm/templates/import/import_status.html:119
msgid "Book"
msgstr "Libro"
#: bookwyrm/templates/import_status.html:122
#: bookwyrm/templates/import/import_status.html:122
#: bookwyrm/templates/user/shelf/shelf.html:111
#: bookwyrm/templates/user/shelf/shelf.html:131
msgid "Title"
msgstr "Título"
#: bookwyrm/templates/import_status.html:125
#: bookwyrm/templates/import/import_status.html:125
#: bookwyrm/templates/user/shelf/shelf.html:112
#: bookwyrm/templates/user/shelf/shelf.html:134
msgid "Author"
msgstr "Autor/Autora"
#: bookwyrm/templates/import_status.html:148
#: bookwyrm/templates/import/import_status.html:148
msgid "Imported"
msgstr "Importado"
#: 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."
msgstr ""
#: bookwyrm/templates/invite.html:4 bookwyrm/templates/invite.html:12
#: bookwyrm/templates/login.html:51
msgid "Create an Account"
@ -1476,8 +1488,27 @@ msgstr "Aprobar"
msgid "Discard"
msgstr "Desechar"
#: bookwyrm/templates/lists/delete_list_modal.html:4
#, fuzzy
#| msgid "Delete this progress update"
msgid "Delete this list?"
msgstr "Eliminar esta actualización de progreso"
#: bookwyrm/templates/lists/delete_list_modal.html:7
#, fuzzy
#| msgid "This field cannot be null."
msgid "This action cannot be un-done"
msgstr "Este campo no puede ser nulo."
#: bookwyrm/templates/lists/delete_list_modal.html:15
#: bookwyrm/templates/settings/announcement.html:20
#: bookwyrm/templates/snippets/delete_readthrough_modal.html:15
#: bookwyrm/templates/snippets/follow_request_buttons.html:12
msgid "Delete"
msgstr "Eliminar"
#: bookwyrm/templates/lists/edit_form.html:5
#: bookwyrm/templates/lists/list_layout.html:16
#: bookwyrm/templates/lists/layout.html:16
msgid "Edit List"
msgstr "Editar lista"
@ -1512,6 +1543,12 @@ msgstr "Abierto"
msgid "Anyone can add books to this list"
msgstr "Cualquer usuario puede agregar libros a esta lista"
#: bookwyrm/templates/lists/form.html:49
#, fuzzy
#| msgid "Delete status"
msgid "Delete list"
msgstr "Eliminar status"
#: bookwyrm/templates/lists/list.html:20
msgid "You successfully suggested a book for this list!"
msgstr "¡Has sugerido un libro para esta lista exitosamente!"
@ -1901,6 +1938,7 @@ msgid "Account"
msgstr "Cuenta"
#: bookwyrm/templates/preferences/layout.html:15
#: bookwyrm/templates/user_admin/user_info.html:7
msgid "Profile"
msgstr "Perfil"
@ -2021,12 +2059,6 @@ msgstr "Volver a la lista de servidores"
msgid "Edit Announcement"
msgstr "Editar anuncio"
#: bookwyrm/templates/settings/announcement.html:20
#: bookwyrm/templates/snippets/delete_readthrough_modal.html:15
#: bookwyrm/templates/snippets/follow_request_buttons.html:12
msgid "Delete"
msgstr "Eliminar"
#: bookwyrm/templates/settings/announcement.html:35
msgid "Visible:"
msgstr "Visible:"
@ -2077,6 +2109,7 @@ msgstr "Fecha final"
#: bookwyrm/templates/settings/manage_invite_requests.html:44
#: bookwyrm/templates/settings/status_filter.html:5
#: bookwyrm/templates/user_admin/user_admin.html:34
#: bookwyrm/templates/user_admin/user_info.html:20
msgid "Status"
msgstr "Status"
@ -2114,7 +2147,7 @@ msgstr "Instancia:"
#: bookwyrm/templates/settings/edit_server.html:37
#: bookwyrm/templates/settings/federated_server.html:31
#: bookwyrm/templates/user_admin/user_info.html:34
#: bookwyrm/templates/user_admin/user_info.html:125
msgid "Status:"
msgstr "Status:"
@ -2129,13 +2162,13 @@ msgstr "Bloqueado"
#: bookwyrm/templates/settings/edit_server.html:48
#: bookwyrm/templates/settings/federated_server.html:23
#: bookwyrm/templates/user_admin/user_info.html:26
#: bookwyrm/templates/user_admin/user_info.html:117
msgid "Software:"
msgstr "Software:"
#: bookwyrm/templates/settings/edit_server.html:55
#: bookwyrm/templates/settings/federated_server.html:27
#: bookwyrm/templates/user_admin/user_info.html:30
#: bookwyrm/templates/user_admin/user_info.html:121
msgid "Version:"
msgstr "Versión:"
@ -2162,6 +2195,7 @@ msgid "View all"
msgstr "Ver todos"
#: bookwyrm/templates/settings/federated_server.html:50
#: bookwyrm/templates/user_admin/user_info.html:59
msgid "Reports:"
msgstr "Informes:"
@ -2178,7 +2212,7 @@ msgid "Blocked by us:"
msgstr "Bloqueado por nosotros:"
#: bookwyrm/templates/settings/federated_server.html:82
#: bookwyrm/templates/user_admin/user_info.html:39
#: bookwyrm/templates/user_admin/user_info.html:130
msgid "Notes"
msgstr "Notas"
@ -3108,7 +3142,7 @@ msgstr[1] "%(mutuals_display)s seguidores que sigues"
msgid "No followers you follow"
msgstr "Ningún seguidor que tu sigues"
#: bookwyrm/templates/user_admin/user.html:9
#: bookwyrm/templates/user_admin/user.html:8
msgid "Back to users"
msgstr "Volver a usuarios"
@ -3135,31 +3169,85 @@ msgid "Remote instance"
msgstr "Instancia remota"
#: bookwyrm/templates/user_admin/user_admin.html:47
#: bookwyrm/templates/user_admin/user_info.html:24
msgid "Active"
msgstr "Activ@"
#: bookwyrm/templates/user_admin/user_admin.html:47
#: bookwyrm/templates/user_admin/user_info.html:28
msgid "Inactive"
msgstr "Inactiv@"
#: bookwyrm/templates/user_admin/user_admin.html:52
#: bookwyrm/templates/user_admin/user_info.html:49
#: bookwyrm/templates/user_admin/user_info.html:140
msgid "Not set"
msgstr "No establecido"
#: bookwyrm/templates/user_admin/user_info.html:5
msgid "User details"
msgstr "Detalles"
#: bookwyrm/templates/user_admin/user_info.html:14
#: bookwyrm/templates/user_admin/user_info.html:16
msgid "View user profile"
msgstr "Ver perfil de usuario"
#: bookwyrm/templates/user_admin/user_info.html:20
#: bookwyrm/templates/user_admin/user_info.html:36
msgid "Local"
msgstr "Local"
#: bookwyrm/templates/user_admin/user_info.html:38
#, fuzzy
#| msgid "Remove"
msgid "Remote"
msgstr "Quitar"
#: bookwyrm/templates/user_admin/user_info.html:47
msgid "User details"
msgstr "Detalles"
#: bookwyrm/templates/user_admin/user_info.html:52
#, fuzzy
#| msgid "Email"
msgid "Email:"
msgstr "Correo electronico"
#: bookwyrm/templates/user_admin/user_info.html:64
#, fuzzy
#| msgid "View directory"
msgid "(View reports)"
msgstr "Ver directorio"
#: bookwyrm/templates/user_admin/user_info.html:72
#, fuzzy
#| msgid "Blocked by us:"
msgid "Blocked by count:"
msgstr "Bloqueado por nosotros:"
#: bookwyrm/templates/user_admin/user_info.html:77
#, fuzzy
#| msgid "last active"
msgid "Last active date:"
msgstr "actividad reciente"
#: bookwyrm/templates/user_admin/user_info.html:82
#, fuzzy
#| msgid "Manually approve followers:"
msgid "Manually approved followers:"
msgstr "Aprobar seguidores a mano:"
#: bookwyrm/templates/user_admin/user_info.html:87
#, fuzzy
#| msgid "Discover"
msgid "Discoverable:"
msgstr "Descubrir"
#: bookwyrm/templates/user_admin/user_info.html:93
#, fuzzy
#| msgid "Deactivate user"
msgid "Deactivation reason:"
msgstr "Desactivar usuario"
#: bookwyrm/templates/user_admin/user_info.html:111
msgid "Instance details"
msgstr "Detalles de instancia"
#: bookwyrm/templates/user_admin/user_info.html:46
#: bookwyrm/templates/user_admin/user_info.html:137
msgid "View instance"
msgstr "Ver instancia"
@ -3188,14 +3276,14 @@ msgstr "Archivo excede el tamaño máximo: 10MB"
msgid "%(title)s: %(subtitle)s"
msgstr "%(title)s: %(subtitle)s"
#: bookwyrm/views/authentication.py:69
msgid "Username or password are incorrect"
msgstr "Nombre de usuario o contraseña es incorrecta"
#: bookwyrm/views/import_data.py:67
msgid "Not a valid csv file"
msgstr "No un archivo csv válido"
#: bookwyrm/views/login.py:70
msgid "Username or password are incorrect"
msgstr "Nombre de usuario o contraseña es incorrecta"
#: bookwyrm/views/password.py:32
msgid "No user with that email address was found."
msgstr "No se pudo encontrar un usuario con esa dirección de correo electrónico."
@ -3222,9 +3310,6 @@ msgstr "Actualizaciones de status de {obj.display_name}"
#~ msgid "Federated Timeline"
#~ msgstr "Línea temporal federalizado"
#~ msgid "Local"
#~ msgstr "Local"
#~ msgid "Federated Servers"
#~ msgstr "Servidores federalizados"
@ -3348,9 +3433,6 @@ msgstr "Actualizaciones de status de {obj.display_name}"
#~ msgid "Value %(value)r is not a valid choice."
#~ msgstr "El valor %(value)s no es una opción válida."
#~ msgid "This field cannot be null."
#~ msgstr "Este campo no puede ser nulo."
#~ msgid "This field cannot be blank."
#~ msgstr "Este campo no puede ser vacio."
@ -4128,9 +4210,6 @@ msgstr "Actualizaciones de status de {obj.display_name}"
#~ msgid "Books tagged \"%(tag.name)s\""
#~ msgstr "Libros etiquetados con \"%(tag.name)s\""
#~ msgid "Deactivate user"
#~ msgstr "Desactivar usuario"
#~ msgid "Reactivate user"
#~ msgstr "Reactivar usuario"

Binary file not shown.

View file

@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: 0.1.1\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-09-05 23:09+0000\n"
"POT-Creation-Date: 2021-09-08 15:57+0000\n"
"PO-Revision-Date: 2021-04-05 12:44+0100\n"
"Last-Translator: Fabien Basmaison <contact@arkhi.org>\n"
"Language-Team: Mouse Reeve <LL@li.org>\n"
@ -92,47 +92,47 @@ msgstr "nom du compte:"
msgid "A user with that username already exists."
msgstr "Ce nom est déjà associé à un compte."
#: bookwyrm/settings.py:124
#: bookwyrm/settings.py:113
msgid "Home Timeline"
msgstr "Mon fil dactualité"
#: bookwyrm/settings.py:124
#: bookwyrm/settings.py:113
msgid "Home"
msgstr "Accueil"
#: bookwyrm/settings.py:125
#: bookwyrm/settings.py:114
#, fuzzy
#| msgid "Book Title"
msgid "Books Timeline"
msgstr "Titre du livre"
#: bookwyrm/settings.py:125 bookwyrm/templates/search/layout.html:21
#: bookwyrm/settings.py:114 bookwyrm/templates/search/layout.html:21
#: bookwyrm/templates/search/layout.html:42
#: bookwyrm/templates/user/layout.html:81
msgid "Books"
msgstr "Livres"
#: bookwyrm/settings.py:171
#: bookwyrm/settings.py:160
msgid "English"
msgstr "English"
#: bookwyrm/settings.py:172
#: bookwyrm/settings.py:161
msgid "German"
msgstr "Deutsch"
#: bookwyrm/settings.py:173
#: bookwyrm/settings.py:162
msgid "Spanish"
msgstr "Español"
#: bookwyrm/settings.py:174
#: bookwyrm/settings.py:163
msgid "French"
msgstr "Français"
#: bookwyrm/settings.py:175
#: bookwyrm/settings.py:164
msgid "Simplified Chinese"
msgstr "简化字"
#: bookwyrm/settings.py:176
#: bookwyrm/settings.py:165
#, fuzzy
#| msgid "Additional info:"
msgid "Traditional Chinese"
@ -283,7 +283,7 @@ msgstr "Clé Goodreads:"
#: bookwyrm/templates/book/edit_book.html:328
#: bookwyrm/templates/book/readthrough.html:76
#: bookwyrm/templates/lists/bookmark_button.html:15
#: bookwyrm/templates/lists/form.html:42
#: bookwyrm/templates/lists/form.html:44
#: bookwyrm/templates/preferences/edit_user.html:78
#: bookwyrm/templates/settings/announcement_form.html:69
#: bookwyrm/templates/settings/edit_server.html:68
@ -300,6 +300,7 @@ msgstr "Enregistrer"
#: bookwyrm/templates/book/cover_modal.html:32
#: bookwyrm/templates/book/edit_book.html:329
#: bookwyrm/templates/book/readthrough.html:77
#: bookwyrm/templates/lists/delete_list_modal.html:17
#: bookwyrm/templates/moderation/report_modal.html:34
#: bookwyrm/templates/settings/federated_server.html:99
#: bookwyrm/templates/snippets/delete_readthrough_modal.html:17
@ -678,6 +679,7 @@ msgstr "Résultats de recherche"
#: bookwyrm/templates/components/inline_form.html:8
#: bookwyrm/templates/components/modal.html:11
#: bookwyrm/templates/components/tooltip.html:7
#: bookwyrm/templates/feed/layout.html:71
#: bookwyrm/templates/get_started/layout.html:20
#: bookwyrm/templates/get_started/layout.html:53
@ -686,6 +688,10 @@ msgstr "Résultats de recherche"
msgid "Close"
msgstr "Fermer"
#: bookwyrm/templates/components/tooltip.html:3
msgid "Help"
msgstr ""
#: bookwyrm/templates/compose.html:5 bookwyrm/templates/compose.html:8
msgid "Compose status"
msgstr "Rédiger un statut"
@ -707,6 +713,7 @@ msgid "Sorry! We couldn't find that code."
msgstr "Pardon! Nous ne reconnaissons pas ce code."
#: bookwyrm/templates/confirm_email/confirm_email.html:19
#: bookwyrm/templates/user_admin/user_info.html:100
msgid "Confirmation code:"
msgstr "Code de confirmation:"
@ -1183,123 +1190,128 @@ msgstr "Vos livres en %(year)s"
msgid "%(username)s's %(year)s Books"
msgstr "Livres de %(username)s en %(year)s"
#: bookwyrm/templates/import.html:5 bookwyrm/templates/import.html:9
#: bookwyrm/templates/import/import.html:5
#: bookwyrm/templates/import/import.html:9
#: bookwyrm/templates/user/shelf/shelf.html:57
msgid "Import Books"
msgstr "Importer des livres"
#: bookwyrm/templates/import.html:16
#: bookwyrm/templates/import/import.html:18
msgid "Data source:"
msgstr "Source de données:"
#: bookwyrm/templates/import.html:32
#: bookwyrm/templates/import/import.html:37
msgid "Data file:"
msgstr "Fichier de données:"
#: bookwyrm/templates/import.html:40
#: bookwyrm/templates/import/import.html:45
msgid "Include reviews"
msgstr "Importer les critiques"
#: bookwyrm/templates/import.html:45
#: bookwyrm/templates/import/import.html:50
msgid "Privacy setting for imported reviews:"
msgstr "Confidentialité des critiques importées:"
#: bookwyrm/templates/import.html:51
#: bookwyrm/templates/import/import.html:56
#: bookwyrm/templates/settings/server_blocklist.html:64
msgid "Import"
msgstr "Importer"
#: bookwyrm/templates/import.html:56
#: bookwyrm/templates/import/import.html:61
msgid "Recent Imports"
msgstr "Importations récentes"
#: bookwyrm/templates/import.html:58
#: bookwyrm/templates/import/import.html:63
msgid "No recent imports"
msgstr "Aucune importation récente"
#: bookwyrm/templates/import_status.html:6
#: bookwyrm/templates/import_status.html:10
#: bookwyrm/templates/import/import_status.html:6
#: bookwyrm/templates/import/import_status.html:10
msgid "Import Status"
msgstr "Statut de limportation"
#: bookwyrm/templates/import_status.html:11
#: bookwyrm/templates/import/import_status.html:11
#, fuzzy
#| msgid "Back to reports"
msgid "Back to imports"
msgstr "Retour aux signalements"
#: bookwyrm/templates/import_status.html:15
#: bookwyrm/templates/import/import_status.html:15
msgid "Import started:"
msgstr "Début de limportation:"
#: bookwyrm/templates/import_status.html:20
#: bookwyrm/templates/import/import_status.html:20
msgid "Import completed:"
msgstr "Fin de limportation:"
#: bookwyrm/templates/import_status.html:24
#: bookwyrm/templates/import/import_status.html:24
msgid "TASK FAILED"
msgstr "la tâche a échoué"
#: bookwyrm/templates/import_status.html:32
#: bookwyrm/templates/import/import_status.html:32
msgid "Import still in progress."
msgstr "Limportation est toujours en cours"
#: bookwyrm/templates/import_status.html:34
#: bookwyrm/templates/import/import_status.html:34
msgid "(Hit reload to update!)"
msgstr "(Rechargez la page pour la mettre à jour!)"
#: bookwyrm/templates/import_status.html:41
#: bookwyrm/templates/import/import_status.html:41
msgid "Failed to load"
msgstr "Éléments non importés"
#: bookwyrm/templates/import_status.html:50
#: bookwyrm/templates/import/import_status.html:50
#, python-format
msgid "Jump to the bottom of the list to select the %(failed_count)s items which failed to import."
msgstr "Sauter en bas de liste pour sélectionner les %(failed_count)s items nayant pu être importés."
#: bookwyrm/templates/import_status.html:62
#: bookwyrm/templates/import/import_status.html:62
#, python-format
msgid "Line %(index)s: <strong>%(title)s</strong> by %(author)s"
msgstr "Ligne %(index)s: <strong>%(title)s</strong> par %(author)s"
#: bookwyrm/templates/import_status.html:82
#: bookwyrm/templates/import/import_status.html:82
msgid "Select all"
msgstr "Tout sélectionner"
#: bookwyrm/templates/import_status.html:85
#: bookwyrm/templates/import/import_status.html:85
msgid "Retry items"
msgstr "Réessayer limportation de ces éléments"
#: bookwyrm/templates/import_status.html:112
#: bookwyrm/templates/import/import_status.html:112
msgid "Successfully imported"
msgstr "Importation réussie"
#: bookwyrm/templates/import_status.html:114
#: bookwyrm/templates/import/import_status.html:114
#, fuzzy
#| msgid "Import still in progress."
msgid "Import Progress"
msgstr "Limportation est toujours en cours"
#: bookwyrm/templates/import_status.html:119
#: bookwyrm/templates/import/import_status.html:119
msgid "Book"
msgstr "Livre"
#: bookwyrm/templates/import_status.html:122
#: bookwyrm/templates/import/import_status.html:122
#: bookwyrm/templates/user/shelf/shelf.html:111
#: bookwyrm/templates/user/shelf/shelf.html:131
msgid "Title"
msgstr "Titre"
#: bookwyrm/templates/import_status.html:125
#: bookwyrm/templates/import/import_status.html:125
#: bookwyrm/templates/user/shelf/shelf.html:112
#: bookwyrm/templates/user/shelf/shelf.html:134
msgid "Author"
msgstr "Auteur/autrice"
#: bookwyrm/templates/import_status.html:148
#: bookwyrm/templates/import/import_status.html:148
msgid "Imported"
msgstr "Importé"
#: 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."
msgstr ""
#: bookwyrm/templates/invite.html:4 bookwyrm/templates/invite.html:12
#: bookwyrm/templates/login.html:51
msgid "Create an Account"
@ -1495,8 +1507,27 @@ msgstr "Approuver"
msgid "Discard"
msgstr "Rejeter"
#: bookwyrm/templates/lists/delete_list_modal.html:4
#, fuzzy
#| msgid "Delete this progress update"
msgid "Delete this list?"
msgstr "Supprimer cette mise à jour"
#: bookwyrm/templates/lists/delete_list_modal.html:7
#, fuzzy
#| msgid "This shelf is empty."
msgid "This action cannot be un-done"
msgstr "Cette étagère est vide"
#: bookwyrm/templates/lists/delete_list_modal.html:15
#: bookwyrm/templates/settings/announcement.html:20
#: bookwyrm/templates/snippets/delete_readthrough_modal.html:15
#: bookwyrm/templates/snippets/follow_request_buttons.html:12
msgid "Delete"
msgstr "Supprimer"
#: bookwyrm/templates/lists/edit_form.html:5
#: bookwyrm/templates/lists/list_layout.html:16
#: bookwyrm/templates/lists/layout.html:16
msgid "Edit List"
msgstr "Modifier la liste"
@ -1531,6 +1562,12 @@ msgstr "Ouverte"
msgid "Anyone can add books to this list"
msgstr "Nimporte qui peut suggérer des livres"
#: bookwyrm/templates/lists/form.html:49
#, fuzzy
#| msgid "Delete status"
msgid "Delete list"
msgstr "Supprimer le statut"
#: bookwyrm/templates/lists/list.html:20
msgid "You successfully suggested a book for this list!"
msgstr "Vous avez suggéré un livre à cette liste!"
@ -1930,6 +1967,7 @@ msgid "Account"
msgstr "Compte"
#: bookwyrm/templates/preferences/layout.html:15
#: bookwyrm/templates/user_admin/user_info.html:7
msgid "Profile"
msgstr "Profil"
@ -2053,12 +2091,6 @@ msgstr "Retour à la liste"
msgid "Edit Announcement"
msgstr "Modifier lannonce"
#: bookwyrm/templates/settings/announcement.html:20
#: bookwyrm/templates/snippets/delete_readthrough_modal.html:15
#: bookwyrm/templates/snippets/follow_request_buttons.html:12
msgid "Delete"
msgstr "Supprimer"
#: bookwyrm/templates/settings/announcement.html:35
msgid "Visible:"
msgstr "Visible:"
@ -2109,6 +2141,7 @@ msgstr "Date de fin"
#: bookwyrm/templates/settings/manage_invite_requests.html:44
#: bookwyrm/templates/settings/status_filter.html:5
#: bookwyrm/templates/user_admin/user_admin.html:34
#: bookwyrm/templates/user_admin/user_info.html:20
msgid "Status"
msgstr "Statut"
@ -2146,7 +2179,7 @@ msgstr "Instance:"
#: bookwyrm/templates/settings/edit_server.html:37
#: bookwyrm/templates/settings/federated_server.html:31
#: bookwyrm/templates/user_admin/user_info.html:34
#: bookwyrm/templates/user_admin/user_info.html:125
msgid "Status:"
msgstr "Statut:"
@ -2161,13 +2194,13 @@ msgstr "Bloqué"
#: bookwyrm/templates/settings/edit_server.html:48
#: bookwyrm/templates/settings/federated_server.html:23
#: bookwyrm/templates/user_admin/user_info.html:26
#: bookwyrm/templates/user_admin/user_info.html:117
msgid "Software:"
msgstr "Logiciel:"
#: bookwyrm/templates/settings/edit_server.html:55
#: bookwyrm/templates/settings/federated_server.html:27
#: bookwyrm/templates/user_admin/user_info.html:30
#: bookwyrm/templates/user_admin/user_info.html:121
msgid "Version:"
msgstr "Description:"
@ -2194,6 +2227,7 @@ msgid "View all"
msgstr "Voir tous"
#: bookwyrm/templates/settings/federated_server.html:50
#: bookwyrm/templates/user_admin/user_info.html:59
msgid "Reports:"
msgstr "Signalements:"
@ -2210,7 +2244,7 @@ msgid "Blocked by us:"
msgstr "Bloqués par nous:"
#: bookwyrm/templates/settings/federated_server.html:82
#: bookwyrm/templates/user_admin/user_info.html:39
#: bookwyrm/templates/user_admin/user_info.html:130
msgid "Notes"
msgstr "Remarques"
@ -3159,7 +3193,7 @@ msgstr[1] "%(mutuals_display)s abonné(e)s que vous suivez"
msgid "No followers you follow"
msgstr "compte que vous suivez"
#: bookwyrm/templates/user_admin/user.html:9
#: bookwyrm/templates/user_admin/user.html:8
msgid "Back to users"
msgstr "Retour aux comptes"
@ -3186,31 +3220,85 @@ msgid "Remote instance"
msgstr "Instance distante"
#: bookwyrm/templates/user_admin/user_admin.html:47
#: bookwyrm/templates/user_admin/user_info.html:24
msgid "Active"
msgstr "Actif"
#: bookwyrm/templates/user_admin/user_admin.html:47
#: bookwyrm/templates/user_admin/user_info.html:28
msgid "Inactive"
msgstr "Inactif"
#: bookwyrm/templates/user_admin/user_admin.html:52
#: bookwyrm/templates/user_admin/user_info.html:49
#: bookwyrm/templates/user_admin/user_info.html:140
msgid "Not set"
msgstr "Non défini"
#: bookwyrm/templates/user_admin/user_info.html:5
msgid "User details"
msgstr "Détails du compte"
#: bookwyrm/templates/user_admin/user_info.html:14
#: bookwyrm/templates/user_admin/user_info.html:16
msgid "View user profile"
msgstr "Voir le profil"
#: bookwyrm/templates/user_admin/user_info.html:20
#: bookwyrm/templates/user_admin/user_info.html:36
msgid "Local"
msgstr "Local"
#: bookwyrm/templates/user_admin/user_info.html:38
#, fuzzy
#| msgid "Remove"
msgid "Remote"
msgstr "Retirer"
#: bookwyrm/templates/user_admin/user_info.html:47
msgid "User details"
msgstr "Détails du compte"
#: bookwyrm/templates/user_admin/user_info.html:52
#, fuzzy
#| msgid "Email"
msgid "Email:"
msgstr "Email"
#: bookwyrm/templates/user_admin/user_info.html:64
#, fuzzy
#| msgid "Directory"
msgid "(View reports)"
msgstr "Répertoire"
#: bookwyrm/templates/user_admin/user_info.html:72
#, fuzzy
#| msgid "Blocked by us:"
msgid "Blocked by count:"
msgstr "Bloqués par nous:"
#: bookwyrm/templates/user_admin/user_info.html:77
#, fuzzy
#| msgid "last active"
msgid "Last active date:"
msgstr "dernière activité"
#: bookwyrm/templates/user_admin/user_info.html:82
#, fuzzy
#| msgid "Manually approve followers:"
msgid "Manually approved followers:"
msgstr "Autoriser les abonnements manuellement:"
#: bookwyrm/templates/user_admin/user_info.html:87
#, fuzzy
#| msgid "Discard"
msgid "Discoverable:"
msgstr "Rejeter"
#: bookwyrm/templates/user_admin/user_info.html:93
#, fuzzy
#| msgid "Deactivate user"
msgid "Deactivation reason:"
msgstr "Désactiver le compte"
#: bookwyrm/templates/user_admin/user_info.html:111
msgid "Instance details"
msgstr "Détails de linstance"
#: bookwyrm/templates/user_admin/user_info.html:46
#: bookwyrm/templates/user_admin/user_info.html:137
msgid "View instance"
msgstr "Voir linstance"
@ -3239,14 +3327,14 @@ msgstr "Ce fichier dépasse la taille limite: 10Mo"
msgid "%(title)s: %(subtitle)s"
msgstr "%(title)s (%(subtitle)s)"
#: bookwyrm/views/authentication.py:69
msgid "Username or password are incorrect"
msgstr ""
#: bookwyrm/views/import_data.py:67
msgid "Not a valid csv file"
msgstr "Fichier CSV non valide"
#: bookwyrm/views/login.py:70
msgid "Username or password are incorrect"
msgstr ""
#: bookwyrm/views/password.py:32
msgid "No user with that email address was found."
msgstr "Aucun compte avec cette adresse email na été trouvé."
@ -3273,9 +3361,6 @@ msgstr ""
#~ msgid "Federated Timeline"
#~ msgstr "Fil dactualité des instances fédérées"
#~ msgid "Local"
#~ msgstr "Local"
#, fuzzy
#~| msgid "BookWyrm users"
#~ msgid "BookWyrm\\"
@ -3331,11 +3416,6 @@ msgstr ""
#~ msgid "Value %(value)r is not a valid choice."
#~ msgstr "%(value)s nest pas une remote_id valide."
#, fuzzy
#~| msgid "This shelf is empty."
#~ msgid "This field cannot be null."
#~ msgstr "Cette étagère est vide"
#, fuzzy
#~| msgid "A user with this email already exists."
#~ msgid "%(model_name)s with this %(field_label)s already exists."
@ -3555,9 +3635,6 @@ msgstr ""
#~ msgid "by %(author)s"
#~ msgstr "par %(author)s"
#~ msgid "Deactivate user"
#~ msgstr "Désactiver le compte"
#~ msgid "Reactivate user"
#~ msgstr "Réactiver le compte"

Binary file not shown.

View file

@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: 0.1.1\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-09-05 23:09+0000\n"
"POT-Creation-Date: 2021-09-08 15:57+0000\n"
"PO-Revision-Date: 2021-03-20 00:56+0000\n"
"Last-Translator: Kana <gudzpoz@live.com>\n"
"Language-Team: Mouse Reeve <LL@li.org>\n"
@ -92,45 +92,45 @@ msgstr "用户名"
msgid "A user with that username already exists."
msgstr "已经存在使用该用户名的用户。"
#: bookwyrm/settings.py:124
#: bookwyrm/settings.py:113
msgid "Home Timeline"
msgstr "主页时间线"
#: bookwyrm/settings.py:124
#: bookwyrm/settings.py:113
msgid "Home"
msgstr "主页"
#: bookwyrm/settings.py:125
#: bookwyrm/settings.py:114
msgid "Books Timeline"
msgstr "书目时间线"
#: bookwyrm/settings.py:125 bookwyrm/templates/search/layout.html:21
#: bookwyrm/settings.py:114 bookwyrm/templates/search/layout.html:21
#: bookwyrm/templates/search/layout.html:42
#: bookwyrm/templates/user/layout.html:81
msgid "Books"
msgstr "书目"
#: bookwyrm/settings.py:171
#: bookwyrm/settings.py:160
msgid "English"
msgstr "English英语"
#: bookwyrm/settings.py:172
#: bookwyrm/settings.py:161
msgid "German"
msgstr "Deutsch德语"
#: bookwyrm/settings.py:173
#: bookwyrm/settings.py:162
msgid "Spanish"
msgstr "Español西班牙语"
#: bookwyrm/settings.py:174
#: bookwyrm/settings.py:163
msgid "French"
msgstr "Français法语"
#: bookwyrm/settings.py:175
#: bookwyrm/settings.py:164
msgid "Simplified Chinese"
msgstr "简体中文"
#: bookwyrm/settings.py:176
#: bookwyrm/settings.py:165
msgid "Traditional Chinese"
msgstr "繁體中文(繁体中文)"
@ -279,7 +279,7 @@ msgstr "Goodreads key:"
#: bookwyrm/templates/book/edit_book.html:328
#: bookwyrm/templates/book/readthrough.html:76
#: bookwyrm/templates/lists/bookmark_button.html:15
#: bookwyrm/templates/lists/form.html:42
#: bookwyrm/templates/lists/form.html:44
#: bookwyrm/templates/preferences/edit_user.html:78
#: bookwyrm/templates/settings/announcement_form.html:69
#: bookwyrm/templates/settings/edit_server.html:68
@ -296,6 +296,7 @@ msgstr "保存"
#: bookwyrm/templates/book/cover_modal.html:32
#: bookwyrm/templates/book/edit_book.html:329
#: bookwyrm/templates/book/readthrough.html:77
#: bookwyrm/templates/lists/delete_list_modal.html:17
#: bookwyrm/templates/moderation/report_modal.html:34
#: bookwyrm/templates/settings/federated_server.html:99
#: bookwyrm/templates/snippets/delete_readthrough_modal.html:17
@ -673,6 +674,7 @@ msgstr "搜索结果"
#: bookwyrm/templates/components/inline_form.html:8
#: bookwyrm/templates/components/modal.html:11
#: bookwyrm/templates/components/tooltip.html:7
#: bookwyrm/templates/feed/layout.html:71
#: bookwyrm/templates/get_started/layout.html:20
#: bookwyrm/templates/get_started/layout.html:53
@ -681,6 +683,10 @@ msgstr "搜索结果"
msgid "Close"
msgstr "关闭"
#: bookwyrm/templates/components/tooltip.html:3
msgid "Help"
msgstr ""
#: bookwyrm/templates/compose.html:5 bookwyrm/templates/compose.html:8
msgid "Compose status"
msgstr "撰写状态"
@ -702,6 +708,7 @@ msgid "Sorry! We couldn't find that code."
msgstr "抱歉!我们无法找到该代码。"
#: bookwyrm/templates/confirm_email/confirm_email.html:19
#: bookwyrm/templates/user_admin/user_info.html:100
msgid "Confirmation code:"
msgstr "确认代码:"
@ -1169,119 +1176,124 @@ msgstr "你 %(year)s 的书目"
msgid "%(username)s's %(year)s Books"
msgstr "%(username)s 在 %(year)s 的书目"
#: bookwyrm/templates/import.html:5 bookwyrm/templates/import.html:9
#: bookwyrm/templates/import/import.html:5
#: bookwyrm/templates/import/import.html:9
#: bookwyrm/templates/user/shelf/shelf.html:57
msgid "Import Books"
msgstr "导入书目"
#: bookwyrm/templates/import.html:16
#: bookwyrm/templates/import/import.html:18
msgid "Data source:"
msgstr "数据来源:"
#: bookwyrm/templates/import.html:32
#: bookwyrm/templates/import/import.html:37
msgid "Data file:"
msgstr "数据文件:"
#: bookwyrm/templates/import.html:40
#: bookwyrm/templates/import/import.html:45
msgid "Include reviews"
msgstr "纳入书评"
#: bookwyrm/templates/import.html:45
#: bookwyrm/templates/import/import.html:50
msgid "Privacy setting for imported reviews:"
msgstr "导入书评的隐私设定"
#: bookwyrm/templates/import.html:51
#: bookwyrm/templates/import/import.html:56
#: bookwyrm/templates/settings/server_blocklist.html:64
msgid "Import"
msgstr "导入"
#: bookwyrm/templates/import.html:56
#: bookwyrm/templates/import/import.html:61
msgid "Recent Imports"
msgstr "最近的导入"
#: bookwyrm/templates/import.html:58
#: bookwyrm/templates/import/import.html:63
msgid "No recent imports"
msgstr "无最近的导入"
#: bookwyrm/templates/import_status.html:6
#: bookwyrm/templates/import_status.html:10
#: bookwyrm/templates/import/import_status.html:6
#: bookwyrm/templates/import/import_status.html:10
msgid "Import Status"
msgstr "导入状态"
#: bookwyrm/templates/import_status.html:11
#: bookwyrm/templates/import/import_status.html:11
msgid "Back to imports"
msgstr "回到导入"
#: bookwyrm/templates/import_status.html:15
#: bookwyrm/templates/import/import_status.html:15
msgid "Import started:"
msgstr "导入开始:"
#: bookwyrm/templates/import_status.html:20
#: bookwyrm/templates/import/import_status.html:20
msgid "Import completed:"
msgstr "导入完成:"
#: bookwyrm/templates/import_status.html:24
#: bookwyrm/templates/import/import_status.html:24
msgid "TASK FAILED"
msgstr "任务失败"
#: bookwyrm/templates/import_status.html:32
#: bookwyrm/templates/import/import_status.html:32
msgid "Import still in progress."
msgstr "还在导入中。"
#: bookwyrm/templates/import_status.html:34
#: bookwyrm/templates/import/import_status.html:34
msgid "(Hit reload to update!)"
msgstr "(按下重新加载来更新!)"
#: bookwyrm/templates/import_status.html:41
#: bookwyrm/templates/import/import_status.html:41
msgid "Failed to load"
msgstr "加载失败"
#: bookwyrm/templates/import_status.html:50
#: bookwyrm/templates/import/import_status.html:50
#, 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:62
#: bookwyrm/templates/import/import_status.html:62
#, python-format
msgid "Line %(index)s: <strong>%(title)s</strong> by %(author)s"
msgstr "第 %(index)s 行: %(author)s 所著的 <strong>%(title)s</strong>"
#: bookwyrm/templates/import_status.html:82
#: bookwyrm/templates/import/import_status.html:82
msgid "Select all"
msgstr "全选"
#: bookwyrm/templates/import_status.html:85
#: bookwyrm/templates/import/import_status.html:85
msgid "Retry items"
msgstr "重试项目"
#: bookwyrm/templates/import_status.html:112
#: bookwyrm/templates/import/import_status.html:112
msgid "Successfully imported"
msgstr "成功导入了"
#: bookwyrm/templates/import_status.html:114
#: bookwyrm/templates/import/import_status.html:114
msgid "Import Progress"
msgstr "导入进度"
#: bookwyrm/templates/import_status.html:119
#: bookwyrm/templates/import/import_status.html:119
msgid "Book"
msgstr "书目"
#: bookwyrm/templates/import_status.html:122
#: bookwyrm/templates/import/import_status.html:122
#: bookwyrm/templates/user/shelf/shelf.html:111
#: bookwyrm/templates/user/shelf/shelf.html:131
msgid "Title"
msgstr "标题"
#: bookwyrm/templates/import_status.html:125
#: bookwyrm/templates/import/import_status.html:125
#: bookwyrm/templates/user/shelf/shelf.html:112
#: bookwyrm/templates/user/shelf/shelf.html:134
msgid "Author"
msgstr "作者"
#: bookwyrm/templates/import_status.html:148
#: bookwyrm/templates/import/import_status.html:148
msgid "Imported"
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."
msgstr ""
#: bookwyrm/templates/invite.html:4 bookwyrm/templates/invite.html:12
#: bookwyrm/templates/login.html:51
msgid "Create an Account"
@ -1477,8 +1489,25 @@ msgstr "批准"
msgid "Discard"
msgstr "削除"
#: bookwyrm/templates/lists/delete_list_modal.html:4
#, fuzzy
#| msgid "Delete this progress update"
msgid "Delete this list?"
msgstr "删除此进度更新"
#: bookwyrm/templates/lists/delete_list_modal.html:7
msgid "This action cannot be un-done"
msgstr ""
#: bookwyrm/templates/lists/delete_list_modal.html:15
#: bookwyrm/templates/settings/announcement.html:20
#: bookwyrm/templates/snippets/delete_readthrough_modal.html:15
#: bookwyrm/templates/snippets/follow_request_buttons.html:12
msgid "Delete"
msgstr "删除"
#: bookwyrm/templates/lists/edit_form.html:5
#: bookwyrm/templates/lists/list_layout.html:16
#: bookwyrm/templates/lists/layout.html:16
msgid "Edit List"
msgstr "编辑列表"
@ -1513,6 +1542,12 @@ msgstr "开放"
msgid "Anyone can add books to this list"
msgstr "任何人都可以向此列表中添加书目"
#: bookwyrm/templates/lists/form.html:49
#, fuzzy
#| msgid "Delete status"
msgid "Delete list"
msgstr "删除发文"
#: bookwyrm/templates/lists/list.html:20
msgid "You successfully suggested a book for this list!"
msgstr "你成功向该列表推荐了一本书!"
@ -1909,6 +1944,7 @@ msgid "Account"
msgstr "帐号"
#: bookwyrm/templates/preferences/layout.html:15
#: bookwyrm/templates/user_admin/user_info.html:7
msgid "Profile"
msgstr "个人资料"
@ -2029,12 +2065,6 @@ msgstr "回到列表"
msgid "Edit Announcement"
msgstr "编辑公告"
#: bookwyrm/templates/settings/announcement.html:20
#: bookwyrm/templates/snippets/delete_readthrough_modal.html:15
#: bookwyrm/templates/snippets/follow_request_buttons.html:12
msgid "Delete"
msgstr "删除"
#: bookwyrm/templates/settings/announcement.html:35
msgid "Visible:"
msgstr "可见:"
@ -2085,6 +2115,7 @@ msgstr "结束日期"
#: bookwyrm/templates/settings/manage_invite_requests.html:44
#: bookwyrm/templates/settings/status_filter.html:5
#: bookwyrm/templates/user_admin/user_admin.html:34
#: bookwyrm/templates/user_admin/user_info.html:20
msgid "Status"
msgstr "状态"
@ -2122,7 +2153,7 @@ msgstr "实例:"
#: bookwyrm/templates/settings/edit_server.html:37
#: bookwyrm/templates/settings/federated_server.html:31
#: bookwyrm/templates/user_admin/user_info.html:34
#: bookwyrm/templates/user_admin/user_info.html:125
msgid "Status:"
msgstr "状态:"
@ -2137,13 +2168,13 @@ msgstr "已屏蔽"
#: bookwyrm/templates/settings/edit_server.html:48
#: bookwyrm/templates/settings/federated_server.html:23
#: bookwyrm/templates/user_admin/user_info.html:26
#: bookwyrm/templates/user_admin/user_info.html:117
msgid "Software:"
msgstr "软件:"
#: bookwyrm/templates/settings/edit_server.html:55
#: bookwyrm/templates/settings/federated_server.html:27
#: bookwyrm/templates/user_admin/user_info.html:30
#: bookwyrm/templates/user_admin/user_info.html:121
msgid "Version:"
msgstr "版本:"
@ -2170,6 +2201,7 @@ msgid "View all"
msgstr "查看全部"
#: bookwyrm/templates/settings/federated_server.html:50
#: bookwyrm/templates/user_admin/user_info.html:59
msgid "Reports:"
msgstr "报告:"
@ -2186,7 +2218,7 @@ msgid "Blocked by us:"
msgstr "我们所屏蔽的:"
#: bookwyrm/templates/settings/federated_server.html:82
#: bookwyrm/templates/user_admin/user_info.html:39
#: bookwyrm/templates/user_admin/user_info.html:130
msgid "Notes"
msgstr "备注"
@ -3106,7 +3138,7 @@ msgstr[0] "%(mutuals_display)s 个你也关注的关注者"
msgid "No followers you follow"
msgstr "没有你关注的关注者"
#: bookwyrm/templates/user_admin/user.html:9
#: bookwyrm/templates/user_admin/user.html:8
msgid "Back to users"
msgstr "回到用户"
@ -3133,31 +3165,83 @@ msgid "Remote instance"
msgstr "移除服务器"
#: bookwyrm/templates/user_admin/user_admin.html:47
#: bookwyrm/templates/user_admin/user_info.html:24
msgid "Active"
msgstr "活跃"
#: bookwyrm/templates/user_admin/user_admin.html:47
#: bookwyrm/templates/user_admin/user_info.html:28
msgid "Inactive"
msgstr "停用"
#: bookwyrm/templates/user_admin/user_admin.html:52
#: bookwyrm/templates/user_admin/user_info.html:49
#: bookwyrm/templates/user_admin/user_info.html:140
msgid "Not set"
msgstr "未设置"
#: bookwyrm/templates/user_admin/user_info.html:5
msgid "User details"
msgstr "用户详情"
#: bookwyrm/templates/user_admin/user_info.html:14
#: bookwyrm/templates/user_admin/user_info.html:16
msgid "View user profile"
msgstr "查看用户个人资料"
#: bookwyrm/templates/user_admin/user_info.html:20
#: bookwyrm/templates/user_admin/user_info.html:36
msgid "Local"
msgstr "本站"
#: bookwyrm/templates/user_admin/user_info.html:38
#, fuzzy
#| msgid "Remove"
msgid "Remote"
msgstr "移除"
#: bookwyrm/templates/user_admin/user_info.html:47
msgid "User details"
msgstr "用户详情"
#: bookwyrm/templates/user_admin/user_info.html:52
#, fuzzy
#| msgid "Email"
msgid "Email:"
msgstr "邮箱"
#: bookwyrm/templates/user_admin/user_info.html:64
#, fuzzy
#| msgid "Directory"
msgid "(View reports)"
msgstr "目录"
#: bookwyrm/templates/user_admin/user_info.html:72
#, fuzzy
#| msgid "Blocked by us:"
msgid "Blocked by count:"
msgstr "我们所屏蔽的:"
#: bookwyrm/templates/user_admin/user_info.html:77
#, fuzzy
#| msgid "last active"
msgid "Last active date:"
msgstr "最后活跃"
#: bookwyrm/templates/user_admin/user_info.html:82
#, fuzzy
#| msgid "Manually approve followers:"
msgid "Manually approved followers:"
msgstr "手动批准关注者:"
#: bookwyrm/templates/user_admin/user_info.html:87
#, fuzzy
#| msgid "Discover"
msgid "Discoverable:"
msgstr "发现"
#: bookwyrm/templates/user_admin/user_info.html:93
msgid "Deactivation reason:"
msgstr ""
#: bookwyrm/templates/user_admin/user_info.html:111
msgid "Instance details"
msgstr "实例详情"
#: bookwyrm/templates/user_admin/user_info.html:46
#: bookwyrm/templates/user_admin/user_info.html:137
msgid "View instance"
msgstr "查看实例"
@ -3186,14 +3270,14 @@ msgstr "文件超过了最大大小: 10MB"
msgid "%(title)s: %(subtitle)s"
msgstr "%(title)s%(subtitle)s"
#: bookwyrm/views/authentication.py:69
msgid "Username or password are incorrect"
msgstr "用户名或密码不正确"
#: bookwyrm/views/import_data.py:67
msgid "Not a valid csv file"
msgstr "不是有效的 csv 文件"
#: bookwyrm/views/login.py:70
msgid "Username or password are incorrect"
msgstr "用户名或密码不正确"
#: bookwyrm/views/password.py:32
msgid "No user with that email address was found."
msgstr "没有找到使用该邮箱的用户。"
@ -3219,6 +3303,3 @@ msgstr ""
#~ msgid "Federated Timeline"
#~ msgstr "跨站时间线"
#~ msgid "Local"
#~ msgstr "本站"

Binary file not shown.

View file

@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: 0.0.1\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-09-05 23:09+0000\n"
"POT-Creation-Date: 2021-09-08 15:57+0000\n"
"PO-Revision-Date: 2021-06-30 10:36+0000\n"
"Last-Translator: Grace Cheng <chengracecwy@gmail.com>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@ -92,47 +92,47 @@ msgstr "使用者名稱"
msgid "A user with that username already exists."
msgstr "已經存在使用該名稱的使用者。"
#: bookwyrm/settings.py:124
#: bookwyrm/settings.py:113
msgid "Home Timeline"
msgstr "主頁時間線"
#: bookwyrm/settings.py:124
#: bookwyrm/settings.py:113
msgid "Home"
msgstr "主頁"
#: bookwyrm/settings.py:125
#: bookwyrm/settings.py:114
#, fuzzy
#| msgid "Book Title"
msgid "Books Timeline"
msgstr "書名"
#: bookwyrm/settings.py:125 bookwyrm/templates/search/layout.html:21
#: bookwyrm/settings.py:114 bookwyrm/templates/search/layout.html:21
#: bookwyrm/templates/search/layout.html:42
#: bookwyrm/templates/user/layout.html:81
msgid "Books"
msgstr "書目"
#: bookwyrm/settings.py:171
#: bookwyrm/settings.py:160
msgid "English"
msgstr "English英語"
#: bookwyrm/settings.py:172
#: bookwyrm/settings.py:161
msgid "German"
msgstr "Deutsch德語"
#: bookwyrm/settings.py:173
#: bookwyrm/settings.py:162
msgid "Spanish"
msgstr "Español西班牙語"
#: bookwyrm/settings.py:174
#: bookwyrm/settings.py:163
msgid "French"
msgstr "Français法語"
#: bookwyrm/settings.py:175
#: bookwyrm/settings.py:164
msgid "Simplified Chinese"
msgstr "簡體中文"
#: bookwyrm/settings.py:176
#: bookwyrm/settings.py:165
#, fuzzy
#| msgid "Tranditional Chinese"
msgid "Traditional Chinese"
@ -285,7 +285,7 @@ msgstr "Goodreads key:"
#: bookwyrm/templates/book/edit_book.html:328
#: bookwyrm/templates/book/readthrough.html:76
#: bookwyrm/templates/lists/bookmark_button.html:15
#: bookwyrm/templates/lists/form.html:42
#: bookwyrm/templates/lists/form.html:44
#: bookwyrm/templates/preferences/edit_user.html:78
#: bookwyrm/templates/settings/announcement_form.html:69
#: bookwyrm/templates/settings/edit_server.html:68
@ -302,6 +302,7 @@ msgstr "儲存"
#: bookwyrm/templates/book/cover_modal.html:32
#: bookwyrm/templates/book/edit_book.html:329
#: bookwyrm/templates/book/readthrough.html:77
#: bookwyrm/templates/lists/delete_list_modal.html:17
#: bookwyrm/templates/moderation/report_modal.html:34
#: bookwyrm/templates/settings/federated_server.html:99
#: bookwyrm/templates/snippets/delete_readthrough_modal.html:17
@ -681,6 +682,7 @@ msgstr "搜尋結果"
#: bookwyrm/templates/components/inline_form.html:8
#: bookwyrm/templates/components/modal.html:11
#: bookwyrm/templates/components/tooltip.html:7
#: bookwyrm/templates/feed/layout.html:71
#: bookwyrm/templates/get_started/layout.html:20
#: bookwyrm/templates/get_started/layout.html:53
@ -689,6 +691,10 @@ msgstr "搜尋結果"
msgid "Close"
msgstr "關閉"
#: bookwyrm/templates/components/tooltip.html:3
msgid "Help"
msgstr ""
#: bookwyrm/templates/compose.html:5 bookwyrm/templates/compose.html:8
msgid "Compose status"
msgstr "撰寫狀態"
@ -714,6 +720,7 @@ msgid "Sorry! We couldn't find that code."
msgstr ""
#: bookwyrm/templates/confirm_email/confirm_email.html:19
#: bookwyrm/templates/user_admin/user_info.html:100
#, fuzzy
#| msgid "Confirm password:"
msgid "Confirmation code:"
@ -1194,123 +1201,128 @@ msgstr "你 %(year)s 的書目"
msgid "%(username)s's %(year)s Books"
msgstr "%(username)s 在 %(year)s 的書目"
#: bookwyrm/templates/import.html:5 bookwyrm/templates/import.html:9
#: bookwyrm/templates/import/import.html:5
#: bookwyrm/templates/import/import.html:9
#: bookwyrm/templates/user/shelf/shelf.html:57
msgid "Import Books"
msgstr "匯入書目"
#: bookwyrm/templates/import.html:16
#: bookwyrm/templates/import/import.html:18
msgid "Data source:"
msgstr "資料來源:"
#: bookwyrm/templates/import.html:32
#: bookwyrm/templates/import/import.html:37
msgid "Data file:"
msgstr "資料檔案:"
#: bookwyrm/templates/import.html:40
#: bookwyrm/templates/import/import.html:45
msgid "Include reviews"
msgstr "納入書評"
#: bookwyrm/templates/import.html:45
#: bookwyrm/templates/import/import.html:50
msgid "Privacy setting for imported reviews:"
msgstr "匯入書評的隱私設定"
#: bookwyrm/templates/import.html:51
#: bookwyrm/templates/import/import.html:56
#: bookwyrm/templates/settings/server_blocklist.html:64
msgid "Import"
msgstr "匯入"
#: bookwyrm/templates/import.html:56
#: bookwyrm/templates/import/import.html:61
msgid "Recent Imports"
msgstr "最近的匯入"
#: bookwyrm/templates/import.html:58
#: bookwyrm/templates/import/import.html:63
msgid "No recent imports"
msgstr "無最近的匯入"
#: bookwyrm/templates/import_status.html:6
#: bookwyrm/templates/import_status.html:10
#: bookwyrm/templates/import/import_status.html:6
#: bookwyrm/templates/import/import_status.html:10
msgid "Import Status"
msgstr "匯入狀態"
#: bookwyrm/templates/import_status.html:11
#: bookwyrm/templates/import/import_status.html:11
#, fuzzy
#| msgid "Back to reports"
msgid "Back to imports"
msgstr "回到舉報"
#: bookwyrm/templates/import_status.html:15
#: bookwyrm/templates/import/import_status.html:15
msgid "Import started:"
msgstr "匯入開始:"
#: bookwyrm/templates/import_status.html:20
#: bookwyrm/templates/import/import_status.html:20
msgid "Import completed:"
msgstr "匯入完成:"
#: bookwyrm/templates/import_status.html:24
#: bookwyrm/templates/import/import_status.html:24
msgid "TASK FAILED"
msgstr "任務失敗"
#: bookwyrm/templates/import_status.html:32
#: bookwyrm/templates/import/import_status.html:32
msgid "Import still in progress."
msgstr "還在匯入中。"
#: bookwyrm/templates/import_status.html:34
#: bookwyrm/templates/import/import_status.html:34
msgid "(Hit reload to update!)"
msgstr "(按下重新載入來更新!)"
#: bookwyrm/templates/import_status.html:41
#: bookwyrm/templates/import/import_status.html:41
msgid "Failed to load"
msgstr "載入失敗"
#: bookwyrm/templates/import_status.html:50
#: bookwyrm/templates/import/import_status.html:50
#, 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:62
#: bookwyrm/templates/import/import_status.html:62
#, python-format
msgid "Line %(index)s: <strong>%(title)s</strong> by %(author)s"
msgstr ""
#: bookwyrm/templates/import_status.html:82
#: bookwyrm/templates/import/import_status.html:82
msgid "Select all"
msgstr "全選"
#: bookwyrm/templates/import_status.html:85
#: bookwyrm/templates/import/import_status.html:85
msgid "Retry items"
msgstr "重試項目"
#: bookwyrm/templates/import_status.html:112
#: bookwyrm/templates/import/import_status.html:112
msgid "Successfully imported"
msgstr "成功匯入了"
#: bookwyrm/templates/import_status.html:114
#: bookwyrm/templates/import/import_status.html:114
#, fuzzy
#| msgid "Import still in progress."
msgid "Import Progress"
msgstr "還在匯入中。"
#: bookwyrm/templates/import_status.html:119
#: bookwyrm/templates/import/import_status.html:119
msgid "Book"
msgstr "書目"
#: bookwyrm/templates/import_status.html:122
#: bookwyrm/templates/import/import_status.html:122
#: bookwyrm/templates/user/shelf/shelf.html:111
#: bookwyrm/templates/user/shelf/shelf.html:131
msgid "Title"
msgstr "標題"
#: bookwyrm/templates/import_status.html:125
#: bookwyrm/templates/import/import_status.html:125
#: bookwyrm/templates/user/shelf/shelf.html:112
#: bookwyrm/templates/user/shelf/shelf.html:134
msgid "Author"
msgstr "作者"
#: bookwyrm/templates/import_status.html:148
#: bookwyrm/templates/import/import_status.html:148
msgid "Imported"
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."
msgstr ""
#: bookwyrm/templates/invite.html:4 bookwyrm/templates/invite.html:12
#: bookwyrm/templates/login.html:51
msgid "Create an Account"
@ -1506,8 +1518,25 @@ msgstr "批准"
msgid "Discard"
msgstr "放棄"
#: bookwyrm/templates/lists/delete_list_modal.html:4
#, fuzzy
#| msgid "Delete this progress update"
msgid "Delete this list?"
msgstr "刪除此進度更新"
#: bookwyrm/templates/lists/delete_list_modal.html:7
msgid "This action cannot be un-done"
msgstr ""
#: bookwyrm/templates/lists/delete_list_modal.html:15
#: bookwyrm/templates/settings/announcement.html:20
#: bookwyrm/templates/snippets/delete_readthrough_modal.html:15
#: bookwyrm/templates/snippets/follow_request_buttons.html:12
msgid "Delete"
msgstr "刪除"
#: bookwyrm/templates/lists/edit_form.html:5
#: bookwyrm/templates/lists/list_layout.html:16
#: bookwyrm/templates/lists/layout.html:16
msgid "Edit List"
msgstr "編輯列表"
@ -1542,6 +1571,12 @@ msgstr "開放"
msgid "Anyone can add books to this list"
msgstr "任何人都可以向此列表新增書目"
#: bookwyrm/templates/lists/form.html:49
#, fuzzy
#| msgid "Delete status"
msgid "Delete list"
msgstr "刪除狀態"
#: bookwyrm/templates/lists/list.html:20
msgid "You successfully suggested a book for this list!"
msgstr "你成功!向該列表推薦了一本書"
@ -1941,6 +1976,7 @@ msgid "Account"
msgstr "帳號"
#: bookwyrm/templates/preferences/layout.html:15
#: bookwyrm/templates/user_admin/user_info.html:7
msgid "Profile"
msgstr "使用者資料"
@ -2064,12 +2100,6 @@ msgstr "回到列表"
msgid "Edit Announcement"
msgstr "編輯公告"
#: bookwyrm/templates/settings/announcement.html:20
#: bookwyrm/templates/snippets/delete_readthrough_modal.html:15
#: bookwyrm/templates/snippets/follow_request_buttons.html:12
msgid "Delete"
msgstr "刪除"
#: bookwyrm/templates/settings/announcement.html:35
msgid "Visible:"
msgstr "可見:"
@ -2120,6 +2150,7 @@ msgstr "結束日期"
#: bookwyrm/templates/settings/manage_invite_requests.html:44
#: bookwyrm/templates/settings/status_filter.html:5
#: bookwyrm/templates/user_admin/user_admin.html:34
#: bookwyrm/templates/user_admin/user_info.html:20
msgid "Status"
msgstr "狀態"
@ -2157,7 +2188,7 @@ msgstr "實例:"
#: bookwyrm/templates/settings/edit_server.html:37
#: bookwyrm/templates/settings/federated_server.html:31
#: bookwyrm/templates/user_admin/user_info.html:34
#: bookwyrm/templates/user_admin/user_info.html:125
msgid "Status:"
msgstr "狀態:"
@ -2172,13 +2203,13 @@ msgstr "已封鎖"
#: bookwyrm/templates/settings/edit_server.html:48
#: bookwyrm/templates/settings/federated_server.html:23
#: bookwyrm/templates/user_admin/user_info.html:26
#: bookwyrm/templates/user_admin/user_info.html:117
msgid "Software:"
msgstr "軟件:"
#: bookwyrm/templates/settings/edit_server.html:55
#: bookwyrm/templates/settings/federated_server.html:27
#: bookwyrm/templates/user_admin/user_info.html:30
#: bookwyrm/templates/user_admin/user_info.html:121
msgid "Version:"
msgstr "版本:"
@ -2205,6 +2236,7 @@ msgid "View all"
msgstr "檢視全部"
#: bookwyrm/templates/settings/federated_server.html:50
#: bookwyrm/templates/user_admin/user_info.html:59
msgid "Reports:"
msgstr "舉報:"
@ -2221,7 +2253,7 @@ msgid "Blocked by us:"
msgstr "我們所封鎖的:"
#: bookwyrm/templates/settings/federated_server.html:82
#: bookwyrm/templates/user_admin/user_info.html:39
#: bookwyrm/templates/user_admin/user_info.html:130
msgid "Notes"
msgstr "備註"
@ -3164,7 +3196,7 @@ msgstr[0] "%(mutuals_display)s 個你也關注的關注者"
msgid "No followers you follow"
msgstr "你關注的關注者"
#: bookwyrm/templates/user_admin/user.html:9
#: bookwyrm/templates/user_admin/user.html:8
msgid "Back to users"
msgstr "回到使用者"
@ -3191,31 +3223,83 @@ msgid "Remote instance"
msgstr "移除伺服器"
#: bookwyrm/templates/user_admin/user_admin.html:47
#: bookwyrm/templates/user_admin/user_info.html:24
msgid "Active"
msgstr "活躍"
#: bookwyrm/templates/user_admin/user_admin.html:47
#: bookwyrm/templates/user_admin/user_info.html:28
msgid "Inactive"
msgstr "停用"
#: bookwyrm/templates/user_admin/user_admin.html:52
#: bookwyrm/templates/user_admin/user_info.html:49
#: bookwyrm/templates/user_admin/user_info.html:140
msgid "Not set"
msgstr "未設定"
#: bookwyrm/templates/user_admin/user_info.html:5
msgid "User details"
msgstr "使用者詳情"
#: bookwyrm/templates/user_admin/user_info.html:14
#: bookwyrm/templates/user_admin/user_info.html:16
msgid "View user profile"
msgstr "檢視使用者資料"
#: bookwyrm/templates/user_admin/user_info.html:20
#: bookwyrm/templates/user_admin/user_info.html:36
msgid "Local"
msgstr "本站"
#: bookwyrm/templates/user_admin/user_info.html:38
#, fuzzy
#| msgid "Remove"
msgid "Remote"
msgstr "移除"
#: bookwyrm/templates/user_admin/user_info.html:47
msgid "User details"
msgstr "使用者詳情"
#: bookwyrm/templates/user_admin/user_info.html:52
#, fuzzy
#| msgid "Email"
msgid "Email:"
msgstr "郵箱"
#: bookwyrm/templates/user_admin/user_info.html:64
#, fuzzy
#| msgid "Directory"
msgid "(View reports)"
msgstr "目錄"
#: bookwyrm/templates/user_admin/user_info.html:72
#, fuzzy
#| msgid "Blocked by us:"
msgid "Blocked by count:"
msgstr "我們所封鎖的:"
#: bookwyrm/templates/user_admin/user_info.html:77
#, fuzzy
#| msgid "last active"
msgid "Last active date:"
msgstr "最後活躍"
#: bookwyrm/templates/user_admin/user_info.html:82
#, fuzzy
#| msgid "Manually approve followers:"
msgid "Manually approved followers:"
msgstr "手動批准關注者:"
#: bookwyrm/templates/user_admin/user_info.html:87
#, fuzzy
#| msgid "Discard"
msgid "Discoverable:"
msgstr "放棄"
#: bookwyrm/templates/user_admin/user_info.html:93
msgid "Deactivation reason:"
msgstr ""
#: bookwyrm/templates/user_admin/user_info.html:111
msgid "Instance details"
msgstr "實例詳情"
#: bookwyrm/templates/user_admin/user_info.html:46
#: bookwyrm/templates/user_admin/user_info.html:137
msgid "View instance"
msgstr "檢視實例"
@ -3244,14 +3328,14 @@ msgstr "檔案超過了最大大小: 10MB"
msgid "%(title)s: %(subtitle)s"
msgstr ""
#: bookwyrm/views/authentication.py:69
msgid "Username or password are incorrect"
msgstr ""
#: bookwyrm/views/import_data.py:67
msgid "Not a valid csv file"
msgstr "不是有效的 csv 檔案"
#: bookwyrm/views/login.py:70
msgid "Username or password are incorrect"
msgstr ""
#: bookwyrm/views/password.py:32
msgid "No user with that email address was found."
msgstr "沒有找到使用該郵箱的使用者。"
@ -3277,6 +3361,3 @@ msgstr ""
#~ msgid "Federated Timeline"
#~ msgstr "跨站時間線"
#~ msgid "Local"
#~ msgstr "本站"