Merge pull request #892 from bookwyrm-social/inventaire

Adds inventaire connector
This commit is contained in:
Mouse Reeve 2021-04-29 11:58:38 -07:00 committed by GitHub
commit 13c17fae1a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
42 changed files with 1177 additions and 265 deletions

View file

@ -7,6 +7,9 @@ DEBUG=true
DOMAIN=your.domain.here DOMAIN=your.domain.here
#EMAIL=your@email.here #EMAIL=your@email.here
# Used for deciding which editions to prefer
DEFAULT_LANGUAGE="English"
## Leave unset to allow all hosts ## Leave unset to allow all hosts
# ALLOWED_HOSTS="localhost,127.0.0.1,[::1]" # ALLOWED_HOSTS="localhost,127.0.0.1,[::1]"

View file

@ -7,6 +7,9 @@ DEBUG=false
DOMAIN=your.domain.here DOMAIN=your.domain.here
EMAIL=your@email.here EMAIL=your@email.here
# Used for deciding which editions to prefer
DEFAULT_LANGUAGE="English"
## Leave unset to allow all hosts ## Leave unset to allow all hosts
# ALLOWED_HOSTS="localhost,127.0.0.1,[::1]" # ALLOWED_HOSTS="localhost,127.0.0.1,[::1]"

View file

@ -7,11 +7,22 @@ from .image import Document
@dataclass(init=False) @dataclass(init=False)
class Book(ActivityObject): class BookData(ActivityObject):
"""shared fields for all book data and authors"""
openlibraryKey: str = None
inventaireId: str = None
librarythingKey: str = None
goodreadsKey: str = None
bnfId: str = None
lastEditedBy: str = None
@dataclass(init=False)
class Book(BookData):
"""serializes an edition or work, abstract""" """serializes an edition or work, abstract"""
title: str title: str
lastEditedBy: str = None
sortTitle: str = "" sortTitle: str = ""
subtitle: str = "" subtitle: str = ""
description: str = "" description: str = ""
@ -25,10 +36,6 @@ class Book(ActivityObject):
firstPublishedDate: str = "" firstPublishedDate: str = ""
publishedDate: str = "" publishedDate: str = ""
openlibraryKey: str = ""
librarythingKey: str = ""
goodreadsKey: str = ""
cover: Document = None cover: Document = None
type: str = "Book" type: str = "Book"
@ -55,23 +62,21 @@ class Work(Book):
"""work instance of a book object""" """work instance of a book object"""
lccn: str = "" lccn: str = ""
defaultEdition: str = ""
editions: List[str] = field(default_factory=lambda: []) editions: List[str] = field(default_factory=lambda: [])
type: str = "Work" type: str = "Work"
@dataclass(init=False) @dataclass(init=False)
class Author(ActivityObject): class Author(BookData):
"""author of a book""" """author of a book"""
name: str name: str
lastEditedBy: str = None isni: str = None
viafId: str = None
gutenbergId: str = None
born: str = None born: str = None
died: str = None died: str = None
aliases: List[str] = field(default_factory=lambda: []) aliases: List[str] = field(default_factory=lambda: [])
bio: str = "" bio: str = ""
openlibraryKey: str = ""
librarythingKey: str = ""
goodreadsKey: str = ""
wikipediaLink: str = "" wikipediaLink: str = ""
type: str = "Author" type: str = "Author"

View file

@ -44,7 +44,7 @@ class AbstractMinimalConnector(ABC):
if min_confidence: if min_confidence:
params["min_confidence"] = min_confidence params["min_confidence"] = min_confidence
data = get_data( data = self.get_search_data(
"%s%s" % (self.search_url, query), "%s%s" % (self.search_url, query),
params=params, params=params,
) )
@ -57,7 +57,7 @@ class AbstractMinimalConnector(ABC):
def isbn_search(self, query): def isbn_search(self, query):
"""isbn search""" """isbn search"""
params = {} params = {}
data = get_data( data = self.get_search_data(
"%s%s" % (self.isbn_search_url, query), "%s%s" % (self.isbn_search_url, query),
params=params, params=params,
) )
@ -68,6 +68,10 @@ class AbstractMinimalConnector(ABC):
results.append(self.format_isbn_search_result(doc)) results.append(self.format_isbn_search_result(doc))
return results return results
def get_search_data(self, remote_id, **kwargs): # pylint: disable=no-self-use
"""this allows connectors to override the default behavior"""
return get_data(remote_id, **kwargs)
@abstractmethod @abstractmethod
def get_or_create_book(self, remote_id): def get_or_create_book(self, remote_id):
"""pull up a book record by whatever means possible""" """pull up a book record by whatever means possible"""
@ -112,12 +116,12 @@ class AbstractConnector(AbstractMinimalConnector):
remote_id remote_id
) or models.Work.find_existing_by_remote_id(remote_id) ) or models.Work.find_existing_by_remote_id(remote_id)
if existing: if existing:
if hasattr(existing, "get_default_editon"): if hasattr(existing, "default_edition"):
return existing.get_default_editon() return existing.default_edition
return existing return existing
# load the json # load the json
data = get_data(remote_id) data = self.get_book_data(remote_id)
mapped_data = dict_from_mappings(data, self.book_mappings) mapped_data = dict_from_mappings(data, self.book_mappings)
if self.is_work_data(data): if self.is_work_data(data):
try: try:
@ -128,12 +132,12 @@ class AbstractConnector(AbstractMinimalConnector):
edition_data = data edition_data = data
work_data = mapped_data work_data = mapped_data
else: else:
edition_data = data
try: try:
work_data = self.get_work_from_edition_data(data) work_data = self.get_work_from_edition_data(data)
work_data = dict_from_mappings(work_data, self.book_mappings) work_data = dict_from_mappings(work_data, self.book_mappings)
except (KeyError, ConnectorException): except (KeyError, ConnectorException):
work_data = mapped_data work_data = mapped_data
edition_data = data
if not work_data or not edition_data: if not work_data or not edition_data:
raise ConnectorException("Unable to load book data: %s" % remote_id) raise ConnectorException("Unable to load book data: %s" % remote_id)
@ -150,6 +154,10 @@ class AbstractConnector(AbstractMinimalConnector):
load_more_data.delay(self.connector.id, work.id) load_more_data.delay(self.connector.id, work.id)
return edition return edition
def get_book_data(self, remote_id): # pylint: disable=no-self-use
"""this allows connectors to override the default behavior"""
return get_data(remote_id)
def create_edition_from_data(self, work, edition_data): def create_edition_from_data(self, work, edition_data):
"""if we already have the work, we're ready""" """if we already have the work, we're ready"""
mapped_data = dict_from_mappings(edition_data, self.book_mappings) mapped_data = dict_from_mappings(edition_data, self.book_mappings)
@ -159,10 +167,6 @@ class AbstractConnector(AbstractMinimalConnector):
edition.connector = self.connector edition.connector = self.connector
edition.save() edition.save()
if not work.default_edition:
work.default_edition = edition
work.save()
for author in self.get_authors_from_data(edition_data): for author in self.get_authors_from_data(edition_data):
edition.authors.add(author) edition.authors.add(author)
if not edition.authors.exists() and work.authors.exists(): if not edition.authors.exists() and work.authors.exists():
@ -176,7 +180,7 @@ class AbstractConnector(AbstractMinimalConnector):
if existing: if existing:
return existing return existing
data = get_data(remote_id) data = self.get_book_data(remote_id)
mapped_data = dict_from_mappings(data, self.author_mappings) mapped_data = dict_from_mappings(data, self.author_mappings)
try: try:
@ -273,6 +277,7 @@ class SearchResult:
title: str title: str
key: str key: str
connector: object connector: object
view_link: str = None
author: str = None author: str = None
year: str = None year: str = None
cover: str = None cover: str = None

View file

@ -6,12 +6,8 @@ from .abstract_connector import AbstractMinimalConnector, SearchResult
class Connector(AbstractMinimalConnector): class Connector(AbstractMinimalConnector):
"""this is basically just for search""" """this is basically just for search"""
def get_or_create_book(self, remote_id): def get_or_create_book(self, remote_id, work=None):
edition = activitypub.resolve_remote_id(remote_id, model=models.Edition) return activitypub.resolve_remote_id(remote_id, model=models.Edition)
work = edition.parent_work
work.default_edition = work.get_default_edition()
work.save()
return edition
def parse_search_data(self, data): def parse_search_data(self, data):
return data return data

View file

@ -0,0 +1,214 @@
""" inventaire data connector """
import re
from bookwyrm import models
from .abstract_connector import AbstractConnector, SearchResult, Mapping
from .abstract_connector import get_data
from .connector_manager import ConnectorException
class Connector(AbstractConnector):
"""instantiate a connector for OL"""
def __init__(self, identifier):
super().__init__(identifier)
get_first = lambda a: a[0]
shared_mappings = [
Mapping("id", remote_field="uri", formatter=self.get_remote_id),
Mapping("bnfId", remote_field="wdt:P268", formatter=get_first),
Mapping("openlibraryKey", remote_field="wdt:P648", formatter=get_first),
]
self.book_mappings = [
Mapping("title", remote_field="wdt:P1476", formatter=get_first),
Mapping("subtitle", remote_field="wdt:P1680", formatter=get_first),
Mapping("inventaireId", remote_field="uri"),
Mapping(
"description", remote_field="sitelinks", formatter=self.get_description
),
Mapping("cover", remote_field="image", formatter=self.get_cover_url),
Mapping("isbn13", remote_field="wdt:P212", formatter=get_first),
Mapping("isbn10", remote_field="wdt:P957", formatter=get_first),
Mapping("oclcNumber", remote_field="wdt:P5331", formatter=get_first),
Mapping("goodreadsKey", remote_field="wdt:P2969", formatter=get_first),
Mapping("librarythingKey", remote_field="wdt:P1085", formatter=get_first),
Mapping("languages", remote_field="wdt:P407", formatter=self.resolve_keys),
Mapping("publishers", remote_field="wdt:P123", formatter=self.resolve_keys),
Mapping("publishedDate", remote_field="wdt:P577", formatter=get_first),
Mapping("pages", remote_field="wdt:P1104", formatter=get_first),
Mapping(
"subjectPlaces", remote_field="wdt:P840", formatter=self.resolve_keys
),
Mapping("subjects", remote_field="wdt:P921", formatter=self.resolve_keys),
Mapping("asin", remote_field="wdt:P5749", formatter=get_first),
] + shared_mappings
# TODO: P136: genre, P674 characters, P950 bne
self.author_mappings = [
Mapping("id", remote_field="uri", formatter=self.get_remote_id),
Mapping("name", remote_field="labels", formatter=get_language_code),
Mapping("bio", remote_field="sitelinks", formatter=self.get_description),
Mapping("goodreadsKey", remote_field="wdt:P2963", formatter=get_first),
Mapping("isni", remote_field="wdt:P213", formatter=get_first),
Mapping("viafId", remote_field="wdt:P214", formatter=get_first),
Mapping("gutenberg_id", remote_field="wdt:P1938", formatter=get_first),
Mapping("born", remote_field="wdt:P569", formatter=get_first),
Mapping("died", remote_field="wdt:P570", formatter=get_first),
] + shared_mappings
def get_remote_id(self, value):
"""convert an id/uri into a url"""
return "{:s}?action=by-uris&uris={:s}".format(self.books_url, value)
def get_book_data(self, remote_id):
data = get_data(remote_id)
extracted = list(data.get("entities").values())
try:
data = extracted[0]
except KeyError:
raise ConnectorException("Invalid book data")
# flatten the data so that images, uri, and claims are on the same level
return {
**data.get("claims", {}),
**{k: data.get(k) for k in ["uri", "image", "labels", "sitelinks"]},
}
def parse_search_data(self, data):
return data.get("results")
def format_search_result(self, search_result):
images = search_result.get("image")
cover = (
"{:s}/img/entities/{:s}".format(self.covers_url, images[0])
if images
else None
)
return SearchResult(
title=search_result.get("label"),
key=self.get_remote_id(search_result.get("uri")),
author=search_result.get("description"),
view_link="{:s}/entity/{:s}".format(
self.base_url, search_result.get("uri")
),
cover=cover,
connector=self,
)
def parse_isbn_search_data(self, data):
"""got some daaaata"""
results = data.get("entities")
if not results:
return []
return list(results.values())
def format_isbn_search_result(self, search_result):
"""totally different format than a regular search result"""
title = search_result.get("claims", {}).get("wdt:P1476", [])
if not title:
return None
return SearchResult(
title=title[0],
key=self.get_remote_id(search_result.get("uri")),
author=search_result.get("description"),
view_link="{:s}/entity/{:s}".format(
self.base_url, search_result.get("uri")
),
cover=self.get_cover_url(search_result.get("image")),
connector=self,
)
def is_work_data(self, data):
return data.get("type") == "work"
def load_edition_data(self, work_uri):
"""get a list of editions for a work"""
url = "{:s}?action=reverse-claims&property=wdt:P629&value={:s}".format(
self.books_url, work_uri
)
return get_data(url)
def get_edition_from_work_data(self, data):
data = self.load_edition_data(data.get("uri"))
try:
uri = data["uris"][0]
except KeyError:
raise ConnectorException("Invalid book data")
return self.get_book_data(self.get_remote_id(uri))
def get_work_from_edition_data(self, data):
try:
uri = data["claims"]["wdt:P629"]
except KeyError:
raise ConnectorException("Invalid book data")
return self.get_book_data(self.get_remote_id(uri))
def get_authors_from_data(self, data):
authors = data.get("wdt:P50", [])
for author in authors:
yield self.get_or_create_author(self.get_remote_id(author))
def expand_book_data(self, book):
work = book
# go from the edition to the work, if necessary
if isinstance(book, models.Edition):
work = book.parent_work
try:
edition_options = self.load_edition_data(work.inventaire_id)
except ConnectorException:
# who knows, man
return
for edition_uri in edition_options.get("uris"):
remote_id = self.get_remote_id(edition_uri)
try:
data = self.get_book_data(remote_id)
except ConnectorException:
# who, indeed, knows
continue
self.create_edition_from_data(work, data)
def get_cover_url(self, cover_blob, *_):
"""format the relative cover url into an absolute one:
{"url": "/img/entities/e794783f01b9d4f897a1ea9820b96e00d346994f"}
"""
# covers may or may not be a list
if isinstance(cover_blob, list) and len(cover_blob) > 0:
cover_blob = cover_blob[0]
cover_id = cover_blob.get("url")
if not cover_id:
return None
# cover may or may not be an absolute url already
if re.match(r"^http", cover_id):
return cover_id
return "%s%s" % (self.covers_url, cover_id)
def resolve_keys(self, keys):
"""cool, it's "wd:Q3156592" now what the heck does that mean"""
results = []
for uri in keys:
try:
data = self.get_book_data(self.get_remote_id(uri))
except ConnectorException:
continue
results.append(get_language_code(data.get("labels")))
return results
def get_description(self, links):
"""grab an extracted excerpt from wikipedia"""
link = links.get("enwiki")
if not link:
return ""
url = "{:s}/api/data?action=wp-extract&lang=en&title={:s}".format(
self.base_url, link
)
try:
data = get_data(url)
except ConnectorException:
return ""
return data.get("extract")
def get_language_code(options, code="en"):
"""when there are a bunch of translation but we need a single field"""
return options.get(code)

