mirror of https://gitlab.com/bashrc2/epicyon
Store book information from incoming bookwyrm notes
parent
db4f77e52d
commit
72b1ab9efe
9
inbox.py
9
inbox.py
|
@ -162,6 +162,7 @@ from maps import get_map_links_from_post_content
|
|||
from maps import get_location_from_post
|
||||
from maps import add_tag_map_links
|
||||
from maps import geocoords_from_map_link
|
||||
from reading import store_book_events
|
||||
|
||||
|
||||
def cache_svg_images(session, base_dir: str, http_prefix: str,
|
||||
|
@ -4759,6 +4760,14 @@ def _inbox_after_initial(server, inbox_start_time,
|
|||
debug)
|
||||
inbox_start_time = time.time()
|
||||
|
||||
# store any bookwyrm type notes
|
||||
store_book_events(base_dir,
|
||||
message_json,
|
||||
system_language,
|
||||
languages_understood,
|
||||
translate, debug,
|
||||
1000)
|
||||
|
||||
if _receive_announce(recent_posts_cache,
|
||||
session, handle, is_group,
|
||||
base_dir, http_prefix,
|
||||
|
|
|
@ -0,0 +1,294 @@
|
|||
__filename__ = "reading.py"
|
||||
__author__ = "Bob Mottram"
|
||||
__license__ = "AGPL3+"
|
||||
__version__ = "1.4.0"
|
||||
__maintainer__ = "Bob Mottram"
|
||||
__email__ = "bob@libreserver.org"
|
||||
__status__ = "Production"
|
||||
__module_group__ = "Core"
|
||||
|
||||
|
||||
import os
|
||||
from utils import get_content_from_post
|
||||
from utils import has_object_dict
|
||||
from utils import remove_id_ending
|
||||
from utils import get_attributed_to
|
||||
from utils import load_json
|
||||
from utils import save_json
|
||||
from utils import remove_html
|
||||
|
||||
|
||||
def get_book_link_from_content(content: str) -> str:
|
||||
""" Returns a book link from the given content
|
||||
"""
|
||||
if '/book/' not in content or \
|
||||
'://' not in content or \
|
||||
'"' not in content:
|
||||
return None
|
||||
sections = content.split('/book/')
|
||||
if '"' not in sections[0] or '"' not in sections[1]:
|
||||
return None
|
||||
previous_str = sections[0].split('"')[-1]
|
||||
if '://' not in previous_str:
|
||||
return None
|
||||
next_str = sections[1].split('"')[0]
|
||||
book_url = previous_str + '/book/' + next_str
|
||||
return book_url
|
||||
|
||||
|
||||
def get_book_from_post(post_json_object: {}) -> {}:
|
||||
""" Returns a book details from the given post
|
||||
"""
|
||||
if 'tag' not in post_json_object:
|
||||
return {}
|
||||
if not isinstance(post_json_object['tag'], list):
|
||||
return {}
|
||||
for tag_dict in post_json_object['tag']:
|
||||
if 'type' not in tag_dict:
|
||||
continue
|
||||
if not isinstance(tag_dict['type'], str):
|
||||
continue
|
||||
if tag_dict['type'] != 'Edition':
|
||||
continue
|
||||
if not tag_dict.get('href'):
|
||||
continue
|
||||
if not isinstance(tag_dict['href'], str):
|
||||
continue
|
||||
if not tag_dict.get('name'):
|
||||
continue
|
||||
if not isinstance(tag_dict['name'], str):
|
||||
continue
|
||||
tag_dict['name'] = tag_dict['name'].replace('@', '')
|
||||
return tag_dict
|
||||
return {}
|
||||
|
||||
|
||||
def get_reading_status(post_json_object: {},
|
||||
system_language: str,
|
||||
languages_understood: [],
|
||||
translate: {}) -> {}:
|
||||
"""Returns any reading status from the content of a post
|
||||
"""
|
||||
post_obj = post_json_object
|
||||
if has_object_dict(post_json_object):
|
||||
post_obj = post_json_object['object']
|
||||
|
||||
content = get_content_from_post(post_json_object, system_language,
|
||||
languages_understood,
|
||||
"content")
|
||||
if not content:
|
||||
return {}
|
||||
book_url = get_book_link_from_content(content)
|
||||
if not book_url:
|
||||
return {}
|
||||
|
||||
if not post_obj.get('id'):
|
||||
return {}
|
||||
if not isinstance(post_obj['id'], str):
|
||||
return {}
|
||||
|
||||
# get the published date
|
||||
if not post_obj.get('published'):
|
||||
return {}
|
||||
if not isinstance(post_obj['published'], str):
|
||||
return {}
|
||||
published = post_obj['published']
|
||||
if post_obj.get('updated'):
|
||||
if isinstance(post_obj['updated'], str):
|
||||
published = post_obj['updated']
|
||||
|
||||
if not post_obj.get('attributedTo'):
|
||||
return {}
|
||||
actor = get_attributed_to(post_obj['attributedTo'])
|
||||
if not actor:
|
||||
return {}
|
||||
|
||||
# rating of a book
|
||||
if post_obj.get('rating'):
|
||||
rating = post_obj['rating']
|
||||
if isinstance(rating, (float, int)):
|
||||
translated_str = 'rated'
|
||||
if translate.get('rated'):
|
||||
translated_str = translate['rated']
|
||||
if translated_str in content or \
|
||||
'rated' in content:
|
||||
return {
|
||||
'id': remove_id_ending(post_obj['id']),
|
||||
'actor': actor,
|
||||
'type': 'rated',
|
||||
'href': book_url,
|
||||
'rating': rating,
|
||||
'published': published
|
||||
}
|
||||
|
||||
# get the book details from a post tag
|
||||
book_dict = get_book_from_post(post_json_object)
|
||||
if not book_dict:
|
||||
return {}
|
||||
|
||||
# want to read a book
|
||||
translated_str = 'wants to read'
|
||||
if translate.get('wants to read'):
|
||||
translated_str = translate['wants to read']
|
||||
if translated_str in content or \
|
||||
'wants to read' in content:
|
||||
book_dict['id'] = remove_id_ending(post_obj['id'])
|
||||
book_dict['actor'] = actor
|
||||
book_dict['type'] = 'want'
|
||||
book_dict['published'] = published
|
||||
return book_dict
|
||||
|
||||
translated_str = 'finished reading'
|
||||
if translate.get('finished reading'):
|
||||
translated_str = translate['finished reading']
|
||||
if translated_str in content or \
|
||||
'finished reading' in content:
|
||||
book_dict['id'] = remove_id_ending(post_obj['id'])
|
||||
book_dict['actor'] = actor
|
||||
book_dict['type'] = 'finished'
|
||||
book_dict['published'] = published
|
||||
return book_dict
|
||||
|
||||
return {}
|
||||
|
||||
|
||||
def _add_book_to_reader(reader_books_json: {}, book_dict: {}) -> None:
|
||||
"""Updates reader books
|
||||
"""
|
||||
book_url = book_dict['href']
|
||||
book_event_type = book_dict['type']
|
||||
if not reader_books_json.get(book_url):
|
||||
reader_books_json[book_url] = {
|
||||
book_event_type: book_dict
|
||||
}
|
||||
return
|
||||
reader_books_json[book_url][book_event_type] = book_dict
|
||||
|
||||
|
||||
def _add_reader_to_book(book_json: {}, book_dict: {}) -> None:
|
||||
"""Updates book with a new reader
|
||||
"""
|
||||
book_event_type = book_dict['type']
|
||||
actor = book_dict['actor']
|
||||
if not book_json.get(actor):
|
||||
book_json[actor] = {
|
||||
book_event_type: book_dict
|
||||
}
|
||||
if book_dict.get('name'):
|
||||
book_json['title'] = remove_html(book_dict['name'])
|
||||
return
|
||||
book_json[actor][book_event_type] = book_dict
|
||||
if book_dict.get('name'):
|
||||
book_json['title'] = remove_html(book_dict['name'])
|
||||
|
||||
|
||||
def store_book_events(base_dir: str,
|
||||
post_json_object: {},
|
||||
system_language: str,
|
||||
languages_understood: [],
|
||||
translate: {},
|
||||
debug: bool,
|
||||
max_recent_books: int) -> bool:
|
||||
"""Saves book events to file under accounts/reading/books
|
||||
and accounts/reading/readers
|
||||
"""
|
||||
book_dict = get_reading_status(post_json_object,
|
||||
system_language,
|
||||
languages_understood,
|
||||
translate)
|
||||
if not book_dict:
|
||||
return False
|
||||
reading_path = base_dir + '/accounts/reading'
|
||||
if not os.path.isdir(reading_path):
|
||||
os.mkdir(reading_path)
|
||||
books_path = reading_path + '/books'
|
||||
if not os.path.isdir(books_path):
|
||||
os.mkdir(books_path)
|
||||
readers_path = reading_path + '/readers'
|
||||
if not os.path.isdir(readers_path):
|
||||
os.mkdir(readers_path)
|
||||
|
||||
actor = book_dict['actor']
|
||||
book_url = remove_id_ending(book_dict['href'])
|
||||
|
||||
reader_books_filename = \
|
||||
readers_path + '/' + actor.replace('/', '#') + '.json'
|
||||
reader_books_json = {}
|
||||
if os.path.isfile(reader_books_filename):
|
||||
reader_books_json = load_json(reader_books_filename)
|
||||
_add_book_to_reader(reader_books_json, book_dict)
|
||||
if not save_json(reader_books_json, reader_books_filename):
|
||||
return False
|
||||
|
||||
book_id = book_url.replace('/', '#')
|
||||
book_filename = books_path + '/' + book_id + '.json'
|
||||
book_json = {}
|
||||
if os.path.isfile(book_filename):
|
||||
book_json = load_json(book_filename)
|
||||
_add_reader_to_book(book_json, book_dict)
|
||||
if not save_json(book_json, book_filename):
|
||||
return False
|
||||
|
||||
# prepend to the recent books list
|
||||
recent_books_filename = base_dir + '/accounts/recent_books.txt'
|
||||
if os.path.isfile(recent_books_filename):
|
||||
try:
|
||||
with open(recent_books_filename, 'r+',
|
||||
encoding='utf-8') as recent_file:
|
||||
content = recent_file.read()
|
||||
if book_id + '\n' not in content:
|
||||
recent_file.seek(0, 0)
|
||||
recent_file.write(book_id + '\n' + content)
|
||||
if debug:
|
||||
print('DEBUG: recent book added')
|
||||
except OSError as ex:
|
||||
print('WARN: Failed to write entry to recent books ' +
|
||||
recent_books_filename + ' ' + str(ex))
|
||||
else:
|
||||
try:
|
||||
with open(recent_books_filename, 'w+',
|
||||
encoding='utf-8') as recent_file:
|
||||
recent_file.write(book_id + '\n')
|
||||
except OSError:
|
||||
print('EX: unable to write recent books ' +
|
||||
recent_books_filename)
|
||||
|
||||
# deduplicate and limit the length of the recent books list
|
||||
if os.path.isfile(recent_books_filename):
|
||||
# load recent books as a list
|
||||
recent_lines = []
|
||||
try:
|
||||
with open(recent_books_filename, 'r',
|
||||
encoding='utf-8') as recent_file:
|
||||
recent_lines = recent_file.read().split('\n')
|
||||
except OSError as ex:
|
||||
print('WARN: Failed to read recent books trim ' +
|
||||
recent_books_filename + ' ' + str(ex))
|
||||
|
||||
# deduplicate the list
|
||||
new_recent_lines = []
|
||||
for line in recent_lines:
|
||||
if line not in new_recent_lines:
|
||||
new_recent_lines.append(line)
|
||||
if len(new_recent_lines) < len(recent_lines):
|
||||
recent_lines = new_recent_lines
|
||||
try:
|
||||
with open(recent_books_filename, 'w+',
|
||||
encoding='utf-8') as recent_file:
|
||||
for line in recent_lines:
|
||||
recent_file.write(line + '\n')
|
||||
except OSError:
|
||||
print('EX: unable to deduplicate recent books ' +
|
||||
recent_books_filename)
|
||||
|
||||
# remove excess lines from the list
|
||||
if len(recent_lines) > max_recent_books:
|
||||
try:
|
||||
with open(recent_books_filename, 'w+',
|
||||
encoding='utf-8') as recent_file:
|
||||
for ctr in range(max_recent_books):
|
||||
recent_file.write(recent_lines[ctr] + '\n')
|
||||
except OSError:
|
||||
print('EX: unable to trim recent books ' +
|
||||
recent_books_filename)
|
||||
return True
|
152
tests.py
152
tests.py
|
@ -214,6 +214,9 @@ from webapp_theme_designer import color_contrast
|
|||
from maps import get_map_links_from_post_content
|
||||
from maps import geocoords_from_map_link
|
||||
from followerSync import get_followers_sync_hash
|
||||
from reading import get_book_link_from_content
|
||||
from reading import get_book_from_post
|
||||
from reading import get_reading_status
|
||||
|
||||
|
||||
TEST_SERVER_GROUP_RUNNING = False
|
||||
|
@ -8222,6 +8225,154 @@ def _test_dateformat():
|
|||
assert dtime.tzinfo
|
||||
|
||||
|
||||
def _test_book_link():
|
||||
print('book_link')
|
||||
content = 'Not a link'
|
||||
result = get_book_link_from_content(content)
|
||||
assert result is None
|
||||
|
||||
book_url = 'https://bookwyrm.instance/book/1234567'
|
||||
content = 'xyz wants to read <a ' + \
|
||||
'href="' + book_url + '"><i>Title</i></a>'
|
||||
result = get_book_link_from_content(content)
|
||||
assert result == book_url
|
||||
|
||||
book_url = 'bookwyrm.instance/book/1234567'
|
||||
content = 'xyz wants to read <a ' + \
|
||||
'href="' + book_url + '"><i>Title</i></a>'
|
||||
result = get_book_link_from_content(content)
|
||||
assert result is None
|
||||
|
||||
book_url = 'https://bookwyrm.instance/other/1234567'
|
||||
content = 'xyz wants to read <a ' + \
|
||||
'href="' + book_url + '"><i>Title</i></a>'
|
||||
result = get_book_link_from_content(content)
|
||||
assert result is None
|
||||
|
||||
title = 'Tedious Tome'
|
||||
image_url = 'https://bookwyrm.instance/images/previews/covers/1234.jpg'
|
||||
book_url = 'https://bookwyrm.instance/book/56789'
|
||||
content = 'xyz wants to read <a href="' + book_url + \
|
||||
'"><i>' + title + '</i></a>'
|
||||
actor = 'https://bookwyrm.instance/user/xyz'
|
||||
id_str = actor + '/generatednote/63472854'
|
||||
published = '2024-01-01T10:30:00.2+00:00'
|
||||
post_json_object = {
|
||||
'@context': 'https://www.w3.org/ns/activitystreams',
|
||||
'attachment': [{'@context': 'https://www.w3.org/ns/activitystreams',
|
||||
'name': title,
|
||||
'type': 'Document',
|
||||
'url': image_url}],
|
||||
'attributedTo': actor,
|
||||
'cc': [actor + '/followers'],
|
||||
'content': content,
|
||||
'id': id_str,
|
||||
'published': published,
|
||||
'sensitive': False,
|
||||
'tag': [{'href': book_url,
|
||||
'name': title,
|
||||
'type': 'Edition'}],
|
||||
'to': ['https://www.w3.org/ns/activitystreams#Public'],
|
||||
'type': 'Note'}
|
||||
languages_understood = []
|
||||
translate = {}
|
||||
|
||||
book_dict = get_book_from_post(post_json_object)
|
||||
assert book_dict
|
||||
assert book_dict['name'] == title
|
||||
assert book_dict['href'] == book_url
|
||||
|
||||
result = get_reading_status(post_json_object, 'en',
|
||||
languages_understood,
|
||||
translate)
|
||||
assert result.get('type')
|
||||
assert result['actor'] == actor
|
||||
assert result['published'] == published
|
||||
assert result['type'] == 'want'
|
||||
assert result['href'] == book_url
|
||||
assert result['name'] == title
|
||||
assert result['id'] == id_str
|
||||
|
||||
title = 'The Rise of the Meritocracy'
|
||||
image_url = 'https://bookwyrm.instance/images/previews/covers/6735.jpg'
|
||||
book_url = 'https://bookwyrm.instance/book/7235'
|
||||
content = 'abc finished reading <a href="' + book_url + \
|
||||
'"><i>' + title + '</i></a>'
|
||||
actor = 'https://bookwyrm.instance/user/abc'
|
||||
id_str = actor + '/generatednote/366458384'
|
||||
published = '2024-01-02T11:30:00.2+00:00'
|
||||
post_json_object = {
|
||||
'@context': 'https://www.w3.org/ns/activitystreams',
|
||||
'attachment': [{'@context': 'https://www.w3.org/ns/activitystreams',
|
||||
'name': title,
|
||||
'type': 'Document',
|
||||
'url': image_url}],
|
||||
'attributedTo': actor,
|
||||
'cc': [actor + '/followers'],
|
||||
'content': content,
|
||||
'id': id_str,
|
||||
'published': published,
|
||||
'sensitive': False,
|
||||
'tag': [{'href': book_url,
|
||||
'name': title,
|
||||
'type': 'Edition'}],
|
||||
'to': ['https://www.w3.org/ns/activitystreams#Public'],
|
||||
'type': 'Note'}
|
||||
book_dict = get_book_from_post(post_json_object)
|
||||
assert book_dict
|
||||
assert book_dict['name'] == title
|
||||
assert book_dict['href'] == book_url
|
||||
|
||||
result = get_reading_status(post_json_object, 'en',
|
||||
languages_understood,
|
||||
translate)
|
||||
assert result.get('type')
|
||||
assert result['actor'] == actor
|
||||
assert result['published'] == published
|
||||
assert result['type'] == 'finished'
|
||||
assert result['href'] == book_url
|
||||
assert result['name'] == title
|
||||
assert result['id'] == id_str
|
||||
|
||||
title = 'Pirate Enlightenment, or the Real Libertalia'
|
||||
image_url = 'https://bookwyrm.instance/images/previews/covers/5283.jpg'
|
||||
book_url = 'https://bookwyrm.instance/book/78252'
|
||||
content = 'rated <a href="' + book_url + \
|
||||
'"><i>' + title + '</i></a>'
|
||||
actor = 'https://bookwyrm.instance/user/ghi'
|
||||
rating = 3.5
|
||||
id_str = actor + '/generatednote/73467834576'
|
||||
published = '2024-01-03T12:30:00.2+00:00'
|
||||
post_json_object = {
|
||||
'@context': 'https://www.w3.org/ns/activitystreams',
|
||||
'attachment': [{'@context': 'https://www.w3.org/ns/activitystreams',
|
||||
'name': title,
|
||||
'type': 'Document',
|
||||
'url': image_url}],
|
||||
'attributedTo': actor,
|
||||
'cc': [actor + '/followers'],
|
||||
'content': content,
|
||||
'rating': rating,
|
||||
'id': id_str,
|
||||
'published': published,
|
||||
'sensitive': False,
|
||||
'to': ['https://www.w3.org/ns/activitystreams#Public'],
|
||||
'type': 'Note'}
|
||||
book_dict = get_book_from_post(post_json_object)
|
||||
assert not book_dict
|
||||
|
||||
result = get_reading_status(post_json_object, 'en',
|
||||
languages_understood,
|
||||
translate)
|
||||
assert result.get('type')
|
||||
assert result['actor'] == actor
|
||||
assert result['published'] == published
|
||||
assert result['type'] == 'rated'
|
||||
assert result['href'] == book_url
|
||||
assert result['rating'] == rating
|
||||
assert result['id'] == id_str
|
||||
|
||||
|
||||
def run_all_tests():
|
||||
base_dir = os.getcwd()
|
||||
print('Running tests...')
|
||||
|
@ -8239,6 +8390,7 @@ def run_all_tests():
|
|||
_test_checkbox_names()
|
||||
_test_thread_functions()
|
||||
_test_functions()
|
||||
_test_book_link()
|
||||
_test_dateformat()
|
||||
_test_is_right_to_left()
|
||||
_test_format_mixed_rtl()
|
||||
|
|
|
@ -642,5 +642,8 @@
|
|||
"Mutuals": "التعاضد",
|
||||
"Public replies default to unlisted scope": "الردود العامة افتراضية للنطاق غير المدرج",
|
||||
"About the author": "عن المؤلف",
|
||||
"Do not show follows on your profile": "لا تظهر المتابعات في ملفك الشخصي"
|
||||
"Do not show follows on your profile": "لا تظهر المتابعات في ملفك الشخصي",
|
||||
"rated": "تصنيف",
|
||||
"wants to read": "يريد أن يقرأ",
|
||||
"finished reading": "قراءة الانتهاء"
|
||||
}
|
||||
|
|
|
@ -642,5 +642,8 @@
|
|||
"Mutuals": "পারস্পরিক",
|
||||
"Public replies default to unlisted scope": "অতালিকাভুক্ত সুযোগে সর্বজনীন উত্তর ডিফল্ট",
|
||||
"About the author": "লেখক সম্পর্কে",
|
||||
"Do not show follows on your profile": "আপনার প্রোফাইলে অনুসরণ দেখাবেন না"
|
||||
"Do not show follows on your profile": "আপনার প্রোফাইলে অনুসরণ দেখাবেন না",
|
||||
"rated": "রেট করা",
|
||||
"wants to read": "পড়তে চায়",
|
||||
"finished reading": "পড়া শেষ"
|
||||
}
|
||||
|
|
|
@ -642,5 +642,8 @@
|
|||
"Mutuals": "Mútues",
|
||||
"Public replies default to unlisted scope": "Les respostes públiques són per defecte a l'abast no llistat",
|
||||
"About the author": "Sobre l’autor",
|
||||
"Do not show follows on your profile": "No mostris els seguidors al teu perfil"
|
||||
"Do not show follows on your profile": "No mostris els seguidors al teu perfil",
|
||||
"rated": "valorat",
|
||||
"wants to read": "vol llegir",
|
||||
"finished reading": "acabat de llegir"
|
||||
}
|
||||
|
|
|
@ -642,5 +642,8 @@
|
|||
"Mutuals": "Cydfuddiol",
|
||||
"Public replies default to unlisted scope": "Ymatebion cyhoeddus rhagosodedig i gwmpas heb ei restru",
|
||||
"About the author": "Am yr awdur",
|
||||
"Do not show follows on your profile": "Peidiwch â dangos dilyniannau ar eich proffil"
|
||||
"Do not show follows on your profile": "Peidiwch â dangos dilyniannau ar eich proffil",
|
||||
"rated": "graddio",
|
||||
"wants to read": "eisiau darllen",
|
||||
"finished reading": "gorffen darllen"
|
||||
}
|
||||
|
|
|
@ -642,5 +642,8 @@
|
|||
"Mutuals": "Gegenseitigkeitsgesellschaften",
|
||||
"Public replies default to unlisted scope": "Öffentliche Antworten werden standardmäßig auf den nicht aufgeführten Bereich übertragen",
|
||||
"About the author": "Über den Autor",
|
||||
"Do not show follows on your profile": "Zeigen Sie keine Follower in Ihrem Profil an"
|
||||
"Do not show follows on your profile": "Zeigen Sie keine Follower in Ihrem Profil an",
|
||||
"rated": "bewertet",
|
||||
"wants to read": "will lesen",
|
||||
"finished reading": "fertig gelesen"
|
||||
}
|
||||
|
|
|
@ -642,5 +642,8 @@
|
|||
"Mutuals": "Αμοιβαία",
|
||||
"Public replies default to unlisted scope": "Οι δημόσιες απαντήσεις από προεπιλογή στο μη καταχωρισμένο εύρος",
|
||||
"About the author": "Σχετικά με τον Συγγραφέα",
|
||||
"Do not show follows on your profile": "Μην εμφανίζονται οι ακόλουθοι στο προφίλ σας"
|
||||
"Do not show follows on your profile": "Μην εμφανίζονται οι ακόλουθοι στο προφίλ σας",
|
||||
"rated": "Βαθμολογήθηκε",
|
||||
"wants to read": "θέλει να διαβάσει",
|
||||
"finished reading": "τελείωσε την ανάγνωση"
|
||||
}
|
||||
|
|
|
@ -642,5 +642,8 @@
|
|||
"Mutuals": "Mutuals",
|
||||
"Public replies default to unlisted scope": "Public replies default to unlisted scope",
|
||||
"About the author": "About the author",
|
||||
"Do not show follows on your profile": "Do not show follows on your profile"
|
||||
"Do not show follows on your profile": "Do not show follows on your profile",
|
||||
"rated": "rated",
|
||||
"wants to read": "wants to read",
|
||||
"finished reading": "finished reading"
|
||||
}
|
||||
|
|
|
@ -642,5 +642,8 @@
|
|||
"Mutuals": "Mutuales",
|
||||
"Public replies default to unlisted scope": "Las respuestas públicas tienen por defecto un alcance no listado",
|
||||
"About the author": "Sobre el Autor",
|
||||
"Do not show follows on your profile": "No mostrar seguidores en tu perfil"
|
||||
"Do not show follows on your profile": "No mostrar seguidores en tu perfil",
|
||||
"rated": "clasificado",
|
||||
"wants to read": "quiere leer",
|
||||
"finished reading": "lectura terminada"
|
||||
}
|
||||
|
|
|
@ -642,5 +642,8 @@
|
|||
"Mutuals": "متقابل",
|
||||
"Public replies default to unlisted scope": "پاسخهای عمومی بهطور پیشفرض به محدوده فهرست نشده است",
|
||||
"About the author": "درباره نویسنده",
|
||||
"Do not show follows on your profile": "فالوورها را در نمایه خود نشان ندهید"
|
||||
"Do not show follows on your profile": "فالوورها را در نمایه خود نشان ندهید",
|
||||
"rated": "دارای رتبه",
|
||||
"wants to read": "می خواهد بخواند",
|
||||
"finished reading": "خواندن را تمام کرد"
|
||||
}
|
||||
|
|
|
@ -642,5 +642,8 @@
|
|||
"Mutuals": "Mutuelles",
|
||||
"Public replies default to unlisted scope": "Les réponses publiques ont par défaut une portée non répertoriée",
|
||||
"About the author": "A propos de l'auteur",
|
||||
"Do not show follows on your profile": "Ne pas afficher les suivis sur votre profil"
|
||||
"Do not show follows on your profile": "Ne pas afficher les suivis sur votre profil",
|
||||
"rated": "noté",
|
||||
"wants to read": "veut lire",
|
||||
"finished reading": "fini de lire"
|
||||
}
|
||||
|
|
|
@ -642,5 +642,8 @@
|
|||
"Mutuals": "Comhpháirteacha",
|
||||
"Public replies default to unlisted scope": "Freagraí poiblí réamhshocraithe ar scóip neamhliostaithe",
|
||||
"About the author": "Faoin tÚdar",
|
||||
"Do not show follows on your profile": "Ná taispeáin na nithe seo a leanas ar do phróifíl"
|
||||
"Do not show follows on your profile": "Ná taispeáin na nithe seo a leanas ar do phróifíl",
|
||||
"rated": "rátáil",
|
||||
"wants to read": "ag iarraidh a léamh",
|
||||
"finished reading": "léamh críochnaithe"
|
||||
}
|
||||
|
|
|
@ -642,5 +642,8 @@
|
|||
"Mutuals": "הדדיות",
|
||||
"Public replies default to unlisted scope": "תשובות ציבוריות כברירת מחדל להיקף לא רשום",
|
||||
"About the author": "על הסופר",
|
||||
"Do not show follows on your profile": "אל תראה עוקבים בפרופיל שלך"
|
||||
"Do not show follows on your profile": "אל תראה עוקבים בפרופיל שלך",
|
||||
"rated": "מדורג",
|
||||
"wants to read": "רוצה לקרוא",
|
||||
"finished reading": "סיים לקרוא"
|
||||
}
|
||||
|
|
|
@ -642,5 +642,8 @@
|
|||
"Mutuals": "पारस्परिक",
|
||||
"Public replies default to unlisted scope": "सार्वजनिक उत्तर डिफ़ॉल्ट रूप से असूचीबद्ध दायरे में आते हैं",
|
||||
"About the author": "लेखक के बारे में",
|
||||
"Do not show follows on your profile": "अपनी प्रोफ़ाइल पर फ़ॉलो न दिखाएं"
|
||||
"Do not show follows on your profile": "अपनी प्रोफ़ाइल पर फ़ॉलो न दिखाएं",
|
||||
"rated": "मूल्यांकन",
|
||||
"wants to read": "पढ़ना चाहता है",
|
||||
"finished reading": "पढ़ना समाप्त"
|
||||
}
|
||||
|
|
|
@ -642,5 +642,8 @@
|
|||
"Mutuals": "Mutui",
|
||||
"Public replies default to unlisted scope": "Per impostazione predefinita, le risposte pubbliche hanno un ambito non elencato",
|
||||
"About the author": "Circa l'autore",
|
||||
"Do not show follows on your profile": "Non mostrare follower sul tuo profilo"
|
||||
"Do not show follows on your profile": "Non mostrare follower sul tuo profilo",
|
||||
"rated": "valutato",
|
||||
"wants to read": "vuole leggere",
|
||||
"finished reading": "finito di leggere"
|
||||
}
|
||||
|
|
|
@ -642,5 +642,8 @@
|
|||
"Mutuals": "相互作用",
|
||||
"Public replies default to unlisted scope": "パブリック返信はデフォルトで非公開スコープになります",
|
||||
"About the author": "著者について",
|
||||
"Do not show follows on your profile": "プロフィールにフォローを表示しない"
|
||||
"Do not show follows on your profile": "プロフィールにフォローを表示しない",
|
||||
"rated": "評価された",
|
||||
"wants to read": "読みたい",
|
||||
"finished reading": "読み終わった"
|
||||
}
|
||||
|
|
|
@ -642,5 +642,8 @@
|
|||
"Mutuals": "상호",
|
||||
"Public replies default to unlisted scope": "공개 답글은 기본적으로 비공개 범위로 설정됩니다.",
|
||||
"About the author": "저자에 대해",
|
||||
"Do not show follows on your profile": "프로필에 팔로우를 표시하지 않습니다."
|
||||
"Do not show follows on your profile": "프로필에 팔로우를 표시하지 않습니다.",
|
||||
"rated": "평가됨",
|
||||
"wants to read": "읽고 싶어",
|
||||
"finished reading": "다 읽었다"
|
||||
}
|
||||
|
|
|
@ -642,5 +642,8 @@
|
|||
"Mutuals": "Mutuals",
|
||||
"Public replies default to unlisted scope": "Bersivên gelemperî ji bo çarçoveyek nelîstekirî xwerû dide",
|
||||
"About the author": "Di derbarê nivîskarê de",
|
||||
"Do not show follows on your profile": "Li ser profîla xwe şopandinê nîşan nedin"
|
||||
"Do not show follows on your profile": "Li ser profîla xwe şopandinê nîşan nedin",
|
||||
"rated": "nirxandin",
|
||||
"wants to read": "dixwaze bixwîne",
|
||||
"finished reading": "xwendina xwe qedand"
|
||||
}
|
||||
|
|
|
@ -642,5 +642,8 @@
|
|||
"Mutuals": "Mutualiteiten",
|
||||
"Public replies default to unlisted scope": "Openbare antwoorden hebben standaard een niet-vermeld bereik",
|
||||
"About the author": "Over de auteur",
|
||||
"Do not show follows on your profile": "Laat geen volgers zien op je profiel"
|
||||
"Do not show follows on your profile": "Laat geen volgers zien op je profiel",
|
||||
"rated": "beoordeeld",
|
||||
"wants to read": "wil lezen",
|
||||
"finished reading": "klaar met lezen"
|
||||
}
|
||||
|
|
|
@ -638,5 +638,8 @@
|
|||
"Mutuals": "Mutuals",
|
||||
"Public replies default to unlisted scope": "Public replies default to unlisted scope",
|
||||
"About the author": "About the author",
|
||||
"Do not show follows on your profile": "Do not show follows on your profile"
|
||||
"Do not show follows on your profile": "Do not show follows on your profile",
|
||||
"rated": "rated",
|
||||
"wants to read": "wants to read",
|
||||
"finished reading": "finished reading"
|
||||
}
|
||||
|
|
|
@ -642,5 +642,8 @@
|
|||
"Mutuals": "Wzajemne relacje",
|
||||
"Public replies default to unlisted scope": "Odpowiedzi publiczne domyślnie mają zakres niepubliczny",
|
||||
"About the author": "O autorze",
|
||||
"Do not show follows on your profile": "Nie pokazuj obserwujących w swoim profilu"
|
||||
"Do not show follows on your profile": "Nie pokazuj obserwujących w swoim profilu",
|
||||
"rated": "ocenione",
|
||||
"wants to read": "chce przeczytać",
|
||||
"finished reading": "skończyłem czytać"
|
||||
}
|
||||
|
|
|
@ -642,5 +642,8 @@
|
|||
"Mutuals": "Mútuas",
|
||||
"Public replies default to unlisted scope": "As respostas públicas são padronizadas para escopo não listado",
|
||||
"About the author": "Sobre o autor",
|
||||
"Do not show follows on your profile": "Não mostre seguidores em seu perfil"
|
||||
"Do not show follows on your profile": "Não mostre seguidores em seu perfil",
|
||||
"rated": "avaliada",
|
||||
"wants to read": "quer ler",
|
||||
"finished reading": "terminei de ler"
|
||||
}
|
||||
|
|
|
@ -642,5 +642,8 @@
|
|||
"Mutuals": "Взаимные отношения",
|
||||
"Public replies default to unlisted scope": "Публичные ответы по умолчанию имеют скрытую область действия.",
|
||||
"About the author": "Об авторе",
|
||||
"Do not show follows on your profile": "Не показывать подписчиков в своем профиле"
|
||||
"Do not show follows on your profile": "Не показывать подписчиков в своем профиле",
|
||||
"rated": "рейтинг",
|
||||
"wants to read": "хочет прочитать",
|
||||
"finished reading": "закончил читать"
|
||||
}
|
||||
|
|
|
@ -642,5 +642,8 @@
|
|||
"Mutuals": "Kuheshimiana",
|
||||
"Public replies default to unlisted scope": "Majibu ya umma kwa chaguomsingi kwa upeo ambao haujaorodheshwa",
|
||||
"About the author": "Kuhusu mwandishi",
|
||||
"Do not show follows on your profile": "Usionyeshe wafuasi kwenye wasifu wako"
|
||||
"Do not show follows on your profile": "Usionyeshe wafuasi kwenye wasifu wako",
|
||||
"rated": "imekadiriwa",
|
||||
"wants to read": "anataka kusoma",
|
||||
"finished reading": "kumaliza kusoma"
|
||||
}
|
||||
|
|
|
@ -642,5 +642,8 @@
|
|||
"Mutuals": "Karşılıklar",
|
||||
"Public replies default to unlisted scope": "Genel yanıtlar varsayılan olarak liste dışı kapsama alınır",
|
||||
"About the author": "Yazar hakkında",
|
||||
"Do not show follows on your profile": "Takip edilenleri profilinizde gösterme"
|
||||
"Do not show follows on your profile": "Takip edilenleri profilinizde gösterme",
|
||||
"rated": "oy",
|
||||
"wants to read": "okumak istiyor",
|
||||
"finished reading": "okumayı bitirdim"
|
||||
}
|
||||
|
|
|
@ -642,5 +642,8 @@
|
|||
"Mutuals": "Мутуали",
|
||||
"Public replies default to unlisted scope": "Загальнодоступні відповіді за умовчанням мають приватний обсяг",
|
||||
"About the author": "Про автора",
|
||||
"Do not show follows on your profile": "Не показувати підписки у вашому профілі"
|
||||
"Do not show follows on your profile": "Не показувати підписки у вашому профілі",
|
||||
"rated": "оцінений",
|
||||
"wants to read": "хоче читати",
|
||||
"finished reading": "закінчив читати"
|
||||
}
|
||||
|
|
|
@ -642,5 +642,8 @@
|
|||
"Mutuals": "קעגנצייַטיק",
|
||||
"Public replies default to unlisted scope": "ציבור ענטפֿערס פעליקייַט צו אַנליסטעד פאַרנעם",
|
||||
"About the author": "וועגן דעם מחבר",
|
||||
"Do not show follows on your profile": "דו זאלסט נישט ווייַזן די פאלגענדע אויף דיין פּראָפיל"
|
||||
"Do not show follows on your profile": "דו זאלסט נישט ווייַזן די פאלגענדע אויף דיין פּראָפיל",
|
||||
"rated": "רייטאַד",
|
||||
"wants to read": "וויל לייענען",
|
||||
"finished reading": "פאַרטיק לייענען"
|
||||
}
|
||||
|
|
|
@ -642,5 +642,8 @@
|
|||
"Mutuals": "互助基金",
|
||||
"Public replies default to unlisted scope": "公开回复默认为不公开范围",
|
||||
"About the author": "关于作者",
|
||||
"Do not show follows on your profile": "不要在您的个人资料上显示关注者"
|
||||
"Do not show follows on your profile": "不要在您的个人资料上显示关注者",
|
||||
"rated": "额定",
|
||||
"wants to read": "想读书",
|
||||
"finished reading": "读完"
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue