From 15dc0bca09f623cb275a80e74f1df7837d2b98fb Mon Sep 17 00:00:00 2001 From: James Champ Date: Wed, 10 Jan 2024 10:17:08 -0800 Subject: [PATCH 01/61] Add argument parser and default options --- scripts/gh_scripts/config/bot.ini | 7 +++ scripts/gh_scripts/issue_comment_bot.py | 82 +++++++++++++++++++++++++ 2 files changed, 89 insertions(+) create mode 100644 scripts/gh_scripts/config/bot.ini create mode 100755 scripts/gh_scripts/issue_comment_bot.py diff --git a/scripts/gh_scripts/config/bot.ini b/scripts/gh_scripts/config/bot.ini new file mode 100644 index 00000000000..d8a6609d1f2 --- /dev/null +++ b/scripts/gh_scripts/config/bot.ini @@ -0,0 +1,7 @@ +[tokens] +slack_token= +github_token= + +[issue_digest] +default_hours=48 +slack_channel=#team-abc-plus diff --git a/scripts/gh_scripts/issue_comment_bot.py b/scripts/gh_scripts/issue_comment_bot.py new file mode 100755 index 00000000000..f708abcb6ee --- /dev/null +++ b/scripts/gh_scripts/issue_comment_bot.py @@ -0,0 +1,82 @@ +#!/usr/bin/env python +""" +Fetches Open Library GitHub issues that have been commented on +within some amount of time, in hours. + +Writes links to each issue to Slack channel #team-abc-plus +""" +import argparse +import errno +import sys + +from configparser import ConfigParser +from pathlib import Path + + +config_file = 'config/bot.ini' + +def start_job(args: dict): + """ + Starts the new comment digest job. + """ + pass + + +def _get_parser() -> argparse.ArgumentParser: + """ + Creates and returns an ArgumentParser containing default values which were + read from the config file. + """ + def get_defaults(): + """ + Reads the config file and returns a dict containing the default + values for this script's arguments. + + Location of config file is expected to be a nested within this file's parent + directory. The relative file location is stored as `config_file`. + """ + this_dir = Path(__file__).parent.resolve() + config_path = this_dir / Path(config_file) + if not config_path.exists(): + # XXX : Log to file: + print(f'{config_file} does not exist.') + sys.exit(errno.ENOENT) + + config = ConfigParser() + config.read(config_path) + return { + 'slack_token': config['tokens'].get('slack_token', ''), + 'hours': config['issue_digest'].getint('default_hours', 1), + 'slack_channel': config['issue_digest'].get('slack_channel', '#team-abc-plus'), + } + + defaults = get_defaults() + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument( + '--hours', + metavar='N', + help='Fetch issues that have been updated since N hours ago', + type=int, + default=defaults['hours'], + ) + parser.add_argument( + '-c', + '--channel', + help="Issues will be published to this Slack channel", + type=str, + default=defaults['slack_channel'], + ) + parser.add_argument( + '-t', + '--token', + help='Slack auth token', + type=str, + default=defaults['slack_token'], + ) + + return parser + +if __name__ == '__main__': + parser = _get_parser() + args = parser.parse_args() + start_job(args) From 89bc2f1fdbda1433670cce9208d35d3b33369351 Mon Sep 17 00:00:00 2001 From: James Champ Date: Wed, 10 Jan 2024 10:23:31 -0800 Subject: [PATCH 02/61] Fetch and filter GitHub issues --- scripts/gh_scripts/issue_comment_bot.py | 74 ++++++++++++++++++++++++- scripts/gh_scripts/requirements.txt | 1 + 2 files changed, 74 insertions(+), 1 deletion(-) create mode 100644 scripts/gh_scripts/requirements.txt diff --git a/scripts/gh_scripts/issue_comment_bot.py b/scripts/gh_scripts/issue_comment_bot.py index f708abcb6ee..a5075c98e4a 100755 --- a/scripts/gh_scripts/issue_comment_bot.py +++ b/scripts/gh_scripts/issue_comment_bot.py @@ -10,16 +10,88 @@ import sys from configparser import ConfigParser +from datetime import datetime, timedelta from pathlib import Path +import requests config_file = 'config/bot.ini' +# XXX : Configure? +staff_usernames = ['scottbarnes', 'mekarpeles', 'jimchamp', 'cdrini'] + +def fetch_issues(updated_since: str): + query = f'repo:internetarchive/openlibrary is:open is:issue comments:>1 updated:>{updated_since}' + response = requests.get( + 'https://api.github.com/search/issues?per_page=100', + params={ + 'q': query, + }, + ) + d = response.json() + results = d['items'] + + # Pagination + def get_next_page(url: str): + """Returns list of issues and optional url for next page""" + resp = requests.get(url) + # Get issues + d = resp.json() + issues = d['items'] + # Prepare url for next page + next = resp.links.get('next', {}) + next_url = next.get('url', '') + + return issues, next_url + + links = response.links + next = links.get('next', {}) + next_url = next.get('url', '') + while next_url: + # Make call with next link + issues, next_url = get_next_page(next_url) + results = results + issues + + return results + +def filter_issues(issues: list, since_date: str): + """ + Returns list of issues that were not last responded to by staff. + Requires fetching the most recent comments for the given issues. + """ + filtered_issues = [] + + for i in issues: + comments_url = i.get('comments_url') + resp = requests.get(f'{comments_url}?since{since_date}Z') + comments = resp.json() + last_comment = comments[-1] + last_commenter = last_comment['user']['login'] + + if last_commenter not in staff_usernames: + filtered_issues.append(i) + + return filtered_issues + +def publish_digest(issues: list[str], slack_channel: str, slack_token: str): + pass + +def create_date_string(hours): + now = datetime.now() + # XXX : Add a minute or two to the delta? + since = now - timedelta(hours=hours) + return since.strftime(f'%Y-%m-%dT%H:%M:%S') + def start_job(args: dict): """ Starts the new comment digest job. """ - pass + print(args) + date_string = create_date_string(args.hours) + issues = fetch_issues(date_string) + filtered_issues = filter_issues(issues, date_string) + if filtered_issues: + publish_digest(filtered_issues, args.channel, args.token) def _get_parser() -> argparse.ArgumentParser: diff --git a/scripts/gh_scripts/requirements.txt b/scripts/gh_scripts/requirements.txt new file mode 100644 index 00000000000..2c24336eb31 --- /dev/null +++ b/scripts/gh_scripts/requirements.txt @@ -0,0 +1 @@ +requests==2.31.0 From dc81b181d5717d5122feda5e30704074b65fdc9d Mon Sep 17 00:00:00 2001 From: James Champ Date: Wed, 10 Jan 2024 11:37:42 -0800 Subject: [PATCH 03/61] Publish results to Slack --- scripts/gh_scripts/issue_comment_bot.py | 55 +++++++++++++++++++++++-- 1 file changed, 52 insertions(+), 3 deletions(-) diff --git a/scripts/gh_scripts/issue_comment_bot.py b/scripts/gh_scripts/issue_comment_bot.py index a5075c98e4a..7c40ae3bb3e 100755 --- a/scripts/gh_scripts/issue_comment_bot.py +++ b/scripts/gh_scripts/issue_comment_bot.py @@ -8,6 +8,7 @@ import argparse import errno import sys +import time from configparser import ConfigParser from datetime import datetime, timedelta @@ -74,7 +75,55 @@ def filter_issues(issues: list, since_date: str): return filtered_issues def publish_digest(issues: list[str], slack_channel: str, slack_token: str): - pass + parent_thread_msg = f'There are {len(issues)} issue(s) awaiting response. More details in this thread.' + response = requests.post( + 'https://slack.com/api/chat.postMessage', + headers={ + 'Authorization': f"Bearer {slack_token}", + 'Content-Type': 'application/json; charset=utf-8', + }, + json={ + 'channel': slack_channel, + 'text': parent_thread_msg, + }, + ) + + if response.status_code != 200: + # XXX : Log this + print(f'Failed to send message to Slack. Status code: {response.status_code}') + # XXX : Add retry logic + sys.exit(errno.ECOMM) + + d = response.json() + # Store timestamp, which, along with the channel, uniquely identifies the parent thread + ts = d.get('ts') + + def comment_on_thread(message: str): + response = requests.post( + 'https://slack.com/api/chat.postMessage', + headers={ + 'Authorization': f"Bearer {slack_token}", + 'Content-Type': 'application/json; charset=utf-8', + }, + json={ + 'channel': slack_channel, + 'text': message, + 'thread_ts': ts + }, + ) + if response.status_code != 200: + # XXX : Log this + print(f'Failed to POST slack message\n Status code: {response.status_code}\n Message: {message}') + # XXX : Retry logic? + + for issue in issues: + # Slack rate limit is roughly 1 request per second + time.sleep(1) + + issue_url = issue.get('html_url') + issue_title = issue.get('title') + message = f'<{issue_url}|*{issue_title}*>' + comment_on_thread(message) def create_date_string(hours): now = datetime.now() @@ -86,13 +135,13 @@ def start_job(args: dict): """ Starts the new comment digest job. """ - print(args) date_string = create_date_string(args.hours) issues = fetch_issues(date_string) filtered_issues = filter_issues(issues, date_string) if filtered_issues: publish_digest(filtered_issues, args.channel, args.token) - + # XXX : Log this + print('Digest POSTed to Slack') def _get_parser() -> argparse.ArgumentParser: """ From 6d5d9b4b0b29cba1571b038939f1d8d81e833925 Mon Sep 17 00:00:00 2001 From: James Champ Date: Wed, 10 Jan 2024 17:58:35 -0800 Subject: [PATCH 04/61] Add more information to digest --- scripts/gh_scripts/issue_comment_bot.py | 110 ++++++++++++++++++------ 1 file changed, 84 insertions(+), 26 deletions(-) diff --git a/scripts/gh_scripts/issue_comment_bot.py b/scripts/gh_scripts/issue_comment_bot.py index 7c40ae3bb3e..b21e7e65f26 100755 --- a/scripts/gh_scripts/issue_comment_bot.py +++ b/scripts/gh_scripts/issue_comment_bot.py @@ -22,17 +22,29 @@ staff_usernames = ['scottbarnes', 'mekarpeles', 'jimchamp', 'cdrini'] def fetch_issues(updated_since: str): - query = f'repo:internetarchive/openlibrary is:open is:issue comments:>1 updated:>{updated_since}' + """ + Fetches all GitHub issues that have been updated since the given date string and have at least one comment. + + GitHub results are paginated. This functions appends each result to a list, and does so for all pages. + To keep API calls to a minimum, we request the maximum number of results per request (100 per page, as of writing). + + Important: Updated issues need not have a recent comment. Update events include many other things, such as adding a + label to an issue, or moving an issue to a milestone. Issues returned by this function will require additional + processing in order to determine if they have recent comments. + """ + # Make initial query for updated issues: + query = f'repo:internetarchive/openlibrary is:open is:issue comments:>0 updated:>{updated_since}' response = requests.get( - 'https://api.github.com/search/issues?per_page=100', + 'https://api.github.com/search/issues', params={ 'q': query, + 'per_page': 100, }, ) d = response.json() results = d['items'] - # Pagination + # Fetch additional updated issues, if any exist def get_next_page(url: str): """Returns list of issues and optional url for next page""" resp = requests.get(url) @@ -55,27 +67,63 @@ def get_next_page(url: str): return results -def filter_issues(issues: list, since_date: str): +def filter_issues(issues: list, since: datetime): """ Returns list of issues that were not last responded to by staff. Requires fetching the most recent comments for the given issues. """ - filtered_issues = [] + results = [] for i in issues: + # Fetch comments using URL from previous GitHub search results comments_url = i.get('comments_url') - resp = requests.get(f'{comments_url}?since{since_date}Z') + resp = requests.get( + comments_url, + params={ + 'per_page': 100 + } + ) + + # Ensure that we have the last page of comments + links = resp.links + last = links.get('last', {}) + last_url = last.get('url', '') + + if last_url: + resp = requests.get(last_url) + + # Get last comment comments = resp.json() last_comment = comments[-1] - last_commenter = last_comment['user']['login'] - if last_commenter not in staff_usernames: - filtered_issues.append(i) + # Determine if last comment meets our criteria for Slack notifications + # First step: Ensure that the last comment was left after the given `since` datetime + created = datetime.fromisoformat(last_comment['created_at']) + # Removing timezone info to avoid TypeErrors, which occur when + # comparing a timezone-aware datetime with a timezone-naive datetime + created = created.replace(tzinfo=None) + if created > since: + # Next step: Determine if the last commenter is a staff member + last_commenter = last_comment['user']['login'] + if last_commenter not in staff_usernames: + results.append({ + 'comment_url': last_comment['html_url'], + 'commenter': last_commenter, + 'issue_title': i['title'], + }) - return filtered_issues + return results + +def publish_digest(issues: list[str], slack_channel: str, slack_token: str, hours_passed: int): + """ + Creates a threaded Slack messaged containing a digest of recently commented GitHub issues. + + Parent Slack message will say how many comments were left, and the timeframe. Each reply + will include a link to the comment, as well as addiitonal information. + """ + # Create the parent message + parent_thread_msg = f'At least {len(issues)} new comment(s) have been left by contributors in the past {hours_passed} hour(s)' -def publish_digest(issues: list[str], slack_channel: str, slack_token: str): - parent_thread_msg = f'There are {len(issues)} issue(s) awaiting response. More details in this thread.' response = requests.post( 'https://slack.com/api/chat.postMessage', headers={ @@ -91,7 +139,7 @@ def publish_digest(issues: list[str], slack_channel: str, slack_token: str): if response.status_code != 200: # XXX : Log this print(f'Failed to send message to Slack. Status code: {response.status_code}') - # XXX : Add retry logic + # XXX : Add retry logic? sys.exit(errno.ECOMM) d = response.json() @@ -99,6 +147,9 @@ def publish_digest(issues: list[str], slack_channel: str, slack_token: str): ts = d.get('ts') def comment_on_thread(message: str): + """ + Posts the given message as a reply to the parent message. + """ response = requests.post( 'https://slack.com/api/chat.postMessage', headers={ @@ -108,7 +159,7 @@ def comment_on_thread(message: str): json={ 'channel': slack_channel, 'text': message, - 'thread_ts': ts + 'thread_ts': ts, }, ) if response.status_code != 200: @@ -116,30 +167,34 @@ def comment_on_thread(message: str): print(f'Failed to POST slack message\n Status code: {response.status_code}\n Message: {message}') # XXX : Retry logic? - for issue in issues: + for i in issues: # Slack rate limit is roughly 1 request per second time.sleep(1) - issue_url = issue.get('html_url') - issue_title = issue.get('title') - message = f'<{issue_url}|*{issue_title}*>' + comment_url = i['comment_url'] + issue_title = i['issue_title'] + commenter = i['commenter'] + message = f'<{comment_url}|Latest comment for: *{issue_title}*>\n' + message += f'Commenter: *{commenter}*' comment_on_thread(message) -def create_date_string(hours): +def time_since(hours): + """Returns datetime and string representations of the current time, minus the given hour""" now = datetime.now() - # XXX : Add a minute or two to the delta? + # XXX : Add a minute or two to the delta (to avoid dropping issues)? since = now - timedelta(hours=hours) - return since.strftime(f'%Y-%m-%dT%H:%M:%S') + return since, since.strftime(f'%Y-%m-%dT%H:%M:%S') def start_job(args: dict): """ Starts the new comment digest job. """ - date_string = create_date_string(args.hours) + since, date_string = time_since(args.hours) issues = fetch_issues(date_string) - filtered_issues = filter_issues(issues, date_string) + filtered_issues = filter_issues(issues, since) + if filtered_issues: - publish_digest(filtered_issues, args.channel, args.token) + publish_digest(filtered_issues, args.channel, args.slack_token, args.hours) # XXX : Log this print('Digest POSTed to Slack') @@ -165,6 +220,8 @@ def get_defaults(): config = ConfigParser() config.read(config_path) + # XXX : Setting the defaults both here in the `get` calls and the config file + # is like wearing a belt with suspenders... return { 'slack_token': config['tokens'].get('slack_token', ''), 'hours': config['issue_digest'].getint('default_hours', 1), @@ -188,8 +245,8 @@ def get_defaults(): default=defaults['slack_channel'], ) parser.add_argument( - '-t', - '--token', + '-s', + '--slack-token', help='Slack auth token', type=str, default=defaults['slack_token'], @@ -198,6 +255,7 @@ def get_defaults(): return parser if __name__ == '__main__': + # Process command-line arguments and starts the notification job parser = _get_parser() args = parser.parse_args() start_job(args) From 4fae3a36084c2ff3c32a3246ec24e089924e2d56 Mon Sep 17 00:00:00 2001 From: James Champ Date: Thu, 11 Jan 2024 11:39:30 -0800 Subject: [PATCH 05/61] Remove default configuration --- scripts/gh_scripts/config/bot.ini | 7 ---- scripts/gh_scripts/issue_comment_bot.py | 50 ++++--------------------- 2 files changed, 7 insertions(+), 50 deletions(-) delete mode 100644 scripts/gh_scripts/config/bot.ini diff --git a/scripts/gh_scripts/config/bot.ini b/scripts/gh_scripts/config/bot.ini deleted file mode 100644 index d8a6609d1f2..00000000000 --- a/scripts/gh_scripts/config/bot.ini +++ /dev/null @@ -1,7 +0,0 @@ -[tokens] -slack_token= -github_token= - -[issue_digest] -default_hours=48 -slack_channel=#team-abc-plus diff --git a/scripts/gh_scripts/issue_comment_bot.py b/scripts/gh_scripts/issue_comment_bot.py index b21e7e65f26..003d5c022b7 100755 --- a/scripts/gh_scripts/issue_comment_bot.py +++ b/scripts/gh_scripts/issue_comment_bot.py @@ -3,22 +3,17 @@ Fetches Open Library GitHub issues that have been commented on within some amount of time, in hours. -Writes links to each issue to Slack channel #team-abc-plus +Writes links to each issue to given Slack channel. """ import argparse import errno import sys import time -from configparser import ConfigParser from datetime import datetime, timedelta -from pathlib import Path import requests -config_file = 'config/bot.ini' - -# XXX : Configure? staff_usernames = ['scottbarnes', 'mekarpeles', 'jimchamp', 'cdrini'] def fetch_issues(updated_since: str): @@ -196,60 +191,29 @@ def start_job(args: dict): if filtered_issues: publish_digest(filtered_issues, args.channel, args.slack_token, args.hours) # XXX : Log this - print('Digest POSTed to Slack') + print('Digest posted to Slack') def _get_parser() -> argparse.ArgumentParser: """ Creates and returns an ArgumentParser containing default values which were read from the config file. """ - def get_defaults(): - """ - Reads the config file and returns a dict containing the default - values for this script's arguments. - - Location of config file is expected to be a nested within this file's parent - directory. The relative file location is stored as `config_file`. - """ - this_dir = Path(__file__).parent.resolve() - config_path = this_dir / Path(config_file) - if not config_path.exists(): - # XXX : Log to file: - print(f'{config_file} does not exist.') - sys.exit(errno.ENOENT) - - config = ConfigParser() - config.read(config_path) - # XXX : Setting the defaults both here in the `get` calls and the config file - # is like wearing a belt with suspenders... - return { - 'slack_token': config['tokens'].get('slack_token', ''), - 'hours': config['issue_digest'].getint('default_hours', 1), - 'slack_channel': config['issue_digest'].get('slack_channel', '#team-abc-plus'), - } - - defaults = get_defaults() parser = argparse.ArgumentParser(description=__doc__) parser.add_argument( - '--hours', - metavar='N', - help='Fetch issues that have been updated since N hours ago', + 'hours', + help='Fetch issues that have been updated since this many hours ago', type=int, - default=defaults['hours'], ) parser.add_argument( - '-c', - '--channel', + 'channel', help="Issues will be published to this Slack channel", type=str, - default=defaults['slack_channel'], ) parser.add_argument( - '-s', - '--slack-token', + 'slack_token', + metavar='slack-token', help='Slack auth token', type=str, - default=defaults['slack_token'], ) return parser From 7687c5a14039592d8930d6e410cb05fea3624615 Mon Sep 17 00:00:00 2001 From: James Champ Date: Thu, 11 Jan 2024 13:40:32 -0800 Subject: [PATCH 06/61] Mention leads in Slack message --- scripts/gh_scripts/issue_comment_bot.py | 44 +++++++++++++++++++++++-- 1 file changed, 42 insertions(+), 2 deletions(-) diff --git a/scripts/gh_scripts/issue_comment_bot.py b/scripts/gh_scripts/issue_comment_bot.py index 003d5c022b7..f7ac502e6e9 100755 --- a/scripts/gh_scripts/issue_comment_bot.py +++ b/scripts/gh_scripts/issue_comment_bot.py @@ -14,7 +14,21 @@ import requests -staff_usernames = ['scottbarnes', 'mekarpeles', 'jimchamp', 'cdrini'] +lead_label_to_username = { + 'Lead: @mekarpeles': 'mekarpeles', + 'Lead: @cdrini': 'cdrini', + 'Lead: @scottbarnes': 'scottbarnes', + 'Lead: @seabelis': 'seabelis', + 'Lead: @jimchamp': 'jimchamp', +} + +username_to_slack_id = { + 'mekarpeles': '<@mek>', + 'cdrini': '<@cdrini>', + 'scottbarnes': '<@U03MNR6T7FH>', + 'seabelis': '<@UAHQ39ACT>', + 'jimchamp': '<@U01ARTHG9EV>', +} def fetch_issues(updated_since: str): """ @@ -100,15 +114,31 @@ def filter_issues(issues: list, since: datetime): if created > since: # Next step: Determine if the last commenter is a staff member last_commenter = last_comment['user']['login'] - if last_commenter not in staff_usernames: + if last_commenter not in username_to_slack_id: + lead_label= find_lead_label(i.get('labels', [])) results.append({ 'comment_url': last_comment['html_url'], 'commenter': last_commenter, 'issue_title': i['title'], + 'lead_label': lead_label, }) return results +def find_lead_label(labels: list[str]) -> str: + """ + Finds and returns the name of the first lead label found in the given list of GitHub labels. + + Returns an empty string if no lead label is found + """ + result = '' + for label in labels: + if label['name'].startswith('Lead:'): + result = label['name'] + break + + return result + def publish_digest(issues: list[str], slack_channel: str, slack_token: str, hours_passed: int): """ Creates a threaded Slack messaged containing a digest of recently commented GitHub issues. @@ -170,6 +200,16 @@ def comment_on_thread(message: str): issue_title = i['issue_title'] commenter = i['commenter'] message = f'<{comment_url}|Latest comment for: *{issue_title}*>\n' + + username = lead_label_to_username.get(i['lead_label'], '') + slack_id = username_to_slack_id.get(username, '') + if slack_id: + message += f'Lead: {slack_id}\n' + elif i['lead_label']: + message += f'i["lead_label"]\n' + else: + message += 'Lead: N/A\n' + message += f'Commenter: *{commenter}*' comment_on_thread(message) From fac8e060108c61d5b54a53fa0ba6f51e0139f6bb Mon Sep 17 00:00:00 2001 From: James Champ Date: Thu, 11 Jan 2024 14:06:39 -0800 Subject: [PATCH 07/61] Add more documentation --- scripts/gh_scripts/README.md | 27 +++++++++++++++++++++++++ scripts/gh_scripts/issue_comment_bot.py | 11 ++++++++-- 2 files changed, 36 insertions(+), 2 deletions(-) create mode 100644 scripts/gh_scripts/README.md diff --git a/scripts/gh_scripts/README.md b/scripts/gh_scripts/README.md new file mode 100644 index 00000000000..1549c5fcf4f --- /dev/null +++ b/scripts/gh_scripts/README.md @@ -0,0 +1,27 @@ +# GitHub Project Management Scripts + +This directory contains scripts that the Open Library team uses to interact with this GitHub repository. + +To quickly see a script's purpose and arguments, run the script with the `-h` or `--help` flag. + +## `issue_comment_bot.py` + +This script fetches issues that have new comments from contributors within the past number of hours, then posts a message to the team in our Slack channel. + +### Usage: +This script has three positional arguments: +``` + hours Fetch issues that have been updated since this many hours ago + channel Issues will be published to this Slack channel + slack-token Slack authentication token +``` + +__Running the script locally:__ +``` +docker compose exec -e PYTHONPATH=. web bash + +# Publish digest of new comments from the past day to #openlibrary-g: +./scripts/gh_scripts/issue_comment_bot.py 24 "#openlibrary-g" "replace-with-slack-token" +``` + +__Note:__ When adding arguments, be sure to place any hyphenated values within double quotes. diff --git a/scripts/gh_scripts/issue_comment_bot.py b/scripts/gh_scripts/issue_comment_bot.py index f7ac502e6e9..f77eeaee901 100755 --- a/scripts/gh_scripts/issue_comment_bot.py +++ b/scripts/gh_scripts/issue_comment_bot.py @@ -188,6 +188,7 @@ def comment_on_thread(message: str): }, ) if response.status_code != 200: + # XXX : Check "ok" field for errors # XXX : Log this print(f'Failed to POST slack message\n Status code: {response.status_code}\n Message: {message}') # XXX : Retry logic? @@ -228,10 +229,16 @@ def start_job(args: dict): issues = fetch_issues(date_string) filtered_issues = filter_issues(issues, since) + # XXX : If we are only running this script daily, we can remove this condition to + # always post a message to Slack. If the digest is ever not published, we'll know + # that something is wrong with our script runner. if filtered_issues: publish_digest(filtered_issues, args.channel, args.slack_token, args.hours) - # XXX : Log this - print('Digest posted to Slack') + # XXX : Log this + print('Digest posted to Slack.') + else: + # XXX : Log this + print('No issues needing attention found.') def _get_parser() -> argparse.ArgumentParser: """ From 9a535ac8ba229560ba0235ed761aabff23cacb7f Mon Sep 17 00:00:00 2001 From: James Champ Date: Thu, 11 Jan 2024 14:10:51 -0800 Subject: [PATCH 08/61] Update parent thread message --- scripts/gh_scripts/issue_comment_bot.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/gh_scripts/issue_comment_bot.py b/scripts/gh_scripts/issue_comment_bot.py index f77eeaee901..4c773d6b6e8 100755 --- a/scripts/gh_scripts/issue_comment_bot.py +++ b/scripts/gh_scripts/issue_comment_bot.py @@ -147,7 +147,7 @@ def publish_digest(issues: list[str], slack_channel: str, slack_token: str, hour will include a link to the comment, as well as addiitonal information. """ # Create the parent message - parent_thread_msg = f'At least {len(issues)} new comment(s) have been left by contributors in the past {hours_passed} hour(s)' + parent_thread_msg = f'{len(issues)} new GitHub comment(s) since {hours_passed} hour(s) ago' response = requests.post( 'https://slack.com/api/chat.postMessage', From 4748e6aa2c32950e3ffe4abe92aef3777a623ff2 Mon Sep 17 00:00:00 2001 From: James Champ Date: Thu, 11 Jan 2024 14:26:44 -0800 Subject: [PATCH 09/61] Fix string interpolation bug --- scripts/gh_scripts/issue_comment_bot.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/gh_scripts/issue_comment_bot.py b/scripts/gh_scripts/issue_comment_bot.py index 4c773d6b6e8..30af9b21b30 100755 --- a/scripts/gh_scripts/issue_comment_bot.py +++ b/scripts/gh_scripts/issue_comment_bot.py @@ -1,6 +1,6 @@ #!/usr/bin/env python """ -Fetches Open Library GitHub issues that have been commented on +Fetches Open Library GitHub issues that have been commented on within some amount of time, in hours. Writes links to each issue to given Slack channel. @@ -207,7 +207,7 @@ def comment_on_thread(message: str): if slack_id: message += f'Lead: {slack_id}\n' elif i['lead_label']: - message += f'i["lead_label"]\n' + message += f'{i["lead_label"]}\n' else: message += 'Lead: N/A\n' @@ -219,7 +219,7 @@ def time_since(hours): now = datetime.now() # XXX : Add a minute or two to the delta (to avoid dropping issues)? since = now - timedelta(hours=hours) - return since, since.strftime(f'%Y-%m-%dT%H:%M:%S') + return since, since.strftime('%Y-%m-%dT%H:%M:%S') def start_job(args: dict): """ @@ -230,7 +230,7 @@ def start_job(args: dict): filtered_issues = filter_issues(issues, since) # XXX : If we are only running this script daily, we can remove this condition to - # always post a message to Slack. If the digest is ever not published, we'll know + # always post a message to Slack. If the digest is ever not published, we'll know # that something is wrong with our script runner. if filtered_issues: publish_digest(filtered_issues, args.channel, args.slack_token, args.hours) From 449fc9a770c4b308f44856c6c387f13e36624de2 Mon Sep 17 00:00:00 2001 From: James Champ Date: Thu, 11 Jan 2024 14:46:43 -0800 Subject: [PATCH 10/61] Fix linting issues --- scripts/gh_scripts/issue_comment_bot.py | 28 +++++++++++++++++-------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/scripts/gh_scripts/issue_comment_bot.py b/scripts/gh_scripts/issue_comment_bot.py index 30af9b21b30..8cf1492ce96 100755 --- a/scripts/gh_scripts/issue_comment_bot.py +++ b/scripts/gh_scripts/issue_comment_bot.py @@ -11,6 +11,7 @@ import time from datetime import datetime, timedelta +from typing import Any import requests @@ -30,6 +31,7 @@ 'jimchamp': '<@U01ARTHG9EV>', } + def fetch_issues(updated_since: str): """ Fetches all GitHub issues that have been updated since the given date string and have at least one comment. @@ -43,12 +45,13 @@ def fetch_issues(updated_since: str): """ # Make initial query for updated issues: query = f'repo:internetarchive/openlibrary is:open is:issue comments:>0 updated:>{updated_since}' + p: dict[str, str|int] = { + 'q': query, + 'per_page': 100, + } response = requests.get( 'https://api.github.com/search/issues', - params={ - 'q': query, - 'per_page': 100, - }, + params=p, ) d = response.json() results = d['items'] @@ -76,6 +79,7 @@ def get_next_page(url: str): return results + def filter_issues(issues: list, since: datetime): """ Returns list of issues that were not last responded to by staff. @@ -115,7 +119,7 @@ def filter_issues(issues: list, since: datetime): # Next step: Determine if the last commenter is a staff member last_commenter = last_comment['user']['login'] if last_commenter not in username_to_slack_id: - lead_label= find_lead_label(i.get('labels', [])) + lead_label = find_lead_label(i.get('labels', [])) results.append({ 'comment_url': last_comment['html_url'], 'commenter': last_commenter, @@ -125,7 +129,8 @@ def filter_issues(issues: list, since: datetime): return results -def find_lead_label(labels: list[str]) -> str: + +def find_lead_label(labels: list[dict[str, Any]]) -> str: """ Finds and returns the name of the first lead label found in the given list of GitHub labels. @@ -139,12 +144,13 @@ def find_lead_label(labels: list[str]) -> str: return result -def publish_digest(issues: list[str], slack_channel: str, slack_token: str, hours_passed: int): + +def publish_digest(issues: list[dict[str, str]], slack_channel: str, slack_token: str, hours_passed: int): """ Creates a threaded Slack messaged containing a digest of recently commented GitHub issues. Parent Slack message will say how many comments were left, and the timeframe. Each reply - will include a link to the comment, as well as addiitonal information. + will include a link to the comment, as well as additional information. """ # Create the parent message parent_thread_msg = f'{len(issues)} new GitHub comment(s) since {hours_passed} hour(s) ago' @@ -214,6 +220,7 @@ def comment_on_thread(message: str): message += f'Commenter: *{commenter}*' comment_on_thread(message) + def time_since(hours): """Returns datetime and string representations of the current time, minus the given hour""" now = datetime.now() @@ -221,7 +228,8 @@ def time_since(hours): since = now - timedelta(hours=hours) return since, since.strftime('%Y-%m-%dT%H:%M:%S') -def start_job(args: dict): + +def start_job(args: argparse.Namespace): """ Starts the new comment digest job. """ @@ -240,6 +248,7 @@ def start_job(args: dict): # XXX : Log this print('No issues needing attention found.') + def _get_parser() -> argparse.ArgumentParser: """ Creates and returns an ArgumentParser containing default values which were @@ -265,6 +274,7 @@ def _get_parser() -> argparse.ArgumentParser: return parser + if __name__ == '__main__': # Process command-line arguments and starts the notification job parser = _get_parser() From 5a2ce149685ec2132ea6053e4d428532e957b580 Mon Sep 17 00:00:00 2001 From: James Champ Date: Wed, 17 Jan 2024 09:58:12 -0800 Subject: [PATCH 11/61] Exclude Charles' comments from the digest --- scripts/gh_scripts/issue_comment_bot.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/scripts/gh_scripts/issue_comment_bot.py b/scripts/gh_scripts/issue_comment_bot.py index 8cf1492ce96..f5618f29996 100755 --- a/scripts/gh_scripts/issue_comment_bot.py +++ b/scripts/gh_scripts/issue_comment_bot.py @@ -15,6 +15,7 @@ import requests +# Maps lead label to GitHub username lead_label_to_username = { 'Lead: @mekarpeles': 'mekarpeles', 'Lead: @cdrini': 'cdrini', @@ -23,12 +24,14 @@ 'Lead: @jimchamp': 'jimchamp', } +# Maps GitHub username to Slack ID username_to_slack_id = { 'mekarpeles': '<@mek>', 'cdrini': '<@cdrini>', 'scottbarnes': '<@U03MNR6T7FH>', 'seabelis': '<@UAHQ39ACT>', 'jimchamp': '<@U01ARTHG9EV>', + 'hornc': '<@U0EUS8DV0>', } From d64a630c966921d99795682957675bd4b5b815aa Mon Sep 17 00:00:00 2001 From: James Champ Date: Wed, 17 Jan 2024 13:30:18 -0800 Subject: [PATCH 12/61] Create workflow for comment digest job --- .github/workflows/new_comment_digest.yml | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 .github/workflows/new_comment_digest.yml diff --git a/.github/workflows/new_comment_digest.yml b/.github/workflows/new_comment_digest.yml new file mode 100644 index 00000000000..ce72caf6430 --- /dev/null +++ b/.github/workflows/new_comment_digest.yml @@ -0,0 +1,21 @@ +name: new_comment_digest +on: + schedule: # 08:30 daily + - cron: '30 8 * * *' + workflow_dispatch: +permissions: + contents: read + +jobs: + new_comment_digest: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v4 + with: + python-version: 3.x + - run: pip install requests + - run: scripts/gh_scripts/issue_comment_bot.py 24 "$SLACK_CHANNEL" "$SLACK_TOKEN" + env: + SLACK_TOKEN: ${{ secrets.SLACK_TOKEN }} + SLACK_CHANNEL: ${{ secrets.SLACK_CHANNEL_ABC_TEAM_PLUS }} From ec89090c79f2d0c838449081a6599741e1bc1162 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 17 Jan 2024 21:31:16 +0000 Subject: [PATCH 13/61] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- scripts/gh_scripts/issue_comment_bot.py | 41 ++++++++++++++----------- 1 file changed, 23 insertions(+), 18 deletions(-) diff --git a/scripts/gh_scripts/issue_comment_bot.py b/scripts/gh_scripts/issue_comment_bot.py index f5618f29996..9f12ec0f9c6 100755 --- a/scripts/gh_scripts/issue_comment_bot.py +++ b/scripts/gh_scripts/issue_comment_bot.py @@ -48,7 +48,7 @@ def fetch_issues(updated_since: str): """ # Make initial query for updated issues: query = f'repo:internetarchive/openlibrary is:open is:issue comments:>0 updated:>{updated_since}' - p: dict[str, str|int] = { + p: dict[str, str | int] = { 'q': query, 'per_page': 100, } @@ -93,12 +93,7 @@ def filter_issues(issues: list, since: datetime): for i in issues: # Fetch comments using URL from previous GitHub search results comments_url = i.get('comments_url') - resp = requests.get( - comments_url, - params={ - 'per_page': 100 - } - ) + resp = requests.get(comments_url, params={'per_page': 100}) # Ensure that we have the last page of comments links = resp.links @@ -123,12 +118,14 @@ def filter_issues(issues: list, since: datetime): last_commenter = last_comment['user']['login'] if last_commenter not in username_to_slack_id: lead_label = find_lead_label(i.get('labels', [])) - results.append({ - 'comment_url': last_comment['html_url'], - 'commenter': last_commenter, - 'issue_title': i['title'], - 'lead_label': lead_label, - }) + results.append( + { + 'comment_url': last_comment['html_url'], + 'commenter': last_commenter, + 'issue_title': i['title'], + 'lead_label': lead_label, + } + ) return results @@ -148,7 +145,12 @@ def find_lead_label(labels: list[dict[str, Any]]) -> str: return result -def publish_digest(issues: list[dict[str, str]], slack_channel: str, slack_token: str, hours_passed: int): +def publish_digest( + issues: list[dict[str, str]], + slack_channel: str, + slack_token: str, + hours_passed: int, +): """ Creates a threaded Slack messaged containing a digest of recently commented GitHub issues. @@ -156,7 +158,9 @@ def publish_digest(issues: list[dict[str, str]], slack_channel: str, slack_token will include a link to the comment, as well as additional information. """ # Create the parent message - parent_thread_msg = f'{len(issues)} new GitHub comment(s) since {hours_passed} hour(s) ago' + parent_thread_msg = ( + f'{len(issues)} new GitHub comment(s) since {hours_passed} hour(s) ago' + ) response = requests.post( 'https://slack.com/api/chat.postMessage', @@ -199,7 +203,9 @@ def comment_on_thread(message: str): if response.status_code != 200: # XXX : Check "ok" field for errors # XXX : Log this - print(f'Failed to POST slack message\n Status code: {response.status_code}\n Message: {message}') + print( + f'Failed to POST slack message\n Status code: {response.status_code}\n Message: {message}' + ) # XXX : Retry logic? for i in issues: @@ -238,12 +244,11 @@ def start_job(args: argparse.Namespace): """ since, date_string = time_since(args.hours) issues = fetch_issues(date_string) - filtered_issues = filter_issues(issues, since) # XXX : If we are only running this script daily, we can remove this condition to # always post a message to Slack. If the digest is ever not published, we'll know # that something is wrong with our script runner. - if filtered_issues: + if filtered_issues := filter_issues(issues, since): publish_digest(filtered_issues, args.channel, args.slack_token, args.hours) # XXX : Log this print('Digest posted to Slack.') From 00ffa6111c971a1c10a8edfb3c82b7084af2b864 Mon Sep 17 00:00:00 2001 From: James Champ Date: Wed, 17 Jan 2024 16:20:42 -0800 Subject: [PATCH 14/61] Fetch and filter assigned issues --- .github/workflows/auto_unassigner.yml | 21 ++ scripts/gh_scripts/auto_unassigner.mjs | 349 +++++++++++++++++++++++++ 2 files changed, 370 insertions(+) create mode 100644 .github/workflows/auto_unassigner.yml create mode 100644 scripts/gh_scripts/auto_unassigner.mjs diff --git a/.github/workflows/auto_unassigner.yml b/.github/workflows/auto_unassigner.yml new file mode 100644 index 00000000000..809dfe8f6b4 --- /dev/null +++ b/.github/workflows/auto_unassigner.yml @@ -0,0 +1,21 @@ +name: auto_unassigner +on: + schedule: # Run at 0800 AM, daily (runs before new_comment_digest workflow). + - cron: '0 8 * * *' + workflow_dispatch: # This can be run on-demand (do we want this?) +permissions: + contents: read # Double-check perms + issues: write + +jobs: + auto_unassigner: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v3 + with: + node-version: '20' + - run: npm install @octokit/action + - run: node scripts/gh_scripts/auto_unassigner.mjs --repoOwner 'jimchamp' + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/scripts/gh_scripts/auto_unassigner.mjs b/scripts/gh_scripts/auto_unassigner.mjs new file mode 100644 index 00000000000..15e07077a28 --- /dev/null +++ b/scripts/gh_scripts/auto_unassigner.mjs @@ -0,0 +1,349 @@ +/** + * Runs the auto-unassigner script. + * + * Optional parameters: + * --daysSince : Number : Issues that have had the same assignee for at least this many days are candidates for unassignment + * --repoOwner : String : Pass to run on a specific OpenLibrary fork + */ +import {Octokit} from "@octokit/action"; + +console.log('starting...') + +const DEFAULT_OPTIONS = { + daysSince: 14, + repoOwner: 'internetarchive' +} + +const passedArguments = parseArgs() + +/** + * Parses any arguments that were passed when this script was executed, and returns + * an object containing the arguments. + * + * Exits the script if an odd number of arguments are provided. The script takes only + * options as arguments, and we expect a space between the option flag and value: + * `--flag value_of_flag` + * + * @returns {Record} + */ +function parseArgs() { + const result = {} + // XXX : Does this check make sense? + if (process.argv.length % 2 !== 0) { + console.log('Unexpected number of arguments') + process.exit(1) + } + if (process.argv.length > 2) { + for (let i = 2, j = 3; i < process.argv.length; i+=2, j+=2) { + let arg = process.argv[i] + // Remove leading `-` characters + while (arg.charAt(0) === '-') { + arg = arg.substring(1) + } + result[arg] = process.argv[j] + } + } + + return result +} + +// Octokit is authenticated with the `GITHUB_TOKEN` that is added to the +// environment in the `auto_unassigner` workflow +const octokit = new Octokit(); + +/** + * List of GitHub usernames who, if assigned to an issue, should not be unassigned. + * @type {String[]} + * @see {excludeAssigneesFilter} + */ +const excludeAssignees = [] + +/** + * List of GitHub labels that, if on an issue, excludes the issue from automation. + * @type {String[]} + */ +const excludeLabels = ['no-automation'] + +/** + * Functions used to filter out issues that should have their assignees removed. + * + * Each function in this array should take an array of records as a parameter, and + * return a promise that resolves to an array of records. + * + * Functions will be called in order, and pass along their results to the next filter. + * If possible, long-running or otherwise expensive calls should be added to the end + * of this array. + * @type {CallableFunction[]} + * @see {filterIssues} + */ +const filters = [ + excludePullRequestsFilter, + excludeLabelsFilter, + excludeAssigneesFilter, + recentAssigneeFilter, + linkedPullRequestFilter +] + +/** + * Multiple filters will require data from the GitHub Timeline API before a decision + * about an issue can be made. In order to avoid redundant API calls, timeline results + * will be stored here. Issue number is used as the key. + * @type {Record} + */ +const issueTimelines = {} + +// XXX : What do we want to say when we remove assignees? +const issueComment = 'Assignees removed automatically after two weeks.' + +const mainOptions = Object.assign({}, DEFAULT_OPTIONS, passedArguments) +// `daysSince` will be a string if passed in from the command line +mainOptions.daysSince = Number(mainOptions.daysSince) + +await main() + +console.log('finished....') + +/** + * Runs the auto-unassigner job. + * + * @returns {Promise} + */ +async function main() { // XXX : Inject octokit for easier testing + const issues = await fetchIssues() + console.log(`Open issues with assignees: ${issues.length}`) + + if (issues.length === 0) { + console.log('No issues were returned by the initial query') + return + } + + const actionableIssues = await filterIssues(issues, filters) + console.log(`Issues remaining after filtering: ${actionableIssues.length}`) +} + +// START: API Calls +/** + * Returns all GitHub issues that are open and one or more assignees. + * + * __Important:__ GitHub's REST API considers every pull request to be an + * issue. Pull requests may be included in the results returned by this + * function, and can be identified by the presence of a `pull_request` key. + * + * @returns {Promise>} + * @see [GitHub REST documentation]{@link https://docs.github.com/en/rest/issues/issues?apiVersion=2022-11-28#list-repository-issues} + */ +async function fetchIssues() { + const result = await octokit.paginate('GET /repos/{owner}/{repo}/issues', { + owner: mainOptions.repoOwner, + repo: 'openlibrary', + headers: { + 'X-GitHub-Api-Version': '2022-11-28' + }, + assignee: '*', + state: 'open', + per_page: 100 + }) + + return result +} + +/** + * Returns the timeline of the given issue. + * + * Attempts to get the timeline from the `issueTimelines` store. If no + * timeline is found, calls GitHub's Timeline API and stores the result + * before returning. + * + * @param issue {Record} + * @returns {Promise>} + * @see {issueTimelines} + */ +async function getTimeline(issue) { + const issueNumber = issue.number + if (issueTimelines[issueNumber]) { + return issueTimelines[issueNumber] + } + + // Fetching timeline: + const repoUrl = issue.repository_url + const splitUrl = repoUrl.split('/') + const repoOwner = splitUrl[splitUrl.length - 2] + const timeline = await octokit.paginate( + octokit.request('GET /repos/{owner}/{repo}/issues/{issue_number}/timeline', { + owner: repoOwner, + repo: 'openlibrary', + issue_number: issueNumber, + headers: { + 'X-GitHub-Api-Version': '2022-11-28' + } + }) + ) + + // Store timeline for future use: + issueTimelines[issueNumber] = timeline + + return timeline +} + +/** + * Returns the results of filtering the given issues. + * + * The given filters are functions that are meant to be + * passed to Array. + * @param issues {Array} + * @param filters {Array} + * @returns {Promise>} + */ +async function filterIssues(issues, filters) { + let results = issues + + for (const f of filters) { + // Array.filter() doesn't handle filters that return promises. + // Wrapping this in Promise.all() ensures that the filter functions + // return the correct results. + results = await Promise.all(results.filter(f)) + } + return results +} + +// Filters: +/** + * Returns `false` if the given issue is a pull request. + * + * Necessary because GitHub's REST API considers pull requests to be a + * type of issue. + * + * @param issue {Record} + * @returns {Promise} + */ +async function excludePullRequestsFilter(issue) { + return !('pull_request' in issue) +} + +/** + * Returns `true` if the given issue has a label that is on the exclude list. + * + * Label matching is case-insensitive. + * + * @param issue {Record} + * @returns {Promise} + * @see {excludeLabels} + */ +async function excludeLabelsFilter(issue) { + const labels = issue.labels + for (const label of labels) { + if (excludeLabels.includes(label.name.toLowerCase())) { + return false + } + } + return true +} + +/** + * Returns `true` when all assignees to the given issue also appear on the exclude + * assignee list. + * + * __Important__: This function also updates the given issue. A `ol_unassign_ignore` flag + * is added to any `assignee` that appears on the exclude list. + * + * @param issue {Record} + * @returns {Promise} + * @see {excludeAssignees} + */ +async function excludeAssigneesFilter(issue) { + let allAssigneesExcluded = true + const assignees = issue.assignees + for (const assignee of assignees) { + const username = assignee.login + if (!excludeAssignees.includes(username)) { + allAssigneesExcluded = false + } else { + // Flag excluded assignees + assignee.ol_unassign_ignore = true + } + } + return !allAssigneesExcluded +} + +/** + * Returns `true` if any assignee to the given issue has been assigned + * longer than the `daysSince` configuration. + * + * __Important__: This function adds the `ol_unassign_ignore` flag to + * assignees that haven't yet been assigned for too long. + * + * @param issue {Record} + * @returns {Promise} + */ +async function recentAssigneeFilter(issue) { + const timeline = await getTimeline(issue) + const daysSince = mainOptions.daysSince + + const currentDate = new Date() + const assignees = issue.assignees + let result = true + + for (const assignee of assignees) { + if ('ol_unassign_ignore' in assignee) { + continue + } + const assignmentDate = getAssignmentDate(assignee, timeline) + const timeDelta = currentDate.getTime() - assignmentDate.getTime() + const daysPassed = timeDelta/(1000 * 60 * 60 * 24) + if (daysPassed > daysSince) { + result = false + } else { + assignee.ol_unassign_ignore = true + } + } + return result +} + +/** + * Returns the date that the given assignee was assigned to an issue. + * + * @param assignee {Record} + * @param issueTimeline {Record} + * @returns {Date} + */ +function getAssignmentDate(assignee, issueTimeline) { + const assigneeName = assignee.login + const assignmentEvent = issueTimeline.findLast((event) => { + return event.event === 'assigned' && event.assignee.login === assigneeName + }) + + if (!assignmentEvent) { // Somehow, the assignment event was not found + // Avoid accidental unassignment by sending the current time + return new Date() + } + + return new Date(assignmentEvent.created_at) +} + +/** + * Returns `true` if there is no open pull linked to the given issue's assignees. + * + * @param issue {Record} + * @returns {Promise} + */ +async function linkedPullRequestFilter(issue) { + const timeline = await getTimeline(issue) + const assignees = issue.assignees.filter((assignee) => !('ol_unassign_ignore' in assignee)) + const crossReferences = timeline.filter((event) => event.event === 'cross-referenced') + + for (const assignee of assignees) { + const linkedPullRequest = crossReferences.find((event) => { + const hasLinkedPullRequest = event.source.type === 'issue' && + event.source.issue.state === 'open' && + ('pull_request' in event.source.issue) && + event.source.issue.user.login === assignee.login && + event.source.issue.body.toLowerCase().includes(`closes #${issue.number}`) + if (hasLinkedPullRequest) { + return false + } + }) + } + + return true +} +// END: Issue Filtering From 273d99d4608d79216465d48608abd8bffece9447 Mon Sep 17 00:00:00 2001 From: James Champ Date: Tue, 23 Jan 2024 08:54:57 -0800 Subject: [PATCH 15/61] Add comments to filtered issues --- scripts/gh_scripts/auto_unassigner.mjs | 32 ++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/scripts/gh_scripts/auto_unassigner.mjs b/scripts/gh_scripts/auto_unassigner.mjs index 15e07077a28..19a35bb96df 100644 --- a/scripts/gh_scripts/auto_unassigner.mjs +++ b/scripts/gh_scripts/auto_unassigner.mjs @@ -119,6 +119,8 @@ async function main() { // XXX : Inject octokit for easier testing const actionableIssues = await filterIssues(issues, filters) console.log(`Issues remaining after filtering: ${actionableIssues.length}`) + + await commentOnIssue(180, 'Comment from automated workflow') } // START: API Calls @@ -185,6 +187,36 @@ async function getTimeline(issue) { return timeline } +/** + * Creates a new comment on the given issue. + * + * @param issueNumber {number} + * @param comment {string} + * @returns {Promise} `true` if the comment was created + */ +async function commentOnIssue(issueNumber, comment) { + return await octokit.request('POST /repos/{owner}/{repo}/issues/{issue_number}/comments', { + owner: mainOptions.repoOwner, + repo: 'openlibrary', + issue_number: issueNumber, + body: comment, + headers: { + 'X-GitHub-Api-Version': '2022-11-28' + }, + }) + .then(() => { + return true + }) + .catch((error) => { + // Promise is rejected if the call fails + console.log(`Failed to comment on issue #${issueNumber}`) + console.log(`Response status: ${error.status}`) + return false + }) +} +// END: API Calls + +// START: Issue Filtering /** * Returns the results of filtering the given issues. * From 4987e99528829abe18d2acb1ac1bbf0f3e1fe017 Mon Sep 17 00:00:00 2001 From: James Champ Date: Tue, 23 Jan 2024 10:43:11 -0800 Subject: [PATCH 16/61] Remove assignees from issues --- scripts/gh_scripts/auto_unassigner.mjs | 43 +++++++++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/scripts/gh_scripts/auto_unassigner.mjs b/scripts/gh_scripts/auto_unassigner.mjs index 19a35bb96df..5ae03d8bbdd 100644 --- a/scripts/gh_scripts/auto_unassigner.mjs +++ b/scripts/gh_scripts/auto_unassigner.mjs @@ -120,7 +120,21 @@ async function main() { // XXX : Inject octokit for easier testing const actionableIssues = await filterIssues(issues, filters) console.log(`Issues remaining after filtering: ${actionableIssues.length}`) - await commentOnIssue(180, 'Comment from automated workflow') + for (const issue of actionableIssues) { + const assigneesToRemove = [] + for (const assignee of issue.assignees) { + if (!('ol_unassign_ignore' in assignee)) { + assigneesToRemove.push(assignee.login) + } + } + + if (assigneesToRemove.length > 0) { + const wasRemoved = await unassignFromIssue(issue.number, assigneesToRemove) + if (wasRemoved) { + await commentOnIssue(issue.number, issueComment) + } + } + } } // START: API Calls @@ -187,6 +201,33 @@ async function getTimeline(issue) { return timeline } +/** + * Removes the given assignees from the issue identified by the given issue number. + * + * @param issueNumber {number} + * @param assignees {Array} + * @returns {Promise} `true` if unassignment operation was successful + */ +async function unassignFromIssue(issueNumber, assignees) { + return await octokit.request('DELETE /repos/{owner}/{repo}/issues/{issue_number}/assignees', { + owner: mainOptions.repoOwner, + repo: 'openlibrary', + issue_number: issueNumber, + assignees: assignees, + headers: { + 'X-GitHub-Api-Version': '2022-11-28' + } + }) + .then(() => { + return true + }) + .catch((error) => { + console.log(`Failed to remove assignees from issue #${issueNumber}`) + console.log(`Response status: ${error.status}`) + return false + }) +} + /** * Creates a new comment on the given issue. * From e9df97983f8a4e0453a5667e4bf2e46e337b2705 Mon Sep 17 00:00:00 2001 From: Drini Cami Date: Tue, 9 Jan 2024 14:15:22 -0500 Subject: [PATCH 17/61] Add sentry transactions to covers server --- conf/coverstore.yml | 1 + openlibrary/plugins/openlibrary/sentry.py | 4 +- openlibrary/utils/sentry.py | 66 ++++++++++++++--------- 3 files changed, 43 insertions(+), 28 deletions(-) diff --git a/conf/coverstore.yml b/conf/coverstore.yml index 2baaebdec59..44c538f481c 100644 --- a/conf/coverstore.yml +++ b/conf/coverstore.yml @@ -12,4 +12,5 @@ sentry: enabled: false # Dummy endpoint; where sentry logs are sent to dsn: 'https://examplePublicKey@o0.ingest.sentry.io/0' + traces_sample_rate: 1.0 environment: 'local' diff --git a/openlibrary/plugins/openlibrary/sentry.py b/openlibrary/plugins/openlibrary/sentry.py index e21ef55c4bd..ef729885c6e 100644 --- a/openlibrary/plugins/openlibrary/sentry.py +++ b/openlibrary/plugins/openlibrary/sentry.py @@ -1,6 +1,6 @@ import infogami from infogami.utils import delegate -from openlibrary.utils.sentry import Sentry, SentryProcessor +from openlibrary.utils.sentry import Sentry, InfogamiSentryProcessor sentry: Sentry | None = None @@ -12,4 +12,4 @@ def setup(): if sentry.enabled: sentry.init() delegate.add_exception_hook(lambda: sentry.capture_exception_webpy()) - delegate.app.add_processor(SentryProcessor()) + delegate.app.add_processor(InfogamiSentryProcessor(delegate.app)) diff --git a/openlibrary/utils/sentry.py b/openlibrary/utils/sentry.py index 8de2c91a923..cd17f266abb 100644 --- a/openlibrary/utils/sentry.py +++ b/openlibrary/utils/sentry.py @@ -76,6 +76,7 @@ def capture_exception(): return _internalerror() app.internalerror = capture_exception + app.add_processor(WebPySentryProcessor(app)) def capture_exception_webpy(self): with sentry_sdk.push_scope() as scope: @@ -97,12 +98,48 @@ def to_sentry_name(self) -> str: ) -class SentryProcessor: +class WebPySentryProcessor: + def __init__(self, app: web.application): + self.app = app + + def find_route_name(self) -> str: + handler, groups = self.app._match(self.app.mapping, web.ctx.path) + web.debug('ROUTE HANDLER', handler, groups) + return handler or '' + + def __call__(self, handler): + route_name = self.find_route_name() + hub = sentry_sdk.Hub.current + with sentry_sdk.Hub(hub) as hub: + with hub.configure_scope() as scope: + scope.clear_breadcrumbs() + scope.add_event_processor(add_web_ctx_to_event) + + environ = dict(web.ctx.env) + # Don't forward cookies to Sentry + if 'HTTP_COOKIE' in environ: + del environ['HTTP_COOKIE'] + + transaction = Transaction.continue_from_environ( + environ, + op="http.server", + name=route_name, + source=TRANSACTION_SOURCE_ROUTE, + ) + + with hub.start_transaction(transaction): + try: + return handler() + finally: + transaction.set_http_status(int(web.ctx.status.split()[0])) + + +class InfogamiSentryProcessor(WebPySentryProcessor): """ Processor to profile the webpage and send a transaction to Sentry. """ - def __call__(self, handler): + def find_route_name(self) -> str: def find_type() -> tuple[str, str] | None: return next( ( @@ -134,27 +171,4 @@ def find_route() -> InfogamiRoute: return result - route = find_route() - hub = sentry_sdk.Hub.current - with sentry_sdk.Hub(hub) as hub: - with hub.configure_scope() as scope: - scope.clear_breadcrumbs() - scope.add_event_processor(add_web_ctx_to_event) - - environ = dict(web.ctx.env) - # Don't forward cookies to Sentry - if 'HTTP_COOKIE' in environ: - del environ['HTTP_COOKIE'] - - transaction = Transaction.continue_from_environ( - environ, - op="http.server", - name=route.to_sentry_name(), - source=TRANSACTION_SOURCE_ROUTE, - ) - - with hub.start_transaction(transaction): - try: - return handler() - finally: - transaction.set_http_status(int(web.ctx.status.split()[0])) + return find_route().to_sentry_name() From a014cedcbfe8b06e5be076983ae5de9ff1ce9279 Mon Sep 17 00:00:00 2001 From: Drini Cami Date: Tue, 9 Jan 2024 14:30:28 -0500 Subject: [PATCH 18/61] Switch covers.GET to use return instead of raise This lets the requests correctly be reflected in Sentry as non-erroring --- openlibrary/coverstore/code.py | 24 ++++++++---------------- 1 file changed, 8 insertions(+), 16 deletions(-) diff --git a/openlibrary/coverstore/code.py b/openlibrary/coverstore/code.py index 0d34b7fb976..9900f2bc8eb 100644 --- a/openlibrary/coverstore/code.py +++ b/openlibrary/coverstore/code.py @@ -239,17 +239,9 @@ def notfound(): ): return read_file(config.default_image) elif is_valid_url(i.default): - raise web.seeother(i.default) + return web.seeother(i.default) else: - raise web.notfound("") - - def redirect(id): - size_part = size and ("-" + size) or "" - url = f"/{category}/id/{id}{size_part}.jpg" - - if query := web.ctx.env.get('QUERY_STRING'): - url += '?' + query - raise web.found(url) + return web.notfound("") if key == 'isbn': value = value.replace("-", "").strip() # strip hyphens from ISBN @@ -257,7 +249,7 @@ def redirect(id): elif key == 'ia': url = self.get_ia_cover_url(value, size) if url: - raise web.found(url) + return web.found(url) else: value = None # notfound or redirect to default. handled later. elif key != 'id': @@ -269,7 +261,7 @@ def redirect(id): # redirect to archive.org cluster for large size and original images whenever possible if size in ("L", "") and self.is_cover_in_cluster(value): url = zipview_url_from_id(int(value), size) - raise web.found(url) + return web.found(url) # covers_0008 batches [_00, _82] are tar'd / zip'd in archive.org items if isinstance(value, int) or value.isnumeric(): # noqa: SIM102 @@ -281,7 +273,7 @@ def redirect(id): item_file = f"{pid}{'-' + size.upper() if size else ''}" path = f"{item_id}/{item_tar}/{item_file}.jpg" protocol = web.ctx.protocol - raise web.found(f"{protocol}://archive.org/download/{path}") + return web.found(f"{protocol}://archive.org/download/{path}") d = self.get_details(value, size.lower()) if not d: @@ -291,7 +283,7 @@ def redirect(id): if key == 'id': etag = f"{d.id}-{size.lower()}" if not web.modified(trim_microsecond(d.created), etag=etag): - raise web.notmodified() + return web.notmodified() web.header('Cache-Control', 'public') # this image is not going to expire in next 100 years. @@ -306,14 +298,14 @@ def redirect(id): from openlibrary.coverstore import archive if d.id >= 8_820_000 and d.uploaded and '.zip' in d.filename: - raise web.found( + return web.found( archive.Cover.get_cover_url( d.id, size=size, protocol=web.ctx.protocol ) ) return read_image(d, size) except OSError: - raise web.notfound() + return web.notfound() def get_ia_cover_url(self, identifier, size="M"): url = "https://archive.org/metadata/%s/metadata" % identifier From a587095d936175e9bcc365956170d331ddf74c2f Mon Sep 17 00:00:00 2001 From: James Champ Date: Tue, 23 Jan 2024 12:42:25 -0800 Subject: [PATCH 19/61] Update filter functions --- scripts/gh_scripts/auto_unassigner.mjs | 201 +++++++++++++++---------- 1 file changed, 118 insertions(+), 83 deletions(-) diff --git a/scripts/gh_scripts/auto_unassigner.mjs b/scripts/gh_scripts/auto_unassigner.mjs index 5ae03d8bbdd..fa858f22c43 100644 --- a/scripts/gh_scripts/auto_unassigner.mjs +++ b/scripts/gh_scripts/auto_unassigner.mjs @@ -184,16 +184,15 @@ async function getTimeline(issue) { const repoUrl = issue.repository_url const splitUrl = repoUrl.split('/') const repoOwner = splitUrl[splitUrl.length - 2] - const timeline = await octokit.paginate( - octokit.request('GET /repos/{owner}/{repo}/issues/{issue_number}/timeline', { + const timeline = await octokit.paginate('GET /repos/{owner}/{repo}/issues/{issue_number}/timeline', { owner: repoOwner, repo: 'openlibrary', issue_number: issueNumber, + per_page: 100, headers: { 'X-GitHub-Api-Version': '2022-11-28' } }) - ) // Store timeline for future use: issueTimelines[issueNumber] = timeline @@ -271,105 +270,132 @@ async function filterIssues(issues, filters) { let results = issues for (const f of filters) { - // Array.filter() doesn't handle filters that return promises. - // Wrapping this in Promise.all() ensures that the filter functions - // return the correct results. - results = await Promise.all(results.filter(f)) + results = await f(results) } return results } // Filters: /** - * Returns `false` if the given issue is a pull request. + * Iterates over given issues and filters out pull requests. * * Necessary because GitHub's REST API considers pull requests to be a * type of issue. - * - * @param issue {Record} - * @returns {Promise} + * @param issues {Array} + * @returns {Promise>} */ -async function excludePullRequestsFilter(issue) { - return !('pull_request' in issue) +async function excludePullRequestsFilter(issues) { + const results = [] + for (const issue of issues) { + if (!('pull_request' in issue)) { + results.push(issue) + } + } + return results } /** - * Returns `true` if the given issue has a label that is on the exclude list. - * - * Label matching is case-insensitive. + * Checks each given issue and returns array of issues that do not have + * an exclusion label. * - * @param issue {Record} - * @returns {Promise} + * @param issues {Array} + * @returns {Promise>} * @see {excludeLabels} */ -async function excludeLabelsFilter(issue) { - const labels = issue.labels - for (const label of labels) { - if (excludeLabels.includes(label.name.toLowerCase())) { - return false +async function excludeLabelsFilter(issues) { + const results = [] + for (const issue of issues) { + let hasLabel = false + const labels = issue.labels + for (const label of labels) { + if (excludeLabels.includes(label.name)) { + hasLabel = true + } + } + if (!hasLabel) { + results.push(issue) } } - return true + + return results } /** - * Returns `true` when all assignees to the given issue also appear on the exclude - * assignee list. + * Checks each given issue and returns array of issues that have at least + * one assignee who is not on the exclusion list. * - * __Important__: This function also updates the given issue. A `ol_unassign_ignore` flag - * is added to any `assignee` that appears on the exclude list. + * __Important__: This function also updates the given issue. A `ol_unassign_ignore` + * flag is added to any `assignee` that appears on the exclude list. * - * @param issue {Record} - * @returns {Promise} + * @param issues {Array} + * @returns {Promise>} * @see {excludeAssignees} */ -async function excludeAssigneesFilter(issue) { - let allAssigneesExcluded = true - const assignees = issue.assignees - for (const assignee of assignees) { - const username = assignee.login - if (!excludeAssignees.includes(username)) { - allAssigneesExcluded = false - } else { - // Flag excluded assignees - assignee.ol_unassign_ignore = true +async function excludeAssigneesFilter(issues) { + const results = [] + + for (const issue of issues) { + let allAssigneesExcluded = true + const assignees = issue.assignees + + for (const assignee of assignees) { + const username = assignee.login + if (!excludeAssignees.includes(username)) { + allAssigneesExcluded = false + } else { + // Flag excluded assignees + assignee.ol_unassign_ignore = true + } + } + + if (!allAssigneesExcluded) { + results.push(issue) } } - return !allAssigneesExcluded + + return results } /** - * Returns `true` if any assignee to the given issue has been assigned - * longer than the `daysSince` configuration. + * Iterates over given issues, returning array of issues that have stale + * assignees. * * __Important__: This function adds the `ol_unassign_ignore` flag to * assignees that haven't yet been assigned for too long. - * - * @param issue {Record} - * @returns {Promise} + * @param issues {Array} + * @returns {Promise>} */ -async function recentAssigneeFilter(issue) { - const timeline = await getTimeline(issue) - const daysSince = mainOptions.daysSince +async function recentAssigneeFilter(issues) { + const results = [] - const currentDate = new Date() - const assignees = issue.assignees - let result = true + for (const issue of issues) { + const timeline = await getTimeline(issue) + const daysSince = mainOptions.daysSince - for (const assignee of assignees) { - if ('ol_unassign_ignore' in assignee) { - continue + const currentDate = new Date() + const assignees = issue.assignees + let staleAssigneeFound = false + + for (const assignee of assignees) { + if ('ol_unassign_ignore' in assignee) { + continue + } + const assignmentDate = getAssignmentDate(assignee, timeline) + const timeDelta = currentDate.getTime() - assignmentDate.getTime() + const daysPassed = timeDelta/(1000 * 60 * 60 * 24) + if (daysPassed > daysSince) { + staleAssigneeFound = true + } else { + assignee.ol_unassign_ignore = true + } } - const assignmentDate = getAssignmentDate(assignee, timeline) - const timeDelta = currentDate.getTime() - assignmentDate.getTime() - const daysPassed = timeDelta/(1000 * 60 * 60 * 24) - if (daysPassed > daysSince) { - result = false - } else { - assignee.ol_unassign_ignore = true + + if (staleAssigneeFound) { + results.push(issue) } } - return result + + return results } /** @@ -394,29 +420,38 @@ function getAssignmentDate(assignee, issueTimeline) { } /** - * Returns `true` if there is no open pull linked to the given issue's assignees. + * Iterates over given issues, and returns array containing issues that + * have no linked pull requests that are open. * - * @param issue {Record} - * @returns {Promise} + * @param issues {Array} + * @returns {Promise<*[]>} */ -async function linkedPullRequestFilter(issue) { - const timeline = await getTimeline(issue) - const assignees = issue.assignees.filter((assignee) => !('ol_unassign_ignore' in assignee)) - const crossReferences = timeline.filter((event) => event.event === 'cross-referenced') - - for (const assignee of assignees) { - const linkedPullRequest = crossReferences.find((event) => { - const hasLinkedPullRequest = event.source.type === 'issue' && - event.source.issue.state === 'open' && - ('pull_request' in event.source.issue) && - event.source.issue.user.login === assignee.login && - event.source.issue.body.toLowerCase().includes(`closes #${issue.number}`) - if (hasLinkedPullRequest) { - return false - } - }) +async function linkedPullRequestFilter(issues) { + const results = [] + for (const issue of issues) { + const timeline = await getTimeline(issue) + const assignees = issue.assignees.filter((assignee) => !('ol_unassign_ignore' in assignee)) + const crossReferences = timeline.filter((event) => event.event === 'cross-referenced') + + let noLinkedPullRequest = true + for (const assignee of assignees) { + const linkedPullRequest = crossReferences.find((event) => { + const hasLinkedPullRequest = event.source.type === 'issue' && + event.source.issue.state === 'open' && + ('pull_request' in event.source.issue) && + event.source.issue.user.login === assignee.login && + event.source.issue.body.toLowerCase().includes(`closes #${issue.number}`) + if (hasLinkedPullRequest) { + noLinkedPullRequest = false + } + }) + } + + if (noLinkedPullRequest) { + results.push(issue) + } } - return true + return results } // END: Issue Filtering From c1e9ed86ebc346eca3d5fe427ef04e07e2589734 Mon Sep 17 00:00:00 2001 From: James Champ Date: Tue, 23 Jan 2024 14:11:44 -0800 Subject: [PATCH 20/61] Code clean-up --- scripts/gh_scripts/auto_unassigner.mjs | 89 +++++++++++++------------- 1 file changed, 46 insertions(+), 43 deletions(-) diff --git a/scripts/gh_scripts/auto_unassigner.mjs b/scripts/gh_scripts/auto_unassigner.mjs index fa858f22c43..7d5af7f4b6f 100644 --- a/scripts/gh_scripts/auto_unassigner.mjs +++ b/scripts/gh_scripts/auto_unassigner.mjs @@ -7,7 +7,7 @@ */ import {Octokit} from "@octokit/action"; -console.log('starting...') +console.log('Script starting...') const DEFAULT_OPTIONS = { daysSince: 14, @@ -16,41 +16,22 @@ const DEFAULT_OPTIONS = { const passedArguments = parseArgs() -/** - * Parses any arguments that were passed when this script was executed, and returns - * an object containing the arguments. - * - * Exits the script if an odd number of arguments are provided. The script takes only - * options as arguments, and we expect a space between the option flag and value: - * `--flag value_of_flag` - * - * @returns {Record} - */ -function parseArgs() { - const result = {} - // XXX : Does this check make sense? - if (process.argv.length % 2 !== 0) { - console.log('Unexpected number of arguments') - process.exit(1) - } - if (process.argv.length > 2) { - for (let i = 2, j = 3; i < process.argv.length; i+=2, j+=2) { - let arg = process.argv[i] - // Remove leading `-` characters - while (arg.charAt(0) === '-') { - arg = arg.substring(1) - } - result[arg] = process.argv[j] - } - } - - return result -} +const mainOptions = Object.assign({}, DEFAULT_OPTIONS, passedArguments) +// `daysSince` will be a string if passed in from the command line +mainOptions.daysSince = Number(mainOptions.daysSince) // Octokit is authenticated with the `GITHUB_TOKEN` that is added to the // environment in the `auto_unassigner` workflow const octokit = new Octokit(); +// XXX : What do we want to say when we remove assignees? +/** + * Comment that will be left on issue when assignee is removed. + * + * @type {string} + */ +const issueComment = `Assignees removed automatically after ${mainOptions.daysSince} days.` + /** * List of GitHub usernames who, if assigned to an issue, should not be unassigned. * @type {String[]} @@ -92,16 +73,9 @@ const filters = [ */ const issueTimelines = {} -// XXX : What do we want to say when we remove assignees? -const issueComment = 'Assignees removed automatically after two weeks.' - -const mainOptions = Object.assign({}, DEFAULT_OPTIONS, passedArguments) -// `daysSince` will be a string if passed in from the command line -mainOptions.daysSince = Number(mainOptions.daysSince) - await main() -console.log('finished....') +console.log('Script terminated...') /** * Runs the auto-unassigner job. @@ -137,6 +111,37 @@ async function main() { // XXX : Inject octokit for easier testing } } +/** + * Parses any arguments that were passed when this script was executed, and returns + * an object containing the arguments. + * + * Exits the script if an odd number of arguments are provided. The script takes only + * options as arguments, and we expect a space between the option flag and value: + * `--flag value_of_flag` + * + * @returns {Record} + */ +function parseArgs() { + const result = {} + // XXX : Does this check make sense? + if (process.argv.length % 2 !== 0) { + console.log('Unexpected number of arguments') + process.exit(1) + } + if (process.argv.length > 2) { + for (let i = 2, j = 3; i < process.argv.length; i+=2, j+=2) { + let arg = process.argv[i] + // Remove leading `-` characters + while (arg.charAt(0) === '-') { + arg = arg.substring(1) + } + result[arg] = process.argv[j] + } + } + + return result +} + // START: API Calls /** * Returns all GitHub issues that are open and one or more assignees. @@ -149,7 +154,7 @@ async function main() { // XXX : Inject octokit for easier testing * @see [GitHub REST documentation]{@link https://docs.github.com/en/rest/issues/issues?apiVersion=2022-11-28#list-repository-issues} */ async function fetchIssues() { - const result = await octokit.paginate('GET /repos/{owner}/{repo}/issues', { + return await octokit.paginate('GET /repos/{owner}/{repo}/issues', { owner: mainOptions.repoOwner, repo: 'openlibrary', headers: { @@ -159,8 +164,6 @@ async function fetchIssues() { state: 'open', per_page: 100 }) - - return result } /** @@ -435,7 +438,7 @@ async function linkedPullRequestFilter(issues) { let noLinkedPullRequest = true for (const assignee of assignees) { - const linkedPullRequest = crossReferences.find((event) => { + crossReferences.find((event) => { const hasLinkedPullRequest = event.source.type === 'issue' && event.source.issue.state === 'open' && ('pull_request' in event.source.issue) && From b791d03a8ff83e388453a651357b33ca91314d8e Mon Sep 17 00:00:00 2001 From: James Champ Date: Tue, 23 Jan 2024 14:26:10 -0800 Subject: [PATCH 21/61] Restrict action to repo that triggered workflow --- .github/workflows/auto_unassigner.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/auto_unassigner.yml b/.github/workflows/auto_unassigner.yml index 809dfe8f6b4..bd5a311f6d3 100644 --- a/.github/workflows/auto_unassigner.yml +++ b/.github/workflows/auto_unassigner.yml @@ -4,7 +4,7 @@ on: - cron: '0 8 * * *' workflow_dispatch: # This can be run on-demand (do we want this?) permissions: - contents: read # Double-check perms + contents: read issues: write jobs: @@ -16,6 +16,6 @@ jobs: with: node-version: '20' - run: npm install @octokit/action - - run: node scripts/gh_scripts/auto_unassigner.mjs --repoOwner 'jimchamp' + - run: node scripts/gh_scripts/auto_unassigner.mjs --repoOwner $GITHUB_REPOSITORY_OWNER env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From b7f8308e2caa7fb98cf3860fb39dbff8d9cc6412 Mon Sep 17 00:00:00 2001 From: James Champ Date: Tue, 23 Jan 2024 14:45:53 -0800 Subject: [PATCH 22/61] Add README.md for script --- scripts/gh_scripts/README.md | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 scripts/gh_scripts/README.md diff --git a/scripts/gh_scripts/README.md b/scripts/gh_scripts/README.md new file mode 100644 index 00000000000..020ed75b1be --- /dev/null +++ b/scripts/gh_scripts/README.md @@ -0,0 +1,26 @@ +# GitHub Project Management Scripts + +This directory contains scripts that the Open Library team uses to interact with this GitHub repository. + +## `auto_unassigner.mjs` + +This script fetches all open issues that have assignees and automatically removes assignees that meet the following criteria: +- Assignee has been assigned for more than 14 days and has not created and linked a pull request to the issue. +- Assignee's GitHub username does not appear on the exclude list. + +This script skips over issues that have the `no-automation` label. + +This script takes two options: +- `--daysSince` Integer that defines the number of days until an assignee is stale. Defaults to `14`. +- `--repoOwner` String that defines the specific `openlibrary` repository. Defaults to `internetarchive`. + +> [!IMPORTANT] +> Include a space between an option and its value when calling this script. + +__Correct:__ +`node auto_unassigner.mjs --daysSince 21` + +__Incorrect:__ +`node auto_unassigner.mjs --daysSince=21` + +The GitHub action that runs this script automatically sets `--repoOwner` to the owner of the repository that triggered the action. From 987759c830962a062c9160874d07d308f2d3e8a2 Mon Sep 17 00:00:00 2001 From: James Champ Date: Tue, 23 Jan 2024 14:49:23 -0800 Subject: [PATCH 23/61] Add staff to exclude list --- scripts/gh_scripts/auto_unassigner.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/gh_scripts/auto_unassigner.mjs b/scripts/gh_scripts/auto_unassigner.mjs index 7d5af7f4b6f..aa5cddd5360 100644 --- a/scripts/gh_scripts/auto_unassigner.mjs +++ b/scripts/gh_scripts/auto_unassigner.mjs @@ -37,7 +37,7 @@ const issueComment = `Assignees removed automatically after ${mainOptions.daysSi * @type {String[]} * @see {excludeAssigneesFilter} */ -const excludeAssignees = [] +const excludeAssignees = ['mekarpeles', 'cdrini', 'scottbarnes', 'seabelis', 'hornc', 'jimchamp'] /** * List of GitHub labels that, if on an issue, excludes the issue from automation. From b7cb1f45a90e78ea16462c447dd30fe94fb582af Mon Sep 17 00:00:00 2001 From: James Champ Date: Tue, 23 Jan 2024 15:19:15 -0800 Subject: [PATCH 24/61] Update pre-commit hooks to skip `*.mjs` files --- .pre-commit-config.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index ab3ba6b3b73..fcaab85073e 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -47,6 +47,7 @@ repos: - id: codespell # See pyproject.toml for args additional_dependencies: - tomli + args: ["--skip", "*.mjs"] - repo: https://github.com/MarcoGorelli/cython-lint rev: v0.16.0 From caa5b28c5889541461b6d92b457d854ae087b32a Mon Sep 17 00:00:00 2001 From: Mek Date: Wed, 24 Jan 2024 11:32:15 -0800 Subject: [PATCH 25/61] collect anonymized login stats per day --- openlibrary/plugins/upstream/account.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openlibrary/plugins/upstream/account.py b/openlibrary/plugins/upstream/account.py index 3dd96f954ee..20b7819effa 100644 --- a/openlibrary/plugins/upstream/account.py +++ b/openlibrary/plugins/upstream/account.py @@ -405,7 +405,6 @@ def POST(self): config.login_cookie_name, web.ctx.conn.get_auth_token(), expires=expires ) ol_account = OpenLibraryAccount.get(email=email) - if ol_account and ol_account.get_user().get_safe_mode() == 'yes': web.setcookie('sfw', 'yes', expires=expires) blacklist = [ @@ -414,6 +413,7 @@ def POST(self): ] if i.redirect == "" or any(path in i.redirect for path in blacklist): i.redirect = "/account/books" + stats.increment('ol.account.xauth.login') raise web.seeother(i.redirect) def POST_resend_verification_email(self, i): From 0e4aa8f904b1e0372301f9128bba3cf2352caecc Mon Sep 17 00:00:00 2001 From: Rebecca Shoptaw Date: Thu, 25 Jan 2024 09:23:01 -0500 Subject: [PATCH 26/61] Switch to built-in HTML url validation. --- openlibrary/templates/covers/add.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openlibrary/templates/covers/add.html b/openlibrary/templates/covers/add.html index f4eaf93fee2..d7a25f6093c 100644 --- a/openlibrary/templates/covers/add.html +++ b/openlibrary/templates/covers/add.html @@ -63,7 +63,7 @@
- +
From 23c30732d0783bf53d3e6510402b2c2fbb7c27a9 Mon Sep 17 00:00:00 2001 From: Rebecca Shoptaw Date: Thu, 25 Jan 2024 09:23:42 -0500 Subject: [PATCH 27/61] Remove outdated js url validation. --- openlibrary/plugins/openlibrary/js/covers.js | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/openlibrary/plugins/openlibrary/js/covers.js b/openlibrary/plugins/openlibrary/js/covers.js index 4aeb0fea338..e9bb687891f 100644 --- a/openlibrary/plugins/openlibrary/js/covers.js +++ b/openlibrary/plugins/openlibrary/js/covers.js @@ -60,20 +60,9 @@ export function initCoversAddManage() { var url = val('#imageUrl'); var coverid = val('#coverid'); - if (file === '' && (url === '' || url === 'http://') && coverid === '') { + if (file === '' && url === '' && coverid === '') { return error('Please choose an image or provide a URL.', event); } - - function test_url(url) { - var obj = { - optional: function () { return false; } - } - return window.$.validator.url.apply(obj, [url, null]); - } - - if (url !== '' && url !== 'http://' && !test_url(url)) { - return error('Please provide a valid URL.'); - } }); // Clicking a cover should set the form value to the data-id of that cover From a90754433ee80b28d80ccff28174aa9ce3f9a0e8 Mon Sep 17 00:00:00 2001 From: Rebecca Shoptaw Date: Thu, 25 Jan 2024 10:27:05 -0500 Subject: [PATCH 28/61] Update placeholder text. --- openlibrary/templates/covers/add.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openlibrary/templates/covers/add.html b/openlibrary/templates/covers/add.html index d7a25f6093c..5b9e91249c7 100644 --- a/openlibrary/templates/covers/add.html +++ b/openlibrary/templates/covers/add.html @@ -63,7 +63,7 @@
- +
From b7f406f355cd0d949e3c350cb4d1b50afca4c096 Mon Sep 17 00:00:00 2001 From: James Champ Date: Thu, 25 Jan 2024 08:09:28 -0800 Subject: [PATCH 29/61] update `setup-node` and `cache` actions to v4 --- .github/workflows/auto_unassigner.yml | 2 +- .github/workflows/javascript_tests.yml | 4 ++-- .github/workflows/python_tests.yml | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/auto_unassigner.yml b/.github/workflows/auto_unassigner.yml index bd5a311f6d3..15287fefbdd 100644 --- a/.github/workflows/auto_unassigner.yml +++ b/.github/workflows/auto_unassigner.yml @@ -12,7 +12,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - uses: actions/setup-node@v3 + - uses: actions/setup-node@v4 with: node-version: '20' - run: npm install @octokit/action diff --git a/.github/workflows/javascript_tests.yml b/.github/workflows/javascript_tests.yml index 12440d142d4..07407967a95 100644 --- a/.github/workflows/javascript_tests.yml +++ b/.github/workflows/javascript_tests.yml @@ -22,11 +22,11 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - uses: actions/setup-node@v3 + - uses: actions/setup-node@v4 with: # Should match what's in our Dockerfile node-version: '20' # Also update the `key` in the `with` map, below - - uses: actions/cache@v3 + - uses: actions/cache@v4 id: npm-cache with: # Caching node_modules isn't recommended because it can break across diff --git a/.github/workflows/python_tests.yml b/.github/workflows/python_tests.yml index 1d33c3c1922..e9ac421482b 100644 --- a/.github/workflows/python_tests.yml +++ b/.github/workflows/python_tests.yml @@ -25,7 +25,7 @@ jobs: - uses: actions/setup-python@v4 with: python-version-file: pyproject.toml - - uses: actions/cache@v3 + - uses: actions/cache@v4 with: path: ${{ env.pythonLocation }} key: ${{ runner.os }}-venv-${{ env.pythonLocation }}-${{ hashFiles('requirements*.txt') }} From 4313400aad4433e331d68e15467e89c401a135da Mon Sep 17 00:00:00 2001 From: Drini Cami Date: Thu, 25 Jan 2024 17:23:05 -0500 Subject: [PATCH 30/61] Feedback from code review --- openlibrary/templates/covers/add.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openlibrary/templates/covers/add.html b/openlibrary/templates/covers/add.html index 5b9e91249c7..3137bff19dc 100644 --- a/openlibrary/templates/covers/add.html +++ b/openlibrary/templates/covers/add.html @@ -63,7 +63,7 @@
- +
From 103a0550100a55ce7007744e3da109770b743415 Mon Sep 17 00:00:00 2001 From: Drini Cami Date: Thu, 25 Jan 2024 17:33:36 -0500 Subject: [PATCH 31/61] Update styling for input type=url --- static/css/components/cbox.less | 3 +++ 1 file changed, 3 insertions(+) diff --git a/static/css/components/cbox.less b/static/css/components/cbox.less index affe5359a88..11c51110507 100644 --- a/static/css/components/cbox.less +++ b/static/css/components/cbox.less @@ -194,6 +194,7 @@ div.floater { text-align: center; } input[type=text], + input[type=url], input[type=file] { font-size: 1.125em; font-family: @lucida_sans_serif-1; @@ -273,6 +274,7 @@ div.floaterAdd { } input[type=text], + input[type=url], input[type=file] { width: 350px; } @@ -289,6 +291,7 @@ div.floaterAdd { .input, .label, input[type=text], + input[type=url], textarea { width: 560px; resize: none; From 0c682169ccdad0c737cc1d9d94802d9fd7936e2c Mon Sep 17 00:00:00 2001 From: Rebecca Shoptaw Date: Wed, 24 Jan 2024 16:07:19 -0500 Subject: [PATCH 32/61] Add loading message data attribute to cover add HTML. --- openlibrary/templates/covers/add.html | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/openlibrary/templates/covers/add.html b/openlibrary/templates/covers/add.html index 3137bff19dc..f03652711bf 100644 --- a/openlibrary/templates/covers/add.html +++ b/openlibrary/templates/covers/add.html @@ -68,7 +68,9 @@
- + $_("Cancel")
From c58256baf9a5556f03852a0c77e9d1f45ae85606 Mon Sep 17 00:00:00 2001 From: Rebecca Shoptaw Date: Wed, 24 Jan 2024 16:08:28 -0500 Subject: [PATCH 33/61] Create and call function to disable Submit button and show loading text on form submit. --- openlibrary/plugins/openlibrary/js/covers.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/openlibrary/plugins/openlibrary/js/covers.js b/openlibrary/plugins/openlibrary/js/covers.js index e9bb687891f..d580ca74ab2 100644 --- a/openlibrary/plugins/openlibrary/js/covers.js +++ b/openlibrary/plugins/openlibrary/js/covers.js @@ -56,6 +56,13 @@ function add_iframe(selector, src) { // covers/manage.html and covers/add.html export function initCoversAddManage() { $('#addcover-form').on('submit', function (event) { + const addLoadingStyling = () => { + let btn = $("#imageUpload"); + btn.prop("disabled", true).html(btn.data("loading-text")); + } + + addLoadingStyling(); + var file = val('#coverFile'); var url = val('#imageUrl'); var coverid = val('#coverid'); From 7234956f11c9c2f842b893988e55ddb25df9805f Mon Sep 17 00:00:00 2001 From: Rebecca Shoptaw Date: Wed, 24 Jan 2024 18:46:43 -0500 Subject: [PATCH 34/61] Move uploading button styling so it runs after error checks. --- openlibrary/plugins/openlibrary/js/covers.js | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/openlibrary/plugins/openlibrary/js/covers.js b/openlibrary/plugins/openlibrary/js/covers.js index d580ca74ab2..a471ad1849e 100644 --- a/openlibrary/plugins/openlibrary/js/covers.js +++ b/openlibrary/plugins/openlibrary/js/covers.js @@ -56,13 +56,6 @@ function add_iframe(selector, src) { // covers/manage.html and covers/add.html export function initCoversAddManage() { $('#addcover-form').on('submit', function (event) { - const addLoadingStyling = () => { - let btn = $("#imageUpload"); - btn.prop("disabled", true).html(btn.data("loading-text")); - } - - addLoadingStyling(); - var file = val('#coverFile'); var url = val('#imageUrl'); var coverid = val('#coverid'); @@ -70,6 +63,9 @@ export function initCoversAddManage() { if (file === '' && url === '' && coverid === '') { return error('Please choose an image or provide a URL.', event); } + + let btn = $("#imageUpload"); + btn.prop("disabled", true).html(btn.data("loading-text")); }); // Clicking a cover should set the form value to the data-id of that cover From 3e9e522f7aa7e06da22b4cda8d91f41ef0720b28 Mon Sep 17 00:00:00 2001 From: Rebecca Shoptaw Date: Fri, 26 Jan 2024 13:37:24 -0500 Subject: [PATCH 35/61] Prevent val function from causing type error. --- openlibrary/plugins/openlibrary/js/covers.js | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/openlibrary/plugins/openlibrary/js/covers.js b/openlibrary/plugins/openlibrary/js/covers.js index a471ad1849e..ba4703172ed 100644 --- a/openlibrary/plugins/openlibrary/js/covers.js +++ b/openlibrary/plugins/openlibrary/js/covers.js @@ -38,7 +38,12 @@ export function initCoversChange() { } function val(selector) { - return $(selector).val().trim(); + const val = $(selector).val(); + if (val) { + return val.trim(); + } else { + return val; + } } function error(message, event) { @@ -60,12 +65,12 @@ export function initCoversAddManage() { var url = val('#imageUrl'); var coverid = val('#coverid'); - if (file === '' && url === '' && coverid === '') { + if (!file && !url && !coverid) { return error('Please choose an image or provide a URL.', event); } - let btn = $("#imageUpload"); - btn.prop("disabled", true).html(btn.data("loading-text")); + const btn = $('#imageUpload'); + btn.prop('disabled', true).html(btn.data('loading-text')); }); // Clicking a cover should set the form value to the data-id of that cover From efa7faedf52c7280d044ee9c4dd61de350ad8a65 Mon Sep 17 00:00:00 2001 From: David Dominguez <352985+ddominguez@users.noreply.github.com> Date: Sat, 27 Jan 2024 00:40:52 -0500 Subject: [PATCH 36/61] Fix broken template rendering on /search requests (#8757) * Fix broken template rendering on /search requests Work search template expects a SearchResponse object, but None was being sent if the request did not have any params. Updated request to always send a SearchResponse object. Also, removed an unused import. --- openlibrary/plugins/worksearch/code.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/openlibrary/plugins/worksearch/code.py b/openlibrary/plugins/worksearch/code.py index 1563e06f251..8b9b8264f53 100644 --- a/openlibrary/plugins/worksearch/code.py +++ b/openlibrary/plugins/worksearch/code.py @@ -19,7 +19,6 @@ from openlibrary.core import cache from openlibrary.core.lending import add_availability from openlibrary.core.models import Edition -from openlibrary.plugins.inside.code import fulltext_search from openlibrary.plugins.openlibrary.processors import urlsafe from openlibrary.plugins.upstream.utils import ( get_language_name, @@ -491,11 +490,14 @@ def GET(self): page = int(param.get('page', 1)) sort = param.get('sort', None) rows = 20 - search_response: SearchResponse | None = None if param: search_response = do_search( param, sort, page, rows=rows, spellcheck_count=3 ) + else: + search_response = SearchResponse( + facet_counts=None, sort='', docs=[], num_found=0, solr_select='' + ) return render.work_search( ' '.join(q_list), search_response, From 9592bb30e8e1c6d7d80d0fdaa04c92bd8c5c32f7 Mon Sep 17 00:00:00 2001 From: James Champ Date: Sat, 27 Jan 2024 04:46:16 -0800 Subject: [PATCH 37/61] Always publish digest --- scripts/gh_scripts/issue_comment_bot.py | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/scripts/gh_scripts/issue_comment_bot.py b/scripts/gh_scripts/issue_comment_bot.py index 9f12ec0f9c6..7aa463ba134 100755 --- a/scripts/gh_scripts/issue_comment_bot.py +++ b/scripts/gh_scripts/issue_comment_bot.py @@ -245,16 +245,9 @@ def start_job(args: argparse.Namespace): since, date_string = time_since(args.hours) issues = fetch_issues(date_string) - # XXX : If we are only running this script daily, we can remove this condition to - # always post a message to Slack. If the digest is ever not published, we'll know - # that something is wrong with our script runner. - if filtered_issues := filter_issues(issues, since): - publish_digest(filtered_issues, args.channel, args.slack_token, args.hours) - # XXX : Log this - print('Digest posted to Slack.') - else: - # XXX : Log this - print('No issues needing attention found.') + filtered_issues = filter_issues(issues, since) + publish_digest(filtered_issues, args.channel, args.slack_token, args.hours) + print('Digest posted to Slack.') def _get_parser() -> argparse.ArgumentParser: From 07ccc86734209c96f802f4612481766718c91fcf Mon Sep 17 00:00:00 2001 From: justcomplaining Date: Sun, 28 Jan 2024 00:04:43 +0100 Subject: [PATCH 38/61] Update messages.po --- openlibrary/i18n/de/messages.po | 4075 ++++++++++++++++++------------- 1 file changed, 2344 insertions(+), 1731 deletions(-) diff --git a/openlibrary/i18n/de/messages.po b/openlibrary/i18n/de/messages.po index a27f5b6600a..176fd193cb2 100644 --- a/openlibrary/i18n/de/messages.po +++ b/openlibrary/i18n/de/messages.po @@ -7,43 +7,26 @@ msgid "" msgstr "" "Project-Id-Version: Open Library VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2021-11-23 08:44+0000\n" -"PO-Revision-Date: 2021-11-23 14:35+0100\n" +"POT-Creation-Date: 2023-01-06 20:53+0000\n" +"PO-Revision-Date: 2024-01-27 23:57+0100\n" "Last-Translator: justcomplaining \n" -"Language: de\n" "Language-Team: German\n" -"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"Language: de\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" "Generated-By: Babel 2.9.1\n" -"X-Generator: Poedit 3.0\n" +"X-Generator: Poedit 3.0.1\n" -#: account.html:7 account.html:12 account/notifications.html:13 -#: account/privacy.html:13 lib/nav_head.html:15 lib/search_head.html:71 -#: type/user/view.html:27 +#: account.html:7 account.html:15 account/notifications.html:13 +#: account/privacy.html:13 lib/nav_head.html:15 type/user/view.html:27 msgid "Settings" msgstr "Einstellungen" -#: account.html:16 -msgid "Change Password" -msgstr "Passwort ändern" - -#: account.html:17 -msgid "Update Email Address" -msgstr "E-Mail-Adresse aktualisieren" - -#: account.html:18 -msgid "Your Notifications" -msgstr "Ihre Benachrichtigungen" - -#: account.html:19 -msgid "Internet Archive Announcements" -msgstr "Benachrichtigungen vom Internet Archive" - #: account.html:20 -msgid "Your Privacy Settings" -msgstr "Ihre Datenschutz-Einstellungen" +msgid "Settings & Privacy" +msgstr "Einstellungen & Datenschutz" #: account.html:21 databarDiff.html:12 msgid "View" @@ -53,8 +36,10 @@ msgstr "Anzeige" msgid "or" msgstr "oder" -#: account.html:21 databarHistory.html:27 databarTemplate.html:13 -#: databarView.html:34 type/edition/view.html:359 type/work/view.html:359 +#: account.html:21 check_ins/check_in_prompt.html:26 +#: check_ins/reading_goal_progress.html:26 databarHistory.html:27 +#: databarTemplate.html:13 databarView.html:34 +#: type/edition/compact_title.html:11 msgid "Edit" msgstr "Bearbeiten" @@ -74,7 +59,31 @@ msgstr "Listen anzeigen oder bearbeiten" msgid "Import and Export Options" msgstr "Import- und Export-Optionen" +#: account.html:25 +msgid "Manage Privacy Settings" +msgstr "Datenschutz-Einstellungen verwalten" + #: account.html:26 +msgid "Manage Notifications Settings" +msgstr "Benachrichtigungs-Einstellungen verwalten" + +#: account.html:27 +msgid "Manage Mailing List Subscriptions" +msgstr "Mailinglisten-Abonnements verwalten" + +#: account.html:28 +msgid "Change Password" +msgstr "Passwort ändern" + +#: account.html:29 +msgid "Update Email Address" +msgstr "E-Mail-Adresse aktualisieren" + +#: account.html:30 +msgid "Deactivate Account" +msgstr "Konto deaktivieren" + +#: account.html:32 msgid "Please contact us if you need help with anything else." msgstr "Bitte nehmen Sie Kontakt mit uns auf, wenn Sie Hilfe benötigen." @@ -82,22 +91,29 @@ msgstr "Bitte nehmen Sie Kontakt mit uns auf, wenn Sie Hilfe benötigen." msgid "Barcode Scanner (Beta)" msgstr "Barcode-Scanner (Beta)" -#: barcodescanner.html:14 +#: barcodescanner.html:15 msgid "Point your camera at a barcode! 📷" msgstr "Richten Sie die Kamera auf den Barcode! 📷" -#: account/loans.html:69 +#: account/loans.html:59 #, python-format msgid "%(count)d Current Loan" msgid_plural "%(count)d Current Loans" msgstr[0] "%(count)d ausgeliehenes Buch" msgstr[1] "%(count)d ausgeliehene Bücher" -#: account/loans.html:71 admin/loans_table.html:41 +#: account/loans.html:65 admin/loans_table.html:41 borrow_admin.html:106 msgid "Loan Expires" msgstr "Rückgabedatum" -#: ManageWaitlistButton.html:11 +#: borrow_admin.html:168 +#, python-format +msgid "%d person waiting" +msgid_plural "%d people waiting" +msgstr[0] "%d Person wartet" +msgstr[1] "%d Personen warten" + +#: ManageWaitlistButton.html:11 borrow_admin.html:186 #, python-format msgid "Waiting for %d day" msgid_plural "Waiting for %d days" @@ -130,11 +146,9 @@ msgstr "Nicht geändert" msgid "Revision %d" msgstr "Revision %d" -#: AuthorList.html:9 SearchResultsWork.html:45 books/check.html:32 -#: books/edit/edition.html:75 books/show.html:16 covers/book_cover.html:41 -#: covers/book_cover_single_edition.html:36 covers/book_cover_work.html:32 -#: diff.html:42 lists/preview.html:20 type/edition/publisher_line.html:15 -#: type/edition/view.html:190 type/work/view.html:190 +#: books/check.html:32 books/edit/edition.html:76 books/show.html:16 +#: covers/book_cover.html:41 covers/book_cover_single_edition.html:36 +#: covers/book_cover_work.html:32 diff.html:42 lists/preview.html:20 msgid "by" msgstr "von" @@ -162,7 +176,7 @@ msgstr "Abbrechen und zurück." msgid "YAML Representation:" msgstr "YAML Darstellung:" -#: BookPreview.html:11 editpage.html:15 +#: BookPreview.html:11 books/edit/edition.html:669 editpage.html:15 msgid "Preview" msgstr "Vorschau" @@ -204,7 +218,7 @@ msgstr "Vergleichen" msgid "See Diff" msgstr "Änderungen ansehen" -#: history.html:48 lib/history.html:78 +#: history.html:48 lib/history.html:76 #, python-format msgid "View revision %s" msgstr "Version %s anschauen" @@ -224,8 +238,8 @@ msgstr "Verzeihung. Es scheint ein Problem mit der Seite zu geben." #: internalerror.html:15 #, python-format msgid "" -"We've noted the error %s and will look into it as soon as possible. Head for home?" +"We've noted the error %s and will look into it as soon as possible. Head for " +"home?" msgstr "" "Wir haben den Fehler %s aufgenommen und werden das Problem schnellstmöglich " "bearbeiten. Zur Startseite gehen?" @@ -234,58 +248,64 @@ msgstr "" msgid "Library Explorer" msgstr "Bibliotheksexplorer" -#: lib/header_dropdown.html:46 lib/nav_head.html:107 login.html:10 login.html:72 +#: lib/header_dropdown.html:59 lib/nav_head.html:118 login.html:10 +#: login.html:75 msgid "Log In" msgstr "Anmelden" -#: login.html:15 +#: account/create.html:19 login.html:16 +msgid "OR" +msgstr "ODER" + +#: login.html:18 msgid "" -"Please enter your Internet Archive email and " -"password to access your Open Library account." +"Please enter your Internet Archive email " +"and password to access your Open Library account." msgstr "" -"Bitte geben Sie Ihre Internet Archive E-Mail-" -"Adresse und das Passwort für Ihr Open-Library-Konto ein." +"Bitte geben Sie Ihre Internet Archive E-" +"Mail-Adresse und das Passwort für Ihr Open-Library-Konto ein." -#: account/create.html:46 login.html:18 +#: account/create.html:50 login.html:21 #, python-format msgid "You are already logged into Open Library as %(user)s." msgstr "Sie sind bereits als %(user)s angemeldet." -#: account/create.html:47 login.html:19 +#: account/create.html:51 login.html:22 msgid "" -"If you'd like to create a new, different Open Library account, you'll need to log out " -"and start the signup process afresh." +"If you'd like to create a new, different Open Library account, you'll need " +"to log out and start the signup process afresh." msgstr "" -"Wenn Sie ein neues Konto anlegen möchten, müssen Sie sich abmelden und den " -"Registrierungsprozess von vorne beginnen." +"Wenn Sie ein neues Konto anlegen möchten, müssen Sie sich abmelden und den Registrierungsprozess von vorne beginnen." -#: login.html:39 +#: login.html:42 msgid "Email" msgstr "E-Mail" -#: login.html:41 +#: login.html:44 msgid "Forgot your Internet Archive email?" msgstr "Internet-Archive-E-Mail vergessen?" -#: account/email/forgot-ia.html:33 forms.py:31 login.html:51 +#: account/email/forgot-ia.html:33 forms.py:31 login.html:54 msgid "Password" msgstr "Passwort" -#: login.html:61 +#: login.html:64 msgid "Remember me" msgstr "Zugangsdaten merken" -#: account/email/forgot.html:48 login.html:76 +#: account/email/forgot.html:48 login.html:79 msgid "Forgot your Password?" msgstr "Passwort vergessen?" -#: login.html:78 -msgid "Not a member of Open Library? Sign up now." +#: login.html:81 +msgid "" +"Not a member of Open Library? Sign up now." msgstr "" -"Kein Mitglied der Open Library? Jetzt registrieren." +"Kein Mitglied der Open Library? Jetzt " +"registrieren." #: messages.html:11 msgid "Created new author record." @@ -372,29 +392,33 @@ msgstr "Ungültiger MARC-Eintrag." msgid "Server status" msgstr "Serverstatus" -#: SubjectTags.html:23 lib/nav_foot.html:28 lib/nav_head.html:29 subjects.html:9 -#: subjects/notfound.html:11 type/author/view.html:161 type/list/view_body.html:276 -#: work_search.html:68 +#: SearchNavigation.html:24 SubjectTags.html:23 lib/nav_foot.html:28 +#: lib/nav_head.html:28 subjects.html:9 subjects/notfound.html:11 +#: type/author/view.html:176 type/list/view_body.html:280 work_search.html:75 msgid "Subjects" msgstr "Themen" -#: SubjectTags.html:27 subjects.html:9 type/author/view.html:162 -#: type/list/view_body.html:278 work_search.html:70 +#: SubjectTags.html:27 subjects.html:9 type/author/view.html:177 +#: type/list/view_body.html:282 work_search.html:79 msgid "Places" msgstr "Orte" -#: SubjectTags.html:25 admin/menu.html:20 subjects.html:9 type/author/view.html:163 -#: type/list/view_body.html:277 work_search.html:69 +#: SubjectTags.html:25 admin/menu.html:20 subjects.html:9 +#: type/author/view.html:178 type/list/view_body.html:281 work_search.html:78 msgid "People" msgstr "Personen" -#: SubjectTags.html:29 subjects.html:9 type/list/view_body.html:279 -#: work_search.html:71 +#: SubjectTags.html:29 subjects.html:9 type/list/view_body.html:283 +#: work_search.html:80 msgid "Times" msgstr "Zeiten" -#: merge/authors.html:89 publishers/view.html:17 subjects.html:19 -#: type/author/view.html:110 +#: subjects.html:19 +msgid "See all works" +msgstr "Alle Werke anzeigen" + +#: merge/authors.html:89 publishers/view.html:13 subjects.html:15 +#: type/author/view.html:117 #, python-format msgid "%(count)d work" msgid_plural "%(count)d works" @@ -406,12 +430,12 @@ msgstr[1] "%(count)d Werke" msgid "Search for books with subject %(name)s." msgstr "Suche nach Büchern mit dem Thema %(name)s." -#: authors/index.html:20 lib/nav_head.html:91 lib/search_head.html:24 -#: lists/home.html:25 publishers/notfound.html:19 publishers/view.html:115 -#: search/advancedsearch.html:44 search/authors.html:48 search/inside.html:17 -#: search/lists.html:17 search/publishers.html:19 search/subjects.html:34 +#: authors/index.html:21 lib/nav_head.html:102 lists/home.html:25 +#: publishers/notfound.html:19 publishers/view.html:115 +#: search/advancedsearch.html:48 search/authors.html:27 search/inside.html:17 +#: search/lists.html:17 search/publishers.html:19 search/subjects.html:28 #: subjects.html:27 subjects/notfound.html:19 type/local_id/view.html:42 -#: work_search.html:84 +#: work_search.html:91 msgid "Search" msgstr "Suche" @@ -421,14 +445,15 @@ msgstr "Veröffentlichungsgeschichte" #: subjects.html:39 msgid "" -"This is a chart to show the publishing history of editions of works about this " -"subject. Along the X axis is time, and on the y axis is the count of editions " -"published. Click here to skip the chart." +"This is a chart to show the publishing history of editions of works about " +"this subject. Along the X axis is time, and on the y axis is the count of " +"editions published. Click here to skip the " +"chart." msgstr "" "Dieses Diagram zeigt die Veröffentlichungsgeschichte der Ausgaben von Werken " -"die das Thema behandeln. Entlang der X-Achse ist die Zeit und auf der Y-Achse " -"die Anzahl der veröffentlichten Ausgaben angegeben. Hier klicken um das Diagram zu überspringen." +"die das Thema behandeln. Entlang der X-Achse ist die Zeit und auf der Y-" +"Achse die Anzahl der veröffentlichten Ausgaben angegeben. Hier klicken um das Diagram zu überspringen." #: publishers/view.html:34 subjects.html:40 msgid "Reset chart" @@ -440,7 +465,8 @@ msgstr "oder näher heranzoomen." #: subjects.html:41 msgid "This graph charts editions published on this subject." -msgstr "Dieser Graph zeigt Ausgaben, die zu diesem Thema veröffentlicht wurden." +msgstr "" +"Dieser Graph zeigt Ausgaben, die zu diesem Thema veröffentlicht wurden." #: publishers/view.html:42 subjects.html:47 msgid "Editions Published" @@ -448,7 +474,8 @@ msgstr "Veröffentlichte Ausgaben" #: admin/imports.html:18 publishers/view.html:44 subjects.html:49 msgid "You need to have JavaScript turned on to see the nifty chart!" -msgstr "Sie müssen JavaScript aktivieren, um dieses praktische Diagramm zu sehen!" +msgstr "" +"Sie müssen JavaScript aktivieren, um dieses praktische Diagramm zu sehen!" #: publishers/view.html:46 subjects.html:51 msgid "Year of Publication" @@ -466,9 +493,9 @@ msgstr "Keine gefunden." msgid "See more books by, and learn about, this author" msgstr "Mehr Bücher von und Informationen über diese*n Autor*in ansehen" -#: account/waitlist.html:68 authors/index.html:27 publishers/view.html:83 -#: search/authors.html:66 search/publishers.html:29 search/subjects.html:89 -#: subjects.html:88 +#: account/loans.html:143 authors/index.html:24 publishers/view.html:79 +#: search/authors.html:56 search/publishers.html:29 search/subjects.html:66 +#: subjects.html:84 #, python-format msgid "%(count)d book" msgid_plural "%(count)d books" @@ -487,8 +514,8 @@ msgstr "die die meisten Bücher zu diesem Thema geschrieben haben" msgid "Get more information about this publisher" msgstr "Mehr Informationen zu diesem Verlag" -#: SearchResultsWork.html:75 books/check.html:36 merge/authors.html:82 -#: publishers/view.html:106 subjects.html:111 +#: SearchResultsWork.html:82 books/check.html:32 merge/authors.html:82 +#: publishers/view.html:102 subjects.html:107 #, python-format msgid "%(count)s edition" msgid_plural "%(count)s editions" @@ -501,9 +528,9 @@ msgstr "Wie können wir unterstützen?" #: support.html:16 msgid "" -"Your question has been sent to our support team. We'll get back to you shortly. " -"You can also contact us on Twitter." +"Your question has been sent to our support team. We'll get back to you " +"shortly. You can also contact us " +"on Twitter." msgstr "" "Ihre Frage wurde an das Support-Team gesendet. Wir melden uns in Kürze. Sie " "können uns auch auf Twitter " @@ -511,13 +538,13 @@ msgstr "" #: support.html:19 msgid "" -"Please check our Help Pages and Frequently Asked Questions (FAQ) to see if your question is answered " -"there. Thank you." +"Please check our Help Pages and Frequently Asked Questions (FAQ) to see if your question is " +"answered there. Thank you." msgstr "" -"Bitte sehen Sie auf unsere Hilfe-Seiten und in die Frequently Asked Questions (FAQ) ob Ihre Frage schon beantwortet wurde. " -"Vielen Dank." +"Bitte sehen Sie auf unsere Hilfe-Seiten und in die Frequently Asked Questions (FAQ) ob Ihre Frage schon " +"beantwortet wurde. Vielen Dank." #: support.html:22 msgid "Your Name" @@ -533,11 +560,11 @@ msgstr "Wir benötigen diese, wenn Sie eine Antwort möchten" #: support.html:29 msgid "" -"If you wish to be contacted by other means, please add your contact preference " -"and details to your question." +"If you wish to be contacted by other means, please add your contact " +"preference and details to your question." msgstr "" -"Wenn Sie auf andere Weise kontaktiert werden möchte, fügen Sie Ihrer Frage bitte " -"Ihre Kontaktdaten hinzu." +"Wenn Sie auf andere Weise kontaktiert werden möchte, fügen Sie Ihrer Frage " +"bitte Ihre Kontaktdaten hinzu." #: support.html:33 msgid "Topic" @@ -582,15 +609,17 @@ msgstr "Ihre Frage" #: support.html:50 msgid "Note: our staff will likely only be able respond in English." msgstr "" -"Hinweis: Unsere Mitarbeitenden können möglicherweise nur in Englisch antworten." +"Hinweis: Unsere Mitarbeitenden können möglicherweise nur in Englisch " +"antworten." #: support.html:54 msgid "" -"If you encounter an error message, please include it. For questions about our " -"books, please provide the title/author or Open Library ID." +"If you encounter an error message, please include it. For questions about " +"our books, please provide the title/author or Open Library ID." msgstr "" -"Wenn Sie eine Fehlermeldung erhalten, bitte geben Sie diese mit an. Für Fragen " -"zu Büchern geben Sie bitte Titel und Autor oder die Open-Library-ID an." +"Wenn Sie eine Fehlermeldung erhalten, bitte geben Sie diese mit an. Für " +"Fragen zu Büchern geben Sie bitte Titel und Autor oder die Open-Library-ID " +"an." #: support.html:58 msgid "Which page were you looking at?" @@ -600,16 +629,82 @@ msgstr "Um welche Seite geht es?" msgid "Send" msgstr "Abschicken" -#: EditButtons.html:23 EditButtonsMacros.html:26 account/create.html:86 -#: account/notifications.html:59 account/password/reset.html:24 -#: account/privacy.html:56 admin/imports-add.html:28 books/add.html:84 -#: books/edit/addfield.html:87 books/edit/addfield.html:133 -#: books/edit/addfield.html:177 covers/add.html:78 covers/manage.html:51 -#: databarAuthor.html:66 databarEdit.html:13 lists/widget.html:340 -#: merge/authors.html:97 support.html:71 +#: EditButtons.html:23 EditButtonsMacros.html:26 ReadingLogDropper.html:167 +#: account/create.html:90 account/notifications.html:59 +#: account/password/reset.html:24 account/privacy.html:56 +#: admin/imports-add.html:28 books/add.html:108 books/edit/addfield.html:87 +#: books/edit/addfield.html:133 books/edit/addfield.html:177 covers/add.html:79 +#: covers/manage.html:52 databarAuthor.html:66 databarEdit.html:13 +#: merge/authors.html:109 support.html:71 msgid "Cancel" msgstr "Abbrechen" +#: trending.html:10 +msgid "Now" +msgstr "Jetzt" + +#: admin/graphs.html:47 check_ins/check_in_form.html:71 +#: check_ins/check_in_prompt.html:36 trending.html:10 +msgid "Today" +msgstr "Heute" + +#: admin/graphs.html:48 trending.html:10 +msgid "This Week" +msgstr "Diese Woche" + +# | msgid "This Edition" +#: stats/readinglog.html:20 stats/readinglog.html:37 stats/readinglog.html:54 +#: trending.html:10 +msgid "This Month" +msgstr "Dieser Monat" + +#: trending.html:10 +msgid "This Year" +msgstr "Dieses Jahr" + +#: trending.html:10 +msgid "All Time" +msgstr "Gesamtzeit" + +#: home/index.html:27 trending.html:11 +msgid "Trending Books" +msgstr "Beliebte Bücher" + +#: trending.html:12 +msgid "See what readers from the community are adding to their bookshelves" +msgstr "Sieh was andere in der Gemeinschaft ihren Regalen hinzufügen" + +#: ReadingLogDropper.html:27 ReadingLogDropper.html:66 +#: ReadingLogDropper.html:189 account/mybooks.html:75 account/sidebar.html:26 +#: trending.html:23 +msgid "Want to Read" +msgstr "Möchte ich lesen" + +#: ReadingLogDropper.html:25 ReadingLogDropper.html:74 account/books.html:33 +#: account/mybooks.html:74 account/readinglog_shelf_name.html:8 +#: account/sidebar.html:25 trending.html:23 +msgid "Currently Reading" +msgstr "Aktuell entliehen" + +#: LoanReadForm.html:9 ReadButton.html:19 +#: book_providers/gutenberg_read_button.html:15 +#: book_providers/openstax_read_button.html:15 +#: book_providers/standard_ebooks_read_button.html:15 +#: books/edit/edition.html:665 books/show.html:34 trending.html:23 +#: type/list/embed.html:69 widget.html:34 +msgid "Read" +msgstr "Lesen" + +#: trending.html:24 +#, python-format +msgid "Someone marked as %(shelf)s %(k_hours_ago)s" +msgstr "Jemand hat %(k_hours_ago)s als %(shelf)s markiert" + +#: trending.html:26 +#, python-format +msgid "Logged %(count)i times %(time_unit)s" +msgstr "Gespeichert %(count)i mal %(time_unit)s" + #: viewpage.html:13 msgid "Revert to this revision?" msgstr "Auf diese Version zurücksetzen?" @@ -623,28 +718,22 @@ msgstr "Auf diese Version zurücksetzen" msgid "Read \"%(title)s\"" msgstr "„%(title)s“ Lesen" -#: LoanReadForm.html:9 ReadButton.html:19 -#: book_providers/gutenberg_read_button.html:15 -#: book_providers/standard_ebooks_read_button.html:15 books/show.html:34 -#: type/list/embed.html:68 widget.html:34 -msgid "Read" -msgstr "Lesen" - #: widget.html:39 #, python-format msgid "Borrow \"%(title)s\"" msgstr "„%(title)s“ ausleihen" -#: ReadButton.html:15 type/list/embed.html:79 widget.html:39 +#: ReadButton.html:15 books/edit/edition.html:668 type/list/embed.html:80 +#: widget.html:39 msgid "Borrow" msgstr "Ausleihen" #: widget.html:45 #, python-format msgid "Join waitlist for "%(title)s"" -msgstr "„%(title)s“ vormerken" +msgstr "„%(title)s“ vormerken;" -#: LoanStatus.html:98 widget.html:45 +#: LoanStatus.html:112 widget.html:45 msgid "Join Waitlist" msgstr "Buch vormerken" @@ -653,7 +742,7 @@ msgstr "Buch vormerken" msgid "Learn more about "%(title)s" at OpenLibrary" msgstr "Mehr über „%(title)s in der OpenLibrary" -#: LoanStatus.html:117 widget.html:49 +#: widget.html:49 msgid "Learn More" msgstr "Mehr erfahren" @@ -661,283 +750,241 @@ msgstr "Mehr erfahren" msgid "on " msgstr "auf " -#: search/authors.html:37 search/subjects.html:27 work_search.html:58 -#: work_search.html:169 -#, python-format -msgid "%(count)s hit" -msgid_plural "%(count)s hits" -msgstr[0] "%(count)s Treffer" -msgstr[1] "%(count)s Treffer" - -#: lib/nav_foot.html:30 lib/nav_head.html:35 search/advancedsearch.html:7 -#: search/advancedsearch.html:10 search/advancedsearch.html:14 work_search.html:62 -msgid "Advanced Search" -msgstr "Erweiterte Suche" +#: work_search.html:68 +msgid "Search Books" +msgstr "Bücher suchen" -#: work_search.html:66 +#: work_search.html:72 msgid "eBook?" msgstr "E-Book?" -#: books/add.html:34 books/edit.html:85 books/edit/edition.html:244 -#: lib/nav_head.html:82 lib/search_foot.html:9 lib/search_head.html:9 -#: search/advancedsearch.html:20 work_search.html:67 work_search.html:147 +#: type/edition/view.html:246 type/work/view.html:246 work_search.html:73 +msgid "Language" +msgstr "Sprache" + +#: books/add.html:42 books/edit.html:92 books/edit/edition.html:245 +#: lib/nav_head.html:93 merge_queue/merge_queue.html:77 +#: search/advancedsearch.html:24 work_search.html:74 work_search.html:164 msgid "Author" msgstr "Autor" -#: work_search.html:72 +#: work_search.html:76 msgid "First published" msgstr "Ersterscheinungsdatum" -#: lib/search_foot.html:14 lib/search_head.html:14 search/advancedsearch.html:40 -#: work_search.html:73 +#: search/advancedsearch.html:44 type/edition/view.html:231 +#: type/work/view.html:231 work_search.html:77 msgid "Publisher" msgstr "Verlag" -#: lib/search_foot.html:29 work_search.html:74 -msgid "Language" -msgstr "Sprache" - -#: work_search.html:75 +#: work_search.html:81 msgid "Classic eBooks" msgstr "Nur E-Books" -#: work_search.html:82 +#: work_search.html:89 msgid "Keywords" msgstr "Schlüsselwörter" -#: recentchanges/index.html:60 work_search.html:87 +#: recentchanges/index.html:60 work_search.html:94 msgid "Everything" msgstr "Alles" -#: work_search.html:89 +#: work_search.html:96 msgid "Ebooks" msgstr "E-Books" -#: work_search.html:91 +#: work_search.html:98 msgid "Print Disabled" msgstr "Druck gesperrt" +#: work_search.html:101 +msgid "This is only visible to super librarians." +msgstr "Das ist nur für Super-Bibliothekare sichtbar." + #: work_search.html:107 +msgid "Solr Editions Beta" +msgstr "Solr-Editionen Beta" + +#: work_search.html:124 msgid "eBook" msgstr "E-Books" -#: work_search.html:110 +#: work_search.html:127 msgid "Classic eBook" msgstr "E-Books" -#: work_search.html:129 +#: work_search.html:146 msgid "Explore Classic eBooks" msgstr "E-Books entdecken" -#: work_search.html:129 +#: work_search.html:146 msgid "Only Classic eBooks" msgstr "Nur E-Books" -#: work_search.html:133 work_search.html:135 work_search.html:137 -#: work_search.html:139 +#: work_search.html:150 work_search.html:152 work_search.html:154 +#: work_search.html:156 #, python-format msgid "Explore books about %(subject)s" msgstr "Bücher über %(subject)s entdecken" -#: merge/authors.html:84 work_search.html:141 +#: merge/authors.html:88 work_search.html:158 msgid "First published in" msgstr "Ersterscheinungsdatum" -#: work_search.html:143 +#: work_search.html:160 msgid "Written in" msgstr "Geschrieben in" -#: work_search.html:145 +#: work_search.html:162 msgid "Published by" msgstr "Veröffentlicht von" -#: work_search.html:148 +#: work_search.html:165 msgid "Click to remove this facet" msgstr "Klicken, um den Filter zu entfernen" -#: work_search.html:150 +#: work_search.html:167 #, python-format msgid "%(title)s - search" msgstr "%(title)s – Suche" -#: search/lists.html:29 work_search.html:164 +#: search/lists.html:29 work_search.html:181 msgid "No results found." msgstr "Keine Ergebnisse gefunden." -#: search/lists.html:30 work_search.html:165 +#: search/lists.html:30 work_search.html:184 #, python-format msgid "Search for books containing the phrase \"%s\"?" msgstr "Suche nach Büchern mit der Phrase „%s“?" -#: work_search.html:194 +#: work_search.html:188 +msgid "Add a new book to Open Library?" +msgstr "Neues Buch zur Open Library hinzufügen?" + +#: account/reading_log.html:36 search/authors.html:41 search/subjects.html:31 +#: work_search.html:190 +#, python-format +msgid "%(count)s hit" +msgid_plural "%(count)s hits" +msgstr[0] "%(count)s Treffer" +msgstr[1] "%(count)s Treffer" + +#: work_search.html:225 msgid "Zoom In" msgstr "Hereinzoomen" -#: work_search.html:195 +#: work_search.html:226 msgid "Focus your results using these" msgstr "Filtern sie hiermit Ihre Ergebnisse" -#: work_search.html:195 +#: work_search.html:226 msgid "filters" msgstr "Filter" -#: work_search.html:207 +#: search/facet_section.html:20 work_search.html:238 msgid "Merge duplicate authors from this search" msgstr "Doppelte Autoren aus dieser Suche zusammenführen" -#: type/work/editions.html:17 work_search.html:207 +#: search/facet_section.html:20 type/work/editions.html:17 work_search.html:238 msgid "Merge duplicates" msgstr "Duplikate zusammenführen" -#: work_search.html:219 +#: search/facet_section.html:32 work_search.html:250 msgid "yes" msgstr "Ja" -#: work_search.html:221 +#: search/facet_section.html:34 work_search.html:252 msgid "no" msgstr "Nein" -#: work_search.html:222 +#: search/facet_section.html:35 work_search.html:253 msgid "Filter results for ebook availability" msgstr "Suche auf entleihbare E-Books begrenzen" -#: work_search.html:224 +#: search/facet_section.html:37 work_search.html:255 #, python-format msgid "Filter results for %(facet)s" msgstr "Suche nach %(facet)s filtern" -#: work_search.html:229 +#: search/facet_section.html:42 work_search.html:260 msgid "more" msgstr "mehr" -#: work_search.html:233 +#: search/facet_section.html:46 work_search.html:264 msgid "less" msgstr "weniger" -#: account/books.html:24 account/books.html:69 type/user/view.html:50 +#: account/books.html:27 account/sidebar.html:24 type/user/view.html:50 #: type/user/view.html:55 msgid "Reading Log" msgstr "Lese-Logbuch" -#: account/books.html:28 account/books.html:83 +#: account/books.html:31 lib/nav_head.html:13 lib/nav_head.html:25 +#: lib/nav_head.html:76 +msgid "My Books" +msgstr "Meine Bücher" + +#: account/books.html:35 account/readinglog_shelf_name.html:10 +msgid "Want To Read" +msgstr "Leseliste" + +#: ReadingLogDropper.html:23 ReadingLogDropper.html:82 account/books.html:37 +#: account/mybooks.html:76 account/readinglog_shelf_name.html:12 +#: account/sidebar.html:27 +msgid "Already Read" +msgstr "Bereits gelesen" + +#: account/books.html:40 account/sidebar.html:38 msgid "Sponsorships" msgstr "Sponsorships" -#: account/books.html:30 +#: account/books.html:42 msgid "Book Notes" msgstr "Notizen" -#: EditionNavBar.html:20 account/books.html:32 account/observations.html:33 +#: EditionNavBar.html:26 account/books.html:44 account/observations.html:33 msgid "Reviews" msgstr "Rezensionen" -#: account/books.html:34 -msgid "Books You've Checked Out" -msgstr "Entliehene Bücher" - -#: account/books.html:36 -msgid "Books You're Waiting For" -msgstr "Warteliste" +#: account/books.html:46 account/sidebar.html:19 admin/menu.html:22 +#: admin/people/view.html:233 type/user/view.html:29 +msgid "Loans" +msgstr "Ausleihen" -#: account/books.html:38 +#: account/books.html:48 msgid "Imports and Exports" msgstr "Importe und Exporte" -#: account/books.html:40 lib/nav_head.html:12 +#: account/books.html:50 msgid "My Lists" msgstr "Meine Listen" -#: account/books.html:52 admin/menu.html:22 admin/people/view.html:225 -#: lib/search_head.html:69 type/user/view.html:29 -msgid "Loans" -msgstr "Ausleihen" - -#: account/books.html:55 type/list/embed.html:74 -msgid "Checked out" -msgstr "Ausgeliehen" - -#: account/books.html:60 -msgid "Waitlist" -msgstr "Warteliste" - -#: account/books.html:64 -msgid "Loan History" -msgstr "Verlauf" - -#: ReadingLogButton.html:9 account/books.html:70 -#: account/readinglog_shelf_name.html:8 lists/widget.html:128 -msgid "Currently Reading" -msgstr "Aktuell entliehen" - -#: ReadingLogButton.html:9 account/books.html:71 lists/widget.html:130 -#: lists/widget.html:170 lists/widget.html:285 -msgid "Want to Read" -msgstr "Möchte ich lesen" - -#: ReadingLogButton.html:9 account/books.html:72 -#: account/readinglog_shelf_name.html:12 lists/widget.html:126 -#: lists/widget.html:190 -msgid "Already Read" -msgstr "Bereits gelesen" - -#: account/books.html:75 -msgid "My Notes" -msgstr "Meine Notizen" - -#: account/books.html:76 -msgid "My Reviews" -msgstr "Meine Rezensionen" - -#: account/books.html:78 -msgid "Reading Stats" -msgstr "Lesestatistik" - #: account/books.html:79 -msgid "Import & Export Options" -msgstr "Import- & Export-Optionen" - -#: account/books.html:87 -msgid "Sponsoring" -msgstr "Sponsoring" - -#: account/books.html:91 lib/nav_head.html:31 lib/nav_head.html:85 -#: lib/search_head.html:70 lists/home.html:7 lists/home.html:10 -#: lists/widget.html:261 recentchanges/index.html:41 search/lists.html:10 -#: type/list/embed.html:27 type/list/view_body.html:47 type/user/view.html:35 -#: type/user/view.html:66 -msgid "Lists" -msgstr "Listen" - -#: account/books.html:91 -msgid "See All" -msgstr "Alles anzeigen" - -#: account/books.html:117 msgid "View stats about this shelf" msgstr "Statistiken zu diesem Regal anzeigen" -#: account/books.html:117 account/readinglog_stats.html:93 admin/index.html:19 +#: account/books.html:79 account/readinglog_stats.html:93 admin/index.html:19 msgid "Stats" msgstr "Statistiken" -#: account/books.html:123 +#: account/books.html:85 msgid "Your book notes are private and cannot be viewed by other patrons." msgstr "" "Ihre Büchernotizen sind privat und können nicht von anderen angesehen werden." -#: account/books.html:125 +#: account/books.html:87 msgid "Your book reviews will be shared anonymously with other patrons." msgstr "Ihre Rezensionen werden anonymisiert mit anderen geteilt." -#: account/books.html:128 +#: account/books.html:90 msgid "Your reading log is currently set to public" msgstr "Ihr Lese-Log ist aktuell öffentlich" -#: account/books.html:131 +#: account/books.html:93 msgid "Your reading log is currently set to private" msgstr "Ihr Lese-Log ist aktuell privat" -#: account/books.html:133 type/user/view.html:57 +#: account/books.html:95 type/user/view.html:57 msgid "Manage your privacy settings" msgstr "Ändern sie ihre Datenschutz-Einstellungen" @@ -945,47 +992,49 @@ msgstr "Ändern sie ihre Datenschutz-Einstellungen" msgid "Sign Up to Open Library" msgstr "Bei Open Library registrieren" -#: account/create.html:12 account/create.html:85 lib/header_dropdown.html:47 -#: lib/nav_head.html:108 lib/search_head.html:78 +#: account/create.html:12 account/create.html:89 lib/header_dropdown.html:60 +#: lib/nav_head.html:119 msgid "Sign Up" msgstr "Registrieren" -#: account/create.html:14 +#: account/create.html:21 msgid "Complete the form below to create a new Internet Archive account." msgstr "" "Füllen Sie das Formular aus, um ein neues Internet-Archive-Konto anzulegen." -#: account/create.html:15 +#: account/create.html:22 msgid "Each field is required" msgstr "Sie müssen alle Felder ausfüllen" -#: account/create.html:56 +#: account/create.html:60 msgid "Your URL" msgstr "Ihre URL" -#: account/create.html:56 +#: account/create.html:60 msgid "screenname" msgstr "Anzeigename" -#: account/create.html:73 +#: account/create.html:77 msgid "" -"If you have security settings or privacy blockers installed, please disable them " -"to see the reCAPTCHA." +"If you have security settings or privacy blockers installed, please disable " +"them to see the reCAPTCHA." msgstr "" -"Wenn Sie Sicherheits- und Datenschutz-Einstellungen, oder Ad-Blocker aktiviert " -"haben, versuchen Sie diese zu deaktiveren, um das reCAPTCHA zu sehen." +"Wenn Sie Sicherheits- und Datenschutz-Einstellungen, oder Ad-Blocker " +"aktiviert haben, versuchen Sie diese zu deaktiveren, um das reCAPTCHA zu " +"sehen." -#: account/create.html:77 +#: account/create.html:81 msgid "Incorrect. Please try again." msgstr "Fehler. Bitte erneut versuchen." -#: account/create.html:81 +#: account/create.html:85 msgid "" -"By signing up, you agree to the Internet Archive's Terms of Service." +"By signing up, you agree to the Internet Archive's Terms of Service." msgstr "" -"Die Nutzung der Open Library fällt unter die Nutzungsbedingungen (AGB) des Internet Archives." +"Die Nutzung der Open Library fällt unter die Nutzungsbedingungen (AGB) des Internet " +"Archives." #: account/delete.html:6 account/delete.html:10 msgid "Delete Account" @@ -1011,37 +1060,185 @@ msgstr "Bücher laden" msgid "Export your Reading Log" msgstr "Lese-Logbuch exportieren" -#: account/import.html:31 +#: account/import.html:28 +msgid "Download a copy of your reading log." +msgstr "Eine Kopie des Lese-Logs herunterladen." + +#: account/import.html:28 +msgid "What is this?" +msgstr "Was ist das?" + +#: account/import.html:33 account/import.html:44 account/import.html:55 +#: account/import.html:66 account/import.html:77 msgid "Download (.csv format)" msgstr "Download (.csv-Format)" -#: account/loans.html:61 +#: account/import.html:38 +msgid "Export your book notes" +msgstr "Buch-Notizen exportieren" + +#: account/import.html:39 +msgid "Download a copy of your book notes." +msgstr "Eine Kopie der Buch-Notizen herunterladen." + +#: account/import.html:39 +msgid "What are book notes?" +msgstr "Was sind Buch-Notizen?" + +#: account/import.html:49 +msgid "Export your reviews" +msgstr "Rezensionen exportieren" + +#: account/import.html:50 +msgid "Download a copy of your review tags." +msgstr "Eine Kopie der Rezensions-Tags herunterladen." + +#: account/import.html:50 +msgid "What are review tags?" +msgstr "Was sind Rezension-Tags?" + +#: account/import.html:60 +msgid "Export your list overview" +msgstr "Listenübersicht exportieren" + +#: account/import.html:61 +msgid "Download a summary of your lists and their contents." +msgstr "Eine Zusammenfassung Ihrer Listen und ihrer Inhalte herunterladen." + +#: account/import.html:61 +msgid "What are lists?" +msgstr "Was sind Listen?" + +#: account/import.html:71 +msgid "Export your star ratings" +msgstr "Sternebewertungen exportieren" + +#: account/import.html:72 +msgid "Download a copy of your star ratings" +msgstr "Eine Kopie der Sternebewertungen herunterladen" + +#: account/import.html:72 +msgid "What are star ratings?" +msgstr "Was sind Sternebewertungen?" + +#: account/loans.html:54 msgid "You've not checked out any books at this moment." msgstr "Sie haben im Moment keine Bücker entliehen." -#: account/loans.html:72 +#: account/loans.html:66 msgid "Loan actions" msgstr "Verwaltung der Ausleihen" -#: ManageLoansButtons.html:13 account/loans.html:99 +#: ManageLoansButtons.html:13 account/loans.html:93 #, python-format msgid "There is one person waiting for this book." msgid_plural "There are %(n)d people waiting for this book." msgstr[0] "Eine Person wartet auf dieses Buch." msgstr[1] "Es warten %(n)d Personen auf dieses Buch." -#: account/loans.html:105 +#: account/loans.html:99 msgid "Not yet downloaded." msgstr "Bisher nicht heruntergeladen." -#: account/loans.html:106 +#: account/loans.html:100 msgid "Download Now" msgstr "Jetzt herunterladen" -#: account/loans.html:115 +#: account/loans.html:109 msgid "Return via
Adobe Digital Editions" msgstr "Zurückgeben mit
Adobe Digital Editions" +#: account/loans.html:120 +msgid "Books You're Waiting For" +msgstr "Warteliste" + +#: account/loans.html:127 +msgid "You are not waiting for any books at this moment." +msgstr "Aktuell haben Sie keine Bücher auf der Warteliste." + +#: account/loans.html:130 +msgid "Leave the Waiting List" +msgstr "Warteliste verlassen" + +#: account/loans.html:130 +msgid "" +"Are you sure you want to leave the waiting list of
TITLE?" +msgstr "" +"Sind Sie sicher, dass sie die Warte liste für
TITLE " +"verlassen wollen?" + +#: account/loans.html:149 admin/loans_table.html:43 admin/menu.html:33 +#: merge_queue/merge_queue.html:59 +msgid "Status" +msgstr "Status" + +#: RecentChangesAdmin.html:23 account/loans.html:150 admin/loans_table.html:47 +msgid "Actions" +msgstr "Aktionen" + +#: account/loans.html:172 +#, python-format +msgid "Waiting for 1 day" +msgid_plural "Waiting for %(count)d days" +msgstr[0] "1 Tag warten" +msgstr[1] "%(count)d Tage warten" + +#: account/loans.html:179 +msgid "You are the next person to receive this book." +msgstr "Sie sind die nächste Person, die dieses Buch erhält." + +#: ManageWaitlistButton.html:21 account/loans.html:181 +#, python-format +msgid "There is one person ahead of you in the waiting list." +msgid_plural "There are %(count)d people ahead of you in the waiting list." +msgstr[0] "Eine Person befindet sich vor Ihnen auf der Warteliste." +msgstr[1] "Es befinden sich %(count)d Personen vor Ihnen auf der Warteliste." + +#: account/loans.html:190 +msgid "You have less than an hour to borrow it." +msgstr "Sie haben weniger als eine Stunde, um es auszuleihen." + +#: account/loans.html:192 +#, python-format +msgid "You have one more hour to borrow it." +msgid_plural "You have %(hours)d more hours to borrow it." +msgstr[0] "Sie haben eine Stunde um es auszuleihen." +msgstr[1] "Sie haben %(hours)d Stunden um es auszuleihen." + +#: account/loans.html:199 +msgid "Borrow Now" +msgstr "Jetzt ausleihen" + +#: account/loans.html:204 +msgid "Leave the waiting list?" +msgstr "Warteliste verlassen?" + +#: account/loans.html:218 home/index.html:34 +msgid "Books We Love" +msgstr "Bücher die wir lieben" + +#: account/mybooks.html:19 account/sidebar.html:33 +msgid "My Reading Stats" +msgstr "Meine Lese-Statistik" + +#: account/mybooks.html:23 account/sidebar.html:34 +msgid "Import & Export Options" +msgstr "Import- & Export-Optionen" + +#: account/mybooks.html:35 +#, python-format +msgid "Set %(year_span)s reading goal" +msgstr "%(year_span)s Leseziel setzen" + +#: account/mybooks.html:69 +msgid "No books are on this shelf" +msgstr "Keine Bücher in diesem Regal" + +#: account/mybooks.html:73 +msgid "My Loans" +msgstr "Entliehene Bücher" + #: account/not_verified.html:7 account/not_verified.html:18 #: account/verify/failed.html:10 msgid "Oops!" @@ -1051,7 +1248,7 @@ msgstr "Ups!" msgid "Resend the verification email" msgstr "Bestätigungs-Email erneut versenden" -#: SearchResultsWork.html:57 SearchResultsWork.html:63 account/notes.html:25 +#: BookByline.html:10 SearchResultsWork.html:68 account/notes.html:25 #: account/observations.html:26 msgid "Unknown author" msgstr "Unbekannter Autor" @@ -1068,21 +1265,21 @@ msgstr "Titel fehlt" msgid "Publish date unknown" msgstr "Veröffentlichungsdatum unbekannt" -#: account/notes.html:48 books/edition-sort.html:58 books/works-show.html:30 +#: account/notes.html:48 books/edition-sort.html:63 books/works-show.html:30 #: type/work/editions.html:38 msgid "Publisher unknown" msgstr "Verlag unbekannt" -#: account/notes.html:53 type/work/editions.html:47 +#: account/notes.html:53 #, python-format msgid "in %(language)s" msgstr "in %(language)s" -#: NotesModal.html:40 account/notes.html:66 +#: NotesModal.html:42 account/notes.html:66 msgid "Delete Note" msgstr "Notiz löschen" -#: NotesModal.html:41 account/notes.html:67 +#: NotesModal.html:43 account/notes.html:67 msgid "Save Note" msgstr "Notiz Speichern" @@ -1100,13 +1297,13 @@ msgstr "Benachrichtigungen" #: account/notifications.html:31 msgid "" -"Notifications are connected to Lists at the moment. If one of the books on your " -"list gets edited, or a new book comes into the catalog, we'll let you know, if " -"you want." +"Notifications are connected to Lists at the moment. If one of the books on " +"your list gets edited, or a new book comes into the catalog, we'll let you " +"know, if you want." msgstr "" -"Benachrichtigngen sind momentan mit Listen verknüpft. Wenn eines der Bücher auf " -"Ihrer Liste bearbeitet wird, oder ein neues Buch zum Katalog hinzugefügt wird, " -"benachrichtigen wir Sie, wenn Sie möchten." +"Benachrichtigngen sind momentan mit Listen verknüpft. Wenn eines der Bücher " +"auf Ihrer Liste bearbeitet wird, oder ein neues Buch zum Katalog hinzugefügt " +"wird, benachrichtigen wir Sie, wenn Sie möchten." #: account/notifications.html:35 msgid "Would you like to receive very occasional emails from Open Library?" @@ -1114,14 +1311,14 @@ msgstr "Möchten Sie ab und zu E-Mails von Open Library bekommen?" #: account/notifications.html:42 msgid "No, thank you" -msgstr "Nein, danke." +msgstr "Nein, danke" #: account/notifications.html:49 msgid "Yes! Please!" msgstr "Ja! Bitte!" #: EditButtons.html:21 EditButtonsMacros.html:24 account/notifications.html:57 -#: account/privacy.html:54 covers/manage.html:50 +#: account/privacy.html:54 covers/manage.html:51 msgid "Save" msgstr "Speichern" @@ -1133,7 +1330,7 @@ msgstr "Löschen" msgid "Update Reviews" msgstr "Rezension aktualisieren" -#: account/observations.html:50 +#: account/observations.html:52 msgid "No observations found." msgstr "Keine Bemerkungen gefunden." @@ -1149,7 +1346,7 @@ msgstr "Datenschutz-Einstellungen" msgid "Yes" msgstr "Ja" -#: account/privacy.html:46 books/edit/edition.html:305 +#: account/privacy.html:46 books/edit/edition.html:306 msgid "No" msgstr "Nein" @@ -1161,8 +1358,8 @@ msgstr "Bücher, die %(username)s gerade liest" #: account/reading_log.html:13 #, python-format msgid "" -"%(username)s is reading %(total)d books. Join %(username)s on OpenLibrary.org " -"and tell the world what you're reading." +"%(username)s is reading %(total)d books. Join %(username)s on OpenLibrary." +"org and tell the world what you're reading." msgstr "" "%(username)s liest gerade %(total)d Bücher. Treten Sie %(username)s auf " "OpenLibrary.org bei und teilen der Welt mit, was Sie lesen." @@ -1175,8 +1372,8 @@ msgstr "Bücher, die %(username)s lesen möchte" #: account/reading_log.html:16 #, python-format msgid "" -"%(username)s wants to read %(total)d books. Join %(username)s on OpenLibrary.org " -"and share the books that you'll soon be reading!" +"%(username)s wants to read %(total)d books. Join %(username)s on OpenLibrary." +"org and share the books that you'll soon be reading!" msgstr "" "%(username)s möchte %(total)d Bücher lesen. Treten Sie %(username)s auf " "OpenLibrary.org bei und teilen die Bücher, die Sie zukünftig lesen wollen!" @@ -1189,50 +1386,60 @@ msgstr "Bücher, die %(username)s gelesen hat" #: account/reading_log.html:19 #, python-format msgid "" -"%(username)s has read %(total)d books. Join %(username)s on OpenLibrary.org and " -"tell the world about the books that you care about." +"%(username)s has read %(total)d books. Join %(username)s on OpenLibrary.org " +"and tell the world about the books that you care about." msgstr "" "%(username)s hat %(total)d Bücher gelesen. Treten Sie %(username)s auf " -"OpenLibrary.org bei und teilen Sie der Welt mit, welche Bücher Ihnen gefallen." +"OpenLibrary.org bei und teilen Sie der Welt mit, welche Bücher Ihnen " +"gefallen." #: account/reading_log.html:21 #, python-format msgid "Books %(userdisplayname)s is sponsoring" msgstr "Bücher die %(userdisplayname)s sponsort" -#: account/reading_log.html:32 search/sort_options.html:7 +#: account/reading_log.html:34 +msgid "Search your reading log" +msgstr "Lese-Logbuch durchsuchen" + +#: account/reading_log.html:42 search/sort_options.html:8 msgid "Sorting by" msgstr "Sortieren nach" -#: account/reading_log.html:34 account/reading_log.html:38 +#: account/reading_log.html:44 account/reading_log.html:48 msgid "Date Added (newest)" msgstr "Datum hinzugefügt (aktuelles)" -#: account/reading_log.html:36 account/reading_log.html:40 +#: account/reading_log.html:46 account/reading_log.html:50 msgid "Date Added (oldest)" msgstr "Datum hinzugefügt (ältestes)" -#: account/reading_log.html:53 -msgid "No books are on this shelf" -msgstr "Keine Bücher in diesem Regal" +#: account/reading_log.html:70 +msgid "You haven't added any books to this shelf yet." +msgstr "Sie haben diesem Regal noch keine Bücher hinzugefügt." -#: account/readinglog_shelf_name.html:10 -msgid "Want To Read" -msgstr "Leseliste" +#: account/reading_log.html:71 +msgid "" +"Search for a book to add to your reading log. Learn more about the reading log." +msgstr "" +"Suchen Sie ein Buch um es ihrem Lese-Log " +"hinzuzufügen. Erfahren Sie mehr über " +"das Lese-Log." #: account/readinglog_stats.html:8 account/readinglog_stats.html:95 #, python-format msgid "\"%(shelf_name)s\" Stats" -msgstr "„%(shelf_name)s“ Statistik" +msgstr "„%(shelf_name)s“ Statistiken" #: account/readinglog_stats.html:97 #, python-format msgid "" -"Displaying stats about %d books. Note all charts show only the " -"top 20 bars. Note reading log stats are private." +"Displaying stats about %d books. Note all charts show only " +"the top 20 bars. Note reading log stats are private." msgstr "" -"Statistik über %d Bücher. Alle Graphen zeigen nur die Top 20- " -"Balken... Lese-Statistiken sind privat." +"Statistik über %d Bücher. Alle Graphen zeigen nur die Top " +"20- Balken... Lese-Statistiken sind privat." #: account/readinglog_stats.html:102 msgid "Author Stats" @@ -1261,12 +1468,12 @@ msgstr "Werke nach dem Geburtsland der Autor*innen" #: account/readinglog_stats.html:121 msgid "" "Demographic statistics powered by Wikidata. Here's a sample of the " -"query used." +"\">Wikidata. Here's a sample of " +"the query used." msgstr "" "Demographische Daten werden durch die Wikidata bereitgestellt. Hier ist ein Beispiel der benutzten Abfrage." +"\">Wikidata bereitgestellt. Hier ist ein Beispiel der benutzten Abfrage." #: account/readinglog_stats.html:123 msgid "Work Stats" @@ -1296,76 +1503,48 @@ msgstr "Passende Werke" msgid "Click on a bar to see matching works" msgstr "Klicken Sie auf die Balken, um passende Werke anzuzeigen" -#: account/verify.html:7 -msgid "Verification email sent" -msgstr "Bestätigungs-E-Mail verschickt" +#: account/sidebar.html:20 +msgid "Loan History" +msgstr "Verlauf" -#: account.py:422 account.py:460 account/password/reset_success.html:10 -#: account/verify.html:9 account/verify/activated.html:10 -#: account/verify/success.html:10 -#, python-format -msgid "Hi, %(user)s" -msgstr "Hallo, %(user)s" - -#: account/waitlist.html:48 -msgid "You are not waiting for any books at this moment." -msgstr "Aktuell haben Sie keine Bücher auf der Warteliste." - -#: account/waitlist.html:51 -msgid "Leave the Waiting List" -msgstr "Warteliste verlassen" - -#: account/waitlist.html:51 -msgid "" -"Are you sure you want to leave the waiting list of
TITLE?" -msgstr "" -"Sind Sie sicher, dass sie die Warte liste für
TITLE " -"verlassen wollen?" +#: account/sidebar.html:30 +msgid "My Notes" +msgstr "Meine Notizen" -#: account/waitlist.html:70 admin/loans_table.html:43 admin/menu.html:33 -msgid "Status" -msgstr "Status" +#: account/sidebar.html:31 +msgid "My Reviews" +msgstr "Meine Rezensionen" -#: RecentChangesAdmin.html:23 account/waitlist.html:71 admin/loans_table.html:47 -msgid "Actions" -msgstr "Aktionen" +#: account/sidebar.html:42 +msgid "Sponsoring" +msgstr "Sponsoring" -#: account/waitlist.html:93 -#, python-format -msgid "Waiting for 1 day" -msgid_plural "Waiting for %(count)d days" -msgstr[0] "1 Tag warten" -msgstr[1] "%(count)d Tage warten" +#: EditionNavBar.html:30 SearchNavigation.html:28 account/sidebar.html:45 +#: lib/nav_head.html:31 lib/nav_head.html:96 lists/home.html:7 +#: lists/home.html:10 lists/widget.html:67 recentchanges/index.html:41 +#: type/list/embed.html:28 type/list/view_body.html:48 type/user/view.html:35 +#: type/user/view.html:66 +msgid "Lists" +msgstr "Listen" -#: account/waitlist.html:100 -msgid "You are the next person to receive this book." -msgstr "Sie sind die nächste Person, die dieses Buch erhält." +#: account/sidebar.html:45 +msgid "See All" +msgstr "Alles anzeigen" -#: ManageWaitlistButton.html:21 account/waitlist.html:102 -#, python-format -msgid "There is one person ahead of you in the waiting list." -msgid_plural "There are %(count)d people ahead of you in the waiting list." -msgstr[0] "Eine Person befindet sich vor Ihnen auf der Warteliste." -msgstr[1] "Es befinden sich %(count)d Personen vor Ihnen auf der Warteliste." +#: account/sidebar.html:47 +msgid "Untitled list" +msgstr "Unbenannte Liste" -#: account/waitlist.html:111 -msgid "You have less than an hour to borrow it." -msgstr "Sie haben weniger als eine Stunde, um es auszuleihen." +#: account/verify.html:7 +msgid "Verification email sent" +msgstr "Bestätigungs-E-Mail verschickt" -#: account/waitlist.html:113 +#: account.py:419 account.py:457 account/password/reset_success.html:10 +#: account/verify.html:9 account/verify/activated.html:10 +#: account/verify/success.html:10 #, python-format -msgid "You have one more hour to borrow it." -msgid_plural "You have %(hours)d more hours to borrow it." -msgstr[0] "Sie haben eine Stunde um es auszuleihen." -msgstr[1] "Sie haben %(hours)d Stunden um es auszuleihen." - -#: account/waitlist.html:120 -msgid "Borrow Now" -msgstr "Jetzt ausleihen" - -#: account/waitlist.html:125 -msgid "Leave the waiting list?" -msgstr "Warteliste verlassen?" +msgid "Hi, %(user)s" +msgstr "Hallo, %(user)s" #: account/email/forgot-ia.html:10 account/email/forgot.html:10 msgid "Forgot Your Internet Archive Email?" @@ -1376,8 +1555,8 @@ msgid "" "Please enter your Open Library email and password, and we’ll retrieve your " "Archive.org email." msgstr "" -"Bitte geben Sie ihre Open-Library-E-Mail and -Password ein und wir laden Ihre " -"Archive.org-E-Mail." +"Bitte geben Sie ihre Open-Library-E-Mail and -Password ein und wir laden " +"Ihre Archive.org-E-Mail." #: account/email/forgot-ia.html:17 msgid "Your email is:" @@ -1417,7 +1596,7 @@ msgid "Send It" msgstr "Abschicken" #: account/password/reset.html:7 account/password/reset.html:10 -#: admin/people/view.html:162 +#: admin/people/view.html:161 msgid "Reset Password" msgstr "Passwort zurücksetzen" @@ -1435,11 +1614,11 @@ msgstr "Passwort erfolgreich zurückgesetzt" #: account/password/reset_success.html:14 msgid "" -"Your password has been updated. Please log " -"in to continue." +"Your password has been updated. Please log in to continue." msgstr "" -"Ihr Passwort wurde geändert. Bitte melden " -"Sie sich erneut an, um fortzufahren." +"Ihr Passwort wurde geändert. Bitte melden Sie sich erneut an, um fortzufahren." #: account/password/sent.html:10 msgid "Done." @@ -1452,9 +1631,9 @@ msgid "" "instructions to help you reset your Open Library password. Please check your " "inbox for a note from us." msgstr "" -"Wir haben Ihnen eine Nachricht mit Ihrem Benutzernamen und einer Anleitung zum " -"Zurücksetzen Ihres Open-Library-Passworts an %(email)s geschickt. Bitte schauen " -"Sie in Ihren Posteingang." +"Wir haben Ihnen eine Nachricht mit Ihrem Benutzernamen und einer Anleitung " +"zum Zurücksetzen Ihres Open-Library-Passworts an %(email)s geschickt. Bitte " +"schauen Sie in Ihren Posteingang." #: account/verify/activated.html:7 msgid "Account already activated" @@ -1466,8 +1645,8 @@ msgid "" "Your account has been activated already. Please log in to " "continue." msgstr "" -"Ihr Konto wurde bereits aktiviert. Bitte melden Sie sich an " -"um fortzufahren." +"Ihr Konto wurde bereits aktiviert. Bitte melden Sie sich an um fortzufahren." #: account/verify/failed.html:7 msgid "Email Verification Failed" @@ -1475,11 +1654,11 @@ msgstr "E-Mail-Überprüfung fehlgeschlagen" #: account/verify/failed.html:14 msgid "" -"Your email address couldn't be verified. Your verification link seems invalid or " -"expired." +"Your email address couldn't be verified. Your verification link seems " +"invalid or expired." msgstr "" -"Ihre E-Mail-Adresse konnte nicht bestätigt werden. Ihr Bestätigungslink scheint " -"ungültig oder abgelaufen zu sein." +"Ihre E-Mail-Adresse konnte nicht bestätigt werden. Ihr Bestätigungslink " +"scheint ungültig oder abgelaufen zu sein." #: account/verify/failed.html:22 msgid "Enter your email address here" @@ -1510,16 +1689,9 @@ msgstr "Warten auf den Debugger…" msgid "Start" msgstr "Start" -#: admin/graphs.html:47 -msgid "Today" -msgstr "Heute" - -#: admin/graphs.html:48 -msgid "This Week" -msgstr "Diese Woche" - #: admin/imports-add.html:26 admin/ip/view.html:95 admin/people/edits.html:92 -#: covers/add.html:77 +#: check_ins/check_in_form.html:77 check_ins/reading_goal_form.html:16 +#: covers/add.html:78 msgid "Submit" msgstr "Abschicken" @@ -1528,12 +1700,14 @@ msgid "BookReader" msgstr "BookReader" #: admin/loans_table.html:18 book_providers/ia_download_options.html:12 +#: book_providers/openstax_download_options.html:13 books/edit/edition.html:677 msgid "PDF" msgstr "PDF" #: admin/loans_table.html:19 book_providers/gutenberg_download_options.html:17 #: book_providers/ia_download_options.html:16 -#: book_providers/standard_ebooks_download_options.html:14 +#: book_providers/standard_ebooks_download_options.html:15 +#: books/edit/edition.html:676 msgid "ePub" msgstr "ePub" @@ -1590,13 +1764,13 @@ msgstr "Speicher untersuchen" msgid "Inspect memcache" msgstr "Memcache untersuchen" -#: admin/people/view.html:172 admin/profile.html:7 +#: admin/people/view.html:171 admin/profile.html:7 msgid "Admin" msgstr "Admin" #: admin/solr.html:31 msgid "Enter the keys to reindex in Solr" -msgstr "" +msgstr "Keys zur Neuindizierung in Solr eingeben" #: admin/solr.html:32 msgid "Write one entry per line" @@ -1606,7 +1780,7 @@ msgstr "Schreiben Sie einen Eintrag pro Zeile" msgid "Update" msgstr "Aktualisieren" -#: admin/waitinglists.html:29 +#: FormatExpiry.html:23 admin/waitinglists.html:29 msgid "Expires" msgstr "Rückgabedatum" @@ -1641,13 +1815,13 @@ msgid "Please, leave a short note about what you changed:" msgstr "Bitte beschreiben Sie kurz Ihre Änderungen:" #: RecentChanges.html:61 RecentChangesAdmin.html:56 RecentChangesUsers.html:58 -#: admin/people/edits.html:100 recentchanges/render.html:61 +#: admin/people/edits.html:100 recentchanges/render.html:66 msgid "Older" msgstr "Älter" #: RecentChanges.html:64 RecentChangesAdmin.html:58 RecentChangesAdmin.html:60 #: RecentChangesUsers.html:61 admin/people/edits.html:103 -#: recentchanges/render.html:64 +#: recentchanges/render.html:69 msgid "Newer" msgstr "Neuer" @@ -1655,7 +1829,7 @@ msgstr "Neuer" msgid "Find Account" msgstr "Konto finden" -#: admin/people/index.html:32 admin/people/view.html:152 +#: admin/people/index.html:32 admin/people/view.html:151 msgid "Email Address:" msgstr "E-Mail-Adresse:" @@ -1663,7 +1837,8 @@ msgstr "E-Mail-Adresse:" msgid "Recent Accounts" msgstr "Neueste Konten" -#: admin/people/index.html:58 type/author/edit.html:26 type/language/view.html:27 +#: admin/people/index.html:58 type/author/edit.html:32 +#: type/language/view.html:27 msgid "Name" msgstr "Name" @@ -1675,57 +1850,65 @@ msgstr "E-Mail" msgid "Edits" msgstr "Bearbeitungen" -#: admin/people/index.html:75 admin/people/view.html:239 +#: admin/people/index.html:75 admin/people/view.html:247 msgid "Edit History" msgstr "Verlauf" -#: admin/people/view.html:148 lists/widget.html:322 +#: ReadingLogDropper.html:149 admin/people/view.html:147 msgid "Name:" msgstr "Name:" -#: admin/people/view.html:174 +#: admin/people/view.html:173 msgid "Bot" msgstr "Bot" -#: admin/people/view.html:192 +#: admin/people/view.html:191 msgid "# Edits" msgstr "# Bearbeitungen" -#: admin/people/view.html:196 +#: admin/people/view.html:195 msgid "Member Since:" msgstr "Mitglied seit:" -#: admin/people/view.html:205 +#: admin/people/view.html:204 msgid "Last Login:" msgstr "Letzter Login:" -#: admin/people/view.html:214 +#: admin/people/view.html:213 msgid "Tags:" msgstr "Tags:" -#: admin/people/view.html:235 +#: admin/people/view.html:221 +msgid "Anonymize Account:" +msgstr "Konto anonymisieren:" + +#: admin/people/view.html:226 +msgid "Anonymize Account" +msgstr "Konto anonymisieren" + +#: admin/people/view.html:243 msgid "Waiting Loans" msgstr "Wartende Ausleihen" -#: admin/people/view.html:248 +#: admin/people/view.html:256 msgid "Debug Info" msgstr "Debug-Informationen" -#: authors/index.html:8 authors/index.html:11 lib/nav_foot.html:27 -#: merge/authors.html:53 publishers/view.html:87 +#: SearchNavigation.html:16 authors/index.html:9 authors/index.html:12 +#: lib/nav_foot.html:27 merge/authors.html:57 publishers/view.html:87 msgid "Authors" msgstr "Autoren" -#: authors/index.html:17 +#: authors/index.html:18 msgid "Search for an Author" msgstr "Suche nach einem Autor" -#: authors/index.html:38 search/authors.html:76 +#: authors/index.html:39 search/authors.html:69 #, python-format msgid "about %(subjects)s" msgstr "über %(subjects)s" -#: authors/index.html:39 search/authors.html:77 +#: authors/index.html:40 search/authors.html:70 #, python-format msgid "including %(topwork)s" msgstr "Inklusive %(topwork)s" @@ -1733,7 +1916,8 @@ msgstr "Inklusive %(topwork)s" #: book_providers/gutenberg_download_options.html:11 #: book_providers/ia_download_options.html:9 #: book_providers/librivox_download_options.html:9 -#: book_providers/standard_ebooks_download_options.html:11 +#: book_providers/openstax_download_options.html:11 +#: book_providers/standard_ebooks_download_options.html:12 msgid "Download Options" msgstr "Download-Optionen" @@ -1742,7 +1926,7 @@ msgid "Download an HTML from Project Gutenberg" msgstr "HTML von Project Gutenberg herunterladen" #: book_providers/gutenberg_download_options.html:15 -#: book_providers/standard_ebooks_download_options.html:13 +#: book_providers/standard_ebooks_download_options.html:14 msgid "HTML" msgstr "HTML" @@ -1778,28 +1962,32 @@ msgstr "Ein eBook von Project Gutenberg lesen" #: book_providers/gutenberg_read_button.html:21 msgid "" "This book is available from Project " -"Gutenberg. Project Gutenberg is a trusted book provider of classic ebooks, " -"supporting thousands of volunteers in the creation and distribution of over " -"60,000 free eBooks." +"Gutenberg. Project Gutenberg is a trusted book provider of classic " +"ebooks, supporting thousands of volunteers in the creation and distribution " +"of over 60,000 free eBooks." msgstr "" -"Dieses Buch ist bei Project Gutenberg " -"verfügbar. Project Gutenberg ist ein vertrauenswürdiger Anbieter von Klassikern " -"als eBook und unterstützt tausende Freiwillige bei der Erstellung und Verteilung " -"von über sechzigtausend freien eBooks." +"Dieses Buch ist bei Project " +"Gutenberg verfügbar. Project Gutenberg ist ein vertrauenswürdiger " +"Anbieter von Klassikern als eBook und unterstützt tausende Freiwillige bei " +"der Erstellung und Verteilung von über sechzigtausend freien eBooks." #: book_providers/gutenberg_read_button.html:22 #: book_providers/librivox_read_button.html:25 +#: book_providers/openstax_read_button.html:22 #: book_providers/standard_ebooks_read_button.html:22 msgid "Learn more" msgstr "Mehr erfahren" -#: BookPreview.html:19 DonateModal.html:15 NotesModal.html:30 -#: ObservationsModal.html:25 book_providers/gutenberg_read_button.html:24 +#: BookPreview.html:20 DonateModal.html:15 NotesModal.html:32 +#: ObservationsModal.html:32 ReadingLogDropper.html:144 ShareModal.html:25 +#: book_providers/gutenberg_read_button.html:24 #: book_providers/librivox_read_button.html:27 -#: book_providers/standard_ebooks_read_button.html:24 covers/author_photo.html:22 -#: covers/book_cover.html:39 covers/book_cover_single_edition.html:34 -#: covers/book_cover_work.html:30 covers/change.html:44 lib/markdown.html:12 -#: lists/lists.html:53 lists/widget.html:317 +#: book_providers/openstax_read_button.html:24 +#: book_providers/standard_ebooks_read_button.html:24 +#: covers/author_photo.html:22 covers/book_cover.html:39 +#: covers/book_cover_single_edition.html:34 covers/book_cover_work.html:30 +#: covers/change.html:44 lib/markdown.html:12 lists/lists.html:53 +#: merge_queue/merge_queue.html:121 msgid "Close" msgstr "Schließen" @@ -1825,9 +2013,10 @@ msgstr "MOBI" #: book_providers/ia_download_options.html:19 msgid "Download open DAISY from Internet Archive (print-disabled format)" -msgstr "Ein open DAISY aus dem Internet Archive herunterladen (Druck deaktiviert)" +msgstr "" +"Ein open DAISY aus dem Internet Archive herunterladen (Druck deaktiviert)" -#: book_providers/ia_download_options.html:19 type/list/embed.html:84 +#: book_providers/ia_download_options.html:19 type/list/embed.html:85 msgid "DAISY" msgstr "DAISY" @@ -1865,43 +2054,69 @@ msgid "" "LibriVox is a trusted book provider of public domain audiobooks, narrated by " "volunteers, distributed for free on the internet." msgstr "" -"Dieses Buch ist verfügbar bei LibriVox. " -"LibriVox ist ein vertrauenswürdiger Anbieter von gemeinfreien Hörbüchern, " -"vorgelesen von Freiwilligen, kostenfrei über das Internet angeboten." +"Dieses Buch ist verfügbar bei LibriVox. LibriVox ist ein vertrauenswürdiger Anbieter von gemeinfreien " +"Hörbüchern, vorgelesen von Freiwilligen, kostenfrei über das Internet " +"angeboten." -#: book_providers/standard_ebooks_download_options.html:13 +#: book_providers/openstax_download_options.html:13 +msgid "Download PDF from OpenStax" +msgstr "PDF von OpenStax herunterladen" + +#: book_providers/openstax_download_options.html:14 +msgid "More at OpenStax" +msgstr "Mehr auf OpenStax" + +#: book_providers/openstax_read_button.html:10 +msgid "Read eBook from OpenStax" +msgstr "OpenStax-eBooks lesen" + +#: book_providers/openstax_read_button.html:21 +msgid "" +"This book is available from OpenStax. OpenStax is a trusted book provider and a 501(c)(3) nonprofit charitable " +"corporation dedicated to providing free high-quality, peer-reviewed, openly " +"licensed textbooks online." +msgstr "" +"Dieses Buch ist auf OpenStax " +"verfügbar. OpenStax ist ein vertrauenswürdiger Buchanbieter und eine 501(c)" +"(3) NGO für die Bereitstellung freier, hochqualitativer, peer-reviewter, " +"offen lizensierter Online-Lehrbücher." + +#: book_providers/standard_ebooks_download_options.html:14 msgid "Download an HTML from Standard Ebooks" msgstr "Ein HTML von Standard Ebooks herunterladen" -#: book_providers/standard_ebooks_download_options.html:14 +#: book_providers/standard_ebooks_download_options.html:15 msgid "Download an ePub from Standard Ebook." msgstr "Ein ePub von Standard Ebooks herunterladen." -#: book_providers/standard_ebooks_download_options.html:15 +#: book_providers/standard_ebooks_download_options.html:16 msgid "Download a Kindle file from Standard Ebooks" msgstr "Ein Kindle von Standard Ebooks herunterladen" -#: book_providers/standard_ebooks_download_options.html:15 +#: book_providers/standard_ebooks_download_options.html:16 msgid "Kindle (azw3)" msgstr "Kindle (azw3)" -#: book_providers/standard_ebooks_download_options.html:16 +#: book_providers/standard_ebooks_download_options.html:17 msgid "Download a Kobo file from Standard Ebooks" msgstr "Ein Kobo von Standard Ebooks herunterladen" -#: book_providers/standard_ebooks_download_options.html:16 +#: book_providers/standard_ebooks_download_options.html:17 msgid "Kobo (kepub)" msgstr "Kobo (kepub)" -#: book_providers/standard_ebooks_download_options.html:17 +#: book_providers/standard_ebooks_download_options.html:18 msgid "View the source code for this Standard Ebook at GitHub" msgstr "Den Quelltext für dieses Standard-Ebook auf GitHub" -#: Subnavigation.html:13 book_providers/standard_ebooks_download_options.html:17 +#: Subnavigation.html:13 +#: book_providers/standard_ebooks_download_options.html:18 msgid "Source Code" msgstr "Quelltext" -#: book_providers/standard_ebooks_download_options.html:18 +#: book_providers/standard_ebooks_download_options.html:19 msgid "More at Standard Ebooks" msgstr "Mehr auf Standard-Ebooks" @@ -1912,90 +2127,150 @@ msgstr "Auf Standard-eBooks lesen" #: book_providers/standard_ebooks_read_button.html:21 msgid "" "This book is available from Standard " -"Ebooks. Standard Ebooks is a trusted book provider and a volunteer-driven " -"project that produces new editions of public domain ebooks that are lovingly " -"formatted, open source, free of copyright restrictions, and free of cost." +"Ebooks. Standard Ebooks is a trusted book provider and a volunteer-" +"driven project that produces new editions of public domain ebooks that are " +"lovingly formatted, open source, free of copyright restrictions, and free of " +"cost." msgstr "" -"Dieses Buch ist bei Standard Ebooks " -"verfügbar. Standard Ebooks ist ein vertrauenswürdiger Anbieter von Büchern und " -"ein Projekt von Freiwilligen die neue Ausgaben von Ggemeinfreien Büchern " -"erstellen. Diese sind liebevoll gestaltet, quelloffen, gemeinfrei und kostenlos." +"Dieses Buch ist bei Standard Ebooks verfügbar. Standard Ebooks ist ein vertrauenswürdiger Anbieter von " +"Büchern und ein Projekt von Freiwilligen die neue Ausgaben von Ggemeinfreien " +"Büchern erstellen. Diese sind liebevoll gestaltet, quelloffen, gemeinfrei " +"und kostenlos." + +#: books/RelatedWorksCarousel.html:19 +msgid "You might also like" +msgstr "Gefallen könnte Ihnen auch" + +#: books/RelatedWorksCarousel.html:25 +msgid "More by this author" +msgid_plural "More by these authors" +msgstr[0] "Mehr von diese*r Autor*in" +msgstr[1] "Diese*n Autor*in entfernen" #: books/add.html:7 books/check.html:10 msgid "Add a book" msgstr "Ein Buch hinzufügen" +#: books/add.html:10 +msgid "Invalid ISBN checksum digit" +msgstr "Ungültige ISBN Check-Summe" + #: books/add.html:11 +msgid "" +"ID must be exactly 10 characters [0-9 or X]. For example 0-19-853453-1 or " +"0198534531" +msgstr "" +"ID muss aus exakt 10 Zeichen [0-9 oder X] bestehen. Zum Beispiel: " +"0-19-853453-1 oder 0198534531" + +#: books/add.html:12 +msgid "" +"ID must be exactly 13 characters [0-9]. For example 978-3-16-148410-0 or " +"9783161484100" +msgstr "" +"ID muss aus exakt 13 Zeichen [0-9] bestehen. Zum Beispiel: 978-3-16-148410-0 " +"oder 9783161484100" + +#: books/add.html:17 msgid "Add a book to Open Library" -msgstr "Ein Buch zur Open Library hinzufügen" +msgstr "Buch zur Open Library hinzufügen" -#: books/add.html:13 +#: books/add.html:19 msgid "" "We require a minimum set of fields to create a new record. These are those " "fields." msgstr "" -"Wir benötigen ein Minimum an Angaben um einen Datensatz zu erzeugen. Dies sind " -"die die Felder." +"Wir benötigen ein Minimum an Angaben um einen Datensatz zu erzeugen. Dies " +"sind die die Felder." -#: books/add.html:24 books/edit.html:79 books/edit/edition.html:95 -#: lib/nav_head.html:81 lib/search_foot.html:8 lib/search_head.html:8 -#: search/advancedsearch.html:16 type/about/edit.html:19 type/i18n/edit.html:64 -#: type/page/edit.html:20 type/rawtext/edit.html:18 type/template/edit.html:18 +#: books/add.html:31 books/edit.html:86 books/edit/edition.html:96 +#: lib/nav_head.html:92 search/advancedsearch.html:20 type/about/edit.html:19 +#: type/i18n/edit.html:64 type/page/edit.html:20 type/rawtext/edit.html:18 +#: type/template/edit.html:18 msgid "Title" msgstr "Titel" -#: books/add.html:34 +#: books/add.html:31 books/edit.html:86 +msgid "Use Title: Subtitle to add a subtitle." +msgstr "" +"Nutzen Sie Titel: Untertitel um einen Untertitel hinzuzufügen." + +#: books/add.html:31 books/add.html:48 books/add.html:57 books/edit.html:86 +#: books/edit/addfield.html:100 books/edit/addfield.html:145 +#: type/author/edit.html:35 +msgid "Required field" +msgstr "Feld zwingend erforderlich" + +#: books/add.html:42 msgid "" -"Like, \"Agatha Christie\" or \"Jean-Paul Sartre.\" We'll also do a live check to " -"see if we already know the author." +"Like, \"Agatha Christie\" or \"Jean-Paul Sartre.\" We'll also do a live " +"check to see if we already know the author." msgstr "Wir überprüfen in Echtzeit ob ein Author bereits angelegt wurde." -#: books/add.html:39 books/edit/edition.html:118 +#: books/add.html:48 books/edit/edition.html:119 msgid "Who is the publisher?" msgstr "Welcher Verlag?" -#: books/add.html:46 books/edit/edition.html:138 +#: books/add.html:48 books/edit/edition.html:120 +msgid "For example" +msgstr "Zum Beispiel" + +#: books/add.html:57 books/edit/edition.html:139 msgid "When was it published?" msgstr "Wann wurde es veröffentlicht?" -#: books/add.html:46 -msgid "The year it was published is plenty." -msgstr "Das Jahr der Veröffentlichung reicht aus." +#: books/add.html:57 books/edit/edition.html:140 +msgid "You should be able to find this in the first few pages of the book." +msgstr "Sie sollten dies auf den ersten Seiten des Buches finden können." -#: books/add.html:54 -msgid "And optionally, an ID number — like an ISBN — would be helpful..." +#: books/add.html:66 +msgid "" +"And optionally, an ID number — like an ISBN — would be helpful..." msgstr "Optional eine ID-Number — wie eine ISBN — wäre hilfreich..." -#: books/add.html:55 +#: books/add.html:67 msgid "ID Type" msgstr "ID-Typ" -#: books/add.html:58 +#: books/add.html:69 +msgid "ISBN may be invalid. Click \"Add\" again to submit." +msgstr "ISBN könnte ungültig sein. Erneut \"Hinzufügen\" zum Absenden klicken." + +#: books/add.html:72 msgid "Select" msgstr "Auswählen" -#: books/add.html:72 +#: books/add.html:87 books/edit/edition.html:652 +msgid "Book URL" +msgstr "Buch-URL" + +#: books/add.html:87 +msgid "Only fill this out if this is a web book" +msgstr "Nur ausfüllen, wenn es ein Web-Buch ist" + +#: books/add.html:96 msgid "Since you are not logged in, please satisfy the reCAPTCHA below." msgstr "Da Sie nicht eingeloggt sind, müssen Sie das reCAPTCHA lösen." -#: EditButtons.html:17 EditButtonsMacros.html:20 books/add.html:79 +#: EditButtons.html:17 EditButtonsMacros.html:20 books/add.html:103 msgid "" "By saving a change to this wiki, you agree that your contribution is given " "freely to the world under CC0. Yippee!" +"zero/1.0/\" target=\"_blank\" title=\"This link to Creative Commons will " +"open in a new window\">CC0. Yippee!" msgstr "" -"Mit dem Speichern der Änderungen in diesem Wikki stimmen Sie der weltweit freien " -"Nutzung Ihres Beitrags unter CC0 zu. Juhu!" +"Mit dem Speichern der Änderungen in diesem Wikki stimmen Sie der weltweit " +"freien Nutzung Ihres Beitrags unter CC0 zu. Juhu!" -#: books/add.html:82 +#: books/add.html:106 msgid "Add this book now" msgstr "Dieses Buch jetzt hinzufügen" -#: books/add.html:82 books/edit/addfield.html:85 books/edit/addfield.html:131 -#: books/edit/addfield.html:175 books/edit/edition.html:226 +#: books/add.html:106 books/edit/addfield.html:85 books/edit/addfield.html:131 +#: books/edit/addfield.html:175 books/edit/edition.html:227 #: books/edit/edition.html:456 books/edit/edition.html:521 #: books/edit/excerpts.html:50 covers/change.html:49 msgid "Add" @@ -2013,15 +2288,15 @@ msgstr "Diese*n Autor*in entfernen" msgid "Add another author?" msgstr "Eine*n weitere*n Autor*in hinzufügen?" -#: books/check.html:13 lib/nav_foot.html:41 lib/nav_head.html:23 +#: books/check.html:13 lib/nav_foot.html:41 lib/nav_head.html:38 msgid "Add a Book" msgstr "Ein Buch hinzufügen" #: books/check.html:15 #, python-format msgid "" -"One moment... It looks like we already have some potential matches for " -"%(title)s by %(author)s." +"One moment... It looks like we already have some potential matches " +"for %(title)s by %(author)s." msgstr "" "Einen Moment... Es sieht aus als hätten wir bereits einige potentielle " "Übereinstimmungen für %(title)s von %(author)s." @@ -2031,8 +2306,8 @@ msgid "" "Rather than creating a duplicate record, please click through the result you " "think best matches your book." msgstr "" -"Bitte prüfen Sie, ob einer der angezeigten Titel dem Buch entspricht, das Sie " -"hinzufügen wollen. So vermeiden Sie Dubletten in der Datenbank." +"Bitte prüfen Sie, ob einer der angezeigten Titel dem Buch entspricht, das " +"Sie hinzufügen wollen. So vermeiden Sie Dubletten in der Datenbank." #: books/check.html:27 books/check.html:31 msgid "Select this book" @@ -2043,154 +2318,153 @@ msgstr "Dieses Buch auswählen" msgid "First published in %(year)d" msgstr "Ersterscheinung im Jahr %(year)d" -#: books/custom_carousel.html:33 +#: books/custom_carousel.html:41 #, python-format msgid " by %(name)s" msgstr " nach %(name)s" -#: books/edit.html:28 books/edit.html:31 books/edit.html:34 +#: books/edit.html:30 books/edit.html:33 books/edit.html:36 msgid "Add a little more?" msgstr "Etwas mehr hinzufügen?" -#: books/edit.html:29 +#: books/edit.html:31 msgid "" -"Thank you for adding that book! Any more information you could provide would be " -"wonderful!" +"Thank you for adding that book! Any more information you could provide would " +"be wonderful!" msgstr "" -"Vielen Dank, dass Sie das Buch hinzugefügt haben. Falls Sie weitere Informationen " -"bereitstellen können, wäre das wundervoll!" +"Vielen Dank, dass Sie das Buch hinzugefügt haben. Falls Sie weitere " +"Informationen bereitstellen können, wäre das wundervoll!" -#: books/edit.html:32 +#: books/edit.html:34 #, python-format msgid "" "We already know about %(work_title)s, but not the %(year)s " "%(publisher)s edition. Thanks! Do you know any more about this edition?" msgstr "" -"Wir kennen bereits das Werk %(work_title)s, aber nicht diese %(year)s " -"%(publisher)s Ausgabe. Danke! Können Sie weitere Informationen hinzufügen?" +"Wir kennen bereits das Werk %(work_title)s, aber nicht diese " +"%(year)s %(publisher)s Ausgabe. Danke! Können Sie weitere " +"Informationen hinzufügen?" -#: books/edit.html:35 +#: books/edit.html:37 #, python-format msgid "" -"We already know about the %(year)s %(publisher)s edition of " -"%(work_title)s, but please, tell us more!" +"We already know about the %(year)s %(publisher)s edition of " +"%(work_title)s, but please, tell us more!" msgstr "" -"Wir kennen bereits die Ausgabe %(year)s %(publisher)s von " -"%(work_title)s aber bitte erzähl uns mehr!" +"Wir kennen bereits die Ausgabe %(year)s %(publisher)s von " +"%(work_title)s aber bitte erzähl uns mehr!" -#: books/edit.html:37 +#: books/edit.html:39 msgid "Editing..." msgstr "Wird bearbeitet..." -#: EditButtons.html:25 EditButtonsMacros.html:27 books/edit.html:41 +#: EditButtonsMacros.html:27 books/edit.html:45 type/author/edit.html:17 #: type/template/edit.html:51 msgid "Delete Record" msgstr "Eintrag löschen" -#: books/edit.html:58 +#: books/edit.html:65 msgid "Work Details" msgstr "Details des Werks" -#: books/edit.html:63 +#: books/edit.html:70 msgid "Unknown publisher edition" msgstr "Ausgabe von unbekanntem Verlag" -#: books/edit.html:73 +#: books/edit.html:80 msgid "This Work" msgstr "Dieses Werk" -#: books/edit.html:79 -msgid "Use Title: Subtitle to add a subtitle." -msgstr "Nutzen Sie Titel: Untertitel um einen Untertitel hinzuzufügen." - -#: books/edit.html:79 books/edit/addfield.html:100 books/edit/addfield.html:145 -#: type/author/edit.html:29 -msgid "Required field" -msgstr "Feld zwingend erforderlich" - -#: books/edit.html:86 +#: books/edit.html:93 msgid "" -"You can search by author name (like j k rowling) or by Open Library ID " -"(like OL23919A)." +"You can search by author name (like j k rowling) or by Open Library " +"ID (like OL23919A)." msgstr "" "Sie können such nach Autorennamen (wie j k rowling oder nach der " -"OpenLibrary-ID (wie OL23919A)." +"OpenLibrary-ID (wie OL23919A)." -#: books/edit.html:91 +#: books/edit.html:98 msgid "Author editing requires javascript" msgstr "Bearbeitung von Autoren benötigt JavaScript" -#: books/edit.html:104 +#: books/edit.html:111 msgid "Add Excerpts" msgstr "Auszüge hinzufügen" -#: books/edit.html:110 type/about/edit.html:39 type/author/edit.html:44 +#: books/edit.html:117 type/about/edit.html:39 type/author/edit.html:50 msgid "Links" msgstr "Links" -#: books/edit/edition.html:65 books/edition-sort.html:37 lists/preview.html:9 -#: lists/snippet.html:9 lists/widget.html:89 type/work/editions.html:27 +#: SearchResultsWork.html:45 SearchResultsWork.html:46 +#: books/edit/edition.html:66 books/edition-sort.html:39 +#: books/edition-sort.html:40 lists/list_overview.html:11 lists/preview.html:9 +#: lists/snippet.html:9 lists/widget.html:83 type/work/editions.html:27 #, python-format msgid "Cover of: %(title)s" msgstr "Cover von: %(title)s" -#: books/edition-sort.html:44 +#: books/edition-sort.html:49 #, python-format msgid "%(title)s: %(subtitle)s" msgstr "%(title)s: %(subtitle)s" -#: books/edition-sort.html:56 +#: books/edition-sort.html:61 #, python-format msgid "Publish date unknown, %(publisher)s" msgstr "Veröffentlichungsdatum unbekannt, %(publisher)s" -#: books/edition-sort.html:66 +#: books/edition-sort.html:71 type/work/editions.html:47 #, python-format msgid "in %(languagelist)s" msgstr "in %(languagelist)s" -#: books/edition-sort.html:94 +#: books/edition-sort.html:99 msgid "Libraries near you:" msgstr "Bibliotheken in Ihrer Nähe:" -#: books/edit/about.html:10 +#: books/edit/about.html:21 +msgid "Select this subject" +msgstr "Dieses Thema auswählen" + +#: books/edit/about.html:28 msgid "How would you describe this book?" msgstr "Wie würden Sie das Buch beschreiben?" -#: books/edit/about.html:11 +#: books/edit/about.html:29 msgid "There's no wrong answer here." msgstr "Es gibt hier keine falschen Antworten." -#: books/edit/about.html:20 +#: books/edit/about.html:38 msgid "Subject keywords?" msgstr "Themen Schlüsselwörter?" -#: books/edit/about.html:21 +#: books/edit/about.html:39 msgid "Please separate with commas." msgstr "Bitte mit Kommata trennen." -#: books/edit/about.html:21 books/edit/about.html:35 books/edit/about.html:49 -#: books/edit/about.html:63 books/edit/addfield.html:77 +#: books/edit/about.html:39 books/edit/about.html:50 books/edit/about.html:61 +#: books/edit/about.html:72 books/edit/addfield.html:77 #: books/edit/addfield.html:104 books/edit/addfield.html:113 -#: books/edit/addfield.html:149 books/edit/edition.html:106 -#: books/edit/edition.html:129 books/edit/edition.html:162 -#: books/edit/edition.html:172 books/edit/edition.html:265 -#: books/edit/edition.html:367 books/edit/edition.html:381 -#: books/edit/edition.html:582 books/edit/excerpts.html:19 books/edit/web.html:23 -#: type/author/edit.html:107 +#: books/edit/addfield.html:149 books/edit/edition.html:107 +#: books/edit/edition.html:130 books/edit/edition.html:163 +#: books/edit/edition.html:173 books/edit/edition.html:266 +#: books/edit/edition.html:368 books/edit/edition.html:382 +#: books/edit/edition.html:582 books/edit/excerpts.html:19 +#: books/edit/web.html:23 type/author/edit.html:113 msgid "For example:" msgstr "Zum Beispiel:" -#: books/edit/about.html:34 +#: books/edit/about.html:49 msgid "A person? Or, people?" msgstr "Eine Person oder Gruppe?" -#: books/edit/about.html:48 +#: books/edit/about.html:60 msgid "Note any places mentioned?" msgstr "Werden Orte erwähnt?" -#: books/edit/about.html:62 +#: books/edit/about.html:71 msgid "When is it set or about?" msgstr "Um welche Zeit geht es?" @@ -2226,208 +2500,202 @@ msgstr "" #: books/edit/addfield.html:167 msgid "" -"Can you direct us to more information about this classification system online?" +"Can you direct us to more information about this classification system " +"online?" msgstr "" -"Können Sie uns einen Hinweis geben, wo wir online mehr Informationen zu dieser " -"Klassifikation finden?" +"Können Sie uns einen Hinweis geben, wo wir online mehr Informationen zu " +"dieser Klassifikation finden?" #: books/edit/edition.html:22 msgid "Select this language" msgstr "Diese Sprache auswählen" -#: books/edit/edition.html:31 books/edit/edition.html:40 +#: books/edit/edition.html:32 books/edit/edition.html:41 msgid "Remove this language" msgstr "Diese Sprache entfernen" -#: books/edit/edition.html:32 +#: books/edit/edition.html:33 msgid "Add another language?" msgstr "Eine weitere Sprache hinzufügen?" -#: books/edit/edition.html:51 +#: books/edit/edition.html:52 msgid "Remove this work" msgstr "Dieses Werk entfernen" -#: books/edit/edition.html:59 books/edit/edition.html:400 +#: books/edit/edition.html:60 books/edit/edition.html:401 msgid "-- Move to a new work" msgstr "-- In ein neues Werk verschieben" -#: EditionNavBar.html:17 books/edit/edition.html:86 +#: books/edit/edition.html:87 msgid "This Edition" msgstr "Diese Ausgabe" -#: books/edit/edition.html:96 +#: books/edit/edition.html:97 msgid "Enter the title of this specific edition." msgstr "Titel dieser spezifischen Ausgabe eingeben." -#: books/edit/edition.html:105 +#: books/edit/edition.html:106 msgid "Subtitle" msgstr "Untertitel" -#: books/edit/edition.html:106 +#: books/edit/edition.html:107 msgid "Usually distinguished by different or smaller type." msgstr "" "Normalerweise durch eine andere Schriftart oder eine kleinere Schriftgröße " "unterschieden." -#: books/edit/edition.html:113 +#: books/edit/edition.html:114 msgid "Publishing Info" msgstr "Veröffentlichungsinformationen" -#: books/edit/edition.html:119 -msgid "For example" -msgstr "Zum Beispiel" - -#: books/edit/edition.html:128 +#: books/edit/edition.html:129 msgid "Where was the book published?" msgstr "Wo wurde das Buch veröffentlicht?" -#: books/edit/edition.html:129 +#: books/edit/edition.html:130 msgid "City, Country please." msgstr "Ort und Land bitte." -#: books/edit/edition.html:139 -msgid "You should be able to find this in the first few pages of the book." -msgstr "Sie sollten dies auf den ersten Seiten des Buches finden können." - -#: books/edit/edition.html:151 +#: books/edit/edition.html:152 msgid "What is the copyright date?" msgstr "Wie ist das Copyright-Datum?" -#: books/edit/edition.html:152 +#: books/edit/edition.html:153 msgid "The year following the copyright statement." msgstr "Das Jahr, welches nach der Copyright-Angabe folgt." -#: books/edit/edition.html:161 +#: books/edit/edition.html:162 msgid "Edition Name (if applicable):" msgstr "Name der Ausgabe (falls zutreffend):" -#: books/edit/edition.html:171 +#: books/edit/edition.html:172 msgid "Series Name (if applicable)" msgstr "Name der Reihe (falls zutreffend)" -#: books/edit/edition.html:172 +#: books/edit/edition.html:173 msgid "The name of the publisher's series." msgstr "Der Name der Verlags-Reihe." -#: books/edit/edition.html:200 type/edition/view.html:441 type/work/view.html:441 +#: books/edit/edition.html:201 type/edition/view.html:417 +#: type/work/view.html:417 msgid "Contributors" msgstr "Mitwirkende" -#: books/edit/edition.html:202 +#: books/edit/edition.html:203 msgid "Please select a role." msgstr "Bitte eine Rolle/Funktion auswählen." -#: books/edit/edition.html:202 +#: books/edit/edition.html:203 msgid "You need to give this ROLE a name." msgstr "Sie müssen dieser Rolle/Funktion einen Namen geben." -#: books/edit/edition.html:204 +#: books/edit/edition.html:205 msgid "List the people involved" msgstr "Beteiligte Personen auflisten" -#: books/edit/edition.html:214 +#: books/edit/edition.html:215 msgid "Select role" msgstr "Rolle/Funktion auswählen" -#: books/edit/edition.html:219 +#: books/edit/edition.html:220 msgid "Add a new role" msgstr "Neue Rolle/Funktion hinzufügen" -#: books/edit/edition.html:239 books/edit/edition.html:255 +#: books/edit/edition.html:240 books/edit/edition.html:256 msgid "Remove this contributor" msgstr "Diese*n Beteiligte*n entfernen" -#: books/edit/edition.html:264 +#: books/edit/edition.html:265 msgid "By Statement:" msgstr "Geschrieben von:" -#: books/edit/edition.html:275 +#: books/edit/edition.html:276 msgid "Languages" msgstr "Sprachen" -#: books/edit/edition.html:279 +#: books/edit/edition.html:280 msgid "What language is this edition written in?" msgstr "In welcher Sprache ist diese Ausgabe verfasst?" -#: books/edit/edition.html:291 +#: books/edit/edition.html:292 msgid "Is it a translation of another book?" msgstr "Ist es eine Übersetzung eines anderen Buches?" -#: books/edit/edition.html:309 +#: books/edit/edition.html:310 msgid "Yes, it's a translation" msgstr "Ja, es ist eine Übersetzung" -#: books/edit/edition.html:314 +#: books/edit/edition.html:315 msgid "What's the original book?" msgstr "Welches ist das Original-Buch?" -#: books/edit/edition.html:323 +#: books/edit/edition.html:324 msgid "What language was the original written in?" msgstr "In welcher Sprache wurde das Original verfasst?" -#: books/edit/edition.html:341 +#: books/edit/edition.html:342 msgid "Description of this edition" msgstr "Beschreibung der Ausgabe" -#: books/edit/edition.html:342 +#: books/edit/edition.html:343 msgid "Only if it's different from the work's description" msgstr "Nur wenn sie von der Beschreibung des Werks abweicht" -#: TableOfContents.html:10 books/edit/edition.html:351 +#: TableOfContents.html:10 books/edit/edition.html:352 msgid "Table of Contents" msgstr "Inhaltsverzeichnis" -#: books/edit/edition.html:352 +#: books/edit/edition.html:353 msgid "" "Use a \"*\" for an indent, a \"|\" to add a column, and line breaks for new " "lines. Like this:" msgstr "" -"Nutzen Sie \"*\" für eine Einrückung, ein \"|\" um eine Spalte hinzuzufügen, und " -"Zeilenumbrüche für neue Zeilen. So wie hier:" +"Nutzen Sie \"*\" für eine Einrückung, ein \"|\" um eine Spalte hinzuzufügen, " +"und Zeilenumbrüche für neue Zeilen. So wie hier:" -#: books/edit/edition.html:366 +#: books/edit/edition.html:367 msgid "Any notes about this specific edition?" msgstr "Hinweise zu dieser spezifischen Ausgabe?" -#: books/edit/edition.html:367 +#: books/edit/edition.html:368 msgid "Anything about the book that may be of interest." msgstr "Alles was zu dem Buch von Interesse sein könnte." -#: books/edit/edition.html:376 +#: books/edit/edition.html:377 msgid "Is it known by any other titles?" msgstr "Kennt man es auch unter anderen Titeln?" -#: books/edit/edition.html:377 +#: books/edit/edition.html:378 msgid "" -"Use this field if the title is different on the cover or spine. If the edition " -"is a collection or anthology, you may also add the individual titles of each " -"work here." +"Use this field if the title is different on the cover or spine. If the " +"edition is a collection or anthology, you may also add the individual titles " +"of each work here." msgstr "" "Benutzen Sie dieses Feld, wenn der Titel sich von dem auf dem Cover und dem " -"Buchrücken unterscheidet. Wenn die Ausgabe eine Sammlung oder Anthologie ist, " -"können Sie die individuellen Titel der einzelnen Werke hier angeben." +"Buchrücken unterscheidet. Wenn die Ausgabe eine Sammlung oder Anthologie " +"ist, können Sie die individuellen Titel der einzelnen Werke hier angeben." -#: books/edit/edition.html:381 +#: books/edit/edition.html:382 msgid "is an alternate title for" msgstr "ist ein alternativer Titel für" -#: books/edit/edition.html:391 +#: books/edit/edition.html:392 msgid "What work is this an edition of?" msgstr "Von welchem Werk ist dies eine Ausgabe?" -#: books/edit/edition.html:393 +#: books/edit/edition.html:394 msgid "" -"Sometimes editions can be associated with the wrong work. You can correct that " -"here." +"Sometimes editions can be associated with the wrong work. You can correct " +"that here." msgstr "" "Manchmal sind Ausgaben dem falschen Werk zugeordnet. sie können dies hier " "korrigieren." -#: books/edit/edition.html:394 +#: books/edit/edition.html:395 msgid "You can search by title or by Open Library ID (like" msgstr "Sie können nach Titel oder OpenLibrary-ID suchen (wie" -#: books/edit/edition.html:397 +#: books/edit/edition.html:398 msgid "WARNING: Any edits made to the work from this page will be ignored." msgstr "" "WARNUNG: Alle Änderungen an diesem Werks von dieser Seite werden ignoriert." @@ -2448,13 +2716,17 @@ msgstr "IDs können keine Leerzeichen enthalten." msgid "ID must be exactly 10 characters [0-9] or X." msgstr "ID muss aus exakt 10 Ziffern (0-9), oder Xs bestehen." +#: books/edit/edition.html:414 +msgid "That ISBN already exists for this edition." +msgstr "Diese ISBN existiert bereits für diese Ausgabe." + #: books/edit/edition.html:414 msgid "ID must be exactly 13 digits [0-9]. For example: 978-1-56619-909-4" msgstr "" "ID muss aus exakt 13 Ziffern [0-9] bestehen. Zum Beispiel: 978-1-56619-909-4" -#: books/edit/edition.html:416 type/author/view.html:173 type/edition/view.html:462 -#: type/work/view.html:462 +#: books/edit/edition.html:416 type/author/view.html:188 +#: type/edition/view.html:438 type/work/view.html:438 msgid "ID Numbers" msgstr "IDs" @@ -2478,8 +2750,8 @@ msgstr "Bitte wählen Sie eine Klassifikation aus." msgid "You need to give a value to CLASS." msgstr "Sie müssen der Klassifikation einen Wert geben." -#: books/edit/edition.html:494 type/edition/view.html:334 -#: type/edition/view.html:416 type/work/view.html:334 type/work/view.html:416 +#: books/edit/edition.html:494 type/edition/view.html:392 +#: type/work/view.html:392 msgid "Classifications" msgstr "Klassifikationen" @@ -2499,7 +2771,8 @@ msgstr "Neuen Klassifizierungs-Typ hinzufügen" msgid "Remove this classification" msgstr "Diese Klassifizierung entfernen" -#: books/edit/edition.html:552 type/edition/view.html:450 type/work/view.html:450 +#: books/edit/edition.html:552 type/edition/view.html:426 +#: type/work/view.html:426 msgid "The Physical Object" msgstr "Das physische Objekt" @@ -2531,7 +2804,8 @@ msgstr "Hilfe" msgid "How much does the book weigh?" msgstr "Wie schwer ist das Buch?" -#: books/edit/edition.html:603 type/edition/view.html:455 type/work/view.html:455 +#: books/edit/edition.html:603 type/edition/view.html:431 +#: type/work/view.html:431 msgid "Dimensions" msgstr "Maße" @@ -2547,19 +2821,51 @@ msgstr "Breite:" msgid "Depth:" msgstr "Tiefe:" -#: books/edit/edition.html:640 +#: books/edit/edition.html:648 +msgid "Web Book Providers" +msgstr "Web-Buch-Anbieter" + +#: books/edit/edition.html:653 +msgid "Access Type" +msgstr "Zugangsart" + +#: books/edit/edition.html:654 +msgid "File Format" +msgstr "Dateiformat" + +#: books/edit/edition.html:655 +msgid "Provider Name" +msgstr "Anbietername" + +#: ReadButton.html:39 books/edit/edition.html:666 +msgid "Listen" +msgstr "Anhören" + +#: books/edit/edition.html:667 +msgid "Buy" +msgstr "Kaufen" + +#: books/edit/edition.html:675 +msgid "Web" +msgstr "Internet" + +#: books/edit/edition.html:685 +msgid "Add a provider" +msgstr "Neuen Anbieter hinzufügen" + +#: books/edit/edition.html:690 msgid "Admin Only" msgstr "Nur Administatoren" -#: books/edit/edition.html:643 +#: books/edit/edition.html:693 msgid "Additional Metadata" msgstr "Zusätzliche Metadaten" -#: books/edit/edition.html:644 +#: books/edit/edition.html:694 msgid "This field can accept arbitrary key:value pairs" msgstr "Dieses Feld darf beliebige Schlüssel-Wert-Paare enthalten" -#: books/edit/edition.html:656 +#: books/edit/edition.html:706 msgid "First sentence" msgstr "Erster Satz" @@ -2616,8 +2922,8 @@ msgid "" "Please connect Open Library records to good* online resources." msgstr "" -"Bitte verknüpfen Sie Open-Library-Einträge zu guten* online resources." +"Bitte verknüpfen Sie Open-Library-Einträge zu guten* online resources." #: books/edit/web.html:19 msgid "Give your link a label" @@ -2630,47 +2936,181 @@ msgstr "Diesen Link entfernen" #: books/edit/web.html:65 msgid "" "\"Good\" means no spammy links, please. Keep them relevant to the book. " -"Irrelevant sites may be removed. Blatant spam will be deleted without remorse." +"Irrelevant sites may be removed. Blatant spam will be deleted without " +"remorse." msgstr "" "„Gut“ heißt in diesem Falle: kein Spam, bitte! Sie sollten relevant sein. " -"Irrelevante Seiten werden gegebenenfalls entfernt. Offensichtlicher Spam oder " -"Werbung werden sofort gelöscht." +"Irrelevante Seiten werden gegebenenfalls entfernt. Offensichtlicher Spam " +"oder Werbung werden sofort gelöscht." + +#: check_ins/check_in_form.html:10 +msgid "January" +msgstr "Januar" + +#: check_ins/check_in_form.html:11 +msgid "February" +msgstr "Februar" + +#: check_ins/check_in_form.html:12 +msgid "March" +msgstr "März" + +#: check_ins/check_in_form.html:13 +msgid "April" +msgstr "April" + +#: check_ins/check_in_form.html:14 +msgid "May" +msgstr "Mai" + +#: check_ins/check_in_form.html:15 +msgid "June" +msgstr "Juni" + +#: check_ins/check_in_form.html:16 +msgid "July" +msgstr "July" + +#: check_ins/check_in_form.html:17 +msgid "August" +msgstr "August" + +#: check_ins/check_in_form.html:18 +msgid "September" +msgstr "August" + +#: check_ins/check_in_form.html:19 +msgid "October" +msgstr "Oktober" + +#: check_ins/check_in_form.html:20 +msgid "November" +msgstr "November" + +#: check_ins/check_in_form.html:21 +msgid "December" +msgstr "Dezember" + +#: check_ins/check_in_form.html:38 +msgid "" +"Add an optional check-in date. Check-in dates are used to track yearly " +"reading goals." +msgstr "" +"Fügen Sie ein optionales Check-In-Datum hinzu. Check-In-Daten werden genutzt," + +#: check_ins/check_in_form.html:41 +msgid "End Date:" +msgstr "Enddatum:" + +#: check_ins/check_in_form.html:43 +msgid "Year:" +msgstr "Jahr:" + +#: check_ins/check_in_form.html:45 +msgid "Year" +msgstr "Jahr" + +# | msgid "This Edition" +#: check_ins/check_in_form.html:53 +msgid "Month:" +msgstr "Monat:" + +# | msgid "This Edition" +#: check_ins/check_in_form.html:55 +msgid "Month" +msgstr "Monat" + +#: check_ins/check_in_form.html:62 +msgid "Day:" +msgstr "Tag:" + +#: check_ins/check_in_form.html:64 +msgid "Day" +msgstr "Tag" + +#: check_ins/check_in_form.html:76 +msgid "Delete Event" +msgstr "Veranstaltung löschen" + +#: check_ins/check_in_prompt.html:32 +msgid "When did you finish this book?" +msgstr "Wann haben Sie dieses Buch beendet?" + +#: check_ins/check_in_prompt.html:37 +msgid "Other" +msgstr "Andere(s)" + +#: check_ins/check_in_prompt.html:42 +msgid "Check-In" +msgstr "Check-In" + +#: check_ins/reading_goal_form.html:12 +msgid "How many books would you like to read this year?" +msgstr "Wie viele Bücher möchten sie dieses Jahr lesen?" + +#: check_ins/reading_goal_form.html:18 +msgid "" +"Note: Submit \"0\" to delete your goal. Your check-ins will be preserved." +msgstr "" +"Hinweis: \"0\" eingeben, um Ihr Ziel zu entfernen. Ihre Check-Ins werden " +"gespeichert." + +#: check_ins/reading_goal_progress.html:18 +#, python-format +msgid "%(year)d Reading Goal:" +msgstr "%(year)d Leseziel:" + +#: check_ins/reading_goal_progress.html:25 +#, python-format +msgid "" +"%(books_read)d/" +"%(goal)d books" +msgstr "" +"%(books_read)d/" +"%(goal)d Bücher" + +#: check_ins/reading_goal_progress.html:31 +#, python-format +msgid "Edit %(year)d Reading Goal" +msgstr "%(year)d Leseziel bearbeiten" -#: covers/add.html:11 +#: covers/add.html:12 msgid "There are two ways to put an author's image on Open Library" msgstr "Es gibt zwei Wege, ein Bild des Autors zur OpenLibrary hinzuzufügen" -#: covers/add.html:13 +#: covers/add.html:14 msgid "Image Guidelines" msgstr "Richtlinien für Bilder" -#: covers/add.html:15 +#: covers/add.html:16 msgid "There are two ways to put a cover image on Open Library" -msgstr "Es gibt zwei Wege, ein Umschlagsbild (Cover) zur Open Library hinzuzufügen" +msgstr "" +"Es gibt zwei Wege, ein Umschlagsbild (Cover) zur Open Library hinzuzufügen" -#: covers/add.html:17 +#: covers/add.html:18 msgid "Cover Guidelines" msgstr "Richtlinien für Umschlagbilder (Cover)" -#: covers/add.html:22 covers/add.html:26 +#: covers/add.html:23 covers/add.html:27 msgid "Please provide a valid image." msgstr "Bitte laden sie eine gültige Bilddatei hoch." -#: covers/add.html:24 +#: covers/add.html:25 msgid "Please provide an image URL." msgstr "Bitte geben sie einen Internet-Link (URL) zu dem Bild an." -#: covers/add.html:39 +#: covers/add.html:40 msgid "Pick one from the existing covers," msgstr "Wählen Sie eines der existierenden Umschlagsbilder," -#: covers/add.html:60 +#: covers/add.html:61 msgid "Choose a JPG, GIF or PNG on your computer," msgstr "Wählen Sie ein JPG, GIF oder PNG von Ihrem Computer," -#: covers/add.html:69 +#: covers/add.html:70 msgid "Or, paste in the image URL if it's on the web." -msgstr "Oder fügen Sie den Link zum Bild ein, wenn es im Internet verfügbar ist." +msgstr "" +"Oder fügen Sie den Link zum Bild ein, wenn es im Internet verfügbar ist." #: covers/book_cover.html:22 msgid "Pull up a bigger book cover" @@ -2727,38 +3167,41 @@ msgstr "Ein Titelbild hinzufügen" msgid "Manage" msgstr "Verwalten" -#: covers/manage.html:11 +#: covers/manage.html:12 msgid "" -"You can drag and drop thumbnails to reorder, or into the trash to delete them." +"You can drag and drop thumbnails to reorder, or into the trash to delete " +"them." msgstr "" -"Sie können die Vorschaubilder per Drag & Drop neu anordnen, oder zum Löschen in " -"den Papierkorb ziehen." +"Sie können die Vorschaubilder per Drag & Drop neu anordnen, oder zum Löschen " +"in den Papierkorb ziehen." -#: covers/saved.html:16 +#: covers/saved.html:17 msgid "Saved!" msgstr "Gespeichert!" -#: covers/saved.html:19 +#: covers/saved.html:20 msgid "Notes:" msgstr "Notizen:" -#: covers/saved.html:29 +#: covers/saved.html:30 msgid "Add another image" msgstr "Ein weiteres Titelbild hinzufügen" -#: covers/saved.html:35 +#: covers/saved.html:36 msgid "Finished" msgstr "Erledigt" #: history/comment.html:26 #, python-format msgid "Imported from %(source)s %(type)s record." -msgstr "Importiert aus einem %(source)s %(type)s Eintrag." +msgstr "" +"Importiert aus einem %(source)s %(type)s Eintrag." #: history/comment.html:28 #, python-format msgid "Found a matching record from %(source)s." -msgstr "Einen passenden Eintrag bei %(source)s gefunden." +msgstr "" +"Einen passenden Eintrag bei %(source)s gefunden." #: history/comment.html:35 msgid "Edited without comment." @@ -2770,29 +3213,28 @@ msgstr "Über das Projekt" #: home/about.html:15 msgid "" -"Open Library is an open, editable library catalog, building towards a web page " -"for every book ever published." +"Open Library is an open, editable library catalog, building towards a web " +"page for every book ever published." msgstr "" "Open Library ist ein frei zugänglicher und von allen zu bearbeitender Online-" "Katalog, welcher jedes Buch enthalten soll, das jemals veröffentlicht wurde." -#: AffiliateLinks.html:70 home/about.html:16 lib/nav_head.html:40 -#: lib/nav_head.html:65 +#: AffiliateLinks.html:70 home/about.html:16 msgid "More" msgstr "Mehr" #: home/about.html:19 msgid "" -"Just like Wikipedia, you can contribute new information or corrections to the " -"catalog. You can browse by subjects, authors or lists members have created. If you love " -"books, why not help build a library?" +"Just like Wikipedia, you can contribute new information or corrections to " +"the catalog. You can browse by subjects, authors or lists members have " +"created. If you love books, why not help build a library?" msgstr "" "Wie in der Wikipedia auch, können Sie neue Informationen und Korrekturen zum " -"Katalog beitragen. Sie können nach Themen, Autoren oder Listen der Mitglieder " -"durchsuchen. Wenn Sie Bücher lieben, warum nicht mithelfen eine Bücherei " -"aufzubauen?" +"Katalog beitragen. Sie können nach Themen, Autoren oder Listen der " +"Mitglieder durchsuchen. Wenn Sie Bücher lieben, warum nicht mithelfen eine " +"Bücherei aufzubauen?" #: home/about.html:23 msgid "Latest Blog Posts" @@ -2807,7 +3249,7 @@ msgstr "Nach Themen durchsuchen" msgid "browse %(subject)s books" msgstr "durchsuche %(subject)s Bücher" -#: home/categories.html:31 +#: home/categories.html:27 #, python-format msgid "%(count)s Book" msgid_plural "%(count)s Books" @@ -2818,31 +3260,27 @@ msgstr[1] "%(count)s Bücher" msgid "Welcome to Open Library" msgstr "Willkommen in der Open Library" -#: home/index.html:26 +#: home/index.html:29 msgid "Classic Books" msgstr "Klassiker und Dauerbrenner" -#: home/index.html:31 -msgid "Books We Love" -msgstr "Bücher die wir lieben" - -#: home/index.html:33 +#: home/index.html:36 msgid "Recently Returned" msgstr "Kürzlich zurückgegeben" -#: home/index.html:35 +#: home/index.html:38 msgid "Romance" msgstr "Romanze" -#: home/index.html:37 +#: home/index.html:40 msgid "Kids" msgstr "Kinder" -#: home/index.html:39 +#: home/index.html:42 msgid "Thrillers" msgstr "Thriller" -#: home/index.html:41 +#: home/index.html:44 msgid "Textbooks" msgstr "Lehrbücher" @@ -2852,8 +3290,8 @@ msgstr "Über die Bibliothek" #: home/stats.html:10 msgid "" -"Here's what's happened over the last 28 days. More recent changes" +"Here's what's happened over the last 28 days. More recent changes" msgstr "" "Hier sehen Sie was über die letzten 28 Tage passiert ist. Mehr kürzlich Änderungen" @@ -2862,6 +3300,10 @@ msgstr "" msgid "See all visitors to OpenLibrary.org" msgstr "Alle Besucher der OpenLibrary.org anzeigen" +#: home/stats.html:16 +msgid "Area graph of recent unique visitors" +msgstr "Flächendiagramm aktueller eindeutiger Besucher*innen" + #: home/stats.html:17 msgid "Unique Visitors" msgstr "Individuelle Besucher" @@ -2870,6 +3312,10 @@ msgstr "Individuelle Besucher" msgid "How many new Open Library members have we welcomed?" msgstr "Wie viele neue OpenLibrary-Mitglieder wurden begrüßt?" +#: home/stats.html:23 +msgid "Area graph of new members" +msgstr "Flächendiagramm neuer Mitglieder" + #: home/stats.html:24 msgid "New Members" msgstr "Neue Mitglieder" @@ -2878,6 +3324,10 @@ msgstr "Neue Mitglieder" msgid "People are constantly updating the catalog" msgstr "Der Katalog wird permanent erweitert" +#: home/stats.html:29 +msgid "Area graph of recent catalog edits" +msgstr "Flächendiagramm aktueller Katalogbearbeitungen" + #: home/stats.html:30 msgid "Catalog Edits" msgstr "Katalog Bearbeitungen" @@ -2886,6 +3336,10 @@ msgstr "Katalog Bearbeitungen" msgid "Members can create Lists" msgstr "Mitglieder können Listen anlegen" +#: home/stats.html:36 +msgid "Area graph of lists created recently" +msgstr "Flächendiagramm kürzlich erstellter Listen" + #: home/stats.html:37 msgid "Lists Created" msgstr "Erstellte Listen" @@ -2894,6 +3348,10 @@ msgstr "Erstellte Listen" msgid "We're a library, so we lend books, too" msgstr "Wir sind eine Bibliothek, also verleihen wir auch Bücher" +#: home/stats.html:43 +msgid "Area graph of ebooks borrowed recently" +msgstr "Flächendiagramm kürzlich entliehener E-Books" + #: home/stats.html:44 msgid "eBooks Borrowed" msgstr "Ausgeliehene eBooks" @@ -2923,19 +3381,29 @@ msgstr "Probieren Sie den virtuellen Bibliotheksexplorer" msgid "Digital shelves organized like a physical library" msgstr "Digitale Regale organisiert wie eine echte Bibliothek" +# | msgid "Subject Search" #: home/welcome.html:68 +msgid "Try Fulltext Search" +msgstr "Volltextsuche ausprobieren" + +#: home/welcome.html:69 +msgid "Find matching results within the text of millions of books" +msgstr "Finden Sie passende Ergebnisse im Text von Millionen Büchern" + +#: home/welcome.html:82 msgid "Be an Open Librarian" msgstr "Seien Sie ein freier Bibliothekar" -#: home/welcome.html:69 +#: home/welcome.html:83 msgid "Dozens of ways you can help improve the library" -msgstr "Dutzende Möglichkeiten wie Sie helfen können die Bibliothek zu verbessern" +msgstr "" +"Dutzende Möglichkeiten wie Sie helfen können die Bibliothek zu verbessern" -#: home/welcome.html:82 +#: home/welcome.html:96 msgid "Send us feedback" msgstr "Senden Sie uns Feedback" -#: home/welcome.html:83 +#: home/welcome.html:97 msgid "Your feedback will help us improve these cards" msgstr "Ihr Feedback hilft uns, diese Karten zu verbessern" @@ -2946,6 +3414,46 @@ msgid_plural "%s books" msgstr[0] "%s Buch" msgstr[1] "%s Bücher" +#: languages/language_list.html:8 +msgid "Czech" +msgstr "Tschechisch" + +#: languages/language_list.html:9 +msgid "German" +msgstr "Deutsch" + +#: languages/language_list.html:10 +msgid "English" +msgstr "Englisch" + +#: languages/language_list.html:11 +msgid "Spanish" +msgstr "Spanisch" + +#: languages/language_list.html:12 +msgid "French" +msgstr "Französisch" + +#: languages/language_list.html:13 +msgid "Croatian" +msgstr "Kroatisch" + +#: languages/language_list.html:14 +msgid "Portuguese" +msgstr "Portugiesisch" + +#: languages/language_list.html:15 +msgid "Telugu" +msgstr "Telugu" + +#: languages/language_list.html:16 +msgid "Ukrainian" +msgstr "Ukrainisch" + +#: languages/language_list.html:17 +msgid "Chinese" +msgstr "Chinesisch" + #: languages/notfound.html:7 #, python-format msgid "%s is not found" @@ -2964,11 +3472,11 @@ msgstr "Letzte Bearbeitung dieses docs von" msgid "This doc was last edited anonymously" msgstr "Dieses doc wurde zuletzt anonym bearbeitet" -#: lib/header_dropdown.html:26 +#: lib/header_dropdown.html:38 msgid "Menu" msgstr "Menü" -#: lib/header_dropdown.html:58 +#: lib/header_dropdown.html:74 msgid "New!" msgstr "Neu!" @@ -2982,7 +3490,7 @@ msgstr "Verlauf" msgid "Created %(date)s" msgstr "%(date)s erstellt" -#: lib/history.html:32 +#: lib/history.html:28 #, python-format msgid "%(count)d revision" msgid_plural "%(count)d revisions" @@ -3005,16 +3513,16 @@ msgstr "Wikipediazitat" msgid "Copy and paste this code into your Wikipedia page." msgstr "Fügen Sie diesen Code in Ihren Wikipedia-Artikel." -#: lib/history.html:83 lib/history.html:85 +#: lib/history.html:81 lib/history.html:83 msgid "an anonymous user" msgstr "ein anonymer Benutzer" -#: lib/history.html:87 +#: lib/history.html:85 #, python-format msgid "Created by %(user)s" msgstr "Erstellt von %(user)s" -#: lib/history.html:89 +#: lib/history.html:87 #, python-format msgid "Edited by %(user)s" msgstr "Bearbeitet von %(user)s" @@ -3023,27 +3531,29 @@ msgstr "Bearbeitet von %(user)s" msgid "" "We use a system called Markdown to format the text in your edits on Open " "Library. Some HTML formatting is also accepted. Paragraphs are automatically " -"generated from any hard-break returns (blank lines) within your text. You can " -"preview your work in real time below the editing field." +"generated from any hard-break returns (blank lines) within your text. You " +"can preview your work in real time below the editing field." msgstr "" "Wir benutzen Markdown um den Text Ihrer Einträge in der Open Library zu " -"formatieren. Manche HTML-Formatierungen sind ebenfalls erlaubt. Absätze werden " -"automatisch aus den doppelten Zeilenumbrüchen (Leerzeilen) im Text erzeugt. Yie " -"können eine Vorschau Ihrer Eingaben in Echtzeit unter dem Textfeld ansehen." +"formatieren. Manche HTML-Formatierungen sind ebenfalls erlaubt. Absätze " +"werden automatisch aus den doppelten Zeilenumbrüchen (Leerzeilen) im Text " +"erzeugt. Yie können eine Vorschau Ihrer Eingaben in Echtzeit unter dem " +"Textfeld ansehen." #: lib/markdown.html:17 msgid "Formatting marks should envelope the effected text, for example:" msgstr "" -"Formatierungszeichen sollten den betreffenden Text einschließen, zum Beispiel:" +"Formatierungszeichen sollten den betreffenden Text einschließen, zum " +"Beispiel:" #: lib/markdown.html:76 msgid "" -"To \"escape\" any Markdown tags (i.e. use an asterisk as an asterisk rather than " -"emphasizing text) place a backslash \\ prior to the character." +"To \"escape\" any Markdown tags (i.e. use an asterisk as an asterisk rather " +"than emphasizing text) place a backslash \\ prior to the character." msgstr "" "Um Markdown-Tags auszuschließen (escaping, zum Beispiel um ein Sternchen als " -"Sternchen im Text anzuzeigen statt den Text damit hervorzuheben), stellen Sie " -"ein Backslash \\ vor dem Zeichen an." +"Sternchen im Text anzuzeigen statt den Text damit hervorzuheben), stellen " +"Sie ein Backslash \\ vor dem Zeichen an." #: lib/nav_foot.html:13 msgid "Vision" @@ -3093,7 +3603,7 @@ msgstr "Startseite" msgid "Explore Books" msgstr "Bücher entdecken" -#: lib/nav_foot.html:26 +#: SearchNavigation.html:12 lib/nav_foot.html:26 msgid "Books" msgstr "Bücher" @@ -3113,8 +3623,14 @@ msgstr "Sammlungen entdecken" msgid "Collections" msgstr "Sammlungen" -#: lib/nav_foot.html:31 -msgid "Navigate to top of this page" +#: SearchNavigation.html:32 lib/nav_foot.html:30 lib/nav_head.html:35 +#: search/advancedsearch.html:7 search/advancedsearch.html:14 +#: search/advancedsearch.html:18 +msgid "Advanced Search" +msgstr "Erweiterte Suche" + +#: lib/nav_foot.html:31 +msgid "Navigate to top of this page" msgstr "Zum Anfang dieser Seite springen" #: lib/nav_foot.html:31 @@ -3129,7 +3645,7 @@ msgstr "Entwickeln" msgid "Explore Open Library Developer Center" msgstr "Entdecken Sie das OpenLibrary-Entwicklungszentrum" -#: lib/nav_foot.html:37 lib/nav_head.html:25 +#: lib/nav_foot.html:37 lib/nav_head.html:43 msgid "Developer Center" msgstr "Entwicklungszentrum" @@ -3143,7 +3659,7 @@ msgstr "API-Dokumentation" #: lib/nav_foot.html:39 msgid "Bulk Open Library data" -msgstr "OpenLibrary-Massendaten " +msgstr "OpenLibrary-Massendaten" #: lib/nav_foot.html:39 msgid "Bulk Data Dumps" @@ -3185,85 +3701,56 @@ msgstr "Änderungen vorschlagen" msgid "Change Website Language" msgstr "Sprache der Seite ändern" -#: lib/nav_foot.html:59 -msgid "Czech" -msgstr "Tschechisch" - -#: lib/nav_foot.html:60 lib/search_foot.html:33 -msgid "German" -msgstr "Deutsch" - -#: lib/nav_foot.html:61 lib/search_foot.html:32 -msgid "English" -msgstr "Englisch" - -#: lib/nav_foot.html:62 lib/search_foot.html:35 -msgid "Spanish" -msgstr "Spanisch" - -#: lib/nav_foot.html:63 lib/search_foot.html:34 -msgid "French" -msgstr "Französisch" - -#: lib/nav_foot.html:64 -msgid "Croatian" -msgstr "Kroatisch" +#: lib/nav_foot.html:63 lib/nav_head.html:63 type/list/embed.html:23 +msgid "Open Library logo" +msgstr "OpenLibrary Logo" #: lib/nav_foot.html:65 -msgid "Telugu" -msgstr "Telugu" - -#: lib/nav_foot.html:73 msgid "" "Open Library is an initiative of the Internet " "Archive, a 501(c)(3) non-profit, building a digital library of Internet " -"sites and other cultural artifacts in digital form. Other projects include the Wayback " -"Machine, archive.org and archive-it.org" +"sites and other cultural artifacts in digital form. Other projects include the Wayback Machine, archive.org and archive-it.org" msgstr "" -"Open Library ist ein Projekt des Internet Archives, einer gemeinnützigen Organisation aus den USA nach 501(c)(3), die eine " -"digitale Bibliothek der Internetseiten und anderer kultureller Gegenstände " -"zusammenstellt. Andere Projekte sind " -"die Wayback Machine, archive.org und archive-it.org" +"Open Library ist ein Projekt des Internet " +"Archives, einer gemeinnützigen Organisation aus den USA nach 501(c)(3), " +"die eine digitale Bibliothek der Internetseiten und anderer kultureller " +"Gegenstände zusammenstellt. Andere Projekte sind die Wayback Machine, " +"archive.org und archive-it.org" -#: lib/nav_foot.html:78 +#: lib/nav_foot.html:70 #, python-format msgid "" -"version %s" +"version %s" msgstr "" -"Version %s" - -#: lib/nav_head.html:10 -msgid "My Profile" -msgstr "Mein Profil" - -#: lib/nav_head.html:11 -msgid "My Loans" -msgstr "Entliehene Bücher" +"Version %s" +# | msgid "Reading Log" #: lib/nav_head.html:13 -msgid "My Reading Log" -msgstr "Mein Lese-Logbuch" +msgid "Loans, Reading Log, Lists, Stats" +msgstr "Ausleihen, Reading-Log, Listen, Statistiken" #: lib/nav_head.html:14 -msgid "My Reading Stats" -msgstr "Meine Lese-Statistik" +msgid "My Profile" +msgstr "Mein Profil" #: lib/nav_head.html:16 msgid "Log out" msgstr "Abmelden" -#: lib/nav_head.html:24 -msgid "Recent Community Edits" -msgstr "Letzte Änderungen der Gemeinschaft" +#: lib/nav_head.html:19 +msgid "Pending Merge Requests" +msgstr "Offene Zusammenführungsanfragen" -#: lib/nav_head.html:26 -msgid "Help & Support" -msgstr "Hilfe & Support" +#: lib/nav_head.html:29 +msgid "Trending" +msgstr "Beliebt" #: lib/nav_head.html:33 msgid "K-12 Student Library" @@ -3274,117 +3761,73 @@ msgid "Random Book" msgstr "Zufälliges Buch" #: lib/nav_head.html:39 +msgid "Recent Community Edits" +msgstr "Letzte Änderungen der Gemeinschaft" + +#: lib/nav_head.html:42 +msgid "Help & Support" +msgstr "Hilfe & Support" + +#: lib/nav_head.html:44 +msgid "Librarians Portal" +msgstr "Bibliothekar*innen-Portal" + +#: lib/nav_head.html:48 msgid "My Open Library" msgstr "Meine Open Library" -#: lib/nav_head.html:40 lib/nav_head.html:59 +#: lib/nav_head.html:49 lib/nav_head.html:70 msgid "Browse" msgstr "Stöbern" -#: lib/nav_head.html:48 +#: lib/nav_head.html:50 +msgid "Contribute" +msgstr "Mitarbeiten" + +#: lib/nav_head.html:51 +msgid "Resources" +msgstr "Resourcen" + +#: lib/nav_head.html:59 msgid "The Internet Archive's Open Library: One page for every book" msgstr "Die OpenLibrary des Internet Archives: Eine Seite für Jedes Buch" -#: lib/nav_head.html:52 type/list/embed.html:22 -msgid "Open Library logo" -msgstr "OpenLibrary Logo" - -#: lib/nav_head.html:80 +#: lib/nav_head.html:91 msgid "All" msgstr "Alle" -#: lib/nav_head.html:83 +#: lib/nav_head.html:94 msgid "Text" msgstr "Text" -#: lib/nav_head.html:84 lib/search_foot.html:11 lib/search_head.html:11 -#: search/advancedsearch.html:28 +#: lib/nav_head.html:95 search/advancedsearch.html:32 msgid "Subject" msgstr "Thema" -#: lib/nav_head.html:86 lib/search_foot.html:63 lib/search_head.html:29 +#: lib/nav_head.html:97 msgid "Advanced" msgstr "Erweitert" -#: NotesModal.html:29 lib/nav_head.html:124 -msgid "My Book Notes" -msgstr "Meine Bücher-Notizen" - #: lib/not_logged.html:7 msgid "" -"You are not logged in. Open Library will record your IP address and include it in this " -"page's publicly accessible edit history. If you create an account, your IP address " -"is concealed and you can more easily keep track of all your edits and additions." +"You are not logged " +"in. Open Library will record your IP address and include it in " +"this page's publicly accessible edit history. If you create an account, " +"your IP address is concealed and you can more easily keep track of all your " +"edits and additions." msgstr "" -"Sie sind nicht eingeloggt." -" Die OpenLibrary zeichnet Ihre IP-Adresse auf und zeigt diese in der " -"öffentlichen Änderungshistorie an. Wenn Sie einen Account anlegen möchten, wird Ihre IP-" -"Adresse nicht mehr angezeigt und Sie können Ihrer Bearbeitungen besser im Blick " -"behalten." +"Sie sind nicht eingeloggt. Die OpenLibrary zeichnet Ihre IP-" +"Adresse auf und zeigt diese in der öffentlichen Änderungshistorie an. Wenn " +"Sie einen Account anlegen möchten, wird Ihre IP-Adresse nicht mehr " +"angezeigt und Sie können Ihrer Bearbeitungen besser im Blick behalten." #: Pager.html:27 lib/pagination.html:22 msgid "Previous" msgstr "Vorherige" -#: lib/search_foot.html:12 lib/search_head.html:12 search/advancedsearch.html:32 -msgid "Place" -msgstr "Ort" - -#: lib/search_foot.html:13 lib/search_head.html:13 search/advancedsearch.html:36 -msgid "Person" -msgstr "Person" - -#: lib/search_foot.html:31 -msgid "any" -msgstr "alle" - -#: lib/search_foot.html:36 -msgid "Russian" -msgstr "Russisch" - -#: lib/search_foot.html:37 -msgid "Italian" -msgstr "Italienisch" - -#: lib/search_foot.html:38 -msgid "Chinese" -msgstr "Chinesisch" - -#: lib/search_foot.html:39 -msgid "Arabic" -msgstr "Arabisch" - -#: lib/search_foot.html:40 -msgid "Japanese" -msgstr "Japanisch" - -#: lib/search_foot.html:41 -msgid "Portuguese" -msgstr "Portugiesisch" - -#: lib/search_foot.html:42 -msgid "Polish" -msgstr "Polnisch" - -#: lib/search_foot.html:68 lib/search_head.html:34 -msgid "Only eBooks" -msgstr "Nur E-Books" - -#: lib/search_foot.html:70 lib/search_head.html:36 -msgid "Search eBook text" -msgstr "E-Book-Text durchsuchen" - -#: lib/search_head.html:68 -msgid "Your Profile" -msgstr "Ihr Profil" - -#: lib/search_head.html:76 -msgid "Log in" -msgstr "Anmelden" - #: lists/header.html:11 lists/header.html:24 lists/header.html:38 #: lists/header.html:47 lists/header.html:56 #, python-format @@ -3433,18 +3876,20 @@ msgstr "Hm. Keine Ergebnisse." #: lists/home.html:16 msgid "Create a list of any Subjects, Authors, Works or specific Editions." msgstr "" -"Erstellen Sie eine Liste nach Themen, Autoren, Werken oder spezifischen Ausgaben." +"Erstellen Sie eine Liste nach Themen, Autoren, Werken oder spezifischen " +"Ausgaben." #: lists/home.html:17 msgid "" "Once you've made a list, you can watch for updates or " -"export all the editions in a list as HTML, BibTeX or JSON. See " -"all your lists and any activity using the \"Lists\" link on your Account page." +"export all the editions in a list as HTML, BibTeX or JSON. " +"See all your lists and any activity using the \"Lists\" link on your Account " +"page." msgstr "" "Wenn Sie eine Liste erstellt haben, können Sie diese nach Änderungen " "beobachten oder die Ausgaben als HTML, BibTeX or JSON " -"exportieren. Zeigen Sie alle Ihre Listen und Aktivitäten über " -"den „Listen“-Link in Ihrem Profil an." +"exportieren. Zeigen Sie alle Ihre Listen und Aktivitäten " +"über den „Listen“-Link in Ihrem Profil an." #: lists/home.html:18 msgid "Enjoy!" @@ -3453,9 +3898,11 @@ msgstr "Hab Spaß!" #: lists/home.html:20 #, python-format msgid "" -"For the top 2000+ most requested eBooks in California for the print disabled click here." +"For the top 2000+ most requested eBooks in California for the print disabled " +"click here." msgstr "" +"Klicken Sie hier für die 2000+ meist angefragten E-" +"Books in Kalifornien für lesebehinderte Menschen." #: lists/home.html:24 msgid "Find a list by name" @@ -3470,8 +3917,8 @@ msgstr "Aktive Listen" msgid "from %(owner)s" msgstr "von %(owner)s" -#: lists/home.html:51 lists/preview.html:23 lists/snippet.html:18 -#: type/list/embed.html:17 type/list/view_body.html:38 +#: lists/home.html:47 lists/preview.html:19 lists/snippet.html:14 +#: type/list/embed.html:12 type/list/view_body.html:35 #, python-format msgid "%(count)d item" msgid_plural "%(count)d items" @@ -3487,7 +3934,7 @@ msgstr "Zuletzt geändert am %(date)s" msgid "No description." msgstr "Keine Beschreibung." -#: lists/home.html:65 lists/lists.html:124 type/user/view.html:81 +#: lists/home.html:65 lists/lists.html:130 type/user/view.html:81 msgid "Recent Activity" msgstr "Letzte Änderungen" @@ -3495,6 +3942,18 @@ msgstr "Letzte Änderungen" msgid "See all" msgstr "Alle anzeigen" +#: lists/list_overview.html:15 lists/widget.html:84 +msgid "See this list" +msgstr "Liste anzeigen" + +#: lists/list_overview.html:25 lists/widget.html:85 +msgid "Remove from your list?" +msgstr "Von Ihrer Liste entfernen?" + +#: lists/list_overview.html:28 lists/widget.html:87 +msgid "You" +msgstr "Sie" + #: lists/lists.html:10 #, python-format msgid "%(owner)s / Lists" @@ -3504,11 +3963,13 @@ msgstr "%(owner)s / Listen" msgid "Delete this list" msgstr "Dieses Buch löschen" -#: lists/lists.html:38 type/list/view_body.html:110 type/list/view_body.html:140 +#: lists/lists.html:38 type/list/view_body.html:111 +#: type/list/view_body.html:141 msgid "Yes, I'm sure" msgstr "Ja, Ich bin sicher" -#: lists/lists.html:49 type/list/view_body.html:127 type/list/view_body.html:149 +#: lists/lists.html:49 type/list/view_body.html:128 +#: type/list/view_body.html:150 msgid "No, cancel" msgstr "Nein, abbrechen" @@ -3526,91 +3987,48 @@ msgstr "Diese Quelle entfernen?" #: lists/lists.html:109 #, python-format -msgid "Are you sure you want to remove %(title)s from this list?" +msgid "" +"Are you sure you want to remove %(title)s from this list?" msgstr "" "Sind sie sicher, dass sie %(title)s von der Liste entfernen " "möchten?" #: lists/lists.html:121 -msgid "No lists yet!" -msgstr "Bisher keine Listen!" +msgid "This reader hasn't created any lists yet." +msgstr "Der/die Leser*in hat bisher keine Listen erstellt." + +#: lists/lists.html:123 +msgid "You haven't created any lists yet." +msgstr "Sie haben noch keine Listen erstellt." + +#: lists/lists.html:126 +msgid "Learn how to create your first list." +msgstr "Lernen Sie, Ihre erste Liste zu erstellen." #: lists/preview.html:17 msgid "by You" msgstr "von Ihnen" -#: lists/widget.html:48 -#, python-format -msgid "Are you sure you want to remove %(title)s from your list?" -msgstr "" -"Sind Sie sicher, dass Sie %(title)s von Ihrer Liste entfernen " -"wollen?" - -#: lists/widget.html:93 -msgid "See this list" -msgstr "Liste anzeigen" - -#: lists/widget.html:95 -msgid "Remove from your list?" -msgstr "Von Ihrer Liste entfernen?" - -#: lists/widget.html:98 -msgid "You" -msgstr "Sie" - -#: lists/widget.html:196 -msgid "My Reading Lists:" -msgstr "Meine Leselisten:" - -#: lists/widget.html:206 -msgid "Use this Work" -msgstr "Dieses Werk nutzen" - -#: databarAuthor.html:27 databarAuthor.html:34 lists/widget.html:209 -#: lists/widget.html:226 lists/widget.html:316 -msgid "Create a new list" -msgstr "Neue Liste erstellen" - -#: lists/widget.html:219 -msgid "Add to List" -msgstr "Zu Liste hinzufügen" - -#: lists/widget.html:251 +#: lists/widget.html:57 msgid "watch for edits or export all records" msgstr "Auf Änderungen überwachen oder alle Einträge exportieren" -#: lists/widget.html:258 +#: lists/widget.html:64 #, python-format msgid "%(count)d List" msgid_plural "%(count)d Lists" msgstr[0] "%(count)d Liste" msgstr[1] "%(count)d Listen" -#: lists/widget.html:298 -msgid "Update my book note" -msgstr "Meine Buch-Notiz aktualisieren" - -#: lists/widget.html:300 -msgid "Add a book note" -msgstr "Buch-Notiz hinzufügen" +#: databarAuthor.html:86 lists/widget.html:86 +msgid "from" +msgstr "Von" -#: ReadingLogButton.html:15 lists/widget.html:310 +#: lists/widget.html:113 msgid "Remove" msgstr "Entfernen" -#: lists/widget.html:330 -msgid "Description:" -msgstr "Beschreibung:" - -#: lists/widget.html:338 -msgid "Create new list" -msgstr "Neue Liste erstellen" - -#: lists/widget.html:674 -msgid "saving..." -msgstr "Speichern..." - -#: merge/authors.html:7 merge/authors.html:11 merge/authors.html:95 +#: merge/authors.html:7 merge/authors.html:11 merge/authors.html:102 msgid "Merge Authors" msgstr "Autoren zusammenführen" @@ -3628,8 +4046,7 @@ msgstr "Bitte Autoren auswählen, die zusammengeführt werden sollen." #: merge/authors.html:27 msgid "Select a \"primary\" record that best represents the author -" -msgstr "" -"Wählen Sie den ältesten Eintrag als \"primären\" Eintrag aus." +msgstr "Wählen Sie den ältesten Eintrag als \"primären\" Eintrag aus -" #: merge/authors.html:30 msgid "Select author records which should be merged with the primary" @@ -3651,30 +4068,148 @@ msgstr "" "Sie müssen einen primären Eintrag auswählen, auf den die Duplikate verweisen " "sollen." -#: merge/authors.html:42 +#: merge/authors.html:43 msgid "Please Be Careful..." msgstr "Bitte Seien Sie vorsichtig…" -#: merge/authors.html:43 +#: merge/authors.html:44 msgid "Are you sure you want to merge these records?" -msgstr "Sind sie sicher, dass sie diese Einträge zusammenführen möchten?" +msgstr "" +"Sind sie sicher, dass sie diese Einträge zusammenführen möchten?" -#: merge/authors.html:51 +#: merge/authors.html:55 msgid "Primary" msgstr "Primär" -#: merge/authors.html:52 +#: merge/authors.html:56 merge_queue/merge_queue.html:125 msgid "Merge" msgstr "Zusammenführen" -#: merge/authors.html:82 +#: merge/authors.html:86 msgid "Open in a new window" msgstr "In neuem Fenster öffnen" -#: merge/authors.html:89 +#: merge/authors.html:93 msgid "Visit this author's page in a new window" msgstr "Die Autoren-Seite in einem neuen Fenster öffnen" +#: merge/authors.html:99 +msgid "Comment:" +msgstr "Kommentieren:" + +#: merge/authors.html:99 +msgid "Comment..." +msgstr "Kommentieren..." + +# | msgid "Author Search" +#: merge/authors.html:104 +msgid "Request Merge" +msgstr "Zusammenführen anfragen" + +#: merge/authors.html:107 +msgid "Reject Merge" +msgstr "Zusammenführen ablehnen" + +#: merge/works.html:9 +msgid "Merge Works" +msgstr "Werke zusammenführen" + +#: merge_queue/comment.html:21 +msgid "Just now" +msgstr "Gerade eben" + +#: merge_queue/merge_queue.html:18 +#, python-format +msgid "Showing %(username)s's requests only." +msgstr "Nur %(username)ss Anfragen gezeigt." + +#: merge_queue/merge_queue.html:19 +msgid "Show all requests" +msgstr "Alle Anfragen anzeigen" + +#: merge_queue/merge_queue.html:22 +msgid "Showing all requests." +msgstr "Alle Anfragen werden gezeigt." + +#: merge_queue/merge_queue.html:23 +msgid "Show my requests" +msgstr "Meine Anfragen anzeigen" + +#: merge_queue/merge_queue.html:27 +msgid "Community Edit Requests" +msgstr "Änderungsanfragen aus der Gemeinschaft" + +#: merge_queue/merge_queue.html:33 +#, python-format +msgid "Showing requests reviewed by %(reviewer)s only." +msgstr "Nur von %(reviewer)s geprüfte Anfragen gezeigt." + +#: merge_queue/merge_queue.html:33 +msgid "Remove reviewer filter" +msgstr "Rezent*innenfilter entfernen" + +#: merge_queue/merge_queue.html:35 +msgid "Show requests that I've reviewed" +msgstr "Von mir geprüfte Anfragen anzeigen" + +#: merge_queue/merge_queue.html:47 +msgid "Open" +msgstr "Offen" + +#: merge_queue/merge_queue.html:50 +msgid "Closed" +msgstr "Abgeschlossen" + +#: merge_queue/merge_queue.html:58 +msgid "Submitter" +msgstr "Einreicher*in" + +#: RecentChangesAdmin.html:22 merge_queue/merge_queue.html:60 +#: merge_queue/merge_queue.html:85 +msgid "Comments" +msgstr "Kommentare" + +#: merge_queue/merge_queue.html:61 +msgid "Reviewer" +msgstr "Prüfer*in" + +#: merge_queue/merge_queue.html:62 +msgid "Resolve" +msgstr "Lösen" + +#: merge_queue/merge_queue.html:68 +msgid "No entries here!" +msgstr "Hier keine Einträge!" + +#: merge_queue/merge_queue.html:77 +msgid "Work" +msgstr "Werk" + +#: merge_queue/merge_queue.html:84 +#, python-format +msgid "" +"%(type)s merge request for %(title)s" +msgstr "" +"%(type)s Zusammenführungsanfragen für %(title)s" + +#: merge_queue/merge_queue.html:90 +msgid "Showing most recent comment only." +msgstr "Nur kürzlich hinzugefügte Kommentare gezeigt." + +#: merge_queue/merge_queue.html:91 +msgid "View all" +msgstr "Alle anzeigen" + +#: merge_queue/merge_queue.html:101 +msgid "No comments yet." +msgstr "Bisher keine Kommentare." + +#: merge_queue/merge_queue.html:106 +msgid "Add a comment..." +msgstr "Kommentar hinzufügen..." + #: observations/review_component.html:16 msgid "Community Reviews" msgstr "Änderungen der Gemeinschaft" @@ -3687,7 +4222,7 @@ msgstr "Die Gemeinschaft hat dieses Werk bisher nicht rezensiert." msgid "+ Add your community review" msgstr "+ Rezension hinzufügen" -#: observations/review_component.html:44 +#: observations/review_component.html:46 msgid "+ Log in to add your community review" msgstr "+ Einloggen und Rezension hinzufügen" @@ -3708,7 +4243,7 @@ msgstr "Etwas anderes probieren?" # | msgid "%s ebook" # | msgid_plural "%s ebooks" -#: publishers/view.html:19 +#: publishers/view.html:15 #, python-format msgid "%(count)s ebook" msgid_plural "%(count)s ebooks" @@ -3718,21 +4253,22 @@ msgstr[1] "%(count)s eBooks" #: publishers/view.html:33 msgid "" "This is a chart to show the when this publisher published books. Along the X " -"axis is time, and on the y axis is the count of editions published. Click here to skip the chart." +"axis is time, and on the y axis is the count of editions published. Click here to skip the chart." msgstr "" -"Dieses Diagramm zeigt, wann dieser Verlag Bücher veröffentlicht hat. Die X-Achse " -"zeigt die Zeit an, und die Y-Achse die Anzahl der veröffetlichten Ausgaben. Klicken Sie hier, um das Diagramm zu überspringen." +"Dieses Diagramm zeigt, wann dieser Verlag Bücher veröffentlicht hat. Die X-" +"Achse zeigt die Zeit an, und die Y-Achse die Anzahl der veröffetlichten " +"Ausgaben. Klicken Sie hier, um das Diagramm zu " +"überspringen." #: publishers/view.html:35 msgid "" -"This graph charts editions from this publisher over time. Click to view a single " -"year, or drag across a range." +"This graph charts editions from this publisher over time. Click to view a " +"single year, or drag across a range." msgstr "" -"Dieser Graph zeigt Ausgaben, die zu diesem Thema veröffentlicht wurden. Klicken " -"um ein einzelne Jahr anzuzeigen oder über einen Bereich ziehen um diesen " -"auszuwählen." +"Dieser Graph zeigt Ausgaben, die zu diesem Thema veröffentlicht wurden. " +"Klicken um ein einzelne Jahr anzuzeigen oder über einen Bereich ziehen um " +"diesen auszuwählen." #: publishers/view.html:52 msgid "Common Subjects" @@ -3774,8 +4310,8 @@ msgstr "Von Personen" msgid "By Bots" msgstr "Von Bots" -#: recentchanges/default/message.html:29 recentchanges/edit-book/message.html:29 -#: site/around_the_library.html:45 +#: recentchanges/default/message.html:29 +#: recentchanges/edit-book/message.html:29 site/around_the_library.html:45 msgid "opened a new Open Library account!" msgstr "hat ein neues Open-Library-Konto eröffnet!" @@ -3811,11 +4347,22 @@ msgid "Master" msgstr "Primär" # | msgid "Deprecated" -#: recentchanges/merge-authors/view.html:36 type/author/view.html:67 +#: recentchanges/merge-authors/view.html:40 type/author/view.html:78 msgid "Duplicates" msgstr "Duplikate" -#: recentchanges/merge-authors/view.html:45 +#: recentchanges/merge/comment.html:20 +#, python-format +msgid "Merged %(count)d duplicate %(type)s record into this primary." +msgid_plural "Merged %(count)d duplicate %(type)s records into this primary." +msgstr[0] "" +"%(count)d doppelter %(type)s-Eintrag mit diesem primären Eintrag " +"zusammengeführt." +msgstr[1] "" +"%(count)d doppelte %(type)s-Einträge mit diesem primären Eintrag " +"zusammengeführt." + +#: recentchanges/merge/view.html:51 #, python-format msgid "%(count)d record modified." msgid_plural "%(count)d records modified." @@ -3823,33 +4370,41 @@ msgstr[0] "%(count)d Eintrag geändert." msgstr[1] "%(count)d Einträge geändert." # | msgid "Delete Record" -#: recentchanges/merge-authors/view.html:49 +#: recentchanges/merge-authors/view.html:53 msgid "Updated Records" msgstr "Geänderte Einträge" -#: search/advancedsearch.html:24 +#: search/advancedsearch.html:28 msgid "ISBN" msgstr "ISBN" +#: search/advancedsearch.html:36 +msgid "Place" +msgstr "Ort" + +#: search/advancedsearch.html:40 +msgid "Person" +msgstr "Person" + # | msgid "Subject Search" -#: search/advancedsearch.html:46 +#: search/advancedsearch.html:50 msgid "Full Text Search" msgstr "Volltextsuche" -#: search/authors.html:28 +#: search/authors.html:19 +msgid "Search Authors" +msgstr "Autor*innen durchsuchen" + +#: search/authors.html:48 msgid "Is the same author listed twice?" -msgstr "Wird der/die selbe Autor*in mehrfach aufgeführt" +msgstr "Wird der/die selbe Autor*in mehrfach aufgeführt?" # | msgid "Explore authors" -#: search/authors.html:29 +#: search/authors.html:48 msgid "Merge authors" msgstr "Autoren zusammenführen" -#: search/authors.html:32 -msgid "Author Search" -msgstr "Autorensuche" - -#: search/authors.html:39 +#: search/authors.html:51 msgid "No hits" msgstr "Keine Ergebnisse" @@ -3859,7 +4414,7 @@ msgstr "Keine Ergebnisse" msgid "Search Open Library for %s" msgstr "OpenLibrary nach %s durchsuchen" -#: BookSearchInside.html:9 search/inside.html:10 +#: BookSearchInside.html:9 SearchNavigation.html:20 search/inside.html:10 msgid "Search Inside" msgstr "Volltextsuche" @@ -3887,70 +4442,78 @@ msgstr[1] "in %(seconds)s gefunden" msgid "Search results for Lists" msgstr "Suchergebnisse" +#: search/lists.html:10 +msgid "Search Lists" +msgstr "Listen durchsuchen" + #: search/publishers.html:9 msgid "Publishers Search" msgstr "Verlagssuche" -#: search/sort_options.html:10 +#: search/sort_options.html:9 +msgid "Sorted by" +msgstr "Sortiert nach" + +#: search/sort_options.html:12 msgid "Relevance" msgstr "Relevanz" -#: search/sort_options.html:11 +#: search/sort_options.html:13 msgid "Most Editions" msgstr "Anzahl der Auflagen" -#: search/sort_options.html:12 +#: search/sort_options.html:14 msgid "First Published" msgstr "Ersterscheinungsdatum" -#: search/sort_options.html:13 +#: search/sort_options.html:15 msgid "Most Recent" msgstr "Datum" # | msgid "Random Book" -#: search/sort_options.html:14 +#: search/sort_options.html:16 msgid "Random" msgstr "Zufällig" -#: search/sort_options.html:31 +#: search/sort_options.html:33 msgid "Shuffle" msgstr "Zufallsmodus" #: search/subjects.html:19 -msgid "Subject Search" -msgstr "Themensuche" +msgid "Search Subjects" +msgstr "Themen durchsuchen" -#: search/subjects.html:74 +#: search/subjects.html:57 msgid "time" msgstr "Zeit" # | msgid "Subject" -#: search/subjects.html:76 +#: search/subjects.html:59 msgid "subject" msgstr "Thema" # | msgid "Place" -#: search/subjects.html:78 +#: search/subjects.html:61 msgid "place" msgstr "Ort" # | msgid "or" -#: search/subjects.html:80 +#: search/subjects.html:63 msgid "org" msgstr "Org" -#: search/subjects.html:82 +#: search/subjects.html:65 msgid "event" msgstr "Event" # | msgid "Person" -#: search/subjects.html:84 +#: search/subjects.html:67 msgid "person" msgstr "Person" # | msgid "%s work" # | msgid_plural "%s works" -#: search/subjects.html:86 +#: search/subjects.html:69 msgid "work" msgstr "Werk" @@ -3959,7 +4522,7 @@ msgstr "Werk" msgid "View all recent changes" msgstr "Letzte Änderungen anzeigen" -#: site/body.html:18 +#: site/body.html:24 msgid "It looks like you're offline." msgstr "Es scheint als seien Sie offline." @@ -3969,9 +4532,9 @@ msgstr "Es scheint als seien Sie offline." # | "page for every book ever published." #: site/neck.html:15 msgid "" -"Open Library is an open, editable library catalog, building towards a web page " -"for every book ever published. Read, borrow, and discover more than 3M books for " -"free." +"Open Library is an open, editable library catalog, building towards a web " +"page for every book ever published. Read, borrow, and discover more than 3M " +"books for free." msgstr "" "Open Library ist ein frei zugänglicher und von allen zu editierbarer Online-" "Katalog, welcher jedes Buch enthalten soll, das jemals veröffentlicht wurde. " @@ -3993,17 +4556,13 @@ msgstr "# Bücher protokolliert" #: stats/readinglog.html:12 msgid "The total number of books logged using the Reading Log feature" -msgstr "Die gesamte Anzahl protokollierter Bücher aus der Leseprotokoll-Funktion" +msgstr "" +"Die gesamte Anzahl protokollierter Bücher aus der Leseprotokoll-Funktion" #: stats/readinglog.html:16 stats/readinglog.html:33 stats/readinglog.html:50 msgid "All time" msgstr "Gesamtzeit" -# | msgid "This Edition" -#: stats/readinglog.html:20 stats/readinglog.html:37 stats/readinglog.html:54 -msgid "This Month" -msgstr "Dieser Monat" - #: stats/readinglog.html:28 msgid "# Unique Users Logging Books" msgstr "Zahl der eindeutigen Benutzer, die Bücher loggen" @@ -4012,8 +4571,9 @@ msgstr "Zahl der eindeutigen Benutzer, die Bücher loggen" msgid "" "The total number of unique users who have logged at least one book using the " "Reading Log feature" -msgstr "Die Gesamtzahl der Benutzer, die zumindest ein Buch mit der Leselog-Funktion" -"geloggt haben" +msgstr "" +"Die Gesamtzahl der Benutzer, die zumindest ein Buch mit der Leselog-" +"Funktiongeloggt haben" #: stats/readinglog.html:45 msgid "# Books Starred" @@ -4027,36 +4587,36 @@ msgstr "Die gesamte Anzahl an Büchern, welche bewertet wurden" msgid "Total # Unique Raters (all time)" msgstr "Gesamtzahl an eindeutigen Bewertern (seit Beginn)" -#: stats/readinglog.html:66 -msgid "Most Wanted Books (All Time)" -msgstr "Meistgewünschte Bücher (seit Beginn)" +#: stats/readinglog.html:67 +msgid "Most Wanted Books (This Month)" +msgstr "Meistgewünschte Bücher (dieser Monat)" -#: stats/readinglog.html:70 stats/readinglog.html:78 stats/readinglog.html:87 -#: stats/readinglog.html:96 +#: stats/readinglog.html:67 stats/readinglog.html:75 stats/readinglog.html:84 +#: stats/readinglog.html:93 #, python-format msgid "Added %(count)d time" msgid_plural "Added %(count)d times" msgstr[0] "Insgesamt %(count)d mal hinzugefügt" msgstr[1] "Insgesamt %(count)d mal hinzugefügt" -#: stats/readinglog.html:74 -msgid "Most Wanted Books (This Month)" -msgstr "Meistgewünschte Bücher (dieser Monat)" +#: stats/readinglog.html:75 +msgid "Most Wanted Books (All Time)" +msgstr "Meistgewünschte Bücher (seit Beginn)" -#: stats/readinglog.html:82 +#: stats/readinglog.html:83 msgid "Most Logged Books" msgstr "Meistgeloggte Bücher" -#: stats/readinglog.html:83 +#: stats/readinglog.html:84 msgid "Most Read Books (All Time)" msgstr "Meistgelesene Bücher (jemals)" # | msgid "About the Book" -#: stats/readinglog.html:91 +#: stats/readinglog.html:92 msgid "Most Rated Books" msgstr "Meistbewertete Bücher" -#: stats/readinglog.html:92 +#: stats/readinglog.html:93 msgid "Most Rated Books (All Time)" msgstr "Meistbewertete Bücher (seit Beginn)" @@ -4066,8 +4626,8 @@ msgid "We couldn't find any books about" msgstr "Wir konnten keine Bücher finden, über" #: type/about/edit.html:9 type/i18n/edit.html:12 type/page/edit.html:9 -#: type/permission/edit.html:9 type/rawtext/edit.html:9 type/template/edit.html:9 -#: type/usergroup/edit.html:9 +#: type/permission/edit.html:9 type/rawtext/edit.html:9 +#: type/template/edit.html:9 type/usergroup/edit.html:9 #, python-format msgid "Edit %(title)s" msgstr "Bearbeite %(title)s" @@ -4075,7 +4635,8 @@ msgstr "Bearbeite %(title)s" #: type/about/edit.html:20 msgid "" "This only appears in the document head, and gets attached to bookmark labels" -msgstr "Das erscheint nur im Dokumentenkopf und wird an Lesezeichen-Label angehängt" +msgstr "" +"Das erscheint nur im Dokumentenkopf und wird an Lesezeichen-Label angehängt" #: type/about/edit.html:29 msgid "Intro" @@ -4083,11 +4644,11 @@ msgstr "Einleitung" #: type/about/edit.html:30 msgid "This appears in first column." -msgstr "Das erscheint in der ersten Spalte" +msgstr "Das erscheint in der ersten Spalte." #: type/about/edit.html:40 msgid "This appears in the second column, at top." -msgstr "Das erscheint in der zweiten Spalte am Anfang" +msgstr "Das erscheint in der zweiten Spalte am Anfang." # | msgid "Reading Lists" #: type/about/edit.html:49 @@ -4096,13 +4657,13 @@ msgstr "Mailingliste" #: type/about/edit.html:50 msgid "This appears below the links list." -msgstr "Das erscheint unter der Link-Liste" +msgstr "Das erscheint unter der Link-Liste." -#: type/author/edit.html:13 +#: type/author/edit.html:19 msgid "Edit Author" msgstr "Autor bearbeiten" -#: type/author/edit.html:28 +#: type/author/edit.html:34 msgid "" "Please use natural order. For example: Leo Tolstoy not " "Tolstoy, Leo." @@ -4110,57 +4671,65 @@ msgstr "" "Bitte benutzen Sie die natürliche Reihenfolge. Zum Beispiel: Leo " "Tolstoy, nicht Tolstoy, Leo." -#: type/author/edit.html:43 +#: type/author/edit.html:49 msgid "About" msgstr "Über" -#: type/author/edit.html:53 +#: type/author/edit.html:59 msgid "A short bio?" msgstr "Eine Kurzbiographie?" -#: type/author/edit.html:54 +#: type/author/edit.html:60 msgid "" "If you \"borrow\" information from somewhere, please make sure to cite the " "source." msgstr "" -"Wenn Sie informationen von anderer Stelle \"leihen\", zitieren Sie bitte Ihre " -"Quelle." +"Wenn Sie informationen von anderer Stelle \"leihen\", zitieren Sie bitte " +"Ihre Quelle." -#: type/author/edit.html:65 +#: type/author/edit.html:71 msgid "Date of birth" msgstr "Geburtsdatum" -#: type/author/edit.html:74 +#: type/author/edit.html:80 msgid "Date of death" msgstr "Zeitpunkt des Todes" -#: type/author/edit.html:83 +#: type/author/edit.html:89 msgid "" "We're not storing structured dates (yet) so a date like \"21 May 2000\" is " "recommended." msgstr "" -"Wir speichen (noch) keine strukturierten Datums-Daten, weshalb Daten wie \"21 " -"May 2000\" empfohlen werden." +"Wir speichen (noch) keine strukturierten Datums-Daten, weshalb Daten wie " +"\"21 May 2000\" empfohlen werden." -#: type/author/edit.html:92 +#: type/author/edit.html:98 msgid "Date" msgstr "Datum" -#: type/author/edit.html:99 +#: type/author/edit.html:105 msgid "" -"This is a deprecated field. You can help improve this record by removing this " -"date and populating the \"Date of birth\" and \"Date of death\" fields. Thanks!" +"This is a deprecated field. You can help improve this record by removing " +"this date and populating the \"Date of birth\" and \"Date of death\" fields. " +"Thanks!" msgstr "" +"Dieses Feld ist veraltet. Sie können diesen Eintrag verbessern, indem Sie " +"das Datum entfernen und stattdessen \"Geburtsdatum\" und \"Todestag\" " +"ausfüllen. Danke!" -#: type/author/edit.html:106 +#: type/author/edit.html:112 msgid "Does this author go by any other names?" msgstr "Ist diese*r Autor*in noch unter anderem Namen bekannt?" -#: type/author/edit.html:123 +#: type/author/edit.html:119 +msgid "Author Identifiers Purpose" +msgstr "Autorenidentifikatoren-Zweck" + +#: type/author/edit.html:129 msgid "Websites" msgstr "Internetseiten" -#: type/author/edit.html:124 +#: type/author/edit.html:130 msgid "Deprecated" msgstr "Veraltet" @@ -4169,7 +4738,7 @@ msgid "name missing" msgstr "Name fehlt" # | msgid "Welcome to Open Library" -#: type/author/view.html:19 type/edition/view.html:60 type/work/view.html:60 +#: type/author/view.html:19 type/edition/view.html:89 type/work/view.html:89 #, python-format msgid "%(page_title)s | Open Library" msgstr "%(page_title)s | Open Library" @@ -4179,90 +4748,93 @@ msgstr "%(page_title)s | Open Library" msgid "Author of %(book_titles)s" msgstr "Autor von %(book_titles)s" -#: type/author/view.html:65 +#: type/author/view.html:76 msgid "Merging Authors..." msgstr "Autoren zusammenführen…" -#: type/author/view.html:66 +#: type/author/view.html:77 msgid "In progress..." msgstr "In Bearbeitung..." -#: type/author/view.html:78 +#: type/author/view.html:89 msgid "Refresh the page?" msgstr "Seite aktualisieren?" -#: type/author/view.html:79 +#: type/author/view.html:90 msgid "Success!" msgstr "Erfolg!" -#: type/author/view.html:80 +#: type/author/view.html:91 msgid "" -"OK. The merge is in motion. It will take a few minutes to finish the " -"update." +"OK. The merge is in motion. It will take a few minutes to finish " +"the update." msgstr "" "Ok. Die Zusammenführung ist gestartet. Es wird ein paar Minuten " "benötigen, das Update fertigzustellen." -#: type/author/view.html:84 +#: type/author/view.html:95 msgid "Argh!" msgstr "Argh!" -#: type/author/view.html:85 +#: type/author/view.html:96 msgid "That merge didn't work. It's our fault, and we've made a note of it." -msgstr "Die Zusammenführung hat nicht geklappt. Wir haben den Fehler registriert." +msgstr "" +"Die Zusammenführung hat nicht geklappt. Wir haben den Fehler registriert." -#: type/author/view.html:97 +#: type/author/view.html:108 msgid "Website" msgstr "Internetseite" -#: type/author/view.html:103 +#: type/author/view.html:114 msgid "Location" msgstr "Ort" # | msgid "Add another author?" -#: type/author/view.html:111 +#: type/author/view.html:122 msgid "Add another?" msgstr "Weitere hinzufügen?" -#: type/author/view.html:119 +#: type/author/view.html:130 #, python-format msgid "" "Showing all works by author. Would you like to see only " "ebooks?" msgstr "" -"Zeigt alle Werke eines Autors. Möchten Sie nur eBooks sehen?" +"Zeigt alle Werke eines Autors. Möchten Sie nur eBooks sehen?" -#: type/author/view.html:121 +#: type/author/view.html:132 #, python-format msgid "" -"Showing ebooks only. Would you like to see everything by " -"this author?" +"Showing ebooks only. Would you like to see everything by this author?" msgstr "" -"Zeige nur eBooks des Autors. Möchten Sie alles von diesem " -"Autor sehen?" +"Zeige nur eBooks des Autors. Möchten Sie alles von " +"diesem Autor sehen?" -#: type/author/view.html:164 +#: type/author/view.html:179 msgid "Time" msgstr "Zeit" -#: type/author/view.html:175 +#: type/author/view.html:190 msgid "OLID" msgstr "OLID" -#: view.html:202 +#: type/author/view.html:201 msgid "Links (outside Open Library)" -msgstr "Links (außerhalb von Open Library)" +msgstr "" +"Links (außerhalb von Open Library)" -#: type/author/view.html:196 +#: type/author/view.html:211 msgid "No links yet." msgstr "Bisher keine Links." # | msgid "Add edition" -#: type/author/view.html:196 +#: type/author/view.html:211 msgid "Add one" msgstr "Ausgabe hinzufügen" -#: type/author/view.html:203 +#: type/author/view.html:218 msgid "Alternative names" msgstr "Weitere Namen" @@ -4311,187 +4883,174 @@ msgid "View Book on Archive.org" msgstr "Buch auf Archive.org ansehen" # | msgid "This Edition" -#: type/edition/admin_bar.html:28 +#: SearchResultsWork.html:107 type/edition/admin_bar.html:28 msgid "Orphaned Edition" msgstr "Verwaiste Ausgabe" -# | msgid "Editions Published" -#: type/edition/publisher_line.html:11 -msgid "This edition was published" -msgstr "Diese Ausgabe wurde veröffentlicht" - -#: SearchResultsWork.html:78 type/edition/publisher_line.html:13 -#: type/edition/publisher_line.html:26 -msgid "in" -msgstr "In" +#: databarHistory.html:26 databarView.html:33 +#: type/edition/compact_title.html:10 +msgid "Edit this template" +msgstr "Vorlage bearbeiten" -# | msgid "Show for other books from %s" -#: type/edition/publisher_line.html:19 -#, python-format -msgid "Show other books from %(publisher)s" -msgstr "Andere Bücher von %(publisher)s anzeigen" +#: type/edition/modal_links.html:25 +msgid "Review" +msgstr "Rezension" -# | msgid "Search for other books from" -#: type/edition/publisher_line.html:23 -#, python-format -msgid "Search for other books from %(publisher)s" -msgstr "Suche nach anderen Büchern von %(publisher)s" +#: type/edition/modal_links.html:28 +msgid "Notes" +msgstr "Notizen" -# | msgid "Search for subjects about" -#: type/edition/publisher_line.html:29 -#, python-format -msgid "Search for subjects about %(place)s" -msgstr "Suche nach Themen über %(place)s" +#: type/edition/modal_links.html:32 +msgid "Share" +msgstr "Teilen" -#: type/edition/view.html:181 type/work/view.html:181 +#: type/edition/title_summary.html:8 msgid "An edition of" msgstr "Eine Ausgabe von" -#: type/edition/view.html:207 type/work/view.html:207 -#, python-format -msgid "Written in %(language)s" -msgstr "Geschrieben in %(language)s" - -#: type/edition/view.html:212 type/work/view.html:212 -msgid "pages" -msgstr "Seiten" - -#: type/edition/view.html:230 type/work/view.html:230 -#, python-format -msgid "" -"This edition doesn't have a description yet. Can you add " -"one?" -msgstr "" -"Diese Ausgabe hat bisher keine Beschreibung. Können Sie eine hinzufügen?" - # | msgid "First Published" -#: type/edition/view.html:268 type/work/view.html:268 +#: type/edition/title_summary.html:10 #, python-format msgid "First published in %s" msgstr "Zuerst veröffentlicht in %s" -#: type/edition/view.html:279 type/edition/view.html:280 type/edition/view.html:377 -#: type/work/view.html:279 type/work/view.html:280 type/work/view.html:377 -msgid "First Sentence" -msgstr "Erster Satz" - -#: type/edition/view.html:283 type/work/view.html:283 -msgid "Work Description" -msgstr "Beschreibung des Werks" - -#: type/edition/view.html:289 type/work/view.html:289 -msgid "Original languages" -msgstr "Ursprungssprache" +#: type/edition/view.html:226 type/work/view.html:226 +msgid "Publish Date" +msgstr "Erscheinungsdatum" -# | msgid "Add Excerpts" -#: type/edition/view.html:294 type/work/view.html:294 -msgid "Excerpts" -msgstr "Auszüge" - -#: type/edition/view.html:304 type/work/view.html:304 +# | msgid "Show for other books from %s" +#: type/edition/view.html:236 type/work/view.html:236 #, python-format -msgid "Page %(page)s" -msgstr "Seite %(page)s" +msgid "Show other books from %(publisher)s" +msgstr "Andere Bücher von %(publisher)s anzeigen" -#: type/edition/view.html:311 type/work/view.html:311 +# | msgid "Search for other books from" +#: type/edition/view.html:240 type/work/view.html:240 #, python-format -msgid "added by %(authorlink)s." -msgstr "Hinzugefügt von %(authorlink)s." - -# | msgid "an anonymous user" -#: type/edition/view.html:313 type/work/view.html:313 -msgid "added anonymously." -msgstr "anonym hinzugefügt." - -#: type/edition/view.html:321 type/work/view.html:321 -msgid "Links outside Open Library" -msgstr "Links außerhalb der Open Library" - -#: type/edition/view.html:325 type/work/view.html:325 -msgid "Wikipedia" -msgstr "Wikipedia" +msgid "Search for other books from %(publisher)s" +msgstr "Suche nach anderen Büchern von %(publisher)s" -#: type/edition/view.html:337 type/work/view.html:337 -msgid "Library of Congress" -msgstr "Library of Congress" +#: type/edition/view.html:251 type/work/view.html:251 +msgid "Pages" +msgstr "Seiten" -#: type/edition/view.html:340 type/work/view.html:340 -msgid "Dewey" -msgstr "Dewey" +#: databarWork.html:101 type/edition/view.html:297 type/work/view.html:297 +msgid "Buy this book" +msgstr "Dieses Buch kaufen" -#: type/edition/view.html:539 -msgid "This work does not appear on any lists." -msgstr "Dieses Werk ist in keiner Liste enthalten." +#: type/edition/view.html:329 type/work/view.html:329 +#, python-format +msgid "" +"This edition doesn't have a description yet. Can you add one?" +msgstr "" +"Diese Ausgabe hat bisher keine Beschreibung. Können Sie eine hinzufügen?" -#: databarHistory.html:26 databarView.html:33 type/edition/view.html:358 -#: type/work/view.html:358 -msgid "Edit this template" -msgstr "Vorlage bearbeiten" +#: type/edition/view.html:352 type/work/view.html:352 +msgid "Book Details" +msgstr "Buchdetails" -# | msgid "editions in" -#: type/edition/view.html:362 type/work/view.html:362 -msgid "No editions available" -msgstr "Keine Ausgaben verfügbar" +#: type/edition/view.html:356 type/work/view.html:356 +msgid "Published in" +msgstr "Veröffentlicht in" -# | msgid "Edition Notes" -#: type/edition/view.html:383 type/work/view.html:383 -msgid "Edition Description" -msgstr "Beschreibung der Ausgabe" +#: type/edition/view.html:364 type/edition/view.html:454 +#: type/edition/view.html:455 type/work/view.html:364 type/work/view.html:454 +#: type/work/view.html:455 +msgid "First Sentence" +msgstr "Erster Satz" -#: type/edition/view.html:398 type/work/view.html:398 +#: type/edition/view.html:374 type/work/view.html:374 msgid "Edition Notes" msgstr "Hinweise zur Ausgabe" -#: type/edition/view.html:403 type/work/view.html:403 +#: type/edition/view.html:379 type/work/view.html:379 msgid "Series" msgstr "Serie" -#: type/edition/view.html:404 type/work/view.html:404 +#: type/edition/view.html:380 type/work/view.html:380 msgid "Volume" msgstr "Band" -#: type/edition/view.html:405 type/work/view.html:405 +#: type/edition/view.html:381 type/work/view.html:381 msgid "Genre" msgstr "Genre" -#: type/edition/view.html:406 type/work/view.html:406 +#: type/edition/view.html:382 type/work/view.html:382 msgid "Other Titles" msgstr "Andere Titel" -#: type/edition/view.html:407 type/work/view.html:407 +#: type/edition/view.html:383 type/work/view.html:383 msgid "Copyright Date" msgstr "Copyright Datum" -#: type/edition/view.html:408 type/work/view.html:408 +#: type/edition/view.html:384 type/work/view.html:384 msgid "Translation Of" msgstr "Übersetzung von" -#: type/edition/view.html:409 type/work/view.html:409 +#: type/edition/view.html:385 type/work/view.html:385 msgid "Translated From" msgstr "Übersetzt aus" -#: type/edition/view.html:428 type/work/view.html:428 +#: type/edition/view.html:404 type/work/view.html:404 msgid "External Links" msgstr "Externe Links" -#: type/edition/view.html:452 type/work/view.html:452 +#: type/edition/view.html:428 type/work/view.html:428 msgid "Format" msgstr "Format" -#: type/edition/view.html:453 type/work/view.html:453 +#: type/edition/view.html:429 type/work/view.html:429 msgid "Pagination" msgstr "Seitennummerierung" -#: type/edition/view.html:454 type/work/view.html:454 +#: type/edition/view.html:430 type/work/view.html:430 msgid "Number of pages" msgstr "Seitenzahl" -#: type/edition/view.html:456 type/work/view.html:456 +#: type/edition/view.html:432 type/work/view.html:432 msgid "Weight" msgstr "Gewicht" -#: type/edition/view.html:488 type/work/view.html:488 +#: type/edition/view.html:458 type/work/view.html:458 +msgid "Work Description" +msgstr "Beschreibung des Werks" + +#: type/edition/view.html:467 type/work/view.html:467 +msgid "Original languages" +msgstr "Ursprungssprache" + +# | msgid "Add Excerpts" +#: type/edition/view.html:472 type/work/view.html:472 +msgid "Excerpts" +msgstr "Auszüge" + +#: type/edition/view.html:482 type/work/view.html:482 +#, python-format +msgid "Page %(page)s" +msgstr "Seite %(page)s" + +#: type/edition/view.html:489 type/work/view.html:489 +#, python-format +msgid "added by %(authorlink)s." +msgstr "Hinzugefügt von %(authorlink)s." + +# | msgid "an anonymous user" +#: type/edition/view.html:491 type/work/view.html:491 +msgid "added anonymously." +msgstr "anonym hinzugefügt." + +#: type/edition/view.html:499 type/work/view.html:499 +msgid "Links outside Open Library" +msgstr "" +"Links außerhalb der Open Library" + +#: type/edition/view.html:503 type/work/view.html:503 +msgid "Wikipedia" +msgstr "Wikipedia" + +#: type/edition/view.html:519 type/work/view.html:519 msgid "Lists containing this Book" msgstr "Listen, die dieses Buch enthalten" @@ -4499,6 +5058,14 @@ msgstr "Listen, die dieses Buch enthalten" msgid "Code" msgstr "Code" +#: type/language/view.html:31 +#, python-format +msgid "" +"Try a search for readable books in %(language)s?" +msgstr "" +"Möchten Sie versuchen, nach lesbaren Büchern in " +"%(language)s zu suchen?" + # | msgid "Reading Lists" #: type/list/edit.html:14 msgid "Edit List" @@ -4513,36 +5080,40 @@ msgid "Description" msgstr "Beschreibung" # | msgid "Sign Up to Open Library" -#: type/list/embed.html:22 +#: type/list/embed.html:23 msgid "Visit Open Library" msgstr "Open Library besuchen" # | msgid "Explore authors" -#: type/list/embed.html:41 type/list/view_body.html:198 +#: type/list/embed.html:42 type/list/view_body.html:199 msgid "Unknown authors" msgstr "Unbekannte Autoren" -#: type/list/embed.html:66 +#: type/list/embed.html:67 msgid "" -"Open in online Book Reader. Downloads available in ePub, DAISY, PDF, TXT formats " -"from main book page" +"Open in online Book Reader. Downloads available in ePub, DAISY, PDF, TXT " +"formats from main book page" msgstr "" -"Online anzeigen. Downloads sind in den Formaten ePub, DAISY, PDF und TXT auf der " -"Hauptseite des Buchs zu finden" +"Online anzeigen. Downloads sind in den Formaten ePub, DAISY, PDF und TXT auf " +"der Hauptseite des Buchs zu finden" -#: embed.html:73 +#: type/list/embed.html:73 msgid "This book is checked out" msgstr "Dieses Buch ist ausgeliehen" -#: type/list/embed.html:77 +#: type/list/embed.html:75 +msgid "Checked out" +msgstr "Ausgeliehen" + +#: type/list/embed.html:78 msgid "Borrow book" msgstr "Buch leihen" -#: type/list/embed.html:82 +#: type/list/embed.html:83 msgid "Protected DAISY" msgstr "Geschütztes DAISY" -#: type/list/embed.html:99 +#: BookByline.html:34 type/list/embed.html:100 #, python-format msgid "by %(name)s" msgstr "von %(name)s" @@ -4563,7 +5134,8 @@ msgstr "Abonnieren" #: type/list/exports.html:36 type/list/exports.html:56 #, python-format msgid "Watch activity via Atom feed" -msgstr "Aktivitäten via Atom feed verfolgen" +msgstr "" +"Aktivitäten via Atom feed verfolgen" #: type/list/exports.html:43 msgid "Export Not Available" @@ -4572,7 +5144,8 @@ msgstr "Export nicht verfügbar" #: type/list/exports.html:48 #, python-format msgid "" -"Only lists with up to %(max)s editions can be exported. This one has %(count)s." +"Only lists with up to %(max)s editions can be exported. This one has " +"%(count)s." msgstr "" "Nur Listen bis maximal %(max)s Ausgaben können exportiert werden. Diese hat " "%(count)s." @@ -4580,9 +5153,11 @@ msgstr "" #: type/list/exports.html:50 #, python-format msgid "" -"Try removing some seeds for more focus, or look at bulk download of the catalog." +"Try removing some seeds for more focus, or look at bulk " +"download of the catalog." msgstr "" +"Versuchen Sie für besseren Fokus, einige Quellen zu entfernen, oder schauen " +"Sie sich den Massendownload des Katalogs an." # | msgid "Sign Up to Open Library" #: type/list/view_body.html:8 @@ -4590,52 +5165,53 @@ msgstr "" msgid "%(name)s | Lists | Open Library" msgstr "%(name)s | Listen | Open Library" -#: view.html:14 +#: type/list/view_body.html:17 #, python-format msgid "%(title)s: %(description)s" msgstr "%(title)s: %(description)s" -#: type/list/view_body.html:27 +#: type/list/view_body.html:26 msgid "View the list on Open Library." msgstr "Die Liste bei OpenLibrary anzeigen." -#: type/list/view_body.html:164 +#: type/list/view_body.html:165 msgid "Remove this item?" msgstr "Diesen Link entfernen?" -#: type/list/view_body.html:178 +#: type/list/view_body.html:179 #, python-format msgid "Last updated %(date)s" msgstr "%(date)s zuletzt aktualisiert" # | msgid "Removed" -#: type/list/view_body.html:189 +#: type/list/view_body.html:190 msgid "Remove seed" msgstr "Quelle entfernen" -#: type/list/view_body.html:189 +#: type/list/view_body.html:190 msgid "Are you sure you want to remove this item from the list?" -msgstr "Sind Sie sicher, dass sie dieses Objekt von der Liste entfernen wollen?" +msgstr "" +"Sind Sie sicher, dass sie dieses Objekt von der Liste entfernen wollen?" # | msgid "Removed" -#: type/list/view_body.html:190 +#: type/list/view_body.html:191 msgid "Remove Seed" msgstr "Quelle entfernen" -#: type/list/view_body.html:191 +#: type/list/view_body.html:192 msgid "" -"You are about to remove the last item in the list. That will delete the whole " -"list. Are you sure you want to continue?" +"You are about to remove the last item in the list. That will delete the " +"whole list. Are you sure you want to continue?" msgstr "" "Sie sind daran, dass letzte verbleibende Objekt von der Liste zu entfernen. " -"Dadurch wird die Liste ebenfalls gelöscht. Sind sie sicher, dass Sie fortfahren " -"wollen?" +"Dadurch wird die Liste ebenfalls gelöscht. Sind sie sicher, dass Sie " +"fortfahren wollen?" -#: type/list/view_body.html:257 +#: type/list/view_body.html:261 msgid "List Metadata" msgstr "Metadaten auflisten" -#: type/list/view_body.html:258 +#: type/list/view_body.html:262 msgid "Derived from seed metadata" msgstr "Abgeleitete Metadaten der Quelle" @@ -4698,13 +5274,14 @@ msgstr "Admin-Seite" #, python-format msgid "" "You are publicly sharing the books you are currently reading, have already read, and want to read." +"currently-reading\">currently reading, have already read, and want to read." msgstr "" -"Sie teilen öffentlich die Bücher, die Sie aktuell lesen, gelesen " -"haben, und lesen möchten." +"Sie teilen öffentlich die Bücher, die Sie aktuell lesen, gelesen haben, und lesen möchten." #: type/user/view.html:55 msgid "You have chosen to make your" @@ -4717,15 +5294,15 @@ msgstr "Privat" #: type/user/view.html:60 #, python-format msgid "" -"Here are the books %(user)s is currently reading, have " -"already read, and want to read!" +"Here are the books %(user)s is currently reading, have already read, and want to read!" msgstr "" "Hier sehen Sie Bücher, die %(user)s aktuell liest, bereits " -"gelesen hat, und lesen möchte!" +"reading\">aktuell liest, bereits gelesen hat, und lesen möchte!" #: type/user/view.html:62 msgid "This reader has chosen to make their Reading Log private." @@ -4735,12 +5312,12 @@ msgstr "Dieses Leselog ist nicht öffentlich." msgid "No edits. Yet." msgstr "Keine Bearbeitungen. Bisher." -#: SearchResultsWork.html:72 type/work/editions.html:11 +#: SearchResultsWork.html:83 type/work/editions.html:11 #, python-format msgid "First published in %(year)s" msgstr "Zuerst erschienen in %(year)s" -#: type/work/editions.html:14 type/work/editions_datatable.html:37 +#: type/work/editions.html:14 type/work/editions_datatable.html:47 #, python-format msgid "Add another edition of %(work)s" msgstr "Weitere Ausgabe von %(work)s hinzufügen" @@ -4753,15 +5330,32 @@ msgstr "Weitere hinzufügen" msgid "Title missing" msgstr "Titel fehlt" -#: type/work/editions_datatable.html:13 +#: type/work/editions_datatable.html:9 +#, python-format +msgid "Showing %(count)d featured edition." +msgid_plural "Showing %(count)d featured editions." +msgstr[0] "%(count)d vorgestellte Ausgabe angezeigt." +msgstr[1] "%(count)d vorgestellte Ausgaben angezeigt." + +#: type/work/editions_datatable.html:14 +#, python-format +msgid "View all %(count)d editions?" +msgstr "Alle %(count)d Ausgaben anzeigen?" + +#: type/work/editions_datatable.html:20 msgid "Edition" msgstr "Ausgabe" -#: type/work/editions_datatable.html:14 +#: type/work/editions_datatable.html:21 msgid "Availability" msgstr "Verfügbarkeit" -#: type/work/editions_datatable.html:37 +# | msgid "editions in" +#: type/work/editions_datatable.html:42 +msgid "No editions available" +msgstr "Keine Ausgaben verfügbar" + +#: type/work/editions_datatable.html:47 msgid "Add another edition?" msgstr "Weitere Ausgabe hinzufügen?" @@ -4786,19 +5380,22 @@ msgstr "Bookshop.org" msgid "Look for this edition for sale at %(store)s" msgstr "Nach dieser Ausgabe bei %(store)s suchen" -#: AuthorList.html:9 -msgid "Go to this author's page" -msgstr "Zur Seite dieses Autors gehen" +#: BookByline.html:11 +#, python-format +msgid "%(n)s other" +msgid_plural "%(n)s others" +msgstr[0] "%(n)s weitere" +msgstr[1] "%(n)s weitere" -#: AuthorList.html:11 +#: BookByline.html:36 msgid "by an unknown author" msgstr "von einem unbekannten Autor" -#: BookPreview.html:18 +#: BookPreview.html:19 msgid "Preview Book" msgstr "Vorschau" -#: BookPreview.html:28 +#: BookPreview.html:29 msgid "See more about this book on Archive.org" msgstr "Mehr über dieses Buch auf Archive.org" @@ -4806,85 +5403,87 @@ msgstr "Mehr über dieses Buch auf Archive.org" msgid "(Optional, but very useful!)" msgstr "(Optional, aber sehr nützlich!)" -#: EditButtons.html:27 -msgid "" -"Please Note: Only Admins can delete things. Let us know if there's a problem." -msgstr "" -"Bitte beachten: Nur Admins können löschen. Teilen Sie uns mit wenn es ein Problem gibt.." - #: EditButtonsMacros.html:27 msgid "Delete this record" msgstr "Diesen Eintrag löschen" -#: EditionNavBar.html:10 +#: EditionNavBar.html:11 +msgid "Overview" +msgstr "Übersicht" + +#: EditionNavBar.html:15 #, python-format msgid "View %(count)s Edition" msgid_plural "View %(count)s Editions" msgstr[0] "%(count)s Ausgabe anzeigen" msgstr[1] "%(count)s Ausgaben anzeigen" -#: EditionNavBar.html:14 -msgid "Overview" -msgstr "Übersicht" +#: EditionNavBar.html:19 +msgid "Details" +msgstr "Details" + +#: EditionNavBar.html:24 +#, python-format +msgid "%(reviews)s Review" +msgid_plural "%(reviews)s Reviews" +msgstr[0] "%(reviews)s Rezension" +msgstr[1] "%(reviews)s Rezensionen" -#: LoanStatus.html:44 +#: EditionNavBar.html:33 +msgid "Related Books" +msgstr "Verwandte Bücher" + +#: LoanStatus.html:56 msgid "Return eBook" -msgstr "eBook zurückgeben" +msgstr "E-Book zurückgeben" -#: LoanStatus.html:51 +#: LoanStatus.html:62 msgid "Really return this book?" msgstr "Dieses Buch zurückgeben?" -#: LoanStatus.html:79 +#: LoanStatus.html:97 msgid "You are next on the waiting list" msgstr "Wählen Sie eines der existierenden Umschlagsbilder" -#: LoanStatus.html:81 +#: LoanStatus.html:99 #, python-format msgid "You are #%(spot)d of %(wlsize)d on the waiting list." msgstr "Sie sind #%(spot)d von %(wlsize)d auf der Warteliste." -#: LoanStatus.html:86 +#: LoanStatus.html:104 msgid "Leave waiting list" msgstr "Warteliste verlassen" -#: LoanStatus.html:92 +#: LoanStatus.html:118 #, python-format -msgid "Readers waiting for this title: %(count)s" -msgstr "Nutzer die auf auf diesen Titel warten: %(count)s" +msgid "Readers in line: %(count)s" +msgstr "Nutzer*innen die auf auf diesen Titel warten: %(count)s" -#: LoanStatus.html:94 +#: LoanStatus.html:120 msgid "You'll be next in line." msgstr "Sie sind an nächster Stelle auf der Liste." -#: LoanStatus.html:103 +#: LoanStatus.html:125 msgid "This book is currently checked out, please check back later." msgstr "Dieses Buch ist aktuell verliehen, bitte kommen Sie später wieder." -#: LoanStatus.html:104 +#: LoanStatus.html:126 msgid "Checked Out" msgstr "Ausgeliehen" -#: LoanStatus.html:113 -msgid "Donate Book" -msgstr "Buch spenden" - -#: LoanStatus.html:116 -msgid "We don't have this book yet. Can you donate it to the Lending Library?" -msgstr "" -"Wir haben dieses Buch bisher nicht. Können Sie es der Leihbücherei spenden?" - -#: LoanStatus.html:120 LoanStatus.html:125 +#: NotInLibrary.html:9 msgid "Not in Library" msgstr "Nicht in Bibliothek vorhanden" -#: NotesModal.html:33 +#: NotesModal.html:31 +msgid "My Book Notes" +msgstr "Meine Bücher-Notizen" + +#: NotesModal.html:35 msgid "My private notes about this edition:" msgstr "Meine persönlichen Notizen zu dieser Ausgabe:" -#: ObservationsModal.html:24 +#: ObservationsModal.html:31 msgid "My Book Review" msgstr "Meine Buchrezension" @@ -4898,21 +5497,19 @@ msgstr "Spezieller Zugriff" #: ReadButton.html:12 msgid "" -"Special ebook access from the Internet Archive for patrons with qualifying print " -"disabilities" -msgstr "Spezieller eBook-Zugriff vom Internet Archive für Gönner mit Einschränkungen für Druckwerke" +"Special ebook access from the Internet Archive for patrons with qualifying " +"print disabilities" +msgstr "" +"Spezieller eBook-Zugriff vom Internet Archive für Gönner mit Einschränkungen " +"für Druckwerke" #: ReadButton.html:16 msgid "Borrow ebook from Internet Archive" -msgstr "eBook aus dem Internet Archive leihen" +msgstr "E-Book aus dem Internet Archive leihen" #: ReadButton.html:20 msgid "Read ebook from Internet Archive" -msgstr "eBook im Internet Archive lesen" - -#: ReadButton.html:39 -msgid "Listen" -msgstr "Anhören" +msgstr "E-Book im Internet Archive lesen" #: ReadMore.html:7 msgid "Read more" @@ -4922,6 +5519,31 @@ msgstr "Mehr lesen" msgid "Read less" msgstr "Weniger lesen" +#: ReadingLogDropper.html:88 +msgid "My Reading Lists:" +msgstr "Meine Leselisten:" + +#: ReadingLogDropper.html:103 +msgid "Use this Work" +msgstr "Dieses Werk nutzen" + +#: ReadingLogDropper.html:106 ReadingLogDropper.html:127 +#: ReadingLogDropper.html:143 databarAuthor.html:27 databarAuthor.html:34 +msgid "Create a new list" +msgstr "Neue Liste erstellen" + +#: ReadingLogDropper.html:117 ReadingLogDropper.html:133 +msgid "Add to List" +msgstr "Zu Liste hinzufügen" + +#: ReadingLogDropper.html:157 +msgid "Description:" +msgstr "Beschreibung:" + +#: ReadingLogDropper.html:165 +msgid "Create new list" +msgstr "Neue Liste erstellen" + #: RecentChanges.html:54 msgid "View MARC" msgstr "MARC anzeigen" @@ -4930,10 +5552,6 @@ msgstr "MARC anzeigen" msgid "Path" msgstr "Pfad" -#: RecentChangesAdmin.html:22 -msgid "Comments" -msgstr "Kommentare" - #: RecentChangesAdmin.html:41 msgid "view" msgstr "Anzeigen" @@ -4950,51 +5568,53 @@ msgstr "Vergleichen" msgid "Return book" msgstr "Buch zurückgeben" -#: SearchResultsWork.html:67 +#: SearchResultsWork.html:89 #, python-format -msgid "%(n)s other" -msgid_plural "%(n)s others" -msgstr[0] "%(n)s weitere" -msgstr[1] "%(n)s weitere" - -#: SearchResultsWork.html:78 -msgid "languages" -msgstr "Sprachen" +msgid "in 1 language" +msgid_plural "" +"in %(count)d languages" +msgstr[0] "in 1 Sprache" +msgstr[1] "" +"in %(count)d Sprachen" -#: SearchResultsWork.html:81 +#: SearchResultsWork.html:92 #, python-format msgid "%s previewable" msgstr "%s vorschaubar" -#: SocialShare.html:16 -msgid "Share this book" -msgstr "Dieses Buch teilen" +#: SearchResultsWork.html:102 +msgid "This is only visible to librarians." +msgstr "Das ist nur für Bibliothekar*innen sichtbar." -#: SocialShare.html:31 +#: SearchResultsWork.html:104 +msgid "Work Title" +msgstr "Werktitel" + +#: ShareModal.html:44 msgid "Embed this book in your website" msgstr "Dieses Buch auf Ihrer Webseite einbetten" -#: SocialShare.html:35 +#: ShareModal.html:48 msgid "Embed" msgstr "Einbetten" -#: StarRatings.html:51 +#: StarRatings.html:52 msgid "Clear my rating" msgstr "Meine Bewertung löschen" -#: StarRatingsStats.html:21 +#: StarRatingsStats.html:22 msgid "Ratings" msgstr "Bewertungen" -#: StarRatingsStats.html:23 +#: StarRatingsStats.html:24 msgid "Want to read" msgstr "Lesewunsch" -#: StarRatingsStats.html:24 +#: StarRatingsStats.html:25 msgid "Currently reading" msgstr "Aktuell Lesend" -#: StarRatingsStats.html:25 +#: StarRatingsStats.html:26 msgid "Have read" msgstr "Haben es gelesen" @@ -5057,17 +5677,17 @@ msgstr "Hä?" #: TypeChanger.html:22 msgid "" "Every piece of Open Library is defined by the type of content it displays, " -"usually by content definition (e.g. Authors, Editions, Works) but sometimes by " -"content use (e.g. macro, template, rawtext). Changing this for an existing page " -"will alter its presentation and the data fields available for editing its " -"content." +"usually by content definition (e.g. Authors, Editions, Works) but sometimes " +"by content use (e.g. macro, template, rawtext). Changing this for an " +"existing page will alter its presentation and the data fields available for " +"editing its content." msgstr "" -"Jedes Stück der Open Library ist definiert über seinen angezeigten Inhaltstyp; " -"üblicherweise über die Definition des Inhalts (z.B. Autoren, Ausgaben, Werke) " -"aber manchmal auch über die Verwendung des Inhalts (z.B. Makro, Vorlage, " -"Klartext). Dies für eine bestehende Seite zu ändern, ändert auch deren " -"Darstellung und die zur Verfügung stehenden Datenfelder um den Inhalt zu " -"bearbeiten." +"Jedes Stück der Open Library ist definiert über seinen angezeigten " +"Inhaltstyp; üblicherweise über die Definition des Inhalts (z.B. Autoren, " +"Ausgaben, Werke) aber manchmal auch über die Verwendung des Inhalts (z.B. " +"Makro, Vorlage, Klartext). Dies für eine bestehende Seite zu ändern, ändert " +"auch deren Darstellung und die zur Verfügung stehenden Datenfelder um den " +"Inhalt zu bearbeiten." #: TypeChanger.html:22 msgid "Please use caution changing Page Type!" @@ -5089,18 +5709,6 @@ msgstr "In nahegelegenen Bibliotheken suchen" msgid "Check WorldCat for an edition near you" msgstr "WolrdCat nach einer Ausgabe in Ihrer Nähe durchsuchen" -#: daisy.html:10 -msgid "Encrypted daisy download for authorized print-disabled patrons" -msgstr "" - -#: daisy.html:12 -msgid "Encrypted daisy lock" -msgstr "" - -#: daisy.html:14 -msgid "Download for print-disabled" -msgstr "" - #: databarAuthor.html:20 msgid "Add to list" msgstr "Zu Liste hinzufügen" @@ -5113,10 +5721,6 @@ msgstr "×" msgid "Add new list" msgstr "Neue Liste hinzufügen" -#: databarAuthor.html:86 -msgid "from" -msgstr "Von" - #: databarHistory.html:16 databarView.html:22 #, python-format msgid "Last edited by %(author_link)s" @@ -5126,89 +5730,86 @@ msgstr "Zuletzt bearbeitet von %(author_link)s" msgid "Last edited anonymously" msgstr "Dieses Buch wurde zuletzt anonym bearbeitet" -#: databarWork.html:86 -msgid "Buy this book" -msgstr "Dieses Buch kaufen" - -#: databarWork.html:105 +#: databarWork.html:115 #, python-format msgid "This book was generously donated by %(donor)s" msgstr "Dieses Buch wurde großzügigerweise von %(donor)s gespendet" -#: databarWork.html:107 +#: databarWork.html:117 msgid "This book was generously donated anonymously" msgstr "Dieses Buch wurde großzügigerweise anonym gespendet" -#: databarWork.html:112 +#: databarWork.html:122 msgid "Learn more about Book Sponsorship" msgstr "Mehr über das Bücher-Sponsorship lernen" -#: account.py:423 account.py:461 +#: account.py:420 account.py:458 #, python-format msgid "" "We've sent the verification email to %(email)s. You'll need to read that and " "click on the verification link to verify your email." msgstr "" -"Wir haben die Bestätigungs-E-Mail an %(email)s verschickt. Bitte lesen Sie die " -"E-Mail und klicken sie auf den Bestätigungs-Link." +"Wir haben die Bestätigungs-E-Mail an %(email)s verschickt. Bitte lesen Sie " +"die E-Mail und klicken sie auf den Bestätigungs-Link." -#: account.py:489 +#: account.py:486 msgid "Username must be between 3-20 characters" msgstr "Der Benutzername muss zwischen 3 und 20 Zeichen lang sein" -#: account.py:491 +#: account.py:488 msgid "Username may only contain numbers and letters" msgstr "Der Benutzername darf nur Ziffern und Buchstaben enthalten" -#: account.py:494 +#: account.py:491 msgid "Username unavailable" msgstr "Der Benutzername ist nicht verfügbar" -#: account.py:499 forms.py:60 +#: account.py:496 forms.py:60 msgid "Must be a valid email address" msgstr "Muss eine gültige Email-Adresse sein" -#: account.py:503 forms.py:41 +#: account.py:500 forms.py:41 msgid "Email already registered" msgstr "E-Mail-Adresse bereits registriert" -#: account.py:530 +#: account.py:526 msgid "Email address is already used." msgstr "E-Mail-Adresse wird bereits verwendet." -#: account.py:531 +#: account.py:527 msgid "" -"Your email address couldn't be updated. The specified email address is already " -"used." +"Your email address couldn't be updated. The specified email address is " +"already used." msgstr "" -"Ihre E-Mail-Adresse konnte nicht aktualisiert werden. Die angegebene Adresse ist " -"bereits in Benutzung." +"Ihre E-Mail-Adresse konnte nicht aktualisiert werden. Die angegebene Adresse " +"ist bereits in Benutzung." -#: account.py:537 +#: account.py:533 msgid "Email verification successful." msgstr "E-Mail-Adresse erfolgreich bestätigt." -#: account.py:538 +#: account.py:534 msgid "" -"Your email address has been successfully verified and updated in your account." +"Your email address has been successfully verified and updated in your " +"account." msgstr "Ihre E-Mail-Adresse wurde erfolgreich bestätigt und aktualisiert." -#: account.py:544 +#: account.py:540 msgid "Email address couldn't be verified." msgstr "E-Mail-Adresse konnte nicht bestätigt werden." -#: account.py:545 +#: account.py:541 msgid "" "Your email address couldn't be verified. The verification link seems invalid." msgstr "" -"Ihre E-Mail-Adresse konnte nicht bestätigt werden. Der Bestätigungs-Link scheint " -"ungültig zu sein." +"Ihre E-Mail-Adresse konnte nicht bestätigt werden. Der Bestätigungs-Link " +"scheint ungültig zu sein." -#: account.py:649 account.py:659 +#: account.py:645 account.py:655 msgid "Password reset failed." msgstr "Zurücksetzen des Passworts fehlgeschlagen." -#: account.py:706 account.py:725 +#: account.py:702 account.py:721 msgid "Notification preferences have been updated successfully." msgstr "Benachrichtigungseinstellungen wurden erfolgreich geändert." @@ -5240,443 +5841,462 @@ msgstr "Muss zwischen 3 und 20 Buchstaben und Ziffern lang sein" msgid "Must be between 3 and 20 characters" msgstr "Muss zwischen 3 und 20 Zeichen lang sein" -#: forms.py:78 forms.py:154 +#: forms.py:78 forms.py:156 msgid "Your email address" msgstr "Ihre E-Mail-Adresse" #: forms.py:90 -msgid "Choose a screen name" -msgstr "Wählen Sie einen Anzeigenamen" +msgid "" +"Choose a screen name. Screen names are public and cannot be changed later." +msgstr "" +"Wählen Sie einen Anzeigenamen. Dieser Name ist öffentlich und kann nicht " +"nachträglich geändert werden." -#: forms.py:92 +#: forms.py:94 msgid "Letters and numbers only please, and at least 3 characters." msgstr "Bitte mindestens 3 Zeichen, sowie nur Buchstaben und Ziffern." -#: forms.py:98 forms.py:160 +#: forms.py:100 forms.py:162 msgid "Choose a password" msgstr "Passwort wählen" -#: forms.py:104 +#: forms.py:106 msgid "Confirm password" msgstr "Passwort bestätigen" -#: forms.py:108 +#: forms.py:110 msgid "Passwords didn't match." msgstr "Die Passwörter stimmen nicht überein." -#: forms.py:113 +#: forms.py:115 msgid "" -"I want to receive news, announcements, and resources from the Internet Archive, the non-profit that runs Open Library." +"I want to receive news, announcements, and resources from the Internet Archive, the non-profit that runs " +"Open Library." msgstr "" "Ich möchte Neuigkeiten, Ankündigungen und Quellen über das Internet Archive, der Non-Profit Organisation welche die " "OpenLibrary betreibt, erhalten." -#: forms.py:149 +#: forms.py:151 msgid "Invalid password" msgstr "Ungültiges Passwort" -#: view.html:160 -#, python-format -msgid "" -"1 edition of %(title)s found in the catalog." -msgid_plural "" -"%(count)d editions of %(title)s found in the catalog." -msgstr[0] "" -"1 Ausgabe von %(title)s in diesem Katalog gefunden." -msgstr[1] "" -"%(count)d Ausgaben von %(title)s in diesem Katalog gefunden." +#~ msgid "Your Notifications" +#~ msgstr "Ihre Benachrichtigungen" -#: view.html:241 -msgid "Published" -msgstr "Herausgegeben" +#~ msgid "Internet Archive Announcements" +#~ msgstr "Benachrichtigungen vom Internet Archive" -#: view.html:288 -msgid "About the Edition" -msgstr "Über diese Ausgabe" +#~ msgid "Your Privacy Settings" +#~ msgstr "Ihre Datenschutz-Einstellungen" -#: view.html:115 view.html:291 -msgid "About the Book" -msgstr "Über das Buch" +#~ msgid "Books You've Checked Out" +#~ msgstr "Entliehene Bücher" -#: view.html:107 view.html:295 -#, python-format -msgid "" -"There's no description for this book yet. Can you add one?" -msgstr "" -"Diese Ausgabe hat bisher keine Beschreibung. Möchten Sie eine hinzufügen?" +#~ msgid "Waitlist" +#~ msgstr "Warteliste" + +#~ msgid "Reading Stats" +#~ msgstr "Lesestatistik" + +#~ msgid "The year it was published is plenty." +#~ msgstr "Das Jahr der Veröffentlichung reicht aus." + +#~ msgid "Russian" +#~ msgstr "Russisch" + +#~ msgid "Italian" +#~ msgstr "Italienisch" + +#~ msgid "Arabic" +#~ msgstr "Arabisch" + +#~ msgid "Japanese" +#~ msgstr "Japanisch" + +#~ msgid "Polish" +#~ msgstr "Polnisch" + +#~ msgid "Only eBooks" +#~ msgstr "Nur E-Books" + +#~ msgid "Your Profile" +#~ msgstr "Ihr Profil" + +#~ msgid "Log in" +#~ msgstr "Anmelden" + +#~ msgid "No lists yet!" +#~ msgstr "Bisher keine Listen!" -#: embed.html:17 home.html:48 preview.html:23 snippet.html:18 view.html:32 #, python-format -msgid "1 seed" -msgid_plural "%(count)d seeds" -msgstr[0] "1 Seed" -msgstr[1] "%(count)d Seeds" +#~ msgid "" +#~ "Are you sure you want to remove %(title)s from your list?" +#~ msgstr "" +#~ "Sind Sie sicher, dass Sie %(title)s von Ihrer Liste " +#~ "entfernen wollen?" + +#~ msgid "saving..." +#~ msgstr "Speichern..." + +#~ msgid "Author Search" +#~ msgstr "Autorensuche" + +#~ msgid "Subject Search" +#~ msgstr "Themensuche" -#: embed.html:113 view.html:222 +# | msgid "Editions Published" +#~ msgid "This edition was published" +#~ msgstr "Diese Ausgabe wurde veröffentlicht" + +#~ msgid "in" +#~ msgstr "In" + +# | msgid "Search for subjects about" #, python-format -msgid "- 1 edition" -msgid_plural "- %(count)s editions" -msgstr[0] "- 1 Ausgabe" -msgstr[1] "- %(count)s Ausgaben" +#~ msgid "Search for subjects about %(place)s" +#~ msgstr "Suche nach Themen über %(place)s" -#: embed.html:115 view.html:224 #, python-format -msgid "- 1 work" -msgid_plural "- %(count)s works" -msgstr[0] "- 1 Werk" -msgstr[1] "- %(count)s Werke" +#~ msgid "Written in %(language)s" +#~ msgstr "Geschrieben in %(language)s" -#: view.html:158 -msgid "" -"You are about the remove the last item in the list. That will delete the whole " -"list. Are you sure you want to continue?" -msgstr "" -"Sie sind dabei, den letzten Eintrag von der Liste zu entfernen. Dadurch wird die " -"gesamte Liste gelöscht. Sind Sie sicher, dass Sie fortfahren möchten?" +#~ msgid "Library of Congress" +#~ msgstr "Library of Congress" -#: view.html:220 -#, python-format -msgid "- 1 edition in catalog" -msgid_plural "- %(count)s editions in catalog" -msgstr[0] "- 1 Ausgabe im Katalog" -msgstr[1] "- %(count)s Ausgaben im Katalog" +#~ msgid "Dewey" +#~ msgstr "Dewey" -#: view.html:228 -msgid "Download DAISY eBook for print disabled" -msgstr "Lade DAISY eBook mit gesperrtem Druck herunter " +# | msgid "Edition Notes" +#~ msgid "Edition Description" +#~ msgstr "Beschreibung der Ausgabe" -#: openlibrary/macros/SearchResultsWork.html:60 view.html:246 -msgid "Download DAISY" -msgstr "DAISY herunterladen" +#~ msgid "Go to this author's page" +#~ msgstr "Zur Seite dieses Autors gehen" -#: view.html:48 -#, python-format -msgid "" -"This user has chosen to publicly share the books they are currently reading, have already read, and want to read!" -msgstr "" -"Diese Person hat sich entschieden die Bücher die sie aktuell liest, bereits gelesen hat und lesen möchte öffentlich zu teilen!" +#~ msgid "" +#~ "Please Note: Only Admins can delete things. Let us know if there's a problem." +#~ msgstr "" +#~ "Bitte beachten: Nur Admins können löschen. Teilen Sie uns mit wenn es ein Problem gibt.." + +#~ msgid "Donate Book" +#~ msgstr "Buch spenden" + +#~ msgid "" +#~ "We don't have this book yet. Can you donate it to the Lending Library?" +#~ msgstr "" +#~ "Wir haben dieses Buch bisher nicht. Können Sie es der Leihbücherei " +#~ "spenden?" -#: view.html:50 -msgid "This user has chosen to keep their Reading Log private" -msgstr "Diese Person hat sich entschieden ihr Lese-Logbuch privat zu halten" +#~ msgid "languages" +#~ msgstr "Sprachen" -#: editions_datatable.html:24 -msgid "Locate" -msgstr "Lokalisieren" +#~ msgid "Share this book" +#~ msgstr "Dieses Buch teilen" + +#~ msgid "Choose a screen name" +#~ msgstr "Wählen Sie einen Anzeigenamen" -#: editions_datatable.html:45 #, python-format -msgid "Merge editions" -msgstr "Ausgaben zusammenführen" +#~ msgid "" +#~ "1 edition of %(title)s found in the catalog." +#~ msgid_plural "" +#~ "%(count)d editions of %(title)s found in the catalog." +#~ msgstr[0] "" +#~ "1 Ausgabe von %(title)s in diesem Katalog gefunden." +#~ msgstr[1] "" +#~ "%(count)d Ausgaben von %(title)s in diesem Katalog gefunden." + +#~ msgid "Published" +#~ msgstr "Herausgegeben" + +#~ msgid "About the Edition" +#~ msgstr "Über diese Ausgabe" + +#~ msgid "About the Book" +#~ msgstr "Über das Buch" -#: view.html:23 #, python-format -msgid "%(title)s by %(author)s" -msgstr "%(title)s von %(author)s" +#~ msgid "" +#~ "There's no description for this book yet. Can you add one?" +#~ msgstr "" +#~ "Diese Ausgabe hat bisher keine Beschreibung. Möchten Sie eine hinzufügen?" -#: view.html:34 #, python-format -msgid "Subjects: %(subjectlist)s" -msgstr "Themen: %(subjectlist)s" +#~ msgid "1 seed" +#~ msgid_plural "%(count)d seeds" +#~ msgstr[0] "1 Seed" +#~ msgstr[1] "%(count)d Seeds" -#: view.html:36 #, python-format -msgid "Places: %(placelist)s" -msgstr "Orte: %(placelist)s" +#~ msgid "- 1 edition" +#~ msgid_plural "- %(count)s editions" +#~ msgstr[0] "- 1 Ausgabe" +#~ msgstr[1] "- %(count)s Ausgaben" -#: view.html:38 #, python-format -msgid "People: %(peoplelist)s" -msgstr "Personen: %(peoplelist)s" +#~ msgid "- 1 work" +#~ msgid_plural "- %(count)s works" +#~ msgstr[0] "- 1 Werk" +#~ msgstr[1] "- %(count)s Werke" + +#~ msgid "" +#~ "You are about the remove the last item in the list. That will delete the " +#~ "whole list. Are you sure you want to continue?" +#~ msgstr "" +#~ "Sie sind dabei, den letzten Eintrag von der Liste zu entfernen. Dadurch " +#~ "wird die gesamte Liste gelöscht. Sind Sie sicher, dass Sie fortfahren " +#~ "möchten?" -#: view.html:40 #, python-format -msgid "Times: %(timelist)s" -msgstr "Zeiten: %(timelist)s" +#~ msgid "- 1 edition in catalog" +#~ msgid_plural "- %(count)s editions in catalog" +#~ msgstr[0] "- 1 Ausgabe im Katalog" +#~ msgstr[1] "- %(count)s Ausgaben im Katalog" + +#~ msgid "Download DAISY eBook for print disabled" +#~ msgstr "Lade DAISY eBook mit gesperrtem Druck herunter " + +#~ msgid "Download DAISY" +#~ msgstr "DAISY herunterladen" -#: view.html:76 #, python-format -msgid "By %(author)s" -msgstr "Von %(author)s" +#~ msgid "" +#~ "This user has chosen to publicly share the books they are currently reading, have already read, and want to read!" +#~ msgstr "" +#~ "Diese Person hat sich entschieden die Bücher die sie aktuell liest, bereits " +#~ "gelesen hat und lesen möchte öffentlich zu " +#~ "teilen!" -#: view.html:78 -msgid "By unknown author" -msgstr "Von unbekannter Person" +#~ msgid "This user has chosen to keep their Reading Log private" +#~ msgstr "Diese Person hat sich entschieden ihr Lese-Logbuch privat zu halten" -#: view.html:82 -msgid "Go to the editions section to read or download ebooks." -msgstr "" -"Gehen Sie auf die Ausgaben-Seite, um ein Buch zu lesen oder ein eBook " -"herunterzuladen." +#~ msgid "Locate" +#~ msgstr "Lokalisieren" -#: books.html:15 nav_head.html:78 nav_head.html:79 -msgid "My Books" -msgstr "Meine Bücher" +#, python-format +#~ msgid "Merge editions" +#~ msgstr "Ausgaben zusammenführen" -#: books.html:77 #, python-format -msgid "Currently Reading (%(count)d)" -msgstr "Lesen aktuell (%(count)d)" +#~ msgid "%(title)s by %(author)s" +#~ msgstr "%(title)s von %(author)s" -#: books.html:78 #, python-format -msgid "Want to Read (%(count)d)" -msgstr "Möchten es lesen (%(count)d)" +#~ msgid "Subjects: %(subjectlist)s" +#~ msgstr "Themen: %(subjectlist)s" -#: books.html:79 #, python-format -msgid "Already Read (%(count)d)" -msgstr "Bereits gelesen (%(count)d)" +#~ msgid "Places: %(placelist)s" +#~ msgstr "Orte: %(placelist)s" -#: borrow.html:52 -msgid "" -"You have 5 ebooks out at the moment. That's the limit! You can free up slots by " -"returning books early." -msgstr "" -"Sie haben aktuell 5 eBooks entliehen. Das ist die Höchstzahl! Sie müssen Bücher " -"zurückgeben, um andere auszuleihen." +#, python-format +#~ msgid "People: %(peoplelist)s" +#~ msgstr "Personen: %(peoplelist)s" -#: borrow.html:85 #, python-format -msgid "%(count)d Current Loans" -msgstr "%(count)d derzeit entliehen" +#~ msgid "Times: %(timelist)s" +#~ msgstr "Zeiten: %(timelist)s" -#: borrow.html:140 -msgid "How To Return eBooks through Adobe Digital Editions" -msgstr "" +#, python-format +#~ msgid "By %(author)s" +#~ msgstr "Von %(author)s" -#: borrow.html:142 -msgid "Open Adobe Digital Editions" -msgstr "Adobe Digital Editions öffnen" +#~ msgid "By unknown author" +#~ msgstr "Von unbekannter Person" -#: borrow.html:143 -msgid "Click the Library icon at the upper left corner" -msgstr "" +#~ msgid "Go to the editions section to read or download ebooks." +#~ msgstr "" +#~ "Gehen Sie auf die Ausgaben-Seite, um ein Buch zu lesen oder ein eBook " +#~ "herunterzuladen." -#: borrow.html:144 -msgid "Move your mouse over the cover of the eBook you wish to return" -msgstr "" +#, python-format +#~ msgid "Currently Reading (%(count)d)" +#~ msgstr "Lesen aktuell (%(count)d)" -#: borrow.html:145 -msgid "Find the arrow at the upper left of the cover and open the eBook menu" -msgstr "" +#, python-format +#~ msgid "Want to Read (%(count)d)" +#~ msgstr "Möchten es lesen (%(count)d)" -#: borrow.html:146 -msgid "Click \"Return Borrowed Item\"" -msgstr "" +#, python-format +#~ msgid "Already Read (%(count)d)" +#~ msgstr "Bereits gelesen (%(count)d)" -#: borrow.html:147 -msgid "Click \"Return\" to confirm" -msgstr "" +#~ msgid "" +#~ "You have 5 ebooks out at the moment. That's the limit! You can free up " +#~ "slots by returning books early." +#~ msgstr "" +#~ "Sie haben aktuell 5 eBooks entliehen. Das ist die Höchstzahl! Sie müssen " +#~ "Bücher zurückgeben, um andere auszuleihen." -#: borrow.html:148 -msgid "Done!" -msgstr "Erledigt!" +#, python-format +#~ msgid "%(count)d Current Loans" +#~ msgstr "%(count)d derzeit entliehen" -#: borrow.html:250 -msgid "Internet Archive" -msgstr "Internet Archive" +#~ msgid "Open Adobe Digital Editions" +#~ msgstr "Adobe Digital Editions öffnen" -#: borrow.html:252 -msgid "eBooks everywhere" -msgstr "E-Books von anderen Anbietern" +#~ msgid "Done!" +#~ msgstr "Erledigt!" -#: borrow.html:274 -msgid "Which books are available?" -msgstr "Welche Bücher sind verfügbar?" +#~ msgid "Internet Archive" +#~ msgstr "Internet Archive" -#: borrow.html:280 -msgid "Help/Downloads" -msgstr "Hilfe/Downloads" +#~ msgid "eBooks everywhere" +#~ msgstr "E-Books von anderen Anbietern" -#: borrow.html:282 -msgid "Download Adobe Digital Editions" -msgstr "" +#~ msgid "Which books are available?" +#~ msgstr "Welche Bücher sind verfügbar?" -#: borrow.html:283 -msgid "" -"Help on " -"adobe.com" -msgstr "" +#~ msgid "Help/Downloads" +#~ msgstr "Hilfe/Downloads" -#: create.html:71 #, python-format -msgid "Your URL: %s" -msgstr "Ihre URL: %s" +#~ msgid "Your URL: %s" +#~ msgstr "Ihre URL: %s" -#: custom_carousel.html:34 -msgid "Sponsor" -msgstr "Sponsor" +#~ msgid "Sponsor" +#~ msgstr "Sponsor" -#: edit.html:149 -msgid "Delete this book?" -msgstr "Dieses Buch löschen?" +#~ msgid "Delete this book?" +#~ msgstr "Dieses Buch löschen?" -#: edition-sort.html:108 -msgid "Look for this edition at WorldCat" -msgstr "Suchen Sie nach dieser Ausgabe in der WorldCat" +#~ msgid "Look for this edition at WorldCat" +#~ msgstr "Suchen Sie nach dieser Ausgabe in der WorldCat" -#: edit/edition.html:161 -msgid "Is there a copyright date?" -msgstr "Gibt es ein Copyright-Datum?" +#~ msgid "Is there a copyright date?" +#~ msgstr "Gibt es ein Copyright-Datum?" -#: edit/edition.html:357 -msgid "Is there a By Statement?" -msgstr "Ist eine Angabe wie „geschrieben von“ vorhanden?" +#~ msgid "Is there a By Statement?" +#~ msgstr "Ist eine Angabe wie „geschrieben von“ vorhanden?" -#: edit/edition.html:740 -msgid "ID must be exactly 13 digits [0-9]." -msgstr "ID muss aus exakt 13 Ziffern bestehen." +#~ msgid "ID must be exactly 13 digits [0-9]." +#~ msgstr "ID muss aus exakt 13 Ziffern bestehen." -#: add.html:11 -msgid "There are two ways to place an author's image on Open Library" -msgstr "Es gibt zwei Wege, ein Autorenbild zur Open Library hinzuzufügen" +#~ msgid "There are two ways to place an author's image on Open Library" +#~ msgstr "Es gibt zwei Wege, ein Autorenbild zur Open Library hinzuzufügen" -#: add.html:14 add.html:20 -msgid "There are two ways to get a cover image on Open Library" -msgstr "" -"Es gibt zwei Wege, ein Titelbild (Cover) zur Open Library hinzuzufügen" - -#: nav_foot.html:35 -msgid "Explore Open Library Development Center" -msgstr "Entdecke das Open Library Entwicklungszentrum" +#~ msgid "There are two ways to get a cover image on Open Library" +#~ msgstr "" +#~ "Es gibt zwei Wege, ein Titelbild (Cover) zur Open Library hinzuzufügen" -#: nav_foot.html:35 -msgid "Development Center" -msgstr "Entwicklungszentrum" +#~ msgid "Explore Open Library Development Center" +#~ msgstr "Entdecke das Open Library Entwicklungszentrum" -#: nav_head.html:116 -msgid "My Account" -msgstr "Mein Konto" +#~ msgid "Development Center" +#~ msgstr "Entwicklungszentrum" -#: preview.html:40 -msgid "Themes:" -msgstr "Themen:" +#~ msgid "Themes:" +#~ msgstr "Themen:" -#: snippet.html:9 widget.html:81 #, python-format -msgid "Cover of: %(listname)s" -msgstr "Buchdeckel von: %(listname)s" +#~ msgid "Cover of: %(listname)s" +#~ msgstr "Buchdeckel von: %(listname)s" -#: authors.html:58 -msgid "Select author records which should be merged with the master" -msgstr "Autoreneinträge wählen, die mit dem Master zusammengeführt werden sollen" +#~ msgid "Select author records which should be merged with the master" +#~ msgstr "" +#~ "Autoreneinträge wählen, die mit dem Master zusammengeführt werden sollen" -#: authors.html:66 -msgid "No Primary record" -msgstr "Kein primärer Eintrag" +#~ msgid "No Primary record" +#~ msgstr "Kein primärer Eintrag" -#: editions.html:7 editions.html:10 editions2.html:7 #, python-format -msgid "Merge Editions" -msgstr "Ausgaben zusammenführen" +#~ msgid "Merge Editions" +#~ msgstr "Ausgaben zusammenführen" -#: editions.html:17 -msgid "No editions selected." -msgstr "Keine Ausgaben ausgewählt." - -#: editions2.html:10 -#, python-format -msgid "Merging %(count)d editions of" -msgstr "Zusammenführen von %(count)d Ausgaben von" +#~ msgid "No editions selected." +#~ msgstr "Keine Ausgaben ausgewählt." -#: editions2.html:43 -msgid "Merge Preview" -msgstr "Vorschau" +#~ msgid "Merge Preview" +#~ msgstr "Vorschau" -#: editions2.html:45 -msgid "Please review the merged values before saving." -msgstr "Bitte überprüfen sie die Werte für die Zusammenführung vor dem Speichern." +#~ msgid "Please review the merged values before saving." +#~ msgstr "" +#~ "Bitte überprüfen sie die Werte für die Zusammenführung vor dem Speichern." -#: history.html:8 history.html:12 -msgid "Merge History" -msgstr "Verlauf" +#~ msgid "Merge History" +#~ msgstr "Verlauf" -#: advancedsearch.html:11 -msgid "Keyword" -msgstr "Schlüsselwort" +#~ msgid "Keyword" +#~ msgstr "Schlüsselwort" -#: body.html:31 -msgid "Together, let's build an Open Library for the World." -msgstr "" -"Lass uns zusammen eine offene Bibliothek für die Welt bauen." +#~ msgid "" +#~ "Together, let's build an Open Library for the World." +#~ msgstr "" +#~ "Lass uns zusammen eine offene Bibliothek für die Welt " +#~ "bauen." -#: openlibrary/templates/work_search.html:220 #, python-format -msgid "Showing books with similar text to: %(query)s" -msgstr "Zeige Bücher mit ähnlichem Text an: %(query)s" +#~ msgid "Showing books with similar text to: %(query)s" +#~ msgstr "Zeige Bücher mit ähnlichem Text an: %(query)s" -#: openlibrary/macros/AvailabilityButton.html:50 -msgid "You're next in line" -msgstr "Sie sind der nächste in der Warteliste" +#~ msgid "You're next in line" +#~ msgstr "Sie sind der nächste in der Warteliste" -#: openlibrary/macros/AvailabilityButton.html:52 #, python-format -msgid "There is #1 reader ahead of you" -msgid_plural "There are #%(count)d readers ahead of you" -msgstr[0] "Es befindet sich 1 Leser vor Ihnen in der Warteliste." -msgstr[1] "" -"Es befinden sich %(count)d Leser vor Ihnen in der Warteliste." +#~ msgid "There is #1 reader ahead of you" +#~ msgid_plural "There are #%(count)d readers ahead of you" +#~ msgstr[0] "" +#~ "Es befindet sich 1 Leser vor Ihnen in der Warteliste." +#~ msgstr[1] "" +#~ "Es befinden sich %(count)d Leser vor Ihnen in der " +#~ "Warteliste." -#: openlibrary/macros/AvailabilityButton.html:68 -msgid "Be first in line!" -msgstr "Seien Sie der Erste in der Warteliste!" +#~ msgid "Be first in line!" +#~ msgstr "Seien Sie der Erste in der Warteliste!" -#: openlibrary/macros/AvailabilityButton.html:76 -msgid "Print-disabled access available" -msgstr "Mit gesperrtem Druck verfügbar" +#~ msgid "Print-disabled access available" +#~ msgstr "Mit gesperrtem Druck verfügbar" -#: openlibrary/macros/LoanStatus.html:73 #, python-format -msgid "Readers waiting for this title: %(count)d" -msgstr "Auf diesen Titel warten: %(count)d Personen" +#~ msgid "Readers waiting for this title: %(count)d" +#~ msgstr "Auf diesen Titel warten: %(count)d Personen" -#: openlibrary/macros/LoanStatus.html:87 #, python-format -msgid "Sponsor eBook" -msgstr "eBook sponsern" +#~ msgid "Sponsor eBook" +#~ msgstr "eBook sponsern" -#: openlibrary/macros/LoanStatus.html:89 #, python-format -msgid "" -"We don't have this book yet. You can add it to our Lending Library with a " -"%(price)s tax deductible donation." -msgstr "" -"Dieses Buch haben wir noch nicht. Sie können es mit einer %(price)s " -"abzugsfähigen Spende unserer Leihbibliothek hinzufügen." +#~ msgid "" +#~ "We don't have this book yet. You can add it to our Lending Library with a " +#~ "%(price)s tax deductible donation." +#~ msgstr "" +#~ "Dieses Buch haben wir noch nicht. Sie können es mit einer %(price)s " +#~ "abzugsfähigen Spende unserer Leihbibliothek hinzufügen." -#: openlibrary/macros/SearchResultsWork.html:44 #, python-format -msgid "- first published in %(year)s" -msgstr "Zuerst veröffentlicht %(year)s" +#~ msgid "- first published in %(year)s" +#~ msgstr "Zuerst veröffentlicht %(year)s" #~ msgid "" -#~ "We've sent an email to %(email)s. You'll need to read that and click on the " -#~ "verification link to update your email." +#~ "We've sent an email to %(email)s. You'll need to read that and click on " +#~ "the verification link to update your email." #~ msgstr "" #~ "Wir haben eine E-Mail an %(email)s verschickt. Bitte lesen Sie diese und " -#~ "klicken sie auf den Bestätigungs-Link, um ihre E-Mail-Adresse zu aktualisieren." +#~ "klicken sie auf den Bestätigungs-Link, um ihre E-Mail-Adresse zu " +#~ "aktualisieren." #~ msgid "Your password has been updated successfully." #~ msgstr "Ihr Password wurde erfolgreich geändert." #~ msgid "Only letters and numbers, please, and at least 3 characters." #~ msgstr "" -#~ "Bitte mindestens 3 Zeichen eingeben und nur Buchstaben und Ziffern verwenden." +#~ "Bitte mindestens 3 Zeichen eingeben und nur Buchstaben und Ziffern " +#~ "verwenden." #~ msgid "Current password" #~ msgstr "Aktuelles Passwort" @@ -5687,31 +6307,32 @@ msgstr "Zuerst veröffentlicht %(year)s" #~ msgid "Your new email address" #~ msgstr "Ihre neue E-Mail-Adresse" -#~ msgid "There are 2 different sources for borrowing books through Open Library:" +#~ msgid "" +#~ "There are 2 different sources for borrowing books through Open Library:" #~ msgstr "" #~ "Es gibt 2 verschiedene Quellen, von denen man Bücher über Open Library " #~ "entleihen kann:" #~ msgid "" #~ "The Internet Archive and several partner " -#~ "libraries are offering thousands of eBooks for loan to anyone with an Open " -#~ "Library account, anywhere in the world." +#~ "libraries are offering thousands of eBooks for loan to anyone with an " +#~ "Open Library account, anywhere in the world." #~ msgstr "" #~ "Das Internet Archive und mehrere " -#~ "Partnerbibliotheken bieten tausende E-Books zur Ausleihe an für jede*n mit " -#~ "einem Open Library-Account, überall auf der Welt." +#~ "Partnerbibliotheken bieten tausende E-Books zur Ausleihe an für jede*n " +#~ "mit einem Open Library-Account, überall auf der Welt." #~ msgid "physical books from your local library" #~ msgstr "gedruckte Bücher aus deiner örtlichen Bibliothek" #~ msgid "" -#~ "We connect our records to WorldCat wherever possible. You can tell WorldCat " -#~ "your postcode/zip code, and it will tell you the closest library with a copy " -#~ "of the book." +#~ "We connect our records to WorldCat wherever possible. You can tell " +#~ "WorldCat your postcode/zip code, and it will tell you the closest library " +#~ "with a copy of the book." #~ msgstr "" #~ "Wir verbinden unsere Daten mit WorldCat, wo immer es geht. Sie können bei " -#~ "WorldCat ihre Postleitzahl eingeben und es wird Ihnen die nächste Bibliothek " -#~ "anzeigen, die eine Kopie des Buches besitzt." +#~ "WorldCat ihre Postleitzahl eingeben und es wird Ihnen die nächste " +#~ "Bibliothek anzeigen, die eine Kopie des Buches besitzt." #~ msgid "Getting Started" #~ msgstr "Einführung" @@ -5720,53 +6341,53 @@ msgstr "Zuerst veröffentlicht %(year)s" #~ "All you need to borrow a book scanned at the Internet Archive is an Open " #~ "Library account and Adobe Digital Editions." #~ msgstr "" -#~ "Um ein durch das Internet Archive digitalisiertes Buch auszuleihen, brauchen " -#~ "sie nur ein Open-Library-Konto und Adobe Digital Editions." +#~ "Um ein durch das Internet Archive digitalisiertes Buch auszuleihen, " +#~ "brauchen sie nur ein Open-Library-Konto und Adobe Digital Editions." #~ msgid "" -#~ "Loans through WorldCat are managed through your local libraries, not through " -#~ "Open Library." +#~ "Loans through WorldCat are managed through your local libraries, not " +#~ "through Open Library." #~ msgstr "" -#~ "Ausleihen über WorldCat werden über Ihre örtlichen Bibliotheken verwaltet, " -#~ "nicht durch Open Library." +#~ "Ausleihen über WorldCat werden über Ihre örtlichen Bibliotheken " +#~ "verwaltet, nicht durch Open Library." #~ msgid "" -#~ "Check out the Lending FAQ for more information." +#~ "Check out the Lending " +#~ "FAQ for more information." #~ msgstr "" -#~ "Lesen Sie die Verleih-FAQ um mehr zu erfahren." +#~ "Lesen Sie die Verleih-" +#~ "FAQ um mehr zu erfahren." #~ msgid "" -#~ "Browse all the available books through the \"Lending Library\" subject, or try a search." +#~ "Browse all the available books through the \"Lending Library\" subject, or try a search." #~ msgstr "" #~ "Stöbern Sie über das \"Verleih-" -#~ "Verzeichnis\" durch alle verfügbaren Bücher, oder nutzen Sie die Suche." +#~ "Verzeichnis\" durch alle verfügbaren Bücher, oder nutzen Sie die Suche." #~ msgid "" -#~ "If you work at a library and are interested to find out more about lending " -#~ "ebooks through Open Library, please get in touch with " -#~ "us!" +#~ "If you work at a library and are interested to find out more about " +#~ "lending ebooks through Open Library, please get in " +#~ "touch with us!" #~ msgstr "" #~ "Wenn Sie in einer Bibliothek arbeiten und mehr Informationen über die " -#~ "Ausleihe von E-Books mit Open Library wissen möchten, kontaktieren sie uns gerne (am besten in Englisch)!" +#~ "Ausleihe von E-Books mit Open Library wissen möchten, kontaktieren sie uns gerne (am besten in Englisch)!" #~ msgid "Please type in the text or number(s) below." #~ msgstr "Bitte geben sie den Text oder die Ziffer(n) nachfolgend ein." #~ msgid "" #~ "I acknowledge that my use of Open Library is subject to the Internet " -#~ "Archive's Terms of Use." +#~ "Archive's Terms of Use." #~ msgstr "" #~ "Ich erkenne an, dass meine Nutzung von Open Library unter die Nutzungsbedingungen (AGB)\" " -#~ "des Internet Archives fallen." +#~ "Nutzungsbedingungen in einem neuen Fenster.'>Nutzungsbedingungen (AGB)\" des Internet Archives fallen." #~ msgid "Change Your Email Address" #~ msgstr "E-Mail-Adresse ändern" @@ -5781,8 +6402,8 @@ msgstr "Zuerst veröffentlicht %(year)s" #~ "Provide your existing password (to verify that it's you) and select a new " #~ "password to replace it." #~ msgstr "" -#~ "Geben Sie ihr aktuelles Passwort ein (um ihre Identität zu prüfen) und wählen " -#~ "Sie ein neues Passwort um das alte zu ersetzen." +#~ "Geben Sie ihr aktuelles Passwort ein (um ihre Identität zu prüfen) und " +#~ "wählen Sie ein neues Passwort um das alte zu ersetzen." #~ msgid "Change" #~ msgstr "Ändern" @@ -5800,7 +6421,8 @@ msgstr "Zuerst veröffentlicht %(year)s" #~ msgstr "Vielleicht ein paar Schlagworte?" #~ msgid "There are two ways to get a cover image on Open Library:" -#~ msgstr "Es gibt zwei Möglichkeiten ein Titelbild zur Open Library hinzuzufügen:" +#~ msgstr "" +#~ "Es gibt zwei Möglichkeiten ein Titelbild zur Open Library hinzuzufügen:" #~ msgid "Either choose a JPG, GIF or PNG on your computer," #~ msgstr "Wählen Sie ein JPG, GIF oder PNG von Ihrem Computer aus," @@ -5809,8 +6431,8 @@ msgstr "Zuerst veröffentlicht %(year)s" #~ "Or, paste in a URL to an image if " #~ "it's on the web." #~ msgstr "" -#~ "Oder fügen Sie den Link zum Bild ein, " -#~ "wenn es im Internet verfügbar ist." +#~ "Oder fügen Sie den Link zum Bild " +#~ "ein, wenn es im Internet verfügbar ist." #~ msgid "Upload" #~ msgstr "Hochladen" @@ -5822,7 +6444,9 @@ msgstr "Zuerst veröffentlicht %(year)s" #~ msgstr "Ist das Buch Teil einer Reihe?" #~ msgid "There are occasionally title variants amongst editions." -#~ msgstr "Gelegentlich besitzen unterschiedliche Ausgaben eines Buchs verschiedene Titel." +#~ msgstr "" +#~ "Gelegentlich besitzen unterschiedliche Ausgaben eines Buchs verschiedene " +#~ "Titel." #~ msgid "Perhaps in another language?" #~ msgstr "Vielleicht in einer anderen Sprache?" @@ -5878,36 +6502,34 @@ msgstr "Zuerst veröffentlicht %(year)s" #~ msgstr "Fantasy" #~ msgid "" -#~ "The Open Library website has not been optimized for Internet Explorer 6, so " -#~ "some features and graphic elements may not appear correctly. Many sites, " -#~ "including Google and Facebook, have phased " -#~ "out support for IE6 due to security and support issues. Please consider " -#~ "upgrading to Internet Explorer 9, Firefox, Chrome, Safari, or Opera to use this and " -#~ "other web sites to your fullest advantage." +#~ "The Open Library website has not been optimized for Internet Explorer 6, " +#~ "so some features and graphic elements may not appear correctly. Many " +#~ "sites, including Google and Facebook, " +#~ "have phased out support for IE6 due to security and support issues. " +#~ "Please consider upgrading to Internet Explorer 9, Firefox, Chrome, Safari, or Opera to use this and other web sites to your fullest " +#~ "advantage." #~ msgstr "" #~ "Die Open Library Webseite ist nicht für den Internet Explorer 6 optimiert " -#~ "worden. Daher könnten manche Funktionen und grafische Elemente nicht korrekt " -#~ "angezeigt werden. Viele Seiten, einschließlich Google und Facebook, haben die Unterstützung für den IE6 aus " -#~ "Gründen der Sicherheit und des technischen Pflegebedarfs aufgegeben. Bitte " -#~ "erwägen Sie einen Wechsel auf Internet Explorer 9, Firefox, Chrome, Safari, oder Opera um diese und andere Webseiten in vollem Umfang nutzen zu " -#~ "können." +#~ "worden. Daher könnten manche Funktionen und grafische Elemente nicht " +#~ "korrekt angezeigt werden. Viele Seiten, einschließlich Google und Facebook, haben die Unterstützung für " +#~ "den IE6 aus Gründen der Sicherheit und des technischen Pflegebedarfs " +#~ "aufgegeben. Bitte erwägen Sie einen Wechsel auf Internet Explorer 9, Firefox, Chrome, Safari, oder " +#~ "Opera um diese und andere Webseiten " +#~ "in vollem Umfang nutzen zu können." #~ msgid "Clear this selection" #~ msgstr "Auswahl entfernen" -#~ msgid "published in" -#~ msgstr "herausgegeben" - #~ msgid "published between" #~ msgstr "herausgegeben zwischen" @@ -5927,17 +6549,8 @@ msgstr "Zuerst veröffentlicht %(year)s" #~ "Oder fügen Sie den Link zum Bild ein, wenn es im Internet " #~ "verfügbar ist." -#~ msgid "Librarian Mode" -#~ msgstr "Bibliothekar*innen-Modus" - #~ msgid "Full-Text Search on Archive.org" #~ msgstr "Volltextsuche auf Archive.org" -#~ msgid "More Subjects" -#~ msgstr "Mehr Themen" - #~ msgid "editions in" #~ msgstr "Ausgaben in" - -#~ msgid "Search Results" -#~ msgstr "Suchergebnisse" From 3b8a77a97c40187022c6e12a6598634133c53b38 Mon Sep 17 00:00:00 2001 From: Ayush Kadam <59080746+Ayush1404@users.noreply.github.com> Date: Mon, 29 Jan 2024 15:26:34 +0530 Subject: [PATCH 39/61] Fixed My Notes Fixes #8771 , Going to page 2 of notes breaks because the page is a string instead of an int so added one line to convert string to int. --- openlibrary/plugins/upstream/mybooks.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openlibrary/plugins/upstream/mybooks.py b/openlibrary/plugins/upstream/mybooks.py index cda18a789e3..004551cf4ba 100644 --- a/openlibrary/plugins/upstream/mybooks.py +++ b/openlibrary/plugins/upstream/mybooks.py @@ -93,7 +93,7 @@ def GET(self, username): i = web.input(page=1) mb = MyBooksTemplate(username, key='notes') if mb.is_my_page: - docs = PatronBooknotes(mb.user).get_notes(page=i.page) + docs = PatronBooknotes(mb.user).get_notes(page=int(i.page)) template = render['account/notes']( docs, mb.user, mb.counts['notes'], page=i.page ) From 5c5166bca2787d6965ccbf86f4b678e6fc2929f4 Mon Sep 17 00:00:00 2001 From: Ayush Date: Mon, 29 Jan 2024 20:31:02 +0530 Subject: [PATCH 40/61] fixed my notes and also fixed my reviews --- openlibrary/plugins/upstream/mybooks.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openlibrary/plugins/upstream/mybooks.py b/openlibrary/plugins/upstream/mybooks.py index 004551cf4ba..ea2d3af6c36 100644 --- a/openlibrary/plugins/upstream/mybooks.py +++ b/openlibrary/plugins/upstream/mybooks.py @@ -108,9 +108,9 @@ def GET(self, username): i = web.input(page=1) mb = MyBooksTemplate(username, key='observations') if mb.is_my_page: - docs = PatronBooknotes(mb.user).get_observations(page=i.page) + docs = PatronBooknotes(mb.user).get_observations(page=int(i.page)) template = render['account/observations']( - docs, mb.user, mb.counts['observations'], page=i.page + docs, mb.user, mb.counts['observations'], page=int(i.page) ) return mb.render(header_title=_("Reviews"), template=template) raise web.seeother(mb.user.key) From 308312d5c37fca08bcd3d3f92a97c0a9ab37a293 Mon Sep 17 00:00:00 2001 From: jimchamp Date: Mon, 29 Jan 2024 12:43:57 -0800 Subject: [PATCH 41/61] Add support for authenticated requests to the comment digest script (#8761) * Add support for authenticated requests Adds authorization token to GitHub request headers, iff a token is found in the environment. Also adds API version and Accept headers. --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .github/workflows/new_comment_digest.yml | 1 + scripts/gh_scripts/issue_comment_bot.py | 22 +++++++++++++++++----- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/.github/workflows/new_comment_digest.yml b/.github/workflows/new_comment_digest.yml index ce72caf6430..b586ab550b7 100644 --- a/.github/workflows/new_comment_digest.yml +++ b/.github/workflows/new_comment_digest.yml @@ -17,5 +17,6 @@ jobs: - run: pip install requests - run: scripts/gh_scripts/issue_comment_bot.py 24 "$SLACK_CHANNEL" "$SLACK_TOKEN" env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} SLACK_TOKEN: ${{ secrets.SLACK_TOKEN }} SLACK_CHANNEL: ${{ secrets.SLACK_CHANNEL_ABC_TEAM_PLUS }} diff --git a/scripts/gh_scripts/issue_comment_bot.py b/scripts/gh_scripts/issue_comment_bot.py index 7aa463ba134..72c3192c5f8 100755 --- a/scripts/gh_scripts/issue_comment_bot.py +++ b/scripts/gh_scripts/issue_comment_bot.py @@ -7,6 +7,7 @@ """ import argparse import errno +import os import sys import time @@ -34,6 +35,11 @@ 'hornc': '<@U0EUS8DV0>', } +github_headers = { + 'X-GitHub-Api-Version': '2022-11-28', + 'Accept': 'application/vnd.github+json', +} + def fetch_issues(updated_since: str): """ @@ -53,8 +59,7 @@ def fetch_issues(updated_since: str): 'per_page': 100, } response = requests.get( - 'https://api.github.com/search/issues', - params=p, + 'https://api.github.com/search/issues', params=p, headers=github_headers ) d = response.json() results = d['items'] @@ -62,7 +67,7 @@ def fetch_issues(updated_since: str): # Fetch additional updated issues, if any exist def get_next_page(url: str): """Returns list of issues and optional url for next page""" - resp = requests.get(url) + resp = requests.get(url, headers=github_headers) # Get issues d = resp.json() issues = d['items'] @@ -93,7 +98,9 @@ def filter_issues(issues: list, since: datetime): for i in issues: # Fetch comments using URL from previous GitHub search results comments_url = i.get('comments_url') - resp = requests.get(comments_url, params={'per_page': 100}) + resp = requests.get( + comments_url, params={'per_page': 100}, headers=github_headers + ) # Ensure that we have the last page of comments links = resp.links @@ -101,7 +108,7 @@ def filter_issues(issues: list, since: datetime): last_url = last.get('url', '') if last_url: - resp = requests.get(last_url) + resp = requests.get(last_url, headers=github_headers) # Get last comment comments = resp.json() @@ -280,4 +287,9 @@ def _get_parser() -> argparse.ArgumentParser: # Process command-line arguments and starts the notification job parser = _get_parser() args = parser.parse_args() + + # If found, add token to GitHub request headers: + github_token = os.environ.get('GITHUB_TOKEN', '') + if github_token: + github_headers['Authorization'] = f'Bearer {github_token}' start_job(args) From 87cc320773021a2428017bebca2614cf4892327a Mon Sep 17 00:00:00 2001 From: Drini Cami Date: Fri, 19 Jan 2024 02:48:06 -0500 Subject: [PATCH 42/61] Add per-key caching to get_availability Also: - DRY methods that call get_availability - Add more types to availability data structures - Add support for memcache's get/set_multi - Decrease get_availability timeout to 10s instead of 30s --- openlibrary/core/cache.py | 75 +++++---- openlibrary/core/lending.py | 194 ++++++++++++++++-------- openlibrary/plugins/admin/code.py | 2 +- openlibrary/plugins/openlibrary/api.py | 13 +- openlibrary/plugins/openlibrary/code.py | 40 +++-- openlibrary/plugins/upstream/models.py | 15 -- 6 files changed, 189 insertions(+), 150 deletions(-) diff --git a/openlibrary/core/cache.py b/openlibrary/core/cache.py index 753a977df07..a1edb5d2cd9 100644 --- a/openlibrary/core/cache.py +++ b/openlibrary/core/cache.py @@ -5,7 +5,7 @@ import time import threading import functools -from typing import Literal +from typing import Any, Literal, cast from collections.abc import Callable import memcache @@ -334,15 +334,32 @@ def memcache(self): return MockMemcacheClient() - def get(self, key): - key = web.safestr(key) + def _encode_key(self, key: str) -> str: + return cast(str, web.safestr(key)) + + def get(self, key: str) -> Any: + key = self._encode_key(key) stats.begin("memcache.get", key=key) value = self.memcache.get(key) stats.end(hit=value is not None) return value and json.loads(value) - def set(self, key, value, expires=0): - key = web.safestr(key) + def get_multi(self, keys: list[str]) -> dict[str, Any]: + keys = [self._encode_key(k) for k in keys] + stats.begin("memcache.get_multi") + d = self.memcache.get_multi(keys) + stats.end(hit=bool(d)) + return {k: json.loads(v) for k, v in d.items()} + + def set_multi(self, mapping: dict[str, Any], expires=0): + mapping = {self._encode_key(k): json.dumps(v) for k, v in mapping.items()} + stats.begin("memcache.set_multi") + d = self.memcache.set_multi(mapping, expires) + stats.end() + return d + + def set(self, key: str, value: Any, expires=0): + key = cast(str, web.safestr(key)) value = json.dumps(value) stats.begin("memcache.set", key=key) value = self.memcache.set(key, value, expires) @@ -394,7 +411,7 @@ def delete(self, key): def get_memcache(): - return memcache_cache.memcache + return memcache_cache def _get_cache(engine): @@ -464,23 +481,19 @@ def get_page(key): def __init__( self, - engine: Literal["memory", "memcache", "request"] = "memory", - key=None, + engine: Literal["memory", "memcache", "request"], + key: str | Callable[..., str | tuple], expires: int = 0, background: bool = False, cacheable: Callable | None = None, ): self.cache = _get_cache(engine) - self.keyfunc = self._make_key_func(key) + self.keyfunc = ( + key if callable(key) else functools.partial(build_memcache_key, key) + ) self.cacheable = cacheable self.expires = expires - def _make_key_func(self, key): - if isinstance(key, str): - return PrefixKeyFunc(key) - else: - return key - def __call__(self, f): """Returns the memoized version of f.""" @@ -544,33 +557,15 @@ def cache_set(self, key: str | tuple, value): return self.cache.set(key, value, expires=self.expires) -class PrefixKeyFunc: - """A function to generate cache keys using a prefix and arguments.""" - - def __init__(self, prefix): - self.prefix = prefix +def build_memcache_key(prefix: str, *args, **kw) -> str: + key = prefix - def __call__(self, *a, **kw): - return self.prefix + "-" + self.encode_args(a, kw) + if args: + key += "-" + json.dumps(args, separators=(",", ":"), sort_keys=True)[1:-1] + if kw: + key += "-" + json.dumps(kw, separators=(",", ":"), sort_keys=True) - def encode_args(self, args, kw=None): - kw = kw or {} - """Encodes arguments to construct the memcache key. - """ - # strip [ and ] from key - a = self.json_encode(list(args))[1:-1] - - if kw: - return a + "-" + self.json_encode(kw) - else: - return a - - def json_encode(self, value): - """json.dumps without extra spaces and consistent ordering of dictionary keys. - - memcache doesn't like spaces in the key. - """ - return json.dumps(value, separators=(",", ":"), sort_keys=True) + return key def method_memoize(f): diff --git a/openlibrary/core/lending.py b/openlibrary/core/lending.py index f59df48a475..9167ec8851d 100644 --- a/openlibrary/core/lending.py +++ b/openlibrary/core/lending.py @@ -1,6 +1,6 @@ """Module for providing core functionality of lending on Open Library. """ -from typing import Literal +from typing import Literal, TypedDict, cast import web import datetime @@ -50,7 +50,7 @@ def __init__(self, message="Access to this item is temporarily locked."): config_ia_loan_api_url = None config_ia_xauth_api_url = None -config_ia_availability_api_v2_url = None +config_ia_availability_api_v2_url = cast(str, None) config_ia_access_secret = None config_ia_domain = None config_ia_ol_shared_key = None @@ -80,7 +80,9 @@ def setup(config): config_bookreader_host = config.get('bookreader_host', 'archive.org') config_ia_domain = config.get('ia_base_url', 'https://archive.org') config_ia_loan_api_url = config.get('ia_loan_api_url') - config_ia_availability_api_v2_url = config.get('ia_availability_api_v2_url') + config_ia_availability_api_v2_url = cast( + str, config.get('ia_availability_api_v2_url') + ) config_ia_xauth_api_url = config.get('ia_xauth_api_url') config_ia_access_secret = config.get('ia_access_secret') config_ia_ol_shared_key = config.get('ia_ol_shared_key') @@ -284,74 +286,148 @@ def get_available( return {'error': 'request_timeout'} -def get_availability(key: str, ids: list[str]) -> dict: +class AvailabilityStatus(TypedDict): + status: Literal["borrow_available", "borrow_unavailable", "open", "error"] + error_message: str | None + available_to_browse: bool | None + available_to_borrow: bool | None + available_to_waitlist: bool | None + is_printdisabled: bool | None + is_readable: bool | None + is_lendable: bool | None + is_previewable: bool + + identifier: str | None + isbn: str | None + oclc: str | None + openlibrary_work: str | None + openlibrary_edition: str | None + + last_loan_date: str | None + """e.g. 2020-07-31T19:07:55Z""" + + num_waitlist: str | None + """A number represented inexplicably as a string""" + + last_waitlist_date: str | None + """e.g. 2020-07-31T19:07:55Z""" + + +class AvailabilityServiceResponse(TypedDict): + success: bool + responses: dict[str, AvailabilityStatus] + + +class AvailabilityStatusV2(AvailabilityStatus): + is_restricted: bool + is_browseable: bool | None + __src__: str + + +def update_availability_schema_to_v2( + v1_resp: AvailabilityStatus, + ocaid: str | None, +) -> AvailabilityStatusV2: """ - :param str key: the type of identifier - :param list of str ids: + This function attempts to take the output of e.g. Bulk Availability + API and add/infer attributes which are missing (but are present on + Ground Truth API) """ + v2_resp = cast(AvailabilityStatusV2, v1_resp) + # TODO: Make less brittle; maybe add simplelists/copy counts to Bulk Availability + v2_resp['identifier'] = ocaid + v2_resp['is_restricted'] = v1_resp['status'] != 'open' + v2_resp['is_browseable'] = v1_resp.get('available_to_browse', False) + # For debugging + v2_resp['__src__'] = 'core.models.lending.get_availability' + return v2_resp + + +def get_availability( + id_type: Literal['identifier', 'openlibrary_work', 'openlibrary_edition'], + ids: list[str], +) -> dict[str, AvailabilityStatusV2]: ids = [id_ for id_ in ids if id_] # remove infogami.infobase.client.Nothing if not ids: return {} - def update_availability_schema_to_v2(v1_resp, ocaid): - """This functionattempts to take the output of e.g. Bulk Availability - API and add/infer attributes which are missing (but are - present on Ground Truth API) - """ - # TODO: Make less brittle; maybe add simplelists/copy counts to Bulk Availability - v1_resp['identifier'] = ocaid - v1_resp['is_restricted'] = v1_resp['status'] != 'open' - v1_resp['is_browseable'] = v1_resp.get('available_to_browse', False) - # For debugging - v1_resp['__src__'] = 'core.models.lending.get_availability' - return v1_resp - - url = '{}?{}={}'.format(config_ia_availability_api_v2_url, key, ','.join(ids)) + def key_func(_id: str) -> str: + return cache.build_memcache_key('lending.get_availability', id_type, _id) + + mc = cache.get_memcache() + + cached_values = cast( + dict[str, AvailabilityStatusV2], mc.get_multi([key_func(_id) for _id in ids]) + ) + availabilities = { + _id: cached_values[key] + for _id in ids + if (key := key_func(_id)) in cached_values + } + ids_to_fetch = set(ids) - set(availabilities) + + if not ids_to_fetch: + return availabilities + try: - client_ip = web.ctx.env.get('HTTP_X_FORWARDED_FOR', 'ol-internal') - params = {"scope": "printdisabled"} headers = { - "x-preferred-client-id": client_ip, + "x-preferred-client-id": web.ctx.env.get( + 'HTTP_X_FORWARDED_FOR', 'ol-internal' + ), "x-application-id": "openlibrary", } if config_ia_ol_metadata_write_s3: headers["authorization"] = "LOW {s3_key}:{s3_secret}".format( **config_ia_ol_metadata_write_s3 ) - - # Make authenticated request to Bulk Availability API - response = requests.get( - url, params=params, headers=headers, timeout=config_http_request_timeout + response = cast( + AvailabilityServiceResponse, + requests.get( + config_ia_availability_api_v2_url, + params={ + id_type: ','.join(ids_to_fetch), + "scope": "printdisabled", + }, + headers=headers, + timeout=10, + ).json(), ) - - items = response.json().get('responses', {}) - for pkey in items: - ocaid = pkey if key == 'identifier' else items[pkey].get('identifier') - items[pkey] = update_availability_schema_to_v2(items[pkey], ocaid) - return items + uncached_values = { + _id: update_availability_schema_to_v2( + availability, + ocaid=( + _id if id_type == 'identifier' else availability.get('identifier') + ), + ) + for _id, availability in response['responses'].items() + } + availabilities |= uncached_values + mc.set_multi( + { + key_func(_id): availability + for _id, availability in uncached_values.items() + }, + expires=5 * dateutil.MINUTE_SECS, + ) + return availabilities except Exception as e: # TODO: Narrow exception scope - logger.exception("get_availability(%s)" % url) - items = {'error': 'request_timeout', 'details': str(e)} - - for pkey in ids: - # key could be isbn, ocaid, or openlibrary_[work|edition] - ocaid = pkey if key == 'identifier' else None - items[pkey] = update_availability_schema_to_v2({'status': 'error'}, ocaid) - return items - - -def get_edition_availability(ol_edition_id): - return get_availability_of_editions([ol_edition_id]) - - -def get_availability_of_editions(ol_edition_ids): - """Given a list of Open Library edition IDs, returns a list of - Availability v2 results. - """ - return get_availability('openlibrary_edition', ol_edition_ids) + logger.exception("lending.get_availability", extra={'ids': ids}) + availabilities.update( + { + _id: update_availability_schema_to_v2( + cast(AvailabilityStatus, {'status': 'error'}), + ocaid=_id if id_type == 'identifier' else None, + ) + for _id in ids_to_fetch + } + ) + return availabilities | { + 'error': 'request_timeout', + 'details': str(e), + } # type:ignore -def get_ocaid(item): +def get_ocaid(item: dict) -> str | None: # Circular import otherwise from ..book_providers import is_non_ia_ocaid @@ -415,7 +491,7 @@ def add_availability( item['availability'] = availabilities.get(ocaid) elif mode == "openlibrary_work": _ids = [item['key'].split('/')[-1] for item in items] - availabilities = get_availability_of_works(_ids) + availabilities = get_availability('openlibrary_work', _ids) for item in items: olid = item['key'].split('/')[-1] if olid: @@ -423,7 +499,6 @@ def add_availability( return items -@public def get_availability_of_ocaid(ocaid): """Retrieves availability based on ocaid/archive.org identifier""" return get_availability('identifier', [ocaid]) @@ -436,15 +511,6 @@ def get_availability_of_ocaids(ocaids: list[str]) -> dict: return get_availability('identifier', ocaids) -@public -def get_work_availability(ol_work_id): - return get_availability_of_works([ol_work_id]) - - -def get_availability_of_works(ol_work_ids): - return get_availability('openlibrary_work', ol_work_ids) - - def is_loaned_out(identifier): """Returns True if the given identifier is loaned out. diff --git a/openlibrary/plugins/admin/code.py b/openlibrary/plugins/admin/code.py index 8b20f988af0..f8650e0633f 100644 --- a/openlibrary/plugins/admin/code.py +++ b/openlibrary/plugins/admin/code.py @@ -785,7 +785,7 @@ def GET_memcache(self): i = web.input(action="read") i.setdefault("keys", "") - mc = cache.get_memcache() + mc = cache.get_memcache().memcache keys = [k.strip() for k in i["keys"].split() if k.strip()] if i.action == "delete": diff --git a/openlibrary/plugins/openlibrary/api.py b/openlibrary/plugins/openlibrary/api.py index 8532ae31742..9685a91ad0d 100644 --- a/openlibrary/plugins/openlibrary/api.py +++ b/openlibrary/plugins/openlibrary/api.py @@ -51,15 +51,10 @@ def POST(self): return delegate.RawText(json.dumps(result), content_type="application/json") def get_book_availability(self, id_type, ids): - return ( - lending.get_availability_of_works(ids) - if id_type == "openlibrary_work" - else lending.get_availability_of_editions(ids) - if id_type == "openlibrary_edition" - else lending.get_availability_of_ocaids(ids) - if id_type == "identifier" - else [] - ) + if id_type in ["openlibrary_work", "openlibrary_edition", "identifier"]: + return lending.get_availability(id_type, ids) + else: + return [] class trending_books_api(delegate.page): diff --git a/openlibrary/plugins/openlibrary/code.py b/openlibrary/plugins/openlibrary/code.py index f7a022aea18..daffbfdf829 100644 --- a/openlibrary/plugins/openlibrary/code.py +++ b/openlibrary/plugins/openlibrary/code.py @@ -36,7 +36,7 @@ from openlibrary.core.vendors import create_edition_from_amazon_metadata from openlibrary.utils.isbn import isbn_13_to_isbn_10, isbn_10_to_isbn_13 from openlibrary.core.models import Edition -from openlibrary.core.lending import get_work_availability, get_edition_availability +from openlibrary.core.lending import get_availability import openlibrary.core.stats from openlibrary.plugins.openlibrary.home import format_work_data from openlibrary.plugins.openlibrary.stats import increment_error_count @@ -252,26 +252,24 @@ def POST(self): class widget(delegate.page): - path = r'/(works|books)/(OL\d+[W|M])/widget' - - def GET(self, _type, olid=None): - if olid: - getter = ( - get_work_availability if _type == 'works' else get_edition_availability - ) - item = web.ctx.site.get(f'/{_type}/{olid}') or {} - item['olid'] = olid - item['availability'] = getter(olid).get(item['olid']) - item['authors'] = [ - web.storage(key=a.key, name=a.name or None) for a in item.get_authors() - ] - return delegate.RawText( - render_template( - 'widget', item if _type == 'books' else format_work_data(item) - ), - content_type='text/html', - ) - raise web.seeother('/') + path = r'(/works/OL\d+W|/books/OL\d+M)/widget' + + def GET(self, key: str): # type: ignore[override] + olid = key.split('/')[-1] + item = web.ctx.site.get(key) + is_work = key.startswith('/works/') + item['olid'] = olid + item['availability'] = get_availability( + 'openlibrary_work' if is_work else 'openlibrary_edition', + [olid], + ).get(olid) + item['authors'] = [ + web.storage(key=a.key, name=a.name or None) for a in item.get_authors() + ] + return delegate.RawText( + render_template('widget', format_work_data(item) if is_work else item), + content_type='text/html', + ) class addauthor(delegate.page): diff --git a/openlibrary/plugins/upstream/models.py b/openlibrary/plugins/upstream/models.py index 7acfabea49f..298e1f73108 100644 --- a/openlibrary/plugins/upstream/models.py +++ b/openlibrary/plugins/upstream/models.py @@ -686,21 +686,6 @@ def is_ascii(s): def get_related_books_subjects(self, filter_unicode=True): return self.filter_problematic_subjects(self.get_subjects(), filter_unicode) - def get_representative_edition(self) -> str | None: - """When we have confidence we can direct patrons to the best edition - of a work (for them), return qualifying edition key. Attempts - to find best (most available) edition of work using - archive.org work availability API. May be extended to support language - - :rtype str: infogami edition key or url which resolves to an edition or None - """ - work_id = self.key.replace('/works/', '') - availability = lending.get_work_availability(work_id) - if ol_edition := availability.get(work_id, {}).get('openlibrary_edition'): - return f'/books/{ol_edition}' - - return None - def get_sorted_editions( self, ebooks_only: bool = False, From 7927be46cf4fc7324aae32e64a3bdf8ad91c86f0 Mon Sep 17 00:00:00 2001 From: Drini Cami Date: Fri, 19 Jan 2024 04:05:00 -0500 Subject: [PATCH 43/61] Add tests for get_availability --- openlibrary/tests/core/test_lending.py | 44 ++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/openlibrary/tests/core/test_lending.py b/openlibrary/tests/core/test_lending.py index f0188075495..87e2282f608 100644 --- a/openlibrary/tests/core/test_lending.py +++ b/openlibrary/tests/core/test_lending.py @@ -1,3 +1,5 @@ +from unittest.mock import Mock, patch + from openlibrary.core import lending @@ -40,3 +42,45 @@ def mock_get_availability_of_ocaids(ocaids): r = f([{'ocaid': 'foo'}]) print(r) assert r[0]['availability']['status'] == 'error' + + +class TestGetAvailability: + def test_cache(self): + with patch("openlibrary.core.lending.requests.get") as mock_get: + mock_get.return_value = Mock() + mock_get.return_value.json.return_value = { + "responses": {"foo": {"status": "open"}} + } + + foo_expected = { + "status": "open", + "identifier": "foo", + "is_restricted": False, + "is_browseable": False, + "__src__": 'core.models.lending.get_availability', + } + bar_expected = { + "status": "error", + "identifier": "bar", + "is_restricted": True, + "is_browseable": False, + "__src__": 'core.models.lending.get_availability', + } + + r = lending.get_availability("identifier", ["foo"]) + assert mock_get.call_count == 1 + assert r == {"foo": foo_expected} + + # Should not make a call to the API again + r2 = lending.get_availability("identifier", ["foo"]) + assert mock_get.call_count == 1 + assert r2 == {"foo": foo_expected} + + # Now should make a call for just the new identifier + mock_get.return_value.json.return_value = { + "responses": {"bar": {"status": "error"}} + } + r3 = lending.get_availability("identifier", ["foo", "bar"]) + assert mock_get.call_count == 2 + assert mock_get.call_args[1]['params']['identifier'] == "bar" + assert r3 == {"foo": foo_expected, "bar": bar_expected} From 3f6e69304758a8a2e9e255a4ae27805913cfcee9 Mon Sep 17 00:00:00 2001 From: Drini Cami Date: Tue, 30 Jan 2024 14:44:36 -0500 Subject: [PATCH 44/61] Fix environment variables in compose file must not have quotes Causing expansion in CRONTAB_FILES to fail; it included the quote in the file path! Co-authored-by: Michael E. Karpeles (mek) --- compose.production.yaml | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/compose.production.yaml b/compose.production.yaml index f3fe66cd17c..240fc5dadea 100644 --- a/compose.production.yaml +++ b/compose.production.yaml @@ -82,8 +82,8 @@ services: user: root command: docker/ol-nginx-start.sh environment: - - CRONTAB_FILES="/etc/cron.d/archive-webserver-logs /etc/cron.d/certbot" - - NGINX_DOMAIN="covers.openlibrary.org" + - CRONTAB_FILES=/etc/cron.d/archive-webserver-logs /etc/cron.d/certbot + - NGINX_DOMAIN=covers.openlibrary.org restart: unless-stopped hostname: "$HOSTNAME" depends_on: @@ -173,8 +173,8 @@ services: user: root command: docker/ol-nginx-start.sh environment: - - CRONTAB_FILES="/etc/cron.d/archive-webserver-logs /etc/cron.d/pull-sitemaps-from-ol-home0 /etc/cron.d/certbot" - - NGINX_DOMAIN="openlibrary.org" + - CRONTAB_FILES=/etc/cron.d/archive-webserver-logs /etc/cron.d/pull-sitemaps-from-ol-home0 /etc/cron.d/certbot + - NGINX_DOMAIN=openlibrary.org volumes: # letsencrypt - letsencrypt-data:/etc/letsencrypt @@ -195,6 +195,8 @@ services: # Archive nginx logs regularly - ../olsystem/etc/cron.d/archive-webserver-logs:/etc/cron.d/archive-webserver-logs - archive-webserver-logs-data:/archive-webserver-logs-data + # Sitemaps + - ../olsystem/etc/cron.d/pull-sitemaps-from-ol-home0:/etc/cron.d/pull-sitemaps-from-ol-home0 ports: - 80:80 - 443:443 From 11719d85726df88ad72b0a729086c1aee43259bd Mon Sep 17 00:00:00 2001 From: Chales Horn Date: Fri, 2 Feb 2024 11:25:45 +1300 Subject: [PATCH 45/61] fix #8785 --- openlibrary/plugins/importapi/code.py | 19 +++---------------- 1 file changed, 3 insertions(+), 16 deletions(-) diff --git a/openlibrary/plugins/importapi/code.py b/openlibrary/plugins/importapi/code.py index bb78a6498e8..e40d2b562a8 100644 --- a/openlibrary/plugins/importapi/code.py +++ b/openlibrary/plugins/importapi/code.py @@ -197,30 +197,17 @@ def ia_import( """ from_marc_record = False - # Case 1 - Is this a valid Archive.org item? + # Check 1 - Is this a valid Archive.org item? metadata = ia.get_metadata(identifier) if not metadata: raise BookImportError('invalid-ia-identifier', '%s not found' % identifier) - # Case 2 - Does the item have an openlibrary field specified? - # The scan operators search OL before loading the book and add the - # OL key if a match is found. We can trust them and attach the item - # to that edition. - edition_olid = metadata.get('openlibrary_edition') or metadata.get( - 'openlibrary' - ) - if metadata.get('mediatype') == 'texts' and edition_olid: - edition_data = cls.get_ia_record(metadata) - edition_data['openlibrary'] = edition_olid - edition_data = cls.populate_edition_data(edition_data, identifier) - return cls.load_book(edition_data) - - # Case 3 - Can the item be loaded into Open Library? + # Check 2 - Can the item be loaded into Open Library? status = ia.get_item_status(identifier, metadata) if status != 'ok' and not force_import: raise BookImportError(status, 'Prohibited Item %s' % identifier) - # Case 4 - Does this item have a marc record? + # Check 3 - Does this item have a marc record? marc_record = get_marc_record_from_ia( identifier=identifier, ia_metadata=metadata ) From 1acdb1ff6afd67c77a4d554863a7b180f4612964 Mon Sep 17 00:00:00 2001 From: Chales Horn Date: Fri, 2 Feb 2024 11:31:28 +1300 Subject: [PATCH 46/61] uppercase MARC in comment --- openlibrary/plugins/importapi/code.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openlibrary/plugins/importapi/code.py b/openlibrary/plugins/importapi/code.py index e40d2b562a8..30f2b222a9d 100644 --- a/openlibrary/plugins/importapi/code.py +++ b/openlibrary/plugins/importapi/code.py @@ -207,7 +207,7 @@ def ia_import( if status != 'ok' and not force_import: raise BookImportError(status, 'Prohibited Item %s' % identifier) - # Check 3 - Does this item have a marc record? + # Check 3 - Does this item have a MARC record? marc_record = get_marc_record_from_ia( identifier=identifier, ia_metadata=metadata ) From 3faa507a0ac7b2d1be0669c4e873b18b8f55bcd4 Mon Sep 17 00:00:00 2001 From: Chales Horn Date: Fri, 2 Feb 2024 11:45:49 +1300 Subject: [PATCH 47/61] use format strings for error logging --- openlibrary/plugins/importapi/code.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/openlibrary/plugins/importapi/code.py b/openlibrary/plugins/importapi/code.py index 30f2b222a9d..c96e96b4d44 100644 --- a/openlibrary/plugins/importapi/code.py +++ b/openlibrary/plugins/importapi/code.py @@ -200,12 +200,12 @@ def ia_import( # Check 1 - Is this a valid Archive.org item? metadata = ia.get_metadata(identifier) if not metadata: - raise BookImportError('invalid-ia-identifier', '%s not found' % identifier) + raise BookImportError('invalid-ia-identifier', f'{identifier} not found') # Check 2 - Can the item be loaded into Open Library? status = ia.get_item_status(identifier, metadata) if status != 'ok' and not force_import: - raise BookImportError(status, 'Prohibited Item %s' % identifier) + raise BookImportError(status, f'Prohibited Item {identifier}') # Check 3 - Does this item have a MARC record? marc_record = get_marc_record_from_ia( @@ -222,7 +222,7 @@ def ia_import( edition_data = read_edition(marc_record) except MarcException as e: logger.error( - 'failed to read from MARC record %s: %s', identifier, str(e) + f'failed to read from MARC record {identifier}: {str(e)}' ) raise BookImportError('invalid-marc-record') else: @@ -267,8 +267,8 @@ def POST(self): rec = MarcBinary(data) edition = read_edition(rec) except MarcException as e: - details = f"{identifier}: {e}" - logger.error("failed to read from bulk MARC record %s", details) + details = f'{identifier}: {e}' + logger.error(f'failed to read from bulk MARC record {details}') return self.error('invalid-marc-record', details, **next_data) actual_length = int(rec.leader()[:MARC_LENGTH_POS]) From 2ca3bbb4f38accad420ccb1d6090b47cc7ada972 Mon Sep 17 00:00:00 2001 From: Chales Horn Date: Fri, 2 Feb 2024 11:57:06 +1300 Subject: [PATCH 48/61] fix explicit-f-string-type-conversion (RUF010) --- openlibrary/plugins/importapi/code.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/openlibrary/plugins/importapi/code.py b/openlibrary/plugins/importapi/code.py index c96e96b4d44..3b2d6732bc9 100644 --- a/openlibrary/plugins/importapi/code.py +++ b/openlibrary/plugins/importapi/code.py @@ -221,9 +221,7 @@ def ia_import( try: edition_data = read_edition(marc_record) except MarcException as e: - logger.error( - f'failed to read from MARC record {identifier}: {str(e)}' - ) + logger.error(f'failed to read from MARC record {identifier}: {e}') raise BookImportError('invalid-marc-record') else: try: From b4ba2470eb9824bd04e7689288709c21add175cb Mon Sep 17 00:00:00 2001 From: Mek Date: Thu, 11 Jan 2024 14:50:58 -0500 Subject: [PATCH 49/61] adds www.openlibrary.org domain to cerbot Co-authored-by: Drini Cami --- compose.production.yaml | 2 +- docker/ol-nginx-start.sh | 15 +++++++++++++-- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/compose.production.yaml b/compose.production.yaml index 240fc5dadea..5f4542ff3f1 100644 --- a/compose.production.yaml +++ b/compose.production.yaml @@ -174,7 +174,7 @@ services: command: docker/ol-nginx-start.sh environment: - CRONTAB_FILES=/etc/cron.d/archive-webserver-logs /etc/cron.d/pull-sitemaps-from-ol-home0 /etc/cron.d/certbot - - NGINX_DOMAIN=openlibrary.org + - NGINX_DOMAIN=openlibrary.org www.openlibrary.org volumes: # letsencrypt - letsencrypt-data:/etc/letsencrypt diff --git a/docker/ol-nginx-start.sh b/docker/ol-nginx-start.sh index 8a4db3d2897..84d20109110 100755 --- a/docker/ol-nginx-start.sh +++ b/docker/ol-nginx-start.sh @@ -1,9 +1,20 @@ #!/bin/bash -if [ -d "/etc/letsencrypt/live/$NGINX_DOMAIN" ] ; then - certbot certonly --webroot --webroot-path /openlibrary/static -d $NGINX_DOMAIN +# Create certs for domains missing them +RUN_CERTBOT=0 +CERTBOT_OPTIONS="" +for domain in $NGINX_DOMAIN; do + CERTBOT_OPTIONS+=" -d $domain" + if [ ! -d "/etc/letsencrypt/live/$domain" ]; then + RUN_CERTBOT=1 + fi +done + +if [ "$RUN_CERTBOT" -eq 1 ]; then + certbot certonly --webroot --webroot-path /openlibrary/static $CERTBOT_OPTIONS fi +# Run crontab if there are files if [ -n "$CRONTAB_FILES" ] ; then cat $CRONTAB_FILES | crontab - service cron start From 05387fc14e7ec7c0e6455721f11107f708e2a8e6 Mon Sep 17 00:00:00 2001 From: "Mark F. Heiman" Date: Fri, 2 Feb 2024 13:27:01 -0600 Subject: [PATCH 50/61] Update merge button only when needed --- openlibrary/components/MergeUI.vue | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openlibrary/components/MergeUI.vue b/openlibrary/components/MergeUI.vue index 315e0910d67..165e08d3e1b 100644 --- a/openlibrary/components/MergeUI.vue +++ b/openlibrary/components/MergeUI.vue @@ -78,8 +78,8 @@ export default { const readyCta = this.isSuperLibrarian ? DO_MERGE : REQUEST_MERGE this.$watch( '$refs.mergeTable.merge', - (new_value, old_value) => { - if (new_value && new_value !== old_value) this.mergeStatus = readyCta; + (new_value) => { + if (new_value !== undefined && this.mergeStatus !== 'Done') this.mergeStatus = readyCta; } ); }, From 94da56e864dbccab404254e4cf846151e967560e Mon Sep 17 00:00:00 2001 From: "Mark F. Heiman" Date: Sat, 3 Feb 2024 11:11:46 -0600 Subject: [PATCH 51/61] Clarified action button label logic --- openlibrary/components/MergeUI.vue | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/openlibrary/components/MergeUI.vue b/openlibrary/components/MergeUI.vue index 165e08d3e1b..90bd279f1b7 100644 --- a/openlibrary/components/MergeUI.vue +++ b/openlibrary/components/MergeUI.vue @@ -27,6 +27,8 @@ import { do_merge, update_merge_request, createMergeRequest, DEFAULT_EDITION_LIM const DO_MERGE = 'Do Merge' const REQUEST_MERGE = 'Request Merge' +const LOADING = 'Loading...' +const SAVING = 'Saving...' export default { name: 'app', @@ -51,7 +53,7 @@ export default { data() { return { url: new URL(location.toString()), - mergeStatus: 'Loading...', + mergeStatus: LOADING, mergeOutput: null, show_diffs: false, comment: '' @@ -79,7 +81,7 @@ export default { this.$watch( '$refs.mergeTable.merge', (new_value) => { - if (new_value !== undefined && this.mergeStatus !== 'Done') this.mergeStatus = readyCta; + if (new_value !== undefined && this.mergeStatus === LOADING) this.mergeStatus = readyCta; } ); }, @@ -88,7 +90,7 @@ export default { if (!this.$refs.mergeTable.merge) return; const { record: master, dupes, editions_to_move, unmergeable_works } = this.$refs.mergeTable.merge; - this.mergeStatus = 'Saving...'; + this.mergeStatus = SAVING; if (this.isSuperLibrarian) { // Perform the merge and create new/update existing merge request try { From 4f1d909a445d9055cdd0d3217b058eb3851b28bf Mon Sep 17 00:00:00 2001 From: Rishabh Kumar Date: Sun, 4 Feb 2024 01:42:07 +0530 Subject: [PATCH 52/61] Addition of Hindi as Change Website Language. (#8766) Enable Hindi support for the site. 1. deleted openlibrary/i18n/hi/legacy-strings.hi.yml 2. added openlibrary/i18n/hi/messages.po 3. updated the UI to Hindi language --- openlibrary/i18n/hi/legacy-strings.hi.yml | 17 - openlibrary/i18n/hi/messages.po | 5482 +++++++++++++++++ .../templates/languages/language_list.html | 1 + 3 files changed, 5483 insertions(+), 17 deletions(-) delete mode 100644 openlibrary/i18n/hi/legacy-strings.hi.yml create mode 100644 openlibrary/i18n/hi/messages.po diff --git a/openlibrary/i18n/hi/legacy-strings.hi.yml b/openlibrary/i18n/hi/legacy-strings.hi.yml deleted file mode 100644 index c245a4fb2a5..00000000000 --- a/openlibrary/i18n/hi/legacy-strings.hi.yml +++ /dev/null @@ -1,17 +0,0 @@ -#created: '2008-05-01T17:03:36.625922' -#last_modified: '2009-05-01T04:08:33.535793' -string_about_us: हमारे बारे में -string_change_language: भाषा परिवर्तन -string_contact_us: हमारा सम्पर्क सूत्र -string_create: निर्माण करें -string_create_it: इसे निर्मित करें -string_hello: नमस्ते -string_history: इतिहास -string_login: आगम -string_logout: निर्गम -string_open_library: मुक्त पुस्तकालय -string_page_does_not_exist: प्रष्ठ अनुपलब्ध -string_powered_by_infogami: इन्फ़ोगामी से अनुप्राणित -string_preferences: वरीयतायें -string_published_in: मुद्रण हुआ -string_welcome_user: सुस्वागतम %s! diff --git a/openlibrary/i18n/hi/messages.po b/openlibrary/i18n/hi/messages.po new file mode 100644 index 00000000000..1014b4518e7 --- /dev/null +++ b/openlibrary/i18n/hi/messages.po @@ -0,0 +1,5482 @@ +# Translations template for Open Library. +# Copyright (C) 2023 Internet Archive +# This file is distributed under the same license as the Open Library +# project. +# FIRST AUTHOR , 2023. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: Open Library VERSION\n" +"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" +"POT-Creation-Date: 2023-01-06 20:53+0000\n" +"PO-Revision-Date: 2024-01-27 01:34+0530\n" +"Last-Translator: \n" +"Language-Team: \n" +"Language: hi\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +# Note: The Unicode Consortium guidelines were considered, and this specific +# pluralization rule was chosen to align with common usage and linguistic +# characteristics in Hindi. +"Generated-By: Babel 2.9.1\n" +"X-Generator: Poedit 3.4.2\n" + +#: account.html:7 account.html:15 account/notifications.html:13 account/privacy.html:13 +#: lib/nav_head.html:15 type/user/view.html:27 +msgid "Settings" +msgstr "सेटिंग" + +#: account.html:20 +msgid "Settings & Privacy" +msgstr "सेटिंग और निजता" + +#: account.html:21 databarDiff.html:12 +msgid "View" +msgstr "देखें" + +#: account.html:21 +msgid "or" +msgstr "या" + +#: account.html:21 check_ins/check_in_prompt.html:26 check_ins/reading_goal_progress.html:26 +#: databarHistory.html:27 databarTemplate.html:13 databarView.html:34 type/edition/compact_title.html:11 +msgid "Edit" +msgstr "बदलाव करें" + +#: account.html:21 +msgid "Your Profile Page" +msgstr "आपका प्रोफ़ाइल" + +#: account.html:22 +msgid "View or Edit your Reading Log" +msgstr "अपना रीडिंग लॉग देखें या उसमें बदलाव करें" + +#: account.html:23 +msgid "View or Edit your Lists" +msgstr "अपनी सूचियाँ देखें या उनमें बदलाव करें" + +#: account.html:24 +msgid "Import and Export Options" +msgstr "आयात और निर्यात विकल्प" + +#: account.html:25 +msgid "Manage Privacy Settings" +msgstr "निजता सेटिंग मैनेज करें" + +#: account.html:26 +msgid "Manage Notifications Settings" +msgstr "नोटिफ़िकेशन सेटिंग मैनेज करें" + +#: account.html:27 +msgid "Manage Mailing List Subscriptions" +msgstr "मेलिंग सूची सदस्यताएं प्रबंधित करें" + +#: account.html:28 +msgid "Change Password" +msgstr "पासवर्ड बदलें" + +#: account.html:29 +msgid "Update Email Address" +msgstr "अपना ईमेल पता अपडेट करें" + +#: account.html:30 +msgid "Deactivate Account" +msgstr "अकाउंट डीऐक्टिवेट करें" + +#: account.html:32 +msgid "Please contact us if you need help with anything else." +msgstr "अगर आपको किसी और चीज़ में मदद चाहिए, तो कृपया हमसे संपर्क करें।" + +#: barcodescanner.html:7 +msgid "Barcode Scanner (Beta)" +msgstr "बारकोड स्कैनर (बीटा)" + +#: barcodescanner.html:15 +msgid "Point your camera at a barcode! 📷" +msgstr "बारकोड पर अपने कैमरे की ओर इशारा करें! 📷" + +#: account/loans.html:59 +#, python-format +msgid "%(count)d Current Loan" +msgid_plural "%(count)d Current Loans" +msgstr[0] "%(count)d वर्तमान ऋण" +msgstr[1] "%(count)d वर्तमान ऋण" + +#: account/loans.html:65 admin/loans_table.html:41 borrow_admin.html:106 +msgid "Loan Expires" +msgstr "लोन की समय सीमा खत्म हो रही है" + +#: borrow_admin.html:168 +#, python-format +msgid "%d person waiting" +msgid_plural "%d people waiting" +msgstr[0] "%d व्यक्ति प्रतीक्षा कर रहा है" +msgstr[1] "%d व्यक्ति प्रतीक्षा कर रहा है" + +#: ManageWaitlistButton.html:11 borrow_admin.html:186 +#, python-format +msgid "Waiting for %d day" +msgid_plural "Waiting for %d days" +msgstr[0] "%d दिन की प्रतीक्षा" +msgstr[1] "%d दिनों की प्रतीक्षा" + +#: diff.html:7 +#, python-format +msgid "Diff on %s" +msgstr "%s पर डिफ़" + +#: diff.html:26 +msgid "Added" +msgstr "जोड़ा गया" + +#: diff.html:27 +msgid "Modified" +msgstr "संशोधित" + +#: diff.html:28 +msgid "Removed" +msgstr "हटाया गया" + +#: diff.html:29 +msgid "Not changed" +msgstr "बदला नहीं गया" + +#: diff.html:39 +#, python-format +msgid "Revision %d" +msgstr "संशोधन %d" + +#: books/check.html:32 books/edit/edition.html:76 books/show.html:16 covers/book_cover.html:41 +#: covers/book_cover_single_edition.html:36 covers/book_cover_work.html:32 diff.html:42 +#: lists/preview.html:20 +msgid "by" +msgstr "द्वारा" + +#: diff.html:44 +msgid "by Anonymous" +msgstr "" + +#: diff.html:110 +msgid "Differences" +msgstr "भिन्नताएँ" + +#: diff.html:168 +msgid "Both the revisions are identical." +msgstr "दोनों संशोधन समान हैं।" + +#: edit_yaml.html:14 lib/edit_head.html:17 +msgid "You're in edit mode." +msgstr "आप एडिट मोड में हैं।" + +#: edit_yaml.html:15 lib/edit_head.html:18 +msgid "Cancel, and go back." +msgstr "कैंसिल करें और वापस जाएँ।" + +#: edit_yaml.html:22 +msgid "YAML Representation:" +msgstr "YAML Representation:" + +#: BookPreview.html:11 books/edit/edition.html:669 editpage.html:15 +msgid "Preview" +msgstr "प्रिव्यू" + +#: history.html:23 +msgid "History of edits to" +msgstr "में किए गए बदलावों का इतिहास" + +#: history.html:31 +msgid "Revision" +msgstr "संशोधन" + +#: RecentChanges.html:17 RecentChangesAdmin.html:20 RecentChangesUsers.html:20 admin/ip/view.html:44 +#: admin/people/edits.html:41 history.html:32 recentchanges/render.html:30 +#: recentchanges/updated_records.html:28 +msgid "When" +msgstr "कब" + +#: RecentChanges.html:19 admin/ip/view.html:46 admin/loans_table.html:46 admin/people/edits.html:43 +#: admin/waitinglists.html:28 history.html:33 recentchanges/render.html:32 +msgid "Who" +msgstr "कौन" + +#: RecentChanges.html:20 RecentChangesUsers.html:22 admin/ip/view.html:47 admin/people/edits.html:44 +#: history.html:34 recentchanges/render.html:33 recentchanges/updated_records.html:30 +msgid "Comment" +msgstr "टिप्पणी" + +#: history.html:35 +msgid "Diff" +msgstr "डिफ" + +#: history.html:42 history.html:73 +msgid "Compare" +msgstr "तुलना" + +#: history.html:42 history.html:73 +msgid "See Diff" +msgstr "डिफ़ देखें" + +#: history.html:48 lib/history.html:76 +#, python-format +msgid "View revision %s" +msgstr "संशोधन %s देखें" + +#: history.html:85 +msgid "Back" +msgstr "पीछे" + +#: Pager.html:34 history.html:87 lib/pagination.html:37 showmarc.html:54 +msgid "Next" +msgstr "अगला" + +#: internalerror.html:14 +msgid "Sorry. There seems to be a problem with what you were just looking at." +msgstr "माफ़ करें। लगता है कि आप जो देख रहे थे उसमें कोई समस्या है।" + +#: internalerror.html:15 +#, python-format +msgid "" +"We've noted the error %s and will look into it as soon as possible. Head for home?" +msgstr "हमने %s की गड़बड़ी नोट कर ली है और हम जल्द - से - जल्द इसकी जाँच करेंगे। home?" + +#: lib/nav_head.html:30 library_explorer.html:6 +msgid "Library Explorer" +msgstr "लाइब्रेरी एक्सप्लोरर" + +#: lib/header_dropdown.html:59 lib/nav_head.html:118 login.html:10 login.html:75 +msgid "Log In" +msgstr "लॉग इन करें" + +#: account/create.html:19 login.html:16 +msgid "OR" +msgstr "या" + +#: login.html:18 +msgid "" +"Please enter your Internet Archive email and password to access " +"your Open Library account." +msgstr "" +"कृपया अपना इंटरनेट आर्काइव ईमेल और पासवर्ड दर्ज करें " +"ओपन लाइब्रेरी एक्सेस करने के लिए।" + +#: account/create.html:50 login.html:21 +#, python-format +msgid "You are already logged into Open Library as %(user)s." +msgstr "आप पहले से ही %(user)s के रूप में ओपन लाइब्रेरी में लॉग इन हैं।" + +#: account/create.html:51 login.html:22 +msgid "" +"If you'd like to create a new, different Open Library account, you'll need to log out and start the signup process afresh." +msgstr "" +"यदि आप एक नया, अलग ओपन लाइब्रेरी खाता बनाना चाहते हैं, तो आपको लॉग आउट करना होगा और साइन अप प्रक्रिया को नए सिरे से शुरू करना होगा।" + +#: login.html:42 +msgid "Email" +msgstr "ईमेल" + +#: login.html:44 +msgid "Forgot your Internet Archive email?" +msgstr "अपना इंटरनेट आर्काइव ईमेल भूल गए?" + +#: account/email/forgot-ia.html:33 forms.py:31 login.html:54 +msgid "Password" +msgstr "पासवर्ड" + +#: login.html:64 +msgid "Remember me" +msgstr "मुझे याद रखें" + +#: account/email/forgot.html:48 login.html:79 +msgid "Forgot your Password?" +msgstr "अपना पासवर्ड भूल गए?" + +#: login.html:81 +msgid "Not a member of Open Library? Sign up now." +msgstr "ओपन लाइब्रेरी का सदस्य नहीं है? अभी साइन अप करें।" + +#: messages.html:11 +msgid "Created new author record." +msgstr "नया लेखक रिकॉर्ड बनाया गया।" + +#: messages.html:12 +msgid "Created new edition record." +msgstr "नया संस्करण रिकॉर्ड बनाया गया।" + +#: messages.html:13 +msgid "Created new work record." +msgstr "नया कार्य रिकॉर्ड बनाया गया।" + +#: messages.html:14 +msgid "Added new book." +msgstr "नई किताब जोड़ी गई।" + +#: messages.html:16 +msgid "Thank you very much for adding that new book!" +msgstr "नई किताब को जोड़ने के लिए बहुत - बहुत धन्यवाद!" + +#: messages.html:17 messages.html:18 +msgid "Thank you very much for improving that record!" +msgstr "रिकॉर्ड को बेहतर बनाने के लिए बहुत - बहुत धन्यवाद!" + +#: notfound.html:7 +#, python-format +msgid "%(path)s is not found" +msgstr "%(path)s नहीं मिला" + +#: languages/notfound.html:10 notfound.html:14 +msgid "404 - Page Not Found" +msgstr "404 - Page Not Found" + +#: notfound.html:18 +#, python-format +msgid "%(path)s does not exist." +msgstr "%(path)s मौजूद नहीं है." + +#: notfound.html:22 +msgid "Create it?" +msgstr "इसे बनाएं?" + +#: notfound.html:26 +msgid "Looking for a template/macro?" +msgstr "टेम्पलेट/मैक्रो की तलाश है?" + +#: permission.html:10 +msgid "Permission of" +msgstr "की अनुमति" + +#: permission.html:18 +msgid "Permission" +msgstr "अनुमति" + +#: permission.html:28 +msgid "Child Permission" +msgstr "Child Permission" + +#: permission_denied.html:10 +msgid "Permission denied." +msgstr "अनुमति अस्वीकृत।" + +#: permission_denied.html:30 +#, python-format +msgid "You may log in if you have the required permissions." +msgstr "अगर आपके पास ज़रूरी अनुमतियाँ हैं, तो आप लॉग इन कर सकते हैं।" + +#: showamazon.html:8 showamazon.html:11 showbwb.html:8 showbwb.html:11 +msgid "Record details of" +msgstr "का विवरण रिकॉर्ड करें" + +#: showamazon.html:15 showbwb.html:15 +msgid "This record came from" +msgstr "यह रिकॉर्ड यहाँ से आया है" + +#: showia.html:42 showmarc.html:48 +msgid "Invalid MARC record." +msgstr "अमान्य MARC रिकॉर्ड।" + +#: status.html:17 +msgid "Server status" +msgstr "सर्वर की स्थिति" + +#: SearchNavigation.html:24 SubjectTags.html:23 lib/nav_foot.html:28 lib/nav_head.html:28 subjects.html:9 +#: subjects/notfound.html:11 type/author/view.html:176 type/list/view_body.html:280 work_search.html:75 +msgid "Subjects" +msgstr "विषय" + +#: SubjectTags.html:27 subjects.html:9 type/author/view.html:177 type/list/view_body.html:282 +#: work_search.html:79 +msgid "Places" +msgstr "जगहें" + +#: SubjectTags.html:25 admin/menu.html:20 subjects.html:9 type/author/view.html:178 +#: type/list/view_body.html:281 work_search.html:78 +msgid "People" +msgstr "लोग" + +#: SubjectTags.html:29 subjects.html:9 type/list/view_body.html:283 work_search.html:80 +msgid "Times" +msgstr "टाइम्स (बार)" + +#: subjects.html:19 +msgid "See all works" +msgstr "सभी काम देखें" + +#: merge/authors.html:89 publishers/view.html:13 subjects.html:15 type/author/view.html:117 +#, python-format +msgid "%(count)d work" +msgid_plural "%(count)d works" +msgstr[0] "%(count)d किताब" +msgstr[1] "%(count)d किताबें" + +#: subjects.html:22 +#, python-format +msgid "Search for books with subject %(name)s." +msgstr "विषय %(name)s वाली पुस्तकें खोजें।" + +#: authors/index.html:21 lib/nav_head.html:102 lists/home.html:25 publishers/notfound.html:19 +#: publishers/view.html:115 search/advancedsearch.html:48 search/authors.html:27 search/inside.html:17 +#: search/lists.html:17 search/publishers.html:19 search/subjects.html:28 subjects.html:27 +#: subjects/notfound.html:19 type/local_id/view.html:42 work_search.html:91 +msgid "Search" +msgstr "किताबें खोजें" + +#: publishers/view.html:32 subjects.html:37 +msgid "Publishing History" +msgstr "पब्लिशिंग हिस्ट्री" + +#: subjects.html:39 +msgid "" +"This is a chart to show the publishing history of editions of works about this subject. Along the X " +"axis is time, and on the y axis is the count of editions published. Click " +"here to skip the chart." +msgstr "" +"यह कृतियों के संस्करणों के प्रकाशन इतिहास को दिखाने के लिए एक चार्ट है। X अक्ष के साथ समय है, और y " +"अक्ष पर प्रकाशित संस्करणों की गणना है। चार्ट को छोड़ने के लिए यहाँ क्लिक करें।" + +#: publishers/view.html:34 subjects.html:40 +msgid "Reset chart" +msgstr "चार्ट रीसेट करें" + +#: publishers/view.html:34 subjects.html:40 +msgid "or continue zooming in." +msgstr "या ज़ूम इन करना जारी रखें।" + +#: subjects.html:41 +msgid "This graph charts editions published on this subject." +msgstr "यह ग्राफ़ इस विषय पर प्रकाशित संस्करणों को चार्ट करता है।" + +#: publishers/view.html:42 subjects.html:47 +msgid "Editions Published" +msgstr "प्रकाशन प्रकाशित" + +#: admin/imports.html:18 publishers/view.html:44 subjects.html:49 +msgid "You need to have JavaScript turned on to see the nifty chart!" +msgstr "निफ्टी चार्ट देखने के लिए आपको जावास्क्रिप्ट चालू करनी होगी!" + +#: publishers/view.html:46 subjects.html:51 +msgid "Year of Publication" +msgstr "प्रकाशन का वर्ष" + +#: subjects.html:57 +msgid "Related..." +msgstr "संबंधित..." + +#: publishers/view.html:64 subjects.html:71 +msgid "None found." +msgstr "कुछ नहीं मिला।" + +#: publishers/view.html:81 subjects.html:86 +msgid "See more books by, and learn about, this author" +msgstr "इस लेखक द्वारा और किताबें देखें, और इसके बारे में जानें" + +#: account/loans.html:143 authors/index.html:24 publishers/view.html:79 search/authors.html:56 +#: search/publishers.html:29 search/subjects.html:66 subjects.html:84 +#, python-format +msgid "%(count)d book" +msgid_plural "%(count)d books" +msgstr[0] "%(count)d किताब" +msgstr[1] "%(count)d किताबें" + +#: subjects.html:92 +msgid "Prolific Authors" +msgstr "विपुल लेखक" + +#: subjects.html:93 +msgid "who have written the most books on this subject" +msgstr "जिन्होंने इस विषय पर सबसे अधिक किताबें लिखी हैं" + +#: publishers/view.html:104 subjects.html:109 +msgid "Get more information about this publisher" +msgstr "इस प्रकाशक के बारे में अधिक जानकारी प्राप्त करें" + +#: SearchResultsWork.html:82 books/check.html:32 merge/authors.html:82 publishers/view.html:102 +#: subjects.html:107 +#, python-format +msgid "%(count)s edition" +msgid_plural "%(count)s editions" +msgstr[0] "%(count)s संस्करण" +msgstr[1] "%(count)s संस्करण" + +#: support.html:7 support.html:10 +msgid "How can we help?" +msgstr "हम आपकी कैसे मदद कर सकते हैं?" + +#: support.html:16 +msgid "" +"Your question has been sent to our support team. We'll get back to you shortly. You can also contact us on Twitter." +msgstr "" +"आपका सवाल हमारी सपोर्ट टीम को भेज दिया गया है। हम जल्द ही आपसे संपर्क करेंगे। आप हमसे ट्विटर पर भी संपर्क कर सकते हैं।" + +#: support.html:19 +msgid "" +"Please check our Help Pages and Frequently Asked " +"Questions (FAQ) to see if your question is answered there. Thank you." +msgstr "" +"कृपया हमारे सहायता पेज और अक्सर पूछे जाने वाले सवाल (FAQ) देखें " +"और देखें कि आपके सवाल का जवाब वहाँ दिया गया है या नहीं। धन्यवाद।" + +#: support.html:22 +msgid "Your Name" +msgstr "आपका नाम" + +#: support.html:27 +msgid "Your Email Address" +msgstr "आपका ईमेल" + +#: support.html:28 +msgid "We'll need this if you want a reply" +msgstr "अगर आपको जवाब चाहिए, तो हमें इसकी ज़रूरत होगी" + +#: support.html:29 +msgid "" +"If you wish to be contacted by other means, please add your contact preference and details to your " +"question." +msgstr "यदि आप किसी अन्य माध्यम से संपर्क करना चाहते हैं, तो कृपया अपने प्रश्न में अपनी संपर्क प्राथमिकता और विवरण जोड़ें।" + +#: support.html:33 +msgid "Topic" +msgstr "विषय" + +#: support.html:36 +msgid "Select..." +msgstr "चुनें..." + +#: support.html:37 +msgid "Borrowing Help" +msgstr "उधार लेने में मदद" + +#: support.html:38 +msgid "Developer/Code Question" +msgstr "डेवलपर/कोड प्रश्न" + +#: support.html:39 +msgid "Editing Issue" +msgstr "समस्या में बदलाव करना" + +#: support.html:40 +msgid "Login Trouble" +msgstr "लॉग इन करने में परेशानी" + +#: support.html:41 +msgid "Spam Report" +msgstr "स्पैम रिपोर्ट" + +#: support.html:42 +msgid "Waiting List" +msgstr "प्रतीक्षा सूची" + +#: support.html:43 +msgid "Other Question" +msgstr "अन्य सवाल" + +#: support.html:49 +msgid "Your Question" +msgstr "आपका सवाल" + +#: support.html:50 +msgid "Note: our staff will likely only be able respond in English." +msgstr "ध्यान दें: हमारे कर्मचारी संभवतः केवल अंग्रेजी में जवाब दे सकेंगे।" + +#: support.html:54 +msgid "" +"If you encounter an error message, please include it. For questions about our books, please provide " +"the title/author or Open Library ID." +msgstr "" +"अगर आपको कोई गड़बड़ी का मैसेज मिलता है, तो कृपया उसे शामिल करें। हमारी पुस्तकों के बारे में प्रश्नों के लिए, कृपया शीर्षक/" +"लेखक या ओपन लाइब्रेरी आईडी प्रदान करें।" + +#: support.html:58 +msgid "Which page were you looking at?" +msgstr "आप कौन सा पेज देख रहे थे?" + +#: support.html:69 +msgid "Send" +msgstr "भेजें" + +#: EditButtons.html:23 EditButtonsMacros.html:26 ReadingLogDropper.html:167 account/create.html:90 +#: account/notifications.html:59 account/password/reset.html:24 account/privacy.html:56 +#: admin/imports-add.html:28 books/add.html:108 books/edit/addfield.html:87 books/edit/addfield.html:133 +#: books/edit/addfield.html:177 covers/add.html:79 covers/manage.html:52 databarAuthor.html:66 +#: databarEdit.html:13 merge/authors.html:109 support.html:71 +msgid "Cancel" +msgstr "रद्द करें" + +#: trending.html:10 +msgid "Now" +msgstr "अब" + +#: admin/graphs.html:47 check_ins/check_in_form.html:71 check_ins/check_in_prompt.html:36 +#: trending.html:10 +msgid "Today" +msgstr "आज" + +#: admin/graphs.html:48 trending.html:10 +msgid "This Week" +msgstr "इस सप्ताह" + +#: stats/readinglog.html:20 stats/readinglog.html:37 stats/readinglog.html:54 trending.html:10 +msgid "This Month" +msgstr "इस महीने" + +#: trending.html:10 +msgid "This Year" +msgstr "यह साल" + +#: trending.html:10 +msgid "All Time" +msgstr "पूरे समय" + +#: home/index.html:27 trending.html:11 +msgid "Trending Books" +msgstr "ट्रेंडिंग बुक्स" + +#: trending.html:12 +msgid "See what readers from the community are adding to their bookshelves" +msgstr "देखें कि समुदाय के पाठक अपने बुकशेल्फ़ में क्या जोड़ रहे हैं" + +#: ReadingLogDropper.html:27 ReadingLogDropper.html:66 ReadingLogDropper.html:189 account/mybooks.html:75 +#: account/sidebar.html:26 trending.html:23 +msgid "Want to Read" +msgstr "पढ़ने में रुचि" + +#: ReadingLogDropper.html:25 ReadingLogDropper.html:74 account/books.html:33 account/mybooks.html:74 +#: account/readinglog_shelf_name.html:8 account/sidebar.html:25 trending.html:23 +msgid "Currently Reading" +msgstr "फ़िलहाल पढ़ रहे हैं" + +#: LoanReadForm.html:9 ReadButton.html:19 book_providers/gutenberg_read_button.html:15 +#: book_providers/openstax_read_button.html:15 book_providers/standard_ebooks_read_button.html:15 +#: books/edit/edition.html:665 books/show.html:34 trending.html:23 type/list/embed.html:69 widget.html:34 +msgid "Read" +msgstr "पढ़ें" + +#: trending.html:24 +#, python-format +msgid "Someone marked as %(shelf)s %(k_hours_ago)s" +msgstr "किसी ने %(shelf)s %(k_hours_ago)s के रूप में चिह्नित किया" + +#: trending.html:26 +#, python-format +msgid "Logged %(count)i times %(time_unit)s" +msgstr "%(count)i बार %(time_unit)s लॉग इन किया गया" + +#: viewpage.html:13 +msgid "Revert to this revision?" +msgstr "इस संशोधन पर वापस जाएँ?" + +#: viewpage.html:16 +msgid "Revert to this revision" +msgstr "इस संशोधन पर वापस जाएँ" + +#: widget.html:34 +#, python-format +msgid "Read \"%(title)s\"" +msgstr "\"%(title)s\" पढ़ें" + +#: widget.html:39 +#, python-format +msgid "Borrow \"%(title)s\"" +msgstr "\"%(title)s\" उधार लें" + +#: ReadButton.html:15 books/edit/edition.html:668 type/list/embed.html:80 widget.html:39 +msgid "Borrow" +msgstr "उधार" + +#: widget.html:45 +#, python-format +msgid "Join waitlist for "%(title)s"" +msgstr "\"%(title)s \"के लिए प्रतीक्षा सूची में शामिल हों;" + +#: LoanStatus.html:112 widget.html:45 +msgid "Join Waitlist" +msgstr "वेटलिस्ट में शामिल हों" + +#: widget.html:49 +#, python-format +msgid "Learn more about "%(title)s" at OpenLibrary" +msgstr "OpenLibrary पर \"%(title)s \"के बारे में और जानें" + +#: widget.html:49 +msgid "Learn More" +msgstr "अधिक जानें" + +#: widget.html:51 +msgid "on " +msgstr "पर " + +#: work_search.html:68 +msgid "Search Books" +msgstr "किताबों के लिए खोज करें" + +#: work_search.html:72 +msgid "eBook?" +msgstr "ई - बुक?" + +#: type/edition/view.html:246 type/work/view.html:246 work_search.html:73 +msgid "Language" +msgstr "भाषा" + +#: books/add.html:42 books/edit.html:92 books/edit/edition.html:245 lib/nav_head.html:93 +#: merge_queue/merge_queue.html:77 search/advancedsearch.html:24 work_search.html:74 work_search.html:164 +msgid "Author" +msgstr "लेखक" + +#: work_search.html:76 +msgid "First published" +msgstr "पहली बार प्रकाशित" + +#: search/advancedsearch.html:44 type/edition/view.html:231 type/work/view.html:231 work_search.html:77 +msgid "Publisher" +msgstr "प्रकाशक" + +#: work_search.html:81 +msgid "Classic eBooks" +msgstr "क्लासिक ई - बुक्स" + +#: work_search.html:89 +msgid "Keywords" +msgstr "Keywords" + +#: recentchanges/index.html:60 work_search.html:94 +msgid "Everything" +msgstr "सभी" + +#: work_search.html:96 +msgid "Ebooks" +msgstr "ई - बुक्स" + +#: work_search.html:98 +msgid "Print Disabled" +msgstr "Print Disabled" + +#: work_search.html:101 +msgid "This is only visible to super librarians." +msgstr "यह केवल सुपर लाइब्रेरियन को दिखाई देता है।" + +#: work_search.html:107 +msgid "Solr Editions Beta" +msgstr "सोलर एडिशन बीटा" + +#: work_search.html:124 +msgid "eBook" +msgstr "ई - बुक" + +#: work_search.html:127 +msgid "Classic eBook" +msgstr "क्लासिक ई - बुक" + +#: work_search.html:146 +msgid "Explore Classic eBooks" +msgstr "क्लासिक ई - बुक्स का जायज़ा लें" + +#: work_search.html:146 +msgid "Only Classic eBooks" +msgstr "केवल क्लासिक ई - बुक्स" + +#: work_search.html:150 work_search.html:152 work_search.html:154 work_search.html:156 +#, python-format +msgid "Explore books about %(subject)s" +msgstr "%(subject)s के बारे में किताबों का जायज़ा लें" + +#: merge/authors.html:88 work_search.html:158 +msgid "First published in" +msgstr "पहली बार में प्रकाशित" + +#: work_search.html:160 +msgid "Written in" +msgstr "में लिखा गया" + +#: work_search.html:162 +msgid "Published by" +msgstr "प्रकाशक" + +#: work_search.html:165 +msgid "Click to remove this facet" +msgstr "इस पहलू को हटाने के लिए क्लिक करें" + +#: work_search.html:167 +#, python-format +msgid "%(title)s - search" +msgstr "%(title)s - खोजें" + +#: search/lists.html:29 work_search.html:181 +msgid "No results found." +msgstr "कोई नतीजा नहीं मिला।" + +#: search/lists.html:30 work_search.html:184 +#, python-format +msgid "Search for books containing the phrase \"%s\"?" +msgstr "\"%s\" वाक्यांश वाली पुस्तकों के लिए खोजें?" + +#: work_search.html:188 +msgid "Add a new book to Open Library?" +msgstr "ओपन लाइब्रेरी में एक नई किताब जोड़ें?" + +#: account/reading_log.html:36 search/authors.html:41 search/subjects.html:31 work_search.html:190 +#, python-format +msgid "%(count)s hit" +msgid_plural "%(count)s hits" +msgstr[0] "%(count)s हिट" +msgstr[1] "%(count)s हिट" + +#: work_search.html:225 +msgid "Zoom In" +msgstr "ज़ूम इन" + +#: work_search.html:226 +msgid "Focus your results using these" +msgstr "इनका उपयोग करके अपने परिणामों पर ध्यान केंद्रित करें" + +#: work_search.html:226 +msgid "filters" +msgstr "फ़िल्टर" + +#: search/facet_section.html:20 work_search.html:238 +msgid "Merge duplicate authors from this search" +msgstr "इस खोज से डुप्लिकेट लेखकों को मर्ज करें" + +#: search/facet_section.html:20 type/work/editions.html:17 work_search.html:238 +msgid "Merge duplicates" +msgstr "डुप्लिकेट मर्ज करें" + +#: search/facet_section.html:32 work_search.html:250 +msgid "yes" +msgstr "हाँ" + +#: search/facet_section.html:34 work_search.html:252 +msgid "no" +msgstr "नहीं" + +#: search/facet_section.html:35 work_search.html:253 +msgid "Filter results for ebook availability" +msgstr "ई - बुक उपलब्धता के लिए फ़िल्टर के नतीजे" + +#: search/facet_section.html:37 work_search.html:255 +#, python-format +msgid "Filter results for %(facet)s" +msgstr "%(facet)s के लिए फ़िल्टर के नतीजे" + +#: search/facet_section.html:42 work_search.html:260 +msgid "more" +msgstr "अधिक" + +#: search/facet_section.html:46 work_search.html:264 +msgid "less" +msgstr "कम" + +#: account/books.html:27 account/sidebar.html:24 type/user/view.html:50 type/user/view.html:55 +msgid "Reading Log" +msgstr "रीडिंग लॉग" + +#: account/books.html:31 lib/nav_head.html:13 lib/nav_head.html:25 lib/nav_head.html:76 +msgid "My Books" +msgstr "मेरी किताबें" + +#: account/books.html:35 account/readinglog_shelf_name.html:10 +msgid "Want To Read" +msgstr "पढ़ने में रुचि" + +#: ReadingLogDropper.html:23 ReadingLogDropper.html:82 account/books.html:37 account/mybooks.html:76 +#: account/readinglog_shelf_name.html:12 account/sidebar.html:27 +msgid "Already Read" +msgstr "पहले पढ़ा है" + +#: account/books.html:40 account/sidebar.html:38 +msgid "Sponsorships" +msgstr "प्रायोजक" + +#: account/books.html:42 +msgid "Book Notes" +msgstr "बुक नोट्स" + +#: EditionNavBar.html:26 account/books.html:44 account/observations.html:33 +msgid "Reviews" +msgstr "समीक्षाएं" + +#: account/books.html:46 account/sidebar.html:19 admin/menu.html:22 admin/people/view.html:233 +#: type/user/view.html:29 +msgid "Loans" +msgstr "ऋण" + +#: account/books.html:48 +msgid "Imports and Exports" +msgstr "आयात और निर्यात" + +#: account/books.html:50 +msgid "My Lists" +msgstr "मेरी लिस्ट" + +#: account/books.html:79 +msgid "View stats about this shelf" +msgstr "इस शेल्फ़ के बारे में आँकड़े देखें" + +#: account/books.html:79 account/readinglog_stats.html:93 admin/index.html:19 +msgid "Stats" +msgstr "स्थिति" + +#: account/books.html:85 +msgid "Your book notes are private and cannot be viewed by other patrons." +msgstr "आपके बुक नोट निजी होते हैं और उन्हें दूसरे सब्सक्राइबर नहीं देख सकते." + +#: account/books.html:87 +msgid "Your book reviews will be shared anonymously with other patrons." +msgstr "आपकी बुक समीक्षाओं को अन्य सब्सक्राइबर्स के साथ गुमनाम रूप से शेयर किया जाएगा." + +#: account/books.html:90 +msgid "Your reading log is currently set to public" +msgstr "आपका रीडिंग लॉग फ़िलहाल पब्लिक के लिए सेट है" + +#: account/books.html:93 +msgid "Your reading log is currently set to private" +msgstr "आपका रीडिंग लॉग फ़िलहाल प्राइवेट पर सेट है" + +#: account/books.html:95 type/user/view.html:57 +msgid "Manage your privacy settings" +msgstr "अपनी निजता सेटिंग मैनेज करें" + +#: account/create.html:9 +msgid "Sign Up to Open Library" +msgstr "ओपन लाइब्रेरी के लिए साइन अप करें" + +#: account/create.html:12 account/create.html:89 lib/header_dropdown.html:60 lib/nav_head.html:119 +msgid "Sign Up" +msgstr "साइन अप" + +#: account/create.html:21 +msgid "Complete the form below to create a new Internet Archive account." +msgstr "नया इंटरनेट आर्काइव अकाउंट बनाने के लिए नीचे दिया गया फ़ॉर्म पूरा करें।" + +#: account/create.html:22 +msgid "Each field is required" +msgstr "फ़ील्ड आवश्यक है" + +#: account/create.html:60 +msgid "Your URL" +msgstr "आपका URL" + +#: account/create.html:60 +msgid "screenname" +msgstr "स्क्रीन नाम" + +#: account/create.html:77 +msgid "" +"If you have security settings or privacy blockers installed, please disable them to see the reCAPTCHA." +msgstr "यदि आपके पास सुरक्षा सेटिंग या गोपनीयता अवरोधक स्थापित हैं, तो कृपया उन्हें reCAPTCHA देखने के लिए अक्षम करें।" + +#: account/create.html:81 +msgid "Incorrect. Please try again." +msgstr "गलत पासवर्ड! कृपया फिर से कोशिश करें." + +#: account/create.html:85 +msgid "" +"By signing up, you agree to the Internet Archive's Terms of Service." +msgstr "" +"साइन अप करके, आप इंटरनेट आर्काइव की सेवा की शर्तों से सहमत होते हैं।" + +#: account/delete.html:6 account/delete.html:10 +msgid "Delete Account" +msgstr "खाता मिटायें" + +#: account/delete.html:15 +msgid "Sorry, this functionality is not yet implemented." +msgstr "माफ़ करें, यह फ़ंक्शन अभी तक लागू नहीं हुआ है।" + +#: account/import.html:12 +msgid "Import from Goodreads" +msgstr "Goodreads से इंपोर्ट करें" + +#: account/import.html:18 +msgid "File size limit 10MB and .csv file type only" +msgstr "फ़ाइल आकार सीमा केवल 10MB और .csv फ़ाइल प्रकार" + +#: account/import.html:22 +msgid "Load Books" +msgstr "किताबें लोड करें" + +#: account/import.html:27 +msgid "Export your Reading Log" +msgstr "अपना रीडिंग लॉग एक्सपोर्ट करें" + +#: account/import.html:28 +msgid "Download a copy of your reading log." +msgstr "अपने रीडिंग लॉग की एक कॉपी डाउनलोड करें।" + +#: account/import.html:28 +msgid "What is this?" +msgstr "यह क्या है?" + +#: account/import.html:33 account/import.html:44 account/import.html:55 account/import.html:66 +#: account/import.html:77 +msgid "Download (.csv format)" +msgstr "डाउनलोड करें (.csv फ़ॉर्मेट)" + +#: account/import.html:38 +msgid "Export your book notes" +msgstr "अपने बुक नोट एक्सपोर्ट करें" + +#: account/import.html:39 +msgid "Download a copy of your book notes." +msgstr "अपने बुक नोट्स की एक कॉपी डाउनलोड करें।" + +#: account/import.html:39 +msgid "What are book notes?" +msgstr "बुक नोट्स क्या होते हैं?" + +#: account/import.html:49 +msgid "Export your reviews" +msgstr "अपनी समीक्षाएँ एक्सपोर्ट करें" + +#: account/import.html:50 +msgid "Download a copy of your review tags." +msgstr "अपने समीक्षा टैग की एक कॉपी डाउनलोड करें।" + +#: account/import.html:50 +msgid "What are review tags?" +msgstr "समीक्षा टैग क्या हैं?" + +#: account/import.html:60 +msgid "Export your list overview" +msgstr "अपनी लिस्ट का ओवरव्यू एक्सपोर्ट करें" + +#: account/import.html:61 +msgid "Download a summary of your lists and their contents." +msgstr "अपनी सूचियों और उनकी सामग्री का सारांश डाउनलोड करें।" + +#: account/import.html:61 +msgid "What are lists?" +msgstr "सूचियाँ क्या हैं?" + +#: account/import.html:71 +msgid "Export your star ratings" +msgstr "अपनी स्टार रेटिंग एक्सपोर्ट करें" + +#: account/import.html:72 +msgid "Download a copy of your star ratings" +msgstr "अपनी स्टार रेटिंग की एक कॉपी डाउनलोड करें" + +#: account/import.html:72 +msgid "What are star ratings?" +msgstr "स्टार रेटिंग क्या होती हैं?" + +#: account/loans.html:54 +msgid "You've not checked out any books at this moment." +msgstr "आपने इस समय कोई किताब नहीं देखी है।" + +#: account/loans.html:66 +msgid "Loan actions" +msgstr "लोन से जुड़ी कार्रवाइयाँ" + +#: ManageLoansButtons.html:13 account/loans.html:93 +#, python-format +msgid "There is one person waiting for this book." +msgid_plural "There are %(n)d people waiting for this book." +msgstr[0] "एक व्यक्ति इस किताब का इंतज़ार कर रहा है।" +msgstr[1] "%(n)d लोग इस पुस्तक का इंतजार कर रहे हैं।" + +#: account/loans.html:99 +msgid "Not yet downloaded." +msgstr "अभी तक डाउनलोड नहीं किया गया है।" + +#: account/loans.html:100 +msgid "Download Now" +msgstr "डाक डाउनलोड करें" + +#: account/loans.html:109 +msgid "Return via
Adobe Digital Editions" +msgstr "Adobe Digital Editions के ज़रिए वापसी
" + +#: account/loans.html:120 +msgid "Books You're Waiting For" +msgstr "जिन किताबों का आप इंतज़ार कर रहे हैं" + +#: account/loans.html:127 +msgid "You are not waiting for any books at this moment." +msgstr "आप इस समय किसी भी किताब का इंतजार नहीं कर रहे हैं।" + +#: account/loans.html:130 +msgid "Leave the Waiting List" +msgstr "प्रतीक्षा सूची छोड़ें" + +#: account/loans.html:130 +msgid "Are you sure you want to leave the waiting list of
TITLE?" +msgstr "क्या आप वाकई
TITLE की प्रतीक्षा सूची छोड़ना चाहते हैं?" + +#: account/loans.html:149 admin/loans_table.html:43 admin/menu.html:33 merge_queue/merge_queue.html:59 +msgid "Status" +msgstr "स्थिति" + +#: RecentChangesAdmin.html:23 account/loans.html:150 admin/loans_table.html:47 +msgid "Actions" +msgstr "कार्रवाईयां" + +#: account/loans.html:172 +#, python-format +msgid "Waiting for 1 day" +msgid_plural "Waiting for %(count)d days" +msgstr[0] "1 दिन का इंतजार" +msgstr[1] "%(count)d दिनों की प्रतीक्षा" + +#: account/loans.html:179 +msgid "You are the next person to receive this book." +msgstr "आप इस पुस्तक को प्राप्त करने वाले अगले व्यक्ति हैं।" + +#: ManageWaitlistButton.html:21 account/loans.html:181 +#, python-format +msgid "There is one person ahead of you in the waiting list." +msgid_plural "There are %(count)d people ahead of you in the waiting list." +msgstr[0] "प्रतीक्षा सूची में आपसे आगे एक व्यक्ति है।" +msgstr[1] "प्रतीक्षा सूची में आपसे आगे %(count)d लोग हैं।" + +#: account/loans.html:190 +msgid "You have less than an hour to borrow it." +msgstr "आपके पास इसे उधार लेने के लिए एक घंटे से भी कम समय है।" + +#: account/loans.html:192 +#, python-format +msgid "You have one more hour to borrow it." +msgid_plural "You have %(hours)d more hours to borrow it." +msgstr[0] "इसे उधार लेने के लिए आपके पास एक घंटा और है।" +msgstr[1] "इसे उधार लेने के लिए आपके पास %(hours)d अधिक घंटे हैं।" + +#: account/loans.html:199 +msgid "Borrow Now" +msgstr "अभी उधार लें" + +#: account/loans.html:204 +msgid "Leave the waiting list?" +msgstr "वेटिंग लिस्ट छोड़ दें?" + +#: account/loans.html:218 home/index.html:34 +msgid "Books We Love" +msgstr "वे किताबें जिन्हें हम पसंद करते हैं" + +#: account/mybooks.html:19 account/sidebar.html:33 +msgid "My Reading Stats" +msgstr "मेरे पढ़ने के आँकड़े" + +#: account/mybooks.html:23 account/sidebar.html:34 +msgid "Import & Export Options" +msgstr "इम्पोर्ट और एक्सपोर्ट विकल्प" + +#: account/mybooks.html:35 +#, python-format +msgid "Set %(year_span)s reading goal" +msgstr "%(year_span)s रीडिंग लक्ष्य सेट करें" + +#: account/mybooks.html:69 +msgid "No books are on this shelf" +msgstr "इस शेल्फ पर कोई किताबें नहीं हैं" + +#: account/mybooks.html:73 +msgid "My Loans" +msgstr "मेरी पुस्तकें" + +#: account/not_verified.html:7 account/not_verified.html:18 account/verify/failed.html:10 +msgid "Oops!" +msgstr "उफ़!" + +#: account/not_verified.html:36 account/verify/failed.html:29 +msgid "Resend the verification email" +msgstr "सत्यापन ईमेल फिर से भेजें" + +#: BookByline.html:10 SearchResultsWork.html:68 account/notes.html:25 account/observations.html:26 +msgid "Unknown author" +msgstr "अज्ञात लेखक" + +#: account/notes.html:35 +msgid "My notes for an edition of " +msgstr "संस्करण के लिए मेरे नोट्स " + +#: account/notes.html:39 +msgid "Title Missing" +msgstr "शीर्षक मौजूद नहीं है" + +#: account/notes.html:46 type/work/editions.html:36 +msgid "Publish date unknown" +msgstr "प्रकाशित करने की तारीख अज्ञात है" + +#: account/notes.html:48 books/edition-sort.html:63 books/works-show.html:30 type/work/editions.html:38 +msgid "Publisher unknown" +msgstr "प्रकाशक अज्ञात" + +#: account/notes.html:53 +#, python-format +msgid "in %(language)s" +msgstr "%(language)s में" + +#: NotesModal.html:42 account/notes.html:66 +msgid "Delete Note" +msgstr "इस नोट को मिटाएं" + +#: NotesModal.html:43 account/notes.html:67 +msgid "Save Note" +msgstr "नोट सेव करें" + +#: account/notes.html:75 +msgid "No notes found." +msgstr "कोई नोट नहीं मिला।" + +#: account/notifications.html:7 account/notifications.html:16 +msgid "Notifications from Open Library" +msgstr "ओपन लाइब्रेरी से सूचनाएँ" + +#: account/notifications.html:14 +msgid "Notifications" +msgstr "नोटिफ़िकेशन" + +#: account/notifications.html:31 +msgid "" +"Notifications are connected to Lists at the moment. If one of the books on your list gets edited, or a " +"new book comes into the catalog, we'll let you know, if you want." +msgstr "" +"नोटिफ़िकेशन फ़िलहाल लिस्ट से जुड़े हुए हैं। यदि आपकी सूची में से कोई एक पुस्तक संपादित हो जाती है, या कोई नई पुस्तक सूची में " +"आती है, तो हम आपको बता देंगे, यदि आप चाहें।" + +#: account/notifications.html:35 +msgid "Would you like to receive very occasional emails from Open Library?" +msgstr "क्या आप ओपन लाइब्रेरी से बहुत ही सामयिक ईमेल प्राप्त करना चाहेंगे?" + +#: account/notifications.html:42 +msgid "No, thank you" +msgstr "नहीं, धन्यवाद" + +#: account/notifications.html:49 +msgid "Yes! Please!" +msgstr "हाँ! ज़रूर!" + +#: EditButtons.html:21 EditButtonsMacros.html:24 account/notifications.html:57 account/privacy.html:54 +#: covers/manage.html:51 +msgid "Save" +msgstr "सेव करें" + +#: account/observations.html:43 +msgid "Delete" +msgstr "डिलीट करें" + +#: account/observations.html:44 +msgid "Update Reviews" +msgstr "समीक्षाएँ अपडेट करें" + +#: account/observations.html:52 +msgid "No observations found." +msgstr "कोई अवलोकन नहीं मिला।" + +#: account/privacy.html:7 +msgid "Your Privacy on Open Library" +msgstr "ओपन लाइब्रेरी पर आपकी निजता" + +#: account/privacy.html:16 +msgid "Privacy Settings" +msgstr "गोपनीयता सेटिंग्स" + +#: account/privacy.html:39 +msgid "Yes" +msgstr "हाँ" + +#: account/privacy.html:46 books/edit/edition.html:306 +msgid "No" +msgstr "नहीं" + +#: account/reading_log.html:12 +#, python-format +msgid "Books %(username)s is reading" +msgstr "किताबें %(username)s पढ़ रही हैं" + +#: account/reading_log.html:13 +#, python-format +msgid "" +"%(username)s is reading %(total)d books. Join %(username)s on OpenLibrary.org and tell the world what " +"you're reading." +msgstr "" +"%(username)s %(total)d किताबें पढ़ रहा है। OpenLibrary.org पर %(username)s से जुड़ें और दुनिया को बताएं कि आप क्या " +"पढ़ रहे हैं।" + +#: account/reading_log.html:15 +#, python-format +msgid "Books %(username)s wants to read" +msgstr "किताबें %(username)s पढ़ना चाहता है" + +#: account/reading_log.html:16 +#, python-format +msgid "" +"%(username)s wants to read %(total)d books. Join %(username)s on OpenLibrary.org and share the books " +"that you'll soon be reading!" +msgstr "" +"%(username)s %(total)d किताबें पढ़ना चाहता है। OpenLibrary.org पर %(username)s से जुड़ें और उन पुस्तकों को साझा करें " +"जिन्हें आप जल्द ही पढ़ेंगे!" + +#: account/reading_log.html:18 +#, python-format +msgid "Books %(username)s has read" +msgstr "पुस्तकें %(username)s ने पढ़ी हैं" + +#: account/reading_log.html:19 +#, python-format +msgid "" +"%(username)s has read %(total)d books. Join %(username)s on OpenLibrary.org and tell the world about " +"the books that you care about." +msgstr "" +"%(username)s ने %(total)d किताबें पढ़ी हैं। OpenLibrary.org पर %(username)s से जुड़ें और दुनिया को उन किताबों के बारे " +"में बताएं जिनकी आपको परवाह है।" + +#: account/reading_log.html:21 +#, python-format +msgid "Books %(userdisplayname)s is sponsoring" +msgstr "पुस्तकें %(userdisplayname)s प्रायोजित कर रही है" + +#: account/reading_log.html:34 +msgid "Search your reading log" +msgstr "अपना रीडिंग लॉग खोजें" + +#: account/reading_log.html:42 search/sort_options.html:8 +msgid "Sorting by" +msgstr "इसके आधार पर छांटा जा रहा है" + +#: account/reading_log.html:44 account/reading_log.html:48 +msgid "Date Added (newest)" +msgstr "जोड़ने की तारीख (सबसे नया)" + +#: account/reading_log.html:46 account/reading_log.html:50 +msgid "Date Added (oldest)" +msgstr "तारीख जोड़ी गई (सबसे पुराना)" + +#: account/reading_log.html:70 +msgid "You haven't added any books to this shelf yet." +msgstr "आपने अभी तक इस शेल्फ में कोई किताब नहीं जोड़ी है।" + +#: account/reading_log.html:71 +msgid "" +"
Search for a book to add to your reading log. Learn more about the reading log." +msgstr "" +"अपने रीडिंग लॉग में जोड़ने के लिए एक किताब खोजें। रीडिंग लॉग के बारे में और जानें।" + +#: account/readinglog_stats.html:8 account/readinglog_stats.html:95 +#, python-format +msgid "\"%(shelf_name)s\" Stats" +msgstr "\"%(shelf_name)s \"आँकड़े" + +#: account/readinglog_stats.html:97 +#, python-format +msgid "" +"Displaying stats about %d books. Note all charts show only the top 20 bars. Note " +"reading log stats are private." +msgstr "" +"%d पुस्तकों के बारे में आँकड़े प्रदर्शित करना। ध्यान दें कि सभी चार्ट केवल शीर्ष 20 बार दिखाते हैं। ध्यान दें " +"कि लॉग आँकड़े पढ़ना निजी है।" + +#: account/readinglog_stats.html:102 +msgid "Author Stats" +msgstr "लेखक के आँकड़े" + +#: account/readinglog_stats.html:107 +msgid "Most Read Authors" +msgstr "सबसे ज़्यादा पढ़े जाने वाले लेखक" + +#: account/readinglog_stats.html:108 +msgid "Works by Author Sex" +msgstr "लेखक के लिंग के आधार पर काम" + +#: account/readinglog_stats.html:109 +msgid "Works by Author Ethnicity" +msgstr "लेखक जातीयता द्वारा कार्य" + +#: account/readinglog_stats.html:110 +msgid "Works by Author Country of Citizenship" +msgstr "लेखक द्वारा काम करता है नागरिकता का देश" + +#: account/readinglog_stats.html:111 +msgid "Works by Author Country of Birth" +msgstr "लेखक के जन्म के देश के अनुसार काम करता है" + +#: account/readinglog_stats.html:121 +msgid "" +"Demographic statistics powered by Wikidata. Here's a sample of the query used." +msgstr "" +"विकिडेटा द्वारा संचालित जनसांख्यिकीय आँकड़े। यहाँ इस्तेमाल की गई क्वेरी " +"का एक नमूना दिया गया है।" + +#: account/readinglog_stats.html:123 +msgid "Work Stats" +msgstr "कार्य आँकड़े" + +#: account/readinglog_stats.html:129 +msgid "Works by Subject" +msgstr "विषय के आधार पर काम" + +#: account/readinglog_stats.html:130 +msgid "Works by People" +msgstr "लोगों द्वारा किए गए काम" + +#: account/readinglog_stats.html:131 +msgid "Works by Places" +msgstr "जगहों के हिसाब से पेशे में हैं" + +#: account/readinglog_stats.html:132 +msgid "Works by Time Period" +msgstr "समय अवधि के हिसाब से काम करता है" + +#: account/readinglog_stats.html:144 +msgid "Matching works" +msgstr "मेल खाने वाले काम" + +#: account/readinglog_stats.html:148 +msgid "Click on a bar to see matching works" +msgstr "मेल खाने वाले कामों को देखने के लिए बार पर क्लिक करें" + +#: account/sidebar.html:20 +msgid "Loan History" +msgstr "लोन का इतिहास" + +#: account/sidebar.html:30 +msgid "My Notes" +msgstr "मेरे नोट्स" + +#: account/sidebar.html:31 +msgid "My Reviews" +msgstr "मेरी समीक्षाएँ" + +#: account/sidebar.html:42 +msgid "Sponsoring" +msgstr "प्रायोजन" + +#: EditionNavBar.html:30 SearchNavigation.html:28 account/sidebar.html:45 lib/nav_head.html:31 +#: lib/nav_head.html:96 lists/home.html:7 lists/home.html:10 lists/widget.html:67 +#: recentchanges/index.html:41 type/list/embed.html:28 type/list/view_body.html:48 type/user/view.html:35 +#: type/user/view.html:66 +msgid "Lists" +msgstr "सूचियाँ" + +#: account/sidebar.html:45 +msgid "See All" +msgstr "सभी देखें" + +#: account/sidebar.html:47 +msgid "Untitled list" +msgstr "बिना शीर्षक वाली लिस्ट" + +#: account/verify.html:7 +msgid "Verification email sent" +msgstr "वेरिफिकेशन ईमेल भेजा गया" + +#: account.py:419 account.py:457 account/password/reset_success.html:10 account/verify.html:9 +#: account/verify/activated.html:10 account/verify/success.html:10 +#, python-format +msgid "Hi, %(user)s" +msgstr "नमस्ते, %(user)s" + +#: account/email/forgot-ia.html:10 account/email/forgot.html:10 +msgid "Forgot Your Internet Archive Email?" +msgstr "अपना इंटरनेट आर्काइव ईमेल भूल गए?" + +#: account/email/forgot-ia.html:12 +msgid "Please enter your Open Library email and password, and we’ll retrieve your Archive.org email." +msgstr "कृपया अपना ओपन लाइब्रेरी ईमेल और पासवर्ड दर्ज करें, और हम आपका Archive.org ईमेल पुनः प्राप्त करेंगे।" + +#: account/email/forgot-ia.html:17 +msgid "Your email is:" +msgstr "आपका ईमेल है:" + +#: account/email/forgot-ia.html:18 +msgid "Return to Log In?" +msgstr "लॉग इन पर लौटें?" + +#: account/email/forgot-ia.html:27 +msgid "Open Library Email" +msgstr "ओपन लाइब्रेरी ईमेल" + +#: account/email/forgot-ia.html:38 +msgid "Retrieve Archive.org Email" +msgstr "Archive.org ईमेल प्राप्त करें" + +#: account/email/forgot-ia.html:43 account/password/forgot.html:10 account/password/forgot.html:11 +msgid "Forgot your Archive.org Password?" +msgstr "अपना Archive.org पासवर्ड भूल गए?" + +#: account/email/forgot.html:15 +msgid "Forgot Your Open Library Email?" +msgstr "अपना ओपन लाइब्रेरी ईमेल भूल गए?" + +#: account/password/forgot.html:7 account/password/sent.html:7 +msgid "Username/Password Reminder" +msgstr "यूज़रनेम/पासवर्ड रिमाइंडर" + +#: account/password/forgot.html:11 +msgid "Click here." +msgstr "यहाँ क्लिक करें।" + +#: account/password/forgot.html:23 +msgid "Send It" +msgstr "इसे भेजें" + +#: account/password/reset.html:7 account/password/reset.html:10 admin/people/view.html:161 +msgid "Reset Password" +msgstr "पासवर्ड रीसेट करें" + +#: account/password/reset.html:15 +msgid "Please enter a new password for your Open Library account." +msgstr "कृपया अपने ओपन लाइब्रेरी खाते के लिए एक नया पासवर्ड दर्ज करें।" + +#: account/password/reset.html:23 +msgid "Reset" +msgstr "रीसेट करें" + +#: account/password/reset_success.html:7 +msgid "Password Reset Successful" +msgstr "पासवर्ड रीसेट सफल" + +#: account/password/reset_success.html:14 +msgid "" +"Your password has been updated. Please log in to continue." +msgstr "" +"आपका पासवर्ड अपडेट कर दिया गया है। जारी रखने के लिए कृपया लॉग इन करें।" + +#: account/password/sent.html:10 +msgid "Done." +msgstr "हो गया।" + +#: account/password/sent.html:14 +#, python-format +msgid "" +"We've sent an email to %(email)s with a reminder of your username and instructions to help you reset " +"your Open Library password. Please check your inbox for a note from us." +msgstr "" +"हमने आपके ओपन लाइब्रेरी पासवर्ड को रीसेट करने में मदद के लिए आपके उपयोगकर्ता नाम और निर्देशों की याद के साथ %(email)s " +"को एक ईमेल भेजा है। कृपया हमारी ओर से नोट के लिए अपना इनबॉक्स जांचें।" + +#: account/verify/activated.html:7 +msgid "Account already activated" +msgstr "अकाउंट पहले ही ऐक्टिवेट हो चुका है" + +#: account/verify/activated.html:15 +#, python-format +msgid "Your account has been activated already. Please log in to continue." +msgstr "आपका अकाउंट पहले ही ऐक्टिवेट कर दिया गया है। जारी रखने के लिए कृपया लॉग इन करें।" + +#: account/verify/failed.html:7 +msgid "Email Verification Failed" +msgstr "ईमेल वेरिफ़िकेशन विफल रहा" + +#: account/verify/failed.html:14 +msgid "Your email address couldn't be verified. Your verification link seems invalid or expired." +msgstr "आपके ईमेल की पुष्टि नहीं की जा सकी। ऐसा लगता है कि आपका वेरिफ़िकेशन लिंक अमान्य है या उसकी समय - सीमा खत्म हो गई." + +#: account/verify/failed.html:22 +msgid "Enter your email address here" +msgstr "अपना ईमेल यहां पर डालें" + +#: account/verify/failed.html:25 +msgid "No user registered with this email address." +msgstr "इस ईमेल के साथ कोई उपयोगकर्ता पंजीकृत नहीं है।" + +#: account/verify/success.html:7 +msgid "Email Verification Successful" +msgstr "ईमेल वेरिफ़िकेशन सफल रहा" + +#: account/verify/success.html:15 +#, python-format +msgid "Yay! Your email address has been verified. Please log in to continue." +msgstr "हाँ! आपका ईमेल सत्यापित कर दिया गया है। जारी रखने के लिए कृपया लॉग इन करें।" + +#: admin/attach_debugger.html:22 +msgid "Waiting for debugger to attach..." +msgstr "डिबगर के अटैच होने का इंतज़ार किया जा रहा है..." + +#: admin/attach_debugger.html:22 +msgid "Start" +msgstr "शुरू करें" + +#: admin/imports-add.html:26 admin/ip/view.html:95 admin/people/edits.html:92 +#: check_ins/check_in_form.html:77 check_ins/reading_goal_form.html:16 covers/add.html:78 +msgid "Submit" +msgstr "सबमिट" + +#: admin/loans_table.html:17 +msgid "BookReader" +msgstr "BookReader" + +#: admin/loans_table.html:18 book_providers/ia_download_options.html:12 +#: book_providers/openstax_download_options.html:13 books/edit/edition.html:677 +msgid "PDF" +msgstr "PDF" + +#: admin/loans_table.html:19 book_providers/gutenberg_download_options.html:17 +#: book_providers/ia_download_options.html:16 book_providers/standard_ebooks_download_options.html:15 +#: books/edit/edition.html:676 +msgid "ePub" +msgstr "ePub" + +#: admin/loans_table.html:31 +#, python-format +msgid "%d Current Loan" +msgid_plural "%d Current Loans" +msgstr[0] "%d वर्तमान ऋण" +msgstr[1] "%d वर्तमान ऋण" + +#: RecentChanges.html:18 RecentChangesUsers.html:21 admin/ip/view.html:45 admin/loans_table.html:45 +#: admin/people/edits.html:42 recentchanges/render.html:31 recentchanges/updated_records.html:29 +msgid "What" +msgstr "आपकी पसंद और नापसंद क्या हैं" + +#: admin/menu.html:19 +msgid "Admin Center" +msgstr "एडमिन सेंटर" + +#: admin/menu.html:21 +msgid "Sponsorship" +msgstr "प्रायोजन" + +#: admin/menu.html:23 +msgid "Waiting Lists" +msgstr "प्रतीक्षा सूची" + +#: admin/menu.html:24 +msgid "Block IPs" +msgstr "IP ब्लॉक करें" + +#: admin/menu.html:25 +msgid "Spam Words" +msgstr "स्पैम के अवांछनीय शब्द" + +#: admin/menu.html:26 +msgid "Solr" +msgstr "Solr" + +#: admin/menu.html:27 +msgid "Imports" +msgstr "आयात" + +#: admin/menu.html:29 +msgid "Graphs" +msgstr "ग्राफ़" + +#: admin/menu.html:30 +msgid "Inspect store" +msgstr "स्टोर का मुआयना करें" + +#: admin/menu.html:31 +msgid "Inspect memcache" +msgstr "मेमकैच का निरीक्षण करें" + +#: admin/people/view.html:171 admin/profile.html:7 +msgid "Admin" +msgstr "एडमिन" + +#: admin/solr.html:31 +msgid "Enter the keys to reindex in Solr" +msgstr "Solr में फिर से इंडेक्स करने की चाबियाँ डालें" + +#: admin/solr.html:32 +msgid "Write one entry per line" +msgstr "प्रति पंक्ति एक प्रविष्टि लिखें" + +#: admin/solr.html:41 +msgid "Update" +msgstr "अपडेट करें" + +#: FormatExpiry.html:23 admin/waitinglists.html:29 +msgid "Expires" +msgstr "समाप्त हो रहा" + +#: admin/waitinglists.html:30 +msgid "Loan Status" +msgstr "लोन की स्थिति" + +#: admin/ip/index.html:18 +msgid "List of Banned IPs" +msgstr "प्रतिबंधित आईपी की सूची" + +#: admin/ip/index.html:22 admin/people/index.html:57 +msgid "Date/Time" +msgstr "तारीख/समय" + +#: admin/ip/index.html:23 +msgid "IP address" +msgstr "IP एड्रेस" + +#: admin/ip/index.html:26 +msgid "# of edits" +msgstr "# संपादन" + +#: admin/ip/view.html:21 +#, python-format +msgid "Block %s" +msgstr "Block %s" + +#: EditButtons.html:9 EditButtonsMacros.html:11 admin/ip/view.html:86 admin/people/edits.html:83 +msgid "Please, leave a short note about what you changed:" +msgstr "कृपया इस बारे में एक छोटा नोट छोड़ें कि आपने क्या बदला है:" + +#: RecentChanges.html:61 RecentChangesAdmin.html:56 RecentChangesUsers.html:58 +#: admin/people/edits.html:100 recentchanges/render.html:66 +msgid "Older" +msgstr "पुराना" + +#: RecentChanges.html:64 RecentChangesAdmin.html:58 RecentChangesAdmin.html:60 RecentChangesUsers.html:61 +#: admin/people/edits.html:103 recentchanges/render.html:69 +msgid "Newer" +msgstr "नया" + +#: admin/people/index.html:16 +msgid "Find Account" +msgstr "अकाउंट ढूँढें" + +#: admin/people/index.html:32 admin/people/view.html:151 +msgid "Email Address:" +msgstr "ईमेल पता:" + +#: admin/people/index.html:51 +msgid "Recent Accounts" +msgstr "हाल के अकाउंट" + +#: admin/people/index.html:58 type/author/edit.html:32 type/language/view.html:27 +msgid "Name" +msgstr "नाम" + +#: admin/people/index.html:59 +msgid "email" +msgstr "ईमेल" + +#: admin/people/index.html:60 +msgid "Edits" +msgstr "संपादित करता" + +#: admin/people/index.html:75 admin/people/view.html:247 +msgid "Edit History" +msgstr "इतिहास में बदलाव करें" + +#: ReadingLogDropper.html:149 admin/people/view.html:147 +msgid "Name:" +msgstr "नाम :" + +#: admin/people/view.html:173 +msgid "Bot" +msgstr "बोट" + +#: admin/people/view.html:191 +msgid "# Edits" +msgstr "संपादित करता" + +#: admin/people/view.html:195 +msgid "Member Since:" +msgstr "से सदस्ये:" + +#: admin/people/view.html:204 +msgid "Last Login:" +msgstr "आखरी लॉगइन:" + +#: admin/people/view.html:213 +msgid "Tags:" +msgstr "टैग :" + +#: admin/people/view.html:221 +msgid "Anonymize Account:" +msgstr "अकाउंट को अनाम करें:" + +#: admin/people/view.html:226 +msgid "Anonymize Account" +msgstr "अकाउंट को अनाम करें" + +#: admin/people/view.html:243 +msgid "Waiting Loans" +msgstr "प्रतीक्षारत ऋण" + +#: admin/people/view.html:256 +msgid "Debug Info" +msgstr "डिबग जानकारी डम्प करें" + +#: SearchNavigation.html:16 authors/index.html:9 authors/index.html:12 lib/nav_foot.html:27 +#: merge/authors.html:57 publishers/view.html:87 +msgid "Authors" +msgstr "लेखक" + +#: authors/index.html:18 +msgid "Search for an Author" +msgstr "लेखक खोजें" + +#: authors/index.html:39 search/authors.html:69 +#, python-format +msgid "about %(subjects)s" +msgstr "about %(subjects)s" + +#: authors/index.html:40 search/authors.html:70 +#, python-format +msgid "including %(topwork)s" +msgstr "%(topwork)s सहित" + +#: book_providers/gutenberg_download_options.html:11 book_providers/ia_download_options.html:9 +#: book_providers/librivox_download_options.html:9 book_providers/openstax_download_options.html:11 +#: book_providers/standard_ebooks_download_options.html:12 +msgid "Download Options" +msgstr "विकल्प डाउनलोड करें" + +#: book_providers/gutenberg_download_options.html:15 +msgid "Download an HTML from Project Gutenberg" +msgstr "प्रोजेक्ट गुटेनबर्ग से एक HTML डाउनलोड करें" + +#: book_providers/gutenberg_download_options.html:15 +#: book_providers/standard_ebooks_download_options.html:14 +msgid "HTML" +msgstr "HTML" + +#: book_providers/gutenberg_download_options.html:16 +msgid "Download a text version from Project Gutenberg" +msgstr "प्रोजेक्ट गुटेनबर्ग से टेक्स्ट वर्ज़न डाउनलोड करें" + +#: book_providers/gutenberg_download_options.html:16 book_providers/ia_download_options.html:14 +msgid "Plain text" +msgstr "सादा पाठ" + +#: book_providers/gutenberg_download_options.html:17 +msgid "Download an ePub from Project Gutenberg" +msgstr "प्रोजेक्ट गुटेनबर्ग से एक ePub डाउनलोड करें" + +#: book_providers/gutenberg_download_options.html:18 +msgid "Download a Kindle file from Project Gutenberg" +msgstr "प्रोजेक्ट गुटेनबर्ग से एक किंडल फ़ाइल डाउनलोड करें" + +#: book_providers/gutenberg_download_options.html:18 +msgid "Kindle" +msgstr "किंडल" + +#: book_providers/gutenberg_download_options.html:19 +msgid "More at Project Gutenberg" +msgstr "प्रोजेक्ट गुटेनबर्ग में और जानकारी पाएँ" + +#: book_providers/gutenberg_read_button.html:10 +msgid "Read eBook from Project Gutenberg" +msgstr "प्रोजेक्ट गुटेनबर्ग की ई - बुक पढ़ें" + +#: book_providers/gutenberg_read_button.html:21 +msgid "" +"This book is available from Project Gutenberg. Project " +"Gutenberg is a trusted book provider of classic ebooks, supporting thousands of volunteers in the " +"creation and distribution of over 60,000 free eBooks." +msgstr "" +"यह पुस्तक प्रोजेक्ट गुटेनबर्ग से उपलब्ध है। प्रोजेक्ट गुटेनबर्ग क्लासिक ई - " +"बुक्स का एक विश्वसनीय पुस्तक प्रदाता है, जो 60,000 से अधिक मुफ्त ई - बुक्स के निर्माण और वितरण में हजारों स्वयंसेवकों का " +"समर्थन करता है।" + +#: book_providers/gutenberg_read_button.html:22 book_providers/librivox_read_button.html:25 +#: book_providers/openstax_read_button.html:22 book_providers/standard_ebooks_read_button.html:22 +msgid "Learn more" +msgstr "और जानें" + +#: BookPreview.html:20 DonateModal.html:15 NotesModal.html:32 ObservationsModal.html:32 +#: ReadingLogDropper.html:144 ShareModal.html:25 book_providers/gutenberg_read_button.html:24 +#: book_providers/librivox_read_button.html:27 book_providers/openstax_read_button.html:24 +#: book_providers/standard_ebooks_read_button.html:24 covers/author_photo.html:22 +#: covers/book_cover.html:39 covers/book_cover_single_edition.html:34 covers/book_cover_work.html:30 +#: covers/change.html:44 lib/markdown.html:12 lists/lists.html:53 merge_queue/merge_queue.html:121 +msgid "Close" +msgstr "बंद करें" + +#: book_providers/ia_download_options.html:12 +msgid "Download a PDF from Internet Archive" +msgstr "इंटरनेट आर्काइव से एक पीडीएफ डाउनलोड करें" + +#: book_providers/ia_download_options.html:14 +msgid "Download a text version from Internet Archive" +msgstr "इंटरनेट आर्काइव से एक टेक्स्ट संस्करण डाउनलोड करें" + +#: book_providers/ia_download_options.html:16 +msgid "Download an ePub from Internet Archive" +msgstr "इंटरनेट आर्काइव से एक ePub डाउनलोड करें" + +#: book_providers/ia_download_options.html:18 +msgid "Download a MOBI file from Internet Archive" +msgstr "इंटरनेट आर्काइव से MOBI फ़ाइल डाउनलोड करें" + +#: book_providers/ia_download_options.html:18 +msgid "MOBI" +msgstr "MOBI" + +#: book_providers/ia_download_options.html:19 +msgid "Download open DAISY from Internet Archive (print-disabled format)" +msgstr "इंटरनेट आर्काइव से डेज़ी डाउनलोड करें (प्रिंट - अक्षम प्रारूप)" + +#: book_providers/ia_download_options.html:19 type/list/embed.html:85 +msgid "DAISY" +msgstr "DAISY" + +#: book_providers/librivox_download_options.html:12 +msgid "Download a ZIP file of the whole book from Internet Archive" +msgstr "इंटरनेट आर्काइव से पूरी किताब की एक ज़िप फ़ाइल डाउनलोड करें" + +#: book_providers/librivox_download_options.html:12 +msgid "Whole Book MP3" +msgstr "पूरी किताब MP3" + +#: book_providers/librivox_download_options.html:13 +msgid "Get the RSS Feed from LibriVox" +msgstr "LibriVox से RSS फ़ीड प्राप्त करें" + +#: book_providers/librivox_download_options.html:13 +msgid "RSS Feed" +msgstr "RSS फ़ीड" + +#: book_providers/librivox_download_options.html:14 +msgid "More at LibriVox" +msgstr "LibriVox पर और जानकारी" + +#: book_providers/librivox_read_button.html:9 +msgid "Listen to a reading from LibriVox" +msgstr "LibriVox की एक रीडिंग सुनें" + +#: book_providers/librivox_read_button.html:17 +msgid "Audiobook" +msgstr "ऑडियोबुक" + +#: book_providers/librivox_read_button.html:24 +msgid "" +"This book is available from LibriVox. LibriVox is a trusted book " +"provider of public domain audiobooks, narrated by volunteers, distributed for free on the internet." +msgstr "" +"यह पुस्तक LibriVox से उपलब्ध है। LibriVox सार्वजनिक डोमेन ऑडियोबुक्स का " +"एक विश्वसनीय पुस्तक प्रदाता है, जिसे स्वयंसेवकों द्वारा सुनाया जाता है, जिसे इंटरनेट पर मुफ्त में वितरित किया जाता है।" + +#: book_providers/openstax_download_options.html:13 +msgid "Download PDF from OpenStax" +msgstr "OpenStax से PDF डाउनलोड करें" + +#: book_providers/openstax_download_options.html:14 +msgid "More at OpenStax" +msgstr "OpenStax पर और जानकारी" + +#: book_providers/openstax_read_button.html:10 +msgid "Read eBook from OpenStax" +msgstr "OpenStax से ई - बुक पढ़ें" + +#: book_providers/openstax_read_button.html:21 +msgid "" +"This book is available from OpenStax. OpenStax is a trusted " +"book provider and a 501(c)(3) nonprofit charitable corporation dedicated to providing free high-" +"quality, peer-reviewed, openly licensed textbooks online." +msgstr "" +"यह पुस्तक OpenStax से उपलब्ध है। OpenStax एक भरोसेमंद बुक प्रोवाइडर " +"और 501(c )( 3) निर्लाभ चैरिटेबल कॉरपोरेशन है, जो मुफ़्त उच्च - गुणवत्ता वाली, सहकर्मी - समीक्षा की गई, खुले तौर पर " +"लाइसेंस प्राप्त पाठ्यपुस्तकें ऑनलाइन प्रदान करने के लिए समर्पित है।" + +#: book_providers/standard_ebooks_download_options.html:14 +msgid "Download an HTML from Standard Ebooks" +msgstr "स्टैंडर्ड ई - बुक्स से एक एचटीएमएल डाउनलोड करें" + +#: book_providers/standard_ebooks_download_options.html:15 +msgid "Download an ePub from Standard Ebook." +msgstr "स्टैंडर्ड ईबुक से एक ई - पब डाउनलोड करें।" + +#: book_providers/standard_ebooks_download_options.html:16 +msgid "Download a Kindle file from Standard Ebooks" +msgstr "स्टैंडर्ड ई - बुक्स से एक किंडल फ़ाइल डाउनलोड करें" + +#: book_providers/standard_ebooks_download_options.html:16 +msgid "Kindle (azw3)" +msgstr "किंडल (azw3)" + +#: book_providers/standard_ebooks_download_options.html:17 +msgid "Download a Kobo file from Standard Ebooks" +msgstr "स्टैंडर्ड ई - बुक्स से कोबो फ़ाइल डाउनलोड करें" + +#: book_providers/standard_ebooks_download_options.html:17 +msgid "Kobo (kepub)" +msgstr "Kobo (kepub)" + +#: book_providers/standard_ebooks_download_options.html:18 +msgid "View the source code for this Standard Ebook at GitHub" +msgstr "GitHub पर इस मानक ईबुक के लिए स्रोत कोड देखें" + +#: Subnavigation.html:13 book_providers/standard_ebooks_download_options.html:18 +msgid "Source Code" +msgstr "काल ग्राफ" + +#: book_providers/standard_ebooks_download_options.html:19 +msgid "More at Standard Ebooks" +msgstr "स्टैंडर्ड ई - बुक्स पर और जानें" + +#: book_providers/standard_ebooks_read_button.html:10 +msgid "Read eBook from Standard eBooks" +msgstr "स्टैंडर्ड ई - बुक्स से ई - बुक पढ़ें" + +#: book_providers/standard_ebooks_read_button.html:21 +msgid "" +"This book is available from Standard Ebooks. Standard " +"Ebooks is a trusted book provider and a volunteer-driven project that produces new editions of public " +"domain ebooks that are lovingly formatted, open source, free of copyright restrictions, and free of " +"cost." +msgstr "" +"यह पुस्तक मानक ई - बुक्स से उपलब्ध है। स्टैंडर्ड ईबुक्स एक विश्वसनीय " +"पुस्तक प्रदाता और एक स्वयंसेवक - संचालित परियोजना है जो सार्वजनिक डोमेन ईबुक्स के नए संस्करणों का उत्पादन करती है जो " +"प्यार से स्वरूपित, खुले स्रोत, कॉपीराइट प्रतिबंधों से मुक्त और नि: शुल्क हैं।" + +#: books/RelatedWorksCarousel.html:19 +msgid "You might also like" +msgstr "यह भी पढ़ कर सकते हैं" + +#: books/RelatedWorksCarousel.html:25 +msgid "More by this author" +msgid_plural "More by these authors" +msgstr[0] "इन लेखकों द्वारा और अधिक" +msgstr[1] "इस लेखक द्वारा और अधिक" + +#: books/add.html:7 books/check.html:10 +msgid "Add a book" +msgstr "एक किताब जोड़ें" + +#: books/add.html:10 +msgid "Invalid ISBN checksum digit" +msgstr "ISBN चेकसम अंक अमान्य है" + +#: books/add.html:11 +msgid "ID must be exactly 10 characters [0-9 or X]. For example 0-19-853453-1 or 0198534531" +msgstr "आईडी ठीक 10 कैरेक्टर [0 -9 या X] की होनी चाहिए। उदाहरण के लिए 0 -19 -853453 -1 या 0198534531" + +#: books/add.html:12 +msgid "ID must be exactly 13 characters [0-9]. For example 978-3-16-148410-0 or 9783161484100" +msgstr "आईडी बिल्कुल 13 कैरेक्टर [0 -9] की होनी चाहिए। उदाहरण के लिए 978 -3 -16 -148410 -0 या 9783161484100" + +#: books/add.html:17 +msgid "Add a book to Open Library" +msgstr "ओपन लाइब्रेरी में एक किताब जोड़ें" + +#: books/add.html:19 +msgid "We require a minimum set of fields to create a new record. These are those fields." +msgstr "नया रिकॉर्ड बनाने के लिए हमें फ़ील्ड के न्यूनतम सेट की ज़रूरत होती है। ये वे क्षेत्र हैं।" + +#: books/add.html:31 books/edit.html:86 books/edit/edition.html:96 lib/nav_head.html:92 +#: search/advancedsearch.html:20 type/about/edit.html:19 type/i18n/edit.html:64 type/page/edit.html:20 +#: type/rawtext/edit.html:18 type/template/edit.html:18 +msgid "Title" +msgstr "शीर्षक" + +#: books/add.html:31 books/edit.html:86 +msgid "Use Title: Subtitle to add a subtitle." +msgstr "उपशीर्षक जोड़ने के लिए शीर्षक: उपशीर्षक का उपयोग करें।" + +#: books/add.html:31 books/add.html:48 books/add.html:57 books/edit.html:86 books/edit/addfield.html:100 +#: books/edit/addfield.html:145 type/author/edit.html:35 +msgid "Required field" +msgstr "आवश्यक फ़ील्ड" + +#: books/add.html:42 +msgid "" +"Like, \"Agatha Christie\" or \"Jean-Paul Sartre.\" We'll also do a live check to see if we already " +"know the author." +msgstr "" +"जैसे, \"अगाथा क्रिस्टी\" या \"जीन - पॉल सार्त्र।\" हम यह देखने के लिए एक लाइव जाँच भी करेंगे कि क्या हम पहले से ही लेखक " +"को जानते हैं।" + +#: books/add.html:48 books/edit/edition.html:119 +msgid "Who is the publisher?" +msgstr "प्रकाशक कौन है?" + +#: books/add.html:48 books/edit/edition.html:120 +msgid "For example" +msgstr "उदाहरण के लिए" + +#: books/add.html:57 books/edit/edition.html:139 +msgid "When was it published?" +msgstr "यह कब प्रकाशित हुआ था?" + +#: books/add.html:57 books/edit/edition.html:140 +msgid "You should be able to find this in the first few pages of the book." +msgstr "आपको इसे पुस्तक के पहले कुछ पृष्ठों में खोजने में सक्षम होना चाहिए।" + +#: books/add.html:66 +msgid "And optionally, an ID number — like an ISBN — would be helpful..." +msgstr "और वैकल्पिक रूप से, एक आईडी नंबर - एक आईएसबीएन की तरह - मददगार होगा..." + +#: books/add.html:67 +msgid "ID Type" +msgstr "आईडी का प्रकार" + +#: books/add.html:69 +msgid "ISBN may be invalid. Click \"Add\" again to submit." +msgstr "ISBN अमान्य हो सकता है। सबमिट करने के लिए फिर से \"जोड़ें\" पर क्लिक करें।" + +#: books/add.html:72 +msgid "Select" +msgstr "चुनें" + +#: books/add.html:87 books/edit/edition.html:652 +msgid "Book URL" +msgstr "यूआरएल बुक करें" + +#: books/add.html:87 +msgid "Only fill this out if this is a web book" +msgstr "अगर यह एक वेब बुक है, तो केवल इसे भरें" + +#: books/add.html:96 +msgid "Since you are not logged in, please satisfy the reCAPTCHA below." +msgstr "चूँकि आपने लॉग इन नहीं किया है, इसलिए कृपया नीचे दिए गए reCAPTCHA को पूरा करें।" + +#: EditButtons.html:17 EditButtonsMacros.html:20 books/add.html:103 +msgid "" +"By saving a change to this wiki, you agree that your contribution is given freely to the world under " +"CC0. Yippee!" +msgstr "" +"इस विकी में बदलाव सेव करके, आप इस बात से सहमत होते हैं कि आपका योगदान CC0 के तहत दुनिया को स्वतंत्र रूप से दिया जाता है। Yippee!" + +#: books/add.html:106 +msgid "Add this book now" +msgstr "इस किताब को अभी जोड़ें" + +#: books/add.html:106 books/edit/addfield.html:85 books/edit/addfield.html:131 +#: books/edit/addfield.html:175 books/edit/edition.html:227 books/edit/edition.html:456 +#: books/edit/edition.html:521 books/edit/excerpts.html:50 covers/change.html:49 +msgid "Add" +msgstr "जोड़ें" + +#: books/author-autocomplete.html:13 +msgid "Create a new record for" +msgstr "के लिए एक नया रिकॉर्ड बनाएँ" + +#: books/author-autocomplete.html:42 +msgid "Remove this author" +msgstr "इस लेखक को हटाएँ" + +#: books/author-autocomplete.html:43 +msgid "Add another author?" +msgstr "एक और लेखक जोड़ें?" + +#: books/check.html:13 lib/nav_foot.html:41 lib/nav_head.html:38 +msgid "Add a Book" +msgstr "एक किताब जोड़ें" + +#: books/check.html:15 +#, python-format +msgid "" +"One moment... It looks like we already have some potential matches for %(title)s by " +"%(author)s." +msgstr "" +"एक क्षण रुकिए... ऐसा लगता है कि हमारे पास पहले से ही कुछ संभावित मैच हैं %(title)s के लिए " +"%(author)s द्वारा." + +#: books/check.html:17 +msgid "" +"Rather than creating a duplicate record, please click through the result you think best matches your " +"book." +msgstr "" +"डुप्लिकेट रिकॉर्ड बनाने के बजाय, कृपया उस परिणाम पर क्लिक करें जो आपको लगता है कि आपकी पुस्तक से सबसे अच्छा मेल खाता है।" + +#: books/check.html:27 books/check.html:31 +msgid "Select this book" +msgstr "इस किताब का चयन करें" + +#: books/check.html:38 +#, python-format +msgid "First published in %(year)d" +msgstr "पहली बार %(year)d में प्रकाशित हुआ" + +#: books/custom_carousel.html:41 +#, python-format +msgid " by %(name)s" +msgstr " %(name)s द्वारा" + +#: books/edit.html:30 books/edit.html:33 books/edit.html:36 +msgid "Add a little more?" +msgstr "थोड़ा और जोड़ें?" + +#: books/edit.html:31 +msgid "Thank you for adding that book! Any more information you could provide would be wonderful!" +msgstr "उस किताब को जोड़ने के लिए धन्यवाद! कोई और जानकारी जो आप प्रदान कर सकते हैं वह अद्भुत होगी!" + +#: books/edit.html:34 +#, python-format +msgid "" +"We already know about %(work_title)s, but not the %(year)s %(publisher)s edition. " +"Thanks! Do you know any more about this edition?" +msgstr "" +"हम पहले से ही %(work_title)s के बारे में जानते हैं, लेकिन %(year)s %(publisher)s संस्करण के बारे में " +"नहीं। धन्यवाद! क्या आप इस संस्करण के बारे में अधिक जानते हैं?" + +#: books/edit.html:37 +#, python-format +msgid "" +"We already know about the %(year)s %(publisher)s edition of %(work_title)s, but please, " +"tell us more!" +msgstr "" +"हम पहले से ही %(work_title)s के %(year)s %(publisher)s संस्करण के बारे में जानते हैं, लेकिन कृपया हमें " +"और बताएँ!" + +#: books/edit.html:39 +msgid "Editing..." +msgstr "संपादन..." + +#: EditButtonsMacros.html:27 books/edit.html:45 type/author/edit.html:17 type/template/edit.html:51 +msgid "Delete Record" +msgstr "इस रेकॉर्ड को मिटाएँ? QSql" + +#: books/edit.html:65 +msgid "Work Details" +msgstr "काम का ब्यौरा" + +#: books/edit.html:70 +msgid "Unknown publisher edition" +msgstr "अज्ञात प्रकाशक संस्करण" + +#: books/edit.html:80 +msgid "This Work" +msgstr "यह कार्य" + +#: books/edit.html:93 +msgid "" +"You can search by author name (like j k rowling) or by Open Library ID (like OL23919A)." +msgstr "" +"आप लेखक के नाम से (जैसे j k rowling) या ओपन लाइब्रेरी आईडी (जैसे OL23919A) से खोज सकते हैं।" + +#: books/edit.html:98 +msgid "Author editing requires javascript" +msgstr "लेखक संपादन के लिए जावास्क्रिप्ट की आवश्यकता है" + +#: books/edit.html:111 +msgid "Add Excerpts" +msgstr "अंश जोड़ें" + +#: books/edit.html:117 type/about/edit.html:39 type/author/edit.html:50 +msgid "Links" +msgstr "लिंक्स" + +#: SearchResultsWork.html:45 SearchResultsWork.html:46 books/edit/edition.html:66 +#: books/edition-sort.html:39 books/edition-sort.html:40 lists/list_overview.html:11 lists/preview.html:9 +#: lists/snippet.html:9 lists/widget.html:83 type/work/editions.html:27 +#, python-format +msgid "Cover of: %(title)s" +msgstr "कवर: %(title)s" + +#: books/edition-sort.html:49 +#, python-format +msgid "%(title)s: %(subtitle)s" +msgstr "%(title)s :%(subtitle)s" + +#: books/edition-sort.html:61 +#, python-format +msgid "Publish date unknown, %(publisher)s" +msgstr "प्रकाशित करने की तारीख अज्ञात, %(publisher)s" + +#: books/edition-sort.html:71 type/work/editions.html:47 +#, python-format +msgid "in %(languagelist)s" +msgstr "%(languagelist)s में" + +#: books/edition-sort.html:99 +msgid "Libraries near you:" +msgstr "आपके आस - पास लाइब्रेरी:" + +#: books/edit/about.html:21 +msgid "Select this subject" +msgstr "इस विषय का चयन करें" + +#: books/edit/about.html:28 +msgid "How would you describe this book?" +msgstr "आप इस पुस्तक का वर्णन कैसे करेंगे?" + +#: books/edit/about.html:29 +msgid "There's no wrong answer here." +msgstr "यहाँ कोई गलत जवाब नहीं है।" + +#: books/edit/about.html:38 +msgid "Subject keywords?" +msgstr "विषय कीवर्ड?" + +#: books/edit/about.html:39 +msgid "Please separate with commas." +msgstr "कृपया अल्पविराम के साथ अलग करें।" + +#: books/edit/about.html:39 books/edit/about.html:50 books/edit/about.html:61 books/edit/about.html:72 +#: books/edit/addfield.html:77 books/edit/addfield.html:104 books/edit/addfield.html:113 +#: books/edit/addfield.html:149 books/edit/edition.html:107 books/edit/edition.html:130 +#: books/edit/edition.html:163 books/edit/edition.html:173 books/edit/edition.html:266 +#: books/edit/edition.html:368 books/edit/edition.html:382 books/edit/edition.html:582 +#: books/edit/excerpts.html:19 books/edit/web.html:23 type/author/edit.html:113 +msgid "For example:" +msgstr "उदाहरण के लिए:" + +#: books/edit/about.html:49 +msgid "A person? Or, people?" +msgstr "एक व्यक्ति? या, लोग?" + +#: books/edit/about.html:60 +msgid "Note any places mentioned?" +msgstr "उल्लेखित किसी भी स्थान पर ध्यान दें?" + +#: books/edit/about.html:71 +msgid "When is it set or about?" +msgstr "यह कब सेट या लगभग है?" + +#: books/edit/addfield.html:70 +msgid "Add a New Contributor Role" +msgstr "एक नई योगदानकर्ता भूमिका जोड़ें" + +#: books/edit/addfield.html:77 books/edit/addfield.html:104 books/edit/addfield.html:149 +msgid "What should we show in the menu?" +msgstr "हमें मेन्यू में क्या दिखाना चाहिए?" + +#: books/edit/addfield.html:96 +msgid "Add a New Type of Identifier" +msgstr "एक नया प्रकार का पहचानकर्ता जोड़ें" + +#: books/edit/addfield.html:113 +msgid "If the system has a website, what's the homepage?" +msgstr "अगर सिस्टम में एक वेबसाइट है, तो होमपेज क्या है?" + +#: books/edit/addfield.html:122 +msgid "Please leave a note: Why is this ID useful?" +msgstr "कृपया एक नोट छोड़ें: यह आईडी उपयोगी क्यों है?" + +#: books/edit/addfield.html:141 +msgid "Add a New Classification System" +msgstr "एक नया वर्गीकरण सिस्टम जोड़ें" + +#: books/edit/addfield.html:158 +msgid "Please leave a note: Why is this classification useful?" +msgstr "कृपया एक नोट छोड़ें: यह वर्गीकरण उपयोगी क्यों है?" + +#: books/edit/addfield.html:167 +msgid "Can you direct us to more information about this classification system online?" +msgstr "क्या आप हमें ऑनलाइन इस वर्गीकरण प्रणाली के बारे में अधिक जानकारी के लिए निर्देशित कर सकते हैं?" + +#: books/edit/edition.html:22 +msgid "Select this language" +msgstr "इस भाषा का चयन करें" + +#: books/edit/edition.html:32 books/edit/edition.html:41 +msgid "Remove this language" +msgstr "इस भाषा को हटाएँ" + +#: books/edit/edition.html:33 +msgid "Add another language?" +msgstr "एक और भाषा?" + +#: books/edit/edition.html:52 +msgid "Remove this work" +msgstr "इस काम को हटाएँ" + +#: books/edit/edition.html:60 books/edit/edition.html:401 +msgid "-- Move to a new work" +msgstr "-- किसी नए काम पर जाएँ" + +#: books/edit/edition.html:87 +msgid "This Edition" +msgstr "यह संस्करण" + +#: books/edit/edition.html:97 +msgid "Enter the title of this specific edition." +msgstr "इस विशिष्ट संस्करण का शीर्षक दर्ज करें।" + +#: books/edit/edition.html:106 +msgid "Subtitle" +msgstr "उपशीर्षक" + +#: books/edit/edition.html:107 +msgid "Usually distinguished by different or smaller type." +msgstr "आमतौर पर अलग या छोटे प्रकार से अलग किया जाता है।" + +#: books/edit/edition.html:114 +msgid "Publishing Info" +msgstr "प्रकाशन की जानकारी" + +#: books/edit/edition.html:129 +msgid "Where was the book published?" +msgstr "यह पुस्तक कहाँ प्रकाशित हुई थी?" + +#: books/edit/edition.html:130 +msgid "City, Country please." +msgstr "शहर, देश कृपया।" + +#: books/edit/edition.html:152 +msgid "What is the copyright date?" +msgstr "कॉपीराइट की तारीख क्या है?" + +#: books/edit/edition.html:153 +msgid "The year following the copyright statement." +msgstr "कॉपीराइट कथन के बाद का वर्ष।" + +#: books/edit/edition.html:162 +msgid "Edition Name (if applicable):" +msgstr "संस्करण का नाम (यदि लागू हो):" + +#: books/edit/edition.html:172 +msgid "Series Name (if applicable)" +msgstr "श्रृंखला का नाम (यदि लागू हो)" + +#: books/edit/edition.html:173 +msgid "The name of the publisher's series." +msgstr "प्रकाशक की श्रृंखला का नाम।" + +#: books/edit/edition.html:201 type/edition/view.html:417 type/work/view.html:417 +msgid "Contributors" +msgstr "योगदान-कर्ता" + +#: books/edit/edition.html:203 +msgid "Please select a role." +msgstr "कृपया एक भूमिका चुनें।" + +#: books/edit/edition.html:203 +msgid "You need to give this ROLE a name." +msgstr "आपको इस भूमिका को एक नाम देना होगा।" + +#: books/edit/edition.html:205 +msgid "List the people involved" +msgstr "इसमें शामिल लोगों की सूची बनाएँ" + +#: books/edit/edition.html:215 +msgid "Select role" +msgstr "भूमिका चुनें" + +#: books/edit/edition.html:220 +msgid "Add a new role" +msgstr "एक नई भूमिका जोड़ें" + +#: books/edit/edition.html:240 books/edit/edition.html:256 +msgid "Remove this contributor" +msgstr "इस योगदानकर्ता को हटाएँ" + +#: books/edit/edition.html:265 +msgid "By Statement:" +msgstr "कथन द्वारा:" + +#: books/edit/edition.html:276 +msgid "Languages" +msgstr "भाषाएँ" + +#: books/edit/edition.html:280 +msgid "What language is this edition written in?" +msgstr "यह संस्करण किस भाषा में लिखा गया है?" + +#: books/edit/edition.html:292 +msgid "Is it a translation of another book?" +msgstr "क्या यह किसी अन्य पुस्तक का अनुवाद है?" + +#: books/edit/edition.html:310 +msgid "Yes, it's a translation" +msgstr "हाँ, यह एक अनुवाद है" + +#: books/edit/edition.html:315 +msgid "What's the original book?" +msgstr "मूल पुस्तक क्या है?" + +#: books/edit/edition.html:324 +msgid "What language was the original written in?" +msgstr "मूल भाषा किस भाषा में लिखी गई थी?" + +#: books/edit/edition.html:342 +msgid "Description of this edition" +msgstr "इस संस्करण का विवरण" + +#: books/edit/edition.html:343 +msgid "Only if it's different from the work's description" +msgstr "केवल अगर यह कार्य के विवरण से अलग है" + +#: TableOfContents.html:10 books/edit/edition.html:352 +msgid "Table of Contents" +msgstr "विषय-सूची" + +#: books/edit/edition.html:353 +msgid "Use a \"*\" for an indent, a \"|\" to add a column, and line breaks for new lines. Like this:" +msgstr "" +"इंडेंट के लिए \"*\" का इस्तेमाल करें, कॉलम जोड़ने के लिए \"|\" का इस्तेमाल करें और नई लाइनों के लिए लाइन ब्रेक का इस्तेमाल " +"करें। इस तरह:" + +#: books/edit/edition.html:367 +msgid "Any notes about this specific edition?" +msgstr "इस विशिष्ट संस्करण के बारे में कोई टिप्पणी?" + +#: books/edit/edition.html:368 +msgid "Anything about the book that may be of interest." +msgstr "किताब के बारे में कुछ भी जो दिलचस्प हो सकता है।" + +#: books/edit/edition.html:377 +msgid "Is it known by any other titles?" +msgstr "क्या यह किसी अन्य शीर्षक से जाना जाता है?" + +#: books/edit/edition.html:378 +msgid "" +"Use this field if the title is different on the cover or spine. If the edition is a collection or " +"anthology, you may also add the individual titles of each work here." +msgstr "" +"अगर कवर या रीढ़ की हड्डी पर शीर्षक अलग है, तो इस फ़ील्ड का इस्तेमाल करें। यदि संस्करण एक संग्रह या संकलन है, तो आप यहां " +"प्रत्येक कार्य के व्यक्तिगत शीर्षक भी जोड़ सकते हैं।" + +#: books/edit/edition.html:382 +msgid "is an alternate title for" +msgstr "के लिए एक वैकल्पिक शीर्षक है" + +#: books/edit/edition.html:392 +msgid "What work is this an edition of?" +msgstr "यह किस काम का संस्करण है?" + +#: books/edit/edition.html:394 +msgid "Sometimes editions can be associated with the wrong work. You can correct that here." +msgstr "कभी - कभी संस्करणों को गलत काम से जोड़ा जा सकता है। आप इसे यहाँ ठीक कर सकते हैं।" + +#: books/edit/edition.html:395 +msgid "You can search by title or by Open Library ID (like" +msgstr "आप शीर्षक या ओपन लाइब्रेरी आईडी द्वारा खोज सकते हैं (जैसे" + +#: books/edit/edition.html:398 +msgid "WARNING: Any edits made to the work from this page will be ignored." +msgstr "चेतावनी: इस पेज से किए गए किसी भी बदलाव को नज़रअंदाज़ कर दिया जाएगा।" + +#: books/edit/edition.html:414 +msgid "Please select an identifier." +msgstr "कृपया एक पहचानकर्ता चुनें।" + +#: books/edit/edition.html:414 +msgid "You need to give a value to ID." +msgstr "आपको आईडी को एक मान देना होगा।" + +#: books/edit/edition.html:414 +msgid "ID ids cannot contain whitespace." +msgstr "आईडी आईडी में व्हाइटस्पेस नहीं हो सकता।" + +#: books/edit/edition.html:414 +msgid "ID must be exactly 10 characters [0-9] or X." +msgstr "आईडी ठीक 10 कैरेक्टर [0 -9] या X की होनी चाहिए।" + +#: books/edit/edition.html:414 +msgid "That ISBN already exists for this edition." +msgstr "कि इस संस्करण के लिए ISBN पहले से मौजूद है।" + +#: books/edit/edition.html:414 +msgid "ID must be exactly 13 digits [0-9]. For example: 978-1-56619-909-4" +msgstr "आईडी बिल्कुल 13 अंकों [0 -9] की होनी चाहिए। उदाहरण के लिए: 978 -1 -56619 -909 -4" + +#: books/edit/edition.html:416 type/author/view.html:188 type/edition/view.html:438 +#: type/work/view.html:438 +msgid "ID Numbers" +msgstr "आईडी नंबर" + +#: books/edit/edition.html:422 +msgid "Do you know any identifiers for this edition?" +msgstr "क्या आप इस संस्करण के लिए किसी पहचानकर्ता को जानते हैं?" + +#: books/edit/edition.html:423 +msgid "Like, ISBN?" +msgstr "पसंद है, ISBN?" + +#: books/edit/edition.html:441 books/edit/edition.html:509 +msgid "Select one of many..." +msgstr "कई में से एक चुनें..." + +#: books/edit/edition.html:493 +msgid "Please select a classification." +msgstr "कृपया कोई वर्गीकरण चुनें।" + +#: books/edit/edition.html:493 +msgid "You need to give a value to CLASS." +msgstr "आपको कक्षा को एक मूल्य देना होगा।" + +#: books/edit/edition.html:494 type/edition/view.html:392 type/work/view.html:392 +msgid "Classifications" +msgstr "वर्गीकरण" + +#: books/edit/edition.html:500 +msgid "Do you know any classifications of this edition?" +msgstr "क्या आप इस संस्करण के किसी वर्गीकरण को जानते हैं?" + +#: books/edit/edition.html:501 +msgid "Like, Dewey Decimal?" +msgstr "Like, Dewey Decimal?" + +#: books/edit/edition.html:514 +msgid "Add a new classification type" +msgstr "एक नया वर्गीकरण प्रकार जोड़ें" + +#: books/edit/edition.html:531 books/edit/edition.html:540 +msgid "Remove this classification" +msgstr "इस वर्गीकरण को हटाएँ" + +#: books/edit/edition.html:552 type/edition/view.html:426 type/work/view.html:426 +msgid "The Physical Object" +msgstr "भौतिक वस्तु" + +#: books/edit/edition.html:558 +msgid "What sort of book is it?" +msgstr "यह किस तरह की किताब है?" + +#: books/edit/edition.html:559 +msgid "Paperback; Hardcover, etc." +msgstr "पेपरबैक; हार्डकवर वगैरह." + +#: books/edit/edition.html:567 +msgid "How many pages?" +msgstr "कितने पेज हैं?" + +#: books/edit/edition.html:577 +msgid "Pagination?" +msgstr "पेजिनेशन?" + +#: books/edit/edition.html:578 +msgid "Note the highest number in each pagination pattern." +msgstr "प्रत्येक पृष्ठीकरण पैटर्न में सबसे अधिक संख्या पर ध्यान दें।" + +#: books/edit/edition.html:578 lib/nav_foot.html:45 +msgid "Help" +msgstr "सहायता" + +#: books/edit/edition.html:588 +msgid "How much does the book weigh?" +msgstr "किताब का वजन कितना है?" + +#: books/edit/edition.html:603 type/edition/view.html:431 type/work/view.html:431 +msgid "Dimensions" +msgstr "आयाम" + +#: books/edit/edition.html:615 +msgid "Height:" +msgstr "ऊँचाई:" + +#: books/edit/edition.html:620 +msgid "Width:" +msgstr "चौड़ाई::" + +#: books/edit/edition.html:625 +msgid "Depth:" +msgstr "गहराई:" + +#: books/edit/edition.html:648 +msgid "Web Book Providers" +msgstr "वेब बुक प्रोवाइडर" + +#: books/edit/edition.html:653 +msgid "Access Type" +msgstr "पहुँच का प्रकार" + +#: books/edit/edition.html:654 +msgid "File Format" +msgstr "छवि तारीख" + +#: books/edit/edition.html:655 +msgid "Provider Name" +msgstr "प्रदाता का नाम" + +#: ReadButton.html:39 books/edit/edition.html:666 +msgid "Listen" +msgstr "बात सुनो" + +#: books/edit/edition.html:667 +msgid "Buy" +msgstr "खरीद" + +#: books/edit/edition.html:675 +msgid "Web" +msgstr "वेब" + +#: books/edit/edition.html:685 +msgid "Add a provider" +msgstr "एक प्रदाता जोड़ें" + +#: books/edit/edition.html:690 +msgid "Admin Only" +msgstr "सिर्फ़ एडमिन के लिए" + +#: books/edit/edition.html:693 +msgid "Additional Metadata" +msgstr "अतिरिक्त मेटाडेटा" + +#: books/edit/edition.html:694 +msgid "This field can accept arbitrary key:value pairs" +msgstr "यह फ़ील्ड मनमानी कुंजी स्वीकार कर सकती है:मान जोड़े" + +#: books/edit/edition.html:706 +msgid "First sentence" +msgstr "पहला वाक्य" + +#: books/edit/excerpts.html:13 +msgid "" +"If there are particular (short) passages you feel represent the book well, please feel free to " +"transcribe them here." +msgstr "" +"यदि कुछ विशेष (छोटे) अंश हैं जो आपको लगता है कि पुस्तक का प्रतिनिधित्व अच्छी तरह से करते हैं, तो कृपया उन्हें यहाँ अनुवाद " +"करने में संकोच न करें।" + +#: books/edit/excerpts.html:18 +msgid "Page number?" +msgstr "पेज नंबर?" + +#: books/edit/excerpts.html:23 +msgid "This is the first sentence." +msgstr "यह पहला वाक्य है।" + +#: books/edit/excerpts.html:30 +msgid "Transcribe the excerpt" +msgstr "अंश को ट्रांसक्रिप्ट करें" + +#: books/edit/excerpts.html:31 +msgid "Maximum length is 2000 characters. No HTML allowed." +msgstr "अधिकतम लंबाई 2000 कैरेक्टर है। HTML की अनुमति नहीं है।" + +#: books/edit/excerpts.html:41 +msgid "Why did you choose this excerpt?" +msgstr "आपने यह अंश क्यों चुना?" + +#: books/edit/excerpts.html:42 +msgid "Add an optional note." +msgstr "एक वैकल्पिक नोट जोड़ें।" + +#: books/edit/excerpts.html:56 +msgid "Excerpts so far..." +msgstr "अब तक के अंश..." + +#: books/edit/excerpts.html:70 books/edit/excerpts.html:92 +msgid "noted:" +msgstr "नोट किया गया:" + +#: books/edit/excerpts.html:72 books/edit/excerpts.html:94 +msgid "An anonymous note:" +msgstr "एक गुमनाम नोट:" + +#: books/edit/excerpts.html:90 +msgid "From page" +msgstr "पृष्ठ से" + +#: books/edit/web.html:13 +msgid "Please connect Open Library records to good* online resources." +msgstr "कृपया ओपन लाइब्रेरी रिकॉर्ड को अच्छे* ऑनलाइन संसाधनों से कनेक्ट करें।" + +#: books/edit/web.html:19 +msgid "Give your link a label" +msgstr "अपने लिंक को एक लेबल दें" + +#: books/edit/web.html:44 books/edit/web.html:53 +msgid "Remove this link" +msgstr "इस लिंक को हटाएँ" + +#: books/edit/web.html:65 +msgid "" +"\"Good\" means no spammy links, please. Keep them relevant to the book. Irrelevant sites may be " +"removed. Blatant spam will be deleted without remorse." +msgstr "" +"\"अच्छा\" का मतलब है कोई स्पैम लिंक नहीं, कृपया। उन्हें किताब के लिए प्रासंगिक रखें। अप्रासंगिक साइटों को हटाया जा सकता " +"है। स्पष्ट स्पैम को बिना किसी पछतावे के हटा दिया जाएगा।" + +#: check_ins/check_in_form.html:10 +msgid "January" +msgstr "जनवरी" + +#: check_ins/check_in_form.html:11 +msgid "February" +msgstr "फरवरी" + +#: check_ins/check_in_form.html:12 +msgid "March" +msgstr "मार्च" + +#: check_ins/check_in_form.html:13 +msgid "April" +msgstr "अप्रैल" + +#: check_ins/check_in_form.html:14 +msgid "May" +msgstr "मई" + +#: check_ins/check_in_form.html:15 +msgid "June" +msgstr "जून" + +#: check_ins/check_in_form.html:16 +msgid "July" +msgstr "जुलाई" + +#: check_ins/check_in_form.html:17 +msgid "August" +msgstr "अगस्त" + +#: check_ins/check_in_form.html:18 +msgid "September" +msgstr "सितम्बर" + +#: check_ins/check_in_form.html:19 +msgid "October" +msgstr "अक्टूबर" + +#: check_ins/check_in_form.html:20 +msgid "November" +msgstr "नवम्बर" + +#: check_ins/check_in_form.html:21 +msgid "December" +msgstr "दिसंबर" + +#: check_ins/check_in_form.html:38 +msgid "Add an optional check-in date. Check-in dates are used to track yearly reading goals." +msgstr "चेक इन की एक वैकल्पिक तारीख जोड़ें। चेक इन की तारीखों का इस्तेमाल सालाना पढ़ने के लक्ष्यों को ट्रैक करने के लिए किया जाता है।" + +#: check_ins/check_in_form.html:41 +msgid "End Date:" +msgstr "समाप्ति तिथि:" + +#: check_ins/check_in_form.html:43 +msgid "Year:" +msgstr "वर्ष:" + +#: check_ins/check_in_form.html:45 +msgid "Year" +msgstr "वर्ष" + +#: check_ins/check_in_form.html:53 +msgid "Month:" +msgstr "महीना:" + +#: check_ins/check_in_form.html:55 +msgid "Month" +msgstr "महीना" + +#: check_ins/check_in_form.html:62 +msgid "Day:" +msgstr "दिन:" + +#: check_ins/check_in_form.html:64 +msgid "Day" +msgstr "दिन" + +#: check_ins/check_in_form.html:76 +msgid "Delete Event" +msgstr "इवेंट मिटाएँ" + +#: check_ins/check_in_prompt.html:32 +msgid "When did you finish this book?" +msgstr "आपने यह किताब कब पूरी की?" + +#: check_ins/check_in_prompt.html:37 +msgid "Other" +msgstr "अन्य" + +#: check_ins/check_in_prompt.html:42 +msgid "Check-In" +msgstr "चेक इन" + +#: check_ins/reading_goal_form.html:12 +msgid "How many books would you like to read this year?" +msgstr "आप इस साल कितनी किताबें पढ़ना चाहेंगे?" + +#: check_ins/reading_goal_form.html:18 +msgid "Note: Submit \"0\" to delete your goal. Your check-ins will be preserved." +msgstr "ध्यान दें: अपना लक्ष्य हटाने के लिए \"0\" सबमिट करें। आपके चेक इन को सुरक्षित रखा जाएगा।" + +#: check_ins/reading_goal_progress.html:18 +#, python-format +msgid "%(year)d Reading Goal:" +msgstr "%(year)d में पढ़ने का लक्ष्य:" + +#: check_ins/reading_goal_progress.html:25 +#, python-format +msgid "" +"%(books_read)d/%(goal)d books" +msgstr "" +"%(books_read)d %(goal)d किताबें" + +#: check_ins/reading_goal_progress.html:31 +#, python-format +msgid "Edit %(year)d Reading Goal" +msgstr "%(year)d रीडिंग लक्ष्य में बदलाव करें" + +#: covers/add.html:12 +msgid "There are two ways to put an author's image on Open Library" +msgstr "ओपन लाइब्रेरी पर लेखक की छवि डालने के दो तरीके हैं" + +#: covers/add.html:14 +msgid "Image Guidelines" +msgstr "छवि दिशानिर्देश" + +#: covers/add.html:16 +msgid "There are two ways to put a cover image on Open Library" +msgstr "ओपन लाइब्रेरी पर कवर इमेज लगाने के दो तरीके हैं" + +#: covers/add.html:18 +msgid "Cover Guidelines" +msgstr "कवर दिशानिर्देश" + +#: covers/add.html:23 covers/add.html:27 +msgid "Please provide a valid image." +msgstr "कृपया एक मान्य फ़ोटो दें।" + +#: covers/add.html:25 +msgid "Please provide an image URL." +msgstr "कृपया एक छवि URL प्रदान करें।" + +#: covers/add.html:40 +msgid "Pick one from the existing covers," +msgstr "मौजूदा कवर में से एक चुनें," + +#: covers/add.html:61 +msgid "Choose a JPG, GIF or PNG on your computer," +msgstr "अपने कंप्यूटर पर एक JPG, GIF या PNG चुनें," + +#: covers/add.html:70 +msgid "Or, paste in the image URL if it's on the web." +msgstr "या, अगर यह वेब पर है तो इमेज का URL पेस्ट करें।" + +#: covers/book_cover.html:22 +msgid "Pull up a bigger book cover" +msgstr "एक बड़ा बुक कवर तैयार करें" + +#: covers/book_cover.html:22 covers/book_cover_single_edition.html:18 covers/book_cover_small.html:18 +#: covers/book_cover_work.html:18 +#, python-format +msgid "Cover of: %(title)s by %(authors)s" +msgstr "%(authors)s द्वारा :%(title)s का कवर" + +#: covers/book_cover.html:33 covers/book_cover_single_edition.html:29 covers/book_cover_small.html:21 +#: covers/book_cover_work.html:21 covers/book_cover_work.html:25 +#, python-format +msgid "We need a book cover for: %(title)s" +msgstr "हमें इसके लिए एक बुक कवर चाहिए: %(title)s" + +#: covers/book_cover_single_edition.html:18 covers/book_cover_work.html:18 +msgid "View a larger book cover" +msgstr "एक बड़ा बुक कवर देखें" + +#: covers/book_cover_small.html:18 covers/book_cover_small.html:21 covers/book_cover_small.html:25 +#, python-format +msgid "Go to the main page for %(title)s" +msgstr "%(title)s के लिए मुख्य पेज पर जाएँ" + +#: covers/change.html:15 +msgid "Author Photos" +msgstr "लेखक की फ़ोटो" + +#: covers/change.html:17 +msgid "Manage Author Photos" +msgstr "लेखक की फ़ोटो मैनेज करें" + +#: covers/change.html:20 +msgid "Add Author Photo" +msgstr "लेखक की फ़ोटो जोड़ें" + +#: covers/change.html:25 +msgid "Book Covers" +msgstr "बुक कवर" + +#: covers/change.html:28 +msgid "Manage Covers" +msgstr "कवर मैनेज करें" + +#: covers/change.html:31 +msgid "Add Cover Image" +msgstr "कवर फ़ोटो जोड़ें" + +#: covers/change.html:50 +msgid "Manage" +msgstr "प्रबंध" + +#: covers/manage.html:12 +msgid "You can drag and drop thumbnails to reorder, or into the trash to delete them." +msgstr "आप थंबनेल को फिर से ऑर्डर करने के लिए ड्रैग और ड्रॉप कर सकते हैं, या उन्हें हटाने के लिए ट्रैश में डाल सकते हैं।" + +#: covers/saved.html:17 +msgid "Saved!" +msgstr "सेव किया गया!" + +#: covers/saved.html:20 +msgid "Notes:" +msgstr "नोट्स:" + +#: covers/saved.html:30 +msgid "Add another image" +msgstr "एक और फ़ोटो जोड़ें" + +#: covers/saved.html:36 +msgid "Finished" +msgstr "पूर्ण" + +#: history/comment.html:26 +#, python-format +msgid "Imported from %(source)s %(type)s record." +msgstr "%(source)s %(type)s रिकॉर्ड से इम्पोर्ट किया गया।" + +#: history/comment.html:28 +#, python-format +msgid "Found a matching record from %(source)s." +msgstr "%(source)s से एक रिकॉर्ड मिला।" + +#: history/comment.html:35 +msgid "Edited without comment." +msgstr "बिना किसी टिप्पणी के एडिट किया गया।" + +#: home/about.html:9 +msgid "About the Project" +msgstr "परियोजना के बारे में" + +#: home/about.html:15 +msgid "" +"Open Library is an open, editable library catalog, building towards a web page for every book ever " +"published." +msgstr "ओपन लाइब्रेरी एक खुली, संपादन योग्य लाइब्रेरी कैटलॉग है, जो प्रत्येक पुस्तक के लिए एक वेब पेज बनाती है।" + +#: AffiliateLinks.html:70 home/about.html:16 +msgid "More" +msgstr "ज़्यादा" + +#: home/about.html:19 +msgid "" +"Just like Wikipedia, you can contribute new information or corrections to the catalog. You can browse " +"by subjects, authors or lists members have created. If you love books, why not help build a library?" +msgstr "" +"विकिपीडिया की तरह, आप कैटलॉग में नई जानकारी या सुधार का योगदान कर सकते हैं। आप विषयों, लेखकों या सदस्यों द्वारा बनाई गई सूचियों के आधार पर ब्राउज़ " +"कर सकते हैं। अगर आपको किताबें पसंद हैं, तो लाइब्रेरी बनाने में मदद क्यों नहीं करते?" + +#: home/about.html:23 +msgid "Latest Blog Posts" +msgstr "नवीनतम ब्लॉग पोस्ट" + +#: home/categories.html:11 +msgid "Browse by Subject" +msgstr "विषय के आधार पर ब्राउज़ करें" + +#: home/categories.html:26 +#, python-format +msgid "browse %(subject)s books" +msgstr "%(subject)s सें किताबें ब्राउज़ करें" + +#: home/categories.html:27 +#, python-format +msgid "%(count)s Book" +msgid_plural "%(count)s Books" +msgstr[0] "%(count)s पुस्तक" +msgstr[1] "%(count)s पुस्तकें" + +#: home/index.html:8 home/welcome.html:9 +msgid "Welcome to Open Library" +msgstr "ओपन लाइब्रेरी में आपका स्वागत है" + +#: home/index.html:29 +msgid "Classic Books" +msgstr "क्लासिक किताबें" + +#: home/index.html:36 +msgid "Recently Returned" +msgstr "हाल ही में लौटाया गया" + +#: home/index.html:38 +msgid "Romance" +msgstr "रोमांस" + +#: home/index.html:40 +msgid "Kids" +msgstr "बच्चे" + +#: home/index.html:42 +msgid "Thrillers" +msgstr "थ्रिलर" + +#: home/index.html:44 +msgid "Textbooks" +msgstr "पाठ्यपुस्तकें" + +#: home/stats.html:9 site/around_the_library.html:52 +msgid "Around the Library" +msgstr "लाइब्रेरी इन दिनों" + +#: home/stats.html:10 +msgid "Here's what's happened over the last 28 days. More recent changes" +msgstr "यहाँ बताया गया है कि पिछले 28 दिनों में क्या हुआ है। और भी हाल के बदलाव" + +#: home/stats.html:15 home/stats.html:17 +msgid "See all visitors to OpenLibrary.org" +msgstr "OpenLibrary.org पर सभी विज़िटर देखें" + +#: home/stats.html:16 +msgid "Area graph of recent unique visitors" +msgstr "हाल ही के अनोखे विज़िटर का एरिया ग्राफ़" + +#: home/stats.html:17 +msgid "Unique Visitors" +msgstr "अनोखे विज़िटर" + +#: home/stats.html:22 home/stats.html:24 +msgid "How many new Open Library members have we welcomed?" +msgstr "हमने कितने नए ओपन लाइब्रेरी सदस्यों का स्वागत किया है?" + +#: home/stats.html:23 +msgid "Area graph of new members" +msgstr "नए सदस्यों का क्षेत्र ग्राफ़" + +#: home/stats.html:24 +msgid "New Members" +msgstr "नए सदस्य" + +#: home/stats.html:28 home/stats.html:30 +msgid "People are constantly updating the catalog" +msgstr "लोग कैटलॉग को लगातार अपडेट कर रहे हैं" + +#: home/stats.html:29 +msgid "Area graph of recent catalog edits" +msgstr "हाल के कैटलॉग संपादन का क्षेत्र ग्राफ़" + +#: home/stats.html:30 +msgid "Catalog Edits" +msgstr "कैटलॉग में बदलाव" + +#: home/stats.html:35 home/stats.html:37 +msgid "Members can create Lists" +msgstr "सदस्य लिस्ट बना सकते हैं" + +#: home/stats.html:36 +msgid "Area graph of lists created recently" +msgstr "हाल ही में बनाई गई सूचियों का क्षेत्र ग्राफ़" + +#: home/stats.html:37 +msgid "Lists Created" +msgstr "बनाई गई सूचियाँ" + +#: home/stats.html:42 home/stats.html:44 +msgid "We're a library, so we lend books, too" +msgstr "हम एक लाइब्रेरी हैं, इसलिए हम किताबें भी उधार देते हैं" + +#: home/stats.html:43 +msgid "Area graph of ebooks borrowed recently" +msgstr "हाल ही में उधार ली गई ई - बुक्स का एरिया ग्राफ़" + +#: home/stats.html:44 +msgid "eBooks Borrowed" +msgstr "उधार ली गई ई - पुस्तकें" + +#: home/welcome.html:26 +msgid "Read Free Library Books Online" +msgstr "मुफ़्त लाइब्रेरी की किताबें ऑनलाइन पढ़ें" + +#: home/welcome.html:27 +msgid "Millions of books available through Controlled Digital Lending" +msgstr "नियंत्रित डिजिटल ऋण के माध्यम से लाखों किताबें उपलब्ध हैं" + +#: home/welcome.html:40 +msgid "Keep Track of your Favorite Books" +msgstr "अपनी पसंदीदा किताबों पर नज़र रखें" + +#: home/welcome.html:41 +msgid "Organize your Books using Lists & the Reading Log" +msgstr "सूचियों और रीडिंग लॉग का उपयोग करके अपनी पुस्तकों को व्यवस्थित करें" + +#: home/welcome.html:54 +msgid "Try the virtual Library Explorer" +msgstr "वर्चुअल लाइब्रेरी एक्सप्लोरर आज़माएँ" + +#: home/welcome.html:55 +msgid "Digital shelves organized like a physical library" +msgstr "डिजिटल अलमारियों को एक भौतिक पुस्तकालय की तरह व्यवस्थित किया गया" + +#: home/welcome.html:68 +msgid "Try Fulltext Search" +msgstr "फुलटेक्स्ट सर्च आज़माएँ" + +#: home/welcome.html:69 +msgid "Find matching results within the text of millions of books" +msgstr "लाखों पुस्तकों के टेक्स्ट के भीतर मिलान करने वाले परिणाम खोजें" + +#: home/welcome.html:82 +msgid "Be an Open Librarian" +msgstr "एक ओपन लाइब्रेरियन बनें" + +#: home/welcome.html:83 +msgid "Dozens of ways you can help improve the library" +msgstr "लाइब्रेरी को बेहतर बनाने में आप दर्जनों तरीकों से मदद कर सकते हैं" + +#: home/welcome.html:96 +msgid "Send us feedback" +msgstr "हमें फीडबैक भेजें" + +#: home/welcome.html:97 +msgid "Your feedback will help us improve these cards" +msgstr "आपके फ़ीडबैक से हमें इन कार्ड को बेहतर बनाने में मदद मिलेगी" + +#: languages/index.html:15 +#, python-format +msgid "%s book" +msgid_plural "%s books" +msgstr[0] "%s पुस्तक" +msgstr[1] "%s पुस्तकें" + +#: languages/language_list.html:8 +msgid "Czech" +msgstr "चेक" + +#: languages/language_list.html:9 +msgid "German" +msgstr "जर्मन" + +#: languages/language_list.html:10 +msgid "English" +msgstr "अंग्रेज़ी" + +#: languages/language_list.html:11 +msgid "Spanish" +msgstr "स्पैनिश" + +#: languages/language_list.html:12 +msgid "French" +msgstr "फ़्रांसीसी" + +#: languages/language_list.html:13 +msgid "Croatian" +msgstr "क्रोएशियन" + +#: languages/language_list.html:14 +msgid "Portuguese" +msgstr "पुर्तगाली" + +#: languages/language_list.html:15 +msgid "Telugu" +msgstr "तेलुगू" + +#: languages/language_list.html:16 +msgid "Ukrainian" +msgstr "यूक्रेनी" + +#: languages/language_list.html:17 +msgid "Chinese" +msgstr "चीनी" + +#: languages/notfound.html:7 +#, python-format +msgid "%s is not found" +msgstr "%s नहीं मिला" + +#: languages/notfound.html:14 +#, python-format +msgid "%s does not exist." +msgstr "%s उपस्थित नहीं है।" + +#: lib/edit_head.html:22 lib/view_head.html:14 +msgid "This doc was last edited by" +msgstr "इस दस्तावेज़ को अंतिम बार द्वारा संपादित किया गया था" + +#: lib/edit_head.html:24 lib/view_head.html:16 +msgid "This doc was last edited anonymously" +msgstr "इस दस्तावेज़ में आखिरी बार गुमनाम रूप से बदलाव किया गया था" + +#: lib/header_dropdown.html:38 +msgid "Menu" +msgstr "मेन्यू" + +#: lib/header_dropdown.html:74 +msgid "New!" +msgstr "नया!" + +#: databarDiff.html:9 databarEdit.html:10 databarTemplate.html:9 databarView.html:26 databarView.html:28 +#: lib/history.html:21 +msgid "History" +msgstr "इतिहास" + +#: lib/history.html:25 +#, python-format +msgid "Created %(date)s" +msgstr "%(date)s बनाया गया" + +#: lib/history.html:28 +#, python-format +msgid "%(count)d revision" +msgid_plural "%(count)d revisions" +msgstr[0] "%(count)d संशोधन" +msgstr[1] "%(count)d संशोधन" + +#: lib/history.html:44 +msgid "Download catalog record:" +msgstr "कैटलॉग रिकॉर्ड डाउनलोड करें:" + +#: lib/history.html:52 +msgid "Cite this on Wikipedia" +msgstr "इसे विकिपीडिया पर उद्धृत करें" + +#: lib/history.html:52 +msgid "Wikipedia citation" +msgstr "विकिपीडिया उद्धरण" + +#: lib/history.html:62 +msgid "Copy and paste this code into your Wikipedia page." +msgstr "इस कोड को कॉपी करके अपने विकिपीडिया पेज में पेस्ट करें।" + +#: lib/history.html:81 lib/history.html:83 +msgid "an anonymous user" +msgstr "एक अनाम यूज़र" + +#: lib/history.html:85 +#, python-format +msgid "Created by %(user)s" +msgstr "%(user)s द्वारा बनाया गया" + +#: lib/history.html:87 +#, python-format +msgid "Edited by %(user)s" +msgstr "%(user)s द्वारा संपादित" + +#: lib/markdown.html:16 +msgid "" +"We use a system called Markdown to format the text in your edits on Open Library. Some HTML formatting " +"is also accepted. Paragraphs are automatically generated from any hard-break returns (blank lines) " +"within your text. You can preview your work in real time below the editing field." +msgstr "" +"हम ओपन लाइब्रेरी पर आपके संपादन में टेक्स्ट को प्रारूपित करने के लिए मार्कडाउन नामक एक सिस्टम का उपयोग करते हैं। कुछ HTML " +"फ़ॉर्मेटिंग भी स्वीकार की जाती है। पैराग्राफ आपके टेक्स्ट के भीतर किसी भी हार्ड - ब्रेक रिटर्न (रिक्त लाइनों) से स्वचालित " +"रूप से उत्पन्न होते हैं। आप संपादन फ़ील्ड के नीचे वास्तविक समय में अपने काम का पूर्वावलोकन कर सकते हैं।" + +#: lib/markdown.html:17 +msgid "Formatting marks should envelope the effected text, for example:" +msgstr "स्वरूपण चिह्नों को प्रभावित पाठ को लिफाफा करना चाहिए, उदाहरण के लिए:" + +#: lib/markdown.html:76 +msgid "" +"To \"escape\" any Markdown tags (i.e. use an asterisk as an asterisk rather than emphasizing text) " +"place a backslash \\ prior to the character." +msgstr "" +"किसी भी मार्कडाउन टैग को \"एस्केप\" करने के लिए (यानी टेक्स्ट पर ज़ोर देने के बजाय तारांकन के रूप में तारांकन का उपयोग " +"करें) चरित्र से पहले एक बैकस्लैश \\ रखें।" + +#: lib/nav_foot.html:13 +msgid "Vision" +msgstr "सोच" + +#: lib/nav_foot.html:14 +msgid "Volunteer" +msgstr "स्वयंसेवक" + +#: lib/nav_foot.html:15 +msgid "Partner With Us" +msgstr "हमारे पार्टनर" + +#: lib/nav_foot.html:16 +msgid "Jobs" +msgstr "नौकरियां" + +#: lib/nav_foot.html:16 +msgid "Careers" +msgstr "करियर" + +#: lib/nav_foot.html:17 +msgid "Blog" +msgstr "ब्लॉग" + +#: lib/nav_foot.html:18 +msgid "Terms of Service" +msgstr "सेवा की शर्तें" + +#: lib/nav_foot.html:19 +msgid "Donate" +msgstr "दान करें" + +#: lib/nav_foot.html:23 +msgid "Discover" +msgstr "ऐसे क्रिएटर तलाशें" + +#: lib/nav_foot.html:25 +msgid "Go home" +msgstr "घर जाओ" + +#: lib/nav_foot.html:25 +msgid "Home" +msgstr "होम" + +#: lib/nav_foot.html:26 +msgid "Explore Books" +msgstr "किताबें एक्सप्लोर करें" + +#: SearchNavigation.html:12 lib/nav_foot.html:26 +msgid "Books" +msgstr "पुस्तक" + +#: lib/nav_foot.html:27 +msgid "Explore authors" +msgstr "लेखकों का जायज़ा लें" + +#: lib/nav_foot.html:28 +msgid "Explore subjects" +msgstr "विषयों का जायज़ा लें" + +#: lib/nav_foot.html:29 +msgid "Explore collections" +msgstr "कलेक्शन एक्सप्लोर करें" + +#: lib/nav_foot.html:29 lib/nav_head.html:32 +msgid "Collections" +msgstr "संग्रह" + +#: SearchNavigation.html:32 lib/nav_foot.html:30 lib/nav_head.html:35 search/advancedsearch.html:7 +#: search/advancedsearch.html:14 search/advancedsearch.html:18 +msgid "Advanced Search" +msgstr "विस्तृत खोज" + +#: lib/nav_foot.html:31 +msgid "Navigate to top of this page" +msgstr "इस पेज के सबसे ऊपर नेविगेट करें" + +#: lib/nav_foot.html:31 +msgid "Return to Top" +msgstr "ऊपर लौटें" + +#: lib/nav_foot.html:35 +msgid "Develop" +msgstr "विकसित करना" + +#: lib/nav_foot.html:37 +msgid "Explore Open Library Developer Center" +msgstr "ओपन लाइब्रेरी डेवलपर सेंटर का जायज़ा लें" + +#: lib/nav_foot.html:37 lib/nav_head.html:43 +msgid "Developer Center" +msgstr "डेवलपर केंद्र" + +#: lib/nav_foot.html:38 +msgid "Explore Open Library APIs" +msgstr "ओपन लाइब्रेरी API का जायज़ा लें" + +#: lib/nav_foot.html:38 +msgid "API Documentation" +msgstr "API डॉक्यूमेंटेशन" + +#: lib/nav_foot.html:39 +msgid "Bulk Open Library data" +msgstr "बल्क ओपन लाइब्रेरी डेटा" + +#: lib/nav_foot.html:39 +msgid "Bulk Data Dumps" +msgstr "बल्क डेटा डंप" + +#: lib/nav_foot.html:40 +msgid "Write a bot" +msgstr "एक बॉट लिखें" + +#: lib/nav_foot.html:40 +msgid "Writing Bots" +msgstr "बॉट लिखना" + +#: lib/nav_foot.html:41 +msgid "Add a new book to Open Library" +msgstr "ओपन लाइब्रेरी में एक नई किताब जोड़ें" + +#: lib/nav_foot.html:47 +msgid "Help Center" +msgstr "सहायता केंद्र" + +#: lib/nav_foot.html:48 +msgid "Problems" +msgstr "समस्याएं" + +#: lib/nav_foot.html:48 +msgid "Report A Problem" +msgstr "समस्या की रिपोर्ट करें" + +#: lib/nav_foot.html:49 +msgid "Suggest Edits" +msgstr "बदलाव सुझाएँ" + +#: lib/nav_foot.html:49 +msgid "Suggesting Edits" +msgstr "बदलावों का सुझाव देना" + +#: lib/nav_foot.html:57 +msgid "Change Website Language" +msgstr "वेबसाइट की भाषा बदलें" + +#: lib/nav_foot.html:63 lib/nav_head.html:63 type/list/embed.html:23 +msgid "Open Library logo" +msgstr "ओपन लाइब्रेरी लोगो" + +#: lib/nav_foot.html:65 +msgid "" +"Open Library is an initiative of the Internet Archive, a 501(c)(3) non-" +"profit, building a digital library of Internet sites and other cultural artifacts in digital form. " +"Other projects include the Wayback Machine, archive.org and archive-it.org" +msgstr "" +"ओपन लाइब्रेरी इंटरनेट आर्काइव की एक पहल है, a 501(c)(3) गैर-" +"लाभ, डिजिटल रूप में इंटरनेट साइटों और अन्य सांस्कृतिक कलाकृतियों की एक डिजिटल लाइब्रेरी। " +"अन्य प्रोजेक्ट में वेबैक मशीन, archive.org और archive-it.org शामिल हैं" + +#: lib/nav_foot.html:70 +#, python-format +msgid "version %s" +msgstr "संस्करण %s" + +#: lib/nav_head.html:13 +msgid "Loans, Reading Log, Lists, Stats" +msgstr "लोन, रीडिंग लॉग, सूचियाँ, आँकड़े" + +#: lib/nav_head.html:14 +msgid "My Profile" +msgstr "मेरी प्रोफ़ाइल" + +#: lib/nav_head.html:16 +msgid "Log out" +msgstr "लॉग आउट करें" + +#: lib/nav_head.html:19 +msgid "Pending Merge Requests" +msgstr "मर्ज के अनुरोध लंबित हैं" + +#: lib/nav_head.html:29 +msgid "Trending" +msgstr "रुझान" + +#: lib/nav_head.html:33 +msgid "K-12 Student Library" +msgstr "K -12 स्टूडेंट लाइब्रेरी" + +#: lib/nav_head.html:34 +msgid "Random Book" +msgstr "रैंडम बुक" + +#: lib/nav_head.html:39 +msgid "Recent Community Edits" +msgstr "हाल ही में समुदाय में किए गए बदलाव" + +#: lib/nav_head.html:42 +msgid "Help & Support" +msgstr "सहायता एवं समर्थन" + +#: lib/nav_head.html:44 +msgid "Librarians Portal" +msgstr "लाइब्रेरियन पोर्टल" + +#: lib/nav_head.html:48 +msgid "My Open Library" +msgstr "My Open Library" + +#: lib/nav_head.html:49 lib/nav_head.html:70 +msgid "Browse" +msgstr "ब्राउज़" + +#: lib/nav_head.html:50 +msgid "Contribute" +msgstr "योगदान" + +#: lib/nav_head.html:51 +msgid "Resources" +msgstr "वसीला" + +#: lib/nav_head.html:59 +msgid "The Internet Archive's Open Library: One page for every book" +msgstr "The Internet Archive's Open Library: One page for every book" + +#: lib/nav_head.html:91 +msgid "All" +msgstr "सभी" + +#: lib/nav_head.html:94 +msgid "Text" +msgstr "टेक्स्ट" + +#: lib/nav_head.html:95 search/advancedsearch.html:32 +msgid "Subject" +msgstr "विषय" + +#: lib/nav_head.html:97 +msgid "Advanced" +msgstr "एडवांस" + +#: lib/not_logged.html:7 +msgid "" +"You are not logged in. Open " +"Library will record your IP address and include it in this page's publicly accessible edit history. If " +"you create an account, your " +"IP address is concealed and you can more easily keep track of all your edits and additions." +msgstr "" +"आपने लॉग इन नहीं किया है। ओपन " +"लाइब्रेरी आपके आईपी पते को रिकॉर्ड करेगी और इसे इस पेज के सार्वजनिक रूप से सुलभ संपादन इतिहास में शामिल करेगी। यदि आप " +"एक खाता बनाते हैं, तो आपका IP " +"पता छिपा दिया जाता है और आप अपने सभी संपादन और परिवर्धन का आसानी से ट्रैक रख सकते हैं।" + +#: Pager.html:27 lib/pagination.html:22 +msgid "Previous" +msgstr "पिछला" + +#: lists/header.html:11 lists/header.html:24 lists/header.html:38 lists/header.html:47 +#: lists/header.html:56 +#, python-format +msgid "%(name)s / Lists" +msgstr "%(name)s/ लिस्ट" + +#: lists/header.html:16 +#, python-format +msgid "This author is on 1 list." +msgid_plural "This author is on %(count)s lists." +msgstr[0] "यह लेखक 1 सूची पर है।" +msgstr[1] "यह लेखक %(count)s सूची पर है।" + +#: lists/header.html:31 +#, python-format +msgid "This is edition is on 1 list." +msgid_plural "This edition is on %(count)s lists." +msgstr[0] "यह संस्करण 1 सूची में है।" +msgstr[1] "यह संस्करण %(count)s सूचियों पर है।" + +#: lists/header.html:40 +#, python-format +msgid "This work is on 1 list." +msgid_plural "This work is on %(count)s lists." +msgstr[0] "यह कार्य 1 सूची में है." +msgstr[1] "यह कार्य %(count)s सूचियों पर है।" + +#: lists/header.html:49 +#, python-format +msgid "%(name)s has 1 list." +msgid_plural "%(name)s has %(count)s lists." +msgstr[0] "%(name)s में 1 सूची है।" +msgstr[1] "%(name)s में %(count)s सूचियाँ हैं।" + +#: lists/header.html:59 +#, python-format +msgid "This subject is on 1 list." +msgid_plural "This subject is on %(count)s lists." +msgstr[0] "यह विषय 1 सूची में है।" +msgstr[1] "यह विषय %(count)s सूची पर है।" + +#: lists/header.html:88 +msgid "Hm. Can't find it." +msgstr "हम्म. यह नहीं मिल रहा है." + +#: lists/home.html:16 +msgid "Create a list of any Subjects, Authors, Works or specific Editions." +msgstr "किसी भी विषय, लेखक, कार्य या विशिष्ट संस्करणों की एक सूची बनाएं।" + +#: lists/home.html:17 +msgid "" +"Once you've made a list, you can watch for updates or export all the " +"editions in a list as HTML, BibTeX or JSON. See all your lists and any activity using the \"Lists\" " +"link on your Account page." +msgstr "" +"एक बार जब आप एक सूची बना लेते हैं, तो आप अपडेट देख सकते हैं या किसी सूची में सभी संस्करणों को HTML, " +"BibTeX या JSON के रूप में निर्यात कर सकते हैं। अपने अकाउंट पेज पर \"सूचियाँ\" लिंक का इस्तेमाल करके " +"अपनी सभी सूचियाँ और कोई भी गतिविधि देखें।" + +#: lists/home.html:18 +msgid "Enjoy!" +msgstr "आनंद लें!" + +#: lists/home.html:20 +#, python-format +msgid "" +"For the top 2000+ most requested eBooks in California for the print disabled click " +"here." +msgstr "" +"प्रिंट अक्षम के लिए कैलिफ़ोर्निया में शीर्ष 2000+ सबसे अधिक अनुरोधित ई - बुक्स के लिए यहां क्लिक " +"करें।" + +#: lists/home.html:24 +msgid "Find a list by name" +msgstr "नाम के आधार पर एक सूची खोजें" + +#: lists/home.html:31 +msgid "Active Lists" +msgstr "सक्रिय सूचियाँ" + +#: lists/home.html:48 +#, python-format +msgid "from %(owner)s" +msgstr "%(owner)s से" + +#: lists/home.html:47 lists/preview.html:19 lists/snippet.html:14 type/list/embed.html:12 +#: type/list/view_body.html:35 +#, python-format +msgid "%(count)d item" +msgid_plural "%(count)d items" +msgstr[0] "%(count)d आइटम" +msgstr[1] "%(count)d आइटम" + +#: lists/home.html:53 lists/preview.html:26 lists/snippet.html:20 +#, python-format +msgid "Last modified %(date)s" +msgstr "आखिरी बार %(date)s में बदलाव किया गया" + +#: lists/home.html:59 lists/snippet.html:26 +msgid "No description." +msgstr "कोई ब्यौरा नेईं." + +#: lists/home.html:65 lists/lists.html:130 type/user/view.html:81 +msgid "Recent Activity" +msgstr "हाल की गतिविधि" + +#: lists/home.html:66 type/user/view.html:67 +msgid "See all" +msgstr "सभी देखें" + +#: lists/list_overview.html:15 lists/widget.html:84 +msgid "See this list" +msgstr "यह लिस्ट देखें" + +#: lists/list_overview.html:25 lists/widget.html:85 +msgid "Remove from your list?" +msgstr "सूची से निकालें?" + +#: lists/list_overview.html:28 lists/widget.html:87 +msgid "You" +msgstr "You" + +#: lists/lists.html:10 +#, python-format +msgid "%(owner)s / Lists" +msgstr "%(owner)s / लिस्ट" + +#: lists/lists.html:30 +msgid "Delete this list" +msgstr "इस सूची को नष्ट" + +#: lists/lists.html:38 type/list/view_body.html:111 type/list/view_body.html:141 +msgid "Yes, I'm sure" +msgstr "हाँ, मुझे यकीन है" + +#: lists/lists.html:49 type/list/view_body.html:128 type/list/view_body.html:150 +msgid "No, cancel" +msgstr "नहीं, कैंसिल करें" + +#: lists/lists.html:61 +msgid "Delete list" +msgstr "ज्ञापन सूची हटाएँ" + +#: lists/lists.html:61 +msgid "Are you sure you want to delete this list?" +msgstr "क्या आप निश्चित हैं कि आप इन संपर्क सूची को मिटाना चाहते हैं ?" + +#: lists/lists.html:68 +msgid "Remove this seed?" +msgstr "इस बीज को हटा दें?" + +#: lists/lists.html:109 +#, python-format +msgid "Are you sure you want to remove %(title)s from this list?" +msgstr "क्या आप वाकई %(title)s को इस लिस्ट से हटाना चाहते हैं?" + +#: lists/lists.html:121 +msgid "This reader hasn't created any lists yet." +msgstr "इस रीडर ने अभी तक कोई लिस्ट नहीं बनाई है।" + +#: lists/lists.html:123 +msgid "You haven't created any lists yet." +msgstr "आपने अभी तक कोई लिस्ट नहीं बनाई है।" + +#: lists/lists.html:126 +msgid "Learn how to create your first list." +msgstr "अपनी पहली लिस्ट बनाने का तरीका जानें।" + +#: lists/preview.html:17 +msgid "by You" +msgstr "आपके द्वारा" + +#: lists/widget.html:57 +msgid "watch for edits or export all records" +msgstr "संपादन के लिए देखें या सभी रिकॉर्ड निर्यात करें" + +#: lists/widget.html:64 +#, python-format +msgid "%(count)d List" +msgid_plural "%(count)d Lists" +msgstr[0] "%(count)d लिस्ट" +msgstr[1] "%(count)d लिस्ट" + +#: databarAuthor.html:86 lists/widget.html:86 +msgid "from" +msgstr "से" + +#: lists/widget.html:113 +msgid "Remove" +msgstr "हटायो" + +#: merge/authors.html:7 merge/authors.html:11 merge/authors.html:102 +msgid "Merge Authors" +msgstr "लेखक मर्ज करें" + +#: merge/authors.html:18 +msgid "No authors selected." +msgstr "कोई लेखक नहीं चुना गया।" + +#: merge/authors.html:22 +msgid "Please select a primary author record." +msgstr "कृपया एक प्राथमिक लेखक रिकॉर्ड चुनें।" + +#: merge/authors.html:24 +msgid "Please select some authors to merge." +msgstr "मर्ज करने के लिए कृपया कुछ लेखक चुनें।" + +#: merge/authors.html:27 +msgid "Select a \"primary\" record that best represents the author -" +msgstr "एक \"प्राथमिक\" रिकॉर्ड का चयन करें जो लेखक का सबसे अच्छा प्रतिनिधित्व करता है -" + +#: merge/authors.html:30 +msgid "Select author records which should be merged with the primary" +msgstr "लेखक रिकॉर्ड का चयन करें जिसे प्राथमिक के साथ विलय किया जाना चाहिए" + +#: merge/authors.html:34 +msgid "Press MERGE AUTHORS." +msgstr "मर्ज लेखकों को दबाएँ।" + +#: merge/authors.html:38 +msgid "No primary record" +msgstr "कोई प्राथमिक रिकॉर्ड नहीं" + +#: merge/authors.html:39 +msgid "You must select a primary record to point the duplicates toward." +msgstr "डुप्लिकेट की ओर इशारा करने के लिए आपको एक प्राथमिक रिकॉर्ड चुनना होगा।" + +#: merge/authors.html:43 +msgid "Please Be Careful..." +msgstr "कृपया सावधान रहें ..." + +#: merge/authors.html:44 +msgid "Are you sure you want to merge these records?" +msgstr "क्या आप वाकई इन रिकॉर्ड को मर्ज करना चाहते हैं?" + +#: merge/authors.html:55 +msgid "Primary" +msgstr "प्राथमिक" + +#: merge/authors.html:56 merge_queue/merge_queue.html:125 +msgid "Merge" +msgstr "मिलाएं" + +#: merge/authors.html:86 +msgid "Open in a new window" +msgstr "नई विंडो में खोलें" + +#: merge/authors.html:93 +msgid "Visit this author's page in a new window" +msgstr "इस लेखक के पेज पर एक नई विंडो में जाएँ" + +#: merge/authors.html:99 +msgid "Comment:" +msgstr "टिप्पणी:" + +#: merge/authors.html:99 +msgid "Comment..." +msgstr "टिप्पणी..." + +#: merge/authors.html:104 +msgid "Request Merge" +msgstr "मर्ज का अनुरोध करें" + +#: merge/authors.html:107 +msgid "Reject Merge" +msgstr "मर्ज नामंज़ूर करें" + +#: merge/works.html:9 +msgid "Merge Works" +msgstr "वर्क्स मर्ज करें" + +#: merge_queue/comment.html:21 +msgid "Just now" +msgstr "अभी–अभी" + +#: merge_queue/merge_queue.html:18 +#, python-format +msgid "Showing %(username)s's requests only." +msgstr "सिर्फ़ %(username)s के अनुरोध दिखाए जा रहे हैं।" + +#: merge_queue/merge_queue.html:19 +msgid "Show all requests" +msgstr "सभी अनुरोध दिखाएँ" + +#: merge_queue/merge_queue.html:22 +msgid "Showing all requests." +msgstr "सभी अनुरोध दिखाए जा रहे हैं।" + +#: merge_queue/merge_queue.html:23 +msgid "Show my requests" +msgstr "मेरे अनुरोध दिखाएँ" + +#: merge_queue/merge_queue.html:27 +msgid "Community Edit Requests" +msgstr "समुदाय में बदलाव के अनुरोध" + +#: merge_queue/merge_queue.html:33 +#, python-format +msgid "Showing requests reviewed by %(reviewer)s only." +msgstr "सिर्फ़ %(reviewer)s द्वारा समीक्षा किए गए अनुरोध दिखाए जा रहे हैं।" + +#: merge_queue/merge_queue.html:33 +msgid "Remove reviewer filter" +msgstr "समीक्षक फ़िल्टर हटाएँ" + +#: merge_queue/merge_queue.html:35 +msgid "Show requests that I've reviewed" +msgstr "उन अनुरोधों को दिखाएँ जिनकी मैंने समीक्षा की है" + +#: merge_queue/merge_queue.html:47 +msgid "Open" +msgstr "खुला" + +#: merge_queue/merge_queue.html:50 +msgid "Closed" +msgstr "बन्द" + +#: merge_queue/merge_queue.html:58 +msgid "Submitter" +msgstr "सबमिट करने वाला" + +#: RecentChangesAdmin.html:22 merge_queue/merge_queue.html:60 merge_queue/merge_queue.html:85 +msgid "Comments" +msgstr "टिप्पणियाँ" + +#: merge_queue/merge_queue.html:61 +msgid "Reviewer" +msgstr "समीक्षक" + +#: merge_queue/merge_queue.html:62 +msgid "Resolve" +msgstr "निराकरण" + +#: merge_queue/merge_queue.html:68 +msgid "No entries here!" +msgstr "यहाँ कोई एंट्री नहीं है!" + +#: merge_queue/merge_queue.html:77 +msgid "Work" +msgstr "काम" + +#: merge_queue/merge_queue.html:84 +#, python-format +msgid "%(type)s merge request for %(title)s" +msgstr "%(type)s %(title)s के लिए मर्ज अनुरोध" + +#: merge_queue/merge_queue.html:90 +msgid "Showing most recent comment only." +msgstr "सिर्फ़ सबसे हालिया कमेंट दिखाया जा रहा है।" + +#: merge_queue/merge_queue.html:91 +msgid "View all" +msgstr "सभी देखें" + +#: merge_queue/merge_queue.html:101 +msgid "No comments yet." +msgstr "अभी तक कोई टिप्पणियाँ नहीं।" + +#: merge_queue/merge_queue.html:106 +msgid "Add a comment..." +msgstr "टिप्पणी जोड़े..." + +#: observations/review_component.html:16 +msgid "Community Reviews" +msgstr "Community Reviews" + +#: observations/review_component.html:39 +msgid "No community reviews have been submitted for this work." +msgstr "इस काम के लिए कोई सामुदायिक समीक्षा सबमिट नहीं की गई है।" + +#: observations/review_component.html:42 +msgid "+ Add your community review" +msgstr "+ अपनी समुदाय समीक्षा जोड़ें" + +#: observations/review_component.html:46 +msgid "+ Log in to add your community review" +msgstr "+ अपनी समुदाय समीक्षा जोड़ने के लिए लॉग इन करें" + +#: publishers/notfound.html:10 +msgid "Publishers" +msgstr "प्रकाशक" + +#: publishers/notfound.html:13 +#, python-format +msgid "We couldn't find any books published by %(publisher)s." +msgstr "हमें %(publisher)s द्वारा प्रकाशित कोई भी किताब नहीं मिली।" + +#: publishers/notfound.html:15 subjects/notfound.html:15 +msgid "Try something else?" +msgstr "कुछ और आज़माएँ?" + +#: publishers/view.html:15 +#, python-format +msgid "%(count)s ebook" +msgid_plural "%(count)s ebooks" +msgstr[0] "%(count)s ई - बुक्स" +msgstr[1] "%(count)s ई - बुक्स" + +#: publishers/view.html:33 +msgid "" +"This is a chart to show the when this publisher published books. Along the X axis is time, and on the " +"y axis is the count of editions published. Click here to skip the chart." +msgstr "" +"यह चार्ट यह दिखाने के लिए है कि इस प्रकाशक ने कब किताबें प्रकाशित कीं। X अक्ष के साथ समय है, और y अक्ष पर प्रकाशित " +"संस्करणों की गणना है। चार्ट को छोड़ने के लिए यहाँ क्लिक करें।" + +#: publishers/view.html:35 +msgid "" +"This graph charts editions from this publisher over time. Click to view a single year, or drag across " +"a range." +msgstr "" +"यह ग्राफ़ समय के साथ इस प्रकाशक के संस्करणों को चार्ट करता है। एक साल देखने के लिए क्लिक करें या किसी रेंज में ड्रैग करें।" + +#: publishers/view.html:52 +msgid "Common Subjects" +msgstr "सामान्य विषय" + +#: publishers/view.html:88 +msgid "published most by this publisher" +msgstr "इस प्रकाशक द्वारा सबसे अधिक प्रकाशित" + +#: recentchanges/header.html:31 recentchanges/index.html:7 recentchanges/index.html:13 +msgid "Recent Changes" +msgstr "Recent changes" + +#: recentchanges/index.html:34 +msgid "Books Edited" +msgstr "किताबें संपादित की गईं" + +#: recentchanges/index.html:35 +msgid "Author Merges" +msgstr "लेखक मर्ज करता है" + +#: recentchanges/index.html:36 +msgid "Books Added" +msgstr "किताबें जोड़ी गईं" + +#: recentchanges/index.html:37 +msgid "Covers Added" +msgstr "कवर जोड़े गए" + +#: recentchanges/index.html:58 +msgid "By Humans" +msgstr "मनुष्यों द्वारा" + +#: recentchanges/index.html:59 +msgid "By Bots" +msgstr "बॉट द्वारा" + +#: recentchanges/default/message.html:29 recentchanges/edit-book/message.html:29 +#: site/around_the_library.html:45 +msgid "opened a new Open Library account!" +msgstr "एक नया ओपन लाइब्रेरी खाता खोला!" + +#: recentchanges/merge-authors/message.html:13 +#, python-format +msgid "%(who)s merged one duplicate of %(master)s" +msgid_plural "%(who)s merged %(count)d duplicates of %(master)s" +msgstr[0] "%(who)s ने %(master)s की एक डुप्लिकेट को मर्ज किया है" +msgstr[1] "%(who)s ने %(master)s के %(count)d डुप्लिकेट को मर्ज कर दिया है" + +#: recentchanges/merge-authors/message.html:20 +#, python-format +msgid "one duplicate of %(master)s was merged anonymously" +msgid_plural "%(count)d duplicates of %(master)s were merged anonymously" +msgstr[0] "%(master)s की एक डुप्लिकेट गुमनाम व्यक्ति द्वारा मर्ज किया गया था" +msgstr[1] "%(count)d %(master)s के डुप्लिकेट को गुमनाम व्यक्ति द्वारा मर्ज किया गया था" + +#: recentchanges/merge-authors/view.html:8 +msgid "Author Merge" +msgstr "लेखक मर्ज करें" + +#: recentchanges/merge-authors/view.html:16 +msgid "This merge has been undone." +msgstr "यह विलय पूर्ववत कर दिया गया है।" + +#: recentchanges/merge-authors/view.html:17 +msgid "Details here" +msgstr "विवरण यहाँ" + +#: recentchanges/merge-authors/view.html:21 +msgid "Master" +msgstr "निपुण" + +#: recentchanges/merge-authors/view.html:40 type/author/view.html:78 +msgid "Duplicates" +msgstr "डुप्लीकेट" + +#: recentchanges/merge/comment.html:20 +#, python-format +msgid "Merged %(count)d duplicate %(type)s record into this primary." +msgid_plural "Merged %(count)d duplicate %(type)s records into this primary." +msgstr[0] "इस प्राथमिक में %(count)d डुप्लिकेट %(type)s रिकॉर्ड को मर्ज किया गया।" +msgstr[1] "इस प्राथमिक में %(count)d डुप्लिकेट %(type)s रिकॉर्ड को मर्ज किया गया।" + +#: recentchanges/merge/view.html:51 +#, python-format +msgid "%(count)d record modified." +msgid_plural "%(count)d records modified." +msgstr[0] "%(count)d रिकॉर्ड संशोधित।" +msgstr[1] "%(count)d रिकॉर्ड संशोधित।" + +#: recentchanges/merge-authors/view.html:53 +msgid "Updated Records" +msgstr "अपडेट किए गए रिकॉर्ड" + +#: search/advancedsearch.html:28 +msgid "ISBN" +msgstr "ISBN" + +#: search/advancedsearch.html:36 +msgid "Place" +msgstr "जगह" + +#: search/advancedsearch.html:40 +msgid "Person" +msgstr "व्यक्ति" + +#: search/advancedsearch.html:50 +msgid "Full Text Search" +msgstr "पूरा टेक्स्ट खोजें" + +#: search/authors.html:19 +msgid "Search Authors" +msgstr "लेखक खोजें" + +#: search/authors.html:48 +msgid "Is the same author listed twice?" +msgstr "क्या एक ही लेखक दो बार सूचीबद्ध है?" + +#: search/authors.html:48 +msgid "Merge authors" +msgstr "लेखकों को मिलाएँ" + +#: search/authors.html:51 +msgid "No hits" +msgstr "कोई हिट नहीं" + +#: search/inside.html:7 +#, python-format +msgid "Search Open Library for %s" +msgstr "%s के लिए ओपन लाइब्रेरी खोजें" + +#: BookSearchInside.html:9 SearchNavigation.html:20 search/inside.html:10 +msgid "Search Inside" +msgstr "अंदर खोजें" + +#: search/inside.html:34 +#, python-format +msgid "No hits for: %(query)s" +msgstr "%(query)s के लिए कोई हिट नहीं" + +#: search/inside.html:29 +#, python-format +msgid "About %(count)s result found" +msgid_plural "About %(count)s results found" +msgstr[0] "लगभग %(count)s परिणाम मिला" +msgstr[1] "लगभग %(count)s परिणाम मिला" + +#: search/inside.html:29 +#, python-format +msgid "in %(seconds)s second" +msgid_plural "in %(seconds)s seconds" +msgstr[0] "%(seconds)s सेकंड में" +msgstr[1] "%(seconds)s सेकंड में" + +#: search/lists.html:7 +msgid "Search results for Lists" +msgstr "सूचियों के लिए खोज परिणाम" + +#: search/lists.html:10 +msgid "Search Lists" +msgstr "खोज सूचियाँ" + +#: search/publishers.html:9 +msgid "Publishers Search" +msgstr "प्रकाशक खोजें" + +#: search/sort_options.html:9 +msgid "Sorted by" +msgstr "से छांटें" + +#: search/sort_options.html:12 +msgid "Relevance" +msgstr "प्रासंगिकता" + +#: search/sort_options.html:13 +msgid "Most Editions" +msgstr "अधिकांश संस्करण" + +#: search/sort_options.html:14 +msgid "First Published" +msgstr "पहली बार प्रकाशित" + +#: search/sort_options.html:15 +msgid "Most Recent" +msgstr "सबसे हाल का" + +#: search/sort_options.html:16 +msgid "Random" +msgstr "बेतरतीब" + +#: search/sort_options.html:33 +msgid "Shuffle" +msgstr "आवाज़ कम करें" + +#: search/subjects.html:19 +msgid "Search Subjects" +msgstr "विषय खोजें" + +#: search/subjects.html:57 +msgid "time" +msgstr "समय" + +#: search/subjects.html:59 +msgid "subject" +msgstr "विषय" + +#: search/subjects.html:61 +msgid "place" +msgstr "करें" + +#: search/subjects.html:63 +msgid "org" +msgstr "org" + +#: search/subjects.html:65 +msgid "event" +msgstr "आयोजन" + +#: search/subjects.html:67 +msgid "person" +msgstr "व्यक्ति" + +#: search/subjects.html:69 +msgid "work" +msgstr "कार्य" + +#: site/around_the_library.html:52 +msgid "View all recent changes" +msgstr "सभी हाल के बदलाव देखें" + +#: site/body.html:24 +msgid "It looks like you're offline." +msgstr "ऐसा लगता है कि आप ऑफ़लाइन हैं।" + +#: site/neck.html:15 +msgid "" +"Open Library is an open, editable library catalog, building towards a web page for every book ever " +"published. Read, borrow, and discover more than 3M books for free." +msgstr "" +"ओपन लाइब्रेरी एक खुली, संपादन योग्य लाइब्रेरी कैटलॉग है, जो हर किताब के लिए एक वेब पेज बनाती है " +"3 मिलियन से ज़्यादा किताबें मुफ़्त में पढ़ें, उधार लें और खोजें।" + +#: site/neck.html:17 +msgid "Open Library" +msgstr "ओपन लाइब्रेरी" + +#: stats/readinglog.html:8 +msgid "Reading Log Stats" +msgstr "लॉग आँकड़े पढ़ना" + +#: stats/readinglog.html:11 +msgid "# Books Logged" +msgstr "# पुस्तकें लॉग की गईं" + +#: stats/readinglog.html:12 +msgid "The total number of books logged using the Reading Log feature" +msgstr "रीडिंग लॉग सुविधा का उपयोग करके लॉग की गई पुस्तकों की कुल संख्या" + +#: stats/readinglog.html:16 stats/readinglog.html:33 stats/readinglog.html:50 +msgid "All time" +msgstr "पूरे समय" + +#: stats/readinglog.html:28 +msgid "# Unique Users Logging Books" +msgstr "# यूनीक यूज़र लॉगिंग बुक्स" + +#: stats/readinglog.html:29 +msgid "The total number of unique users who have logged at least one book using the Reading Log feature" +msgstr "रीडिंग लॉग सुविधा का उपयोग करके कम से कम एक पुस्तक लॉग करने वाले अद्वितीय उपयोगकर्ताओं की कुल संख्या" + +#: stats/readinglog.html:45 +msgid "# Books Starred" +msgstr "# Books Starred" + +#: stats/readinglog.html:46 +msgid "The total number of books which have been starred" +msgstr "उन पुस्तकों की कुल संख्या जिन्हें तारांकित किया गया है" + +#: stats/readinglog.html:58 +msgid "Total # Unique Raters (all time)" +msgstr "कुल # Unique Raters (हर समय)" + +#: stats/readinglog.html:67 +msgid "Most Wanted Books (This Month)" +msgstr "मोस्ट वांटेड बुक्स (इस महीने)" + +#: stats/readinglog.html:67 stats/readinglog.html:75 stats/readinglog.html:84 stats/readinglog.html:93 +#, python-format +msgid "Added %(count)d time" +msgid_plural "Added %(count)d times" +msgstr[0] "%(count)d बार जोड़ा गया" +msgstr[1] "%(count)d बार जोड़ा गया" + +#: stats/readinglog.html:75 +msgid "Most Wanted Books (All Time)" +msgstr "मोस्ट वांटेड बुक्स (हर समय)" + +#: stats/readinglog.html:83 +msgid "Most Logged Books" +msgstr "सबसे ज़्यादा लॉग की गई किताबें" + +#: stats/readinglog.html:84 +msgid "Most Read Books (All Time)" +msgstr "सबसे ज़्यादा पढ़ी जाने वाली किताबें (हर समय)" + +#: stats/readinglog.html:92 +msgid "Most Rated Books" +msgstr "सबसे ज़्यादा रेटिंग वाली किताबें" + +#: stats/readinglog.html:93 +msgid "Most Rated Books (All Time)" +msgstr "सबसे ज़्यादा रेटिंग वाली किताबें (सभी समय)" + +#: subjects/notfound.html:13 +msgid "We couldn't find any books about" +msgstr "हमें इसके बारे में कोई किताबें नहीं मिलीं" + +#: type/about/edit.html:9 type/i18n/edit.html:12 type/page/edit.html:9 type/permission/edit.html:9 +#: type/rawtext/edit.html:9 type/template/edit.html:9 type/usergroup/edit.html:9 +#, python-format +msgid "Edit %(title)s" +msgstr "%(title)s में बदलाव करें" + +#: type/about/edit.html:20 +msgid "This only appears in the document head, and gets attached to bookmark labels" +msgstr "यह सिर्फ़ डॉक्यूमेंट हेड में दिखाई देता है, और बुकमार्क लेबल से अटैच हो जाता है" + +#: type/about/edit.html:29 +msgid "Intro" +msgstr "परिचय" + +#: type/about/edit.html:30 +msgid "This appears in first column." +msgstr "यह पहले कॉलम में दिखाई देता है।" + +#: type/about/edit.html:40 +msgid "This appears in the second column, at top." +msgstr "यह दूसरे कॉलम में सबसे ऊपर दिखाई देता है।" + +#: type/about/edit.html:49 +msgid "Mailing List" +msgstr "डाक- सूची को जवाब भेजें" + +#: type/about/edit.html:50 +msgid "This appears below the links list." +msgstr "यह लिंक सूची के नीचे दिखाई देता है।" + +#: type/author/edit.html:19 +msgid "Edit Author" +msgstr "लेखक में बदलाव करें" + +#: type/author/edit.html:34 +msgid "" +"Please use natural order. For example: Leo Tolstoy not Tolstoy, Leo." +msgstr "" +"कृपया कुदरती ऑर्डर का इस्तेमाल करें। उदाहरण के लिए: लियो टॉल्स्टॉय टॉल्स्टॉय नहीं, लियो।" + +#: type/author/edit.html:49 +msgid "About" +msgstr "परिचय" + +#: type/author/edit.html:59 +msgid "A short bio?" +msgstr "एक छोटा सा बायो?" + +#: type/author/edit.html:60 +msgid "If you \"borrow\" information from somewhere, please make sure to cite the source." +msgstr "अगर आप कहीं से जानकारी \"उधार\" लेते हैं, तो कृपया स्रोत का हवाला देना न भूलें।" + +#: type/author/edit.html:71 +msgid "Date of birth" +msgstr "जन्मतिथि" + +#: type/author/edit.html:80 +msgid "Date of death" +msgstr "मृत्यु की तिथि" + +#: type/author/edit.html:89 +msgid "We're not storing structured dates (yet) so a date like \"21 May 2000\" is recommended." +msgstr "" +"हम संरचित तारीखों (अभी तक) को संग्रहीत नहीं कर रहे हैं, इसलिए \"21 मई 2000\" जैसी तारीख की सिफारिश की जाती है।" + +#: type/author/edit.html:98 +msgid "Date" +msgstr "तारीख़" + +#: type/author/edit.html:105 +msgid "" +"This is a deprecated field. You can help improve this record by removing this date and populating the " +"\"Date of birth\" and \"Date of death\" fields. Thanks!" +msgstr "" +"यह एक पदावनत फ़ील्ड है। आप इस तारीख को हटाकर और \"जन्म की तारीख\" और \"मृत्यु की तारीख\" फ़ील्ड को पॉप्युलेट करके " +"इस रिकॉर्ड को बेहतर बनाने में मदद कर सकते हैं। धन्यवाद!" + +#: type/author/edit.html:112 +msgid "Does this author go by any other names?" +msgstr "क्या यह लेखक किसी अन्य नाम से जाना जाता है?" + +#: type/author/edit.html:119 +msgid "Author Identifiers Purpose" +msgstr "लेखक पहचानकर्ता उद्देश्य" + +#: type/author/edit.html:129 +msgid "Websites" +msgstr "वेबसाइटें" + +#: type/author/edit.html:130 +msgid "Deprecated" +msgstr "पदावनत" + +#: type/author/view.html:18 +msgid "name missing" +msgstr "नाम गायब है" + +#: type/author/view.html:19 type/edition/view.html:89 type/work/view.html:89 +#, python-format +msgid "%(page_title)s | Open Library" +msgstr "%(page_title)s | ओपन लाइब्रेरी" + +#: type/author/view.html:32 +#, python-format +msgid "Author of %(book_titles)s" +msgstr "%(book_titles)s के लेखक" + +#: type/author/view.html:76 +msgid "Merging Authors..." +msgstr "लेखकों को मिलाया जा रहा है..." + +#: type/author/view.html:77 +msgid "In progress..." +msgstr "प्रगति पर है..." + +#: type/author/view.html:89 +msgid "Refresh the page?" +msgstr "पेज रीफ़्रेश करें?" + +#: type/author/view.html:90 +msgid "Success!" +msgstr "सफ़ल!" + +#: type/author/view.html:91 +msgid "OK. The merge is in motion. It will take a few minutes to finish the update." +msgstr "ठीक है। विलय गति में है। अपडेट पूरा होने में कुछ मिनट लगेंगे" + +#: type/author/view.html:95 +msgid "Argh!" +msgstr "आर्घ के साथ!" + +#: type/author/view.html:96 +msgid "That merge didn't work. It's our fault, and we've made a note of it." +msgstr "यह विलय काम नहीं आया। यह हमारी गलती है, और हमने इसे नोट कर लिया है।" + +#: type/author/view.html:108 +msgid "Website" +msgstr "वेबसाइट" + +#: type/author/view.html:114 +msgid "Location" +msgstr "लोकेशन" + +#: type/author/view.html:122 +msgid "Add another?" +msgstr "एक और जोड़ें?" + +#: type/author/view.html:130 +#, python-format +msgid "Showing all works by author. Would you like to see only ebooks?" +msgstr "लेखक द्वारा सभी कार्य दिखाए जा रहे हैं। क्या आप केवल ई - बुक्स देखना चाहेंगे?" + +#: type/author/view.html:132 +#, python-format +msgid "Showing ebooks only. Would you like to see everything by this author?" +msgstr "केवल ई - बुक्स दिखाई जा रही हैं। क्या आप इस लेखक द्वारा सब कुछ देखना चाहेंगे?" + +#: type/author/view.html:179 +msgid "Time" +msgstr "मैंने आपके दोस्त को एक किताब दी" + +#: type/author/view.html:190 +msgid "OLID" +msgstr "OLID" + +#: type/author/view.html:201 +msgid "Links (outside Open Library)" +msgstr "लिंक (ओपन लाइब्रेरी के बाहर)" + +#: type/author/view.html:211 +msgid "No links yet." +msgstr "अभी तक कोई लिंक नहीं है।" + +#: type/author/view.html:211 +msgid "Add one" +msgstr "एक जोड़ें" + +#: type/author/view.html:218 +msgid "Alternative names" +msgstr "वैकल्पिक नाम" + +#: type/delete/view.html:11 +msgid "This page has been deleted" +msgstr "यह पेज हटा दिया गया है" + +#: type/delete/view.html:16 +msgid "What would you like to do?" +msgstr "आप क्या करना चाहेंगे ?" + +#: type/delete/view.html:19 +msgid "Go back where I came from" +msgstr "जहाँ से मैं आया हूँ वहाँ वापस जाएँ" + +#: type/delete/view.html:21 +msgid "View the previous version" +msgstr "पिछला वर्ज़न देखें" + +#: type/delete/view.html:22 +msgid "Recreate it" +msgstr "इसे फिर से बनाएँ" + +#: type/delete/view.html:23 +msgid "See the page's history" +msgstr "पेज का इतिहास देखें" + +#: type/doc/edit.html:33 type/page/edit.html:31 +msgid "Document Body:" +msgstr "दस्तावेज़ का मुख्य भाग:" + +#: type/edition/admin_bar.html:17 +msgid "Add to Staff Picks" +msgstr "कर्मचारियों की पसंद में जोड़ें" + +#: type/edition/admin_bar.html:22 +msgid "Sync Archive.org ID" +msgstr "Archive.org आईडी सिंक करें" + +#: type/edition/admin_bar.html:25 +msgid "View Book on Archive.org" +msgstr "Archive.org पर बुक देखें" + +#: SearchResultsWork.html:107 type/edition/admin_bar.html:28 +msgid "Orphaned Edition" +msgstr "अनाथ संस्करण" + +#: databarHistory.html:26 databarView.html:33 type/edition/compact_title.html:10 +msgid "Edit this template" +msgstr "Edit this template" + +#: type/edition/modal_links.html:25 +msgid "Review" +msgstr "समीक्षा" + +#: type/edition/modal_links.html:28 +msgid "Notes" +msgstr "टीप" + +#: type/edition/modal_links.html:32 +msgid "Share" +msgstr "शेयर" + +#: type/edition/title_summary.html:8 +msgid "An edition of" +msgstr "का एक संस्करण" + +#: type/edition/title_summary.html:10 +#, python-format +msgid "First published in %s" +msgstr "पहली बार %s में प्रकाशित किया गया" + +#: type/edition/view.html:226 type/work/view.html:226 +msgid "Publish Date" +msgstr "प्रकाशित होने की तिथि" + +#: type/edition/view.html:236 type/work/view.html:236 +#, python-format +msgid "Show other books from %(publisher)s" +msgstr "%(publisher)s की अन्य किताबें दिखाएँ" + +#: type/edition/view.html:240 type/work/view.html:240 +#, python-format +msgid "Search for other books from %(publisher)s" +msgstr "%(publisher)s से अन्य किताबें खोजें" + +#: type/edition/view.html:251 type/work/view.html:251 +msgid "Pages" +msgstr "पेज" + +#: databarWork.html:101 type/edition/view.html:297 type/work/view.html:297 +msgid "Buy this book" +msgstr "यह किताब खरीदें" + +#: type/edition/view.html:329 type/work/view.html:329 +#, python-format +msgid "This edition doesn't have a description yet. Can you add one?" +msgstr "इस संस्करण का अभी तक कोई विवरण नहीं है। क्या आप एक जोड़ सकते हैं?" + +#: type/edition/view.html:352 type/work/view.html:352 +msgid "Book Details" +msgstr "पुस्तक विवरण" + +#: type/edition/view.html:356 type/work/view.html:356 +msgid "Published in" +msgstr "में प्रकाशित" + +#: type/edition/view.html:364 type/edition/view.html:454 type/edition/view.html:455 +#: type/work/view.html:364 type/work/view.html:454 type/work/view.html:455 +msgid "First Sentence" +msgstr "पहला वाक्य" + +#: type/edition/view.html:374 type/work/view.html:374 +msgid "Edition Notes" +msgstr "संस्करण नोट्स" + +#: type/edition/view.html:379 type/work/view.html:379 +msgid "Series" +msgstr "श्रेणी" + +#: type/edition/view.html:380 type/work/view.html:380 +msgid "Volume" +msgstr "आवाज़ निर्धारक" + +#: type/edition/view.html:381 type/work/view.html:381 +msgid "Genre" +msgstr "घराना" + +#: type/edition/view.html:382 type/work/view.html:382 +msgid "Other Titles" +msgstr "अन्य शीर्षक" + +#: type/edition/view.html:383 type/work/view.html:383 +msgid "Copyright Date" +msgstr "कॉपीराइट की तारीख" + +#: type/edition/view.html:384 type/work/view.html:384 +msgid "Translation Of" +msgstr "का अनुवाद" + +#: type/edition/view.html:385 type/work/view.html:385 +msgid "Translated From" +msgstr "से अनुवादित" + +#: type/edition/view.html:404 type/work/view.html:404 +msgid "External Links" +msgstr "5 बाहरी कड़ियाँ" + +#: type/edition/view.html:428 type/work/view.html:428 +msgid "Format" +msgstr "प्रारूप" + +#: type/edition/view.html:429 type/work/view.html:429 +msgid "Pagination" +msgstr "पृष्ठांकन" + +#: type/edition/view.html:430 type/work/view.html:430 +msgid "Number of pages" +msgstr "पृष्ठों की संख्या" + +#: type/edition/view.html:432 type/work/view.html:432 +msgid "Weight" +msgstr "भार" + +#: type/edition/view.html:458 type/work/view.html:458 +msgid "Work Description" +msgstr "काम का ब्यौरा" + +#: type/edition/view.html:467 type/work/view.html:467 +msgid "Original languages" +msgstr "मूल भाषाएँ" + +#: type/edition/view.html:472 type/work/view.html:472 +msgid "Excerpts" +msgstr "अंश" + +#: type/edition/view.html:482 type/work/view.html:482 +#, python-format +msgid "Page %(page)s" +msgstr "पेज %(page)s" + +#: type/edition/view.html:489 type/work/view.html:489 +#, python-format +msgid "added by %(authorlink)s." +msgstr "%(authorlink)s द्वारा जोड़ा गया।" + +#: type/edition/view.html:491 type/work/view.html:491 +msgid "added anonymously." +msgstr "गुमनाम रूप से जोड़ा गया।" + +#: type/edition/view.html:499 type/work/view.html:499 +msgid "Links outside Open Library" +msgstr "ओपन लाइब्रेरी के बाहर लिंक" + +#: type/edition/view.html:503 type/work/view.html:503 +msgid "Wikipedia" +msgstr "विकिपीडिया" + +#: type/edition/view.html:519 type/work/view.html:519 +msgid "Lists containing this Book" +msgstr "इस पुस्तक वाली सूचियाँ" + +#: type/language/view.html:22 +msgid "Code" +msgstr "कोड" + +#: type/language/view.html:31 +#, python-format +msgid "Try a search for readable books in %(language)s?" +msgstr "%(language)s में पठनीय पुस्तकों की खोज करें?" + +#: type/list/edit.html:14 +msgid "Edit List" +msgstr "सूची संपादित करें..." + +#: type/list/edit.html:25 +msgid "Label" +msgstr "लेबल" + +#: type/list/edit.html:34 type/user/edit.html:39 +msgid "Description" +msgstr "विवरण" + +#: type/list/embed.html:23 +msgid "Visit Open Library" +msgstr "ओपन लाइब्रेरी पर जाएँ" + +#: type/list/embed.html:42 type/list/view_body.html:199 +msgid "Unknown authors" +msgstr "अज्ञात लेखक" + +#: type/list/embed.html:67 +msgid "" +"Open in online Book Reader. Downloads available in ePub, DAISY, PDF, TXT formats from main book page" +msgstr "ऑनलाइन बुक रीडर में खोलें। मुख्य पुस्तक पृष्ठ से ePub, DAISY, PDF, TXT प्रारूपों में उपलब्ध डाउनलोड" + +#: type/list/embed.html:73 +msgid "This book is checked out" +msgstr "यह किताब चेक आउट की गई है" + +#: type/list/embed.html:75 +msgid "Checked out" +msgstr "चेक आउट किया गया" + +#: type/list/embed.html:78 +msgid "Borrow book" +msgstr "उधार लेने की किताब" + +#: type/list/embed.html:83 +msgid "Protected DAISY" +msgstr "संरक्षित डेज़ी" + +#: BookByline.html:34 type/list/embed.html:100 +#, python-format +msgid "by %(name)s" +msgstr "%(name)s द्वारा" + +#: type/list/exports.html:30 +msgid "Export" +msgstr "एक्सपोर्ट करें" + +#: type/list/exports.html:31 +#, python-format +msgid "as %(json_link)s, %(html_link)s, or %(bibtex_link)s" +msgstr "%(json_link)s, %(html_link)s, या %(bibtex_link)s के रूप में" + +#: type/list/exports.html:35 type/list/exports.html:55 +msgid "Subscribe" +msgstr "सब्सक्राइब करें" + +#: type/list/exports.html:36 type/list/exports.html:56 +#, python-format +msgid "Watch activity via Atom feed" +msgstr "एटम फ़ीड के ज़रिए गतिविधि देखें" + +#: type/list/exports.html:43 +msgid "Export Not Available" +msgstr "निर्यात उपलब्ध नहीं है" + +#: type/list/exports.html:48 +#, python-format +msgid "Only lists with up to %(max)s editions can be exported. This one has %(count)s." +msgstr "केवल %(max)s संस्करणों वाली सूचियों को निर्यात किया जा सकता है। इसमें %(count)s हैं।" + +#: type/list/exports.html:50 +#, python-format +msgid "" +"Try removing some seeds for more focus, or look at bulk download of the catalog." +msgstr "अधिक फ़ोकस के लिए कुछ बीज निकालने की कोशिश करें, या कैटलॉग के थोक डाउनलोड को देखें।" + +#: type/list/view_body.html:8 +#, python-format +msgid "%(name)s | Lists | Open Library" +msgstr "%(name)s | सूचियाँ | Open Library" + +#: type/list/view_body.html:17 +#, python-format +msgid "%(title)s: %(description)s" +msgstr "%(title)s :%(description)s" + +#: type/list/view_body.html:26 +msgid "View the list on Open Library." +msgstr "ओपन लाइब्रेरी पर सूची देखें।" + +#: type/list/view_body.html:165 +msgid "Remove this item?" +msgstr "यह आइटम हटाएँ?" + +#: type/list/view_body.html:179 +#, python-format +msgid "Last updated %(date)s" +msgstr "आखिरी बार %(date)s अपडेट किया गया" + +#: type/list/view_body.html:190 +msgid "Remove seed" +msgstr "बीज निकालें" + +#: type/list/view_body.html:190 +msgid "Are you sure you want to remove this item from the list?" +msgstr "क्या आप वाकई इस आइटम को लिस्ट से हटाना चाहते हैं?" + +#: type/list/view_body.html:191 +msgid "Remove Seed" +msgstr "बीज निकालें" + +#: type/list/view_body.html:192 +msgid "" +"You are about to remove the last item in the list. That will delete the whole list. Are you sure you " +"want to continue?" +msgstr "आप सूची में से अंतिम आइटम को हटाने वाले हैं। इससे पूरी लिस्ट डिलीट हो जाएगी। क्या आप वाकई जारी रखना चाहते हैं?" + +#: type/list/view_body.html:261 +msgid "List Metadata" +msgstr "सूची मेटाडेटा" + +#: type/list/view_body.html:262 +msgid "Derived from seed metadata" +msgstr "बीज मेटाडेटा से व्युत्पन्न" + +#: type/local_id/view.html:22 +msgid "Source Item" +msgstr "स्रोत आइटम" + +#: type/local_id/view.html:34 +msgid "prefix" +msgstr "उपसर्ग" + +#: type/local_id/view.html:38 +msgid "Example" +msgstr "उदहारण" + +#: type/permission/view.html:17 +msgid "Readers" +msgstr "रीडर्स" + +#: type/permission/view.html:23 +msgid "Writers" +msgstr "लेखक" + +#: type/rawtext/edit.html:31 type/template/edit.html:41 +msgid "Document Body" +msgstr "दस्तावेज़ का मुख्य हिस्सा" + +#: type/template/edit.html:51 +msgid "Delete this template?" +msgstr "इस टेम्प्लेट को मिटाएँ?" + +#: type/type/view.html:19 +msgid "Kind" +msgstr "क़िस्म" + +#: type/type/view.html:31 +msgid "Backreferences" +msgstr "बैकरेफ़रेंस" + +#: type/user/edit.html:13 +msgid "Currently Editing:" +msgstr "वर्तमान में संपादन:" + +#: type/user/edit.html:25 +msgid "Display Name" +msgstr "नाम प्रदर्शित करें" + +#: type/user/view.html:24 +#, python-format +msgid "Joined %(date)s" +msgstr "%(date)s में शामिल हुए" + +#: type/user/view.html:32 +msgid "admin page" +msgstr "एडमिन पेज" + +#: type/user/view.html:53 +#, python-format +msgid "" +"You are publicly sharing the books you are currently " +"reading, have already read, and want to read." +msgstr "" +"आप उन पुस्तकों को सार्वजनिक रूप से साझा कर रहे हैं जिन्हें आप वर्तमान में पढ़ रहे हैं, पहले ही पढ़ चुके हैं, और पढ़ना चाहते हैं।" + +#: type/user/view.html:55 +msgid "You have chosen to make your" +msgstr "आपने अपना" + +#: type/user/view.html:55 +msgid "private" +msgstr "निजी" + +#: type/user/view.html:60 +#, python-format +msgid "" +"Here are the books %(user)s is currently reading, " +"have already read, and want to read!" +msgstr "" +"यहाँ वे पुस्तकें दिखाई गई हैं जो %(user)s वर्तमान में पढ़ रहे " +"हैं, पहले ही पढ़ चुके हैं, और पढ़ना चाहते हैं!" + +#: type/user/view.html:62 +msgid "This reader has chosen to make their Reading Log private." +msgstr "इस पाठक ने अपने रीडिंग लॉग को निजी बनाने के लिए चुना है।" + +#: RecentChangesUsers.html:64 type/user/view.html:84 +msgid "No edits. Yet." +msgstr "कोई बदलाव नहीं। फिर भी।" + +#: SearchResultsWork.html:83 type/work/editions.html:11 +#, python-format +msgid "First published in %(year)s" +msgstr "पहली बार %(year)s में प्रकाशित हुआ" + +#: type/work/editions.html:14 type/work/editions_datatable.html:47 +#, python-format +msgid "Add another edition of %(work)s" +msgstr "%(work)s का एक और संस्करण जोड़ें" + +#: UserEditRow.html:25 type/work/editions.html:14 +msgid "Add another" +msgstr "एक और जोड़ें" + +#: type/work/editions.html:43 +msgid "Title missing" +msgstr "शीर्षक मौजूद नहीं है" + +#: type/work/editions_datatable.html:9 +#, python-format +msgid "Showing %(count)d featured edition." +msgid_plural "Showing %(count)d featured editions." +msgstr[0] "%(count)d फीचर्ड संस्करण दिखाया जा रहा है।" +msgstr[1] "%(count)d फीचर्ड संस्करण दिखाए जा रहे हैं।" + +#: type/work/editions_datatable.html:14 +#, python-format +msgid "View all %(count)d editions?" +msgstr "सभी %(count)d संस्करण देखें?" + +#: type/work/editions_datatable.html:20 +msgid "Edition" +msgstr "संस्करण" + +#: type/work/editions_datatable.html:21 +msgid "Availability" +msgstr "उपलब्धता" + +#: type/work/editions_datatable.html:42 +msgid "No editions available" +msgstr "कोई संस्करण उपलब्ध नहीं है" + +#: type/work/editions_datatable.html:47 +msgid "Add another edition?" +msgstr "एक और संस्करण जोड़ें?" + +#: AffiliateLinks.html:15 +msgid "Better World Books" +msgstr "बेहतर विश्व पुस्तकें" + +#: AffiliateLinks.html:19 +msgid " - includes shipping" +msgstr " - इसमें शिपिंग भी शामिल है" + +#: AffiliateLinks.html:25 +msgid "Amazon" +msgstr "Amazon" + +#: AffiliateLinks.html:32 +msgid "Bookshop.org" +msgstr "Bookshop.org" + +#: AffiliateLinks.html:54 +#, python-format +msgid "Look for this edition for sale at %(store)s" +msgstr "%(store)s पर बिक्री के लिए इस संस्करण को देखें" + +#: BookByline.html:11 +#, python-format +msgid "%(n)s other" +msgid_plural "%(n)s others" +msgstr[0] "%(n)s अन्य" +msgstr[1] "%(n)s अन्य" + +#: BookByline.html:36 +msgid "by an unknown author" +msgstr "एक अज्ञात लेखक द्वारा" + +#: BookPreview.html:19 +msgid "Preview Book" +msgstr "पुस्तक का पूर्वावलोकन करें" + +#: BookPreview.html:29 +msgid "See more about this book on Archive.org" +msgstr "Archive.org पर इस किताब के बारे में और देखें" + +#: EditButtons.html:9 EditButtonsMacros.html:12 +msgid "(Optional, but very useful!)" +msgstr "(वैकल्पिक, लेकिन बहुत उपयोगी!)" + +#: EditButtonsMacros.html:27 +msgid "Delete this record" +msgstr "इस रेकॉर्ड को मिटाएँ" + +#: EditionNavBar.html:11 +msgid "Overview" +msgstr "समीक्षा" + +#: EditionNavBar.html:15 +#, python-format +msgid "View %(count)s Edition" +msgid_plural "View %(count)s Editions" +msgstr[0] "%(count)s संस्करण देखें" +msgstr[1] "%(count)s संस्करण देखें" + +#: EditionNavBar.html:19 +msgid "Details" +msgstr "विवरण" + +#: EditionNavBar.html:24 +#, python-format +msgid "%(reviews)s Review" +msgid_plural "%(reviews)s Reviews" +msgstr[0] "%(reviews)s समीक्षा" +msgstr[1] "%(reviews)s समीक्षाएँ" + +#: EditionNavBar.html:33 +msgid "Related Books" +msgstr "संबंधित पुस्तकें" + +#: LoanStatus.html:56 +msgid "Return eBook" +msgstr "ई - बुक लौटाएँ" + +#: LoanStatus.html:62 +msgid "Really return this book?" +msgstr "क्या आप वाकई यह किताब लौटा रहे हैं?" + +#: LoanStatus.html:97 +msgid "You are next on the waiting list" +msgstr "आप प्रतीक्षा सूची में अगले स्थान पर हैं" + +#: LoanStatus.html:99 +#, python-format +msgid "You are #%(spot)d of %(wlsize)d on the waiting list." +msgstr "आप प्रतीक्षा सूची में %(wlsize)d में से #%(spot)d में हैं।" + +#: LoanStatus.html:104 +msgid "Leave waiting list" +msgstr "प्रतीक्षा सूची छोड़ें" + +#: LoanStatus.html:118 +#, python-format +msgid "Readers in line: %(count)s" +msgstr "पंक्ति में पाठक: %(count)s" + +#: LoanStatus.html:120 +msgid "You'll be next in line." +msgstr "आप अगली कतार में होंगे।" + +#: LoanStatus.html:125 +msgid "This book is currently checked out, please check back later." +msgstr "यह किताब फ़िलहाल चेक आउट की जा रही है, कृपया बाद में फिर से देखें।" + +#: LoanStatus.html:126 +msgid "Checked Out" +msgstr "चेक आउट किया गया" + +#: NotInLibrary.html:9 +msgid "Not in Library" +msgstr "लाइब्रेरी में नहीं है" + +#: NotesModal.html:31 +msgid "My Book Notes" +msgstr "मेरी किताब के नोट्स" + +#: NotesModal.html:35 +msgid "My private notes about this edition:" +msgstr "इस संस्करण के बारे में मेरे निजी नोट:" + +#: ObservationsModal.html:31 +msgid "My Book Review" +msgstr "मेरी किताब की समीक्षा" + +#: Pager.html:26 +msgid "First" +msgstr "पहला नाम" + +#: ReadButton.html:11 +msgid "Special Access" +msgstr "खास ऐक्सेस" + +#: ReadButton.html:12 +msgid "Special ebook access from the Internet Archive for patrons with qualifying print disabilities" +msgstr "योग्य प्रिंट विकलांगता वाले सब्सक्राइबर्स के लिए इंटरनेट आर्काइव से विशेष ईबुक एक्सेस" + +#: ReadButton.html:16 +msgid "Borrow ebook from Internet Archive" +msgstr "इंटरनेट आर्काइव से ई - बुक उधार लें" + +#: ReadButton.html:20 +msgid "Read ebook from Internet Archive" +msgstr "इंटरनेट आर्काइव से ईबुक पढ़ें" + +#: ReadMore.html:7 +msgid "Read more" +msgstr "और पढ़ें" + +#: ReadMore.html:8 +msgid "Read less" +msgstr "कम पढ़ें" + +#: ReadingLogDropper.html:88 +msgid "My Reading Lists:" +msgstr "मेरी पठन सूची:" + +#: ReadingLogDropper.html:103 +msgid "Use this Work" +msgstr "इस कार्य का उपयोग करें" + +#: ReadingLogDropper.html:106 ReadingLogDropper.html:127 ReadingLogDropper.html:143 databarAuthor.html:27 +#: databarAuthor.html:34 +msgid "Create a new list" +msgstr "नई कार्य सूची सृजित करें" + +#: ReadingLogDropper.html:117 ReadingLogDropper.html:133 +msgid "Add to List" +msgstr "बड्डी सूची जोड़ें" + +#: ReadingLogDropper.html:157 +msgid "Description:" +msgstr "विवरण :" + +#: ReadingLogDropper.html:165 +msgid "Create new list" +msgstr "नया बनाएँ" + +#: RecentChanges.html:54 +msgid "View MARC" +msgstr "मार्क देखें" + +#: RecentChangesAdmin.html:21 +msgid "Path" +msgstr "पाथ" + +#: RecentChangesAdmin.html:41 +msgid "view" +msgstr "देखें" + +#: RecentChangesAdmin.html:42 +msgid "edit" +msgstr "एडिट करें" + +#: RecentChangesAdmin.html:43 +msgid "diff" +msgstr "डिफ" + +#: ReturnForm.html:8 +msgid "Return book" +msgstr "रिटर्न बुक" + +#: SearchResultsWork.html:89 +#, python-format +msgid "in 1 language" +msgid_plural "in %(count)d languages" +msgstr[0] "1 भाषा में" +msgstr[1] "%(count)d भाषाओं में" + +#: SearchResultsWork.html:92 +#, python-format +msgid "%s previewable" +msgstr "%s पूर्वावलोकन योग्य" + +#: SearchResultsWork.html:102 +msgid "This is only visible to librarians." +msgstr "यह केवल लाइब्रेरियन को दिखाई देता है।" + +#: SearchResultsWork.html:104 +msgid "Work Title" +msgstr "कार्य का शीर्षक" + +#: ShareModal.html:44 +msgid "Embed this book in your website" +msgstr "इस किताब को अपनी वेबसाइट में एम्बेड करें" + +#: ShareModal.html:48 +msgid "Embed" +msgstr "एम्बेड करें" + +#: StarRatings.html:52 +msgid "Clear my rating" +msgstr "मेरी रेटिंग साफ़ करें" + +#: StarRatingsStats.html:22 +msgid "Ratings" +msgstr "रेटिंग" + +#: StarRatingsStats.html:24 +msgid "Want to read" +msgstr "पढ़ने में रुचि" + +#: StarRatingsStats.html:25 +msgid "Currently reading" +msgstr "फ़िलहाल पढ़ रहे हैं" + +#: StarRatingsStats.html:26 +msgid "Have read" +msgstr "पढ़ लिया है" + +#: Subnavigation.html:9 +msgid "Developer Center (Home)" +msgstr "डेवलपर केंद्र (होम)" + +#: Subnavigation.html:10 +msgid "Web API" +msgstr "वेब एपीआई" + +#: Subnavigation.html:11 +msgid "Client Library" +msgstr "Client Library" + +#: Subnavigation.html:12 +msgid "Data Dumps" +msgstr "डेटा डंप" + +#: Subnavigation.html:14 +msgid "Report an Issue" +msgstr "किसी समस्या की रिपोर्ट करें" + +#: Subnavigation.html:15 +msgid "Licensing" +msgstr "लाइसेंसिंग" + +#: Subnavigation.html:19 +msgid "Welcome" +msgstr "स्वागत" + +#: Subnavigation.html:20 +msgid "Searching Open Library" +msgstr "ओपन लाइब्रेरी सर्च की जा रही है" + +#: Subnavigation.html:21 +msgid "Reading Books" +msgstr "गाने सुनना" + +#: Subnavigation.html:22 +msgid "Adding & Editing Books" +msgstr "किताबें जोड़ना और संपादित करना" + +#: Subnavigation.html:23 +msgid "Using Subjects" +msgstr "विषयों का उपयोग करना" + +#: Subnavigation.html:24 +msgid "Open Library F.A.Q." +msgstr "Open Library F.A.Q." + +#: TypeChanger.html:17 +msgid "Page Type" +msgstr "पेज का प्रकार" + +#: TypeChanger.html:19 +msgid "Huh?" +msgstr "Huh?" + +#: TypeChanger.html:22 +msgid "" +"Every piece of Open Library is defined by the type of content it displays, usually by content " +"definition (e.g. Authors, Editions, Works) but sometimes by content use (e.g. macro, template, " +"rawtext). Changing this for an existing page will alter its presentation and the data fields available " +"for editing its content." +msgstr "" +"ओपन लाइब्रेरी के प्रत्येक टुकड़े को आमतौर पर सामग्री परिभाषा (जैसे लेखक, संस्करण, कार्य) द्वारा प्रदर्शित सामग्री के प्रकार " +"से परिभाषित किया जाता है, लेकिन कभी - कभी सामग्री उपयोग (जैसे मैक्रो, टेम्पलेट, रॉटेक्स्ट) द्वारा परिभाषित किया जाता " +"है। मौजूदा पेज के लिए इसे बदलने से इसकी प्रस्तुति और इसके कंटेंट को एडिट करने के लिए उपलब्ध डेटा फ़ील्ड में बदलाव होगा।" + +#: TypeChanger.html:22 +msgid "Please use caution changing Page Type!" +msgstr "कृपया पेज का प्रकार बदलने में सावधानी बरतें!" + +#: TypeChanger.html:22 +msgid "(Simplest solution: If you aren't sure whether this should be changed, don't change it.)" +msgstr "(सबसे आसान समाधान: अगर आपको यकीन नहीं है कि इसे बदला जाना चाहिए या नहीं, तो इसे न बदलें।)" + +#: WorldcatLink.html:9 +msgid "Check nearby libraries" +msgstr "आस - पास के लाइब्रेरी देखें" + +#: WorldcatLink.html:13 +msgid "Check WorldCat for an edition near you" +msgstr "अपने आस - पास के किसी संस्करण के लिए WorldCat देखें" + +#: databarAuthor.html:20 +msgid "Add to list" +msgstr "सूचि में जोड़े" + +#: databarAuthor.html:35 +msgid "×" +msgstr "×" + +#: databarAuthor.html:64 +msgid "Add new list" +msgstr "नई लिस्ट जोड़ें" + +#: databarHistory.html:16 databarView.html:22 +#, python-format +msgid "Last edited by %(author_link)s" +msgstr "अंतिम बार %(author_link)s द्वारा संपादित किया गया" + +#: databarHistory.html:18 databarView.html:24 +msgid "Last edited anonymously" +msgstr "अंतिम बार गुमनाम रूप से संपादित किया गया" + +#: databarWork.html:115 +#, python-format +msgid "This book was generously donated by %(donor)s" +msgstr "इस किताब को %(donor)s ने दान किया था" + +#: databarWork.html:117 +msgid "This book was generously donated anonymously" +msgstr "इस किताब को गुमनाम रूप से दान किया गया था" + +#: databarWork.html:122 +msgid "Learn more about Book Sponsorship" +msgstr "बुक स्पॉन्सरशिप के बारे में और जानें" + +#: account.py:420 account.py:458 +#, python-format +msgid "" +"We've sent the verification email to %(email)s. You'll need to read that and click on the verification " +"link to verify your email." +msgstr "" +"हमने वेरिफ़िकेशन ईमेल %(email)s पर भेज दिया है। आपको इसे पढ़ना होगा और अपना ईमेल वेरिफ़ाई करने के लिए वेरिफ़िकेशन लिंक " +"पर क्लिक करना होगा।" + +#: account.py:486 +msgid "Username must be between 3-20 characters" +msgstr "यूज़रनेम 3 -20 कैरेक्टर के बीच होना चाहिए" + +#: account.py:488 +msgid "Username may only contain numbers and letters" +msgstr "यूज़रनेम में सिर्फ़ संख्याएँ और अक्षर हो सकते हैं" + +#: account.py:491 +msgid "Username unavailable" +msgstr "यूज़रनेम उपलब्ध नहीं है" + +#: account.py:496 forms.py:60 +msgid "Must be a valid email address" +msgstr "एक मान्य ईमेल पता होना चाहिए" + +#: account.py:500 forms.py:41 +msgid "Email already registered" +msgstr "ईमेल पहले से ही पंजीकृत है" + +#: account.py:526 +msgid "Email address is already used." +msgstr "ईमेल पता पहले ही इस्तेमाल किया जा चुका है।" + +#: account.py:527 +msgid "Your email address couldn't be updated. The specified email address is already used." +msgstr "आपका ईमेल पता अपडेट नहीं किया जा सका। निर्दिष्ट ईमेल पता पहले से ही उपयोग किया जाता है।" + +#: account.py:533 +msgid "Email verification successful." +msgstr "ईमेल पता सत्यापन सफल." + +#: account.py:534 +msgid "Your email address has been successfully verified and updated in your account." +msgstr "आपका ईमेल पता आपके अकाउंट में सफलतापूर्वक सत्यापित और अपडेट कर दिया गया है।" + +#: account.py:540 +msgid "Email address couldn't be verified." +msgstr "ईमेल पते की पुष्टि नहीं की जा सकी।" + +#: account.py:541 +msgid "Your email address couldn't be verified. The verification link seems invalid." +msgstr "आपके ईमेल पते की पुष्टि नहीं की जा सकी। वेरिफ़िकेशन का लिंक अमान्य लगता है।" + +#: account.py:645 account.py:655 +msgid "Password reset failed." +msgstr "पासवर्ड रीसेट विफल." + +#: account.py:702 account.py:721 +msgid "Notification preferences have been updated successfully." +msgstr "नोटिफ़िकेशन की प्राथमिकताएँ सफलतापूर्वक अपडेट कर दी गई हैं।" + +#: forms.py:30 +msgid "Username" +msgstr "यूज़रनेम" + +#: forms.py:37 +msgid "No user registered with this email address" +msgstr "इस ईमेल पते के साथ कोई उपयोगकर्ता पंजीकृत नहीं है" + +#: forms.py:44 +msgid "Disposable email not permitted" +msgstr "डिस्पोजेबल ईमेल की अनुमति नहीं है" + +#: forms.py:48 +msgid "Your email provider is not recognized." +msgstr "आपका ईमेल प्रदाता पहचाना नहीं जा सका।" + +#: forms.py:52 +msgid "Username already used" +msgstr "यूज़रनेम पहले ही इस्तेमाल किया जा चुका है" + +#: forms.py:57 +msgid "Must be between 3 and 20 letters and numbers" +msgstr "3 से 20 अक्षरों और संख्याओं के बीच होना चाहिए" + +#: forms.py:59 +msgid "Must be between 3 and 20 characters" +msgstr "3 से 20 कैरेक्टर के बीच होना चाहिए" + +#: forms.py:78 forms.py:156 +msgid "Your email address" +msgstr "आपका ईमेल पता" + +#: forms.py:90 +msgid "Choose a screen name. Screen names are public and cannot be changed later." +msgstr "स्क्रीन का नाम चुनें। स्क्रीन नाम सार्वजनिक हैं और उन्हें बाद में बदला नहीं जा सकता।" + +#: forms.py:94 +msgid "Letters and numbers only please, and at least 3 characters." +msgstr "कृपया सिर्फ़ अक्षरों और संख्याओं और कम - से - कम 3 कैरेक्टर का इस्तेमाल करें।" + +#: forms.py:100 forms.py:162 +msgid "Choose a password" +msgstr "पासवर्ड चुनें" + +#: forms.py:106 +msgid "Confirm password" +msgstr "पासवर्ड कन्फ़र्म करें" + +#: forms.py:110 +msgid "Passwords didn't match." +msgstr "पासवर्ड मेल नहीं खाते।" + +#: forms.py:115 +msgid "" +"I want to receive news, announcements, and resources from the Internet Archive, the non-profit that runs Open Library." +msgstr "" +"मैं इंटरनेट आर्काइव से समाचार, घोषणाएं और संसाधन प्राप्त करना चाहता हूं, जो " +"ओपन लाइब्रेरी चलाने वाली गैर - लाभकारी संस्था है।" + +#: forms.py:151 +msgid "Invalid password" +msgstr "अमान्य पासवर्ड" diff --git a/openlibrary/templates/languages/language_list.html b/openlibrary/templates/languages/language_list.html index cc8017cdf81..bd10e48c7b9 100644 --- a/openlibrary/templates/languages/language_list.html +++ b/openlibrary/templates/languages/language_list.html @@ -9,6 +9,7 @@
  • Hrvatski (hr)
  • Italiano (it)
  • Português (pt)
  • +
  • हिंदी (hi)
  • తెలుగు (te)
  • Українська (uk)
  • 中文 (zh)
  • From f05ac84ae106c2e405804202908ec5e84b3b9f82 Mon Sep 17 00:00:00 2001 From: Scott Barnes Date: Sun, 4 Feb 2024 19:20:21 -0800 Subject: [PATCH 53/61] Docker `README.md`: clarify directory path --- docker/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docker/README.md b/docker/README.md index 7d1c2f9ba68..3fc40541631 100644 --- a/docker/README.md +++ b/docker/README.md @@ -113,7 +113,7 @@ Note: please update this README with the exact wording of the error if you run i The following should populate the target of the `infogami` symbolic link (i.e. `vendor/infogami/`): ``` -cd local-openlibrary-dev-directory +cd path/to/your/cloned/openlibrary git submodule init; git submodule sync; git submodule update ``` @@ -126,7 +126,7 @@ Ensure you're running `docker compose` commands from within the `local-openlibra ## Teardown commands ```sh -cd local-openlibrary-dev-directory +cd path/to/your/cloned/openlibrary # stop the app (if started in detached mode) docker compose down From 2e831cffd6c7ecd53cca063eb2ec778192e89ff9 Mon Sep 17 00:00:00 2001 From: Drini Cami Date: Tue, 6 Feb 2024 15:19:25 -0500 Subject: [PATCH 54/61] Handle ProductJSON being undefined (#8794) --- scripts/promise_batch_imports.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/scripts/promise_batch_imports.py b/scripts/promise_batch_imports.py index 3edbc987c08..c1d93ef6edb 100644 --- a/scripts/promise_batch_imports.py +++ b/scripts/promise_batch_imports.py @@ -38,9 +38,15 @@ def format_date(date: str, only_year: bool) -> str: def map_book_to_olbook(book, promise_id): + def clean_null(val: str | None) -> str | None: + if val in ('', 'null', 'null--'): + return None + return val + asin_is_isbn_10 = book.get('ASIN') and book.get('ASIN')[0].isdigit() - publish_date = book['ProductJSON'].get('PublicationDate') - title = book['ProductJSON'].get('Title') + product_json = book.get('ProductJSON', {}) + publish_date = clean_null(product_json.get('PublicationDate')) + title = product_json.get('Title') isbn = book.get('ISBN') or ' ' sku = book['BookSKUB'] or book['BookSKU'] or book['BookBarcode'] olbook = { @@ -52,8 +58,8 @@ def map_book_to_olbook(book, promise_id): **({'isbn_13': [isbn]} if is_isbn_13(isbn) else {}), **({'isbn_10': [book.get('ASIN')]} if asin_is_isbn_10 else {}), **({'title': title} if title else {}), - 'authors': [{"name": book['ProductJSON'].get('Author') or '????'}], - 'publishers': [book['ProductJSON'].get('Publisher') or '????'], + 'authors': [{"name": clean_null(product_json.get('Author')) or '????'}], + 'publishers': [clean_null(product_json.get('Publisher')) or '????'], 'source_records': [f"promise:{promise_id}:{sku}"], # format_date adds hyphens between YYYY-MM-DD, or use only YYYY if date is suspect. 'publish_date': format_date( From 3ecb735145b37a2e57c2a55dd2bb45378e9a6326 Mon Sep 17 00:00:00 2001 From: Rebecca Shoptaw Date: Tue, 6 Feb 2024 15:24:21 -0500 Subject: [PATCH 55/61] Add accept attribute to image input. --- openlibrary/templates/covers/add.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openlibrary/templates/covers/add.html b/openlibrary/templates/covers/add.html index f03652711bf..22738d5d199 100644 --- a/openlibrary/templates/covers/add.html +++ b/openlibrary/templates/covers/add.html @@ -54,7 +54,7 @@
    - +
    From 82382fda8ba0e6b1836cca1d2ce099309ee18481 Mon Sep 17 00:00:00 2001 From: Drini Cami Date: Tue, 6 Feb 2024 17:22:26 -0500 Subject: [PATCH 56/61] Fix my books breadcrumb/headers mismatch --- openlibrary/plugins/upstream/mybooks.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openlibrary/plugins/upstream/mybooks.py b/openlibrary/plugins/upstream/mybooks.py index ea2d3af6c36..5298cc9d6d2 100644 --- a/openlibrary/plugins/upstream/mybooks.py +++ b/openlibrary/plugins/upstream/mybooks.py @@ -229,10 +229,10 @@ def GET(self, username, key='want-to-read'): mb = MyBooksTemplate(username, key) KEYS_TITLES = { 'currently-reading': _( - "Want to Read (%(count)d)", count=mb.counts['want-to-read'] + "Currently Reading (%(count)d)", count=mb.counts['currently-reading'] ), 'want-to-read': _( - "Currently Reading (%(count)d)", count=mb.counts['currently-reading'] + "Want to Read (%(count)d)", count=mb.counts['want-to-read'] ), 'already-read': _( "Already Read (%(count)d)", count=mb.counts['already-read'] From 34ed58baff5d1b56e5721eef816334a4a0cfd9e3 Mon Sep 17 00:00:00 2001 From: DebbieSan Date: Thu, 8 Feb 2024 13:36:14 -0800 Subject: [PATCH 57/61] remove second notes option --- openlibrary/templates/books/mybooks_breadcrumb_select.html | 1 - 1 file changed, 1 deletion(-) diff --git a/openlibrary/templates/books/mybooks_breadcrumb_select.html b/openlibrary/templates/books/mybooks_breadcrumb_select.html index fdb08b6f6cd..64c6caa3bbf 100644 --- a/openlibrary/templates/books/mybooks_breadcrumb_select.html +++ b/openlibrary/templates/books/mybooks_breadcrumb_select.html @@ -14,7 +14,6 @@ $ options += [ $ (_("Loans"), "/account/loans"), $ (_("Notes"), url_prefix + "/books/notes"), - $ (_("Notes"), url_prefix + "/books/notes"), $ (_("Reviews"), url_prefix + "/books/observations"), $ (_("Imports and Exports"), "/account/import") $ ] From 1d7f17ea3fa28d4b9c1f98f87ec6c53ad620789d Mon Sep 17 00:00:00 2001 From: Rishabh Kumar Date: Sat, 10 Feb 2024 02:27:00 +0530 Subject: [PATCH 58/61] Fixed Unable to render this page Error when empty spaces passed to search/authors and search/subjects (#8805) --- openlibrary/templates/search/authors.html | 2 +- openlibrary/templates/search/subjects.html | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/openlibrary/templates/search/authors.html b/openlibrary/templates/search/authors.html index a1644ff9a2a..fe93b1a2b7e 100644 --- a/openlibrary/templates/search/authors.html +++ b/openlibrary/templates/search/authors.html @@ -1,6 +1,6 @@ $def with (get_results) -$ q = query_param('q', '') +$ q = query_param('q', '').strip() $ results_per_page = 100 $ page = query_param('page') $if page: diff --git a/openlibrary/templates/search/subjects.html b/openlibrary/templates/search/subjects.html index 48a06fc0a0c..5038076b54f 100644 --- a/openlibrary/templates/search/subjects.html +++ b/openlibrary/templates/search/subjects.html @@ -1,6 +1,6 @@ $def with (get_results) -$ q = query_param('q') +$ q = query_param('q', '').strip() $ results_per_page = 100 $ page = query_param('page') $if page: From 1333498ce80eb2c899255506cd17abf12a6e57dd Mon Sep 17 00:00:00 2001 From: Rebecca Shoptaw Date: Tue, 13 Feb 2024 13:28:15 -0500 Subject: [PATCH 59/61] Generate new .pot file. --- openlibrary/i18n/messages.pot | 2132 +++++++++++++++++++-------------- 1 file changed, 1212 insertions(+), 920 deletions(-) diff --git a/openlibrary/i18n/messages.pot b/openlibrary/i18n/messages.pot index 81a78e23946..65bf3840619 100644 --- a/openlibrary/i18n/messages.pot +++ b/openlibrary/i18n/messages.pot @@ -1,88 +1,88 @@ # Translations template for Open Library. -# Copyright (C) 2023 Internet Archive +# Copyright (C) 2024 Internet Archive # This file is distributed under the same license as the Open Library # project. -# FIRST AUTHOR , 2023. +# FIRST AUTHOR , 2024. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: Open Library VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2023-01-06 20:53+0000\n" +"POT-Creation-Date: 2024-02-13 18:24+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" -"Generated-By: Babel 2.9.1\n" +"Generated-By: Babel 2.12.1\n" -#: account.html:7 account.html:15 account/notifications.html:13 +#: account.html:7 account.html:18 account/notifications.html:13 #: account/privacy.html:13 lib/nav_head.html:15 type/user/view.html:27 msgid "Settings" msgstr "" -#: account.html:20 +#: account.html:23 msgid "Settings & Privacy" msgstr "" -#: account.html:21 databarDiff.html:12 +#: account.html:24 databarDiff.html:12 msgid "View" msgstr "" -#: account.html:21 +#: account.html:24 msgid "or" msgstr "" -#: account.html:21 check_ins/check_in_prompt.html:26 -#: check_ins/reading_goal_progress.html:26 databarHistory.html:27 -#: databarTemplate.html:13 databarView.html:34 -#: type/edition/compact_title.html:11 +#: account.html:24 check_ins/check_in_prompt.html:26 +#: check_ins/reading_goal_progress.html:27 databarHistory.html:27 +#: databarTemplate.html:13 databarView.html:35 subjects.html:25 +#: type/edition/compact_title.html:11 type/user/view.html:67 msgid "Edit" msgstr "" -#: account.html:21 +#: account.html:24 msgid "Your Profile Page" msgstr "" -#: account.html:22 +#: account.html:25 msgid "View or Edit your Reading Log" msgstr "" -#: account.html:23 +#: account.html:26 msgid "View or Edit your Lists" msgstr "" -#: account.html:24 +#: account.html:27 msgid "Import and Export Options" msgstr "" -#: account.html:25 -msgid "Manage Privacy Settings" +#: account.html:28 +msgid "Manage Privacy & Content Moderation Settings" msgstr "" -#: account.html:26 +#: account.html:29 msgid "Manage Notifications Settings" msgstr "" -#: account.html:27 +#: account.html:30 msgid "Manage Mailing List Subscriptions" msgstr "" -#: account.html:28 +#: account.html:31 msgid "Change Password" msgstr "" -#: account.html:29 +#: account.html:32 msgid "Update Email Address" msgstr "" -#: account.html:30 +#: account.html:33 msgid "Deactivate Account" msgstr "" -#: account.html:32 +#: account.html:35 msgid "Please contact us if you need help with anything else." msgstr "" @@ -90,34 +90,21 @@ msgstr "" msgid "Barcode Scanner (Beta)" msgstr "" -#: barcodescanner.html:15 -msgid "Point your camera at a barcode! 📷" +#: barcodescanner.html:20 lib/nav_head.html:98 +msgid "Advanced" msgstr "" -#: account/loans.html:59 -#, python-format -msgid "%(count)d Current Loan" -msgid_plural "%(count)d Current Loans" -msgstr[0] "" -msgstr[1] "" - -#: account/loans.html:65 admin/loans_table.html:41 borrow_admin.html:106 -msgid "Loan Expires" +#: barcodescanner.html:24 +msgid "Read Text ISBN" msgstr "" -#: borrow_admin.html:168 -#, python-format -msgid "%d person waiting" -msgid_plural "%d people waiting" -msgstr[0] "" -msgstr[1] "" +#: barcodescanner.html:26 +msgid "For books that print the ISBN without a barcode" +msgstr "" -#: ManageWaitlistButton.html:11 borrow_admin.html:186 -#, python-format -msgid "Waiting for %d day" -msgid_plural "Waiting for %d days" -msgstr[0] "" -msgstr[1] "" +#: barcodescanner.html:31 +msgid "Point your camera at a barcode! 📷" +msgstr "" #: diff.html:7 #, python-format @@ -145,29 +132,29 @@ msgstr "" msgid "Revision %d" msgstr "" -#: books/check.html:32 books/edit/edition.html:76 books/show.html:16 -#: covers/book_cover.html:41 covers/book_cover_single_edition.html:36 -#: covers/book_cover_work.html:32 diff.html:42 lists/preview.html:20 +#: books/check.html:32 books/show.html:16 covers/book_cover.html:56 +#: covers/book_cover_single_edition.html:36 covers/book_cover_work.html:32 +#: diff.html:43 jsdef/LazyWorkPreview.html:25 lists/preview.html:21 msgid "by" msgstr "" -#: diff.html:44 +#: diff.html:45 diff.html:47 msgid "by Anonymous" msgstr "" -#: diff.html:110 +#: diff.html:113 msgid "Differences" msgstr "" -#: diff.html:168 +#: diff.html:171 msgid "Both the revisions are identical." msgstr "" -#: edit_yaml.html:14 lib/edit_head.html:17 +#: edit_yaml.html:14 lib/edit_head.html:9 msgid "You're in edit mode." msgstr "" -#: edit_yaml.html:15 lib/edit_head.html:18 +#: edit_yaml.html:15 lib/edit_head.html:10 msgid "Cancel, and go back." msgstr "" @@ -175,7 +162,7 @@ msgstr "" msgid "YAML Representation:" msgstr "" -#: BookPreview.html:11 books/edit/edition.html:669 editpage.html:15 +#: BookPreview.html:12 books/edit/edition.html:664 editpage.html:15 msgid "Preview" msgstr "" @@ -189,19 +176,19 @@ msgstr "" #: RecentChanges.html:17 RecentChangesAdmin.html:20 RecentChangesUsers.html:20 #: admin/ip/view.html:44 admin/people/edits.html:41 history.html:32 -#: recentchanges/render.html:30 recentchanges/updated_records.html:28 +#: recentchanges/render.html:31 recentchanges/updated_records.html:32 msgid "When" msgstr "" #: RecentChanges.html:19 admin/ip/view.html:46 admin/loans_table.html:46 #: admin/people/edits.html:43 admin/waitinglists.html:28 history.html:33 -#: recentchanges/render.html:32 +#: recentchanges/render.html:33 msgid "Who" msgstr "" #: RecentChanges.html:20 RecentChangesUsers.html:22 admin/ip/view.html:47 -#: admin/people/edits.html:44 history.html:34 recentchanges/render.html:33 -#: recentchanges/updated_records.html:30 +#: admin/people/edits.html:44 history.html:34 recentchanges/render.html:34 +#: recentchanges/updated_records.html:34 msgid "Comment" msgstr "" @@ -226,7 +213,7 @@ msgstr "" msgid "Back" msgstr "" -#: Pager.html:34 history.html:87 lib/pagination.html:37 showmarc.html:54 +#: Pager.html:38 history.html:87 lib/pagination.html:37 showmarc.html:54 msgid "Next" msgstr "" @@ -245,7 +232,7 @@ msgstr "" msgid "Library Explorer" msgstr "" -#: lib/header_dropdown.html:59 lib/nav_head.html:118 login.html:10 +#: lib/header_dropdown.html:59 lib/nav_head.html:128 login.html:10 #: login.html:75 msgid "Log In" msgstr "" @@ -268,9 +255,8 @@ msgstr "" #: account/create.html:51 login.html:22 msgid "" "If you'd like to create a new, different Open Library account, you'll " -"need to log out and start the " -"signup process afresh." +"need to log out and start the signup process afresh." msgstr "" #: login.html:42 @@ -364,6 +350,16 @@ msgstr "" msgid "You may log in if you have the required permissions." msgstr "" +#: recaptcha.html:10 +msgid "" +"Please satisfy the reCAPTCHA below. If you have security settings or " +"privacy blockers installed, please disable them to see the reCAPTCHA." +msgstr "" + +#: recaptcha.html:15 +msgid "Incorrect. Please try again." +msgstr "" + #: showamazon.html:8 showamazon.html:11 showbwb.html:8 showbwb.html:11 msgid "Record details of" msgstr "" @@ -376,62 +372,66 @@ msgstr "" msgid "Invalid MARC record." msgstr "" -#: status.html:17 +#: status.html:30 msgid "Server status" msgstr "" #: SearchNavigation.html:24 SubjectTags.html:23 lib/nav_foot.html:28 #: lib/nav_head.html:28 subjects.html:9 subjects/notfound.html:11 -#: type/author/view.html:176 type/list/view_body.html:280 work_search.html:75 +#: type/author/view.html:178 type/list/view_body.html:196 work_search.html:51 msgid "Subjects" msgstr "" -#: SubjectTags.html:27 subjects.html:9 type/author/view.html:177 -#: type/list/view_body.html:282 work_search.html:79 +#: SubjectTags.html:27 subjects.html:9 type/author/view.html:179 +#: type/list/view_body.html:198 work_search.html:55 msgid "Places" msgstr "" #: SubjectTags.html:25 admin/menu.html:20 subjects.html:9 -#: type/author/view.html:178 type/list/view_body.html:281 work_search.html:78 +#: type/author/view.html:180 type/list/view_body.html:197 work_search.html:54 msgid "People" msgstr "" -#: SubjectTags.html:29 subjects.html:9 type/list/view_body.html:283 -#: work_search.html:80 +#: SubjectTags.html:29 subjects.html:9 type/list/view_body.html:199 +#: work_search.html:56 msgid "Times" msgstr "" -#: subjects.html:19 +#: subjects.html:24 +msgid "Edit Subject Tag" +msgstr "" + +#: subjects.html:32 msgid "See all works" msgstr "" -#: merge/authors.html:89 publishers/view.html:13 subjects.html:15 -#: type/author/view.html:117 +#: merge/authors.html:102 publishers/view.html:17 subjects.html:32 +#: type/author/view.html:121 #, python-format msgid "%(count)d work" msgid_plural "%(count)d works" msgstr[0] "" msgstr[1] "" -#: subjects.html:22 +#: subjects.html:39 #, python-format msgid "Search for books with subject %(name)s." msgstr "" -#: authors/index.html:21 lib/nav_head.html:102 lists/home.html:25 +#: authors/index.html:21 lib/nav_head.html:103 lists/home.html:25 #: publishers/notfound.html:19 publishers/view.html:115 -#: search/advancedsearch.html:48 search/authors.html:27 search/inside.html:17 +#: search/advancedsearch.html:48 search/authors.html:28 search/inside.html:17 #: search/lists.html:17 search/publishers.html:19 search/subjects.html:28 -#: subjects.html:27 subjects/notfound.html:19 type/local_id/view.html:42 -#: work_search.html:91 +#: subjects.html:44 subjects/notfound.html:19 type/local_id/view.html:42 +#: work_search.html:67 msgid "Search" msgstr "" -#: publishers/view.html:32 subjects.html:37 +#: publishers/view.html:32 subjects.html:54 msgid "Publishing History" msgstr "" -#: subjects.html:39 +#: subjects.html:56 msgid "" "This is a chart to show the publishing history of editions of works about" " this subject. Along the X axis is time, and on the y axis is the count " @@ -439,65 +439,65 @@ msgid "" " chart." msgstr "" -#: publishers/view.html:34 subjects.html:40 +#: publishers/view.html:34 subjects.html:57 msgid "Reset chart" msgstr "" -#: publishers/view.html:34 subjects.html:40 +#: publishers/view.html:34 subjects.html:57 msgid "or continue zooming in." msgstr "" -#: subjects.html:41 +#: subjects.html:58 msgid "This graph charts editions published on this subject." msgstr "" -#: publishers/view.html:42 subjects.html:47 +#: publishers/view.html:42 subjects.html:64 msgid "Editions Published" msgstr "" -#: admin/imports.html:18 publishers/view.html:44 subjects.html:49 +#: admin/imports.html:25 publishers/view.html:44 subjects.html:66 msgid "You need to have JavaScript turned on to see the nifty chart!" msgstr "" -#: publishers/view.html:46 subjects.html:51 +#: publishers/view.html:46 subjects.html:68 msgid "Year of Publication" msgstr "" -#: subjects.html:57 +#: subjects.html:74 msgid "Related..." msgstr "" -#: publishers/view.html:64 subjects.html:71 +#: publishers/view.html:64 subjects.html:88 msgid "None found." msgstr "" -#: publishers/view.html:81 subjects.html:86 +#: publishers/view.html:81 subjects.html:103 msgid "See more books by, and learn about, this author" msgstr "" -#: account/loans.html:143 authors/index.html:24 publishers/view.html:79 -#: search/authors.html:56 search/publishers.html:29 search/subjects.html:66 -#: subjects.html:84 +#: account/loans.html:147 authors/index.html:28 publishers/view.html:83 +#: search/authors.html:60 search/publishers.html:29 search/subjects.html:73 +#: subjects.html:105 #, python-format msgid "%(count)d book" msgid_plural "%(count)d books" msgstr[0] "" msgstr[1] "" -#: subjects.html:92 +#: subjects.html:109 msgid "Prolific Authors" msgstr "" -#: subjects.html:93 +#: subjects.html:110 msgid "who have written the most books on this subject" msgstr "" -#: publishers/view.html:104 subjects.html:109 +#: publishers/view.html:104 subjects.html:126 msgid "Get more information about this publisher" msgstr "" -#: SearchResultsWork.html:82 books/check.html:32 merge/authors.html:82 -#: publishers/view.html:102 subjects.html:107 +#: SearchResultsWork.html:95 books/check.html:36 merge/authors.html:95 +#: publishers/view.html:106 subjects.html:128 #, python-format msgid "%(count)s edition" msgid_plural "%(count)s editions" @@ -594,17 +594,17 @@ msgstr "" msgid "Which page were you looking at?" msgstr "" -#: support.html:69 +#: support.html:67 msgid "Send" msgstr "" -#: EditButtons.html:23 EditButtonsMacros.html:26 ReadingLogDropper.html:167 -#: account/create.html:90 account/notifications.html:59 -#: account/password/reset.html:24 account/privacy.html:56 -#: admin/imports-add.html:28 books/add.html:108 books/edit/addfield.html:87 -#: books/edit/addfield.html:133 books/edit/addfield.html:177 covers/add.html:79 +#: CreateListModal.html:34 EditButtons.html:23 EditButtonsMacros.html:26 +#: account/create.html:86 account/notifications.html:59 +#: account/password/reset.html:24 account/privacy.html:79 +#: admin/imports-add.html:28 books/add.html:107 covers/add.html:81 #: covers/manage.html:52 databarAuthor.html:66 databarEdit.html:13 -#: merge/authors.html:109 support.html:71 +#: merge/authors.html:118 my_books/dropdown_content.html:114 support.html:69 +#: tag/add.html:66 msgid "Cancel" msgstr "" @@ -630,7 +630,8 @@ msgstr "" msgid "This Year" msgstr "" -#: trending.html:10 +#: account/view.html:38 books/year_breadcrumb_select.html:2 +#: books/year_breadcrumb_select.html:11 trending.html:10 msgid "All Time" msgstr "" @@ -642,15 +643,15 @@ msgstr "" msgid "See what readers from the community are adding to their bookshelves" msgstr "" -#: ReadingLogDropper.html:27 ReadingLogDropper.html:66 -#: ReadingLogDropper.html:189 account/mybooks.html:75 account/sidebar.html:26 -#: trending.html:23 +#: account/mybooks.html:70 account/sidebar.html:24 +#: my_books/dropdown_content.html:32 my_books/primary_action.html:16 +#: search/sort_options.html:36 trending.html:27 msgid "Want to Read" msgstr "" -#: ReadingLogDropper.html:25 ReadingLogDropper.html:74 account/books.html:33 -#: account/mybooks.html:74 account/readinglog_shelf_name.html:8 -#: account/sidebar.html:25 trending.html:23 +#: account/mybooks.html:69 account/readinglog_shelf_name.html:8 +#: account/sidebar.html:21 my_books/dropdown_content.html:40 +#: my_books/primary_action.html:14 search/sort_options.html:37 trending.html:27 msgid "Currently Reading" msgstr "" @@ -658,17 +659,17 @@ msgstr "" #: book_providers/gutenberg_read_button.html:15 #: book_providers/openstax_read_button.html:15 #: book_providers/standard_ebooks_read_button.html:15 -#: books/edit/edition.html:665 books/show.html:34 trending.html:23 -#: type/list/embed.html:69 widget.html:34 +#: books/custom_carousel.html:18 books/edit/edition.html:660 books/show.html:34 +#: trending.html:27 type/list/embed.html:70 widget.html:34 msgid "Read" msgstr "" -#: trending.html:24 +#: trending.html:28 #, python-format msgid "Someone marked as %(shelf)s %(k_hours_ago)s" msgstr "" -#: trending.html:26 +#: trending.html:30 #, python-format msgid "Logged %(count)i times %(time_unit)s" msgstr "" @@ -691,8 +692,8 @@ msgstr "" msgid "Borrow \"%(title)s\"" msgstr "" -#: ReadButton.html:15 books/edit/edition.html:668 type/list/embed.html:80 -#: widget.html:39 +#: ReadButton.html:15 books/custom_carousel.html:19 books/edit/edition.html:663 +#: type/list/embed.html:81 widget.html:39 msgid "Borrow" msgstr "" @@ -701,7 +702,7 @@ msgstr "" msgid "Join waitlist for "%(title)s"" msgstr "" -#: LoanStatus.html:112 widget.html:45 +#: LoanStatus.html:97 books/custom_carousel.html:20 widget.html:45 msgid "Join Waitlist" msgstr "" @@ -718,249 +719,180 @@ msgstr "" msgid "on " msgstr "" -#: work_search.html:68 +#: work_search.html:44 msgid "Search Books" msgstr "" -#: work_search.html:72 +#: work_search.html:48 msgid "eBook?" msgstr "" -#: type/edition/view.html:246 type/work/view.html:246 work_search.html:73 +#: type/edition/view.html:291 type/work/view.html:291 work_search.html:49 msgid "Language" msgstr "" -#: books/add.html:42 books/edit.html:92 books/edit/edition.html:245 -#: lib/nav_head.html:93 merge_queue/merge_queue.html:77 -#: search/advancedsearch.html:24 work_search.html:74 work_search.html:164 +#: books/add.html:42 books/edit.html:94 books/edit/edition.html:231 +#: lib/nav_head.html:94 search/advancedsearch.html:24 work_search.html:50 +#: work_search.html:138 msgid "Author" msgstr "" -#: work_search.html:76 +#: work_search.html:52 msgid "First published" msgstr "" -#: search/advancedsearch.html:44 type/edition/view.html:231 -#: type/work/view.html:231 work_search.html:77 +#: search/advancedsearch.html:44 type/edition/view.html:276 +#: type/work/view.html:276 work_search.html:53 msgid "Publisher" msgstr "" -#: work_search.html:81 +#: work_search.html:57 msgid "Classic eBooks" msgstr "" -#: work_search.html:89 +#: work_search.html:65 msgid "Keywords" msgstr "" -#: recentchanges/index.html:60 work_search.html:94 +#: recentchanges/index.html:61 work_search.html:70 msgid "Everything" msgstr "" -#: work_search.html:96 +#: work_search.html:72 msgid "Ebooks" msgstr "" -#: work_search.html:98 +#: work_search.html:74 msgid "Print Disabled" msgstr "" -#: work_search.html:101 +#: work_search.html:77 msgid "This is only visible to super librarians." msgstr "" -#: work_search.html:107 +#: work_search.html:83 msgid "Solr Editions Beta" msgstr "" -#: work_search.html:124 +#: work_search.html:100 msgid "eBook" msgstr "" -#: work_search.html:127 +#: work_search.html:103 msgid "Classic eBook" msgstr "" -#: work_search.html:146 +#: work_search.html:120 msgid "Explore Classic eBooks" msgstr "" -#: work_search.html:146 +#: work_search.html:120 msgid "Only Classic eBooks" msgstr "" -#: work_search.html:150 work_search.html:152 work_search.html:154 -#: work_search.html:156 +#: work_search.html:124 work_search.html:126 work_search.html:128 +#: work_search.html:130 #, python-format msgid "Explore books about %(subject)s" msgstr "" -#: merge/authors.html:88 work_search.html:158 +#: merge/authors.html:97 work_search.html:132 msgid "First published in" msgstr "" -#: work_search.html:160 +#: work_search.html:134 msgid "Written in" msgstr "" -#: work_search.html:162 +#: work_search.html:136 msgid "Published by" msgstr "" -#: work_search.html:165 +#: work_search.html:139 msgid "Click to remove this facet" msgstr "" -#: work_search.html:167 +#: work_search.html:141 #, python-format msgid "%(title)s - search" msgstr "" -#: search/lists.html:29 work_search.html:181 +#: search/lists.html:29 work_search.html:154 msgid "No results found." msgstr "" -#: search/lists.html:30 work_search.html:184 +#: search/lists.html:30 work_search.html:157 #, python-format msgid "Search for books containing the phrase \"%s\"?" msgstr "" -#: work_search.html:188 +#: work_search.html:161 msgid "Add a new book to Open Library?" msgstr "" -#: account/reading_log.html:36 search/authors.html:41 search/subjects.html:31 -#: work_search.html:190 +#: account/reading_log.html:49 search/authors.html:45 search/subjects.html:35 +#: work_search.html:167 #, python-format msgid "%(count)s hit" msgid_plural "%(count)s hits" msgstr[0] "" msgstr[1] "" -#: work_search.html:225 +#: work_search.html:199 msgid "Zoom In" msgstr "" -#: work_search.html:226 +#: work_search.html:200 msgid "Focus your results using these" msgstr "" -#: work_search.html:226 +#: work_search.html:200 msgid "filters" msgstr "" -#: search/facet_section.html:20 work_search.html:238 +#: work_search.html:212 msgid "Merge duplicate authors from this search" msgstr "" -#: search/facet_section.html:20 type/work/editions.html:17 work_search.html:238 +#: type/work/editions.html:17 work_search.html:212 msgid "Merge duplicates" msgstr "" -#: search/facet_section.html:32 work_search.html:250 +#: work_search.html:224 msgid "yes" msgstr "" -#: search/facet_section.html:34 work_search.html:252 +#: work_search.html:226 msgid "no" msgstr "" -#: search/facet_section.html:35 work_search.html:253 +#: work_search.html:227 msgid "Filter results for ebook availability" msgstr "" -#: search/facet_section.html:37 work_search.html:255 +#: work_search.html:229 #, python-format msgid "Filter results for %(facet)s" msgstr "" -#: search/facet_section.html:42 work_search.html:260 +#: work_search.html:234 msgid "more" msgstr "" -#: search/facet_section.html:46 work_search.html:264 +#: work_search.html:238 msgid "less" msgstr "" -#: account/books.html:27 account/sidebar.html:24 type/user/view.html:50 -#: type/user/view.html:55 -msgid "Reading Log" -msgstr "" - -#: account/books.html:31 lib/nav_head.html:13 lib/nav_head.html:25 -#: lib/nav_head.html:76 -msgid "My Books" -msgstr "" - -#: account/books.html:35 account/readinglog_shelf_name.html:10 -msgid "Want To Read" -msgstr "" - -#: ReadingLogDropper.html:23 ReadingLogDropper.html:82 account/books.html:37 -#: account/mybooks.html:76 account/readinglog_shelf_name.html:12 -#: account/sidebar.html:27 -msgid "Already Read" -msgstr "" - -#: account/books.html:40 account/sidebar.html:38 -msgid "Sponsorships" -msgstr "" - -#: account/books.html:42 -msgid "Book Notes" -msgstr "" - -#: EditionNavBar.html:26 account/books.html:44 account/observations.html:33 -msgid "Reviews" -msgstr "" - -#: account/books.html:46 account/sidebar.html:19 admin/menu.html:22 -#: admin/people/view.html:233 type/user/view.html:29 -msgid "Loans" -msgstr "" - -#: account/books.html:48 -msgid "Imports and Exports" -msgstr "" - -#: account/books.html:50 -msgid "My Lists" -msgstr "" - -#: account/books.html:79 -msgid "View stats about this shelf" -msgstr "" - -#: account/books.html:79 account/readinglog_stats.html:93 admin/index.html:19 -msgid "Stats" -msgstr "" - -#: account/books.html:85 -msgid "Your book notes are private and cannot be viewed by other patrons." -msgstr "" - -#: account/books.html:87 -msgid "Your book reviews will be shared anonymously with other patrons." -msgstr "" - -#: account/books.html:90 -msgid "Your reading log is currently set to public" -msgstr "" - -#: account/books.html:93 -msgid "Your reading log is currently set to private" -msgstr "" - -#: account/books.html:95 type/user/view.html:57 -msgid "Manage your privacy settings" +#: about/index.html:6 +msgid "The Open Library Team" msgstr "" #: account/create.html:9 msgid "Sign Up to Open Library" msgstr "" -#: account/create.html:12 account/create.html:89 lib/header_dropdown.html:60 -#: lib/nav_head.html:119 +#: account/create.html:12 account/create.html:85 lib/header_dropdown.html:60 +#: lib/nav_head.html:129 msgid "Sign Up" msgstr "" @@ -980,17 +912,7 @@ msgstr "" msgid "screenname" msgstr "" -#: account/create.html:77 -msgid "" -"If you have security settings or privacy blockers installed, please " -"disable them to see the reCAPTCHA." -msgstr "" - -#: account/create.html:81 -msgid "Incorrect. Please try again." -msgstr "" - -#: account/create.html:85 +#: account/create.html:75 msgid "" "By signing up, you agree to the Internet Archive's Terms of " @@ -1086,14 +1008,25 @@ msgstr "" msgid "You've not checked out any books at this moment." msgstr "" +#: account/loans.html:63 +#, python-format +msgid "%(count)d Current Loan" +msgid_plural "%(count)d Current Loans" +msgstr[0] "" +msgstr[1] "" + +#: account/loans.html:65 admin/loans_table.html:41 +msgid "Loan Expires" +msgstr "" + #: account/loans.html:66 msgid "Loan actions" msgstr "" -#: ManageLoansButtons.html:13 account/loans.html:93 +#: account/loans.html:93 #, python-format -msgid "There is one person waiting for this book." -msgid_plural "There are %(n)d people waiting for this book." +msgid "There is %(count)d person waiting for this book." +msgid_plural "There are %(count)d people waiting for this book." msgstr[0] "" msgstr[1] "" @@ -1128,7 +1061,6 @@ msgid "" msgstr "" #: account/loans.html:149 admin/loans_table.html:43 admin/menu.html:33 -#: merge_queue/merge_queue.html:59 msgid "Status" msgstr "" @@ -1173,29 +1105,64 @@ msgstr "" msgid "Leave the waiting list?" msgstr "" -#: account/loans.html:218 home/index.html:34 +#: account/loans.html:218 home/index.html:31 msgid "Books We Love" msgstr "" -#: account/mybooks.html:19 account/sidebar.html:33 +#: account/mybooks.html:29 +#, python-format +msgid "Set %(year_span)s reading goal" +msgstr "" + +#: account/mybooks.html:63 +msgid "No books are on this shelf" +msgstr "" + +#: account/mybooks.html:68 account/sidebar.html:13 +msgid "My Loans" +msgstr "" + +#: account/mybooks.html:71 account/readinglog_shelf_name.html:12 +#: account/sidebar.html:27 my_books/dropdown_content.html:48 +#: my_books/primary_action.html:12 mybooks.py:222 search/sort_options.html:38 +msgid "Already Read" +msgstr "" + +#: account/mybooks.html:81 type/user/view.html:62 +msgid "This reader has chosen to make their Reading Log private." +msgstr "" + +#: account/mybooks.html:132 account/sidebar.html:53 +msgid "Untitled list" +msgstr "" + +#: account/mybooks.html:161 +msgid "View All Lists" +msgstr "" + +#: account/mybooks.html:176 account/sidebar.html:34 account/topmenu.html:17 msgid "My Reading Stats" msgstr "" -#: account/mybooks.html:23 account/sidebar.html:34 -msgid "Import & Export Options" +#: account/mybooks.html:184 account/sidebar.html:15 +msgid "Loan History" msgstr "" -#: account/mybooks.html:35 -#, python-format -msgid "Set %(year_span)s reading goal" +#: account/mybooks.html:192 account/sidebar.html:31 +msgid "My Notes" msgstr "" -#: account/mybooks.html:69 -msgid "No books are on this shelf" +#: account/mybooks.html:201 account/sidebar.html:32 +msgid "My Reviews" msgstr "" -#: account/mybooks.html:73 -msgid "My Loans" +#: account/mybooks.html:210 account/sidebar.html:35 +msgid "Import & Export Options" +msgstr "" + +#: account/mybooks.html:219 account/sidebar.html:39 +#: books/mybooks_breadcrumb_select.html:25 mybooks.py:156 +msgid "Sponsorships" msgstr "" #: account/not_verified.html:7 account/not_verified.html:18 @@ -1207,7 +1174,7 @@ msgstr "" msgid "Resend the verification email" msgstr "" -#: BookByline.html:10 SearchResultsWork.html:68 account/notes.html:25 +#: BookByline.html:10 SearchResultsWork.html:77 account/notes.html:25 #: account/observations.html:26 msgid "Unknown author" msgstr "" @@ -1274,10 +1241,15 @@ msgid "Yes! Please!" msgstr "" #: EditButtons.html:21 EditButtonsMacros.html:24 account/notifications.html:57 -#: account/privacy.html:54 covers/manage.html:51 +#: account/privacy.html:77 covers/manage.html:51 msgid "Save" msgstr "" +#: EditionNavBar.html:28 account/observations.html:33 +#: books/mybooks_breadcrumb_select.html:21 mybooks.py:115 +msgid "Reviews" +msgstr "" + #: account/observations.html:43 msgid "Delete" msgstr "" @@ -1295,17 +1267,39 @@ msgid "Your Privacy on Open Library" msgstr "" #: account/privacy.html:16 -msgid "Privacy Settings" +msgid "Privacy & Content Moderation Settings" +msgstr "" + +#: account/privacy.html:33 +msgid "" +"Would you like to make your Reading Log " +"public?" +msgstr "" + +#: account/privacy.html:34 +msgid "" +"This will enable others to see what books you want to read, are reading, " +"or have already read." msgstr "" -#: account/privacy.html:39 +#: account/privacy.html:41 account/privacy.html:62 msgid "Yes" msgstr "" -#: account/privacy.html:46 books/edit/edition.html:306 +#: account/privacy.html:48 account/privacy.html:69 books/edit/edition.html:293 msgid "No" msgstr "" +#: account/privacy.html:54 +msgid "Enable Safe Mode?" +msgstr "" + +#: account/privacy.html:55 +msgid "" +"Safe Mode blurs book covers that have been flagged as having sensitive " +"imagery." +msgstr "" + #: account/reading_log.html:12 #, python-format msgid "Books %(username)s is reading" @@ -1330,163 +1324,204 @@ msgid "" "OpenLibrary.org and share the books that you'll soon be reading!" msgstr "" -#: account/reading_log.html:18 +#: account/reading_log.html:19 +#, python-format +msgid "Books %(username)s has read in %(year)d" +msgstr "" + +#: account/reading_log.html:20 +#, python-format +msgid "" +"%(username)s has read %(total)d books in %(year)d. Join %(username)s on " +"OpenLibrary.org and tell the world about the books that you care about." +msgstr "" + +#: account/reading_log.html:22 #, python-format msgid "Books %(username)s has read" msgstr "" -#: account/reading_log.html:19 +#: account/reading_log.html:23 #, python-format msgid "" "%(username)s has read %(total)d books. Join %(username)s on " "OpenLibrary.org and tell the world about the books that you care about." msgstr "" -#: account/reading_log.html:21 +#: account/reading_log.html:25 #, python-format msgid "Books %(userdisplayname)s is sponsoring" msgstr "" -#: account/reading_log.html:34 +#: account/reading_log.html:45 msgid "Search your reading log" msgstr "" -#: account/reading_log.html:42 search/sort_options.html:8 +#: account/reading_log.html:51 search/sort_options.html:8 msgid "Sorting by" msgstr "" -#: account/reading_log.html:44 account/reading_log.html:48 +#: account/reading_log.html:53 account/reading_log.html:57 msgid "Date Added (newest)" msgstr "" -#: account/reading_log.html:46 account/reading_log.html:50 +#: account/reading_log.html:55 account/reading_log.html:59 msgid "Date Added (oldest)" msgstr "" -#: account/reading_log.html:70 +#: account/reading_log.html:79 +msgid "You haven't marked any books as read for this year." +msgstr "" + +#: account/reading_log.html:81 msgid "You haven't added any books to this shelf yet." msgstr "" -#: account/reading_log.html:71 +#: account/reading_log.html:82 msgid "" "Search for a book to add to your reading log. Learn more about the reading log." msgstr "" -#: account/readinglog_stats.html:8 account/readinglog_stats.html:95 +#: account/readinglog_shelf_name.html:10 +msgid "Want To Read" +msgstr "" + +#: account/readinglog_stats.html:15 account/readinglog_stats.html:109 #, python-format -msgid "\"%(shelf_name)s\" Stats" +msgid "My %(shelf_name)s Stats" msgstr "" -#: account/readinglog_stats.html:97 +#: account/readinglog_stats.html:111 #, python-format msgid "" "Displaying stats about %d books. Note all charts show " "only the top 20 bars. Note reading log stats are private." msgstr "" -#: account/readinglog_stats.html:102 +#: account/readinglog_stats.html:116 msgid "Author Stats" msgstr "" -#: account/readinglog_stats.html:107 +#: account/readinglog_stats.html:121 msgid "Most Read Authors" msgstr "" -#: account/readinglog_stats.html:108 +#: account/readinglog_stats.html:122 msgid "Works by Author Sex" msgstr "" -#: account/readinglog_stats.html:109 -msgid "Works by Author Ethnicity" -msgstr "" - -#: account/readinglog_stats.html:110 +#: account/readinglog_stats.html:123 msgid "Works by Author Country of Citizenship" msgstr "" -#: account/readinglog_stats.html:111 +#: account/readinglog_stats.html:124 msgid "Works by Author Country of Birth" msgstr "" -#: account/readinglog_stats.html:121 +#: account/readinglog_stats.html:134 msgid "" "Demographic statistics powered by Wikidata. Here's a sample of the query used." msgstr "" -#: account/readinglog_stats.html:123 +#: account/readinglog_stats.html:136 msgid "Work Stats" msgstr "" -#: account/readinglog_stats.html:129 +#: account/readinglog_stats.html:142 msgid "Works by Subject" msgstr "" -#: account/readinglog_stats.html:130 +#: account/readinglog_stats.html:143 msgid "Works by People" msgstr "" -#: account/readinglog_stats.html:131 +#: account/readinglog_stats.html:144 msgid "Works by Places" msgstr "" -#: account/readinglog_stats.html:132 +#: account/readinglog_stats.html:145 msgid "Works by Time Period" msgstr "" -#: account/readinglog_stats.html:144 +#: account/readinglog_stats.html:157 msgid "Matching works" msgstr "" -#: account/readinglog_stats.html:148 +#: account/readinglog_stats.html:161 msgid "Click on a bar to see matching works" msgstr "" -#: account/sidebar.html:20 -msgid "Loan History" -msgstr "" - -#: account/sidebar.html:30 -msgid "My Notes" +#: account/sidebar.html:19 search/sort_options.html:31 type/user/view.html:50 +#: type/user/view.html:55 +msgid "Reading Log" msgstr "" -#: account/sidebar.html:31 -msgid "My Reviews" +#: account/sidebar.html:43 +msgid "Sponsoring" msgstr "" -#: account/sidebar.html:42 -msgid "Sponsoring" +#: account/sidebar.html:46 +msgid "My lists" msgstr "" -#: EditionNavBar.html:30 SearchNavigation.html:28 account/sidebar.html:45 -#: lib/nav_head.html:31 lib/nav_head.html:96 lists/home.html:7 -#: lists/home.html:10 lists/widget.html:67 recentchanges/index.html:41 -#: type/list/embed.html:28 type/list/view_body.html:48 type/user/view.html:35 -#: type/user/view.html:66 +#: EditionNavBar.html:32 SearchNavigation.html:28 account/sidebar.html:46 +#: lib/nav_head.html:31 lib/nav_head.html:97 lists/home.html:7 +#: lists/home.html:10 lists/widget.html:62 recentchanges/index.html:42 +#: type/edition/view.html:540 type/list/embed.html:29 +#: type/list/view_body.html:50 type/user/view.html:35 type/user/view.html:66 +#: type/work/view.html:540 msgid "Lists" msgstr "" -#: account/sidebar.html:45 +#: account/sidebar.html:46 type/edition/view.html:545 type/user/view.html:67 +#: type/work/view.html:545 msgid "See All" msgstr "" -#: account/sidebar.html:47 -msgid "Untitled list" +#: account/sidebar.html:49 type/list/edit.html:7 type/list/edit.html:17 +msgid "Create a list" +msgstr "" + +#: account/topmenu.html:21 +msgid "Import & Export" +msgstr "" + +#: account/topmenu.html:25 +#, python-format +msgid "Privacy settings: %(public)s" msgstr "" #: account/verify.html:7 msgid "Verification email sent" msgstr "" -#: account.py:419 account.py:457 account/password/reset_success.html:10 +#: account.py:430 account.py:484 account/password/reset_success.html:10 #: account/verify.html:9 account/verify/activated.html:10 #: account/verify/success.html:10 #, python-format msgid "Hi, %(user)s" msgstr "" +#: account/view.html:62 +msgid "Your book notes are private and cannot be viewed by other patrons." +msgstr "" + +#: account/view.html:64 +msgid "Your book reviews will be shared anonymously with other patrons." +msgstr "" + +#: account/yrg_banner.html:11 +#, python-format +msgid "Set your %(year)s Yearly Reading Goal:" +msgstr "" + +#: account/yrg_banner.html:12 +msgid "Set my goal" +msgstr "" + #: account/email/forgot-ia.html:10 account/email/forgot.html:10 msgid "Forgot Your Internet Archive Email?" msgstr "" @@ -1619,23 +1654,27 @@ msgstr "" #: admin/imports-add.html:26 admin/ip/view.html:95 admin/people/edits.html:92 #: check_ins/check_in_form.html:77 check_ins/reading_goal_form.html:16 -#: covers/add.html:78 +#: covers/add.html:78 covers/add.html:79 msgid "Submit" msgstr "" +#: admin/index.html:19 +msgid "Stats" +msgstr "" + #: admin/loans_table.html:17 msgid "BookReader" msgstr "" #: admin/loans_table.html:18 book_providers/ia_download_options.html:12 -#: book_providers/openstax_download_options.html:13 books/edit/edition.html:677 +#: book_providers/openstax_download_options.html:13 books/edit/edition.html:672 msgid "PDF" msgstr "" #: admin/loans_table.html:19 book_providers/gutenberg_download_options.html:17 #: book_providers/ia_download_options.html:16 #: book_providers/standard_ebooks_download_options.html:15 -#: books/edit/edition.html:676 +#: books/edit/edition.html:671 msgid "ePub" msgstr "" @@ -1648,7 +1687,7 @@ msgstr[1] "" #: RecentChanges.html:18 RecentChangesUsers.html:21 admin/ip/view.html:45 #: admin/loans_table.html:45 admin/people/edits.html:42 -#: recentchanges/render.html:31 recentchanges/updated_records.html:29 +#: recentchanges/render.html:32 recentchanges/updated_records.html:33 msgid "What" msgstr "" @@ -1660,6 +1699,11 @@ msgstr "" msgid "Sponsorship" msgstr "" +#: account.py:1064 admin/menu.html:22 admin/people/view.html:233 +#: books/mybooks_breadcrumb_select.html:19 type/user/view.html:29 +msgid "Loans" +msgstr "" + #: admin/menu.html:23 msgid "Waiting Lists" msgstr "" @@ -1708,7 +1752,7 @@ msgstr "" msgid "Update" msgstr "" -#: FormatExpiry.html:23 admin/waitinglists.html:29 +#: FormatExpiry.html:10 admin/waitinglists.html:29 msgid "Expires" msgstr "" @@ -1720,7 +1764,7 @@ msgstr "" msgid "List of Banned IPs" msgstr "" -#: admin/ip/index.html:22 admin/people/index.html:57 +#: admin/ip/index.html:22 admin/people/index.html:71 msgid "Date/Time" msgstr "" @@ -1743,13 +1787,13 @@ msgid "Please, leave a short note about what you changed:" msgstr "" #: RecentChanges.html:61 RecentChangesAdmin.html:56 RecentChangesUsers.html:58 -#: admin/people/edits.html:100 recentchanges/render.html:66 +#: admin/people/edits.html:100 recentchanges/render.html:67 msgid "Older" msgstr "" #: RecentChanges.html:64 RecentChangesAdmin.html:58 RecentChangesAdmin.html:60 #: RecentChangesUsers.html:61 admin/people/edits.html:103 -#: recentchanges/render.html:69 +#: recentchanges/render.html:70 msgid "Newer" msgstr "" @@ -1757,32 +1801,45 @@ msgstr "" msgid "Find Account" msgstr "" -#: admin/people/index.html:32 admin/people/view.html:151 +#: admin/people/index.html:33 admin/people/view.html:151 msgid "Email Address:" msgstr "" -#: admin/people/index.html:51 +#: admin/people/index.html:46 +msgid "IA ID:" +msgstr "" + +#: admin/people/index.html:50 +msgid "Find" +msgstr "" + +#: admin/people/index.html:53 +msgid "No account found with this ID." +msgstr "" + +#: admin/people/index.html:65 msgid "Recent Accounts" msgstr "" -#: admin/people/index.html:58 type/author/edit.html:32 +#: admin/people/index.html:72 type/author/edit.html:32 #: type/language/view.html:27 msgid "Name" msgstr "" -#: admin/people/index.html:59 +#: admin/people/index.html:73 msgid "email" msgstr "" -#: admin/people/index.html:60 +#: admin/people/index.html:74 msgid "Edits" msgstr "" -#: admin/people/index.html:75 admin/people/view.html:247 +#: admin/people/index.html:89 admin/people/view.html:247 msgid "Edit History" msgstr "" -#: ReadingLogDropper.html:149 admin/people/view.html:147 +#: CreateListModal.html:16 admin/people/view.html:147 +#: my_books/dropdown_content.html:96 msgid "Name:" msgstr "" @@ -1831,12 +1888,12 @@ msgstr "" msgid "Search for an Author" msgstr "" -#: authors/index.html:39 search/authors.html:69 +#: authors/index.html:39 search/authors.html:72 #, python-format msgid "about %(subjects)s" msgstr "" -#: authors/index.html:40 search/authors.html:70 +#: authors/index.html:40 search/authors.html:73 #, python-format msgid "including %(topwork)s" msgstr "" @@ -1900,19 +1957,19 @@ msgstr "" #: book_providers/librivox_read_button.html:25 #: book_providers/openstax_read_button.html:22 #: book_providers/standard_ebooks_read_button.html:22 +#: check_ins/reading_goal_progress.html:28 msgid "Learn more" msgstr "" -#: BookPreview.html:20 DonateModal.html:15 NotesModal.html:32 -#: ObservationsModal.html:32 ReadingLogDropper.html:144 ShareModal.html:25 +#: BookPreview.html:21 CreateListModal.html:11 DonateModal.html:15 +#: NotesModal.html:32 ObservationsModal.html:32 ShareModal.html:25 #: book_providers/gutenberg_read_button.html:24 #: book_providers/librivox_read_button.html:27 #: book_providers/openstax_read_button.html:24 #: book_providers/standard_ebooks_read_button.html:24 -#: covers/author_photo.html:22 covers/book_cover.html:39 +#: covers/author_photo.html:22 covers/book_cover.html:54 #: covers/book_cover_single_edition.html:34 covers/book_cover_work.html:30 -#: covers/change.html:44 lib/markdown.html:12 lists/lists.html:53 -#: merge_queue/merge_queue.html:121 +#: covers/change.html:44 lib/markdown.html:12 my_books/dropdown_content.html:91 msgid "Close" msgstr "" @@ -1940,7 +1997,7 @@ msgstr "" msgid "Download open DAISY from Internet Archive (print-disabled format)" msgstr "" -#: book_providers/ia_download_options.html:19 type/list/embed.html:85 +#: book_providers/ia_download_options.html:19 type/list/embed.html:86 msgid "DAISY" msgstr "" @@ -2081,30 +2138,36 @@ msgid "" "9783161484100" msgstr "" -#: books/add.html:17 -msgid "Add a book to Open Library" +#: books/add.html:13 +msgid "Invalid LC Control Number format" +msgstr "" + +#: books/add.html:14 +msgid "Please enter a value for the selected identifier" msgstr "" #: books/add.html:19 +msgid "Add a book to Open Library" +msgstr "" + +#: books/add.html:21 tag/add.html:13 msgid "" "We require a minimum set of fields to create a new record. These are " "those fields." msgstr "" -#: books/add.html:31 books/edit.html:86 books/edit/edition.html:96 -#: lib/nav_head.html:92 search/advancedsearch.html:20 type/about/edit.html:19 -#: type/i18n/edit.html:64 type/page/edit.html:20 type/rawtext/edit.html:18 -#: type/template/edit.html:18 +#: books/add.html:31 books/edit.html:88 books/edit/edition.html:75 +#: lib/nav_head.html:93 search/advancedsearch.html:20 type/about/edit.html:19 +#: type/page/edit.html:20 type/rawtext/edit.html:18 type/template/edit.html:18 msgid "Title" msgstr "" -#: books/add.html:31 books/edit.html:86 +#: books/add.html:31 books/edit.html:88 msgid "Use Title: Subtitle to add a subtitle." msgstr "" -#: books/add.html:31 books/add.html:48 books/add.html:57 books/edit.html:86 -#: books/edit/addfield.html:100 books/edit/addfield.html:145 -#: type/author/edit.html:35 +#: books/add.html:31 books/add.html:48 books/add.html:57 books/edit.html:88 +#: tag/add.html:24 type/author/edit.html:35 type/tag/edit.html:23 msgid "Required field" msgstr "" @@ -2114,19 +2177,19 @@ msgid "" "check to see if we already know the author." msgstr "" -#: books/add.html:48 books/edit/edition.html:119 +#: books/add.html:48 books/edit/edition.html:105 msgid "Who is the publisher?" msgstr "" -#: books/add.html:48 books/edit/edition.html:120 +#: books/add.html:48 books/edit/edition.html:106 msgid "For example" msgstr "" -#: books/add.html:57 books/edit/edition.html:139 +#: books/add.html:57 books/edit/edition.html:125 msgid "When was it published?" msgstr "" -#: books/add.html:57 books/edit/edition.html:140 +#: books/add.html:57 books/edit/edition.html:126 msgid "You should be able to find this in the first few pages of the book." msgstr "" @@ -2144,23 +2207,20 @@ msgstr "" msgid "ISBN may be invalid. Click \"Add\" again to submit." msgstr "" -#: books/add.html:72 +#: books/add.html:72 tag/add.html:47 msgid "Select" msgstr "" -#: books/add.html:87 books/edit/edition.html:652 +#: books/add.html:89 books/edit/edition.html:647 msgid "Book URL" msgstr "" -#: books/add.html:87 +#: books/add.html:89 msgid "Only fill this out if this is a web book" msgstr "" -#: books/add.html:96 -msgid "Since you are not logged in, please satisfy the reCAPTCHA below." -msgstr "" - -#: EditButtons.html:17 EditButtonsMacros.html:20 books/add.html:103 +#: EditButtons.html:17 EditButtonsMacros.html:20 books/add.html:102 +#: tag/add.html:61 msgid "" "By saving a change to this wiki, you agree that your contribution is " "given freely to the world under CC0. Yippee!" msgstr "" -#: books/add.html:106 +#: books/add.html:105 msgid "Add this book now" msgstr "" -#: books/add.html:106 books/edit/addfield.html:85 books/edit/addfield.html:131 -#: books/edit/addfield.html:175 books/edit/edition.html:227 -#: books/edit/edition.html:456 books/edit/edition.html:521 -#: books/edit/excerpts.html:50 covers/change.html:49 +#: books/add.html:105 books/edit/edition.html:213 books/edit/edition.html:451 +#: books/edit/edition.html:516 books/edit/excerpts.html:50 +#: covers/change.html:49 tag/add.html:64 msgid "Add" msgstr "" -#: books/author-autocomplete.html:13 +#: books/author-autocomplete.html:16 msgid "Create a new record for" msgstr "" -#: books/author-autocomplete.html:42 +#: books/author-autocomplete.html:27 msgid "Remove this author" msgstr "" -#: books/author-autocomplete.html:43 +#: books/author-autocomplete.html:34 msgid "Add another author?" msgstr "" -#: books/check.html:13 lib/nav_foot.html:41 lib/nav_head.html:38 +#: books/check.html:13 lib/nav_foot.html:49 lib/nav_head.html:39 msgid "Add a Book" msgstr "" @@ -2218,7 +2277,15 @@ msgstr "" msgid "First published in %(year)d" msgstr "" -#: books/custom_carousel.html:41 +#: NotInLibrary.html:13 NotInLibrary.html:17 books/custom_carousel.html:21 +msgid "Not in Library" +msgstr "" + +#: books/custom_carousel.html:22 +msgid "Loading..." +msgstr "" + +#: books/custom_carousel.html:60 books/mobile_carousel.html:41 #, python-format msgid " by %(name)s" msgstr "" @@ -2257,41 +2324,42 @@ msgstr "" msgid "Delete Record" msgstr "" -#: books/edit.html:65 +#: books/edit.html:67 msgid "Work Details" msgstr "" -#: books/edit.html:70 +#: books/edit.html:72 msgid "Unknown publisher edition" msgstr "" -#: books/edit.html:80 +#: books/edit.html:82 msgid "This Work" msgstr "" -#: books/edit.html:93 +#: books/edit.html:95 msgid "" "You can search by author name (like j k rowling) or by Open " "Library ID (like OL23919A)." msgstr "" -#: books/edit.html:98 +#: books/edit.html:100 msgid "Author editing requires javascript" msgstr "" -#: books/edit.html:111 +#: books/edit.html:113 msgid "Add Excerpts" msgstr "" -#: books/edit.html:117 type/about/edit.html:39 type/author/edit.html:50 +#: books/edit.html:119 type/about/edit.html:39 type/author/edit.html:50 msgid "Links" msgstr "" -#: SearchResultsWork.html:45 SearchResultsWork.html:46 -#: books/edit/edition.html:66 books/edition-sort.html:39 -#: books/edition-sort.html:40 lists/list_overview.html:11 lists/preview.html:9 -#: lists/snippet.html:9 lists/widget.html:83 type/work/editions.html:27 +#: SearchResultsWork.html:54 SearchResultsWork.html:55 +#: books/edition-sort.html:39 books/edition-sort.html:40 +#: jsdef/LazyWorkPreview.html:13 lists/list_overview.html:13 +#: lists/preview.html:9 lists/snippet.html:9 lists/widget.html:78 +#: my_books/dropdown_content.html:126 type/work/editions.html:27 #, python-format msgid "Cover of: %(title)s" msgstr "" @@ -2311,441 +2379,447 @@ msgstr "" msgid "in %(languagelist)s" msgstr "" -#: books/edition-sort.html:99 +#: books/edition-sort.html:100 msgid "Libraries near you:" msgstr "" -#: books/edit/about.html:21 -msgid "Select this subject" +#: SearchNavigation.html:12 books/mybooks_breadcrumb_select.html:9 +#: lib/nav_foot.html:26 mybooks.py:39 +msgid "Books" msgstr "" -#: books/edit/about.html:28 -msgid "How would you describe this book?" +#: books/mybooks_breadcrumb_select.html:12 mybooks.py:234 +#, python-format +msgid "Want to Read (%(count)d)" msgstr "" -#: books/edit/about.html:29 -msgid "There's no wrong answer here." +#: books/mybooks_breadcrumb_select.html:13 mybooks.py:231 +#, python-format +msgid "Currently Reading (%(count)d)" msgstr "" -#: books/edit/about.html:38 -msgid "Subject keywords?" +#: books/mybooks_breadcrumb_select.html:14 mybooks.py:237 +#, python-format +msgid "Already Read (%(count)d)" msgstr "" -#: books/edit/about.html:39 -msgid "Please separate with commas." +#: books/mybooks_breadcrumb_select.html:16 +#, python-format +msgid "Lists (%(count)d)" msgstr "" -#: books/edit/about.html:39 books/edit/about.html:50 books/edit/about.html:61 -#: books/edit/about.html:72 books/edit/addfield.html:77 -#: books/edit/addfield.html:104 books/edit/addfield.html:113 -#: books/edit/addfield.html:149 books/edit/edition.html:107 -#: books/edit/edition.html:130 books/edit/edition.html:163 -#: books/edit/edition.html:173 books/edit/edition.html:266 -#: books/edit/edition.html:368 books/edit/edition.html:382 -#: books/edit/edition.html:582 books/edit/excerpts.html:19 -#: books/edit/web.html:23 type/author/edit.html:113 -msgid "For example:" -msgstr "" - -#: books/edit/about.html:49 -msgid "A person? Or, people?" +#: books/mybooks_breadcrumb_select.html:20 mybooks.py:100 +#: type/edition/modal_links.html:30 +msgid "Notes" msgstr "" -#: books/edit/about.html:60 -msgid "Note any places mentioned?" +#: account.py:820 books/mybooks_breadcrumb_select.html:22 +msgid "Imports and Exports" msgstr "" -#: books/edit/about.html:71 -msgid "When is it set or about?" +#: books/edit/about.html:20 +msgid "Select this subject" msgstr "" -#: books/edit/addfield.html:70 -msgid "Add a New Contributor Role" +#: books/edit/about.html:27 +msgid "How would you describe this book?" msgstr "" -#: books/edit/addfield.html:77 books/edit/addfield.html:104 -#: books/edit/addfield.html:149 -msgid "What should we show in the menu?" +#: books/edit/about.html:28 tag/add.html:35 +msgid "There's no wrong answer here." msgstr "" -#: books/edit/addfield.html:96 -msgid "Add a New Type of Identifier" +#: books/edit/about.html:37 +msgid "Subject keywords?" msgstr "" -#: books/edit/addfield.html:113 -msgid "If the system has a website, what's the homepage?" +#: books/edit/about.html:38 +msgid "Please separate with commas." msgstr "" -#: books/edit/addfield.html:122 -msgid "Please leave a note: Why is this ID useful?" +#: books/edit/about.html:38 books/edit/about.html:49 books/edit/about.html:60 +#: books/edit/about.html:71 books/edit/edition.html:93 +#: books/edit/edition.html:116 books/edit/edition.html:149 +#: books/edit/edition.html:159 books/edit/edition.html:252 +#: books/edit/edition.html:355 books/edit/edition.html:369 +#: books/edit/edition.html:577 books/edit/excerpts.html:19 +#: books/edit/web.html:23 type/author/edit.html:113 +msgid "For example:" msgstr "" -#: books/edit/addfield.html:141 -msgid "Add a New Classification System" +#: books/edit/about.html:48 +msgid "A person? Or, people?" msgstr "" -#: books/edit/addfield.html:158 -msgid "Please leave a note: Why is this classification useful?" +#: books/edit/about.html:59 +msgid "Note any places mentioned?" msgstr "" -#: books/edit/addfield.html:167 -msgid "" -"Can you direct us to more information about this classification system " -"online?" +#: books/edit/about.html:70 +msgid "When is it set or about?" msgstr "" #: books/edit/edition.html:22 msgid "Select this language" msgstr "" -#: books/edit/edition.html:32 books/edit/edition.html:41 -msgid "Remove this language" -msgstr "" - #: books/edit/edition.html:33 -msgid "Add another language?" +msgid "Remove this language" msgstr "" -#: books/edit/edition.html:52 +#: books/edit/edition.html:50 msgid "Remove this work" msgstr "" -#: books/edit/edition.html:60 books/edit/edition.html:401 +#: books/edit/edition.html:59 books/edit/edition.html:388 msgid "-- Move to a new work" msgstr "" -#: books/edit/edition.html:87 +#: books/edit/edition.html:66 msgid "This Edition" msgstr "" -#: books/edit/edition.html:97 +#: books/edit/edition.html:76 msgid "Enter the title of this specific edition." msgstr "" -#: books/edit/edition.html:106 +#: books/edit/edition.html:92 msgid "Subtitle" msgstr "" -#: books/edit/edition.html:107 +#: books/edit/edition.html:93 msgid "Usually distinguished by different or smaller type." msgstr "" -#: books/edit/edition.html:114 +#: books/edit/edition.html:100 msgid "Publishing Info" msgstr "" -#: books/edit/edition.html:129 +#: books/edit/edition.html:115 msgid "Where was the book published?" msgstr "" -#: books/edit/edition.html:130 +#: books/edit/edition.html:116 msgid "City, Country please." msgstr "" -#: books/edit/edition.html:152 +#: books/edit/edition.html:138 msgid "What is the copyright date?" msgstr "" -#: books/edit/edition.html:153 +#: books/edit/edition.html:139 msgid "The year following the copyright statement." msgstr "" -#: books/edit/edition.html:162 +#: books/edit/edition.html:148 msgid "Edition Name (if applicable):" msgstr "" -#: books/edit/edition.html:172 +#: books/edit/edition.html:158 msgid "Series Name (if applicable)" msgstr "" -#: books/edit/edition.html:173 +#: books/edit/edition.html:159 msgid "The name of the publisher's series." msgstr "" -#: books/edit/edition.html:201 type/edition/view.html:417 -#: type/work/view.html:417 +#: books/edit/edition.html:187 type/edition/view.html:437 +#: type/work/view.html:437 msgid "Contributors" msgstr "" -#: books/edit/edition.html:203 +#: books/edit/edition.html:189 msgid "Please select a role." msgstr "" -#: books/edit/edition.html:203 +#: books/edit/edition.html:189 msgid "You need to give this ROLE a name." msgstr "" -#: books/edit/edition.html:205 +#: books/edit/edition.html:191 msgid "List the people involved" msgstr "" -#: books/edit/edition.html:215 +#: books/edit/edition.html:201 msgid "Select role" msgstr "" -#: books/edit/edition.html:220 +#: books/edit/edition.html:206 msgid "Add a new role" msgstr "" -#: books/edit/edition.html:240 books/edit/edition.html:256 +#: books/edit/edition.html:226 books/edit/edition.html:242 msgid "Remove this contributor" msgstr "" -#: books/edit/edition.html:265 +#: books/edit/edition.html:251 msgid "By Statement:" msgstr "" -#: books/edit/edition.html:276 +#: books/edit/edition.html:262 msgid "Languages" msgstr "" -#: books/edit/edition.html:280 +#: books/edit/edition.html:266 msgid "What language is this edition written in?" msgstr "" -#: books/edit/edition.html:292 +#: books/edit/edition.html:274 +msgid "Add another language?" +msgstr "" + +#: books/edit/edition.html:279 msgid "Is it a translation of another book?" msgstr "" -#: books/edit/edition.html:310 +#: books/edit/edition.html:297 msgid "Yes, it's a translation" msgstr "" -#: books/edit/edition.html:315 +#: books/edit/edition.html:302 msgid "What's the original book?" msgstr "" -#: books/edit/edition.html:324 +#: books/edit/edition.html:311 msgid "What language was the original written in?" msgstr "" -#: books/edit/edition.html:342 +#: books/edit/edition.html:329 msgid "Description of this edition" msgstr "" -#: books/edit/edition.html:343 +#: books/edit/edition.html:330 msgid "Only if it's different from the work's description" msgstr "" -#: TableOfContents.html:10 books/edit/edition.html:352 +#: books/edit/edition.html:339 type/edition/view.html:383 +#: type/work/view.html:383 msgid "Table of Contents" msgstr "" -#: books/edit/edition.html:353 +#: books/edit/edition.html:340 msgid "" "Use a \"*\" for an indent, a \"|\" to add a column, and line breaks for " "new lines. Like this:" msgstr "" -#: books/edit/edition.html:367 +#: books/edit/edition.html:354 msgid "Any notes about this specific edition?" msgstr "" -#: books/edit/edition.html:368 +#: books/edit/edition.html:355 msgid "Anything about the book that may be of interest." msgstr "" -#: books/edit/edition.html:377 +#: books/edit/edition.html:364 msgid "Is it known by any other titles?" msgstr "" -#: books/edit/edition.html:378 +#: books/edit/edition.html:365 msgid "" "Use this field if the title is different on the cover or spine. If the " "edition is a collection or anthology, you may also add the individual " "titles of each work here." msgstr "" -#: books/edit/edition.html:382 +#: books/edit/edition.html:369 msgid "is an alternate title for" msgstr "" -#: books/edit/edition.html:392 +#: books/edit/edition.html:379 msgid "What work is this an edition of?" msgstr "" -#: books/edit/edition.html:394 +#: books/edit/edition.html:381 msgid "" "Sometimes editions can be associated with the wrong work. You can correct" " that here." msgstr "" -#: books/edit/edition.html:395 +#: books/edit/edition.html:382 msgid "You can search by title or by Open Library ID (like" msgstr "" -#: books/edit/edition.html:398 +#: books/edit/edition.html:385 msgid "WARNING: Any edits made to the work from this page will be ignored." msgstr "" -#: books/edit/edition.html:414 +#: books/edit/edition.html:402 msgid "Please select an identifier." msgstr "" -#: books/edit/edition.html:414 +#: books/edit/edition.html:403 msgid "You need to give a value to ID." msgstr "" -#: books/edit/edition.html:414 +#: books/edit/edition.html:404 msgid "ID ids cannot contain whitespace." msgstr "" -#: books/edit/edition.html:414 +#: books/edit/edition.html:405 msgid "ID must be exactly 10 characters [0-9] or X." msgstr "" -#: books/edit/edition.html:414 -msgid "That ISBN already exists for this edition." +#: books/edit/edition.html:406 +msgid "That ID already exists for this edition." msgstr "" -#: books/edit/edition.html:414 +#: books/edit/edition.html:407 msgid "ID must be exactly 13 digits [0-9]. For example: 978-1-56619-909-4" msgstr "" -#: books/edit/edition.html:416 type/author/view.html:188 -#: type/edition/view.html:438 type/work/view.html:438 +#: books/edit/edition.html:408 +msgid "Invalid ID format" +msgstr "" + +#: books/edit/edition.html:411 type/author/view.html:190 +#: type/edition/view.html:458 type/work/view.html:458 msgid "ID Numbers" msgstr "" -#: books/edit/edition.html:422 +#: books/edit/edition.html:417 msgid "Do you know any identifiers for this edition?" msgstr "" -#: books/edit/edition.html:423 +#: books/edit/edition.html:418 msgid "Like, ISBN?" msgstr "" -#: books/edit/edition.html:441 books/edit/edition.html:509 +#: books/edit/edition.html:436 books/edit/edition.html:504 msgid "Select one of many..." msgstr "" -#: books/edit/edition.html:493 +#: books/edit/edition.html:488 msgid "Please select a classification." msgstr "" -#: books/edit/edition.html:493 +#: books/edit/edition.html:488 msgid "You need to give a value to CLASS." msgstr "" -#: books/edit/edition.html:494 type/edition/view.html:392 -#: type/work/view.html:392 +#: books/edit/edition.html:489 type/edition/view.html:412 +#: type/work/view.html:412 msgid "Classifications" msgstr "" -#: books/edit/edition.html:500 +#: books/edit/edition.html:495 msgid "Do you know any classifications of this edition?" msgstr "" -#: books/edit/edition.html:501 +#: books/edit/edition.html:496 msgid "Like, Dewey Decimal?" msgstr "" -#: books/edit/edition.html:514 +#: books/edit/edition.html:509 msgid "Add a new classification type" msgstr "" -#: books/edit/edition.html:531 books/edit/edition.html:540 +#: books/edit/edition.html:526 books/edit/edition.html:535 msgid "Remove this classification" msgstr "" -#: books/edit/edition.html:552 type/edition/view.html:426 -#: type/work/view.html:426 +#: books/edit/edition.html:547 type/edition/view.html:446 +#: type/work/view.html:446 msgid "The Physical Object" msgstr "" -#: books/edit/edition.html:558 +#: books/edit/edition.html:553 msgid "What sort of book is it?" msgstr "" -#: books/edit/edition.html:559 +#: books/edit/edition.html:554 msgid "Paperback; Hardcover, etc." msgstr "" -#: books/edit/edition.html:567 +#: books/edit/edition.html:562 msgid "How many pages?" msgstr "" -#: books/edit/edition.html:577 +#: books/edit/edition.html:572 msgid "Pagination?" msgstr "" -#: books/edit/edition.html:578 +#: books/edit/edition.html:573 msgid "Note the highest number in each pagination pattern." msgstr "" -#: books/edit/edition.html:578 lib/nav_foot.html:45 +#: books/edit/edition.html:573 lib/nav_foot.html:44 msgid "Help" msgstr "" -#: books/edit/edition.html:588 +#: books/edit/edition.html:583 msgid "How much does the book weigh?" msgstr "" -#: books/edit/edition.html:603 type/edition/view.html:431 -#: type/work/view.html:431 +#: books/edit/edition.html:598 type/edition/view.html:451 +#: type/work/view.html:451 msgid "Dimensions" msgstr "" -#: books/edit/edition.html:615 +#: books/edit/edition.html:610 msgid "Height:" msgstr "" -#: books/edit/edition.html:620 +#: books/edit/edition.html:615 msgid "Width:" msgstr "" -#: books/edit/edition.html:625 +#: books/edit/edition.html:620 msgid "Depth:" msgstr "" -#: books/edit/edition.html:648 +#: books/edit/edition.html:643 msgid "Web Book Providers" msgstr "" -#: books/edit/edition.html:653 +#: books/edit/edition.html:648 msgid "Access Type" msgstr "" -#: books/edit/edition.html:654 +#: books/edit/edition.html:649 msgid "File Format" msgstr "" -#: books/edit/edition.html:655 +#: books/edit/edition.html:650 msgid "Provider Name" msgstr "" -#: ReadButton.html:39 books/edit/edition.html:666 +#: ReadButton.html:39 books/edit/edition.html:661 msgid "Listen" msgstr "" -#: books/edit/edition.html:667 +#: books/edit/edition.html:662 msgid "Buy" msgstr "" -#: books/edit/edition.html:675 +#: books/edit/edition.html:670 msgid "Web" msgstr "" -#: books/edit/edition.html:685 +#: books/edit/edition.html:680 msgid "Add a provider" msgstr "" -#: books/edit/edition.html:690 +#: books/edit/edition.html:687 +msgid "First sentence" +msgstr "" + +#: books/edit/edition.html:688 +msgid "The opening line that starts the lead paragraph" +msgstr "" + +#: books/edit/edition.html:698 msgid "Admin Only" msgstr "" -#: books/edit/edition.html:693 +#: books/edit/edition.html:701 msgid "Additional Metadata" msgstr "" -#: books/edit/edition.html:694 +#: books/edit/edition.html:702 msgid "This field can accept arbitrary key:value pairs" msgstr "" -#: books/edit/edition.html:706 -msgid "First sentence" -msgstr "" - #: books/edit/excerpts.html:13 msgid "" "If there are particular (short) passages you feel represent the book " @@ -2757,7 +2831,7 @@ msgid "Page number?" msgstr "" #: books/edit/excerpts.html:23 -msgid "This is the first sentence." +msgid "This excerpt is the first sentence of the book." msgstr "" #: books/edit/excerpts.html:30 @@ -2916,7 +2990,7 @@ msgid "How many books would you like to read this year?" msgstr "" #: check_ins/reading_goal_form.html:18 -msgid "Note: Submit \"0\" to delete your goal. Your check-ins will be preserved." +msgid "Enter \"0\" to unset your reading goal. Your check-ins will be preserved." msgstr "" #: check_ins/reading_goal_progress.html:18 @@ -2932,7 +3006,7 @@ msgid "" "progress__goal\">%(goal)d
    books" msgstr "" -#: check_ins/reading_goal_progress.html:31 +#: check_ins/reading_goal_progress.html:33 #, python-format msgid "Edit %(year)d Reading Goal" msgstr "" @@ -2973,27 +3047,30 @@ msgstr "" msgid "Or, paste in the image URL if it's on the web." msgstr "" -#: covers/book_cover.html:22 +#: covers/add.html:78 +msgid "Uploading..." +msgstr "" + +#: covers/book_cover.html:29 msgid "Pull up a bigger book cover" msgstr "" -#: covers/book_cover.html:22 covers/book_cover_single_edition.html:18 +#: covers/book_cover.html:37 covers/book_cover_single_edition.html:18 #: covers/book_cover_small.html:18 covers/book_cover_work.html:18 #, python-format msgid "Cover of: %(title)s by %(authors)s" msgstr "" -#: covers/book_cover.html:33 covers/book_cover_single_edition.html:29 -#: covers/book_cover_small.html:21 covers/book_cover_work.html:21 -#: covers/book_cover_work.html:25 -#, python-format -msgid "We need a book cover for: %(title)s" -msgstr "" - #: covers/book_cover_single_edition.html:18 covers/book_cover_work.html:18 msgid "View a larger book cover" msgstr "" +#: covers/book_cover_single_edition.html:29 covers/book_cover_small.html:21 +#: covers/book_cover_work.html:21 covers/book_cover_work.html:25 +#, python-format +msgid "We need a book cover for: %(title)s" +msgstr "" + #: covers/book_cover_small.html:18 covers/book_cover_small.html:21 #: covers/book_cover_small.html:25 #, python-format @@ -3050,17 +3127,22 @@ msgstr "" msgid "Finished" msgstr "" -#: history/comment.html:26 +#: history/comment.html:27 +#, python-format +msgid "Imported from %(source)s" +msgstr "" + +#: history/comment.html:29 #, python-format msgid "Imported from %(source)s %(type)s record." msgstr "" -#: history/comment.html:28 +#: history/comment.html:31 #, python-format msgid "Found a matching record from %(source)s." msgstr "" -#: history/comment.html:35 +#: history/comment.html:39 msgid "Edited without comment." msgstr "" @@ -3099,7 +3181,7 @@ msgstr "" msgid "browse %(subject)s books" msgstr "" -#: home/categories.html:27 +#: home/categories.html:31 #, python-format msgid "%(count)s Book" msgid_plural "%(count)s Books" @@ -3114,23 +3196,23 @@ msgstr "" msgid "Classic Books" msgstr "" -#: home/index.html:36 +#: home/index.html:33 msgid "Recently Returned" msgstr "" -#: home/index.html:38 +#: home/index.html:35 msgid "Romance" msgstr "" -#: home/index.html:40 +#: home/index.html:37 msgid "Kids" msgstr "" -#: home/index.html:42 +#: home/index.html:39 msgid "Thrillers" msgstr "" -#: home/index.html:44 +#: home/index.html:41 msgid "Textbooks" msgstr "" @@ -3204,51 +3286,67 @@ msgstr "" msgid "eBooks Borrowed" msgstr "" -#: home/welcome.html:26 +#: home/welcome.html:19 msgid "Read Free Library Books Online" msgstr "" -#: home/welcome.html:27 +#: home/welcome.html:20 msgid "Millions of books available through Controlled Digital Lending" msgstr "" -#: home/welcome.html:40 +#: home/welcome.html:28 +msgid "Set a Yearly Reading Goal" +msgstr "" + +#: home/welcome.html:29 +msgid "Learn how to set a yearly reading goal and track what you read" +msgstr "" + +#: home/welcome.html:38 msgid "Keep Track of your Favorite Books" msgstr "" -#: home/welcome.html:41 +#: home/welcome.html:39 msgid "Organize your Books using Lists & the Reading Log" msgstr "" -#: home/welcome.html:54 +#: home/welcome.html:47 msgid "Try the virtual Library Explorer" msgstr "" -#: home/welcome.html:55 +#: home/welcome.html:48 msgid "Digital shelves organized like a physical library" msgstr "" -#: home/welcome.html:68 +#: home/welcome.html:56 msgid "Try Fulltext Search" msgstr "" -#: home/welcome.html:69 +#: home/welcome.html:57 msgid "Find matching results within the text of millions of books" msgstr "" -#: home/welcome.html:82 +#: home/welcome.html:65 msgid "Be an Open Librarian" msgstr "" -#: home/welcome.html:83 +#: home/welcome.html:66 msgid "Dozens of ways you can help improve the library" msgstr "" -#: home/welcome.html:96 +#: home/welcome.html:74 +msgid "Volunteer at Open Library" +msgstr "" + +#: home/welcome.html:75 +msgid "Discover opportunities to improve the library" +msgstr "" + +#: home/welcome.html:84 msgid "Send us feedback" msgstr "" -#: home/welcome.html:97 +#: home/welcome.html:85 msgid "Your feedback will help us improve these cards" msgstr "" @@ -3284,18 +3382,26 @@ msgid "Croatian" msgstr "" #: languages/language_list.html:14 -msgid "Portuguese" +msgid "Italian" msgstr "" #: languages/language_list.html:15 -msgid "Telugu" +msgid "Portuguese" msgstr "" #: languages/language_list.html:16 -msgid "Ukrainian" +msgid "Hindi" msgstr "" #: languages/language_list.html:17 +msgid "Telugu" +msgstr "" + +#: languages/language_list.html:18 +msgid "Ukrainian" +msgstr "" + +#: languages/language_list.html:19 msgid "Chinese" msgstr "" @@ -3309,11 +3415,11 @@ msgstr "" msgid "%s does not exist." msgstr "" -#: lib/edit_head.html:22 lib/view_head.html:14 +#: lib/edit_head.html:14 lib/view_head.html:14 msgid "This doc was last edited by" msgstr "" -#: lib/edit_head.html:24 lib/view_head.html:16 +#: lib/edit_head.html:16 lib/view_head.html:16 msgid "This doc was last edited anonymously" msgstr "" @@ -3321,12 +3427,12 @@ msgstr "" msgid "Menu" msgstr "" -#: lib/header_dropdown.html:74 +#: lib/header_dropdown.html:75 msgid "New!" msgstr "" #: databarDiff.html:9 databarEdit.html:10 databarTemplate.html:9 -#: databarView.html:26 databarView.html:28 lib/history.html:21 +#: databarView.html:27 databarView.html:29 lib/history.html:21 msgid "History" msgstr "" @@ -3335,7 +3441,7 @@ msgstr "" msgid "Created %(date)s" msgstr "" -#: lib/history.html:28 +#: lib/history.html:32 #, python-format msgid "%(count)d revision" msgid_plural "%(count)d revisions" @@ -3420,7 +3526,7 @@ msgstr "" msgid "Terms of Service" msgstr "" -#: lib/nav_foot.html:19 +#: lib/nav_foot.html:19 site/alert.html:13 msgid "Donate" msgstr "" @@ -3440,10 +3546,6 @@ msgstr "" msgid "Explore Books" msgstr "" -#: SearchNavigation.html:12 lib/nav_foot.html:26 -msgid "Books" -msgstr "" - #: lib/nav_foot.html:27 msgid "Explore authors" msgstr "" @@ -3460,7 +3562,7 @@ msgstr "" msgid "Collections" msgstr "" -#: SearchNavigation.html:32 lib/nav_foot.html:30 lib/nav_head.html:35 +#: SearchNavigation.html:32 lib/nav_foot.html:30 lib/nav_head.html:36 #: search/advancedsearch.html:7 search/advancedsearch.html:14 #: search/advancedsearch.html:18 msgid "Advanced Search" @@ -3482,7 +3584,7 @@ msgstr "" msgid "Explore Open Library Developer Center" msgstr "" -#: lib/nav_foot.html:37 lib/nav_head.html:43 +#: lib/nav_foot.html:37 lib/nav_head.html:44 msgid "Developer Center" msgstr "" @@ -3510,39 +3612,43 @@ msgstr "" msgid "Writing Bots" msgstr "" -#: lib/nav_foot.html:41 -msgid "Add a new book to Open Library" +#: lib/nav_foot.html:46 +msgid "Help Center" msgstr "" #: lib/nav_foot.html:47 -msgid "Help Center" +msgid "Problems" +msgstr "" + +#: lib/nav_foot.html:47 +msgid "Report A Problem" msgstr "" #: lib/nav_foot.html:48 -msgid "Problems" +msgid "Suggest Edits" msgstr "" #: lib/nav_foot.html:48 -msgid "Report A Problem" +msgid "Suggesting Edits" msgstr "" #: lib/nav_foot.html:49 -msgid "Suggest Edits" +msgid "Add a new book to Open Library" msgstr "" -#: lib/nav_foot.html:49 -msgid "Suggesting Edits" +#: lib/nav_foot.html:50 +msgid "Release Notes" msgstr "" -#: lib/nav_foot.html:57 +#: lib/nav_foot.html:58 msgid "Change Website Language" msgstr "" -#: lib/nav_foot.html:63 lib/nav_head.html:63 type/list/embed.html:23 +#: lib/nav_foot.html:64 lib/nav_head.html:64 type/list/embed.html:23 msgid "Open Library logo" msgstr "" -#: lib/nav_foot.html:65 +#: lib/nav_foot.html:66 msgid "" "Open Library is an initiative of the Internet " "Archive, a 501(c)(3) non-profit, building a digital library of " @@ -3553,13 +3659,17 @@ msgid "" "\">archive-it.org" msgstr "" -#: lib/nav_foot.html:70 +#: lib/nav_foot.html:71 #, python-format msgid "" "version %s" msgstr "" +#: lib/nav_head.html:13 lib/nav_head.html:25 lib/nav_head.html:77 +msgid "My Books" +msgstr "" + #: lib/nav_head.html:13 msgid "Loans, Reading Log, Lists, Stats" msgstr "" @@ -3585,55 +3695,59 @@ msgid "K-12 Student Library" msgstr "" #: lib/nav_head.html:34 +msgid "Book Talks" +msgstr "" + +#: lib/nav_head.html:35 msgid "Random Book" msgstr "" -#: lib/nav_head.html:39 +#: lib/nav_head.html:40 msgid "Recent Community Edits" msgstr "" -#: lib/nav_head.html:42 +#: lib/nav_head.html:43 msgid "Help & Support" msgstr "" -#: lib/nav_head.html:44 +#: lib/nav_head.html:45 msgid "Librarians Portal" msgstr "" -#: lib/nav_head.html:48 +#: lib/nav_head.html:49 msgid "My Open Library" msgstr "" -#: lib/nav_head.html:49 lib/nav_head.html:70 +#: lib/nav_head.html:50 lib/nav_head.html:71 msgid "Browse" msgstr "" -#: lib/nav_head.html:50 +#: lib/nav_head.html:51 msgid "Contribute" msgstr "" -#: lib/nav_head.html:51 +#: lib/nav_head.html:52 msgid "Resources" msgstr "" -#: lib/nav_head.html:59 +#: lib/nav_head.html:60 msgid "The Internet Archive's Open Library: One page for every book" msgstr "" -#: lib/nav_head.html:91 +#: lib/nav_head.html:92 msgid "All" msgstr "" -#: lib/nav_head.html:94 +#: lib/nav_head.html:95 msgid "Text" msgstr "" -#: lib/nav_head.html:95 search/advancedsearch.html:32 +#: lib/nav_head.html:96 search/advancedsearch.html:32 msgid "Subject" msgstr "" -#: lib/nav_head.html:97 -msgid "Advanced" +#: lib/nav_head.html:110 lib/nav_head.html:111 +msgid "Search by barcode" msgstr "" #: lib/not_logged.html:7 @@ -3726,120 +3840,114 @@ msgstr "" msgid "Active Lists" msgstr "" -#: lists/home.html:48 +#: lists/home.html:49 #, python-format msgid "from %(owner)s" msgstr "" -#: lists/home.html:47 lists/preview.html:19 lists/snippet.html:14 -#: type/list/embed.html:12 type/list/view_body.html:35 +#: lists/home.html:52 lists/preview.html:24 lists/snippet.html:18 +#: type/list/embed.html:18 type/list/view_body.html:40 #, python-format msgid "%(count)d item" msgid_plural "%(count)d items" msgstr[0] "" msgstr[1] "" -#: lists/home.html:53 lists/preview.html:26 lists/snippet.html:20 +#: lists/home.html:54 lists/preview.html:27 lists/snippet.html:20 #, python-format msgid "Last modified %(date)s" msgstr "" -#: lists/home.html:59 lists/snippet.html:26 +#: lists/home.html:60 lists/snippet.html:26 msgid "No description." msgstr "" -#: lists/home.html:65 lists/lists.html:130 type/user/view.html:81 +#: lists/home.html:66 lists/lists.html:67 type/user/view.html:81 msgid "Recent Activity" msgstr "" -#: lists/home.html:66 type/user/view.html:67 +#: lists/home.html:67 msgid "See all" msgstr "" -#: lists/list_overview.html:15 lists/widget.html:84 +#: lists/list_overview.html:17 lists/widget.html:79 +#: my_books/dropdown_content.html:127 msgid "See this list" msgstr "" -#: lists/list_overview.html:25 lists/widget.html:85 +#: lists/list_overview.html:27 lists/widget.html:80 +#: my_books/dropdown_content.html:128 msgid "Remove from your list?" msgstr "" -#: lists/list_overview.html:28 lists/widget.html:87 +#: lists/list_overview.html:30 lists/widget.html:82 +#: my_books/dropdown_content.html:130 msgid "You" msgstr "" -#: lists/lists.html:10 +#: lists/lists.html:12 #, python-format msgid "%(owner)s / Lists" msgstr "" -#: lists/lists.html:30 -msgid "Delete this list" -msgstr "" - -#: lists/lists.html:38 type/list/view_body.html:111 -#: type/list/view_body.html:141 +#: lists/lists.html:31 lists/lists.html:38 type/list/view_body.html:66 msgid "Yes, I'm sure" msgstr "" -#: lists/lists.html:49 type/list/view_body.html:128 -#: type/list/view_body.html:150 +#: lists/lists.html:31 lists/lists.html:38 type/list/view_body.html:66 msgid "No, cancel" msgstr "" -#: lists/lists.html:61 +#: lists/lists.html:31 +msgid "Delete this list" +msgstr "" + +#: lists/lists.html:33 msgid "Delete list" msgstr "" -#: lists/lists.html:61 +#: lists/lists.html:33 msgid "Are you sure you want to delete this list?" msgstr "" -#: lists/lists.html:68 +#: lists/lists.html:38 msgid "Remove this seed?" msgstr "" -#: lists/lists.html:109 +#: lists/lists.html:40 #, python-format msgid "Are you sure you want to remove %(title)s from this list?" msgstr "" -#: lists/lists.html:121 +#: lists/lists.html:58 msgid "This reader hasn't created any lists yet." msgstr "" -#: lists/lists.html:123 +#: lists/lists.html:60 msgid "You haven't created any lists yet." msgstr "" -#: lists/lists.html:126 +#: lists/lists.html:63 msgid "Learn how to create your first list." msgstr "" -#: lists/preview.html:17 +#: lists/preview.html:18 msgid "by You" msgstr "" -#: lists/widget.html:57 -msgid "watch for edits or export all records" -msgstr "" - -#: lists/widget.html:64 +#: lists/widget.html:59 #, python-format msgid "%(count)d List" msgid_plural "%(count)d Lists" msgstr[0] "" msgstr[1] "" -#: databarAuthor.html:86 lists/widget.html:86 +#: databarAuthor.html:86 lists/widget.html:81 +#: my_books/dropdown_content.html:129 msgid "from" msgstr "" -#: lists/widget.html:113 -msgid "Remove" -msgstr "" - -#: merge/authors.html:7 merge/authors.html:11 merge/authors.html:102 +#: merge/authors.html:7 merge/authors.html:11 merge/authors.html:111 msgid "Merge Authors" msgstr "" @@ -3856,7 +3964,7 @@ msgid "Please select some authors to merge." msgstr "" #: merge/authors.html:27 -msgid "Select a \"primary\" record that best represents the author -" +msgid "Select the oldest profile as primary -" msgstr "" #: merge/authors.html:30 @@ -3883,35 +3991,35 @@ msgstr "" msgid "Are you sure you want to merge these records?" msgstr "" -#: merge/authors.html:55 +#: merge/authors.html:55 recentchanges/merge/view.html:23 msgid "Primary" msgstr "" -#: merge/authors.html:56 merge_queue/merge_queue.html:125 +#: merge/authors.html:56 msgid "Merge" msgstr "" -#: merge/authors.html:86 +#: merge/authors.html:95 msgid "Open in a new window" msgstr "" -#: merge/authors.html:93 +#: merge/authors.html:102 msgid "Visit this author's page in a new window" msgstr "" -#: merge/authors.html:99 +#: merge/authors.html:108 msgid "Comment:" msgstr "" -#: merge/authors.html:99 +#: merge/authors.html:108 msgid "Comment..." msgstr "" -#: merge/authors.html:104 +#: merge/authors.html:113 msgid "Request Merge" msgstr "" -#: merge/authors.html:107 +#: merge/authors.html:116 msgid "Reject Merge" msgstr "" @@ -3919,100 +4027,184 @@ msgstr "" msgid "Merge Works" msgstr "" -#: merge_queue/comment.html:21 -msgid "Just now" +#: merge_request_table/merge_request_table.html:12 +msgid "Failed to submit comment. Please try again in a few moments." msgstr "" -#: merge_queue/merge_queue.html:18 +#: merge_request_table/merge_request_table.html:13 +msgid "(Optional) Why are you closing this request?" +msgstr "" + +#: merge_request_table/merge_request_table.html:25 #, python-format msgid "Showing %(username)s's requests only." msgstr "" -#: merge_queue/merge_queue.html:19 +#: merge_request_table/merge_request_table.html:26 msgid "Show all requests" msgstr "" -#: merge_queue/merge_queue.html:22 +#: merge_request_table/merge_request_table.html:29 msgid "Showing all requests." msgstr "" -#: merge_queue/merge_queue.html:23 +#: merge_request_table/merge_request_table.html:30 msgid "Show my requests" msgstr "" -#: merge_queue/merge_queue.html:27 +#: merge_request_table/merge_request_table.html:34 msgid "Community Edit Requests" msgstr "" -#: merge_queue/merge_queue.html:33 -#, python-format -msgid "Showing requests reviewed by %(reviewer)s only." +#: merge_request_table/merge_request_table.html:43 +msgid "No entries here!" msgstr "" -#: merge_queue/merge_queue.html:33 -msgid "Remove reviewer filter" +#: merge_request_table/table_header.html:17 +msgid "Open" msgstr "" -#: merge_queue/merge_queue.html:35 -msgid "Show requests that I've reviewed" +#: merge_request_table/table_header.html:18 +msgid "Closed" msgstr "" -#: merge_queue/merge_queue.html:47 -msgid "Open" +#: merge_request_table/table_header.html:22 +msgid "Status ▾" msgstr "" -#: merge_queue/merge_queue.html:50 -msgid "Closed" +#: merge_request_table/table_header.html:25 +msgid "Request Status" msgstr "" -#: merge_queue/merge_queue.html:58 +#: merge_request_table/table_header.html:31 +msgid "Merged" +msgstr "" + +#: merge_request_table/table_header.html:36 +msgid "Declined" +msgstr "" + +#: merge_request_table/table_header.html:40 +msgid "Submitter ▾" +msgstr "" + +#: merge_request_table/table_header.html:43 msgid "Submitter" msgstr "" -#: RecentChangesAdmin.html:22 merge_queue/merge_queue.html:60 -#: merge_queue/merge_queue.html:85 -msgid "Comments" +#: merge_request_table/table_header.html:46 +msgid "Filter submitters" msgstr "" -#: merge_queue/merge_queue.html:61 +#: merge_request_table/table_header.html:50 +msgid "Submitted by me" +msgstr "" + +#: merge_request_table/table_header.html:61 +msgid "Reviewer ▾" +msgstr "" + +#: merge_request_table/table_header.html:64 msgid "Reviewer" msgstr "" -#: merge_queue/merge_queue.html:62 -msgid "Resolve" +#: merge_request_table/table_header.html:67 +msgid "Filter reviewers" msgstr "" -#: merge_queue/merge_queue.html:68 -msgid "No entries here!" +#: merge_request_table/table_header.html:72 +msgid "Assigned to nobody" msgstr "" -#: merge_queue/merge_queue.html:77 -msgid "Work" +#: merge_request_table/table_header.html:84 +msgid "Sort ▾" msgstr "" -#: merge_queue/merge_queue.html:84 -#, python-format -msgid "" -"%(type)s merge request for %(title)s" +#: merge_request_table/table_header.html:87 +msgid "Sort" +msgstr "" + +#: merge_request_table/table_header.html:93 +msgid "Newest" +msgstr "" + +#: merge_request_table/table_header.html:98 +msgid "Oldest" msgstr "" -#: merge_queue/merge_queue.html:90 -msgid "Showing most recent comment only." +#: merge_request_table/table_row.html:10 +msgid "An untitled work" msgstr "" -#: merge_queue/merge_queue.html:91 -msgid "View all" +#: merge_request_table/table_row.html:10 +msgid "An unnamed author" msgstr "" -#: merge_queue/merge_queue.html:101 +#: merge_request_table/table_row.html:28 +msgid "Open request" +msgstr "" + +#: merge_request_table/table_row.html:28 +msgid "Closed request" +msgstr "" + +#: merge_request_table/table_row.html:37 msgid "No comments yet." msgstr "" -#: merge_queue/merge_queue.html:106 +#: merge_request_table/table_row.html:52 msgid "Add a comment..." msgstr "" +#: merge_request_table/table_row.html:53 +msgid "Reply" +msgstr "" + +#: merge_request_table/table_row.html:60 +#, python-format +msgid "" +"MR #%(id)s opened %(date)s by @%(submitter)s" +msgstr "" + +#: merge_request_table/table_row.html:62 +msgid "Click to close this Merge Request" +msgstr "" + +#: merge_request_table/table_row.html:85 type/edition/modal_links.html:25 +msgid "Review" +msgstr "" + +#: my_books/dropdown_content.html:24 +msgid "Remove From Shelf" +msgstr "" + +#: my_books/dropdown_content.html:60 +msgid "My Reading Lists:" +msgstr "" + +#: my_books/dropdown_content.html:75 +msgid "Use this Work" +msgstr "" + +#: CreateListModal.html:10 databarAuthor.html:27 databarAuthor.html:34 +#: my_books/dropdown_content.html:79 my_books/dropdown_content.html:90 +msgid "Create a new list" +msgstr "" + +#: CreateListModal.html:24 my_books/dropdown_content.html:104 +msgid "Description:" +msgstr "" + +#: CreateListModal.html:32 my_books/dropdown_content.html:112 +#: type/list/edit.html:145 +msgid "Create new list" +msgstr "" + +#: my_books/primary_action.html:42 my_books/primary_action.html:46 +msgid "Add to List" +msgstr "" + #: observations/review_component.html:16 msgid "Community Reviews" msgstr "" @@ -4042,7 +4234,7 @@ msgstr "" msgid "Try something else?" msgstr "" -#: publishers/view.html:15 +#: publishers/view.html:19 #, python-format msgid "%(count)s ebook" msgid_plural "%(count)s ebooks" @@ -4084,18 +4276,22 @@ msgid "Author Merges" msgstr "" #: recentchanges/index.html:36 -msgid "Books Added" +msgid "Work Merges" msgstr "" #: recentchanges/index.html:37 +msgid "Books Added" +msgstr "" + +#: recentchanges/index.html:38 msgid "Covers Added" msgstr "" -#: recentchanges/index.html:58 +#: recentchanges/index.html:59 msgid "By Humans" msgstr "" -#: recentchanges/index.html:59 +#: recentchanges/index.html:60 msgid "By Bots" msgstr "" @@ -4104,55 +4300,47 @@ msgstr "" msgid "opened a new Open Library account!" msgstr "" -#: recentchanges/merge-authors/message.html:13 +#: recentchanges/merge/comment.html:24 +#, python-format +msgid "Merged %(count)d duplicate %(type)s record into this primary." +msgid_plural "Merged %(count)d duplicate %(type)s records into this primary." +msgstr[0] "" +msgstr[1] "" + +#: recentchanges/merge/message.html:13 #, python-format msgid "%(who)s merged one duplicate of %(master)s" msgid_plural "%(who)s merged %(count)d duplicates of %(master)s" msgstr[0] "" msgstr[1] "" -#: recentchanges/merge-authors/message.html:20 +#: recentchanges/merge/message.html:20 #, python-format msgid "one duplicate of %(master)s was merged anonymously" msgid_plural "%(count)d duplicates of %(master)s were merged anonymously" msgstr[0] "" msgstr[1] "" -#: recentchanges/merge-authors/view.html:8 -msgid "Author Merge" -msgstr "" - -#: recentchanges/merge-authors/view.html:16 +#: recentchanges/merge/view.html:18 msgid "This merge has been undone." msgstr "" -#: recentchanges/merge-authors/view.html:17 +#: recentchanges/merge/view.html:19 msgid "Details here" msgstr "" -#: recentchanges/merge-authors/view.html:21 -msgid "Master" -msgstr "" - -#: recentchanges/merge-authors/view.html:40 type/author/view.html:78 +#: recentchanges/merge/view.html:43 type/author/view.html:78 msgid "Duplicates" msgstr "" -#: recentchanges/merge/comment.html:20 -#, python-format -msgid "Merged %(count)d duplicate %(type)s record into this primary." -msgid_plural "Merged %(count)d duplicate %(type)s records into this primary." -msgstr[0] "" -msgstr[1] "" - -#: recentchanges/merge/view.html:51 +#: recentchanges/merge/view.html:55 #, python-format msgid "%(count)d record modified." msgid_plural "%(count)d records modified." msgstr[0] "" msgstr[1] "" -#: recentchanges/merge-authors/view.html:53 +#: recentchanges/merge/view.html:59 msgid "Updated Records" msgstr "" @@ -4169,22 +4357,26 @@ msgid "Person" msgstr "" #: search/advancedsearch.html:50 -msgid "Full Text Search" +msgid "How to search?" msgstr "" -#: search/authors.html:19 +#: search/advancedsearch.html:51 +msgid "Full Text Search?" +msgstr "" + +#: search/authors.html:20 msgid "Search Authors" msgstr "" -#: search/authors.html:48 +#: search/authors.html:51 msgid "Is the same author listed twice?" msgstr "" -#: search/authors.html:48 +#: search/authors.html:51 msgid "Merge authors" msgstr "" -#: search/authors.html:51 +#: search/authors.html:54 msgid "No hits" msgstr "" @@ -4202,14 +4394,14 @@ msgstr "" msgid "No hits for: %(query)s" msgstr "" -#: search/inside.html:29 +#: search/inside.html:37 #, python-format msgid "About %(count)s result found" msgid_plural "About %(count)s results found" msgstr[0] "" msgstr[1] "" -#: search/inside.html:29 +#: search/inside.html:37 #, python-format msgid "in %(seconds)s second" msgid_plural "in %(seconds)s seconds" @@ -4232,27 +4424,51 @@ msgstr "" msgid "Sorted by" msgstr "" -#: search/sort_options.html:12 +#: search/sort_options.html:13 search/sort_options.html:24 msgid "Relevance" msgstr "" -#: search/sort_options.html:13 +#: search/sort_options.html:14 +msgid "Work Count" +msgstr "" + +#: search/sort_options.html:15 search/sort_options.html:41 +msgid "Random" +msgstr "" + +#: search/sort_options.html:19 +msgid "List Order" +msgstr "" + +#: search/sort_options.html:20 +msgid "Last Modified" +msgstr "" + +#: search/sort_options.html:25 msgid "Most Editions" msgstr "" -#: search/sort_options.html:14 +#: search/sort_options.html:26 msgid "First Published" msgstr "" -#: search/sort_options.html:15 +#: search/sort_options.html:27 msgid "Most Recent" msgstr "" -#: search/sort_options.html:16 -msgid "Random" +#: search/sort_options.html:28 +msgid "Top Rated" msgstr "" -#: search/sort_options.html:33 +#: search/sort_options.html:35 +msgid "Any" +msgstr "" + +#: search/sort_options.html:44 +msgid "Work Title (beta: Librarian only)" +msgstr "" + +#: search/sort_options.html:74 msgid "Shuffle" msgstr "" @@ -4292,7 +4508,7 @@ msgstr "" msgid "View all recent changes" msgstr "" -#: site/body.html:24 +#: site/body.html:22 msgid "It looks like you're offline." msgstr "" @@ -4349,8 +4565,8 @@ msgstr "" msgid "Most Wanted Books (This Month)" msgstr "" -#: stats/readinglog.html:67 stats/readinglog.html:75 stats/readinglog.html:84 -#: stats/readinglog.html:93 +#: stats/readinglog.html:71 stats/readinglog.html:79 stats/readinglog.html:88 +#: stats/readinglog.html:97 #, python-format msgid "Added %(count)d time" msgid_plural "Added %(count)d times" @@ -4381,9 +4597,37 @@ msgstr "" msgid "We couldn't find any books about" msgstr "" -#: type/about/edit.html:9 type/i18n/edit.html:12 type/page/edit.html:9 -#: type/permission/edit.html:9 type/rawtext/edit.html:9 -#: type/template/edit.html:9 type/usergroup/edit.html:9 +#: swagger/swaggerui.html:9 +msgid "OpenAPI" +msgstr "" + +#: tag/add.html:7 +msgid "Add a tag" +msgstr "" + +#: tag/add.html:11 +msgid "Add a tag to Open Library" +msgstr "" + +#: tag/add.html:24 +msgid "Name of Tag" +msgstr "" + +#: tag/add.html:34 +msgid "How would you describe this tag?" +msgstr "" + +#: tag/add.html:44 type/tag/edit.html:40 +msgid "Tag type" +msgstr "" + +#: tag/add.html:64 +msgid "Add this tag now" +msgstr "" + +#: type/about/edit.html:9 type/page/edit.html:9 type/permission/edit.html:9 +#: type/rawtext/edit.html:9 type/template/edit.html:9 +#: type/usergroup/edit.html:9 #, python-format msgid "Edit %(title)s" msgstr "" @@ -4483,7 +4727,7 @@ msgstr "" msgid "name missing" msgstr "" -#: type/author/view.html:19 type/edition/view.html:89 type/work/view.html:89 +#: type/author/view.html:19 type/edition/view.html:98 type/work/view.html:98 #, python-format msgid "%(page_title)s | Open Library" msgstr "" @@ -4549,27 +4793,27 @@ msgid "" "href=\"%(url)s\">everything by this author?" msgstr "" -#: type/author/view.html:179 +#: type/author/view.html:181 msgid "Time" msgstr "" -#: type/author/view.html:190 +#: type/author/view.html:192 msgid "OLID" msgstr "" -#: type/author/view.html:201 +#: type/author/view.html:208 msgid "Links (outside Open Library)" msgstr "" -#: type/author/view.html:211 +#: type/author/view.html:218 msgid "No links yet." msgstr "" -#: type/author/view.html:211 +#: type/author/view.html:218 msgid "Add one" msgstr "" -#: type/author/view.html:218 +#: type/author/view.html:225 msgid "Alternative names" msgstr "" @@ -4597,10 +4841,6 @@ msgstr "" msgid "See the page's history" msgstr "" -#: type/doc/edit.html:33 type/page/edit.html:31 -msgid "Document Body:" -msgstr "" - #: type/edition/admin_bar.html:17 msgid "Add to Staff Picks" msgstr "" @@ -4613,167 +4853,159 @@ msgstr "" msgid "View Book on Archive.org" msgstr "" -#: SearchResultsWork.html:107 type/edition/admin_bar.html:28 +#: SearchResultsWork.html:117 type/edition/admin_bar.html:28 msgid "Orphaned Edition" msgstr "" -#: databarHistory.html:26 databarView.html:33 +#: databarHistory.html:26 databarView.html:34 #: type/edition/compact_title.html:10 msgid "Edit this template" msgstr "" -#: type/edition/modal_links.html:25 -msgid "Review" -msgstr "" - -#: type/edition/modal_links.html:28 -msgid "Notes" -msgstr "" - -#: type/edition/modal_links.html:32 +#: type/edition/modal_links.html:34 type/edition/title_and_author.html:58 msgid "Share" msgstr "" -#: type/edition/title_summary.html:8 +#: type/edition/title_and_author.html:25 msgid "An edition of" msgstr "" -#: type/edition/title_summary.html:10 +#: type/edition/title_and_author.html:27 #, python-format msgid "First published in %s" msgstr "" -#: type/edition/view.html:226 type/work/view.html:226 +#: type/edition/view.html:262 type/work/view.html:262 +#, python-format +msgid "" +"This edition doesn't have a description yet. Can you add one?" +msgstr "" + +#: type/edition/view.html:271 type/work/view.html:271 msgid "Publish Date" msgstr "" -#: type/edition/view.html:236 type/work/view.html:236 +#: type/edition/view.html:281 type/work/view.html:281 #, python-format msgid "Show other books from %(publisher)s" msgstr "" -#: type/edition/view.html:240 type/work/view.html:240 +#: type/edition/view.html:285 type/work/view.html:285 #, python-format msgid "Search for other books from %(publisher)s" msgstr "" -#: type/edition/view.html:251 type/work/view.html:251 +#: type/edition/view.html:296 type/work/view.html:296 msgid "Pages" msgstr "" -#: databarWork.html:101 type/edition/view.html:297 type/work/view.html:297 +#: databarWork.html:84 type/edition/view.html:329 type/work/view.html:329 msgid "Buy this book" msgstr "" -#: type/edition/view.html:329 type/work/view.html:329 -#, python-format -msgid "" -"This edition doesn't have a description yet. Can you add one?" -msgstr "" - -#: type/edition/view.html:352 type/work/view.html:352 +#: type/edition/view.html:364 type/work/view.html:364 msgid "Book Details" msgstr "" -#: type/edition/view.html:356 type/work/view.html:356 +#: type/edition/view.html:368 type/work/view.html:368 msgid "Published in" msgstr "" -#: type/edition/view.html:364 type/edition/view.html:454 -#: type/edition/view.html:455 type/work/view.html:364 type/work/view.html:454 -#: type/work/view.html:455 +#: type/edition/view.html:376 type/edition/view.html:474 +#: type/edition/view.html:475 type/work/view.html:376 type/work/view.html:474 +#: type/work/view.html:475 msgid "First Sentence" msgstr "" -#: type/edition/view.html:374 type/work/view.html:374 +#: type/edition/view.html:392 type/work/view.html:392 msgid "Edition Notes" msgstr "" -#: type/edition/view.html:379 type/work/view.html:379 +#: type/edition/view.html:399 type/work/view.html:399 msgid "Series" msgstr "" -#: type/edition/view.html:380 type/work/view.html:380 +#: type/edition/view.html:400 type/work/view.html:400 msgid "Volume" msgstr "" -#: type/edition/view.html:381 type/work/view.html:381 +#: type/edition/view.html:401 type/work/view.html:401 msgid "Genre" msgstr "" -#: type/edition/view.html:382 type/work/view.html:382 +#: type/edition/view.html:402 type/work/view.html:402 msgid "Other Titles" msgstr "" -#: type/edition/view.html:383 type/work/view.html:383 +#: type/edition/view.html:403 type/work/view.html:403 msgid "Copyright Date" msgstr "" -#: type/edition/view.html:384 type/work/view.html:384 +#: type/edition/view.html:404 type/work/view.html:404 msgid "Translation Of" msgstr "" -#: type/edition/view.html:385 type/work/view.html:385 +#: type/edition/view.html:405 type/work/view.html:405 msgid "Translated From" msgstr "" -#: type/edition/view.html:404 type/work/view.html:404 +#: type/edition/view.html:424 type/work/view.html:424 msgid "External Links" msgstr "" -#: type/edition/view.html:428 type/work/view.html:428 +#: type/edition/view.html:448 type/work/view.html:448 msgid "Format" msgstr "" -#: type/edition/view.html:429 type/work/view.html:429 +#: type/edition/view.html:449 type/work/view.html:449 msgid "Pagination" msgstr "" -#: type/edition/view.html:430 type/work/view.html:430 +#: type/edition/view.html:450 type/work/view.html:450 msgid "Number of pages" msgstr "" -#: type/edition/view.html:432 type/work/view.html:432 +#: type/edition/view.html:452 type/work/view.html:452 msgid "Weight" msgstr "" -#: type/edition/view.html:458 type/work/view.html:458 +#: type/edition/view.html:478 type/work/view.html:478 msgid "Work Description" msgstr "" -#: type/edition/view.html:467 type/work/view.html:467 +#: type/edition/view.html:487 type/work/view.html:487 msgid "Original languages" msgstr "" -#: type/edition/view.html:472 type/work/view.html:472 +#: type/edition/view.html:492 type/work/view.html:492 msgid "Excerpts" msgstr "" -#: type/edition/view.html:482 type/work/view.html:482 +#: type/edition/view.html:502 type/work/view.html:502 #, python-format msgid "Page %(page)s" msgstr "" -#: type/edition/view.html:489 type/work/view.html:489 +#: type/edition/view.html:509 type/work/view.html:509 #, python-format msgid "added by %(authorlink)s." msgstr "" -#: type/edition/view.html:491 type/work/view.html:491 +#: type/edition/view.html:511 type/work/view.html:511 msgid "added anonymously." msgstr "" -#: type/edition/view.html:499 type/work/view.html:499 +#: type/edition/view.html:519 type/work/view.html:519 msgid "Links outside Open Library" msgstr "" -#: type/edition/view.html:503 type/work/view.html:503 +#: type/edition/view.html:523 type/work/view.html:523 msgid "Wikipedia" msgstr "" -#: type/edition/view.html:519 type/work/view.html:519 -msgid "Lists containing this Book" +#: type/edition/view.html:558 type/work/view.html:558 +msgid "This work does not appear on any lists." msgstr "" #: type/language/view.html:22 @@ -4785,49 +5017,72 @@ msgstr "" msgid "Try a search for readable books in %(language)s?" msgstr "" -#: type/list/edit.html:14 +#: type/list/edit.html:7 +#, python-format +msgid "Editing list: %(list_name)s" +msgstr "" + +#: type/list/edit.html:17 msgid "Edit List" msgstr "" -#: type/list/edit.html:25 -msgid "Label" +#: type/list/edit.html:21 +msgid "" +"⚠️ Saving global lists is admin-only while the feature is under " +"development." msgstr "" -#: type/list/edit.html:34 type/user/edit.html:39 -msgid "Description" +#: type/list/edit.html:46 +msgid "Search for a book" +msgstr "" + +#: type/list/edit.html:65 +msgid "Notes (optional)" +msgstr "" + +#: type/list/edit.html:114 +msgid "List Name" +msgstr "" + +#: type/list/edit.html:121 +msgid "Description (optional)" +msgstr "" + +#: type/list/edit.html:138 +msgid "Add another book" msgstr "" #: type/list/embed.html:23 msgid "Visit Open Library" msgstr "" -#: type/list/embed.html:42 type/list/view_body.html:199 +#: type/list/embed.html:43 type/list/view_body.html:101 msgid "Unknown authors" msgstr "" -#: type/list/embed.html:67 +#: type/list/embed.html:68 msgid "" "Open in online Book Reader. Downloads available in ePub, DAISY, PDF, TXT " "formats from main book page" msgstr "" -#: type/list/embed.html:73 +#: type/list/embed.html:74 msgid "This book is checked out" msgstr "" -#: type/list/embed.html:75 +#: type/list/embed.html:76 msgid "Checked out" msgstr "" -#: type/list/embed.html:78 +#: type/list/embed.html:79 msgid "Borrow book" msgstr "" -#: type/list/embed.html:83 +#: type/list/embed.html:84 msgid "Protected DAISY" msgstr "" -#: BookByline.html:34 type/list/embed.html:100 +#: BookByline.html:34 type/list/embed.html:101 #, python-format msgid "by %(name)s" msgstr "" @@ -4882,38 +5137,52 @@ msgstr "" msgid "View the list on Open Library." msgstr "" -#: type/list/view_body.html:165 +#: type/list/view_body.html:66 msgid "Remove this item?" msgstr "" -#: type/list/view_body.html:179 +#: type/list/view_body.html:80 #, python-format -msgid "Last updated %(date)s" +msgid "Last modified %(date)s" msgstr "" -#: type/list/view_body.html:190 +#: type/list/view_body.html:92 msgid "Remove seed" msgstr "" -#: type/list/view_body.html:190 +#: type/list/view_body.html:92 msgid "Are you sure you want to remove this item from the list?" msgstr "" -#: type/list/view_body.html:191 +#: type/list/view_body.html:93 msgid "Remove Seed" msgstr "" -#: type/list/view_body.html:192 +#: type/list/view_body.html:94 msgid "" "You are about to remove the last item in the list. That will delete the " "whole list. Are you sure you want to continue?" msgstr "" -#: type/list/view_body.html:261 +#: type/list/view_body.html:122 +msgid "There are no items on this list." +msgstr "" + +#: type/list/view_body.html:124 +msgid "" +"Search for book, subjects, or " +"authors to add to your list." +msgstr "" + +#: type/list/view_body.html:125 +msgid "Learn more." +msgstr "" + +#: type/list/view_body.html:177 msgid "List Metadata" msgstr "" -#: type/list/view_body.html:262 +#: type/list/view_body.html:178 msgid "Derived from seed metadata" msgstr "" @@ -4929,6 +5198,10 @@ msgstr "" msgid "Example" msgstr "" +#: type/page/edit.html:31 +msgid "Document Body:" +msgstr "" + #: type/permission/view.html:17 msgid "Readers" msgstr "" @@ -4941,6 +5214,23 @@ msgstr "" msgid "Document Body" msgstr "" +#: type/tag/edit.html:14 +msgid "Edit Tag" +msgstr "" + +#: type/tag/edit.html:23 +msgid "Label" +msgstr "" + +#: type/tag/edit.html:32 type/user/edit.html:39 +msgid "Description" +msgstr "" + +#: type/tag/view.html:22 +#, python-format +msgid "View subject page for %(name)s." +msgstr "" + #: type/template/edit.html:51 msgid "Delete this template?" msgstr "" @@ -4987,6 +5277,10 @@ msgstr "" msgid "private" msgstr "" +#: type/user/view.html:57 +msgid "Manage your privacy settings" +msgstr "" + #: type/user/view.html:60 #, python-format msgid "" @@ -4996,15 +5290,15 @@ msgid "" "read\">want to read!" msgstr "" -#: type/user/view.html:62 -msgid "This reader has chosen to make their Reading Log private." +#: type/user/view.html:66 +msgid "My Lists" msgstr "" #: RecentChangesUsers.html:64 type/user/view.html:84 msgid "No edits. Yet." msgstr "" -#: SearchResultsWork.html:83 type/work/editions.html:11 +#: SearchResultsWork.html:92 type/work/editions.html:11 #, python-format msgid "First published in %(year)s" msgstr "" @@ -5022,7 +5316,7 @@ msgstr "" msgid "Title missing" msgstr "" -#: type/work/editions_datatable.html:9 +#: type/work/editions_datatable.html:13 #, python-format msgid "Showing %(count)d featured edition." msgid_plural "Showing %(count)d featured editions." @@ -5071,7 +5365,7 @@ msgstr "" msgid "Look for this edition for sale at %(store)s" msgstr "" -#: BookByline.html:11 +#: BookByline.html:20 #, python-format msgid "%(n)s other" msgid_plural "%(n)s others" @@ -5082,12 +5376,12 @@ msgstr[1] "" msgid "by an unknown author" msgstr "" -#: BookPreview.html:19 -msgid "Preview Book" +#: BookPreview.html:12 +msgid "Only" msgstr "" -#: BookPreview.html:29 -msgid "See more about this book on Archive.org" +#: BookPreview.html:20 +msgid "Preview Book" msgstr "" #: EditButtons.html:9 EditButtonsMacros.html:12 @@ -5098,73 +5392,75 @@ msgstr "" msgid "Delete this record" msgstr "" -#: EditionNavBar.html:11 +#: EditionNavBar.html:13 msgid "Overview" msgstr "" -#: EditionNavBar.html:15 +#: EditionNavBar.html:17 #, python-format msgid "View %(count)s Edition" msgid_plural "View %(count)s Editions" msgstr[0] "" msgstr[1] "" -#: EditionNavBar.html:19 +#: EditionNavBar.html:21 msgid "Details" msgstr "" -#: EditionNavBar.html:24 +#: EditionNavBar.html:26 #, python-format msgid "%(reviews)s Review" msgid_plural "%(reviews)s Reviews" msgstr[0] "" msgstr[1] "" -#: EditionNavBar.html:33 +#: EditionNavBar.html:35 msgid "Related Books" msgstr "" -#: LoanStatus.html:56 -msgid "Return eBook" -msgstr "" - -#: LoanStatus.html:62 -msgid "Really return this book?" -msgstr "" - -#: LoanStatus.html:97 +#: LoanStatus.html:82 msgid "You are next on the waiting list" msgstr "" -#: LoanStatus.html:99 +#: LoanStatus.html:84 #, python-format msgid "You are #%(spot)d of %(wlsize)d on the waiting list." msgstr "" -#: LoanStatus.html:104 +#: LoanStatus.html:89 msgid "Leave waiting list" msgstr "" -#: LoanStatus.html:118 +#: LoanStatus.html:103 #, python-format msgid "Readers in line: %(count)s" msgstr "" -#: LoanStatus.html:120 +#: LoanStatus.html:105 msgid "You'll be next in line." msgstr "" -#: LoanStatus.html:125 +#: LoanStatus.html:110 msgid "This book is currently checked out, please check back later." msgstr "" -#: LoanStatus.html:126 +#: LoanStatus.html:111 msgid "Checked Out" msgstr "" -#: NotInLibrary.html:9 -msgid "Not in Library" -msgstr "" +#: ManageLoansButtons.html:13 +#, python-format +msgid "There is one person waiting for this book." +msgid_plural "There are %(n)s people waiting for this book." +msgstr[0] "" +msgstr[1] "" + +#: ManageWaitlistButton.html:11 +#, python-format +msgid "Waiting for %d day" +msgid_plural "Waiting for %d days" +msgstr[0] "" +msgstr[1] "" #: NotesModal.html:31 msgid "My Book Notes" @@ -5200,39 +5496,14 @@ msgstr "" msgid "Read ebook from Internet Archive" msgstr "" -#: ReadMore.html:7 +#: ReadMore.html:6 msgid "Read more" msgstr "" -#: ReadMore.html:8 +#: ReadMore.html:7 msgid "Read less" msgstr "" -#: ReadingLogDropper.html:88 -msgid "My Reading Lists:" -msgstr "" - -#: ReadingLogDropper.html:103 -msgid "Use this Work" -msgstr "" - -#: ReadingLogDropper.html:106 ReadingLogDropper.html:127 -#: ReadingLogDropper.html:143 databarAuthor.html:27 databarAuthor.html:34 -msgid "Create a new list" -msgstr "" - -#: ReadingLogDropper.html:117 ReadingLogDropper.html:133 -msgid "Add to List" -msgstr "" - -#: ReadingLogDropper.html:157 -msgid "Description:" -msgstr "" - -#: ReadingLogDropper.html:165 -msgid "Create new list" -msgstr "" - #: RecentChanges.html:54 msgid "View MARC" msgstr "" @@ -5241,6 +5512,10 @@ msgstr "" msgid "Path" msgstr "" +#: RecentChangesAdmin.html:22 +msgid "Comments" +msgstr "" + #: RecentChangesAdmin.html:41 msgid "view" msgstr "" @@ -5254,54 +5529,60 @@ msgid "diff" msgstr "" #: ReturnForm.html:8 +msgid "Really return this book?" +msgstr "" + +#: ReturnForm.html:17 msgid "Return book" msgstr "" -#: SearchResultsWork.html:89 +#: SearchResultsWork.html:98 #, python-format -msgid "in 1 language" +msgid "in %(count)d language" msgid_plural "in %(count)d languages" msgstr[0] "" msgstr[1] "" -#: SearchResultsWork.html:92 +#: SearchResultsWork.html:101 #, python-format msgid "%s previewable" msgstr "" -#: SearchResultsWork.html:102 +#: SearchResultsWork.html:112 msgid "This is only visible to librarians." msgstr "" -#: SearchResultsWork.html:104 +#: SearchResultsWork.html:114 msgid "Work Title" msgstr "" -#: ShareModal.html:44 +#: ShareModal.html:48 msgid "Embed this book in your website" msgstr "" -#: ShareModal.html:48 +#: ShareModal.html:57 msgid "Embed" msgstr "" -#: StarRatings.html:52 +#: StarRatings.html:53 msgid "Clear my rating" msgstr "" -#: StarRatingsStats.html:22 -msgid "Ratings" -msgstr "" +#: StarRatingsStats.html:25 +msgid "Rating" +msgid_plural "Ratings" +msgstr[0] "" +msgstr[1] "" -#: StarRatingsStats.html:24 +#: StarRatingsStats.html:27 msgid "Want to read" msgstr "" -#: StarRatingsStats.html:25 +#: StarRatingsStats.html:28 msgid "Currently reading" msgstr "" -#: StarRatingsStats.html:26 +#: StarRatingsStats.html:29 msgid "Have read" msgstr "" @@ -5353,15 +5634,20 @@ msgstr "" msgid "Open Library F.A.Q." msgstr "" -#: TypeChanger.html:17 +#: TableOfContents.html:34 +#, python-format +msgid "Page %s" +msgstr "" + +#: TypeChanger.html:11 msgid "Page Type" msgstr "" -#: TypeChanger.html:19 +#: TypeChanger.html:13 msgid "Huh?" msgstr "" -#: TypeChanger.html:22 +#: TypeChanger.html:16 msgid "" "Every piece of Open Library is defined by the type of content it " "displays, usually by content definition (e.g. Authors, Editions, Works) " @@ -5370,11 +5656,11 @@ msgid "" " available for editing its content." msgstr "" -#: TypeChanger.html:22 +#: TypeChanger.html:16 msgid "Please use caution changing Page Type!" msgstr "" -#: TypeChanger.html:22 +#: TypeChanger.html:16 msgid "" "(Simplest solution: If you aren't sure whether this should be changed, " "don't change it.)" @@ -5400,93 +5686,99 @@ msgstr "" msgid "Add new list" msgstr "" -#: databarHistory.html:16 databarView.html:22 +#: databarHistory.html:16 databarView.html:23 #, python-format msgid "Last edited by %(author_link)s" msgstr "" -#: databarHistory.html:18 databarView.html:24 +#: databarHistory.html:18 databarView.html:25 msgid "Last edited anonymously" msgstr "" -#: databarWork.html:115 +#: databarWork.html:96 #, python-format msgid "This book was generously donated by %(donor)s" msgstr "" -#: databarWork.html:117 +#: databarWork.html:98 msgid "This book was generously donated anonymously" msgstr "" -#: databarWork.html:122 +#: databarWork.html:103 msgid "Learn more about Book Sponsorship" msgstr "" -#: account.py:420 account.py:458 +#: account.py:431 account.py:485 #, python-format msgid "" "We've sent the verification email to %(email)s. You'll need to read that " "and click on the verification link to verify your email." msgstr "" -#: account.py:486 +#: account.py:513 msgid "Username must be between 3-20 characters" msgstr "" -#: account.py:488 +#: account.py:515 msgid "Username may only contain numbers and letters" msgstr "" -#: account.py:491 +#: account.py:518 msgid "Username unavailable" msgstr "" -#: account.py:496 forms.py:60 +#: account.py:523 forms.py:60 msgid "Must be a valid email address" msgstr "" -#: account.py:500 forms.py:41 +#: account.py:527 forms.py:41 msgid "Email already registered" msgstr "" -#: account.py:526 +#: account.py:553 msgid "Email address is already used." msgstr "" -#: account.py:527 +#: account.py:554 msgid "" "Your email address couldn't be updated. The specified email address is " "already used." msgstr "" -#: account.py:533 +#: account.py:560 msgid "Email verification successful." msgstr "" -#: account.py:534 +#: account.py:561 msgid "" "Your email address has been successfully verified and updated in your " "account." msgstr "" -#: account.py:540 +#: account.py:567 msgid "Email address couldn't be verified." msgstr "" -#: account.py:541 +#: account.py:568 msgid "" "Your email address couldn't be verified. The verification link seems " "invalid." msgstr "" -#: account.py:645 account.py:655 +#: account.py:671 account.py:681 msgid "Password reset failed." msgstr "" -#: account.py:702 account.py:721 +#: account.py:733 account.py:752 msgid "Notification preferences have been updated successfully." msgstr "" +#: borrow.py:204 +msgid "" +"Your account has hit a lending limit. Please try again later or contact " +"info@archive.org." +msgstr "" + #: forms.py:30 msgid "Username" msgstr "" @@ -5515,7 +5807,7 @@ msgstr "" msgid "Must be between 3 and 20 characters" msgstr "" -#: forms.py:78 forms.py:156 +#: forms.py:78 forms.py:159 msgid "Your email address" msgstr "" @@ -5523,30 +5815,30 @@ msgstr "" msgid "Choose a screen name. Screen names are public and cannot be changed later." msgstr "" -#: forms.py:94 +#: forms.py:95 msgid "Letters and numbers only please, and at least 3 characters." msgstr "" -#: forms.py:100 forms.py:162 +#: forms.py:101 forms.py:165 msgid "Choose a password" msgstr "" -#: forms.py:106 +#: forms.py:107 msgid "Confirm password" msgstr "" -#: forms.py:110 +#: forms.py:111 msgid "Passwords didn't match." msgstr "" -#: forms.py:115 +#: forms.py:116 msgid "" "I want to receive news, announcements, and resources from the Internet Archive, the non-profit that " "runs Open Library." msgstr "" -#: forms.py:151 +#: forms.py:154 msgid "Invalid password" msgstr "" From 50987573b7fad8f91830621404ca191df0e33a39 Mon Sep 17 00:00:00 2001 From: Arihant Pal Date: Wed, 14 Feb 2024 02:28:31 +0530 Subject: [PATCH 60/61] Moving to single password field for registration (#7729) * Use localized strings * Adjust margin for password inputs --------- Co-authored-by: James Champ --- openlibrary/plugins/openlibrary/js/index.js | 6 ++++++ .../plugins/openlibrary/js/password-toggle.js | 19 +++++++++++++++++++ openlibrary/plugins/upstream/forms.py | 9 --------- openlibrary/templates/account/create.html | 10 +++++++--- static/css/components/form.olform.less | 3 ++- 5 files changed, 34 insertions(+), 13 deletions(-) create mode 100644 openlibrary/plugins/openlibrary/js/password-toggle.js diff --git a/openlibrary/plugins/openlibrary/js/index.js b/openlibrary/plugins/openlibrary/js/index.js index 290db0a6e50..589607b9448 100644 --- a/openlibrary/plugins/openlibrary/js/index.js +++ b/openlibrary/plugins/openlibrary/js/index.js @@ -526,4 +526,10 @@ jQuery(function () { .then(module => module.initBreadcrumbSelect(crumbs)); } + // Password visibility toggle: + const passwordVisibilityToggle = document.querySelector('.password-visibility-toggle') + if (passwordVisibilityToggle) { + import(/* webpackChunkName: "password-visibility-toggle" */ './password-toggle') + .then(module => module.initPasswordToggling(passwordVisibilityToggle)) + } }); diff --git a/openlibrary/plugins/openlibrary/js/password-toggle.js b/openlibrary/plugins/openlibrary/js/password-toggle.js new file mode 100644 index 00000000000..84761db3426 --- /dev/null +++ b/openlibrary/plugins/openlibrary/js/password-toggle.js @@ -0,0 +1,19 @@ +/** + * Adds ability to toggle a password field's visibilty. + * + * @param {HTMLElement} elem Reference to affordance that toggles a password input's visibility + */ +export function initPasswordToggling(elem) { + const i18nStrings = JSON.parse(elem.dataset.i18n) + const passwordInput = document.querySelector('input[type=password]') + + elem.addEventListener('click', () => { + if (passwordInput.type === 'password') { + passwordInput.type = 'text' + elem.textContent = i18nStrings['hide_password'] + } else { + passwordInput.type = 'password' + elem.textContent = i18nStrings['show_password'] + } + }) +} diff --git a/openlibrary/plugins/upstream/forms.py b/openlibrary/plugins/upstream/forms.py index 87a4b57751c..da96ec0a5c2 100644 --- a/openlibrary/plugins/upstream/forms.py +++ b/openlibrary/plugins/upstream/forms.py @@ -102,15 +102,6 @@ class RegisterForm(Form): klass='required', validators=[vpass], ), - Password( - 'password2', - description=_('Confirm password'), - klass='required', - validators=[ - vpass, - EqualToValidator('password', _("Passwords didn't match.")), - ], - ), Checkbox( 'ia_newsletter', description=_( diff --git a/openlibrary/templates/account/create.html b/openlibrary/templates/account/create.html index c3f7e64ca8a..9305d4dde2b 100644 --- a/openlibrary/templates/account/create.html +++ b/openlibrary/templates/account/create.html @@ -29,11 +29,18 @@

    $_("Sign Up")

    $:input.render() + $if input.name == 'password': + $ i18n_strings = { + $ "show_password": _('Show password'), + $ "hide_password": _('Hide password') + $ } + $i18n_strings["show_password"] $input.note $:suffix
    + $if ctx.user: $def user_link(): $ctx.user.displayname

    $:_("You are already logged into Open Library as %(user)s.", user=str(user_link()))

    @@ -48,9 +55,7 @@

    $_("Sign Up")

    $:field(form.email) $:field(form.username, suffix=str(screenname_url())) $:field(form.password) - $:field(form.password2) -
    @@ -71,5 +76,4 @@

    $_("Sign Up")

    $_("Cancel") - diff --git a/static/css/components/form.olform.less b/static/css/components/form.olform.less index b9ff6a97223..8a7dd27b6d0 100644 --- a/static/css/components/form.olform.less +++ b/static/css/components/form.olform.less @@ -85,7 +85,8 @@ } input[type=number], - input[type=text] { + input[type=text], + input[type=password] { margin: 0 10px 5px 0; } From 2f590171b1d95cc124c44fb6ec647c85f1ca9581 Mon Sep 17 00:00:00 2001 From: Scott Barnes Date: Tue, 13 Feb 2024 15:26:45 -0800 Subject: [PATCH 61/61] Re-enable Amazon imports from /isbn (#8690) * Renable `/isbn` and AMZ imports This PR reenables AMZ imports from `/isbn` and `/api/books.json`. See this comment examples of how to use the endpoints and what to expect: https://github.com/internetarchive/openlibrary/pull/8690#issuecomment-1920508733 The logic upon visiting `/isbn/[some isbn]` is now: - attempt to fetch the book from the OL database; - attempt to fetch the book from the `import_item` table (likely ISBNdb); - attempt to fetch the metadata from the Amazon Products API, clean that metadata for import, add the as a `staged` import in the `import_item` table, and then immediately import it `load()`, by way of `ImportItem.import_first_staged()`. If any of these find or create an edition, the ention is returned. * Stop bulk imports from AMZ records Import AMZ records as `staged`. See https://github.com/internetarchive/openlibrary/issues/8541 * Modify the affiliate server to accept a GET parameter, `high_priority`, at `/isbn`. E.g., `http://localhost:31337/isbn/059035342X?high_priority=true`. `high_priority=true` will put the ISBN straight to the front of the queue for an AMZ Products API look up, and attempt for three seconds to fetch the cached AMZ data (if it becomes available), returning that data, marshalled into a form suitable for creating an Edition, if possible. * Use `high_priority=false` (the default) on the affiliate server to fetch AM data if available,then queue for import and immediately import the item, returning the resulting `Edition`, or `None`. * Feature: `/api/books` will attempt to import from ISBN - adds an `high_priority` parameter to `/api/books` to make the API try to import books from ISBN. - relies on changes to `Edition.from_isbn()` which attempt to import editions first from `staged` items in the `import_item` table, and second from Amazon via the affiliate server. --------- Co-authored-by: Mek --- openlibrary/core/models.py | 28 +++++- openlibrary/core/vendors.py | 37 ++++--- openlibrary/plugins/books/code.py | 30 +++++- openlibrary/plugins/books/dynlinks.py | 76 ++++++++++++-- openlibrary/plugins/openlibrary/code.py | 26 ++++- openlibrary/plugins/upstream/borrow.py | 1 + openlibrary/tests/core/test_vendors.py | 64 ++++++++++++ scripts/affiliate_server.py | 127 ++++++++++++++++++++---- 8 files changed, 338 insertions(+), 51 deletions(-) diff --git a/openlibrary/core/models.py b/openlibrary/core/models.py index 4f868576c89..cd3a13fea8c 100644 --- a/openlibrary/core/models.py +++ b/openlibrary/core/models.py @@ -2,6 +2,7 @@ """ from datetime import datetime, timedelta import logging +from openlibrary.core.vendors import get_amazon_metadata import web import json @@ -24,7 +25,6 @@ from openlibrary.core.imports import ImportItem from openlibrary.core.observations import Observations from openlibrary.core.ratings import Ratings -from openlibrary.core.vendors import create_edition_from_amazon_metadata from openlibrary.utils import extract_numeric_id_from_olid, dateutil from openlibrary.utils.isbn import to_isbn_13, isbn_13_to_isbn_10, canonical @@ -373,11 +373,16 @@ def get_ia_download_link(self, suffix): return f"https://archive.org/download/{self.ocaid}/{filename}" @classmethod - def from_isbn(cls, isbn: str) -> "Edition | None": # type: ignore[return] + def from_isbn(cls, isbn: str, high_priority: bool = False) -> "Edition | None": """ Attempts to fetch an edition by ISBN, or if no edition is found, then check the import_item table for a match, then as a last result, attempt to import from Amazon. + :param bool high_priority: If `True`, (1) any AMZ import requests will block + until AMZ has fetched data, and (2) the AMZ request will go to + the front of the queue. If `False`, the import will simply be + queued up if the item is not in the AMZ cache, and the affiliate + server will return a promise. :return: an open library edition for this ISBN or None. """ isbn = canonical(isbn) @@ -401,10 +406,23 @@ def from_isbn(cls, isbn: str) -> "Edition | None": # type: ignore[return] return web.ctx.site.get(matches[0]) # Attempt to fetch the book from the import_item table - if result := ImportItem.import_first_staged(identifiers=isbns): - return result + if edition := ImportItem.import_first_staged(identifiers=isbns): + return edition - # TODO: Final step - call affiliate server, with retry code migrated there. + # Finally, try to fetch the book data from Amazon + import. + # If `high_priority=True`, then the affiliate-server, which `get_amazon_metadata()` + # uses, will block + wait until the Product API responds and the result, if any, + # is staged in `import_item`. + try: + get_amazon_metadata( + id_=isbn10 or isbn13, id_type="isbn", high_priority=high_priority + ) + return ImportItem.import_first_staged(identifiers=isbns) + except requests.exceptions.ConnectionError: + logger.exception("Affiliate Server unreachable") + except requests.exceptions.HTTPError: + logger.exception(f"Affiliate Server: id {isbn10 or isbn13} not found") + return None def is_ia_scan(self): metadata = self.get_ia_meta_fields() diff --git a/openlibrary/core/vendors.py b/openlibrary/core/vendors.py index 41c4c4e563b..fe6e62f4e02 100644 --- a/openlibrary/core/vendors.py +++ b/openlibrary/core/vendors.py @@ -2,6 +2,8 @@ import logging import re import time + +from datetime import date from typing import Any, Literal import requests @@ -280,16 +282,21 @@ def get_amazon_metadata( id_: str, id_type: Literal['asin', 'isbn'] = 'isbn', resources: Any = None, - retries: int = 0, + high_priority: bool = False, ) -> dict | None: """Main interface to Amazon LookupItem API. Will cache results. :param str id_: The item id: isbn (10/13), or Amazon ASIN. :param str id_type: 'isbn' or 'asin'. + :param bool high_priority: Priority in the import queue. High priority + goes to the front of the queue. :return: A single book item's metadata, or None. """ return cached_get_amazon_metadata( - id_, id_type=id_type, resources=resources, retries=retries + id_, + id_type=id_type, + resources=resources, + high_priority=high_priority, ) @@ -307,8 +314,7 @@ def _get_amazon_metadata( id_: str, id_type: Literal['asin', 'isbn'] = 'isbn', resources: Any = None, - retries: int = 0, - sleep_sec: float = 1, + high_priority: bool = False, ) -> dict | None: """Uses the Amazon Product Advertising API ItemLookup operation to locate a specific book by identifier; either 'isbn' or 'asin'. @@ -318,12 +324,10 @@ def _get_amazon_metadata( :param str id_type: 'isbn' or 'asin'. :param Any resources: Used for AWSE Commerce Service lookup See https://webservices.amazon.com/paapi5/documentation/get-items.html - :param int retries: Number of times to query affiliate server before returning None - :param float sleep_sec: Delay time.sleep(sleep_sec) seconds before each retry + :param bool high_priority: Priority in the import queue. High priority + goes to the front of the queue. :return: A single book item's metadata, or None. """ - # TMP: This is causing a bunch of duplicate imports - return None if not affiliate_server_url: return None @@ -339,14 +343,15 @@ def _get_amazon_metadata( id_ = isbn try: - r = requests.get(f'http://{affiliate_server_url}/isbn/{id_}') + priority = "true" if high_priority else "false" + r = requests.get( + f'http://{affiliate_server_url}/isbn/{id_}?high_priority={priority}' + ) r.raise_for_status() - if hit := r.json().get('hit'): - return hit - if retries <= 1: + if data := r.json().get('hit'): + return data + else: return None - time.sleep(sleep_sec) # sleep before recursive call - return _get_amazon_metadata(id_, id_type, resources, retries - 1, sleep_sec) except requests.exceptions.ConnectionError: logger.exception("Affiliate Server unreachable") except requests.exceptions.HTTPError: @@ -415,7 +420,7 @@ def clean_amazon_metadata_for_load(metadata: dict) -> dict: def create_edition_from_amazon_metadata( - id_: str, id_type: Literal['asin', 'isbn'] = 'isbn', retries: int = 0 + id_: str, id_type: Literal['asin', 'isbn'] = 'isbn' ) -> str | None: """Fetches Amazon metadata by id from Amazon Product Advertising API, attempts to create OL edition from metadata, and returns the resulting edition key `/key/OL..M` @@ -426,7 +431,7 @@ def create_edition_from_amazon_metadata( :return: Edition key '/key/OL..M' or None """ - md = get_amazon_metadata(id_, id_type=id_type, retries=retries) + md = get_amazon_metadata(id_, id_type=id_type) if md and md.get('product_group') == 'Book': with accounts.RunAs('ImportBot'): diff --git a/openlibrary/plugins/books/code.py b/openlibrary/plugins/books/code.py index 82d29cdfea2..3560368c092 100644 --- a/openlibrary/plugins/books/code.py +++ b/openlibrary/plugins/books/code.py @@ -13,14 +13,40 @@ class books_json(delegate.page): + """ + Endpoint for mapping bib keys (e.g. ISBN, LCCN) to certain links associated + with Open Library editions, such as the thumbnail URL. + + - `bibkeys` is expected a comma separated string of ISBNs, LCCNs, etc. + - `'high_priority=true'` will attempt to import an edition from a supplied ISBN + if no matching edition is found. If not `high_priority`, then missed bib_keys + are queued for lookup on the affiliate-server, and any responses are `staged` + in `import_item`. + + Example call: + http://localhost:8080/api/books.json?bibkeys=059035342X,0312368615&high_priority=true + + Returns a JSONified dictionary of the form: + {"059035342X": { + "bib_key": "059035342X", + "info_url": "http://localhost:8080/books/OL43M/Harry_Potter_and_the_Sorcerer's_Stone", + "preview": "noview", + "preview_url": "https://archive.org/details/lccn_078073006991", + "thumbnail_url": "https://covers.openlibrary.org/b/id/21-S.jpg" + } + "0312368615": {...} + } + """ + path = "/api/books" @jsonapi def GET(self): - i = web.input(bibkeys='', callback=None, details="false") + i = web.input(bibkeys='', callback=None, details="false", high_priority="false") if web.ctx.path.endswith('.json'): i.format = 'json' - return dynlinks.dynlinks(i.bibkeys.split(","), i) + i.high_priority = i.get("high_priority") == "true" + return dynlinks.dynlinks(bib_keys=i.bibkeys.split(","), options=i) class read_singleget(delegate.page): diff --git a/openlibrary/plugins/books/dynlinks.py b/openlibrary/plugins/books/dynlinks.py index a9aadec0c0c..92c7d76b884 100644 --- a/openlibrary/plugins/books/dynlinks.py +++ b/openlibrary/plugins/books/dynlinks.py @@ -1,8 +1,10 @@ +from typing import Any import json import sys -from collections.abc import Hashable, Iterable, Mapping +from collections.abc import Generator, Hashable, Iterable, Mapping import web +from openlibrary.core.models import Edition from openlibrary.plugins.openlibrary.processors import urlsafe from openlibrary.core import helpers as h @@ -426,7 +428,7 @@ def process_doc_for_viewapi(bib_key, page): return d -def format_result(result, options): +def format_result(result: dict, options: web.storage) -> str: """Format result as js or json. >>> format_result({'x': 1}, {}) @@ -447,20 +449,80 @@ def format_result(result, options): return "var _OLBookInfo = %s;" % json_data -def dynlinks(bib_keys, options): +def is_isbn(bib_key: str) -> bool: + """Return True if the bib_key is ostensibly an ISBN (i.e. 10 or 13 characters).""" + return len(bib_key) in {10, 13} + + +def get_missed_isbn_bib_keys( + bib_keys: Iterable[str], found_records: dict +) -> Generator[str, None, None]: + """ + Return a Generator[str, None, None] with all ISBN bib_keys not in `found_records`. + """ + return ( + bib_key + for bib_key in bib_keys + if bib_key not in found_records and is_isbn(bib_key) + ) + + +def get_isbn_editiondict_map( + isbns: Iterable, high_priority: bool = False +) -> dict[str, Any]: + """ + Attempts to import items from their ISBN, returning a mapping of possibly + imported edition_dicts in the following form: + {isbn_string: edition_dict...}} + """ + # Get a mapping of ISBNs to new Editions (or `None`) + isbn_edition_map = { + isbn: Edition.from_isbn(isbn=isbn, high_priority=high_priority) + for isbn in isbns + } + + # Convert edictions to dicts, dropping ISBNs for which no edition was created. + return { + isbn: edition.dict() for isbn, edition in isbn_edition_map.items() if edition + } + + +def dynlinks(bib_keys: Iterable[str], options: web.storage) -> str: + """ + Return a JSONified dictionary of bib_keys (e.g. ISBN, LCCN) and select URLs + associated with the corresponding edition, if any. + + If a bib key is an ISBN, options.high_priority=True, and no edition is found, + an import is attempted with high priority; otherwise missed bib_keys are queued + for lookup via the affiliate-server and responses are `staged` in `import_item`. + + Example return value for a bib key of the ISBN "1452303886": + '{"1452303886": {"bib_key": "1452303886", "info_url": ' + '"http://localhost:8080/books/OL24630277M/Fires_of_Prophecy_The_Morcyth_Saga_Book_Two", ' + '"preview": "restricted", "preview_url": ' + '"https://archive.org/details/978-1-4523-0388-8"}}' + """ # for backward-compatibility if options.get("details", "").lower() == "true": options["jscmd"] = "details" try: - result = query_docs(bib_keys) - result = process_result(result, options.get('jscmd')) + edition_dicts = query_docs(bib_keys) + # For any ISBN bib_keys without hits, attempt to import+use immediately if + # `high_priority`. Otherwise, queue them for lookup via the AMZ Products + # API and process whatever editions were found in existing data. + if missed_isbns := get_missed_isbn_bib_keys(bib_keys, edition_dicts): + new_editions = get_isbn_editiondict_map( + isbns=missed_isbns, high_priority=options.get("high_priority") + ) + edition_dicts.update(new_editions) + edition_dicts = process_result(edition_dicts, options.get('jscmd')) except: print("Error in processing Books API", file=sys.stderr) register_exception() - result = {} - return format_result(result, options) + edition_dicts = {} + return format_result(edition_dicts, options) if __name__ == "__main__": diff --git a/openlibrary/plugins/openlibrary/code.py b/openlibrary/plugins/openlibrary/code.py index daffbfdf829..456a41b3b77 100644 --- a/openlibrary/plugins/openlibrary/code.py +++ b/openlibrary/plugins/openlibrary/code.py @@ -2,6 +2,7 @@ Open Library Plugin. """ +from urllib.parse import parse_qs, urlparse, urlencode, urlunparse import requests import web import json @@ -461,10 +462,33 @@ def GET(self): return web.ok('OK') +def remove_high_priority(query: str) -> str: + """ + Remove `high_priority=true` and `high_priority=false` from query parameters, + as the API expects to pass URL parameters through to another query, and + these may interfere with that query. + + >>> remove_high_priority('high_priority=true&v=1') + 'v=1' + """ + query_params = parse_qs(query) + query_params.pop("high_priority", None) + new_query = urlencode(query_params, doseq=True) + return new_query + + class isbn_lookup(delegate.page): path = r'/(?:isbn|ISBN)/([0-9xX-]+)' def GET(self, isbn): + input = web.input(high_priority=False) + + high_priority = input.get("high_priority") == "true" + if "high_priority" in web.ctx.env.get('QUERY_STRING'): + web.ctx.env['QUERY_STRING'] = remove_high_priority( + web.ctx.env.get('QUERY_STRING') + ) + # Preserve the url type (e.g. `.json`) and query params ext = '' if web.ctx.encoding and web.ctx.path.endswith('.' + web.ctx.encoding): @@ -473,7 +497,7 @@ def GET(self, isbn): ext += '?' + web.ctx.env['QUERY_STRING'] try: - if ed := Edition.from_isbn(isbn): + if ed := Edition.from_isbn(isbn=isbn, high_priority=high_priority): return web.found(ed.key + ext) except Exception as e: logger.error(e) diff --git a/openlibrary/plugins/upstream/borrow.py b/openlibrary/plugins/upstream/borrow.py index 07f429fbc98..8557eceb0fe 100644 --- a/openlibrary/plugins/upstream/borrow.py +++ b/openlibrary/plugins/upstream/borrow.py @@ -17,6 +17,7 @@ from infogami.utils.view import public, render_template, add_flash_message from infogami.infobase.utils import parse_datetime +from openlibrary.core import models from openlibrary.core import stats from openlibrary.core import lending from openlibrary.core import vendors diff --git a/openlibrary/tests/core/test_vendors.py b/openlibrary/tests/core/test_vendors.py index 1804fd3f22d..37b285383d2 100644 --- a/openlibrary/tests/core/test_vendors.py +++ b/openlibrary/tests/core/test_vendors.py @@ -1,5 +1,8 @@ +from unittest.mock import patch + import pytest from openlibrary.core.vendors import ( + get_amazon_metadata, split_amazon_title, clean_amazon_metadata_for_load, betterworldbooks_fmt, @@ -192,3 +195,64 @@ def test_betterworldbooks_fmt(): # Test cases to add: # Multiple authors + + +def test_get_amazon_metadata() -> None: + """ + Mock a reply from the Amazon Products API so we can do a basic test for + get_amazon_metadata() and cached_get_amazon_metadata(). + """ + + class MockRequests: + def get(self): + pass + + def raise_for_status(self): + return True + + def json(self): + return mock_response + + mock_response = { + 'status': 'success', + 'hit': { + 'url': 'https://www.amazon.com/dp/059035342X/?tag=internetarchi-20', + 'source_records': ['amazon:059035342X'], + 'isbn_10': ['059035342X'], + 'isbn_13': ['9780590353427'], + 'price': '$5.10', + 'price_amt': 509, + 'title': "Harry Potter and the Sorcerer's Stone", + 'cover': 'https://m.media-amazon.com/images/I/51Wbz5GypgL._SL500_.jpg', + 'authors': [{'name': 'Rowling, J.K.'}, {'name': 'GrandPr_, Mary'}], + 'publishers': ['Scholastic'], + 'number_of_pages': 309, + 'edition_num': '1', + 'publish_date': 'Sep 02, 1998', + 'product_group': 'Book', + 'physical_format': 'paperback', + }, + } + expected = { + 'url': 'https://www.amazon.com/dp/059035342X/?tag=internetarchi-20', + 'source_records': ['amazon:059035342X'], + 'isbn_10': ['059035342X'], + 'isbn_13': ['9780590353427'], + 'price': '$5.10', + 'price_amt': 509, + 'title': "Harry Potter and the Sorcerer's Stone", + 'cover': 'https://m.media-amazon.com/images/I/51Wbz5GypgL._SL500_.jpg', + 'authors': [{'name': 'Rowling, J.K.'}, {'name': 'GrandPr_, Mary'}], + 'publishers': ['Scholastic'], + 'number_of_pages': 309, + 'edition_num': '1', + 'publish_date': 'Sep 02, 1998', + 'product_group': 'Book', + 'physical_format': 'paperback', + } + isbn = "059035342X" + with patch("requests.get", return_value=MockRequests()), patch( + "openlibrary.core.vendors.affiliate_server_url", new=True + ): + got = get_amazon_metadata(id_=isbn, id_type="isbn") + assert got == expected diff --git a/scripts/affiliate_server.py b/scripts/affiliate_server.py index c1c8ea71554..e615ff492be 100644 --- a/scripts/affiliate_server.py +++ b/scripts/affiliate_server.py @@ -33,7 +33,6 @@ products = web.amazon_api.get_products(["195302114X", "0312368615"], serialize=True) ``` """ - import itertools import json import logging @@ -42,7 +41,11 @@ import sys import threading import time -from datetime import date + +from dataclasses import dataclass, field +from datetime import datetime +from enum import Enum +from typing import Final import web @@ -52,7 +55,7 @@ from infogami import config from openlibrary.config import load_config as openlibrary_load_config from openlibrary.core import cache, stats -from openlibrary.core.imports import Batch +from openlibrary.core.imports import Batch, ImportItem from openlibrary.core.vendors import AmazonAPI, clean_amazon_metadata_for_load from openlibrary.utils.dateutil import WEEK_SECS from openlibrary.utils.isbn import ( @@ -81,22 +84,23 @@ 'publish_date': 'publish_date', 'number_of_pages': 'number_of_pages', } +RETRIES: Final = 5 -batch_name = "" batch: Batch | None = None -web.amazon_queue = queue.Queue() # a thread-safe multi-producer, multi-consumer queue +web.amazon_queue = ( + queue.PriorityQueue() +) # a thread-safe multi-producer, multi-consumer queue web.amazon_lookup_thread = None def get_current_amazon_batch() -> Batch: """ - At startup or when the month changes, create a new openlibrary.core.imports.Batch() + At startup, get the Amazon openlibrary.core.imports.Batch() for global use. """ - global batch_name, batch - if batch_name != (new_batch_name := f"amz-{date.today():%Y%m}"): - batch_name = new_batch_name - batch = Batch.find(batch_name) or Batch.new(batch_name) + global batch + if not batch: + batch = Batch.find("amz") or Batch.new("amz") assert batch return batch @@ -201,13 +205,17 @@ def process_amazon_batch(isbn_10s: list[str]) -> None: return books = [clean_amazon_metadata_for_load(product) for product in products] - if books and (pending_books := get_pending_books(books)): + + if books: stats.increment( "ol.affiliate.amazon.total_items_batched_for_import", - n=len(pending_books), + n=len(books), ) get_current_amazon_batch().add_items( - [{'ia_id': b['source_records'][0], 'data': b} for b in pending_books] + [ + {'ia_id': b['source_records'][0], 'status': 'staged', 'data': b} + for b in books + ] ) @@ -230,7 +238,7 @@ def amazon_lookup(site, stats_client, logger) -> None: while len(isbn_10s) < API_MAX_ITEMS_PER_CALL and seconds_remaining(start_time): try: # queue.get() will block (sleep) until successful or it times out isbn_10s.add( - web.amazon_queue.get(timeout=seconds_remaining(start_time)) + web.amazon_queue.get(timeout=seconds_remaining(start_time)).isbn ) except queue.Empty: pass @@ -282,6 +290,45 @@ def GET(self) -> str: return json.dumps({"Cleared": "True", "qsize": qsize}) +class Priority(Enum): + """ + Priority for the `PrioritizedISBN` class. + + `queue.PriorityQueue` has a lowest-value-is-highest-priority system, but + setting `PrioritizedISBN.priority` to 0 can make it look as if priority is + disabled. Using an `Enum` can help with that. + """ + + HIGH = 0 + LOW = 1 + + def __lt__(self, other): + if isinstance(other, Priority): + return self.value < other.value + return NotImplemented + + +@dataclass(order=True, slots=True) +class PrioritizedISBN: + """ + Represent an ISBN's priority in the queue. Sorting is based on the `priority` + attribute, then the `timestamp` to solve tie breaks within a specific priority, + with priority going to whatever `min([items])` would return. + For more, see https://docs.python.org/3/library/queue.html#queue.PriorityQueue. + + Therefore, priority 0, which is equivalent to `Priority.HIGH`, is the highest + priority. + + This exists so certain ISBNs can go to the front of the queue for faster + processing as their look-ups are time sensitive and should return look up data + to the caller (e.g. interactive API usage through `/isbn`). + """ + + isbn: str = field(compare=False) + priority: Priority = field(default=Priority.LOW) + timestamp: datetime = field(default_factory=datetime.now) + + class Submit: @classmethod def unpack_isbn(cls, isbn) -> tuple[str, str]: @@ -296,8 +343,16 @@ def unpack_isbn(cls, isbn) -> tuple[str, str]: def GET(self, isbn: str) -> str: """ - If isbn is in memcache then return the `hit`. If not then queue the isbn to be - looked up and return the equivalent of a promise as `submitted` + If `isbn` is in memcache, then return the `hit` (which is marshaled into + a format appropriate for import on Open Library if `?high_priority=true`). + + If no hit, then queue the isbn for look up and either attempt to return + a promise as `submitted`, or if `?high_priority=true`, return marshalled data + from the cache. + + `Priority.HIGH` is set when `?high_priority=true` and is the highest priority. + It is used when the caller is waiting for a response with the AMZ data, if + available. See `PrioritizedISBN` for more on prioritization. """ # cache could be None if reached before initialized (mypy) if not web.amazon_api: @@ -309,13 +364,25 @@ def GET(self, isbn: str) -> str: {"error": "rejected_isbn", "isbn10": isbn10, "isbn13": isbn13} ) + input = web.input(high_priority=False) + priority = ( + Priority.HIGH if input.get("high_priority") == "true" else Priority.LOW + ) + # Cache lookup by isbn13. If there's a hit return the product to the caller if product := cache.memcache_cache.get(f'amazon_product_{isbn13}'): - return json.dumps({"status": "success", "hit": product}) + return json.dumps( + { + "status": "success", + "hit": product, + } + ) - # Cache misses will be submitted to Amazon as ASINs (isbn10) + # Cache misses will be submitted to Amazon as ASINs (isbn10) and the response + # will be `staged` for import. if isbn10 not in web.amazon_queue.queue: - web.amazon_queue.put_nowait(isbn10) + isbn10_queue_item = PrioritizedISBN(isbn=isbn10, priority=priority) + web.amazon_queue.put_nowait(isbn10_queue_item) # Give us a snapshot over time of how many new isbns are currently queued stats.put( @@ -323,7 +390,27 @@ def GET(self, isbn: str) -> str: web.amazon_queue.qsize(), rate=0.2, ) - return json.dumps({"status": "submitted", "queue": web.amazon_queue.qsize()}) + + # Check the cache a few times for product data to return to the client, + # or otherwise return. + if priority == Priority.HIGH: + for _ in range(RETRIES): + time.sleep(1) + if product := cache.memcache_cache.get(f'amazon_product_{isbn13}'): + cleaned_metadata = clean_amazon_metadata_for_load(product) + source, pid = cleaned_metadata['source_records'][0].split(":") + if ImportItem.find_staged_or_pending( + identifiers=[pid], sources=[source] + ): + return json.dumps({"status": "success", "hit": product}) + + stats.increment("ol.affiliate.amazon.total_items_not_found") + return json.dumps({"status": "not found"}) + + else: + return json.dumps( + {"status": "submitted", "queue": web.amazon_queue.qsize()} + ) def load_config(configfile):