View file

@ -14,8 +14,8 @@ class Connector(AbstractConnector):
def __init__(self, identifier): def __init__(self, identifier):
super().__init__(identifier) super().__init__(identifier)
get_first = lambda a: a[0] get_first = lambda a, *args: a[0]
get_remote_id = lambda a: self.base_url + a get_remote_id = lambda a, *args: self.base_url + a
self.book_mappings = [ self.book_mappings = [
Mapping("title"), Mapping("title"),
Mapping("id", remote_field="key", formatter=get_remote_id), Mapping("id", remote_field="key", formatter=get_remote_id),

View file

@ -3,7 +3,7 @@ from functools import reduce
import operator import operator
from django.contrib.postgres.search import SearchRank, SearchVector from django.contrib.postgres.search import SearchRank, SearchVector
from django.db.models import Count, F, Q from django.db.models import Count, OuterRef, Subquery, F, Q
from bookwyrm import models from bookwyrm import models
from .abstract_connector import AbstractConnector, SearchResult from .abstract_connector import AbstractConnector, SearchResult
@ -47,7 +47,16 @@ class Connector(AbstractConnector):
# when there are multiple editions of the same work, pick the default. # when there are multiple editions of the same work, pick the default.
# it would be odd for this to happen. # it would be odd for this to happen.
results = results.filter(parent_work__default_edition__id=F("id")) or results
default_editions = models.Edition.objects.filter(
parent_work=OuterRef("parent_work")
).order_by("-edition_rank")
results = (
results.annotate(
default_id=Subquery(default_editions.values("id")[:1])
).filter(default_id=F("id"))
or results
)
search_results = [] search_results = []
for result in results: for result in results:
@ -112,7 +121,15 @@ def search_identifiers(query, *filters):
# when there are multiple editions of the same work, pick the default. # when there are multiple editions of the same work, pick the default.
# it would be odd for this to happen. # it would be odd for this to happen.
return results.filter(parent_work__default_edition__id=F("id")) or results default_editions = models.Edition.objects.filter(
parent_work=OuterRef("parent_work")
).order_by("-edition_rank")
return (
results.annotate(default_id=Subquery(default_editions.values("id")[:1])).filter(
default_id=F("id")
)
or results
)
def search_title_author(query, min_confidence, *filters): def search_title_author(query, min_confidence, *filters):
@ -140,10 +157,10 @@ def search_title_author(query, min_confidence, *filters):
for work_id in set(editions_of_work): for work_id in set(editions_of_work):
editions = results.filter(parent_work=work_id) editions = results.filter(parent_work=work_id)
default = editions.filter(parent_work__default_edition=F("id")) default = editions.order_by("-edition_rank").first()
default_rank = default.first().rank if default.exists() else 0 default_rank = default.rank if default else 0
# if mutliple books have the top rank, pick the default edition # if mutliple books have the top rank, pick the default edition
if default_rank == editions.first().rank: if default_rank == editions.first().rank:
yield default.first() yield default
else: else:
yield editions.first() yield editions.first()

View file

@ -1,3 +1,3 @@
""" settings book data connectors """ """ settings book data connectors """
CONNECTORS = ["openlibrary", "self_connector", "bookwyrm_connector"] CONNECTORS = ["openlibrary", "inventaire", "self_connector", "bookwyrm_connector"]

View file

@ -94,6 +94,18 @@ def init_connectors():
priority=2, priority=2,
) )
Connector.objects.create(
identifier="inventaire.io",
name="Inventaire",
connector_file="inventaire",
base_url="https://inventaire.io",
books_url="https://inventaire.io/api/entities",
covers_url="https://inventaire.io",
search_url="https://inventaire.io/api/search?types=works&types=works&search=",
isbn_search_url="https://inventaire.io/api/entities?action=by-uris&uris=isbn%3A",
priority=3,
)
Connector.objects.create( Connector.objects.create(
identifier="openlibrary.org", identifier="openlibrary.org",
name="OpenLibrary", name="OpenLibrary",

View file

@ -0,0 +1,30 @@
# Generated by Django 3.1.6 on 2021-04-06 17:31
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("bookwyrm", "0061_auto_20210402_1435"),
]
operations = [
migrations.RemoveConstraint(
model_name="connector",
name="connector_file_valid",
),
migrations.AlterField(
model_name="connector",
name="connector_file",
field=models.CharField(
choices=[
("openlibrary", "Openlibrary"),
("inventaire", "Inventaire"),
("self_connector", "Self Connector"),
("bookwyrm_connector", "Bookwyrm Connector"),
],
max_length=255,
),
),
]

View file

@ -0,0 +1,63 @@
# Generated by Django 3.1.6 on 2021-04-07 00:45
import bookwyrm.models.fields
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
("bookwyrm", "0062_auto_20210406_1731"),
]
operations = [
migrations.AddField(
model_name="author",
name="bnf_id",
field=bookwyrm.models.fields.CharField(
blank=True, max_length=255, null=True
),
),
migrations.AddField(
model_name="author",
name="gutenberg_id",
field=bookwyrm.models.fields.CharField(
blank=True, max_length=255, null=True
),
),
migrations.AddField(
model_name="author",
name="inventaire_id",
field=bookwyrm.models.fields.CharField(
blank=True, max_length=255, null=True
),
),
migrations.AddField(
model_name="author",
name="isni",
field=bookwyrm.models.fields.CharField(
blank=True, max_length=255, null=True
),
),
migrations.AddField(
model_name="author",
name="viaf_id",
field=bookwyrm.models.fields.CharField(
blank=True, max_length=255, null=True
),
),
migrations.AddField(
model_name="book",
name="bnf_id",
field=bookwyrm.models.fields.CharField(
blank=True, max_length=255, null=True
),
),
migrations.AddField(
model_name="book",
name="inventaire_id",
field=bookwyrm.models.fields.CharField(
blank=True, max_length=255, null=True
),
),
]

View file

@ -0,0 +1,13 @@
# Generated by Django 3.2 on 2021-04-26 21:32
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
("bookwyrm", "0063_auto_20210407_0045"),
("bookwyrm", "0070_auto_20210423_0121"),
]
operations = []

View file

@ -0,0 +1,17 @@
# Generated by Django 3.2 on 2021-04-28 22:16
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
("bookwyrm", "0071_merge_0063_auto_20210407_0045_0070_auto_20210423_0121"),
]
operations = [
migrations.RemoveField(
model_name="work",
name="default_edition",
),
]

View file

@ -14,6 +14,15 @@ class Author(BookDataModel):
wikipedia_link = fields.CharField( wikipedia_link = fields.CharField(
max_length=255, blank=True, null=True, deduplication_field=True max_length=255, blank=True, null=True, deduplication_field=True
) )
isni = fields.CharField(
max_length=255, blank=True, null=True, deduplication_field=True
)
viaf_id = fields.CharField(
max_length=255, blank=True, null=True, deduplication_field=True
)
gutenberg_id = fields.CharField(
max_length=255, blank=True, null=True, deduplication_field=True
)
# idk probably other keys would be useful here? # idk probably other keys would be useful here?
born = fields.DateTimeField(blank=True, null=True) born = fields.DateTimeField(blank=True, null=True)
died = fields.DateTimeField(blank=True, null=True) died = fields.DateTimeField(blank=True, null=True)

View file

@ -1,11 +1,11 @@
""" database schema for books and shelves """ """ database schema for books and shelves """
import re import re
from django.db import models, transaction from django.db import models
from model_utils.managers import InheritanceManager from model_utils.managers import InheritanceManager
from bookwyrm import activitypub from bookwyrm import activitypub
from bookwyrm.settings import DOMAIN from bookwyrm.settings import DOMAIN, DEFAULT_LANGUAGE
from .activitypub_mixin import OrderedCollectionPageMixin, ObjectMixin from .activitypub_mixin import OrderedCollectionPageMixin, ObjectMixin
from .base_model import BookWyrmModel from .base_model import BookWyrmModel
@ -19,12 +19,18 @@ class BookDataModel(ObjectMixin, BookWyrmModel):
openlibrary_key = fields.CharField( openlibrary_key = fields.CharField(
max_length=255, blank=True, null=True, deduplication_field=True max_length=255, blank=True, null=True, deduplication_field=True
) )
inventaire_id = fields.CharField(
max_length=255, blank=True, null=True, deduplication_field=True
)
librarything_key = fields.CharField( librarything_key = fields.CharField(
max_length=255, blank=True, null=True, deduplication_field=True max_length=255, blank=True, null=True, deduplication_field=True
) )
goodreads_key = fields.CharField( goodreads_key = fields.CharField(
max_length=255, blank=True, null=True, deduplication_field=True max_length=255, blank=True, null=True, deduplication_field=True
) )
bnf_id = fields.CharField( # Bibliothèque nationale de France
max_length=255, blank=True, null=True, deduplication_field=True
)
last_edited_by = fields.ForeignKey( last_edited_by = fields.ForeignKey(
"User", "User",
@ -137,10 +143,6 @@ class Work(OrderedCollectionPageMixin, Book):
lccn = fields.CharField( lccn = fields.CharField(
max_length=255, blank=True, null=True, deduplication_field=True max_length=255, blank=True, null=True, deduplication_field=True
) )
# this has to be nullable but should never be null
default_edition = fields.ForeignKey(
"Edition", on_delete=models.PROTECT, null=True, load_remote=False
)
def save(self, *args, **kwargs): def save(self, *args, **kwargs):
"""set some fields on the edition object""" """set some fields on the edition object"""
@ -149,18 +151,10 @@ class Work(OrderedCollectionPageMixin, Book):
edition.save() edition.save()
return super().save(*args, **kwargs) return super().save(*args, **kwargs)
def get_default_edition(self): @property
def default_edition(self):
"""in case the default edition is not set""" """in case the default edition is not set"""
return self.default_edition or self.editions.order_by("-edition_rank").first() return self.editions.order_by("-edition_rank").first()
@transaction.atomic()
def reset_default_edition(self):
"""sets a new default edition based on computed rank"""
self.default_edition = None
# editions are re-ranked implicitly
self.save()
self.default_edition = self.get_default_edition()
self.save()
def to_edition_list(self, **kwargs): def to_edition_list(self, **kwargs):
"""an ordered collection of editions""" """an ordered collection of editions"""
@ -214,17 +208,20 @@ class Edition(Book):
activity_serializer = activitypub.Edition activity_serializer = activitypub.Edition
name_field = "title" name_field = "title"
def get_rank(self, ignore_default=False): def get_rank(self):
"""calculate how complete the data is on this edition""" """calculate how complete the data is on this edition"""
if (
not ignore_default
and self.parent_work
and self.parent_work.default_edition == self
):
# default edition has the highest rank
return 20
rank = 0 rank = 0
# big ups for havinga cover
rank += int(bool(self.cover)) * 3 rank += int(bool(self.cover)) * 3
# is it in the instance's preferred language?
rank += int(bool(DEFAULT_LANGUAGE in self.languages))
# prefer print editions
if self.physical_format:
rank += int(
bool(self.physical_format.lower() in ["paperback", "hardcover"])
)
# does it have metadata?
rank += int(bool(self.isbn_13)) rank += int(bool(self.isbn_13))
rank += int(bool(self.isbn_10)) rank += int(bool(self.isbn_10))
rank += int(bool(self.oclc_number)) rank += int(bool(self.oclc_number))
@ -242,6 +239,12 @@ class Edition(Book):
if self.isbn_10 and not self.isbn_13: if self.isbn_10 and not self.isbn_13:
self.isbn_13 = isbn_10_to_13(self.isbn_10) self.isbn_13 = isbn_10_to_13(self.isbn_10)
# normalize isbn format
if self.isbn_10:
self.isbn_10 = re.sub(r"[^0-9X]", "", self.isbn_10)
if self.isbn_13:
self.isbn_13 = re.sub(r"[^0-9X]", "", self.isbn_13)
# set rank # set rank
self.edition_rank = self.get_rank() self.edition_rank = self.get_rank()

View file

@ -31,16 +31,6 @@ class Connector(BookWyrmModel):
# when to reset the query count back to 0 (ie, after 1 day) # when to reset the query count back to 0 (ie, after 1 day)
query_count_expiry = models.DateTimeField(auto_now_add=True, blank=True) query_count_expiry = models.DateTimeField(auto_now_add=True, blank=True)
class Meta:
"""check that there's code to actually use this connector"""
constraints = [
models.CheckConstraint(
check=models.Q(connector_file__in=ConnectorFiles),
name="connector_file_valid",
)
]
def __str__(self): def __str__(self):
return "{} ({})".format( return "{} ({})".format(
self.identifier, self.identifier,

View file

@ -11,6 +11,7 @@ DOMAIN = env("DOMAIN")
VERSION = "0.0.1" VERSION = "0.0.1"
PAGE_LENGTH = env("PAGE_LENGTH", 15) PAGE_LENGTH = env("PAGE_LENGTH", 15)
DEFAULT_LANGUAGE = env("DEFAULT_LANGUAGE", "English")
# celery # celery
CELERY_BROKER = env("CELERY_BROKER") CELERY_BROKER = env("CELERY_BROKER")

View file

@ -81,6 +81,9 @@
{% if book.openlibrary_key %} {% if book.openlibrary_key %}
<p><a href="https://openlibrary.org/books/{{ book.openlibrary_key }}" target="_blank" rel="noopener">{% trans "View on OpenLibrary" %}</a></p> <p><a href="https://openlibrary.org/books/{{ book.openlibrary_key }}" target="_blank" rel="noopener">{% trans "View on OpenLibrary" %}</a></p>
{% endif %} {% endif %}
{% if book.inventaire_id %}
<p><a href="https://inventaire.io/entity/{{ book.inventaire_id }}" target="_blank" rel="noopener">{% trans "View on Inventaire" %}</a></p>
{% endif %}
</section> </section>
</div> </div>

View file

@ -16,10 +16,15 @@
<div class="column"> <div class="column">
<p> <p>
<strong> <strong>
<a href="{{ result.key }}"{% if remote_result %} rel=”noopener” target="_blank"{% endif %}>{{ result.title }}</a> <a href="{{ result.view_link|default:result.key }}"{% if remote_result %} rel=”noopener” target="_blank"{% endif %}>{{ result.title }}</a>
</strong> </strong>
</p>
<p>
{% if result.author %} {% if result.author %}
{% blocktrans with author=result.author %}by {{ author }}{% endblocktrans %}{% endif %}{% if result.year %} ({{ result.year }}) {{ result.author }}
{% endif %}
{% if result.year %}
({{ result.year }})
{% endif %} {% endif %}
</p> </p>

View file

@ -17,8 +17,6 @@ class ConnectorManager(TestCase):
self.edition = models.Edition.objects.create( self.edition = models.Edition.objects.create(
title="Example Edition", parent_work=self.work, isbn_10="0000000000" title="Example Edition", parent_work=self.work, isbn_10="0000000000"
) )
self.work.default_edition = self.edition
self.work.save()
self.connector = models.Connector.objects.create( self.connector = models.Connector.objects.create(
identifier="test_connector", identifier="test_connector",

View file

@ -0,0 +1,158 @@
""" testing book data connectors """
import json
import pathlib
from django.test import TestCase
import responses
from bookwyrm import models
from bookwyrm.connectors.inventaire import Connector
class Inventaire(TestCase):
"""test loading data from inventaire.io"""
def setUp(self):
"""creates the connector we'll use"""
models.Connector.objects.create(
identifier="inventaire.io",
name="Inventaire",
connector_file="inventaire",
base_url="https://inventaire.io",
books_url="https://inventaire.io",
covers_url="https://covers.inventaire.io",
search_url="https://inventaire.io/search?q=",
isbn_search_url="https://inventaire.io/isbn",
)
self.connector = Connector("inventaire.io")
@responses.activate
def test_get_book_data(self):
"""flattens the default structure to make it easier to parse"""
responses.add(
responses.GET,
"https://test.url/ok",
json={
"entities": {
"isbn:9780375757853": {
"claims": {
"wdt:P31": ["wd:Q3331189"],
},
"uri": "isbn:9780375757853",
}
},
"redirects": {},
},
)
result = self.connector.get_book_data("https://test.url/ok")
self.assertEqual(result["wdt:P31"], ["wd:Q3331189"])
self.assertEqual(result["uri"], "isbn:9780375757853")
def test_format_search_result(self):
"""json to search result objs"""
search_file = pathlib.Path(__file__).parent.joinpath(
"../data/inventaire_search.json"
)
search_results = json.loads(search_file.read_bytes())
results = self.connector.parse_search_data(search_results)
formatted = self.connector.format_search_result(results[0])
self.assertEqual(formatted.title, "The Stories of Vladimir Nabokov")
self.assertEqual(
formatted.key, "https://inventaire.io?action=by-uris&uris=wd:Q7766679"
)
self.assertEqual(
formatted.cover,
"https://covers.inventaire.io/img/entities/ddb32e115a28dcc0465023869ba19f6868ec4042",
)
def test_get_cover_url(self):
"""figure out where the cover image is"""
cover_blob = {"url": "/img/entities/d46a8"}
result = self.connector.get_cover_url(cover_blob)
self.assertEqual(result, "https://covers.inventaire.io/img/entities/d46a8")
cover_blob = {
"url": "https://commons.wikimedia.org/wiki/Special:FilePath/The%20Moonstone%201st%20ed.jpg?width=1000",
"file": "The Moonstone 1st ed.jpg",
"credits": {
"text": "Wikimedia Commons",
"url": "https://commons.wikimedia.org/wiki/File:The Moonstone 1st ed.jpg",
},
}
result = self.connector.get_cover_url(cover_blob)
self.assertEqual(
result,
"https://commons.wikimedia.org/wiki/Special:FilePath/The%20Moonstone%201st%20ed.jpg?width=1000",
)
@responses.activate
def test_resolve_keys(self):
"""makes an http request"""
responses.add(
responses.GET,
"https://inventaire.io?action=by-uris&uris=wd:Q465821",
json={
"entities": {
"wd:Q465821": {
"type": "genre",
"labels": {
"nl": "briefroman",
"en": "epistolary novel",
"de-ch": "Briefroman",
"en-ca": "Epistolary novel",
"nb": "brev- og dagbokroman",
},
"descriptions": {
"en": "novel written as a series of documents",
"es": "novela escrita como una serie de documentos",
"eo": "romano en la formo de serio de leteroj",
},
},
"redirects": {},
}
},
)
responses.add(
responses.GET,
"https://inventaire.io?action=by-uris&uris=wd:Q208505",
json={
"entities": {
"wd:Q208505": {
"type": "genre",
"labels": {
"en": "crime novel",
},
},
}
},
)
keys = [
"wd:Q465821",
"wd:Q208505",
]
result = self.connector.resolve_keys(keys)
self.assertEqual(result, ["epistolary novel", "crime novel"])
def test_isbn_search(self):
"""another search type"""
search_file = pathlib.Path(__file__).parent.joinpath(
"../data/inventaire_isbn_search.json"
)
search_results = json.loads(search_file.read_bytes())
results = self.connector.parse_isbn_search_data(search_results)
formatted = self.connector.format_isbn_search_result(results[0])
self.assertEqual(formatted.title, "L'homme aux cercles bleus")
self.assertEqual(
formatted.key,
"https://inventaire.io?action=by-uris&uris=isbn:9782290349229",
)
self.assertEqual(
formatted.cover,
"https://covers.inventaire.io/img/entities/12345",
)

View file

@ -84,11 +84,11 @@ class SelfConnector(TestCase):
title="Edition 1 Title", parent_work=work title="Edition 1 Title", parent_work=work
) )
edition_2 = models.Edition.objects.create( edition_2 = models.Edition.objects.create(
title="Edition 2 Title", parent_work=work title="Edition 2 Title",
parent_work=work,
edition_rank=20, # that's default babey
) )
edition_3 = models.Edition.objects.create(title="Fish", parent_work=work) edition_3 = models.Edition.objects.create(title="Fish", parent_work=work)
work.default_edition = edition_2
work.save()
# pick the best edition # pick the best edition
results = self.connector.search("Edition 1 Title") results = self.connector.search("Edition 1 Title")

View file

@ -0,0 +1,45 @@
{
"entities": {
"isbn:9780375757853": {
"_id": "7beee121a8d9ac345cdf4e9128577723",
"_rev": "2-ac318b04b953ca3894deb77fee28211c",
"type": "edition",
"labels": {},
"claims": {
"wdt:P31": [
"wd:Q3331189"
],
"wdt:P212": [
"978-0-375-75785-3"
],
"wdt:P957": [
"0-375-75785-6"
],
"wdt:P407": [
"wd:Q1860"
],
"wdt:P1476": [
"The Moonstone"
],
"wdt:P577": [
"2001"
],
"wdt:P629": [
"wd:Q2362563"
],
"invp:P2": [
"d46a8eac7555afa479b8bbb5149f35858e8e19c4"
]
},
"created": 1495452670475,
"updated": 1541032981834,
"version": 3,
"uri": "isbn:9780375757853",
"originalLang": "en",
"image": {
"url": "/img/entities/d46a8eac7555afa479b8bbb5149f35858e8e19c4"
}
}
},
"redirects": {}
}

View file

@ -0,0 +1,48 @@
{
"entities": {
"isbn:9782290349229": {
"_id": "d59e3e64f92c6340fbb10c5dcf7c0abf",
"_rev": "3-079ed51158a001dc74caafb21cff1c22",
"type": "edition",
"labels": {},
"claims": {
"wdt:P31": [
"wd:Q3331189"
],
"wdt:P212": [
"978-2-290-34922-9"
],
"wdt:P957": [
"2-290-34922-4"
],
"wdt:P407": [
"wd:Q150"
],
"wdt:P1476": [
"L'homme aux cercles bleus"
],
"wdt:P629": [
"wd:Q3203603"
],
"wdt:P123": [
"wd:Q3156592"
],
"invp:P2": [
"57883743aa7c6ad25885a63e6e94349ec4f71562"
],
"wdt:P577": [
"2005-05-01"
]
},
"created": 1485023383338,
"updated": 1609171008418,
"version": 5,
"uri": "isbn:9782290349229",
"originalLang": "fr",
"image": {
"url": "/img/entities/12345"
}
}
},
"redirects": {}
}

View file

@ -0,0 +1,111 @@
{
"results": [
{
"id": "Q7766679",
"type": "works",
"uri": "wd:Q7766679",
"label": "The Stories of Vladimir Nabokov",
"description": "book by Vladimir Nabokov",
"image": [
"ddb32e115a28dcc0465023869ba19f6868ec4042"
],
"_score": 25.180836,
"_popularity": 4
},
{
"id": "Q47407212",
"type": "works",
"uri": "wd:Q47407212",
"label": "Conversations with Vladimir Nabokov",
"description": "book edited by Robert Golla",
"image": [],
"_score": 24.41498,
"_popularity": 2
},
{
"id": "Q6956987",
"type": "works",
"uri": "wd:Q6956987",
"label": "Nabokov's Congeries",
"description": "book by Vladimir Nabokov",
"image": [],
"_score": 22.343866,
"_popularity": 2
},
{
"id": "Q6956986",
"type": "works",
"uri": "wd:Q6956986",
"label": "Nabokov's Butterflies",
"description": "book by Brian Boyd",
"image": [],
"_score": 22.343866,
"_popularity": 2
},
{
"id": "Q47472170",
"type": "works",
"uri": "wd:Q47472170",
"label": "A Reader's Guide to Nabokov's \"Lolita\"",
"description": "book by Julian W. Connolly",
"image": [],
"_score": 19.482553,
"_popularity": 2
},
{
"id": "Q7936323",
"type": "works",
"uri": "wd:Q7936323",
"label": "Visiting Mrs Nabokov: And Other Excursions",
"description": "book by Martin Amis",
"image": [],
"_score": 18.684965,
"_popularity": 2
},
{
"id": "1732d81bf7376e04da27568a778561a4",
"type": "works",
"uri": "inv:1732d81bf7376e04da27568a778561a4",
"label": "Nabokov's Dark Cinema",
"image": [
"7512805a53da569b11bf29cc3fb272c969619749"
],
"_score": 16.56681,
"_popularity": 1
},
{
"id": "00f118336b02219e1bddc8fa93c56050",
"type": "works",
"uri": "inv:00f118336b02219e1bddc8fa93c56050",
"label": "The Cambridge Companion to Nabokov",
"image": [
"0683a059fb95430cfa73334f9eff2ef377f3ae3d"
],
"_score": 15.502292,
"_popularity": 1
},
{
"id": "6e59f968a1cd00dbedeb1964dec47507",
"type": "works",
"uri": "inv:6e59f968a1cd00dbedeb1964dec47507",
"label": "Vladimir Nabokov : selected letters, 1940-1977",
"image": [
"e3ce8c0ee89d576adf2651a6e5ce55fc6d9f8cb3"
],
"_score": 15.019735,
"_popularity": 1
},
{
"id": "Q127149",
"type": "works",
"uri": "wd:Q127149",
"label": "Lolita",
"description": "novel by Vladimir Nabokov",
"image": [
"51cbfdbf7257b1a6bb3ea3fbb167dbce1fb44a0e"
],
"_score": 13.458428,
"_popularity": 32
}
]
}

View file

@ -0,0 +1,155 @@
{
"entities": {
"wd:Q2362563": {
"type": "work",
"labels": {
"zh-hans": "月亮宝石",
"zh-hant": "月亮寶石",
"zh-hk": "月光石",
"zh-tw": "月光石",
"cy": "The Moonstone",
"ml": "ദ മൂൺസ്റ്റോൺ",
"ja": "月長石",
"te": "ది మూన్ స్టోన్",
"ru": "Лунный камень",
"fr": "La Pierre de lune",
"en": "The Moonstone",
"es": "La piedra lunar",
"it": "La Pietra di Luna",
"zh": "月亮宝石",
"pl": "Kamień Księżycowy",
"sr": "2 Јн",
"ta": "moon stone",
"ar": "حجر القمر",
"fa": "ماه‌الماس",
"uk": "Місячний камінь",
"nl": "The Moonstone",
"de": "Der Monddiamant",
"sl": "Diamant",
"sv": "Månstenen",
"he": "אבן הירח",
"eu": "Ilargi-harriak",
"bg": "Лунният камък",
"ka": "მთვარის ქვა",
"eo": "La Lunŝtono",
"hy": "Լուսնաքար",
"ro": "Piatra Lunii",
"ca": "The Moonstone",
"is": "The Moonstone"
},
"descriptions": {
"it": "romanzo scritto da Wilkie Collins",
"en": "novel by Wilkie Collins",
"de": "Buch von Wilkie Collins",
"nl": "boek van Wilkie Collins",
"ru": "роман Уилки Коллинза",
"he": "רומן מאת וילקי קולינס",
"ar": "رواية من تأليف ويلكي كولينز",
"fr": "livre de Wilkie Collins",
"es": "libro de Wilkie Collins",
"bg": "роман на Уилки Колинс",
"ka": "უილკი კოლინსის რომანი",
"eo": "angalingva romano far Wilkie Collins",
"ro": "roman de Wilkie Collins"
},
"aliases": {
"zh": [
"月光石"
],
"ml": [
"The Moonstone"
],
"fr": [
"The Moonstone"
],
"it": [
"Il diamante indiano",
"La pietra della luna",
"La maledizione del diamante indiano"
],
"ro": [
"The Moonstone"
]
},
"claims": {
"wdt:P18": [
"The Moonstone 1st ed.jpg"
],
"wdt:P31": [
"wd:Q7725634"
],
"wdt:P50": [
"wd:Q210740"
],
"wdt:P123": [
"wd:Q4457856"
],
"wdt:P136": [
"wd:Q465821",
"wd:Q208505",
"wd:Q10992055"
],
"wdt:P156": [
"wd:Q7228798"
],
"wdt:P268": [
"12496407z"
],
"wdt:P407": [
"wd:Q7979"
],
"wdt:P577": [
"1868"
],
"wdt:P1433": [
"wd:Q21"
],
"wdt:P1476": [
"The Moonstone"
],
"wdt:P1680": [
"A Romance"
],
"wdt:P2034": [
"155"
]
},
"sitelinks": {
"arwiki": "حجر القمر (رواية)",
"bgwiki": "Лунният камък (роман)",
"cywiki": "The Moonstone",
"dewiki": "Der Monddiamant",
"enwiki": "The Moonstone",
"enwikisource": "The Moonstone",
"eswiki": "La piedra lunar",
"euwiki": "Ilargi-harria",
"fawiki": "ماه‌الماس",
"frwiki": "La Pierre de lune (roman de Wilkie Collins)",
"hewiki": "אבן הירח",
"hywiki": "Լուսնաքար",
"iswiki": "The Moonstone",
"itwiki": "La pietra di Luna",
"jawiki": "月長石 (小説)",
"mlwiki": "ദ മൂൺസ്റ്റോൺ",
"plwiki": "Kamień Księżycowy (powieść)",
"ruwiki": "Лунный камень (роман)",
"slwiki": "Diamant (roman)",
"srwikisource": "Нови завјет (Караџић) / 2. Јованова",
"svwiki": "Månstenen",
"tewiki": "ది మూన్‌స్టోన్",
"ukwiki": "Місячний камінь (роман)",
"zhwiki": "月亮宝石"
},
"uri": "wd:Q2362563",
"image": {
"url": "https://commons.wikimedia.org/wiki/Special:FilePath/The%20Moonstone%201st%20ed.jpg?width=1000",
"file": "The Moonstone 1st ed.jpg",
"credits": {
"text": "Wikimedia Commons",
"url": "https://commons.wikimedia.org/wiki/File:The Moonstone 1st ed.jpg"
}
}
}
},
"redirects": {}
}

View file

@ -84,9 +84,3 @@ class Book(TestCase):
self.first_edition.description = "hi" self.first_edition.description = "hi"
self.first_edition.save() self.first_edition.save()
self.assertEqual(self.first_edition.edition_rank, 1) self.assertEqual(self.first_edition.edition_rank, 1)
# default edition
self.work.default_edition = self.first_edition
self.work.save()
self.first_edition.refresh_from_db()
self.assertEqual(self.first_edition.edition_rank, 20)

View file

@ -2,7 +2,7 @@
from django.test import TestCase from django.test import TestCase
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from bookwyrm import models, settings from bookwyrm import models
class ReadThrough(TestCase): class ReadThrough(TestCase):
@ -19,8 +19,6 @@ class ReadThrough(TestCase):
self.edition = models.Edition.objects.create( self.edition = models.Edition.objects.create(
title="Example Edition", parent_work=self.work title="Example Edition", parent_work=self.work
) )
self.work.default_edition = self.edition
self.work.save()
self.readthrough = models.ReadThrough.objects.create( self.readthrough = models.ReadThrough.objects.create(
user=self.user, book=self.edition user=self.user, book=self.edition

View file

@ -20,8 +20,6 @@ class ReadThrough(TestCase):
self.edition = models.Edition.objects.create( self.edition = models.Edition.objects.create(
title="Example Edition", parent_work=self.work title="Example Edition", parent_work=self.work
) )
self.work.default_edition = self.edition
self.work.save()
self.user = models.User.objects.create_user( self.user = models.User.objects.create_user(
"cinco", "cinco@example.com", "seissiete", local=True, localname="cinco" "cinco", "cinco@example.com", "seissiete", local=True, localname="cinco"

View file

@ -27,7 +27,7 @@ class Author(View):
).distinct() ).distinct()
data = { data = {
"author": author, "author": author,
"books": [b.get_default_edition() for b in books], "books": [b.default_edition for b in books],
} }
return TemplateResponse(request, "author.html", data) return TemplateResponse(request, "author.html", data)

View file

@ -39,7 +39,7 @@ class Book(View):
return ActivitypubResponse(book.to_activity()) return ActivitypubResponse(book.to_activity())
if isinstance(book, models.Work): if isinstance(book, models.Work):
book = book.get_default_edition() book = book.default_edition
if not book or not book.parent_work: if not book or not book.parent_work:
return HttpResponseNotFound() return HttpResponseNotFound()
@ -156,7 +156,6 @@ class EditBook(View):
), ),
} }
) )
print(data["author_matches"])
# we're creating a new book # we're creating a new book
if not book: if not book:

View file

@ -123,7 +123,7 @@ def get_edition(book_id):
"""look up a book in the db and return an edition""" """look up a book in the db and return an edition"""
book = models.Book.objects.select_subclasses().get(id=book_id) book = models.Book.objects.select_subclasses().get(id=book_id)
if isinstance(book, models.Work): if isinstance(book, models.Work):
book = book.get_default_edition() book = book.default_edition
return book return book

Binary file not shown.

View file

@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: 0.0.1\n" "Project-Id-Version: 0.0.1\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-04-26 09:56-0700\n" "POT-Creation-Date: 2021-04-29 11:36-0700\n"
"PO-Revision-Date: 2021-03-02 17:19-0800\n" "PO-Revision-Date: 2021-03-02 17:19-0800\n"
"Last-Translator: Mouse Reeve <mousereeve@riseup.net>\n" "Last-Translator: Mouse Reeve <mousereeve@riseup.net>\n"
"Language-Team: English <LL@li.org>\n" "Language-Team: English <LL@li.org>\n"
@ -100,23 +100,23 @@ msgstr "Username"
msgid "A user with that username already exists." msgid "A user with that username already exists."
msgstr "Dieser Benutzename ist bereits vergeben." msgstr "Dieser Benutzename ist bereits vergeben."
#: bookwyrm/settings.py:152 #: bookwyrm/settings.py:155
msgid "English" msgid "English"
msgstr "Englisch" msgstr "Englisch"
#: bookwyrm/settings.py:153 #: bookwyrm/settings.py:156
msgid "German" msgid "German"
msgstr "Deutsch" msgstr "Deutsch"
#: bookwyrm/settings.py:154 #: bookwyrm/settings.py:157
msgid "Spanish" msgid "Spanish"
msgstr "Spanisch" msgstr "Spanisch"
#: bookwyrm/settings.py:155 #: bookwyrm/settings.py:158
msgid "French" msgid "French"
msgstr "Französisch" msgstr "Französisch"
#: bookwyrm/settings.py:156 #: bookwyrm/settings.py:159
msgid "Simplified Chinese" msgid "Simplified Chinese"
msgstr "Vereinfachtes Chinesisch" msgstr "Vereinfachtes Chinesisch"
@ -178,24 +178,30 @@ msgstr "Laden fehlgeschlagen"
msgid "View on OpenLibrary" msgid "View on OpenLibrary"
msgstr "In OpenLibrary ansehen" msgstr "In OpenLibrary ansehen"
#: bookwyrm/templates/book/book.html:102 #: bookwyrm/templates/book/book.html:85
#, fuzzy
#| msgid "View on OpenLibrary"
msgid "View on Inventaire"
msgstr "In OpenLibrary ansehen"
#: bookwyrm/templates/book/book.html:105
#, python-format #, python-format
msgid "(%(review_count)s review)" msgid "(%(review_count)s review)"
msgid_plural "(%(review_count)s reviews)" msgid_plural "(%(review_count)s reviews)"
msgstr[0] "(%(review_count)s Bewertung)" msgstr[0] "(%(review_count)s Bewertung)"
msgstr[1] "(%(review_count)s Bewertungen)" msgstr[1] "(%(review_count)s Bewertungen)"
#: bookwyrm/templates/book/book.html:114 #: bookwyrm/templates/book/book.html:117
msgid "Add Description" msgid "Add Description"
msgstr "Beschreibung hinzufügen" msgstr "Beschreibung hinzufügen"
#: bookwyrm/templates/book/book.html:121 #: bookwyrm/templates/book/book.html:124
#: bookwyrm/templates/book/edit_book.html:107 #: bookwyrm/templates/book/edit_book.html:107
#: bookwyrm/templates/lists/form.html:12 #: bookwyrm/templates/lists/form.html:12
msgid "Description:" msgid "Description:"
msgstr "Beschreibung:" msgstr "Beschreibung:"
#: bookwyrm/templates/book/book.html:125 #: bookwyrm/templates/book/book.html:128
#: bookwyrm/templates/book/edit_book.html:240 #: bookwyrm/templates/book/edit_book.html:240
#: bookwyrm/templates/edit_author.html:78 bookwyrm/templates/lists/form.html:42 #: bookwyrm/templates/edit_author.html:78 bookwyrm/templates/lists/form.html:42
#: bookwyrm/templates/preferences/edit_user.html:70 #: bookwyrm/templates/preferences/edit_user.html:70
@ -210,7 +216,7 @@ msgstr "Beschreibung:"
msgid "Save" msgid "Save"
msgstr "Speichern" msgstr "Speichern"
#: bookwyrm/templates/book/book.html:126 bookwyrm/templates/book/book.html:175 #: bookwyrm/templates/book/book.html:129 bookwyrm/templates/book/book.html:178
#: bookwyrm/templates/book/cover_modal.html:32 #: bookwyrm/templates/book/cover_modal.html:32
#: bookwyrm/templates/book/edit_book.html:241 #: bookwyrm/templates/book/edit_book.html:241
#: bookwyrm/templates/edit_author.html:79 #: bookwyrm/templates/edit_author.html:79
@ -226,19 +232,19 @@ msgstr "Speichern"
msgid "Cancel" msgid "Cancel"
msgstr "Abbrechen" msgstr "Abbrechen"
#: bookwyrm/templates/book/book.html:135 #: bookwyrm/templates/book/book.html:138
#, fuzzy, python-format #, fuzzy, python-format
#| msgid "<a href=\"%(path)s\">%(title)s</a> by " #| msgid "<a href=\"%(path)s\">%(title)s</a> by "
msgid "<a href=\"%(path)s/editions\">%(count)s editions</a>" msgid "<a href=\"%(path)s/editions\">%(count)s editions</a>"
msgstr "<a href=\"%(path)s\">%(title)s</a> von" msgstr "<a href=\"%(path)s\">%(title)s</a> von"
#: bookwyrm/templates/book/book.html:143 #: bookwyrm/templates/book/book.html:146
#, fuzzy, python-format #, fuzzy, python-format
#| msgid "Direct Messages with <a href=\"%(path)s\">%(username)s</a>" #| msgid "Direct Messages with <a href=\"%(path)s\">%(username)s</a>"
msgid "This edition is on your <a href=\"%(path)s\">%(shelf_name)s</a> shelf." msgid "This edition is on your <a href=\"%(path)s\">%(shelf_name)s</a> shelf."
msgstr "Direktnachrichten mit <a href=\"%(path)s\">%(username)s</a>" msgstr "Direktnachrichten mit <a href=\"%(path)s\">%(username)s</a>"
#: bookwyrm/templates/book/book.html:149 #: bookwyrm/templates/book/book.html:152
#, fuzzy, python-format #, fuzzy, python-format
#| msgid "" #| msgid ""
#| " added <em><a href=\"%(book_path)s\">%(book_title)s</a></em> to your list " #| " added <em><a href=\"%(book_path)s\">%(book_title)s</a></em> to your list "
@ -250,74 +256,74 @@ msgstr ""
"hat <em><a href=\"%(book_path)s\">%(book_title)s</a></em> zu deiner Liste " "hat <em><a href=\"%(book_path)s\">%(book_title)s</a></em> zu deiner Liste "
"\"<a href=\"%(list_path)s\">%(list_name)s</a>\" Hinzugefügt" "\"<a href=\"%(list_path)s\">%(list_name)s</a>\" Hinzugefügt"
#: bookwyrm/templates/book/book.html:158 #: bookwyrm/templates/book/book.html:161
msgid "Your reading activity" msgid "Your reading activity"
msgstr "Deine Leseaktivität" msgstr "Deine Leseaktivität"
#: bookwyrm/templates/book/book.html:160 #: bookwyrm/templates/book/book.html:163
msgid "Add read dates" msgid "Add read dates"
msgstr "Lesedaten hinzufügen" msgstr "Lesedaten hinzufügen"
#: bookwyrm/templates/book/book.html:165 #: bookwyrm/templates/book/book.html:168
msgid "You don't have any reading activity for this book." msgid "You don't have any reading activity for this book."
msgstr "Du hast keine Leseaktivität für dieses Buch." msgstr "Du hast keine Leseaktivität für dieses Buch."
#: bookwyrm/templates/book/book.html:172 #: bookwyrm/templates/book/book.html:175
msgid "Create" msgid "Create"
msgstr "Erstellen" msgstr "Erstellen"
#: bookwyrm/templates/book/book.html:194 #: bookwyrm/templates/book/book.html:197
msgid "Subjects" msgid "Subjects"
msgstr "Themen" msgstr "Themen"
#: bookwyrm/templates/book/book.html:206 #: bookwyrm/templates/book/book.html:209
msgid "Places" msgid "Places"
msgstr "Orte" msgstr "Orte"
#: bookwyrm/templates/book/book.html:217 bookwyrm/templates/layout.html:65 #: bookwyrm/templates/book/book.html:220 bookwyrm/templates/layout.html:65
#: bookwyrm/templates/lists/lists.html:5 bookwyrm/templates/lists/lists.html:12 #: bookwyrm/templates/lists/lists.html:5 bookwyrm/templates/lists/lists.html:12
#: bookwyrm/templates/search_results.html:91 #: bookwyrm/templates/search_results.html:91
#: bookwyrm/templates/user/user_layout.html:62 #: bookwyrm/templates/user/user_layout.html:62
msgid "Lists" msgid "Lists"
msgstr "Listen" msgstr "Listen"
#: bookwyrm/templates/book/book.html:228 #: bookwyrm/templates/book/book.html:231
#, fuzzy #, fuzzy
#| msgid "Go to list" #| msgid "Go to list"
msgid "Add to list" msgid "Add to list"
msgstr "Zur Liste" msgstr "Zur Liste"
#: bookwyrm/templates/book/book.html:238 #: bookwyrm/templates/book/book.html:241
#: bookwyrm/templates/book/cover_modal.html:31 #: bookwyrm/templates/book/cover_modal.html:31
#: bookwyrm/templates/lists/list.html:133 #: bookwyrm/templates/lists/list.html:133
msgid "Add" msgid "Add"
msgstr "Hinzufügen" msgstr "Hinzufügen"
#: bookwyrm/templates/book/book.html:254 #: bookwyrm/templates/book/book.html:257
#, fuzzy #, fuzzy
#| msgid "Review" #| msgid "Review"
msgid "Reviews" msgid "Reviews"
msgstr "Bewerten" msgstr "Bewerten"
#: bookwyrm/templates/book/book.html:259 #: bookwyrm/templates/book/book.html:262
#, fuzzy #, fuzzy
#| msgid "Your shelves" #| msgid "Your shelves"
msgid "Your reviews" msgid "Your reviews"
msgstr "Deine Regale" msgstr "Deine Regale"
#: bookwyrm/templates/book/book.html:265 #: bookwyrm/templates/book/book.html:268
#, fuzzy #, fuzzy
#| msgid "Your Account" #| msgid "Your Account"
msgid "Your comments" msgid "Your comments"
msgstr "Dein Account" msgstr "Dein Account"
#: bookwyrm/templates/book/book.html:271 #: bookwyrm/templates/book/book.html:274
#, fuzzy #, fuzzy
#| msgid "Your books" #| msgid "Your books"
msgid "Your quotes" msgid "Your quotes"
msgstr "Deine Bücher" msgstr "Deine Bücher"
#: bookwyrm/templates/book/book.html:305 #: bookwyrm/templates/book/book.html:308
msgid "rated it" msgid "rated it"
msgstr "bewertet" msgstr "bewertet"
@ -2728,12 +2734,7 @@ msgstr "kommentierte"
msgid "quoted" msgid "quoted"
msgstr "zitierte" msgstr "zitierte"
#: bookwyrm/templates/snippets/search_result_text.html:22 #: bookwyrm/templates/snippets/search_result_text.html:35
#, python-format
msgid "by %(author)s"
msgstr "von %(author)s"
#: bookwyrm/templates/snippets/search_result_text.html:30
msgid "Import book" msgid "Import book"
msgstr "Buch importieren" msgstr "Buch importieren"
@ -4495,6 +4496,10 @@ msgctxt "stick"
msgid "club" msgid "club"
msgstr "" msgstr ""
#, python-format
#~ msgid "by %(author)s"
#~ msgstr "von %(author)s"
#~ msgid "Deactivate user" #~ msgid "Deactivate user"
#~ msgstr "Nutzer:in deaktivieren" #~ msgstr "Nutzer:in deaktivieren"

View file

@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: 0.0.1\n" "Project-Id-Version: 0.0.1\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-04-26 09:56-0700\n" "POT-Creation-Date: 2021-04-29 11:36-0700\n"
"PO-Revision-Date: 2021-02-28 17:19-0800\n" "PO-Revision-Date: 2021-02-28 17:19-0800\n"
"Last-Translator: Mouse Reeve <mousereeve@riseup.net>\n" "Last-Translator: Mouse Reeve <mousereeve@riseup.net>\n"
"Language-Team: English <LL@li.org>\n" "Language-Team: English <LL@li.org>\n"
@ -90,23 +90,23 @@ msgstr ""
msgid "A user with that username already exists." msgid "A user with that username already exists."
msgstr "" msgstr ""
#: bookwyrm/settings.py:152 #: bookwyrm/settings.py:155
msgid "English" msgid "English"
msgstr "" msgstr ""
#: bookwyrm/settings.py:153 #: bookwyrm/settings.py:156
msgid "German" msgid "German"
msgstr "" msgstr ""
#: bookwyrm/settings.py:154 #: bookwyrm/settings.py:157
msgid "Spanish" msgid "Spanish"
msgstr "" msgstr ""
#: bookwyrm/settings.py:155 #: bookwyrm/settings.py:158
msgid "French" msgid "French"
msgstr "" msgstr ""
#: bookwyrm/settings.py:156 #: bookwyrm/settings.py:159
msgid "Simplified Chinese" msgid "Simplified Chinese"
msgstr "" msgstr ""
@ -166,24 +166,28 @@ msgstr ""
msgid "View on OpenLibrary" msgid "View on OpenLibrary"
msgstr "" msgstr ""
#: bookwyrm/templates/book/book.html:102 #: bookwyrm/templates/book/book.html:85
msgid "View on Inventaire"
msgstr ""
#: bookwyrm/templates/book/book.html:105
#, python-format #, python-format
msgid "(%(review_count)s review)" msgid "(%(review_count)s review)"
msgid_plural "(%(review_count)s reviews)" msgid_plural "(%(review_count)s reviews)"
msgstr[0] "" msgstr[0] ""
msgstr[1] "" msgstr[1] ""
#: bookwyrm/templates/book/book.html:114 #: bookwyrm/templates/book/book.html:117
msgid "Add Description" msgid "Add Description"
msgstr "" msgstr ""
#: bookwyrm/templates/book/book.html:121 #: bookwyrm/templates/book/book.html:124
#: bookwyrm/templates/book/edit_book.html:107 #: bookwyrm/templates/book/edit_book.html:107
#: bookwyrm/templates/lists/form.html:12 #: bookwyrm/templates/lists/form.html:12
msgid "Description:" msgid "Description:"
msgstr "" msgstr ""
#: bookwyrm/templates/book/book.html:125 #: bookwyrm/templates/book/book.html:128
#: bookwyrm/templates/book/edit_book.html:240 #: bookwyrm/templates/book/edit_book.html:240
#: bookwyrm/templates/edit_author.html:78 bookwyrm/templates/lists/form.html:42 #: bookwyrm/templates/edit_author.html:78 bookwyrm/templates/lists/form.html:42
#: bookwyrm/templates/preferences/edit_user.html:70 #: bookwyrm/templates/preferences/edit_user.html:70
@ -198,7 +202,7 @@ msgstr ""
msgid "Save" msgid "Save"
msgstr "" msgstr ""
#: bookwyrm/templates/book/book.html:126 bookwyrm/templates/book/book.html:175 #: bookwyrm/templates/book/book.html:129 bookwyrm/templates/book/book.html:178
#: bookwyrm/templates/book/cover_modal.html:32 #: bookwyrm/templates/book/cover_modal.html:32
#: bookwyrm/templates/book/edit_book.html:241 #: bookwyrm/templates/book/edit_book.html:241
#: bookwyrm/templates/edit_author.html:79 #: bookwyrm/templates/edit_author.html:79
@ -214,81 +218,81 @@ msgstr ""
msgid "Cancel" msgid "Cancel"
msgstr "" msgstr ""
#: bookwyrm/templates/book/book.html:135 #: bookwyrm/templates/book/book.html:138
#, python-format #, python-format
msgid "<a href=\"%(path)s/editions\">%(count)s editions</a>" msgid "<a href=\"%(path)s/editions\">%(count)s editions</a>"
msgstr "" msgstr ""
#: bookwyrm/templates/book/book.html:143 #: bookwyrm/templates/book/book.html:146
#, python-format #, python-format
msgid "This edition is on your <a href=\"%(path)s\">%(shelf_name)s</a> shelf." msgid "This edition is on your <a href=\"%(path)s\">%(shelf_name)s</a> shelf."
msgstr "" msgstr ""
#: bookwyrm/templates/book/book.html:149 #: bookwyrm/templates/book/book.html:152
#, python-format #, python-format
msgid "" msgid ""
"A <a href=\"%(book_path)s\">different edition</a> of this book is on your <a " "A <a href=\"%(book_path)s\">different edition</a> of this book is on your <a "
"href=\"%(shelf_path)s\">%(shelf_name)s</a> shelf." "href=\"%(shelf_path)s\">%(shelf_name)s</a> shelf."
msgstr "" msgstr ""
#: bookwyrm/templates/book/book.html:158 #: bookwyrm/templates/book/book.html:161
msgid "Your reading activity" msgid "Your reading activity"
msgstr "" msgstr ""
#: bookwyrm/templates/book/book.html:160 #: bookwyrm/templates/book/book.html:163
msgid "Add read dates" msgid "Add read dates"
msgstr "" msgstr ""
#: bookwyrm/templates/book/book.html:165 #: bookwyrm/templates/book/book.html:168
msgid "You don't have any reading activity for this book." msgid "You don't have any reading activity for this book."
msgstr "" msgstr ""
#: bookwyrm/templates/book/book.html:172 #: bookwyrm/templates/book/book.html:175
msgid "Create" msgid "Create"
msgstr "" msgstr ""
#: bookwyrm/templates/book/book.html:194 #: bookwyrm/templates/book/book.html:197
msgid "Subjects" msgid "Subjects"
msgstr "" msgstr ""
#: bookwyrm/templates/book/book.html:206 #: bookwyrm/templates/book/book.html:209
msgid "Places" msgid "Places"
msgstr "" msgstr ""
#: bookwyrm/templates/book/book.html:217 bookwyrm/templates/layout.html:65 #: bookwyrm/templates/book/book.html:220 bookwyrm/templates/layout.html:65
#: bookwyrm/templates/lists/lists.html:5 bookwyrm/templates/lists/lists.html:12 #: bookwyrm/templates/lists/lists.html:5 bookwyrm/templates/lists/lists.html:12
#: bookwyrm/templates/search_results.html:91 #: bookwyrm/templates/search_results.html:91
#: bookwyrm/templates/user/user_layout.html:62 #: bookwyrm/templates/user/user_layout.html:62
msgid "Lists" msgid "Lists"
msgstr "" msgstr ""
#: bookwyrm/templates/book/book.html:228 #: bookwyrm/templates/book/book.html:231
msgid "Add to list" msgid "Add to list"
msgstr "" msgstr ""
#: bookwyrm/templates/book/book.html:238 #: bookwyrm/templates/book/book.html:241
#: bookwyrm/templates/book/cover_modal.html:31 #: bookwyrm/templates/book/cover_modal.html:31
#: bookwyrm/templates/lists/list.html:133 #: bookwyrm/templates/lists/list.html:133
msgid "Add" msgid "Add"
msgstr "" msgstr ""
#: bookwyrm/templates/book/book.html:254 #: bookwyrm/templates/book/book.html:257
msgid "Reviews" msgid "Reviews"
msgstr "" msgstr ""
#: bookwyrm/templates/book/book.html:259 #: bookwyrm/templates/book/book.html:262
msgid "Your reviews" msgid "Your reviews"
msgstr "" msgstr ""
#: bookwyrm/templates/book/book.html:265 #: bookwyrm/templates/book/book.html:268
msgid "Your comments" msgid "Your comments"
msgstr "" msgstr ""
#: bookwyrm/templates/book/book.html:271 #: bookwyrm/templates/book/book.html:274
msgid "Your quotes" msgid "Your quotes"
msgstr "" msgstr ""
#: bookwyrm/templates/book/book.html:305 #: bookwyrm/templates/book/book.html:308
msgid "rated it" msgid "rated it"
msgstr "" msgstr ""
@ -2481,12 +2485,7 @@ msgstr ""
msgid "quoted" msgid "quoted"
msgstr "" msgstr ""
#: bookwyrm/templates/snippets/search_result_text.html:22 #: bookwyrm/templates/snippets/search_result_text.html:35
#, python-format
msgid "by %(author)s"
msgstr ""
#: bookwyrm/templates/snippets/search_result_text.html:30
msgid "Import book" msgid "Import book"
msgstr "" msgstr ""

Binary file not shown.

View file

@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: 0.0.1\n" "Project-Id-Version: 0.0.1\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-04-26 09:56-0700\n" "POT-Creation-Date: 2021-04-29 11:36-0700\n"
"PO-Revision-Date: 2021-03-19 11:49+0800\n" "PO-Revision-Date: 2021-03-19 11:49+0800\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
@ -90,23 +90,23 @@ msgstr "nombre de usuario"
msgid "A user with that username already exists." msgid "A user with that username already exists."
msgstr "Ya existe un usuario con ese nombre." msgstr "Ya existe un usuario con ese nombre."
#: bookwyrm/settings.py:152 #: bookwyrm/settings.py:155
msgid "English" msgid "English"
msgstr "Inglés" msgstr "Inglés"
#: bookwyrm/settings.py:153 #: bookwyrm/settings.py:156
msgid "German" msgid "German"
msgstr "Aléman" msgstr "Aléman"
#: bookwyrm/settings.py:154 #: bookwyrm/settings.py:157
msgid "Spanish" msgid "Spanish"
msgstr "Español" msgstr "Español"
#: bookwyrm/settings.py:155 #: bookwyrm/settings.py:158
msgid "French" msgid "French"
msgstr "Francés" msgstr "Francés"
#: bookwyrm/settings.py:156 #: bookwyrm/settings.py:159
msgid "Simplified Chinese" msgid "Simplified Chinese"
msgstr "Chino simplificado" msgstr "Chino simplificado"
@ -166,24 +166,30 @@ msgstr "No se pudo cargar la portada"
msgid "View on OpenLibrary" msgid "View on OpenLibrary"
msgstr "Ver en OpenLibrary" msgstr "Ver en OpenLibrary"
#: bookwyrm/templates/book/book.html:102 #: bookwyrm/templates/book/book.html:85
#, fuzzy
#| msgid "View on OpenLibrary"
msgid "View on Inventaire"
msgstr "Ver en OpenLibrary"
#: bookwyrm/templates/book/book.html:105
#, python-format #, python-format
msgid "(%(review_count)s review)" msgid "(%(review_count)s review)"
msgid_plural "(%(review_count)s reviews)" msgid_plural "(%(review_count)s reviews)"
msgstr[0] "(%(review_count)s reseña)" msgstr[0] "(%(review_count)s reseña)"
msgstr[1] "(%(review_count)s reseñas)" msgstr[1] "(%(review_count)s reseñas)"
#: bookwyrm/templates/book/book.html:114 #: bookwyrm/templates/book/book.html:117
msgid "Add Description" msgid "Add Description"
msgstr "Agregar descripción" msgstr "Agregar descripción"
#: bookwyrm/templates/book/book.html:121 #: bookwyrm/templates/book/book.html:124
#: bookwyrm/templates/book/edit_book.html:107 #: bookwyrm/templates/book/edit_book.html:107
#: bookwyrm/templates/lists/form.html:12 #: bookwyrm/templates/lists/form.html:12
msgid "Description:" msgid "Description:"
msgstr "Descripción:" msgstr "Descripción:"
#: bookwyrm/templates/book/book.html:125 #: bookwyrm/templates/book/book.html:128
#: bookwyrm/templates/book/edit_book.html:240 #: bookwyrm/templates/book/edit_book.html:240
#: bookwyrm/templates/edit_author.html:78 bookwyrm/templates/lists/form.html:42 #: bookwyrm/templates/edit_author.html:78 bookwyrm/templates/lists/form.html:42
#: bookwyrm/templates/preferences/edit_user.html:70 #: bookwyrm/templates/preferences/edit_user.html:70
@ -198,7 +204,7 @@ msgstr "Descripción:"
msgid "Save" msgid "Save"
msgstr "Guardar" msgstr "Guardar"
#: bookwyrm/templates/book/book.html:126 bookwyrm/templates/book/book.html:175 #: bookwyrm/templates/book/book.html:129 bookwyrm/templates/book/book.html:178
#: bookwyrm/templates/book/cover_modal.html:32 #: bookwyrm/templates/book/cover_modal.html:32
#: bookwyrm/templates/book/edit_book.html:241 #: bookwyrm/templates/book/edit_book.html:241
#: bookwyrm/templates/edit_author.html:79 #: bookwyrm/templates/edit_author.html:79
@ -214,18 +220,18 @@ msgstr "Guardar"
msgid "Cancel" msgid "Cancel"
msgstr "Cancelar" msgstr "Cancelar"
#: bookwyrm/templates/book/book.html:135 #: bookwyrm/templates/book/book.html:138
#, python-format #, python-format
msgid "<a href=\"%(path)s/editions\">%(count)s editions</a>" msgid "<a href=\"%(path)s/editions\">%(count)s editions</a>"
msgstr "<a href=\"%(path)s/editions\">%(count)s ediciones</a>" msgstr "<a href=\"%(path)s/editions\">%(count)s ediciones</a>"
#: bookwyrm/templates/book/book.html:143 #: bookwyrm/templates/book/book.html:146
#, python-format #, python-format
msgid "This edition is on your <a href=\"%(path)s\">%(shelf_name)s</a> shelf." msgid "This edition is on your <a href=\"%(path)s\">%(shelf_name)s</a> shelf."
msgstr "" msgstr ""
"Esta edición está en tu <a href=\"%(path)s\">%(shelf_name)s</a> estante." "Esta edición está en tu <a href=\"%(path)s\">%(shelf_name)s</a> estante."
#: bookwyrm/templates/book/book.html:149 #: bookwyrm/templates/book/book.html:152
#, python-format #, python-format
msgid "" msgid ""
"A <a href=\"%(book_path)s\">different edition</a> of this book is on your <a " "A <a href=\"%(book_path)s\">different edition</a> of this book is on your <a "
@ -234,72 +240,72 @@ msgstr ""
"Una <a href=\"%(book_path)s\">edición diferente</a> de este libro está en tu " "Una <a href=\"%(book_path)s\">edición diferente</a> de este libro está en tu "
"<a href=\"%(shelf_path)s\">%(shelf_name)s</a> estante." "<a href=\"%(shelf_path)s\">%(shelf_name)s</a> estante."
#: bookwyrm/templates/book/book.html:158 #: bookwyrm/templates/book/book.html:161
msgid "Your reading activity" msgid "Your reading activity"
msgstr "Tu actividad de lectura" msgstr "Tu actividad de lectura"
#: bookwyrm/templates/book/book.html:160 #: bookwyrm/templates/book/book.html:163
msgid "Add read dates" msgid "Add read dates"
msgstr "Agregar fechas de lectura" msgstr "Agregar fechas de lectura"
#: bookwyrm/templates/book/book.html:165 #: bookwyrm/templates/book/book.html:168
msgid "You don't have any reading activity for this book." msgid "You don't have any reading activity for this book."
msgstr "No tienes ninguna actividad de lectura para este libro." msgstr "No tienes ninguna actividad de lectura para este libro."
#: bookwyrm/templates/book/book.html:172 #: bookwyrm/templates/book/book.html:175
msgid "Create" msgid "Create"
msgstr "Crear" msgstr "Crear"
#: bookwyrm/templates/book/book.html:194 #: bookwyrm/templates/book/book.html:197
msgid "Subjects" msgid "Subjects"
msgstr "Sujetos" msgstr "Sujetos"
#: bookwyrm/templates/book/book.html:206 #: bookwyrm/templates/book/book.html:209
msgid "Places" msgid "Places"
msgstr "Lugares" msgstr "Lugares"
#: bookwyrm/templates/book/book.html:217 bookwyrm/templates/layout.html:65 #: bookwyrm/templates/book/book.html:220 bookwyrm/templates/layout.html:65
#: bookwyrm/templates/lists/lists.html:5 bookwyrm/templates/lists/lists.html:12 #: bookwyrm/templates/lists/lists.html:5 bookwyrm/templates/lists/lists.html:12
#: bookwyrm/templates/search_results.html:91 #: bookwyrm/templates/search_results.html:91
#: bookwyrm/templates/user/user_layout.html:62 #: bookwyrm/templates/user/user_layout.html:62
msgid "Lists" msgid "Lists"
msgstr "Listas" msgstr "Listas"
#: bookwyrm/templates/book/book.html:228 #: bookwyrm/templates/book/book.html:231
msgid "Add to list" msgid "Add to list"
msgstr "Agregar a lista" msgstr "Agregar a lista"
#: bookwyrm/templates/book/book.html:238 #: bookwyrm/templates/book/book.html:241
#: bookwyrm/templates/book/cover_modal.html:31 #: bookwyrm/templates/book/cover_modal.html:31
#: bookwyrm/templates/lists/list.html:133 #: bookwyrm/templates/lists/list.html:133
msgid "Add" msgid "Add"
msgstr "Agregar" msgstr "Agregar"
#: bookwyrm/templates/book/book.html:254 #: bookwyrm/templates/book/book.html:257
#, fuzzy #, fuzzy
#| msgid "Review" #| msgid "Review"
msgid "Reviews" msgid "Reviews"
msgstr "Reseña" msgstr "Reseña"
#: bookwyrm/templates/book/book.html:259 #: bookwyrm/templates/book/book.html:262
#, fuzzy #, fuzzy
#| msgid "Your shelves" #| msgid "Your shelves"
msgid "Your reviews" msgid "Your reviews"
msgstr "Tus estantes" msgstr "Tus estantes"
#: bookwyrm/templates/book/book.html:265 #: bookwyrm/templates/book/book.html:268
#, fuzzy #, fuzzy
#| msgid "Your Account" #| msgid "Your Account"
msgid "Your comments" msgid "Your comments"
msgstr "Tu cuenta" msgstr "Tu cuenta"
#: bookwyrm/templates/book/book.html:271 #: bookwyrm/templates/book/book.html:274
#, fuzzy #, fuzzy
#| msgid "Your books" #| msgid "Your books"
msgid "Your quotes" msgid "Your quotes"
msgstr "Tus libros" msgstr "Tus libros"
#: bookwyrm/templates/book/book.html:305 #: bookwyrm/templates/book/book.html:308
msgid "rated it" msgid "rated it"
msgstr "lo calificó con" msgstr "lo calificó con"
@ -2571,12 +2577,7 @@ msgstr "comentó en"
msgid "quoted" msgid "quoted"
msgstr "citó" msgstr "citó"
#: bookwyrm/templates/snippets/search_result_text.html:22 #: bookwyrm/templates/snippets/search_result_text.html:35
#, python-format
msgid "by %(author)s"
msgstr "por %(author)s"
#: bookwyrm/templates/snippets/search_result_text.html:30
msgid "Import book" msgid "Import book"
msgstr "Importar libro" msgstr "Importar libro"
@ -4299,6 +4300,10 @@ msgctxt "stick"
msgid "club" msgid "club"
msgstr "garrote" msgstr "garrote"
#, python-format
#~ msgid "by %(author)s"
#~ msgstr "por %(author)s"
#, python-format #, python-format
#~ msgid "%(rating)s star" #~ msgid "%(rating)s star"
#~ msgid_plural "%(rating)s stars" #~ msgid_plural "%(rating)s stars"

Binary file not shown.

View file

@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: 0.1.1\n" "Project-Id-Version: 0.1.1\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-04-26 09:56-0700\n" "POT-Creation-Date: 2021-04-29 11:36-0700\n"
"PO-Revision-Date: 2021-04-05 12:44+0100\n" "PO-Revision-Date: 2021-04-05 12:44+0100\n"
"Last-Translator: Fabien Basmaison <contact@arkhi.org>\n" "Last-Translator: Fabien Basmaison <contact@arkhi.org>\n"
"Language-Team: Mouse Reeve <LL@li.org>\n" "Language-Team: Mouse Reeve <LL@li.org>\n"
@ -96,23 +96,23 @@ msgstr "nom du compte:"
msgid "A user with that username already exists." msgid "A user with that username already exists."
msgstr "Ce nom est déjà associé à un compte." msgstr "Ce nom est déjà associé à un compte."
#: bookwyrm/settings.py:152 #: bookwyrm/settings.py:155
msgid "English" msgid "English"
msgstr "English" msgstr "English"
#: bookwyrm/settings.py:153 #: bookwyrm/settings.py:156
msgid "German" msgid "German"
msgstr "Deutsch" msgstr "Deutsch"
#: bookwyrm/settings.py:154 #: bookwyrm/settings.py:157
msgid "Spanish" msgid "Spanish"
msgstr "Español" msgstr "Español"
#: bookwyrm/settings.py:155 #: bookwyrm/settings.py:158
msgid "French" msgid "French"
msgstr "Français" msgstr "Français"
#: bookwyrm/settings.py:156 #: bookwyrm/settings.py:159
msgid "Simplified Chinese" msgid "Simplified Chinese"
msgstr "简化字" msgstr "简化字"
@ -172,24 +172,30 @@ msgstr "La couverture na pu être chargée"
msgid "View on OpenLibrary" msgid "View on OpenLibrary"
msgstr "Voir sur OpenLibrary" msgstr "Voir sur OpenLibrary"
#: bookwyrm/templates/book/book.html:102 #: bookwyrm/templates/book/book.html:85
#, fuzzy
#| msgid "View on OpenLibrary"
msgid "View on Inventaire"
msgstr "Voir sur OpenLibrary"
#: bookwyrm/templates/book/book.html:105
#, python-format #, python-format
msgid "(%(review_count)s review)" msgid "(%(review_count)s review)"
msgid_plural "(%(review_count)s reviews)" msgid_plural "(%(review_count)s reviews)"
msgstr[0] "(%(review_count)s critique)" msgstr[0] "(%(review_count)s critique)"
msgstr[1] "(%(review_count)s critiques)" msgstr[1] "(%(review_count)s critiques)"
#: bookwyrm/templates/book/book.html:114 #: bookwyrm/templates/book/book.html:117
msgid "Add Description" msgid "Add Description"
msgstr "Ajouter une description" msgstr "Ajouter une description"
#: bookwyrm/templates/book/book.html:121 #: bookwyrm/templates/book/book.html:124
#: bookwyrm/templates/book/edit_book.html:107 #: bookwyrm/templates/book/edit_book.html:107
#: bookwyrm/templates/lists/form.html:12 #: bookwyrm/templates/lists/form.html:12
msgid "Description:" msgid "Description:"
msgstr "Description:" msgstr "Description:"
#: bookwyrm/templates/book/book.html:125 #: bookwyrm/templates/book/book.html:128
#: bookwyrm/templates/book/edit_book.html:240 #: bookwyrm/templates/book/edit_book.html:240
#: bookwyrm/templates/edit_author.html:78 bookwyrm/templates/lists/form.html:42 #: bookwyrm/templates/edit_author.html:78 bookwyrm/templates/lists/form.html:42
#: bookwyrm/templates/preferences/edit_user.html:70 #: bookwyrm/templates/preferences/edit_user.html:70
@ -204,7 +210,7 @@ msgstr "Description:"
msgid "Save" msgid "Save"
msgstr "Enregistrer" msgstr "Enregistrer"
#: bookwyrm/templates/book/book.html:126 bookwyrm/templates/book/book.html:175 #: bookwyrm/templates/book/book.html:129 bookwyrm/templates/book/book.html:178
#: bookwyrm/templates/book/cover_modal.html:32 #: bookwyrm/templates/book/cover_modal.html:32
#: bookwyrm/templates/book/edit_book.html:241 #: bookwyrm/templates/book/edit_book.html:241
#: bookwyrm/templates/edit_author.html:79 #: bookwyrm/templates/edit_author.html:79
@ -220,18 +226,18 @@ msgstr "Enregistrer"
msgid "Cancel" msgid "Cancel"
msgstr "Annuler" msgstr "Annuler"
#: bookwyrm/templates/book/book.html:135 #: bookwyrm/templates/book/book.html:138
#, python-format #, python-format
msgid "<a href=\"%(path)s/editions\">%(count)s editions</a>" msgid "<a href=\"%(path)s/editions\">%(count)s editions</a>"
msgstr "<a href=\"%(path)s/editions\">%(count)s éditions</a>" msgstr "<a href=\"%(path)s/editions\">%(count)s éditions</a>"
#: bookwyrm/templates/book/book.html:143 #: bookwyrm/templates/book/book.html:146
#, python-format #, python-format
msgid "This edition is on your <a href=\"%(path)s\">%(shelf_name)s</a> shelf." msgid "This edition is on your <a href=\"%(path)s\">%(shelf_name)s</a> shelf."
msgstr "" msgstr ""
"Cette édition est sur votre étagère <a href=\"%(path)s\">%(shelf_name)s</a>." "Cette édition est sur votre étagère <a href=\"%(path)s\">%(shelf_name)s</a>."
#: bookwyrm/templates/book/book.html:149 #: bookwyrm/templates/book/book.html:152
#, python-format #, python-format
msgid "" msgid ""
"A <a href=\"%(book_path)s\">different edition</a> of this book is on your <a " "A <a href=\"%(book_path)s\">different edition</a> of this book is on your <a "
@ -240,72 +246,72 @@ msgstr ""
"Une <a href=\"%(book_path)s\">édition différente</a> de ce livre existe sur " "Une <a href=\"%(book_path)s\">édition différente</a> de ce livre existe sur "
"votre étagère <a href=\"%(shelf_path)s\">%(shelf_name)s</a>." "votre étagère <a href=\"%(shelf_path)s\">%(shelf_name)s</a>."
#: bookwyrm/templates/book/book.html:158 #: bookwyrm/templates/book/book.html:161
msgid "Your reading activity" msgid "Your reading activity"
msgstr "Votre activité de lecture" msgstr "Votre activité de lecture"
#: bookwyrm/templates/book/book.html:160 #: bookwyrm/templates/book/book.html:163
msgid "Add read dates" msgid "Add read dates"
msgstr "Ajouter des dates de lecture" msgstr "Ajouter des dates de lecture"
#: bookwyrm/templates/book/book.html:165 #: bookwyrm/templates/book/book.html:168
msgid "You don't have any reading activity for this book." msgid "You don't have any reading activity for this book."
msgstr "Vous navez aucune activité de lecture pour ce livre" msgstr "Vous navez aucune activité de lecture pour ce livre"
#: bookwyrm/templates/book/book.html:172 #: bookwyrm/templates/book/book.html:175
msgid "Create" msgid "Create"
msgstr "Créer" msgstr "Créer"
#: bookwyrm/templates/book/book.html:194 #: bookwyrm/templates/book/book.html:197
msgid "Subjects" msgid "Subjects"
msgstr "Sujets" msgstr "Sujets"
#: bookwyrm/templates/book/book.html:206 #: bookwyrm/templates/book/book.html:209
msgid "Places" msgid "Places"
msgstr "Lieux" msgstr "Lieux"
#: bookwyrm/templates/book/book.html:217 bookwyrm/templates/layout.html:65 #: bookwyrm/templates/book/book.html:220 bookwyrm/templates/layout.html:65
#: bookwyrm/templates/lists/lists.html:5 bookwyrm/templates/lists/lists.html:12 #: bookwyrm/templates/lists/lists.html:5 bookwyrm/templates/lists/lists.html:12
#: bookwyrm/templates/search_results.html:91 #: bookwyrm/templates/search_results.html:91
#: bookwyrm/templates/user/user_layout.html:62 #: bookwyrm/templates/user/user_layout.html:62
msgid "Lists" msgid "Lists"
msgstr "Listes" msgstr "Listes"
#: bookwyrm/templates/book/book.html:228 #: bookwyrm/templates/book/book.html:231
msgid "Add to list" msgid "Add to list"
msgstr "Ajouter à la liste" msgstr "Ajouter à la liste"
#: bookwyrm/templates/book/book.html:238 #: bookwyrm/templates/book/book.html:241
#: bookwyrm/templates/book/cover_modal.html:31 #: bookwyrm/templates/book/cover_modal.html:31
#: bookwyrm/templates/lists/list.html:133 #: bookwyrm/templates/lists/list.html:133
msgid "Add" msgid "Add"
msgstr "Ajouter" msgstr "Ajouter"
#: bookwyrm/templates/book/book.html:254 #: bookwyrm/templates/book/book.html:257
#, fuzzy #, fuzzy
#| msgid "Review" #| msgid "Review"
msgid "Reviews" msgid "Reviews"
msgstr "Critique" msgstr "Critique"
#: bookwyrm/templates/book/book.html:259 #: bookwyrm/templates/book/book.html:262
#, fuzzy #, fuzzy
#| msgid "Your shelves" #| msgid "Your shelves"
msgid "Your reviews" msgid "Your reviews"
msgstr "Vos étagères" msgstr "Vos étagères"
#: bookwyrm/templates/book/book.html:265 #: bookwyrm/templates/book/book.html:268
#, fuzzy #, fuzzy
#| msgid "Your Account" #| msgid "Your Account"
msgid "Your comments" msgid "Your comments"
msgstr "Votre compte" msgstr "Votre compte"
#: bookwyrm/templates/book/book.html:271 #: bookwyrm/templates/book/book.html:274
#, fuzzy #, fuzzy
#| msgid "Your books" #| msgid "Your books"
msgid "Your quotes" msgid "Your quotes"
msgstr "Vos livres" msgstr "Vos livres"
#: bookwyrm/templates/book/book.html:305 #: bookwyrm/templates/book/book.html:308
msgid "rated it" msgid "rated it"
msgstr "la noté" msgstr "la noté"
@ -2605,12 +2611,7 @@ msgstr "a commenté"
msgid "quoted" msgid "quoted"
msgstr "a cité" msgstr "a cité"
#: bookwyrm/templates/snippets/search_result_text.html:22 #: bookwyrm/templates/snippets/search_result_text.html:35
#, python-format
msgid "by %(author)s"
msgstr "par %(author)s"
#: bookwyrm/templates/snippets/search_result_text.html:30
msgid "Import book" msgid "Import book"
msgstr "Importer le livre" msgstr "Importer le livre"
@ -4341,6 +4342,10 @@ msgctxt "stick"
msgid "club" msgid "club"
msgstr "" msgstr ""
#, python-format
#~ msgid "by %(author)s"
#~ msgstr "par %(author)s"
#~ msgid "Deactivate user" #~ msgid "Deactivate user"
#~ msgstr "Désactiver le compte" #~ msgstr "Désactiver le compte"

Binary file not shown.

View file

@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: 0.1.1\n" "Project-Id-Version: 0.1.1\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-04-26 09:56-0700\n" "POT-Creation-Date: 2021-04-29 11:36-0700\n"
"PO-Revision-Date: 2021-03-20 00:56+0000\n" "PO-Revision-Date: 2021-03-20 00:56+0000\n"
"Last-Translator: Kana <gudzpoz@live.com>\n" "Last-Translator: Kana <gudzpoz@live.com>\n"
"Language-Team: Mouse Reeve <LL@li.org>\n" "Language-Team: Mouse Reeve <LL@li.org>\n"
@ -96,23 +96,23 @@ msgstr "用户名"
msgid "A user with that username already exists." msgid "A user with that username already exists."
msgstr "已经存在使用该用户名的用户。" msgstr "已经存在使用该用户名的用户。"
#: bookwyrm/settings.py:152 #: bookwyrm/settings.py:155
msgid "English" msgid "English"
msgstr "English英语" msgstr "English英语"
#: bookwyrm/settings.py:153 #: bookwyrm/settings.py:156
msgid "German" msgid "German"
msgstr "Deutsch德语" msgstr "Deutsch德语"
#: bookwyrm/settings.py:154 #: bookwyrm/settings.py:157
msgid "Spanish" msgid "Spanish"
msgstr "Español西班牙语" msgstr "Español西班牙语"
#: bookwyrm/settings.py:155 #: bookwyrm/settings.py:158
msgid "French" msgid "French"
msgstr "Français法语" msgstr "Français法语"
#: bookwyrm/settings.py:156 #: bookwyrm/settings.py:159
msgid "Simplified Chinese" msgid "Simplified Chinese"
msgstr "简体中文" msgstr "简体中文"
@ -172,23 +172,29 @@ msgstr "加载封面失败"
msgid "View on OpenLibrary" msgid "View on OpenLibrary"
msgstr "在 OpenLibrary 查看" msgstr "在 OpenLibrary 查看"
#: bookwyrm/templates/book/book.html:102 #: bookwyrm/templates/book/book.html:85
#, fuzzy
#| msgid "View on OpenLibrary"
msgid "View on Inventaire"
msgstr "在 OpenLibrary 查看"
#: bookwyrm/templates/book/book.html:105
#, python-format #, python-format
msgid "(%(review_count)s review)" msgid "(%(review_count)s review)"
msgid_plural "(%(review_count)s reviews)" msgid_plural "(%(review_count)s reviews)"
msgstr[0] "(%(review_count)s 则书评)" msgstr[0] "(%(review_count)s 则书评)"
#: bookwyrm/templates/book/book.html:114 #: bookwyrm/templates/book/book.html:117
msgid "Add Description" msgid "Add Description"
msgstr "添加描述" msgstr "添加描述"
#: bookwyrm/templates/book/book.html:121 #: bookwyrm/templates/book/book.html:124
#: bookwyrm/templates/book/edit_book.html:107 #: bookwyrm/templates/book/edit_book.html:107
#: bookwyrm/templates/lists/form.html:12 #: bookwyrm/templates/lists/form.html:12
msgid "Description:" msgid "Description:"
msgstr "描述:" msgstr "描述:"
#: bookwyrm/templates/book/book.html:125 #: bookwyrm/templates/book/book.html:128
#: bookwyrm/templates/book/edit_book.html:240 #: bookwyrm/templates/book/edit_book.html:240
#: bookwyrm/templates/edit_author.html:78 bookwyrm/templates/lists/form.html:42 #: bookwyrm/templates/edit_author.html:78 bookwyrm/templates/lists/form.html:42
#: bookwyrm/templates/preferences/edit_user.html:70 #: bookwyrm/templates/preferences/edit_user.html:70
@ -203,7 +209,7 @@ msgstr "描述:"
msgid "Save" msgid "Save"
msgstr "保存" msgstr "保存"
#: bookwyrm/templates/book/book.html:126 bookwyrm/templates/book/book.html:175 #: bookwyrm/templates/book/book.html:129 bookwyrm/templates/book/book.html:178
#: bookwyrm/templates/book/cover_modal.html:32 #: bookwyrm/templates/book/cover_modal.html:32
#: bookwyrm/templates/book/edit_book.html:241 #: bookwyrm/templates/book/edit_book.html:241
#: bookwyrm/templates/edit_author.html:79 #: bookwyrm/templates/edit_author.html:79
@ -219,17 +225,17 @@ msgstr "保存"
msgid "Cancel" msgid "Cancel"
msgstr "取消" msgstr "取消"
#: bookwyrm/templates/book/book.html:135 #: bookwyrm/templates/book/book.html:138
#, python-format #, python-format
msgid "<a href=\"%(path)s/editions\">%(count)s editions</a>" msgid "<a href=\"%(path)s/editions\">%(count)s editions</a>"
msgstr "<a href=\"%(path)s/editions\">%(count)s 个版本</a>" msgstr "<a href=\"%(path)s/editions\">%(count)s 个版本</a>"
#: bookwyrm/templates/book/book.html:143 #: bookwyrm/templates/book/book.html:146
#, python-format #, python-format
msgid "This edition is on your <a href=\"%(path)s\">%(shelf_name)s</a> shelf." msgid "This edition is on your <a href=\"%(path)s\">%(shelf_name)s</a> shelf."
msgstr "此版本在你的 <a href=\"%(path)s\">%(shelf_name)s</a> 书架上。" msgstr "此版本在你的 <a href=\"%(path)s\">%(shelf_name)s</a> 书架上。"
#: bookwyrm/templates/book/book.html:149 #: bookwyrm/templates/book/book.html:152
#, python-format #, python-format
msgid "" msgid ""
"A <a href=\"%(book_path)s\">different edition</a> of this book is on your <a " "A <a href=\"%(book_path)s\">different edition</a> of this book is on your <a "
@ -238,72 +244,72 @@ msgstr ""
"本书的 <a href=\"%(book_path)s\">另一个版本</a> 在你的 <a href=" "本书的 <a href=\"%(book_path)s\">另一个版本</a> 在你的 <a href="
"\"%(shelf_path)s\">%(shelf_name)s</a> 书架上。" "\"%(shelf_path)s\">%(shelf_name)s</a> 书架上。"
#: bookwyrm/templates/book/book.html:158 #: bookwyrm/templates/book/book.html:161
msgid "Your reading activity" msgid "Your reading activity"
msgstr "你的阅读活动" msgstr "你的阅读活动"
#: bookwyrm/templates/book/book.html:160 #: bookwyrm/templates/book/book.html:163
msgid "Add read dates" msgid "Add read dates"
msgstr "添加阅读日期" msgstr "添加阅读日期"
#: bookwyrm/templates/book/book.html:165 #: bookwyrm/templates/book/book.html:168
msgid "You don't have any reading activity for this book." msgid "You don't have any reading activity for this book."
msgstr "你还没有任何这本书的阅读活动。" msgstr "你还没有任何这本书的阅读活动。"
#: bookwyrm/templates/book/book.html:172 #: bookwyrm/templates/book/book.html:175
msgid "Create" msgid "Create"
msgstr "创建" msgstr "创建"
#: bookwyrm/templates/book/book.html:194 #: bookwyrm/templates/book/book.html:197
msgid "Subjects" msgid "Subjects"
msgstr "主题" msgstr "主题"
#: bookwyrm/templates/book/book.html:206 #: bookwyrm/templates/book/book.html:209
msgid "Places" msgid "Places"
msgstr "地点" msgstr "地点"
#: bookwyrm/templates/book/book.html:217 bookwyrm/templates/layout.html:65 #: bookwyrm/templates/book/book.html:220 bookwyrm/templates/layout.html:65
#: bookwyrm/templates/lists/lists.html:5 bookwyrm/templates/lists/lists.html:12 #: bookwyrm/templates/lists/lists.html:5 bookwyrm/templates/lists/lists.html:12
#: bookwyrm/templates/search_results.html:91 #: bookwyrm/templates/search_results.html:91
#: bookwyrm/templates/user/user_layout.html:62 #: bookwyrm/templates/user/user_layout.html:62
msgid "Lists" msgid "Lists"
msgstr "列表" msgstr "列表"
#: bookwyrm/templates/book/book.html:228 #: bookwyrm/templates/book/book.html:231
msgid "Add to list" msgid "Add to list"
msgstr "添加到列表" msgstr "添加到列表"
#: bookwyrm/templates/book/book.html:238 #: bookwyrm/templates/book/book.html:241
#: bookwyrm/templates/book/cover_modal.html:31 #: bookwyrm/templates/book/cover_modal.html:31
#: bookwyrm/templates/lists/list.html:133 #: bookwyrm/templates/lists/list.html:133
msgid "Add" msgid "Add"
msgstr "添加" msgstr "添加"
#: bookwyrm/templates/book/book.html:254 #: bookwyrm/templates/book/book.html:257
#, fuzzy #, fuzzy
#| msgid "Review" #| msgid "Review"
msgid "Reviews" msgid "Reviews"
msgstr "书评" msgstr "书评"
#: bookwyrm/templates/book/book.html:259 #: bookwyrm/templates/book/book.html:262
#, fuzzy #, fuzzy
#| msgid "Your shelves" #| msgid "Your shelves"
msgid "Your reviews" msgid "Your reviews"
msgstr "你的书架" msgstr "你的书架"
#: bookwyrm/templates/book/book.html:265 #: bookwyrm/templates/book/book.html:268
#, fuzzy #, fuzzy
#| msgid "Your Account" #| msgid "Your Account"
msgid "Your comments" msgid "Your comments"
msgstr "你的帐号" msgstr "你的帐号"
#: bookwyrm/templates/book/book.html:271 #: bookwyrm/templates/book/book.html:274
#, fuzzy #, fuzzy
#| msgid "Your books" #| msgid "Your books"
msgid "Your quotes" msgid "Your quotes"
msgstr "你的书目" msgstr "你的书目"
#: bookwyrm/templates/book/book.html:305 #: bookwyrm/templates/book/book.html:308
msgid "rated it" msgid "rated it"
msgstr "评价了" msgstr "评价了"
@ -2570,12 +2576,7 @@ msgstr "评论了"
msgid "quoted" msgid "quoted"
msgstr "引用了" msgstr "引用了"
#: bookwyrm/templates/snippets/search_result_text.html:22 #: bookwyrm/templates/snippets/search_result_text.html:35
#, python-format
msgid "by %(author)s"
msgstr "由 %(author)s 所著"
#: bookwyrm/templates/snippets/search_result_text.html:30
msgid "Import book" msgid "Import book"
msgstr "导入书目" msgstr "导入书目"
@ -4292,6 +4293,10 @@ msgctxt "stick"
msgid "club" msgid "club"
msgstr "" msgstr ""
#, python-format
#~ msgid "by %(author)s"
#~ msgstr "由 %(author)s 所著"
#~ msgid "Deactivate user" #~ msgid "Deactivate user"
#~ msgstr "停用用户" #~ msgstr "停用用户"