Merge branch 'main' into production

This commit is contained in:
Mouse Reeve 2021-12-29 09:35:08 -08:00
commit 638352ba26
45 changed files with 2638 additions and 2302 deletions

24
.github/workflows/prettier.yaml vendored Normal file
View file

@ -0,0 +1,24 @@
# @url https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions
name: JavaScript Prettier (run ./bw-dev prettier to fix)
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
lint:
name: Lint with Prettier
runs-on: ubuntu-20.04
steps:
# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it.
- uses: actions/checkout@v2
- name: Install modules
run: npm install .
# See .stylelintignore for files that are not linted.
- name: Run Prettier
run: npx prettier --check bookwyrm/static/js/*.js

View file

@ -14,7 +14,8 @@ class LibrarythingImporter(Importer):
"""use the dataclass to create the formatted row of data"""
remove_brackets = lambda v: re.sub(r"\[|\]", "", v) if v else None
normalized = {k: remove_brackets(entry.get(v)) for k, v in mappings.items()}
isbn_13 = normalized["isbn_13"].split(", ")
isbn_13 = normalized.get("isbn_13")
isbn_13 = isbn_13.split(", ") if isbn_13 else []
normalized["isbn_13"] = isbn_13[1] if len(isbn_13) > 0 else None
return normalized

View file

@ -6,7 +6,7 @@ from bookwyrm import activitystreams, models
def populate_streams(stream=None):
"""build all the streams for all the users"""
streams = [stream] if stream else activitystreams.streams.keys()
print("Populations streams", streams)
print("Populating streams", streams)
users = models.User.objects.filter(
local=True,
is_active=True,

View file

@ -84,6 +84,7 @@ class BookWyrmModel(models.Model):
# you can see groups of which you are a member
if (
hasattr(self, "memberships")
and viewer.is_authenticated
and self.memberships.filter(user=viewer).exists()
):
return

View file

@ -125,9 +125,9 @@ STREAMS = [
DATABASES = {
"default": {
"ENGINE": "django.db.backends.postgresql_psycopg2",
"NAME": env("POSTGRES_DB", "fedireads"),
"USER": env("POSTGRES_USER", "fedireads"),
"PASSWORD": env("POSTGRES_PASSWORD", "fedireads"),
"NAME": env("POSTGRES_DB", "bookwyrm"),
"USER": env("POSTGRES_USER", "bookwyrm"),
"PASSWORD": env("POSTGRES_PASSWORD", "bookwyrm"),
"HOST": env("POSTGRES_HOST", ""),
"PORT": env("PGPORT", 5432),
},

View file

@ -8,6 +8,41 @@ body {
flex-direction: column;
}
button {
border: none;
margin: 0;
padding: 0;
width: auto;
overflow: visible;
background: transparent;
/* inherit font, color & alignment from ancestor */
color: inherit;
font: inherit;
text-align: inherit;
/* Normalize `line-height`. Cannot be changed from `normal` in Firefox 4+. */
line-height: normal;
/* Corrects font smoothing for webkit */
-webkit-font-smoothing: inherit;
-moz-osx-font-smoothing: inherit;
/* Corrects inability to style clickable `input` types in iOS */
-webkit-appearance: none;
}
button::-moz-focus-inner {
/* Remove excess padding and border in Firefox 4+ */
border: 0;
padding: 0;
}
/* Better accessibility for keyboard users */
*:focus-visible {
outline-style: auto !important;
}
.image {
overflow: hidden;
}
@ -29,10 +64,30 @@ body {
overflow-x: auto;
}
/* stylelint-disable no-descending-specificity */
.modal-card:focus {
outline-style: auto;
}
.modal-card:focus:not(:focus-visible) {
outline-style: initial;
}
.modal-card:focus-visible {
outline-style: auto;
}
/* stylelint-enable no-descending-specificity */
.modal-card.is-fullwidth {
min-width: 75% !important;
}
@media only screen and (min-width: 769px) {
.modal-card.is-thin {
width: 350px !important;
}
}
.modal-card-body {
max-height: 70vh;
}
@ -612,8 +667,8 @@ ol.ordered-list li::before {
.books-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(10em, 1fr));
gap: 1.5rem;
grid-template-columns: repeat(2, 1fr);
gap: 1rem;
align-items: end;
justify-items: stretch;
}
@ -622,7 +677,6 @@ ol.ordered-list li::before {
grid-column: span 2;
grid-row: span 2;
justify-self: stretch;
padding: 1.5rem 1.5rem 0;
}
.books-grid .book-cover {
@ -638,6 +692,17 @@ ol.ordered-list li::before {
min-height: calc(2 * var(--height-basis));
}
@media only screen and (min-width: 769px) {
.books-grid {
gap: 1.5rem;
grid-template-columns: repeat(auto-fit, minmax(10em, 1fr));
}
.books-grid > .is-big {
padding: 1.5rem 1.5rem 0;
}
}
/* Copy
******************************************************************************/

View file

@ -1,9 +1,10 @@
/* exported BlockHref */
let BlockHref = new class {
let BlockHref = new (class {
constructor() {
document.querySelectorAll('[data-href]')
.forEach(t => t.addEventListener('click', this.followLink.bind(this)));
document
.querySelectorAll("[data-href]")
.forEach((t) => t.addEventListener("click", this.followLink.bind(this)));
}
/**
@ -17,5 +18,4 @@ let BlockHref = new class {
window.location.href = url;
}
}();
})();

View file

@ -1,7 +1,7 @@
/* exported BookWyrm */
/* globals TabGroup */
let BookWyrm = new class {
let BookWyrm = new (class {
constructor() {
this.MAX_FILE_SIZE_BYTES = 10 * 1000000;
this.initOnDOMLoaded();
@ -10,54 +10,42 @@ let BookWyrm = new class {
}
initEventListeners() {
document.querySelectorAll('[data-controls]')
.forEach(button => button.addEventListener(
'click',
this.toggleAction.bind(this))
);
document
.querySelectorAll("[data-controls]")
.forEach((button) => button.addEventListener("click", this.toggleAction.bind(this)));
document.querySelectorAll('.interaction')
.forEach(button => button.addEventListener(
'submit',
this.interact.bind(this))
);
document
.querySelectorAll(".interaction")
.forEach((button) => button.addEventListener("submit", this.interact.bind(this)));
document.querySelectorAll('.hidden-form input')
.forEach(button => button.addEventListener(
'change',
this.revealForm.bind(this))
);
document
.querySelectorAll(".hidden-form input")
.forEach((button) => button.addEventListener("change", this.revealForm.bind(this)));
document.querySelectorAll('[data-hides]')
.forEach(button => button.addEventListener(
'change',
this.hideForm.bind(this))
);
document
.querySelectorAll("[data-hides]")
.forEach((button) => button.addEventListener("change", this.hideForm.bind(this)));
document.querySelectorAll('[data-back]')
.forEach(button => button.addEventListener(
'click',
this.back)
);
document
.querySelectorAll("[data-back]")
.forEach((button) => button.addEventListener("click", this.back));
document.querySelectorAll('input[type="file"]')
.forEach(node => node.addEventListener(
'change',
this.disableIfTooLarge.bind(this)
));
document.querySelectorAll('[data-duplicate]')
.forEach(node => node.addEventListener(
'click',
this.duplicateInput.bind(this)
))
document.querySelectorAll('details.dropdown')
.forEach(node => node.addEventListener(
'toggle',
this.handleDetailsDropdown.bind(this)
))
document
.querySelectorAll('input[type="file"]')
.forEach((node) => node.addEventListener("change", this.disableIfTooLarge.bind(this)));
document
.querySelectorAll("button[data-modal-open]")
.forEach((node) => node.addEventListener("click", this.handleModalButton.bind(this)));
document
.querySelectorAll("[data-duplicate]")
.forEach((node) => node.addEventListener("click", this.duplicateInput.bind(this)));
document
.querySelectorAll("details.dropdown")
.forEach((node) =>
node.addEventListener("toggle", this.handleDetailsDropdown.bind(this))
);
}
/**
@ -66,15 +54,12 @@ let BookWyrm = new class {
initOnDOMLoaded() {
const bookwyrm = this;
window.addEventListener('DOMContentLoaded', function() {
document.querySelectorAll('.tab-group')
.forEach(tabs => new TabGroup(tabs));
document.querySelectorAll('input[type="file"]').forEach(
bookwyrm.disableIfTooLarge.bind(bookwyrm)
);
document.querySelectorAll('[data-copytext]').forEach(
bookwyrm.copyText.bind(bookwyrm)
);
window.addEventListener("DOMContentLoaded", function () {
document.querySelectorAll(".tab-group").forEach((tabs) => new TabGroup(tabs));
document
.querySelectorAll('input[type="file"]')
.forEach(bookwyrm.disableIfTooLarge.bind(bookwyrm));
document.querySelectorAll("[data-copytext]").forEach(bookwyrm.copyText.bind(bookwyrm));
});
}
@ -83,8 +68,7 @@ let BookWyrm = new class {
*/
initReccuringTasks() {
// Polling
document.querySelectorAll('[data-poll]')
.forEach(liveArea => this.polling(liveArea));
document.querySelectorAll("[data-poll]").forEach((liveArea) => this.polling(liveArea));
}
/**
@ -110,15 +94,19 @@ let BookWyrm = new class {
const bookwyrm = this;
delay = delay || 10000;
delay += (Math.random() * 1000);
delay += Math.random() * 1000;
setTimeout(function() {
fetch('/api/updates/' + counter.dataset.poll)
.then(response => response.json())
.then(data => bookwyrm.updateCountElement(counter, data));
setTimeout(
function () {
fetch("/api/updates/" + counter.dataset.poll)
.then((response) => response.json())
.then((data) => bookwyrm.updateCountElement(counter, data));
bookwyrm.polling(counter, delay * 1.25);
}, delay, counter);
bookwyrm.polling(counter, delay * 1.25);
},
delay,
counter
);
}
/**
@ -133,60 +121,56 @@ let BookWyrm = new class {
const count_by_type = data.count_by_type;
const currentCount = counter.innerText;
const hasMentions = data.has_mentions;
const allowedStatusTypesEl = document.getElementById('unread-notifications-wrapper');
const allowedStatusTypesEl = document.getElementById("unread-notifications-wrapper");
// If we're on the right counter element
if (counter.closest('[data-poll-wrapper]').contains(allowedStatusTypesEl)) {
if (counter.closest("[data-poll-wrapper]").contains(allowedStatusTypesEl)) {
const allowedStatusTypes = JSON.parse(allowedStatusTypesEl.textContent);
// For keys in common between allowedStatusTypes and count_by_type
// This concerns 'review', 'quotation', 'comment'
count = allowedStatusTypes.reduce(function(prev, currentKey) {
count = allowedStatusTypes.reduce(function (prev, currentKey) {
const currentValue = count_by_type[currentKey] | 0;
return prev + currentValue;
}, 0);
// Add all the "other" in count_by_type if 'everything' is allowed
if (allowedStatusTypes.includes('everything')) {
if (allowedStatusTypes.includes("everything")) {
// Clone count_by_type with 0 for reviews/quotations/comments
const count_by_everything_else = Object.assign(
{},
count_by_type,
{review: 0, quotation: 0, comment: 0}
);
const count_by_everything_else = Object.assign({}, count_by_type, {
review: 0,
quotation: 0,
comment: 0,
});
count = Object.keys(count_by_everything_else).reduce(
function(prev, currentKey) {
const currentValue =
count_by_everything_else[currentKey] | 0
count = Object.keys(count_by_everything_else).reduce(function (prev, currentKey) {
const currentValue = count_by_everything_else[currentKey] | 0;
return prev + currentValue;
},
count
);
return prev + currentValue;
}, count);
}
}
if (count != currentCount) {
this.addRemoveClass(counter.closest('[data-poll-wrapper]'), 'is-hidden', count < 1);
this.addRemoveClass(counter.closest("[data-poll-wrapper]"), "is-hidden", count < 1);
counter.innerText = count;
this.addRemoveClass(counter.closest('[data-poll-wrapper]'), 'is-danger', hasMentions);
this.addRemoveClass(counter.closest("[data-poll-wrapper]"), "is-danger", hasMentions);
}
}
/**
* Show form.
*
*
* @param {Event} event
* @return {undefined}
*/
revealForm(event) {
let trigger = event.currentTarget;
let hidden = trigger.closest('.hidden-form').querySelectorAll('.is-hidden')[0];
let hidden = trigger.closest(".hidden-form").querySelectorAll(".is-hidden")[0];
if (hidden) {
this.addRemoveClass(hidden, 'is-hidden', !hidden);
this.addRemoveClass(hidden, "is-hidden", !hidden);
}
}
@ -198,10 +182,10 @@ let BookWyrm = new class {
*/
hideForm(event) {
let trigger = event.currentTarget;
let targetId = trigger.dataset.hides
let visible = document.getElementById(targetId)
let targetId = trigger.dataset.hides;
let visible = document.getElementById(targetId);
this.addRemoveClass(visible, 'is-hidden', true);
this.addRemoveClass(visible, "is-hidden", true);
}
/**
@ -216,31 +200,34 @@ let BookWyrm = new class {
if (!trigger.dataset.allowDefault || event.currentTarget == event.target) {
event.preventDefault();
}
let pressed = trigger.getAttribute('aria-pressed') === 'false';
let pressed = trigger.getAttribute("aria-pressed") === "false";
let targetId = trigger.dataset.controls;
// Toggle pressed status on all triggers controlling the same target.
document.querySelectorAll('[data-controls="' + targetId + '"]')
.forEach(otherTrigger => otherTrigger.setAttribute(
'aria-pressed',
otherTrigger.getAttribute('aria-pressed') === 'false'
));
document
.querySelectorAll('[data-controls="' + targetId + '"]')
.forEach((otherTrigger) =>
otherTrigger.setAttribute(
"aria-pressed",
otherTrigger.getAttribute("aria-pressed") === "false"
)
);
// @todo Find a better way to handle the exception.
if (targetId && ! trigger.classList.contains('pulldown-menu')) {
if (targetId && !trigger.classList.contains("pulldown-menu")) {
let target = document.getElementById(targetId);
this.addRemoveClass(target, 'is-hidden', !pressed);
this.addRemoveClass(target, 'is-active', pressed);
this.addRemoveClass(target, "is-hidden", !pressed);
this.addRemoveClass(target, "is-active", pressed);
}
// Show/hide pulldown-menus.
if (trigger.classList.contains('pulldown-menu')) {
if (trigger.classList.contains("pulldown-menu")) {
this.toggleMenu(trigger, targetId);
}
// Show/hide container.
let container = document.getElementById('hide_' + targetId);
let container = document.getElementById("hide_" + targetId);
if (container) {
this.toggleContainer(container, pressed);
@ -277,14 +264,14 @@ let BookWyrm = new class {
* @return {undefined}
*/
toggleMenu(trigger, targetId) {
let expanded = trigger.getAttribute('aria-expanded') == 'false';
let expanded = trigger.getAttribute("aria-expanded") == "false";
trigger.setAttribute('aria-expanded', expanded);
trigger.setAttribute("aria-expanded", expanded);
if (targetId) {
let target = document.getElementById(targetId);
this.addRemoveClass(target, 'is-active', expanded);
this.addRemoveClass(target, "is-active", expanded);
}
}
@ -296,7 +283,7 @@ let BookWyrm = new class {
* @return {undefined}
*/
toggleContainer(container, pressed) {
this.addRemoveClass(container, 'is-hidden', pressed);
this.addRemoveClass(container, "is-hidden", pressed);
}
/**
@ -333,7 +320,7 @@ let BookWyrm = new class {
node.focus();
setTimeout(function() {
setTimeout(function () {
node.selectionStart = node.selectionEnd = 10000;
}, 0);
}
@ -353,15 +340,17 @@ let BookWyrm = new class {
const relatedforms = document.querySelectorAll(`.${form.dataset.id}`);
// Toggle class on all related forms.
relatedforms.forEach(relatedForm => bookwyrm.addRemoveClass(
relatedForm,
'is-hidden',
relatedForm.className.indexOf('is-hidden') == -1
));
relatedforms.forEach((relatedForm) =>
bookwyrm.addRemoveClass(
relatedForm,
"is-hidden",
relatedForm.className.indexOf("is-hidden") == -1
)
);
this.ajaxPost(form).catch(error => {
this.ajaxPost(form).catch((error) => {
// @todo Display a notification in the UI instead.
console.warn('Request failed:', error);
console.warn("Request failed:", error);
});
}
@ -373,11 +362,11 @@ let BookWyrm = new class {
*/
ajaxPost(form) {
return fetch(form.action, {
method : "POST",
method: "POST",
body: new FormData(form),
headers: {
'Accept': 'application/json',
}
Accept: "application/json",
},
});
}
@ -402,24 +391,106 @@ let BookWyrm = new class {
const element = eventOrElement.currentTarget || eventOrElement;
const submits = element.form.querySelectorAll('[type="submit"]');
const warns = element.parentElement.querySelectorAll('.file-too-big');
const isTooBig = element.files &&
element.files[0] &&
element.files[0].size > MAX_FILE_SIZE_BYTES;
const warns = element.parentElement.querySelectorAll(".file-too-big");
const isTooBig =
element.files && element.files[0] && element.files[0].size > MAX_FILE_SIZE_BYTES;
if (isTooBig) {
submits.forEach(submitter => submitter.disabled = true);
warns.forEach(
sib => addRemoveClass(sib, 'is-hidden', false)
);
submits.forEach((submitter) => (submitter.disabled = true));
warns.forEach((sib) => addRemoveClass(sib, "is-hidden", false));
} else {
submits.forEach(submitter => submitter.disabled = false);
warns.forEach(
sib => addRemoveClass(sib, 'is-hidden', true)
);
submits.forEach((submitter) => (submitter.disabled = false));
warns.forEach((sib) => addRemoveClass(sib, "is-hidden", true));
}
}
/**
* Handle the modal component.
*
* @param {Event} event - Event fired by an element
* with the `data-modal-open` attribute
* pointing to a modal by its id.
* @return {undefined}
*
* See https://github.com/bookwyrm-social/bookwyrm/pull/1633
* for information about using the modal.
*/
handleModalButton(event) {
const modalButton = event.currentTarget;
const targetModalId = modalButton.dataset.modalOpen;
const htmlElement = document.querySelector("html");
const modal = document.getElementById(targetModalId);
if (!modal) {
return;
}
// Helper functions
function handleModalOpen(modalElement) {
htmlElement.classList.add("is-clipped");
modalElement.classList.add("is-active");
modalElement.getElementsByClassName("modal-card")[0].focus();
const closeButtons = modalElement.querySelectorAll("[data-modal-close]");
closeButtons.forEach((button) => {
button.addEventListener("click", function () {
handleModalClose(modalElement);
});
});
document.addEventListener("keydown", function (event) {
if (event.key === "Escape") {
handleModalClose(modalElement);
}
});
modalElement.addEventListener("keydown", handleFocusTrap);
}
function handleModalClose(modalElement) {
modalElement.removeEventListener("keydown", handleFocusTrap);
htmlElement.classList.remove("is-clipped");
modalElement.classList.remove("is-active");
modalButton.focus();
}
function handleFocusTrap(event) {
if (event.key !== "Tab") {
return;
}
const focusableEls = event.currentTarget.querySelectorAll(
[
"a[href]:not([disabled])",
"button:not([disabled])",
"textarea:not([disabled])",
'input:not([type="hidden"]):not([disabled])',
"select:not([disabled])",
"details:not([disabled])",
'[tabindex]:not([tabindex="-1"]):not([disabled])',
].join(",")
);
const firstFocusableEl = focusableEls[0];
const lastFocusableEl = focusableEls[focusableEls.length - 1];
if (event.shiftKey) {
/* Shift + tab */ if (document.activeElement === firstFocusableEl) {
lastFocusableEl.focus();
event.preventDefault();
}
} /* Tab */ else {
if (document.activeElement === lastFocusableEl) {
firstFocusableEl.focus();
event.preventDefault();
}
}
}
// Open modal
handleModalOpen(modal);
}
/**
* Display pop up window.
*
@ -428,31 +499,27 @@ let BookWyrm = new class {
* @return {undefined}
*/
displayPopUp(url, windowName) {
window.open(
url,
windowName,
"left=100,top=100,width=430,height=600"
);
window.open(url, windowName, "left=100,top=100,width=430,height=600");
}
duplicateInput (event ) {
duplicateInput(event) {
const trigger = event.currentTarget;
const input_id = trigger.dataset['duplicate']
const input_id = trigger.dataset["duplicate"];
const orig = document.getElementById(input_id);
const parent = orig.parentNode;
const new_count = parent.querySelectorAll("input").length + 1
const new_count = parent.querySelectorAll("input").length + 1;
let input = orig.cloneNode();
input.id += ("-" + (new_count))
input.value = ""
input.id += "-" + new_count;
input.value = "";
let label = parent.querySelector("label").cloneNode();
label.setAttribute("for", input.id)
label.setAttribute("for", input.id);
parent.appendChild(label)
parent.appendChild(input)
parent.appendChild(label);
parent.appendChild(input);
}
/**
@ -467,24 +534,19 @@ let BookWyrm = new class {
copyText(textareaEl) {
const text = textareaEl.textContent;
const copyButtonEl = document.createElement('button');
const copyButtonEl = document.createElement("button");
copyButtonEl.textContent = textareaEl.dataset.copytextLabel;
copyButtonEl.classList.add(
"button",
"is-small",
"is-primary",
"is-light"
);
copyButtonEl.addEventListener('click', () => {
navigator.clipboard.writeText(text).then(function() {
textareaEl.classList.add('is-success');
copyButtonEl.classList.replace('is-primary', 'is-success');
copyButtonEl.classList.add("button", "is-small", "is-primary", "is-light");
copyButtonEl.addEventListener("click", () => {
navigator.clipboard.writeText(text).then(function () {
textareaEl.classList.add("is-success");
copyButtonEl.classList.replace("is-primary", "is-success");
copyButtonEl.textContent = textareaEl.dataset.copytextSuccess;
});
});
textareaEl.parentNode.appendChild(copyButtonEl)
textareaEl.parentNode.appendChild(copyButtonEl);
}
/**
@ -496,41 +558,41 @@ let BookWyrm = new class {
*/
handleDetailsDropdown(event) {
const detailsElement = event.target;
const summaryElement = detailsElement.querySelector('summary');
const menuElement = detailsElement.querySelector('.dropdown-menu');
const htmlElement = document.querySelector('html');
const summaryElement = detailsElement.querySelector("summary");
const menuElement = detailsElement.querySelector(".dropdown-menu");
const htmlElement = document.querySelector("html");
if (detailsElement.open) {
// Focus first menu element
menuElement.querySelectorAll(
'a[href]:not([disabled]), button:not([disabled])'
)[0].focus();
menuElement
.querySelectorAll("a[href]:not([disabled]), button:not([disabled])")[0]
.focus();
// Enable focus trap
menuElement.addEventListener('keydown', this.handleFocusTrap);
menuElement.addEventListener("keydown", this.handleFocusTrap);
// Close on Esc
detailsElement.addEventListener('keydown', handleEscKey);
detailsElement.addEventListener("keydown", handleEscKey);
// Clip page if Mobile
if (this.isMobile()) {
htmlElement.classList.add('is-clipped');
htmlElement.classList.add("is-clipped");
}
} else {
summaryElement.focus();
// Disable focus trap
menuElement.removeEventListener('keydown', this.handleFocusTrap);
menuElement.removeEventListener("keydown", this.handleFocusTrap);
// Unclip page
if (this.isMobile()) {
htmlElement.classList.remove('is-clipped');
htmlElement.classList.remove("is-clipped");
}
}
function handleEscKey(event) {
if (event.key !== 'Escape') {
return;
if (event.key !== "Escape") {
return;
}
summaryElement.click();
@ -553,34 +615,34 @@ let BookWyrm = new class {
* @return {undefined}
*/
handleFocusTrap(event) {
if (event.key !== 'Tab') {
return;
if (event.key !== "Tab") {
return;
}
const focusableEls = event.currentTarget.querySelectorAll(
[
'a[href]:not([disabled])',
'button:not([disabled])',
'textarea:not([disabled])',
"a[href]:not([disabled])",
"button:not([disabled])",
"textarea:not([disabled])",
'input:not([type="hidden"]):not([disabled])',
'select:not([disabled])',
'details:not([disabled])',
'[tabindex]:not([tabindex="-1"]):not([disabled])'
].join(',')
"select:not([disabled])",
"details:not([disabled])",
'[tabindex]:not([tabindex="-1"]):not([disabled])',
].join(",")
);
const firstFocusableEl = focusableEls[0];
const firstFocusableEl = focusableEls[0];
const lastFocusableEl = focusableEls[focusableEls.length - 1];
if (event.shiftKey ) /* Shift + tab */ {
if (document.activeElement === firstFocusableEl) {
if (event.shiftKey) {
/* Shift + tab */ if (document.activeElement === firstFocusableEl) {
lastFocusableEl.focus();
event.preventDefault();
}
} else /* Tab */ {
} /* Tab */ else {
if (document.activeElement === lastFocusableEl) {
firstFocusableEl.focus();
event.preventDefault();
}
}
}
}();
})();

View file

@ -1,13 +1,13 @@
/* exported LocalStorageTools */
/* globals BookWyrm */
let LocalStorageTools = new class {
let LocalStorageTools = new (class {
constructor() {
document.querySelectorAll('[data-hide]')
.forEach(t => this.setDisplay(t));
document.querySelectorAll("[data-hide]").forEach((t) => this.setDisplay(t));
document.querySelectorAll('.set-display')
.forEach(t => t.addEventListener('click', this.updateDisplay.bind(this)));
document
.querySelectorAll(".set-display")
.forEach((t) => t.addEventListener("click", this.updateDisplay.bind(this)));
}
/**
@ -23,8 +23,9 @@ let LocalStorageTools = new class {
window.localStorage.setItem(key, value);
document.querySelectorAll('[data-hide="' + key + '"]')
.forEach(node => this.setDisplay(node));
document
.querySelectorAll('[data-hide="' + key + '"]')
.forEach((node) => this.setDisplay(node));
}
/**
@ -38,6 +39,6 @@ let LocalStorageTools = new class {
let key = node.dataset.hide;
let value = window.localStorage.getItem(key);
BookWyrm.addRemoveClass(node, 'is-hidden', value);
BookWyrm.addRemoveClass(node, "is-hidden", value);
}
}();
})();

View file

@ -1,22 +1,21 @@
/* exported StatusCache */
/* globals BookWyrm */
let StatusCache = new class {
let StatusCache = new (class {
constructor() {
document.querySelectorAll('[data-cache-draft]')
.forEach(t => t.addEventListener('change', this.updateDraft.bind(this)));
document
.querySelectorAll("[data-cache-draft]")
.forEach((t) => t.addEventListener("change", this.updateDraft.bind(this)));
document.querySelectorAll('[data-cache-draft]')
.forEach(t => this.populateDraft(t));
document.querySelectorAll("[data-cache-draft]").forEach((t) => this.populateDraft(t));
document.querySelectorAll('.submit-status')
.forEach(button => button.addEventListener(
'submit',
this.submitStatus.bind(this))
);
document
.querySelectorAll(".submit-status")
.forEach((button) => button.addEventListener("submit", this.submitStatus.bind(this)));
document.querySelectorAll('.form-rate-stars label.icon')
.forEach(button => button.addEventListener('click', this.toggleStar.bind(this)));
document
.querySelectorAll(".form-rate-stars label.icon")
.forEach((button) => button.addEventListener("click", this.toggleStar.bind(this)));
}
/**
@ -80,25 +79,26 @@ let StatusCache = new class {
event.preventDefault();
BookWyrm.addRemoveClass(form, 'is-processing', true);
trigger.setAttribute('disabled', null);
BookWyrm.addRemoveClass(form, "is-processing", true);
trigger.setAttribute("disabled", null);
BookWyrm.ajaxPost(form).finally(() => {
// Change icon to remove ongoing activity on the current UI.
// Enable back the element used to submit the form.
BookWyrm.addRemoveClass(form, 'is-processing', false);
trigger.removeAttribute('disabled');
})
.then(response => {
if (!response.ok) {
throw new Error();
}
this.submitStatusSuccess(form);
})
.catch(error => {
console.warn(error);
this.announceMessage('status-error-message');
});
BookWyrm.ajaxPost(form)
.finally(() => {
// Change icon to remove ongoing activity on the current UI.
// Enable back the element used to submit the form.
BookWyrm.addRemoveClass(form, "is-processing", false);
trigger.removeAttribute("disabled");
})
.then((response) => {
if (!response.ok) {
throw new Error();
}
this.submitStatusSuccess(form);
})
.catch((error) => {
console.warn(error);
this.announceMessage("status-error-message");
});
}
/**
@ -112,12 +112,16 @@ let StatusCache = new class {
let copy = element.cloneNode(true);
copy.id = null;
element.insertAdjacentElement('beforebegin', copy);
element.insertAdjacentElement("beforebegin", copy);
BookWyrm.addRemoveClass(copy, 'is-hidden', false);
setTimeout(function() {
copy.remove();
}, 10000, copy);
BookWyrm.addRemoveClass(copy, "is-hidden", false);
setTimeout(
function () {
copy.remove();
},
10000,
copy
);
}
/**
@ -131,8 +135,9 @@ let StatusCache = new class {
form.reset();
// Clear localstorage
form.querySelectorAll('[data-cache-draft]')
.forEach(node => window.localStorage.removeItem(node.dataset.cacheDraft));
form.querySelectorAll("[data-cache-draft]").forEach((node) =>
window.localStorage.removeItem(node.dataset.cacheDraft)
);
// Close modals
let modal = form.closest(".modal.is-active");
@ -142,8 +147,11 @@ let StatusCache = new class {
// Update shelve buttons
if (form.reading_status) {
document.querySelectorAll("[data-shelve-button-book='" + form.book.value +"']")
.forEach(button => this.cycleShelveButtons(button, form.reading_status.value));
document
.querySelectorAll("[data-shelve-button-book='" + form.book.value + "']")
.forEach((button) =>
this.cycleShelveButtons(button, form.reading_status.value)
);
}
return;
@ -156,7 +164,7 @@ let StatusCache = new class {
document.querySelector("[data-controls=" + reply.id + "]").click();
}
this.announceMessage('status-success-message');
this.announceMessage("status-success-message");
}
/**
@ -172,8 +180,9 @@ let StatusCache = new class {
let next_identifier = shelf.dataset.shelfNext;
// Set all buttons to hidden
button.querySelectorAll("[data-shelf-identifier]")
.forEach(item => BookWyrm.addRemoveClass(item, "is-hidden", true));
button
.querySelectorAll("[data-shelf-identifier]")
.forEach((item) => BookWyrm.addRemoveClass(item, "is-hidden", true));
// Button that should be visible now
let next = button.querySelector("[data-shelf-identifier=" + next_identifier + "]");
@ -183,15 +192,17 @@ let StatusCache = new class {
// ------ update the dropdown buttons
// Remove existing hidden class
button.querySelectorAll("[data-shelf-dropdown-identifier]")
.forEach(item => BookWyrm.addRemoveClass(item, "is-hidden", false));
button
.querySelectorAll("[data-shelf-dropdown-identifier]")
.forEach((item) => BookWyrm.addRemoveClass(item, "is-hidden", false));
// Remove existing disabled states
button.querySelectorAll("[data-shelf-dropdown-identifier] button")
.forEach(item => item.disabled = false);
button
.querySelectorAll("[data-shelf-dropdown-identifier] button")
.forEach((item) => (item.disabled = false));
next_identifier = next_identifier == 'complete' ? 'read' : next_identifier;
next_identifier = next_identifier == "complete" ? "read" : next_identifier;
// Disable the current state
button.querySelector(
@ -206,8 +217,9 @@ let StatusCache = new class {
BookWyrm.addRemoveClass(main_button, "is-hidden", true);
// Just hide the other two menu options, idk what to do with them
button.querySelectorAll("[data-extra-options]")
.forEach(item => BookWyrm.addRemoveClass(item, "is-hidden", true));
button
.querySelectorAll("[data-extra-options]")
.forEach((item) => BookWyrm.addRemoveClass(item, "is-hidden", true));
// Close menu
let menu = button.querySelector("details[open]");
@ -235,5 +247,4 @@ let StatusCache = new class {
halfStar.checked = "checked";
}
}
}();
})();

View file

@ -58,7 +58,15 @@
{% if year_key %}
<div class="horizontal-copy mb-5">
<textarea rows="1" readonly class="textarea is-small" aria-labelledby="embed-label" data-copytext data-copytext-label="{% trans 'Copy address' %}" data-copytext-success="{% trans 'Copied!' %}">{{ request.scheme|add:"://"|add:request.get_host|add:request.path }}?key={{ year_key }}</textarea>
<textarea
rows="1"
readonly
class="textarea is-small"
aria-labelledby="embed-label"
data-copytext
data-copytext-label="{% trans 'Copy address' %}"
data-copytext-success="{% trans 'Copied!' %}"
>{{ request.scheme|add:"://"|add:request.get_host|add:request.path }}?key={{ year_key }}</textarea>
</div>
{% endif %}
@ -107,7 +115,11 @@
<div class="columns is-mobile">
<div class="column is-8 is-offset-2 has-text-centered">
<h2 class="title is-3 is-serif">
{% blocktrans with pages_total=pages_total|intcomma %}In {{ year }}, {{ display_name }} read {{ books_total }} books<br />for a total of {{ pages_total }} pages!{% endblocktrans %}
{% blocktrans trimmed count counter=books_total with pages_total=pages_total|intcomma %}
In {{ year }}, {{ display_name }} read {{ books_total }} book<br />for a total of {{ pages_total }} pages!
{% plural %}
In {{ year }}, {{ display_name }} read {{ books_total }} books<br />for a total of {{ pages_total }} pages!
{% endblocktrans %}
</h2>
<p class="subtitle is-5">{% trans "Thats great!" %}</p>
@ -130,7 +142,7 @@
{% if book_pages_lowest and book_pages_highest %}
<div class="columns is-align-items-center mt-5">
<div class="column is-2 is-offset-1">
<a href="{{ book_pages_lowest.local_path }}">{% include 'snippets/book_cover.html' with book=book_pages_lowest cover_class='is-w-auto-tablet is-h-l-mobile' %}</a>
<a href="{{ book_pages_lowest.local_path }}">{% include 'snippets/book_cover.html' with book=book_pages_lowest cover_class='is-w-auto-tablet is-h-l-mobile' size='xxlarge' %}</a>
</div>
<div class="column is-3">
{% trans "Their shortest read this year…" %}
@ -151,7 +163,7 @@
</p>
</div>
<div class="column is-2">
<a href="{{ book_pages_highest.local_path }}">{% include 'snippets/book_cover.html' with book=book_pages_highest cover_class='is-w-auto-tablet is-h-l-mobile' %}</a>
<a href="{{ book_pages_highest.local_path }}">{% include 'snippets/book_cover.html' with book=book_pages_highest cover_class='is-w-auto-tablet is-h-l-mobile' size='xxlarge' %}</a>
</div>
<div class="column is-3">
{% trans "…and the longest" %}
@ -184,13 +196,17 @@
<div class="columns">
<div class="column has-text-centered">
<h2 class="title is-3 is-serif">
{% blocktrans %}{{ display_name }} left {{ ratings_total }} ratings, <br />their average rating is {{ rating_average }}{% endblocktrans %}
{% blocktrans trimmed count counter=ratings_total %}
{{ display_name }} left {{ ratings_total }} rating, <br />their average rating is {{ rating_average }}
{% plural %}
{{ display_name }} left {{ ratings_total }} ratings, <br />their average rating is {{ rating_average }}
{% endblocktrans %}
</h2>
</div>
</div>
<div class="columns is-align-items-center">
<div class="column is-3 is-offset-3">
<a href="{{ book_rating_highest.book.local_path }}">{% include 'snippets/book_cover.html' with book=book_rating_highest.book cover_class='is-w-auto-tablet is-h-l-mobile' %}</a>
<div class="column is-2 is-offset-4">
<a href="{{ book_rating_highest.book.local_path }}">{% include 'snippets/book_cover.html' with book=book_rating_highest.book cover_class='is-w-xl-tablet is-h-l-mobile' size='xxlarge' %}</a>
</div>
{% if book_rating_highest %}
<div class="column is-4">
@ -224,7 +240,7 @@
<div class="columns">
<div class="column has-text-centered">
<h2 class="title is-3 is-serif">
{% blocktrans %}All the books {{ display_name }} read in 2021{% endblocktrans %}
{% blocktrans %}All the books {{ display_name }} read in {{ year }}{% endblocktrans %}
</h2>
</div>
</div>
@ -242,7 +258,7 @@
</a>
{% else %}
<a href="{{ book.local_path }}" class="has-text-centered has-text-success-dark">
{% include 'snippets/book_cover.html' with book=book cover_class='is-w-auto' size='large' %}
{% include 'snippets/book_cover.html' with book=book cover_class='is-w-auto' size='xlarge' %}
<span class="book-title is-serif is-size-6">
{{ book.title }}
</span>

View file

@ -38,7 +38,7 @@
<a class="navbar-item" href="/">
<img class="image logo" src="{% if site.logo_small %}{% get_media_prefix %}{{ site.logo_small }}{% else %}{% static "images/logo-small.png" %}{% endif %}" alt="{% blocktrans with site_name=site.name %}{{ site_name }} home page{% endblocktrans %}">
</a>
<form class="navbar-item column" action="{% url 'search' %}">
<form class="navbar-item column is-align-items-start pt-5" action="{% url 'search' %}">
<div class="field has-addons">
<div class="control">
{% if user.is_authenticated %}
@ -58,25 +58,22 @@
</div>
</form>
<div role="button" tabindex="0" class="navbar-burger pulldown-menu" data-controls="main_nav" aria-expanded="false">
<div class="navbar-item mt-3">
<div class="icon icon-dots-three-vertical" title="{% trans 'Main navigation menu' %}">
<span class="is-sr-only">{% trans "Main navigation menu" %}</span>
</div>
</div>
</div>
<button type="button" tabindex="0" class="navbar-burger pulldown-menu my-4" data-controls="main_nav" aria-expanded="false">
<i class="icon icon-dots-three-vertical" aria-hidden="true"></i>
<span class="is-sr-only">{% trans "Main navigation menu" %}</span>
</button>
</div>
<div class="navbar-menu" id="main_nav">
<div class="navbar-start">
{% if request.user.is_authenticated %}
<a href="/#feed" class="navbar-item">
<a href="/#feed" class="navbar-item mt-3 py-0">
{% trans "Feed" %}
</a>
<a href="{% url 'lists' %}" class="navbar-item">
<a href="{% url 'lists' %}" class="navbar-item mt-3 py-0">
{% trans "Lists" %}
</a>
<a href="{% url 'discover' %}" class="navbar-item">
<a href="{% url 'discover' %}" class="navbar-item mt-3 py-0">
{% trans "Discover" %}
</a>
{% endif %}
@ -84,7 +81,7 @@
<div class="navbar-end">
{% if request.user.is_authenticated %}
<div class="navbar-item has-dropdown is-hoverable">
<div class="navbar-item mt-3 py-0 has-dropdown is-hoverable">
<a
href="{{ request.user.local_path }}"
class="navbar-link pulldown-menu"
@ -143,7 +140,7 @@
</li>
</ul>
</div>
<div class="navbar-item">
<div class="navbar-item mt-3 py-0">
<a href="{% url 'notifications' %}" class="tags has-addons">
<span class="tag is-medium">
<span class="icon icon-bell" title="{% trans 'Notifications' %}">
@ -161,7 +158,7 @@
</a>
</div>
{% else %}
<div class="navbar-item">
<div class="navbar-item pt-5 pb-0">
{% if request.path != '/login' and request.path != '/login/' %}
<div class="columns">
<div class="column">

View file

@ -1,4 +1,4 @@
Book Id Title Sort Character Primary Author Primary Author Role Secondary Author Secondary Author Roles Publication Date Review Rating Comment Private Comment Summary Media Physical Description Weight Height Thickness Length Dimensions Page Count LCCN Acquired Date Started Date Read Barcode BCID Tags Collections Languages Original Languages LC Classification ISBN ISBNs Subjects Dewey Decimal Dewey Wording Other Call Number Copies Source Entry Date From Where OCLC Work id Lending Patron Lending Status Lending Start Lending End
5498194 Marelle 1 Cortazar, Julio Gallimard (1979), Poche 1979 chef d'oeuvre 4.5 Marelle by Julio Cortázar (1979) Broché 590 p.; 7.24 inches 1.28 pounds 7.24 inches 1.26 inches 4.96 inches 7.24 x 4.96 x 1.26 inches 590 [2007-04-16] [2007-05-08] roman, espagnol, expérimental, bohème, philosophie Your library French Spanish PQ7797 .C7145 [2070291340] 2070291340, 9782070291342 Cortâazar, Julio. Rayuela 863 Literature > Spanish And Portuguese > Spanish fiction 1 Amazon.fr [2006-08-09] 57814
5015319 Le grand incendie de Londres: Récit, avec incises et bifurcations, 1985-1987 (Fiction & Cie) 1 Roubaud, Jacques Seuil (1989), Unknown Binding 1989 5 Le grand incendie de Londres: Récit, avec incises et bifurcations, 1985-1987 (Fiction & Cie) by Jacques Roubaud (1989) Broché 411 p.; 7.72 inches 0.88 pounds 7.72 inches 1.02 inches 5.43 inches 7.72 x 5.43 x 1.02 inches 411 Your library English PQ2678 .O77 [2020104725] 2020104725, 9782020104722 Autobiographical fiction|Roubaud, Jacques > Fiction 813 American And Canadian > Fiction > Literature 1 Amazon.com [2006-07-25] 478910
5015399 Le Maître et Marguerite 1 Boulgakov, Mikhaïl Pocket (1994), Poche 1994 Le Maître et Marguerite by Mikhaïl Boulgakov (1994) Broché 579 p.; 7.09 inches 0.66 pounds 7.09 inches 1.18 inches 4.33 inches 7.09 x 4.33 x 1.18 inches 579 Your library French PG3476 .B78 [2266062328] 2266062328, 9782266062329 Allegories|Bulgakov|Good and evil > Fiction|Humanities|Jerusalem > Fiction|Jesus Christ > Fiction|Literature|Mental illness > Fiction|Moscow (Russia) > Fiction|Novel|Pilate, Pontius, 1st cent. > Fiction|Political fiction|Russia > Fiction|Russian fiction|Russian publications (Form Entry)|Soviet Union > History > 1925-1953 > Fiction|literature 891.7342 1917-1945 > 1917-1991 (USSR) > Literature > Literature of other Indo-European languages > Other Languages > Russian > Russian Fiction 1 Amazon.fr [2006-07-25] 10151
5015399 Le Maître et Marguerite 1 Boulgakov, Mikhaïl Pocket (1994), Poche 1994 Le Maître et Marguerite by Mikhaïl Boulgakov (1994) Broché 579 p.; 7.09 inches 0.66 pounds 7.09 inches 1.18 inches 4.33 inches 7.09 x 4.33 x 1.18 inches 579 Your library French PG3476 .B78 [2266062328] Allegories|Bulgakov|Good and evil > Fiction|Humanities|Jerusalem > Fiction|Jesus Christ > Fiction|Literature|Mental illness > Fiction|Moscow (Russia) > Fiction|Novel|Pilate, Pontius, 1st cent. > Fiction|Political fiction|Russia > Fiction|Russian fiction|Russian publications (Form Entry)|Soviet Union > History > 1925-1953 > Fiction|literature 891.7342 1917-1945 > 1917-1991 (USSR) > Literature > Literature of other Indo-European languages > Other Languages > Russian > Russian Fiction 1 Amazon.fr [2006-07-25] 10151

1 Book Id Title Sort Character Primary Author Primary Author Role Secondary Author Secondary Author Roles Publication Date Review Rating Comment Private Comment Summary Media Physical Description Weight Height Thickness Length Dimensions Page Count LCCN Acquired Date Started Date Read Barcode BCID Tags Collections Languages Original Languages LC Classification ISBN ISBNs Subjects Dewey Decimal Dewey Wording Other Call Number Copies Source Entry Date From Where OCLC Work id Lending Patron Lending Status Lending Start Lending End
2 5498194 Marelle 1 Cortazar, Julio Gallimard (1979), Poche 1979 chef d'oeuvre 4.5 Marelle by Julio Cortázar (1979) Broché 590 p.; 7.24 inches 1.28 pounds 7.24 inches 1.26 inches 4.96 inches 7.24 x 4.96 x 1.26 inches 590 [2007-04-16] [2007-05-08] roman, espagnol, expérimental, bohème, philosophie Your library French Spanish PQ7797 .C7145 [2070291340] 2070291340, 9782070291342 Cortâazar, Julio. Rayuela 863 Literature > Spanish And Portuguese > Spanish fiction 1 Amazon.fr [2006-08-09] 57814
3 5015319 Le grand incendie de Londres: Récit, avec incises et bifurcations, 1985-1987 (Fiction & Cie) 1 Roubaud, Jacques Seuil (1989), Unknown Binding 1989 5 Le grand incendie de Londres: Récit, avec incises et bifurcations, 1985-1987 (Fiction & Cie) by Jacques Roubaud (1989) Broché 411 p.; 7.72 inches 0.88 pounds 7.72 inches 1.02 inches 5.43 inches 7.72 x 5.43 x 1.02 inches 411 Your library English PQ2678 .O77 [2020104725] 2020104725, 9782020104722 Autobiographical fiction|Roubaud, Jacques > Fiction 813 American And Canadian > Fiction > Literature 1 Amazon.com [2006-07-25] 478910
4 5015399 Le Maître et Marguerite 1 Boulgakov, Mikhaïl Pocket (1994), Poche 1994 Le Maître et Marguerite by Mikhaïl Boulgakov (1994) Broché 579 p.; 7.09 inches 0.66 pounds 7.09 inches 1.18 inches 4.33 inches 7.09 x 4.33 x 1.18 inches 579 Your library French PG3476 .B78 [2266062328] 2266062328, 9782266062329 Allegories|Bulgakov|Good and evil > Fiction|Humanities|Jerusalem > Fiction|Jesus Christ > Fiction|Literature|Mental illness > Fiction|Moscow (Russia) > Fiction|Novel|Pilate, Pontius, 1st cent. > Fiction|Political fiction|Russia > Fiction|Russian fiction|Russian publications (Form Entry)|Soviet Union > History > 1925-1953 > Fiction|literature 891.7342 1917-1945 > 1917-1991 (USSR) > Literature > Literature of other Indo-European languages > Other Languages > Russian > Russian Fiction 1 Amazon.fr [2006-07-25] 10151

View file

@ -48,7 +48,9 @@ class OpenLibraryImport(TestCase):
self.local_user, self.csv, False, "public"
)
import_items = models.ImportItem.objects.filter(job=import_job).all()
import_items = (
models.ImportItem.objects.filter(job=import_job).order_by("index").all()
)
self.assertEqual(len(import_items), 4)
self.assertEqual(import_items[0].index, 0)
self.assertEqual(import_items[0].data["Work Id"], "OL102749W")

View file

@ -60,6 +60,18 @@ class ShelfViews(TestCase):
validate_html(result.render())
self.assertEqual(result.status_code, 200)
def test_shelf_page_all_books_json(self, *_):
"""there is no json view here"""
view = views.Shelf.as_view()
request = self.factory.get("")
request.user = self.local_user
with patch("bookwyrm.views.shelf.shelf.is_api_request") as is_api:
is_api.return_value = True
result = view(request, self.local_user.username)
self.assertIsInstance(result, TemplateResponse)
validate_html(result.render())
self.assertEqual(result.status_code, 200)
def test_shelf_page_all_books_anonymous(self, *_):
"""there are so many views, this just makes sure it LOADS"""
view = views.Shelf.as_view()

View file

@ -36,6 +36,13 @@ class FeedViews(TestCase):
local=True,
localname="mouse",
)
self.another_user = models.User.objects.create_user(
"nutria@local.com",
"nutria@nutria.nutria",
"password",
local=True,
localname="nutria",
)
self.book = models.Edition.objects.create(
parent_work=models.Work.objects.create(title="hi"),
title="Example Edition",
@ -171,6 +178,17 @@ class FeedViews(TestCase):
result.render()
self.assertEqual(result.status_code, 200)
def test_direct_messages_page_user(self, *_):
"""there are so many views, this just makes sure it LOADS"""
view = views.DirectMessage.as_view()
request = self.factory.get("")
request.user = self.local_user
result = view(request, "nutria")
self.assertIsInstance(result, TemplateResponse)
result.render()
self.assertEqual(result.status_code, 200)
self.assertEqual(result.context_data["partner"], self.another_user)
@patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async")
@patch("bookwyrm.activitystreams.add_book_statuses_task.delay")
def test_get_suggested_book(self, *_):

View file

@ -1,6 +1,8 @@
""" test for app action functionality """
from unittest.mock import patch
from django.contrib.auth.models import AnonymousUser
from django.http import Http404
from django.template.response import TemplateResponse
from django.test import TestCase
from django.test.client import RequestFactory
@ -43,6 +45,8 @@ class GroupViews(TestCase):
self.membership = models.GroupMember.objects.create(
group=self.testgroup, user=self.local_user
)
self.anonymous_user = AnonymousUser
self.anonymous_user.is_authenticated = False
models.SiteSettings.objects.create()
@ -56,6 +60,17 @@ class GroupViews(TestCase):
validate_html(result.render())
self.assertEqual(result.status_code, 200)
def test_group_get_anonymous(self, _):
"""there are so many views, this just makes sure it LOADS"""
self.testgroup.privacy = "followers"
self.testgroup.save()
view = views.Group.as_view()
request = self.factory.get("")
request.user = self.anonymous_user
with self.assertRaises(Http404):
view(request, group_id=self.testgroup.id)
def test_usergroups_get(self, _):
"""there are so many views, this just makes sure it LOADS"""
view = views.UserGroups.as_view()

View file

@ -80,6 +80,29 @@ class UserViews(TestCase):
self.assertIsInstance(result, ActivitypubResponse)
self.assertEqual(result.status_code, 200)
def test_user_page_domain(self):
"""when the user domain has dashes in it"""
with patch("bookwyrm.models.user.set_remote_server"):
self.remote_user = models.User.objects.create_user(
"nutria",
"",
"nutriaword",
local=False,
remote_id="https://ex--ample.co----m/users/nutria",
inbox="https://ex--ample.co----m/users/nutria/inbox",
outbox="https://ex--ample.co----m/users/nutria/outbox",
)
view = views.User.as_view()
request = self.factory.get("")
request.user = self.local_user
with patch("bookwyrm.views.user.is_api_request") as is_api:
is_api.return_value = False
result = view(request, "nutria@ex--ample.co----m")
self.assertIsInstance(result, TemplateResponse)
validate_html(result.render())
self.assertEqual(result.status_code, 200)
def test_user_page_blocked(self):
"""there are so many views, this just makes sure it LOADS"""
view = views.User.as_view()
@ -186,3 +209,11 @@ class UserViews(TestCase):
self.local_user.refresh_from_db()
self.assertFalse(self.local_user.show_suggested_users)
def test_user_redirect(self):
"""test the basic redirect"""
request = self.factory.get("@mouse")
request.user = self.anonymous_user
result = views.user_redirect(request, "mouse")
self.assertEqual(result.status_code, 302)

View file

@ -50,7 +50,7 @@ urlpatterns = [
re_path("^api/updates/stream/(?P<stream>[a-z]+)/?$", views.get_unread_status_count),
# authentication
re_path(r"^login/?$", views.Login.as_view(), name="login"),
re_path(r"^login/(?P<confirmed>confirmed)?$", views.Login.as_view(), name="login"),
re_path(r"^login/(?P<confirmed>confirmed)/?$", views.Login.as_view(), name="login"),
re_path(r"^register/?$", views.Register.as_view()),
re_path(r"confirm-email/?$", views.ConfirmEmail.as_view(), name="confirm-email"),
re_path(
@ -112,12 +112,12 @@ urlpatterns = [
name="settings-federated-server",
),
re_path(
r"^settings/federation/(?P<server>\d+)/block?$",
r"^settings/federation/(?P<server>\d+)/block/?$",
views.block_server,
name="settings-federated-server-block",
),
re_path(
r"^settings/federation/(?P<server>\d+)/unblock?$",
r"^settings/federation/(?P<server>\d+)/unblock/?$",
views.unblock_server,
name="settings-federated-server-unblock",
),
@ -140,7 +140,7 @@ urlpatterns = [
name="settings-invite-requests",
),
re_path(
r"^settings/requests/ignore?$",
r"^settings/requests/ignore/?$",
views.ignore_invite_request,
name="settings-invite-requests-ignore",
),
@ -229,7 +229,7 @@ urlpatterns = [
r"^direct-messages/?$", views.DirectMessage.as_view(), name="direct-messages"
),
re_path(
rf"^direct-messages/(?P<username>{regex.USERNAME})?$",
rf"^direct-messages/(?P<username>{regex.USERNAME})/?$",
views.DirectMessage.as_view(),
name="direct-messages-user",
),
@ -276,6 +276,7 @@ urlpatterns = [
# users
re_path(rf"{USER_PATH}\.json$", views.User.as_view()),
re_path(rf"{USER_PATH}/?$", views.User.as_view(), name="user-feed"),
re_path(rf"^@(?P<username>{regex.USERNAME})$", views.user_redirect),
re_path(rf"{USER_PATH}/rss/?$", views.rss_feed.RssFeed(), name="user-rss"),
re_path(
rf"{USER_PATH}/followers(.json)?/?$",
@ -338,7 +339,7 @@ urlpatterns = [
re_path(r"^save-list/(?P<list_id>\d+)/?$", views.save_list, name="list-save"),
re_path(r"^unsave-list/(?P<list_id>\d+)/?$", views.unsave_list, name="list-unsave"),
re_path(
r"^list/(?P<list_id>\d+)/embed/(?P<list_key>[0-9a-f]+)?$",
r"^list/(?P<list_id>\d+)/embed/(?P<list_key>[0-9a-f]+)/?$",
views.unsafe_embed_list,
name="embed-list",
),
@ -355,7 +356,7 @@ urlpatterns = [
name="shelf",
),
re_path(r"^create-shelf/?$", views.create_shelf, name="shelf-create"),
re_path(r"^delete-shelf/(?P<shelf_id>\d+)?$", views.delete_shelf),
re_path(r"^delete-shelf/(?P<shelf_id>\d+)/?$", views.delete_shelf),
re_path(r"^shelve/?$", views.shelve),
re_path(r"^unshelve/?$", views.unshelve),
# goals
@ -422,7 +423,7 @@ urlpatterns = [
re_path(rf"{BOOK_PATH}/edit/?$", views.EditBook.as_view(), name="edit-book"),
re_path(rf"{BOOK_PATH}/confirm/?$", views.ConfirmEditBook.as_view()),
re_path(r"^create-book/?$", views.EditBook.as_view(), name="create-book"),
re_path(r"^create-book/confirm?$", views.ConfirmEditBook.as_view()),
re_path(r"^create-book/confirm/?$", views.ConfirmEditBook.as_view()),
re_path(rf"{BOOK_PATH}/editions(.json)?/?$", views.Editions.as_view()),
re_path(
r"^upload-cover/(?P<book_id>\d+)/?$", views.upload_cover, name="upload-cover"

View file

@ -1,6 +1,6 @@
""" defining regexes for regularly used concepts """
DOMAIN = r"[\w_\-\.]+\.[a-z]{2,}"
DOMAIN = r"[\w_\-\.]+\.[a-z\-]{2,}"
LOCALNAME = r"@?[a-zA-Z_\-\.0-9]+"
STRICT_LOCALNAME = r"@[a-zA-Z_\-\.0-9]+"
USERNAME = rf"{LOCALNAME}(@{DOMAIN})?"

View file

@ -94,7 +94,7 @@ from .search import Search
from .status import CreateStatus, EditStatus, DeleteStatus, update_progress
from .status import edit_readthrough
from .updates import get_notification_count, get_unread_status_count
from .user import User, Followers, Following, hide_suggestions
from .user import User, Followers, Following, hide_suggestions, user_redirect
from .wellknown import *
from .annual_summary import (
AnnualSummary,

View file

@ -39,7 +39,8 @@ class Login(View):
return redirect("/")
login_form = forms.LoginForm(request.POST)
localname = login_form.data["localname"]
localname = login_form.data.get("localname")
if "@" in localname: # looks like an email address to me
try:
username = models.User.objects.get(email=localname).username
@ -47,7 +48,7 @@ class Login(View):
username = localname
else:
username = f"{localname}@{DOMAIN}"
password = login_form.data["password"]
password = login_form.data.get("password")
# perform authentication
user = authenticate(request, username=username, password=password)

View file

@ -52,7 +52,7 @@ class Shelf(View):
)
shelf = FakeShelf("all", _("All books"), user, books, "public")
if is_api_request(request):
if is_api_request(request) and shelf_identifier:
return ActivitypubResponse(shelf.to_activity(**request.GET))
reviews = models.Review.objects

View file

@ -151,3 +151,9 @@ def hide_suggestions(request):
request.user.show_suggested_users = False
request.user.save(broadcast=False, update_fields=["show_suggested_users"])
return redirect(request.headers.get("Referer", "/"))
# pylint: disable=unused-argument
def user_redirect(request, username):
"""redirect to a user's feed"""
return redirect("user-feed", username=username)

3
bw-dev
View file

@ -85,6 +85,9 @@ case "$CMD" in
docker-compose exec web python manage.py collectstatic --no-input
docker-compose restart
;;
prettier)
npx prettier --write bookwyrm/static/js/*.js
;;
populate_streams)
runweb python manage.py populate_streams "$@"
;;

Binary file not shown.

View file

@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: bookwyrm\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-12-27 22:24+0000\n"
"PO-Revision-Date: 2021-12-27 23:03\n"
"POT-Creation-Date: 2021-12-28 15:00+0000\n"
"PO-Revision-Date: 2021-12-28 16:03\n"
"Last-Translator: Mouse Reeve <mousereeve@riseup.net>\n"
"Language-Team: German\n"
"Language: de\n"
@ -243,96 +243,96 @@ msgstr "Serverfehler"
msgid "Something went wrong! Sorry about that."
msgstr "Etwas ist schief gelaufen! Tut uns leid."
#: bookwyrm/templates/annual_summary/layout.html:6
#: bookwyrm/templates/annual_summary/layout.html:7
#: bookwyrm/templates/feed/summary_card.html:8
#, python-format
msgid "%(year)s in the books"
msgstr ""
#: bookwyrm/templates/annual_summary/layout.html:42
#: bookwyrm/templates/annual_summary/layout.html:43
#, python-format
msgid "%(year)s <em>in the books</em>"
msgstr ""
#: bookwyrm/templates/annual_summary/layout.html:46
#: bookwyrm/templates/annual_summary/layout.html:47
#, python-format
msgid "<em>%(display_name)ss</em> year of reading"
msgstr ""
#: bookwyrm/templates/annual_summary/layout.html:52
#: bookwyrm/templates/annual_summary/layout.html:53
msgid "Share this page"
msgstr ""
#: bookwyrm/templates/annual_summary/layout.html:60
#: bookwyrm/templates/annual_summary/layout.html:61
msgid "Copy address"
msgstr ""
#: bookwyrm/templates/annual_summary/layout.html:60
#: bookwyrm/templates/annual_summary/layout.html:61
#: bookwyrm/templates/lists/list.html:194
msgid "Copied!"
msgstr ""
#: bookwyrm/templates/annual_summary/layout.html:68
#: bookwyrm/templates/annual_summary/layout.html:69
msgid "Sharing status: <strong>public with key</strong>"
msgstr ""
#: bookwyrm/templates/annual_summary/layout.html:69
#: bookwyrm/templates/annual_summary/layout.html:70
msgid "The page can be seen by anyone with the complete address."
msgstr ""
#: bookwyrm/templates/annual_summary/layout.html:74
#: bookwyrm/templates/annual_summary/layout.html:75
msgid "Make page private"
msgstr ""
#: bookwyrm/templates/annual_summary/layout.html:80
#: bookwyrm/templates/annual_summary/layout.html:81
msgid "Sharing status: <strong>private</strong>"
msgstr ""
#: bookwyrm/templates/annual_summary/layout.html:81
#: bookwyrm/templates/annual_summary/layout.html:82
msgid "The page is private, only you can see it."
msgstr ""
#: bookwyrm/templates/annual_summary/layout.html:86
#: bookwyrm/templates/annual_summary/layout.html:87
msgid "Make page public"
msgstr ""
#: bookwyrm/templates/annual_summary/layout.html:90
#: bookwyrm/templates/annual_summary/layout.html:91
msgid "When you make your page private, the old key wont give access to the page anymore. A new key will be created if the page is once again made public."
msgstr ""
#: bookwyrm/templates/annual_summary/layout.html:103
#: bookwyrm/templates/annual_summary/layout.html:104
#, python-format
msgid "Sadly %(display_name)s didnt finish any book in %(year)s"
msgstr ""
#: bookwyrm/templates/annual_summary/layout.html:109
#: bookwyrm/templates/annual_summary/layout.html:110
#, python-format
msgid "In %(year)s, %(display_name)s read %(books_total)s books<br />for a total of %(pages_total)s pages!"
msgstr ""
#: bookwyrm/templates/annual_summary/layout.html:111
#: bookwyrm/templates/annual_summary/layout.html:112
msgid "Thats great!"
msgstr ""
#: bookwyrm/templates/annual_summary/layout.html:114
#: bookwyrm/templates/annual_summary/layout.html:115
#, python-format
msgid "That makes an average of %(pages_average)s pages per book."
msgid "That makes an average of %(pages)s pages per book."
msgstr ""
#: bookwyrm/templates/annual_summary/layout.html:119
#: bookwyrm/templates/annual_summary/layout.html:120
#, python-format
msgid "(%(no_page_number)s book doesnt have pages)"
msgid_plural "(%(no_page_number)s books dont have pages)"
msgstr[0] ""
msgstr[1] ""
#: bookwyrm/templates/annual_summary/layout.html:135
#: bookwyrm/templates/annual_summary/layout.html:136
msgid "Their shortest read this year…"
msgstr ""
#: bookwyrm/templates/annual_summary/layout.html:142
#: bookwyrm/templates/annual_summary/layout.html:163
#: bookwyrm/templates/annual_summary/layout.html:203
#: bookwyrm/templates/annual_summary/layout.html:143
#: bookwyrm/templates/annual_summary/layout.html:164
#: bookwyrm/templates/annual_summary/layout.html:204
#: bookwyrm/templates/book/book.html:47
#: bookwyrm/templates/discover/large-book.html:22
#: bookwyrm/templates/landing/large-book.html:25
@ -340,31 +340,31 @@ msgstr ""
msgid "by"
msgstr "von"
#: bookwyrm/templates/annual_summary/layout.html:148
#: bookwyrm/templates/annual_summary/layout.html:169
#: bookwyrm/templates/annual_summary/layout.html:149
#: bookwyrm/templates/annual_summary/layout.html:170
#, python-format
msgid "<strong>%(pages)s</strong> pages"
msgstr ""
#: bookwyrm/templates/annual_summary/layout.html:156
#: bookwyrm/templates/annual_summary/layout.html:157
msgid "…and the longest"
msgstr ""
#: bookwyrm/templates/annual_summary/layout.html:186
#: bookwyrm/templates/annual_summary/layout.html:187
#, python-format
msgid "%(display_name)s left %(ratings_total)s ratings, <br />their average rating is %(rating_average)s"
msgstr ""
#: bookwyrm/templates/annual_summary/layout.html:196
#: bookwyrm/templates/annual_summary/layout.html:197
msgid "Their best rated review"
msgstr ""
#: bookwyrm/templates/annual_summary/layout.html:209
#: bookwyrm/templates/annual_summary/layout.html:210
#, python-format
msgid "Their rating: <strong>%(rating)s</strong>"
msgstr ""
#: bookwyrm/templates/annual_summary/layout.html:226
#: bookwyrm/templates/annual_summary/layout.html:227
#, python-format
msgid "All the books %(display_name)s read in 2021"
msgstr ""
@ -1032,6 +1032,8 @@ msgstr "Du kannst dich jederzeit in deinen <a href=\"%(path)s\">Profileinstellun
#: bookwyrm/templates/directory/directory.html:29
#: bookwyrm/templates/directory/directory.html:31
#: bookwyrm/templates/feed/goal_card.html:17
#: bookwyrm/templates/feed/summary_card.html:12
#: bookwyrm/templates/feed/summary_card.html:14
#: bookwyrm/templates/snippets/announcement.html:34
msgid "Dismiss message"
msgstr "Nachricht schließen"
@ -1379,11 +1381,11 @@ msgstr "Keine vorgeschlagenen Benutzer*innen anzeigen"
msgid "View directory"
msgstr "Verzeichnis anzeigen"
#: bookwyrm/templates/feed/summary_card.html:14
#: bookwyrm/templates/feed/summary_card.html:21
msgid "The end of the year is the best moment to take stock of all the books read during the last 12 months. How many pages have you read? Which book is your best-rated of the year? We compiled these stats, and more!"
msgstr ""
#: bookwyrm/templates/feed/summary_card.html:19
#: bookwyrm/templates/feed/summary_card.html:26
#, python-format
msgid "Discover your stats for %(year)s!"
msgstr ""

View file

@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: 0.0.1\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-12-28 15:00+0000\n"
"POT-Creation-Date: 2021-12-28 20:12+0000\n"
"PO-Revision-Date: 2021-02-28 17:19-0800\n"
"Last-Translator: Mouse Reeve <mousereeve@riseup.net>\n"
"Language-Team: English <LL@li.org>\n"
@ -264,76 +264,78 @@ msgstr ""
msgid "Share this page"
msgstr ""
#: bookwyrm/templates/annual_summary/layout.html:61
#: bookwyrm/templates/annual_summary/layout.html:67
msgid "Copy address"
msgstr ""
#: bookwyrm/templates/annual_summary/layout.html:61
#: bookwyrm/templates/annual_summary/layout.html:68
#: bookwyrm/templates/lists/list.html:194
msgid "Copied!"
msgstr ""
#: bookwyrm/templates/annual_summary/layout.html:69
#: bookwyrm/templates/annual_summary/layout.html:77
msgid "Sharing status: <strong>public with key</strong>"
msgstr ""
#: bookwyrm/templates/annual_summary/layout.html:70
#: bookwyrm/templates/annual_summary/layout.html:78
msgid "The page can be seen by anyone with the complete address."
msgstr ""
#: bookwyrm/templates/annual_summary/layout.html:75
#: bookwyrm/templates/annual_summary/layout.html:83
msgid "Make page private"
msgstr ""
#: bookwyrm/templates/annual_summary/layout.html:81
#: bookwyrm/templates/annual_summary/layout.html:89
msgid "Sharing status: <strong>private</strong>"
msgstr ""
#: bookwyrm/templates/annual_summary/layout.html:82
#: bookwyrm/templates/annual_summary/layout.html:90
msgid "The page is private, only you can see it."
msgstr ""
#: bookwyrm/templates/annual_summary/layout.html:87
#: bookwyrm/templates/annual_summary/layout.html:95
msgid "Make page public"
msgstr ""
#: bookwyrm/templates/annual_summary/layout.html:91
#: bookwyrm/templates/annual_summary/layout.html:99
msgid "When you make your page private, the old key wont give access to the page anymore. A new key will be created if the page is once again made public."
msgstr ""
#: bookwyrm/templates/annual_summary/layout.html:104
#: bookwyrm/templates/annual_summary/layout.html:112
#, python-format
msgid "Sadly %(display_name)s didnt finish any book in %(year)s"
msgstr ""
#: bookwyrm/templates/annual_summary/layout.html:110
#: bookwyrm/templates/annual_summary/layout.html:118
#, python-format
msgid "In %(year)s, %(display_name)s read %(books_total)s books<br />for a total of %(pages_total)s pages!"
msgstr ""
msgid "In %(year)s, %(display_name)s read %(books_total)s book<br />for a total of %(pages_total)s pages!"
msgid_plural "In %(year)s, %(display_name)s read %(books_total)s books<br />for a total of %(pages_total)s pages!"
msgstr[0] ""
msgstr[1] ""
#: bookwyrm/templates/annual_summary/layout.html:112
#: bookwyrm/templates/annual_summary/layout.html:124
msgid "Thats great!"
msgstr ""
#: bookwyrm/templates/annual_summary/layout.html:115
#: bookwyrm/templates/annual_summary/layout.html:127
#, python-format
msgid "That makes an average of %(pages)s pages per book."
msgstr ""
#: bookwyrm/templates/annual_summary/layout.html:120
#: bookwyrm/templates/annual_summary/layout.html:132
#, python-format
msgid "(%(no_page_number)s book doesnt have pages)"
msgid_plural "(%(no_page_number)s books dont have pages)"
msgstr[0] ""
msgstr[1] ""
#: bookwyrm/templates/annual_summary/layout.html:136
#: bookwyrm/templates/annual_summary/layout.html:148
msgid "Their shortest read this year…"
msgstr ""
#: bookwyrm/templates/annual_summary/layout.html:143
#: bookwyrm/templates/annual_summary/layout.html:164
#: bookwyrm/templates/annual_summary/layout.html:204
#: bookwyrm/templates/annual_summary/layout.html:155
#: bookwyrm/templates/annual_summary/layout.html:176
#: bookwyrm/templates/annual_summary/layout.html:220
#: bookwyrm/templates/book/book.html:47
#: bookwyrm/templates/discover/large-book.html:22
#: bookwyrm/templates/landing/large-book.html:25
@ -341,33 +343,35 @@ msgstr ""
msgid "by"
msgstr ""
#: bookwyrm/templates/annual_summary/layout.html:149
#: bookwyrm/templates/annual_summary/layout.html:170
#: bookwyrm/templates/annual_summary/layout.html:161
#: bookwyrm/templates/annual_summary/layout.html:182
#, python-format
msgid "<strong>%(pages)s</strong> pages"
msgstr ""
#: bookwyrm/templates/annual_summary/layout.html:157
#: bookwyrm/templates/annual_summary/layout.html:169
msgid "…and the longest"
msgstr ""
#: bookwyrm/templates/annual_summary/layout.html:187
#: bookwyrm/templates/annual_summary/layout.html:199
#, python-format
msgid "%(display_name)s left %(ratings_total)s ratings, <br />their average rating is %(rating_average)s"
msgstr ""
msgid "%(display_name)s left %(ratings_total)s rating, <br />their average rating is %(rating_average)s"
msgid_plural "%(display_name)s left %(ratings_total)s ratings, <br />their average rating is %(rating_average)s"
msgstr[0] ""
msgstr[1] ""
#: bookwyrm/templates/annual_summary/layout.html:197
#: bookwyrm/templates/annual_summary/layout.html:213
msgid "Their best rated review"
msgstr ""
#: bookwyrm/templates/annual_summary/layout.html:210
#: bookwyrm/templates/annual_summary/layout.html:226
#, python-format
msgid "Their rating: <strong>%(rating)s</strong>"
msgstr ""
#: bookwyrm/templates/annual_summary/layout.html:227
#: bookwyrm/templates/annual_summary/layout.html:243
#, python-format
msgid "All the books %(display_name)s read in 2021"
msgid "All the books %(display_name)s read in %(year)s"
msgstr ""
#: bookwyrm/templates/author/author.html:18

Binary file not shown.

View file

@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: bookwyrm\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-12-27 22:24+0000\n"
"PO-Revision-Date: 2021-12-28 11:43\n"
"POT-Creation-Date: 2021-12-28 15:00+0000\n"
"PO-Revision-Date: 2021-12-28 17:59\n"
"Last-Translator: Mouse Reeve <mousereeve@riseup.net>\n"
"Language-Team: Spanish\n"
"Language: es\n"
@ -243,96 +243,96 @@ msgstr "Error del Servidor"
msgid "Something went wrong! Sorry about that."
msgstr "¡Algo salió mal! Disculpa."
#: bookwyrm/templates/annual_summary/layout.html:6
#: bookwyrm/templates/annual_summary/layout.html:7
#: bookwyrm/templates/feed/summary_card.html:8
#, python-format
msgid "%(year)s in the books"
msgstr "%(year)s en libros"
#: bookwyrm/templates/annual_summary/layout.html:42
#: bookwyrm/templates/annual_summary/layout.html:43
#, python-format
msgid "%(year)s <em>in the books</em>"
msgstr "%(year)s <em>en libros</em>"
#: bookwyrm/templates/annual_summary/layout.html:46
#: bookwyrm/templates/annual_summary/layout.html:47
#, python-format
msgid "<em>%(display_name)ss</em> year of reading"
msgstr "El año de lectura de <em>%(display_name)s</em>"
#: bookwyrm/templates/annual_summary/layout.html:52
#: bookwyrm/templates/annual_summary/layout.html:53
msgid "Share this page"
msgstr "Compartir esta página"
#: bookwyrm/templates/annual_summary/layout.html:60
#: bookwyrm/templates/annual_summary/layout.html:61
msgid "Copy address"
msgstr "Copiar dirección"
#: bookwyrm/templates/annual_summary/layout.html:60
#: bookwyrm/templates/annual_summary/layout.html:61
#: bookwyrm/templates/lists/list.html:194
msgid "Copied!"
msgstr "¡Copiado!"
#: bookwyrm/templates/annual_summary/layout.html:68
#: bookwyrm/templates/annual_summary/layout.html:69
msgid "Sharing status: <strong>public with key</strong>"
msgstr "Nivel de compartido: <strong>público con llave</strong>"
#: bookwyrm/templates/annual_summary/layout.html:69
#: bookwyrm/templates/annual_summary/layout.html:70
msgid "The page can be seen by anyone with the complete address."
msgstr "La página puede ser vista por cualquier persona que tenga la dirección completa."
#: bookwyrm/templates/annual_summary/layout.html:74
#: bookwyrm/templates/annual_summary/layout.html:75
msgid "Make page private"
msgstr "Hacer privada la página"
#: bookwyrm/templates/annual_summary/layout.html:80
#: bookwyrm/templates/annual_summary/layout.html:81
msgid "Sharing status: <strong>private</strong>"
msgstr "Nivel de compartido: <strong>privado</strong>"
#: bookwyrm/templates/annual_summary/layout.html:81
#: bookwyrm/templates/annual_summary/layout.html:82
msgid "The page is private, only you can see it."
msgstr "La página es privada, solo tú puedes verla."
#: bookwyrm/templates/annual_summary/layout.html:86
#: bookwyrm/templates/annual_summary/layout.html:87
msgid "Make page public"
msgstr "Hacer pública la página"
#: bookwyrm/templates/annual_summary/layout.html:90
#: bookwyrm/templates/annual_summary/layout.html:91
msgid "When you make your page private, the old key wont give access to the page anymore. A new key will be created if the page is once again made public."
msgstr "Una vez que haces privada tu página, la clave antigua ya no dará acceso a la página. Si la página se vuelve a hacer pública se creará una nueva clave."
#: bookwyrm/templates/annual_summary/layout.html:103
#: bookwyrm/templates/annual_summary/layout.html:104
#, python-format
msgid "Sadly %(display_name)s didnt finish any book in %(year)s"
msgstr "Lamentablemente, %(display_name)s no terminó ningún libro en %(year)s"
#: bookwyrm/templates/annual_summary/layout.html:109
#: bookwyrm/templates/annual_summary/layout.html:110
#, python-format
msgid "In %(year)s, %(display_name)s read %(books_total)s books<br />for a total of %(pages_total)s pages!"
msgstr "En %(year)s %(display_name)s ha leído %(books_total)s libros<br />¡haciendo un total de %(pages_total)s páginas!"
#: bookwyrm/templates/annual_summary/layout.html:111
#: bookwyrm/templates/annual_summary/layout.html:112
msgid "Thats great!"
msgstr "¡Eso es genial!"
#: bookwyrm/templates/annual_summary/layout.html:114
#: bookwyrm/templates/annual_summary/layout.html:115
#, python-format
msgid "That makes an average of %(pages_average)s pages per book."
msgstr "Eso hace un promedio de %(pages_average)s páginas por libro."
msgid "That makes an average of %(pages)s pages per book."
msgstr "Eso hace un promedio de %(pages)s páginas por libro."
#: bookwyrm/templates/annual_summary/layout.html:119
#: bookwyrm/templates/annual_summary/layout.html:120
#, python-format
msgid "(%(no_page_number)s book doesnt have pages)"
msgid_plural "(%(no_page_number)s books dont have pages)"
msgstr[0] "(%(no_page_number)s libro no tiene páginas)"
msgstr[1] "(%(no_page_number)s libros no tienen páginas)"
#: bookwyrm/templates/annual_summary/layout.html:135
#: bookwyrm/templates/annual_summary/layout.html:136
msgid "Their shortest read this year…"
msgstr "Su lectura más corta de este año…"
#: bookwyrm/templates/annual_summary/layout.html:142
#: bookwyrm/templates/annual_summary/layout.html:163
#: bookwyrm/templates/annual_summary/layout.html:203
#: bookwyrm/templates/annual_summary/layout.html:143
#: bookwyrm/templates/annual_summary/layout.html:164
#: bookwyrm/templates/annual_summary/layout.html:204
#: bookwyrm/templates/book/book.html:47
#: bookwyrm/templates/discover/large-book.html:22
#: bookwyrm/templates/landing/large-book.html:25
@ -340,31 +340,31 @@ msgstr "Su lectura más corta de este año…"
msgid "by"
msgstr "por"
#: bookwyrm/templates/annual_summary/layout.html:148
#: bookwyrm/templates/annual_summary/layout.html:169
#: bookwyrm/templates/annual_summary/layout.html:149
#: bookwyrm/templates/annual_summary/layout.html:170
#, python-format
msgid "<strong>%(pages)s</strong> pages"
msgstr "<strong>%(pages)s</strong> páginas"
#: bookwyrm/templates/annual_summary/layout.html:156
#: bookwyrm/templates/annual_summary/layout.html:157
msgid "…and the longest"
msgstr "… y la más larga"
#: bookwyrm/templates/annual_summary/layout.html:186
#: bookwyrm/templates/annual_summary/layout.html:187
#, python-format
msgid "%(display_name)s left %(ratings_total)s ratings, <br />their average rating is %(rating_average)s"
msgstr "%(display_name)s dio %(ratings_total)s valoraciones, <br />su valoración media es %(rating_average)s"
#: bookwyrm/templates/annual_summary/layout.html:196
#: bookwyrm/templates/annual_summary/layout.html:197
msgid "Their best rated review"
msgstr "Su mejor valoración"
#: bookwyrm/templates/annual_summary/layout.html:209
#: bookwyrm/templates/annual_summary/layout.html:210
#, python-format
msgid "Their rating: <strong>%(rating)s</strong>"
msgstr "Su valoración: <strong>%(rating)s</strong>"
#: bookwyrm/templates/annual_summary/layout.html:226
#: bookwyrm/templates/annual_summary/layout.html:227
#, python-format
msgid "All the books %(display_name)s read in 2021"
msgstr "Todos los libros que ha leído %(display_name)s en 2021"
@ -1032,6 +1032,8 @@ msgstr "Puedes optar por no en cualquier hora en tus <a href=\"%(path)s\">config
#: bookwyrm/templates/directory/directory.html:29
#: bookwyrm/templates/directory/directory.html:31
#: bookwyrm/templates/feed/goal_card.html:17
#: bookwyrm/templates/feed/summary_card.html:12
#: bookwyrm/templates/feed/summary_card.html:14
#: bookwyrm/templates/snippets/announcement.html:34
msgid "Dismiss message"
msgstr "Descartar mensaje"
@ -1379,11 +1381,11 @@ msgstr "No mostrar usuarios sugeridos"
msgid "View directory"
msgstr "Ver directorio"
#: bookwyrm/templates/feed/summary_card.html:14
#: bookwyrm/templates/feed/summary_card.html:21
msgid "The end of the year is the best moment to take stock of all the books read during the last 12 months. How many pages have you read? Which book is your best-rated of the year? We compiled these stats, and more!"
msgstr "El fin del año es el mejor momento para hacer balance de todos los libros leídos durante los últimos 12 meses. ¿Cuántas páginas has leído? ¿Cuál es el libro que mejor has valorado este año? ¡Hemos compilado estas estadísticas, y más!"
#: bookwyrm/templates/feed/summary_card.html:19
#: bookwyrm/templates/feed/summary_card.html:26
#, python-format
msgid "Discover your stats for %(year)s!"
msgstr "¡Descubre tus estadísticas de %(year)s!"

Binary file not shown.

View file

@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: bookwyrm\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-12-27 22:24+0000\n"
"PO-Revision-Date: 2021-12-27 23:03\n"
"POT-Creation-Date: 2021-12-28 15:00+0000\n"
"PO-Revision-Date: 2021-12-28 16:59\n"
"Last-Translator: Mouse Reeve <mousereeve@riseup.net>\n"
"Language-Team: French\n"
"Language: fr\n"
@ -243,96 +243,96 @@ msgstr "Erreur côté serveur"
msgid "Something went wrong! Sorry about that."
msgstr "Une erreur sest produite; désolé!"
#: bookwyrm/templates/annual_summary/layout.html:6
#: bookwyrm/templates/annual_summary/layout.html:7
#: bookwyrm/templates/feed/summary_card.html:8
#, python-format
msgid "%(year)s in the books"
msgstr ""
msgstr "les livres de %(year)s"
#: bookwyrm/templates/annual_summary/layout.html:42
#: bookwyrm/templates/annual_summary/layout.html:43
#, python-format
msgid "%(year)s <em>in the books</em>"
msgstr ""
msgstr "<em>les livres de</em> %(year)s"
#: bookwyrm/templates/annual_summary/layout.html:46
#: bookwyrm/templates/annual_summary/layout.html:47
#, python-format
msgid "<em>%(display_name)ss</em> year of reading"
msgstr ""
msgstr "lannée de lecture de <em>%(display_name)s</em>"
#: bookwyrm/templates/annual_summary/layout.html:52
#: bookwyrm/templates/annual_summary/layout.html:53
msgid "Share this page"
msgstr ""
msgstr "Partager cette page"
#: bookwyrm/templates/annual_summary/layout.html:60
#: bookwyrm/templates/annual_summary/layout.html:61
msgid "Copy address"
msgstr ""
msgstr "Copier ladresse"
#: bookwyrm/templates/annual_summary/layout.html:60
#: bookwyrm/templates/annual_summary/layout.html:61
#: bookwyrm/templates/lists/list.html:194
msgid "Copied!"
msgstr "Copié!"
#: bookwyrm/templates/annual_summary/layout.html:68
msgid "Sharing status: <strong>public with key</strong>"
msgstr ""
#: bookwyrm/templates/annual_summary/layout.html:69
msgid "Sharing status: <strong>public with key</strong>"
msgstr "Statut de partage : <strong>public avec clé</strong>"
#: bookwyrm/templates/annual_summary/layout.html:70
msgid "The page can be seen by anyone with the complete address."
msgstr ""
msgstr "La page peut être consultée par toute personne ayant l'adresse complète."
#: bookwyrm/templates/annual_summary/layout.html:74
#: bookwyrm/templates/annual_summary/layout.html:75
msgid "Make page private"
msgstr ""
#: bookwyrm/templates/annual_summary/layout.html:80
msgid "Sharing status: <strong>private</strong>"
msgstr ""
msgstr "Rendre cette page privée"
#: bookwyrm/templates/annual_summary/layout.html:81
msgid "Sharing status: <strong>private</strong>"
msgstr "Statut de partage : <strong>privé</strong>"
#: bookwyrm/templates/annual_summary/layout.html:82
msgid "The page is private, only you can see it."
msgstr ""
msgstr "La page est privée, seulement vous pouvez la voir."
#: bookwyrm/templates/annual_summary/layout.html:86
#: bookwyrm/templates/annual_summary/layout.html:87
msgid "Make page public"
msgstr ""
msgstr "Rendre cette page publique"
#: bookwyrm/templates/annual_summary/layout.html:90
#: bookwyrm/templates/annual_summary/layout.html:91
msgid "When you make your page private, the old key wont give access to the page anymore. A new key will be created if the page is once again made public."
msgstr ""
msgstr "Lorsque vous rendez votre page privée, lancienne clé ne donnera plus accès à la page. Une nouvelle clé sera créée si la page est à nouveau rendue publique."
#: bookwyrm/templates/annual_summary/layout.html:103
#: bookwyrm/templates/annual_summary/layout.html:104
#, python-format
msgid "Sadly %(display_name)s didnt finish any book in %(year)s"
msgstr ""
msgstr "Malheureusement, %(display_name)s na terminé aucun livre en %(year)s"
#: bookwyrm/templates/annual_summary/layout.html:109
#: bookwyrm/templates/annual_summary/layout.html:110
#, python-format
msgid "In %(year)s, %(display_name)s read %(books_total)s books<br />for a total of %(pages_total)s pages!"
msgstr ""
msgstr "En %(year)s, %(display_name)s a lu %(books_total)s livres<br />pour un total de %(pages_total)s pages !"
#: bookwyrm/templates/annual_summary/layout.html:111
#: bookwyrm/templates/annual_summary/layout.html:112
msgid "Thats great!"
msgstr ""
msgstr "Cest génial !"
#: bookwyrm/templates/annual_summary/layout.html:114
#: bookwyrm/templates/annual_summary/layout.html:115
#, python-format
msgid "That makes an average of %(pages_average)s pages per book."
msgstr ""
msgid "That makes an average of %(pages)s pages per book."
msgstr "Ce qui fait en moyenne %(pages)s pages par livre."
#: bookwyrm/templates/annual_summary/layout.html:119
#: bookwyrm/templates/annual_summary/layout.html:120
#, python-format
msgid "(%(no_page_number)s book doesnt have pages)"
msgid_plural "(%(no_page_number)s books dont have pages)"
msgstr[0] ""
msgstr[1] ""
msgstr[0] "(%(no_page_number)s livre na pas de pages)"
msgstr[1] "(%(no_page_number)s livres nont pas de pages)"
#: bookwyrm/templates/annual_summary/layout.html:135
#: bookwyrm/templates/annual_summary/layout.html:136
msgid "Their shortest read this year…"
msgstr ""
msgstr "Sa lecture la plus courte lannée…"
#: bookwyrm/templates/annual_summary/layout.html:142
#: bookwyrm/templates/annual_summary/layout.html:163
#: bookwyrm/templates/annual_summary/layout.html:203
#: bookwyrm/templates/annual_summary/layout.html:143
#: bookwyrm/templates/annual_summary/layout.html:164
#: bookwyrm/templates/annual_summary/layout.html:204
#: bookwyrm/templates/book/book.html:47
#: bookwyrm/templates/discover/large-book.html:22
#: bookwyrm/templates/landing/large-book.html:25
@ -340,34 +340,34 @@ msgstr ""
msgid "by"
msgstr "de"
#: bookwyrm/templates/annual_summary/layout.html:148
#: bookwyrm/templates/annual_summary/layout.html:169
#: bookwyrm/templates/annual_summary/layout.html:149
#: bookwyrm/templates/annual_summary/layout.html:170
#, python-format
msgid "<strong>%(pages)s</strong> pages"
msgstr ""
msgstr "<strong>%(pages)s</strong> pages"
#: bookwyrm/templates/annual_summary/layout.html:156
#: bookwyrm/templates/annual_summary/layout.html:157
msgid "…and the longest"
msgstr ""
msgstr "…et sa plus longue lecture"
#: bookwyrm/templates/annual_summary/layout.html:186
#: bookwyrm/templates/annual_summary/layout.html:187
#, python-format
msgid "%(display_name)s left %(ratings_total)s ratings, <br />their average rating is %(rating_average)s"
msgstr ""
msgstr "%(display_name)s a laissé %(ratings_total)s notes, <br />sa note moyenne est %(rating_average)s"
#: bookwyrm/templates/annual_summary/layout.html:196
#: bookwyrm/templates/annual_summary/layout.html:197
msgid "Their best rated review"
msgstr ""
msgstr "Son avis le mieux noté"
#: bookwyrm/templates/annual_summary/layout.html:209
#: bookwyrm/templates/annual_summary/layout.html:210
#, python-format
msgid "Their rating: <strong>%(rating)s</strong>"
msgstr ""
msgstr "Sa note : <strong>%(rating)s</strong>"
#: bookwyrm/templates/annual_summary/layout.html:226
#: bookwyrm/templates/annual_summary/layout.html:227
#, python-format
msgid "All the books %(display_name)s read in 2021"
msgstr ""
msgstr "Tous les livres que %(display_name)s a lus"
#: bookwyrm/templates/author/author.html:18
#: bookwyrm/templates/author/author.html:19
@ -1032,6 +1032,8 @@ msgstr "Vous pouvez décider de ne plus y figurer à nimporte quel moment dep
#: bookwyrm/templates/directory/directory.html:29
#: bookwyrm/templates/directory/directory.html:31
#: bookwyrm/templates/feed/goal_card.html:17
#: bookwyrm/templates/feed/summary_card.html:12
#: bookwyrm/templates/feed/summary_card.html:14
#: bookwyrm/templates/snippets/announcement.html:34
msgid "Dismiss message"
msgstr "Fermer le message"
@ -1379,14 +1381,14 @@ msgstr "Ne pas afficher les utilisateurs suggérés"
msgid "View directory"
msgstr "Voir le répertoire"
#: bookwyrm/templates/feed/summary_card.html:14
#: bookwyrm/templates/feed/summary_card.html:21
msgid "The end of the year is the best moment to take stock of all the books read during the last 12 months. How many pages have you read? Which book is your best-rated of the year? We compiled these stats, and more!"
msgstr ""
msgstr "La fin de l'année est le meilleur moment pour faire le point sur tous les livres lus au cours des 12 derniers mois. Combien de pages avez-vous lues ? Quel est votre livre le mieux noté de l'année ? Nous avons compilé ces statistiques, et plus encore !"
#: bookwyrm/templates/feed/summary_card.html:19
#: bookwyrm/templates/feed/summary_card.html:26
#, python-format
msgid "Discover your stats for %(year)s!"
msgstr ""
msgstr "Découvrez vos stats pour %(year)s !"
#: bookwyrm/templates/get_started/book_preview.html:6
#, python-format
@ -1724,7 +1726,7 @@ msgstr "ISBN"
#: bookwyrm/templates/import/import_status.html:110
msgid "Openlibrary key"
msgstr ""
msgstr "Clé Openlibrary"
#: bookwyrm/templates/import/import_status.html:114
#: bookwyrm/templates/shelf/shelf.html:145
@ -2079,7 +2081,7 @@ msgstr "%(list_name)s, une liste de %(owner)s"
#: bookwyrm/templates/lists/embed-list.html:17
#, python-format
msgid "on <a href=\"/\">%(site_name)s</a>"
msgstr ""
msgstr "sur <a href=\"/\">%(site_name)s</a>"
#: bookwyrm/templates/lists/embed-list.html:26
#: bookwyrm/templates/lists/list.html:29

Binary file not shown.

View file

@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: bookwyrm\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-12-27 22:24+0000\n"
"PO-Revision-Date: 2021-12-28 07:08\n"
"POT-Creation-Date: 2021-12-28 15:00+0000\n"
"PO-Revision-Date: 2021-12-28 16:03\n"
"Last-Translator: Mouse Reeve <mousereeve@riseup.net>\n"
"Language-Team: Galician\n"
"Language: gl\n"
@ -243,96 +243,96 @@ msgstr "Erro do servidor"
msgid "Something went wrong! Sorry about that."
msgstr "Algo fallou! Lamentámolo."
#: bookwyrm/templates/annual_summary/layout.html:6
#: bookwyrm/templates/annual_summary/layout.html:7
#: bookwyrm/templates/feed/summary_card.html:8
#, python-format
msgid "%(year)s in the books"
msgstr "Os libros en %(year)s"
#: bookwyrm/templates/annual_summary/layout.html:42
#: bookwyrm/templates/annual_summary/layout.html:43
#, python-format
msgid "%(year)s <em>in the books</em>"
msgstr "<em>os libros en </em> %(year)s"
#: bookwyrm/templates/annual_summary/layout.html:46
#: bookwyrm/templates/annual_summary/layout.html:47
#, python-format
msgid "<em>%(display_name)ss</em> year of reading"
msgstr "Un ano de lecturas de <em>%(display_name)s</em>"
#: bookwyrm/templates/annual_summary/layout.html:52
#: bookwyrm/templates/annual_summary/layout.html:53
msgid "Share this page"
msgstr "Comparte esta páxina"
#: bookwyrm/templates/annual_summary/layout.html:60
#: bookwyrm/templates/annual_summary/layout.html:61
msgid "Copy address"
msgstr "Copiar enderezo"
#: bookwyrm/templates/annual_summary/layout.html:60
#: bookwyrm/templates/annual_summary/layout.html:61
#: bookwyrm/templates/lists/list.html:194
msgid "Copied!"
msgstr "Copiado!"
#: bookwyrm/templates/annual_summary/layout.html:68
#: bookwyrm/templates/annual_summary/layout.html:69
msgid "Sharing status: <strong>public with key</strong>"
msgstr "Compartir estado: <strong>público con chave</strong>"
#: bookwyrm/templates/annual_summary/layout.html:69
#: bookwyrm/templates/annual_summary/layout.html:70
msgid "The page can be seen by anyone with the complete address."
msgstr "Esta páxina será visible para calquera que teña o enderezo completo."
#: bookwyrm/templates/annual_summary/layout.html:74
#: bookwyrm/templates/annual_summary/layout.html:75
msgid "Make page private"
msgstr "Facer privada a páxina"
#: bookwyrm/templates/annual_summary/layout.html:80
#: bookwyrm/templates/annual_summary/layout.html:81
msgid "Sharing status: <strong>private</strong>"
msgstr "Compartir estado: <strong>privado</strong>"
#: bookwyrm/templates/annual_summary/layout.html:81
#: bookwyrm/templates/annual_summary/layout.html:82
msgid "The page is private, only you can see it."
msgstr "Esta páxina é privada só ti podes vela."
#: bookwyrm/templates/annual_summary/layout.html:86
#: bookwyrm/templates/annual_summary/layout.html:87
msgid "Make page public"
msgstr "Facer pública a páxina"
#: bookwyrm/templates/annual_summary/layout.html:90
#: bookwyrm/templates/annual_summary/layout.html:91
msgid "When you make your page private, the old key wont give access to the page anymore. A new key will be created if the page is once again made public."
msgstr "Cando fas privada unha páxina, a chave antiga non dará acceso á mesma nunca máis. Crearase unha nova chave se volves a facer pública a páxina."
#: bookwyrm/templates/annual_summary/layout.html:103
#: bookwyrm/templates/annual_summary/layout.html:104
#, python-format
msgid "Sadly %(display_name)s didnt finish any book in %(year)s"
msgstr "Unha mágoa, pero %(display_name)s aínda non rematou ningún libro en %(year)s"
#: bookwyrm/templates/annual_summary/layout.html:109
#: bookwyrm/templates/annual_summary/layout.html:110
#, python-format
msgid "In %(year)s, %(display_name)s read %(books_total)s books<br />for a total of %(pages_total)s pages!"
msgstr "En %(year)s, %(display_name)s leu %(books_total)s libros<br /> cun total de %(pages_total)s páxinas!"
#: bookwyrm/templates/annual_summary/layout.html:111
#: bookwyrm/templates/annual_summary/layout.html:112
msgid "Thats great!"
msgstr "Está moi ben!"
#: bookwyrm/templates/annual_summary/layout.html:114
#: bookwyrm/templates/annual_summary/layout.html:115
#, python-format
msgid "That makes an average of %(pages_average)s pages per book."
msgstr "Iso fai unha media de %(pages_average)s páxinas por libro."
msgid "That makes an average of %(pages)s pages per book."
msgstr ""
#: bookwyrm/templates/annual_summary/layout.html:119
#: bookwyrm/templates/annual_summary/layout.html:120
#, python-format
msgid "(%(no_page_number)s book doesnt have pages)"
msgid_plural "(%(no_page_number)s books dont have pages)"
msgstr[0] "(%(no_page_number)s libro non ten páxinas)"
msgstr[1] "(%(no_page_number)s libros non teñen páxinas)"
#: bookwyrm/templates/annual_summary/layout.html:135
#: bookwyrm/templates/annual_summary/layout.html:136
msgid "Their shortest read this year…"
msgstr "A lectura máis curta deste ano…"
#: bookwyrm/templates/annual_summary/layout.html:142
#: bookwyrm/templates/annual_summary/layout.html:163
#: bookwyrm/templates/annual_summary/layout.html:203
#: bookwyrm/templates/annual_summary/layout.html:143
#: bookwyrm/templates/annual_summary/layout.html:164
#: bookwyrm/templates/annual_summary/layout.html:204
#: bookwyrm/templates/book/book.html:47
#: bookwyrm/templates/discover/large-book.html:22
#: bookwyrm/templates/landing/large-book.html:25
@ -340,31 +340,31 @@ msgstr "A lectura máis curta deste ano…"
msgid "by"
msgstr "por"
#: bookwyrm/templates/annual_summary/layout.html:148
#: bookwyrm/templates/annual_summary/layout.html:169
#: bookwyrm/templates/annual_summary/layout.html:149
#: bookwyrm/templates/annual_summary/layout.html:170
#, python-format
msgid "<strong>%(pages)s</strong> pages"
msgstr "<strong>%(pages)s</strong> páxinas"
#: bookwyrm/templates/annual_summary/layout.html:156
#: bookwyrm/templates/annual_summary/layout.html:157
msgid "…and the longest"
msgstr "…e a máis longa"
#: bookwyrm/templates/annual_summary/layout.html:186
#: bookwyrm/templates/annual_summary/layout.html:187
#, python-format
msgid "%(display_name)s left %(ratings_total)s ratings, <br />their average rating is %(rating_average)s"
msgstr "%(display_name)s escribiu %(ratings_total)s valoracións, <br />a media das súas valoracións é %(rating_average)s"
#: bookwyrm/templates/annual_summary/layout.html:196
#: bookwyrm/templates/annual_summary/layout.html:197
msgid "Their best rated review"
msgstr "A súa recensión máis valorada"
#: bookwyrm/templates/annual_summary/layout.html:209
#: bookwyrm/templates/annual_summary/layout.html:210
#, python-format
msgid "Their rating: <strong>%(rating)s</strong>"
msgstr "Valoración: <strong>%(rating)s</strong>"
#: bookwyrm/templates/annual_summary/layout.html:226
#: bookwyrm/templates/annual_summary/layout.html:227
#, python-format
msgid "All the books %(display_name)s read in 2021"
msgstr "Tódolos libros lidos por %(display_name)s en 2021"
@ -1032,6 +1032,8 @@ msgstr "Podes retirar o permiso en calquera momento nos <a href=\"%(path)s\">axu
#: bookwyrm/templates/directory/directory.html:29
#: bookwyrm/templates/directory/directory.html:31
#: bookwyrm/templates/feed/goal_card.html:17
#: bookwyrm/templates/feed/summary_card.html:12
#: bookwyrm/templates/feed/summary_card.html:14
#: bookwyrm/templates/snippets/announcement.html:34
msgid "Dismiss message"
msgstr "Desbotar mensaxe"
@ -1379,11 +1381,11 @@ msgstr "Non mostrar suxestións"
msgid "View directory"
msgstr "Ver directorio"
#: bookwyrm/templates/feed/summary_card.html:14
#: bookwyrm/templates/feed/summary_card.html:21
msgid "The end of the year is the best moment to take stock of all the books read during the last 12 months. How many pages have you read? Which book is your best-rated of the year? We compiled these stats, and more!"
msgstr "O final do ano é o mellor momento para botar unha ollada a tódolos libros lidos nos últimos 12 meses. Cantas páxinas liches? Cal é o libro con mellor puntuación do ano? Recollemos estas estatísticas e moitas máis!"
#: bookwyrm/templates/feed/summary_card.html:19
#: bookwyrm/templates/feed/summary_card.html:26
#, python-format
msgid "Discover your stats for %(year)s!"
msgstr "Mira aquí as túas estatísticas de %(year)s!"

Binary file not shown.

View file

@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: bookwyrm\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-12-27 22:24+0000\n"
"PO-Revision-Date: 2021-12-27 23:03\n"
"POT-Creation-Date: 2021-12-28 15:00+0000\n"
"PO-Revision-Date: 2021-12-28 16:03\n"
"Last-Translator: Mouse Reeve <mousereeve@riseup.net>\n"
"Language-Team: Lithuanian\n"
"Language: lt\n"
@ -243,83 +243,83 @@ msgstr "Serverio klaida"
msgid "Something went wrong! Sorry about that."
msgstr "Kažkas nepavyko. Atsiprašome."
#: bookwyrm/templates/annual_summary/layout.html:6
#: bookwyrm/templates/annual_summary/layout.html:7
#: bookwyrm/templates/feed/summary_card.html:8
#, python-format
msgid "%(year)s in the books"
msgstr ""
#: bookwyrm/templates/annual_summary/layout.html:42
#: bookwyrm/templates/annual_summary/layout.html:43
#, python-format
msgid "%(year)s <em>in the books</em>"
msgstr ""
#: bookwyrm/templates/annual_summary/layout.html:46
#: bookwyrm/templates/annual_summary/layout.html:47
#, python-format
msgid "<em>%(display_name)ss</em> year of reading"
msgstr ""
#: bookwyrm/templates/annual_summary/layout.html:52
#: bookwyrm/templates/annual_summary/layout.html:53
msgid "Share this page"
msgstr ""
#: bookwyrm/templates/annual_summary/layout.html:60
#: bookwyrm/templates/annual_summary/layout.html:61
msgid "Copy address"
msgstr ""
#: bookwyrm/templates/annual_summary/layout.html:60
#: bookwyrm/templates/annual_summary/layout.html:61
#: bookwyrm/templates/lists/list.html:194
msgid "Copied!"
msgstr "Nukopijuota"
#: bookwyrm/templates/annual_summary/layout.html:68
#: bookwyrm/templates/annual_summary/layout.html:69
msgid "Sharing status: <strong>public with key</strong>"
msgstr ""
#: bookwyrm/templates/annual_summary/layout.html:69
#: bookwyrm/templates/annual_summary/layout.html:70
msgid "The page can be seen by anyone with the complete address."
msgstr ""
#: bookwyrm/templates/annual_summary/layout.html:74
#: bookwyrm/templates/annual_summary/layout.html:75
msgid "Make page private"
msgstr ""
#: bookwyrm/templates/annual_summary/layout.html:80
#: bookwyrm/templates/annual_summary/layout.html:81
msgid "Sharing status: <strong>private</strong>"
msgstr ""
#: bookwyrm/templates/annual_summary/layout.html:81
#: bookwyrm/templates/annual_summary/layout.html:82
msgid "The page is private, only you can see it."
msgstr ""
#: bookwyrm/templates/annual_summary/layout.html:86
#: bookwyrm/templates/annual_summary/layout.html:87
msgid "Make page public"
msgstr ""
#: bookwyrm/templates/annual_summary/layout.html:90
#: bookwyrm/templates/annual_summary/layout.html:91
msgid "When you make your page private, the old key wont give access to the page anymore. A new key will be created if the page is once again made public."
msgstr ""
#: bookwyrm/templates/annual_summary/layout.html:103
#: bookwyrm/templates/annual_summary/layout.html:104
#, python-format
msgid "Sadly %(display_name)s didnt finish any book in %(year)s"
msgstr ""
#: bookwyrm/templates/annual_summary/layout.html:109
#: bookwyrm/templates/annual_summary/layout.html:110
#, python-format
msgid "In %(year)s, %(display_name)s read %(books_total)s books<br />for a total of %(pages_total)s pages!"
msgstr ""
#: bookwyrm/templates/annual_summary/layout.html:111
#: bookwyrm/templates/annual_summary/layout.html:112
msgid "Thats great!"
msgstr ""
#: bookwyrm/templates/annual_summary/layout.html:114
#: bookwyrm/templates/annual_summary/layout.html:115
#, python-format
msgid "That makes an average of %(pages_average)s pages per book."
msgid "That makes an average of %(pages)s pages per book."
msgstr ""
#: bookwyrm/templates/annual_summary/layout.html:119
#: bookwyrm/templates/annual_summary/layout.html:120
#, python-format
msgid "(%(no_page_number)s book doesnt have pages)"
msgid_plural "(%(no_page_number)s books dont have pages)"
@ -328,13 +328,13 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
#: bookwyrm/templates/annual_summary/layout.html:135
#: bookwyrm/templates/annual_summary/layout.html:136
msgid "Their shortest read this year…"
msgstr ""
#: bookwyrm/templates/annual_summary/layout.html:142
#: bookwyrm/templates/annual_summary/layout.html:163
#: bookwyrm/templates/annual_summary/layout.html:203
#: bookwyrm/templates/annual_summary/layout.html:143
#: bookwyrm/templates/annual_summary/layout.html:164
#: bookwyrm/templates/annual_summary/layout.html:204
#: bookwyrm/templates/book/book.html:47
#: bookwyrm/templates/discover/large-book.html:22
#: bookwyrm/templates/landing/large-book.html:25
@ -342,31 +342,31 @@ msgstr ""
msgid "by"
msgstr " "
#: bookwyrm/templates/annual_summary/layout.html:148
#: bookwyrm/templates/annual_summary/layout.html:169
#: bookwyrm/templates/annual_summary/layout.html:149
#: bookwyrm/templates/annual_summary/layout.html:170
#, python-format
msgid "<strong>%(pages)s</strong> pages"
msgstr ""
#: bookwyrm/templates/annual_summary/layout.html:156
#: bookwyrm/templates/annual_summary/layout.html:157
msgid "…and the longest"
msgstr ""
#: bookwyrm/templates/annual_summary/layout.html:186
#: bookwyrm/templates/annual_summary/layout.html:187
#, python-format
msgid "%(display_name)s left %(ratings_total)s ratings, <br />their average rating is %(rating_average)s"
msgstr ""
#: bookwyrm/templates/annual_summary/layout.html:196
#: bookwyrm/templates/annual_summary/layout.html:197
msgid "Their best rated review"
msgstr ""
#: bookwyrm/templates/annual_summary/layout.html:209
#: bookwyrm/templates/annual_summary/layout.html:210
#, python-format
msgid "Their rating: <strong>%(rating)s</strong>"
msgstr ""
#: bookwyrm/templates/annual_summary/layout.html:226
#: bookwyrm/templates/annual_summary/layout.html:227
#, python-format
msgid "All the books %(display_name)s read in 2021"
msgstr ""
@ -1036,6 +1036,8 @@ msgstr "Tai galite visada atšaukti <a href=\"%(path)s\">paskyros nustatymuose.<
#: bookwyrm/templates/directory/directory.html:29
#: bookwyrm/templates/directory/directory.html:31
#: bookwyrm/templates/feed/goal_card.html:17
#: bookwyrm/templates/feed/summary_card.html:12
#: bookwyrm/templates/feed/summary_card.html:14
#: bookwyrm/templates/snippets/announcement.html:34
msgid "Dismiss message"
msgstr "Pašalinti pranešimą"
@ -1387,11 +1389,11 @@ msgstr "Nerodyti siūlomų vartotojų"
msgid "View directory"
msgstr "Žiūrėti katalogą"
#: bookwyrm/templates/feed/summary_card.html:14
#: bookwyrm/templates/feed/summary_card.html:21
msgid "The end of the year is the best moment to take stock of all the books read during the last 12 months. How many pages have you read? Which book is your best-rated of the year? We compiled these stats, and more!"
msgstr ""
#: bookwyrm/templates/feed/summary_card.html:19
#: bookwyrm/templates/feed/summary_card.html:26
#, python-format
msgid "Discover your stats for %(year)s!"
msgstr ""

Binary file not shown.

View file

@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: bookwyrm\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-12-27 22:24+0000\n"
"PO-Revision-Date: 2021-12-28 02:57\n"
"POT-Creation-Date: 2021-12-28 15:00+0000\n"
"PO-Revision-Date: 2021-12-28 16:59\n"
"Last-Translator: Mouse Reeve <mousereeve@riseup.net>\n"
"Language-Team: Portuguese, Brazilian\n"
"Language: pt\n"
@ -243,96 +243,96 @@ msgstr "Erro no servidor"
msgid "Something went wrong! Sorry about that."
msgstr "Algo deu errado! Foi mal."
#: bookwyrm/templates/annual_summary/layout.html:6
#: bookwyrm/templates/annual_summary/layout.html:7
#: bookwyrm/templates/feed/summary_card.html:8
#, python-format
msgid "%(year)s in the books"
msgstr "Os livros de %(year)s"
#: bookwyrm/templates/annual_summary/layout.html:42
#: bookwyrm/templates/annual_summary/layout.html:43
#, python-format
msgid "%(year)s <em>in the books</em>"
msgstr "<em>Os livros de</em> %(year)s"
#: bookwyrm/templates/annual_summary/layout.html:46
#: bookwyrm/templates/annual_summary/layout.html:47
#, python-format
msgid "<em>%(display_name)ss</em> year of reading"
msgstr "O ano de leitura de <em>%(display_name)ss</em>"
msgstr "O ano de leitura de <em>%(display_name)s</em>"
#: bookwyrm/templates/annual_summary/layout.html:52
#: bookwyrm/templates/annual_summary/layout.html:53
msgid "Share this page"
msgstr "Compartilhe esta página"
#: bookwyrm/templates/annual_summary/layout.html:60
#: bookwyrm/templates/annual_summary/layout.html:61
msgid "Copy address"
msgstr "Copiar endereço"
#: bookwyrm/templates/annual_summary/layout.html:60
#: bookwyrm/templates/annual_summary/layout.html:61
#: bookwyrm/templates/lists/list.html:194
msgid "Copied!"
msgstr "Copiado!"
#: bookwyrm/templates/annual_summary/layout.html:68
msgid "Sharing status: <strong>public with key</strong>"
msgstr "Compartilhamento: <strong>pública com chave</strong>"
#: bookwyrm/templates/annual_summary/layout.html:69
msgid "Sharing status: <strong>public with key</strong>"
msgstr "Compartilhamento: <strong>público com chave</strong>"
#: bookwyrm/templates/annual_summary/layout.html:70
msgid "The page can be seen by anyone with the complete address."
msgstr "Esta página pode ser vista por qualquer pessoa que tenha seu link."
#: bookwyrm/templates/annual_summary/layout.html:74
#: bookwyrm/templates/annual_summary/layout.html:75
msgid "Make page private"
msgstr "Tornar a página particular"
#: bookwyrm/templates/annual_summary/layout.html:80
#: bookwyrm/templates/annual_summary/layout.html:81
msgid "Sharing status: <strong>private</strong>"
msgstr "Compartilhamento: <strong>particular</strong>"
#: bookwyrm/templates/annual_summary/layout.html:81
#: bookwyrm/templates/annual_summary/layout.html:82
msgid "The page is private, only you can see it."
msgstr "A página é particular, só você pode vê-la."
#: bookwyrm/templates/annual_summary/layout.html:86
#: bookwyrm/templates/annual_summary/layout.html:87
msgid "Make page public"
msgstr "Tornar a página pública"
#: bookwyrm/templates/annual_summary/layout.html:90
#: bookwyrm/templates/annual_summary/layout.html:91
msgid "When you make your page private, the old key wont give access to the page anymore. A new key will be created if the page is once again made public."
msgstr "Ao tornar a página particular, a chave antiga passa a não funcionar mais. Uma nova chave será gerada quando a página for tornada pública novamente."
#: bookwyrm/templates/annual_summary/layout.html:103
#: bookwyrm/templates/annual_summary/layout.html:104
#, python-format
msgid "Sadly %(display_name)s didnt finish any book in %(year)s"
msgstr "Infelizmente %(display_name)s não terminou nenhum livro em %(year)s"
#: bookwyrm/templates/annual_summary/layout.html:109
#: bookwyrm/templates/annual_summary/layout.html:110
#, python-format
msgid "In %(year)s, %(display_name)s read %(books_total)s books<br />for a total of %(pages_total)s pages!"
msgstr "Em %(year)s, %(display_name)s leu %(books_total)s livros,<br />um total de %(pages_total)s páginas!"
#: bookwyrm/templates/annual_summary/layout.html:111
#: bookwyrm/templates/annual_summary/layout.html:112
msgid "Thats great!"
msgstr "Muito legal!"
#: bookwyrm/templates/annual_summary/layout.html:114
#: bookwyrm/templates/annual_summary/layout.html:115
#, python-format
msgid "That makes an average of %(pages_average)s pages per book."
msgstr "Os livros têm em média %(pages_average)s páginas."
msgid "That makes an average of %(pages)s pages per book."
msgstr "Isso dá uma média de %(pages)s páginas por livro."
#: bookwyrm/templates/annual_summary/layout.html:119
#: bookwyrm/templates/annual_summary/layout.html:120
#, python-format
msgid "(%(no_page_number)s book doesnt have pages)"
msgid_plural "(%(no_page_number)s books dont have pages)"
msgstr[0] ""
msgstr[0] "(%(no_page_number)s livro não tem páginas cadastradas)"
msgstr[1] "(%(no_page_number)s livros não têm páginas cadastradas)"
#: bookwyrm/templates/annual_summary/layout.html:135
#: bookwyrm/templates/annual_summary/layout.html:136
msgid "Their shortest read this year…"
msgstr "A leitura mais curta do ano…"
#: bookwyrm/templates/annual_summary/layout.html:142
#: bookwyrm/templates/annual_summary/layout.html:163
#: bookwyrm/templates/annual_summary/layout.html:203
#: bookwyrm/templates/annual_summary/layout.html:143
#: bookwyrm/templates/annual_summary/layout.html:164
#: bookwyrm/templates/annual_summary/layout.html:204
#: bookwyrm/templates/book/book.html:47
#: bookwyrm/templates/discover/large-book.html:22
#: bookwyrm/templates/landing/large-book.html:25
@ -340,31 +340,31 @@ msgstr "A leitura mais curta do ano…"
msgid "by"
msgstr "de"
#: bookwyrm/templates/annual_summary/layout.html:148
#: bookwyrm/templates/annual_summary/layout.html:169
#: bookwyrm/templates/annual_summary/layout.html:149
#: bookwyrm/templates/annual_summary/layout.html:170
#, python-format
msgid "<strong>%(pages)s</strong> pages"
msgstr "<strong>%(pages)s</strong> páginas"
#: bookwyrm/templates/annual_summary/layout.html:156
#: bookwyrm/templates/annual_summary/layout.html:157
msgid "…and the longest"
msgstr "…e a mais longa"
#: bookwyrm/templates/annual_summary/layout.html:186
#: bookwyrm/templates/annual_summary/layout.html:187
#, python-format
msgid "%(display_name)s left %(ratings_total)s ratings, <br />their average rating is %(rating_average)s"
msgstr "%(display_name)s fez %(ratings_total)s avaliações, <br />com uma média de %(rating_average)s"
msgstr "%(display_name)s fez %(ratings_total)s avaliações, <br />e sua nota média é %(rating_average)s"
#: bookwyrm/templates/annual_summary/layout.html:196
#: bookwyrm/templates/annual_summary/layout.html:197
msgid "Their best rated review"
msgstr "Sua melhor avaliação"
#: bookwyrm/templates/annual_summary/layout.html:209
#: bookwyrm/templates/annual_summary/layout.html:210
#, python-format
msgid "Their rating: <strong>%(rating)s</strong>"
msgstr "Avaliação: <strong>%(rating)s</strong>"
#: bookwyrm/templates/annual_summary/layout.html:226
#: bookwyrm/templates/annual_summary/layout.html:227
#, python-format
msgid "All the books %(display_name)s read in 2021"
msgstr "Todos os livros que %(display_name)s leu em 2021"
@ -1032,6 +1032,8 @@ msgstr "Você pode desabilitar esta opção a qualquer momento em suas <a href=\
#: bookwyrm/templates/directory/directory.html:29
#: bookwyrm/templates/directory/directory.html:31
#: bookwyrm/templates/feed/goal_card.html:17
#: bookwyrm/templates/feed/summary_card.html:12
#: bookwyrm/templates/feed/summary_card.html:14
#: bookwyrm/templates/snippets/announcement.html:34
msgid "Dismiss message"
msgstr "Dispensar mensagem"
@ -1379,11 +1381,11 @@ msgstr "Não mostrar usuários sugeridos"
msgid "View directory"
msgstr "Ver diretório"
#: bookwyrm/templates/feed/summary_card.html:14
#: bookwyrm/templates/feed/summary_card.html:21
msgid "The end of the year is the best moment to take stock of all the books read during the last 12 months. How many pages have you read? Which book is your best-rated of the year? We compiled these stats, and more!"
msgstr "O fim do ano é o melhor momento para pensar nos livros que lemos nos últimos 12 meses. Quantas páginas você leu? Qual foi o seu livro mais bem avaliado? Compilamos esses e outros dados!"
#: bookwyrm/templates/feed/summary_card.html:19
#: bookwyrm/templates/feed/summary_card.html:26
#, python-format
msgid "Discover your stats for %(year)s!"
msgstr "Descubra suas estatísticas de %(year)s!"

Binary file not shown.

View file

@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: bookwyrm\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-12-27 22:24+0000\n"
"PO-Revision-Date: 2021-12-28 01:57\n"
"POT-Creation-Date: 2021-12-28 15:00+0000\n"
"PO-Revision-Date: 2021-12-28 16:03\n"
"Last-Translator: Mouse Reeve <mousereeve@riseup.net>\n"
"Language-Team: Chinese Simplified\n"
"Language: zh\n"
@ -243,95 +243,95 @@ msgstr "服务器错误"
msgid "Something went wrong! Sorry about that."
msgstr "某些东西出错了!对不起啦。"
#: bookwyrm/templates/annual_summary/layout.html:6
#: bookwyrm/templates/annual_summary/layout.html:7
#: bookwyrm/templates/feed/summary_card.html:8
#, python-format
msgid "%(year)s in the books"
msgstr ""
#: bookwyrm/templates/annual_summary/layout.html:42
#: bookwyrm/templates/annual_summary/layout.html:43
#, python-format
msgid "%(year)s <em>in the books</em>"
msgstr ""
#: bookwyrm/templates/annual_summary/layout.html:46
#: bookwyrm/templates/annual_summary/layout.html:47
#, python-format
msgid "<em>%(display_name)ss</em> year of reading"
msgstr ""
#: bookwyrm/templates/annual_summary/layout.html:52
#: bookwyrm/templates/annual_summary/layout.html:53
msgid "Share this page"
msgstr ""
#: bookwyrm/templates/annual_summary/layout.html:60
#: bookwyrm/templates/annual_summary/layout.html:61
msgid "Copy address"
msgstr ""
#: bookwyrm/templates/annual_summary/layout.html:60
#: bookwyrm/templates/annual_summary/layout.html:61
#: bookwyrm/templates/lists/list.html:194
msgid "Copied!"
msgstr ""
#: bookwyrm/templates/annual_summary/layout.html:68
#: bookwyrm/templates/annual_summary/layout.html:69
msgid "Sharing status: <strong>public with key</strong>"
msgstr ""
#: bookwyrm/templates/annual_summary/layout.html:69
#: bookwyrm/templates/annual_summary/layout.html:70
msgid "The page can be seen by anyone with the complete address."
msgstr ""
#: bookwyrm/templates/annual_summary/layout.html:74
#: bookwyrm/templates/annual_summary/layout.html:75
msgid "Make page private"
msgstr ""
#: bookwyrm/templates/annual_summary/layout.html:80
#: bookwyrm/templates/annual_summary/layout.html:81
msgid "Sharing status: <strong>private</strong>"
msgstr ""
#: bookwyrm/templates/annual_summary/layout.html:81
#: bookwyrm/templates/annual_summary/layout.html:82
msgid "The page is private, only you can see it."
msgstr ""
#: bookwyrm/templates/annual_summary/layout.html:86
#: bookwyrm/templates/annual_summary/layout.html:87
msgid "Make page public"
msgstr ""
#: bookwyrm/templates/annual_summary/layout.html:90
#: bookwyrm/templates/annual_summary/layout.html:91
msgid "When you make your page private, the old key wont give access to the page anymore. A new key will be created if the page is once again made public."
msgstr ""
#: bookwyrm/templates/annual_summary/layout.html:103
#: bookwyrm/templates/annual_summary/layout.html:104
#, python-format
msgid "Sadly %(display_name)s didnt finish any book in %(year)s"
msgstr ""
#: bookwyrm/templates/annual_summary/layout.html:109
#: bookwyrm/templates/annual_summary/layout.html:110
#, python-format
msgid "In %(year)s, %(display_name)s read %(books_total)s books<br />for a total of %(pages_total)s pages!"
msgstr ""
#: bookwyrm/templates/annual_summary/layout.html:111
#: bookwyrm/templates/annual_summary/layout.html:112
msgid "Thats great!"
msgstr ""
#: bookwyrm/templates/annual_summary/layout.html:114
#: bookwyrm/templates/annual_summary/layout.html:115
#, python-format
msgid "That makes an average of %(pages_average)s pages per book."
msgid "That makes an average of %(pages)s pages per book."
msgstr ""
#: bookwyrm/templates/annual_summary/layout.html:119
#: bookwyrm/templates/annual_summary/layout.html:120
#, python-format
msgid "(%(no_page_number)s book doesnt have pages)"
msgid_plural "(%(no_page_number)s books dont have pages)"
msgstr[0] ""
#: bookwyrm/templates/annual_summary/layout.html:135
#: bookwyrm/templates/annual_summary/layout.html:136
msgid "Their shortest read this year…"
msgstr ""
#: bookwyrm/templates/annual_summary/layout.html:142
#: bookwyrm/templates/annual_summary/layout.html:163
#: bookwyrm/templates/annual_summary/layout.html:203
#: bookwyrm/templates/annual_summary/layout.html:143
#: bookwyrm/templates/annual_summary/layout.html:164
#: bookwyrm/templates/annual_summary/layout.html:204
#: bookwyrm/templates/book/book.html:47
#: bookwyrm/templates/discover/large-book.html:22
#: bookwyrm/templates/landing/large-book.html:25
@ -339,31 +339,31 @@ msgstr ""
msgid "by"
msgstr "作者"
#: bookwyrm/templates/annual_summary/layout.html:148
#: bookwyrm/templates/annual_summary/layout.html:169
#: bookwyrm/templates/annual_summary/layout.html:149
#: bookwyrm/templates/annual_summary/layout.html:170
#, python-format
msgid "<strong>%(pages)s</strong> pages"
msgstr ""
#: bookwyrm/templates/annual_summary/layout.html:156
#: bookwyrm/templates/annual_summary/layout.html:157
msgid "…and the longest"
msgstr ""
#: bookwyrm/templates/annual_summary/layout.html:186
#: bookwyrm/templates/annual_summary/layout.html:187
#, python-format
msgid "%(display_name)s left %(ratings_total)s ratings, <br />their average rating is %(rating_average)s"
msgstr ""
#: bookwyrm/templates/annual_summary/layout.html:196
#: bookwyrm/templates/annual_summary/layout.html:197
msgid "Their best rated review"
msgstr ""
#: bookwyrm/templates/annual_summary/layout.html:209
#: bookwyrm/templates/annual_summary/layout.html:210
#, python-format
msgid "Their rating: <strong>%(rating)s</strong>"
msgstr ""
#: bookwyrm/templates/annual_summary/layout.html:226
#: bookwyrm/templates/annual_summary/layout.html:227
#, python-format
msgid "All the books %(display_name)s read in 2021"
msgstr ""
@ -1030,6 +1030,8 @@ msgstr "你可以在任何时候从你的 <a href=\"%(path)s\">个人资料设
#: bookwyrm/templates/directory/directory.html:29
#: bookwyrm/templates/directory/directory.html:31
#: bookwyrm/templates/feed/goal_card.html:17
#: bookwyrm/templates/feed/summary_card.html:12
#: bookwyrm/templates/feed/summary_card.html:14
#: bookwyrm/templates/snippets/announcement.html:34
msgid "Dismiss message"
msgstr "遣散消息"
@ -1375,11 +1377,11 @@ msgstr "不显示推荐用户"
msgid "View directory"
msgstr "查看目录"
#: bookwyrm/templates/feed/summary_card.html:14
#: bookwyrm/templates/feed/summary_card.html:21
msgid "The end of the year is the best moment to take stock of all the books read during the last 12 months. How many pages have you read? Which book is your best-rated of the year? We compiled these stats, and more!"
msgstr ""
#: bookwyrm/templates/feed/summary_card.html:19
#: bookwyrm/templates/feed/summary_card.html:26
#, python-format
msgid "Discover your stats for %(year)s!"
msgstr ""

Binary file not shown.

View file

@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: bookwyrm\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-12-27 22:24+0000\n"
"PO-Revision-Date: 2021-12-27 23:03\n"
"POT-Creation-Date: 2021-12-28 15:00+0000\n"
"PO-Revision-Date: 2021-12-28 16:03\n"
"Last-Translator: Mouse Reeve <mousereeve@riseup.net>\n"
"Language-Team: Chinese Traditional\n"
"Language: zh\n"
@ -243,95 +243,95 @@ msgstr "伺服器錯誤"
msgid "Something went wrong! Sorry about that."
msgstr "某些東西出錯了!抱歉。"
#: bookwyrm/templates/annual_summary/layout.html:6
#: bookwyrm/templates/annual_summary/layout.html:7
#: bookwyrm/templates/feed/summary_card.html:8
#, python-format
msgid "%(year)s in the books"
msgstr ""
#: bookwyrm/templates/annual_summary/layout.html:42
#: bookwyrm/templates/annual_summary/layout.html:43
#, python-format
msgid "%(year)s <em>in the books</em>"
msgstr ""
#: bookwyrm/templates/annual_summary/layout.html:46
#: bookwyrm/templates/annual_summary/layout.html:47
#, python-format
msgid "<em>%(display_name)ss</em> year of reading"
msgstr ""
#: bookwyrm/templates/annual_summary/layout.html:52
#: bookwyrm/templates/annual_summary/layout.html:53
msgid "Share this page"
msgstr ""
#: bookwyrm/templates/annual_summary/layout.html:60
#: bookwyrm/templates/annual_summary/layout.html:61
msgid "Copy address"
msgstr ""
#: bookwyrm/templates/annual_summary/layout.html:60
#: bookwyrm/templates/annual_summary/layout.html:61
#: bookwyrm/templates/lists/list.html:194
msgid "Copied!"
msgstr ""
#: bookwyrm/templates/annual_summary/layout.html:68
#: bookwyrm/templates/annual_summary/layout.html:69
msgid "Sharing status: <strong>public with key</strong>"
msgstr ""
#: bookwyrm/templates/annual_summary/layout.html:69
#: bookwyrm/templates/annual_summary/layout.html:70
msgid "The page can be seen by anyone with the complete address."
msgstr ""
#: bookwyrm/templates/annual_summary/layout.html:74
#: bookwyrm/templates/annual_summary/layout.html:75
msgid "Make page private"
msgstr ""
#: bookwyrm/templates/annual_summary/layout.html:80
#: bookwyrm/templates/annual_summary/layout.html:81
msgid "Sharing status: <strong>private</strong>"
msgstr ""
#: bookwyrm/templates/annual_summary/layout.html:81
#: bookwyrm/templates/annual_summary/layout.html:82
msgid "The page is private, only you can see it."
msgstr ""
#: bookwyrm/templates/annual_summary/layout.html:86
#: bookwyrm/templates/annual_summary/layout.html:87
msgid "Make page public"
msgstr ""
#: bookwyrm/templates/annual_summary/layout.html:90
#: bookwyrm/templates/annual_summary/layout.html:91
msgid "When you make your page private, the old key wont give access to the page anymore. A new key will be created if the page is once again made public."
msgstr ""
#: bookwyrm/templates/annual_summary/layout.html:103
#: bookwyrm/templates/annual_summary/layout.html:104
#, python-format
msgid "Sadly %(display_name)s didnt finish any book in %(year)s"
msgstr ""
#: bookwyrm/templates/annual_summary/layout.html:109
#: bookwyrm/templates/annual_summary/layout.html:110
#, python-format
msgid "In %(year)s, %(display_name)s read %(books_total)s books<br />for a total of %(pages_total)s pages!"
msgstr ""
#: bookwyrm/templates/annual_summary/layout.html:111
#: bookwyrm/templates/annual_summary/layout.html:112
msgid "Thats great!"
msgstr ""
#: bookwyrm/templates/annual_summary/layout.html:114
#: bookwyrm/templates/annual_summary/layout.html:115
#, python-format
msgid "That makes an average of %(pages_average)s pages per book."
msgid "That makes an average of %(pages)s pages per book."
msgstr ""
#: bookwyrm/templates/annual_summary/layout.html:119
#: bookwyrm/templates/annual_summary/layout.html:120
#, python-format
msgid "(%(no_page_number)s book doesnt have pages)"
msgid_plural "(%(no_page_number)s books dont have pages)"
msgstr[0] ""
#: bookwyrm/templates/annual_summary/layout.html:135
#: bookwyrm/templates/annual_summary/layout.html:136
msgid "Their shortest read this year…"
msgstr ""
#: bookwyrm/templates/annual_summary/layout.html:142
#: bookwyrm/templates/annual_summary/layout.html:163
#: bookwyrm/templates/annual_summary/layout.html:203
#: bookwyrm/templates/annual_summary/layout.html:143
#: bookwyrm/templates/annual_summary/layout.html:164
#: bookwyrm/templates/annual_summary/layout.html:204
#: bookwyrm/templates/book/book.html:47
#: bookwyrm/templates/discover/large-book.html:22
#: bookwyrm/templates/landing/large-book.html:25
@ -339,31 +339,31 @@ msgstr ""
msgid "by"
msgstr "作者"
#: bookwyrm/templates/annual_summary/layout.html:148
#: bookwyrm/templates/annual_summary/layout.html:169
#: bookwyrm/templates/annual_summary/layout.html:149
#: bookwyrm/templates/annual_summary/layout.html:170
#, python-format
msgid "<strong>%(pages)s</strong> pages"
msgstr ""
#: bookwyrm/templates/annual_summary/layout.html:156
#: bookwyrm/templates/annual_summary/layout.html:157
msgid "…and the longest"
msgstr ""
#: bookwyrm/templates/annual_summary/layout.html:186
#: bookwyrm/templates/annual_summary/layout.html:187
#, python-format
msgid "%(display_name)s left %(ratings_total)s ratings, <br />their average rating is %(rating_average)s"
msgstr ""
#: bookwyrm/templates/annual_summary/layout.html:196
#: bookwyrm/templates/annual_summary/layout.html:197
msgid "Their best rated review"
msgstr ""
#: bookwyrm/templates/annual_summary/layout.html:209
#: bookwyrm/templates/annual_summary/layout.html:210
#, python-format
msgid "Their rating: <strong>%(rating)s</strong>"
msgstr ""
#: bookwyrm/templates/annual_summary/layout.html:226
#: bookwyrm/templates/annual_summary/layout.html:227
#, python-format
msgid "All the books %(display_name)s read in 2021"
msgstr ""
@ -1030,6 +1030,8 @@ msgstr "你可以在任何時候從你的 <a href=\"%(path)s\">使用者資料
#: bookwyrm/templates/directory/directory.html:29
#: bookwyrm/templates/directory/directory.html:31
#: bookwyrm/templates/feed/goal_card.html:17
#: bookwyrm/templates/feed/summary_card.html:12
#: bookwyrm/templates/feed/summary_card.html:14
#: bookwyrm/templates/snippets/announcement.html:34
msgid "Dismiss message"
msgstr "關閉訊息"
@ -1375,11 +1377,11 @@ msgstr ""
msgid "View directory"
msgstr ""
#: bookwyrm/templates/feed/summary_card.html:14
#: bookwyrm/templates/feed/summary_card.html:21
msgid "The end of the year is the best moment to take stock of all the books read during the last 12 months. How many pages have you read? Which book is your best-rated of the year? We compiled these stats, and more!"
msgstr ""
#: bookwyrm/templates/feed/summary_card.html:19
#: bookwyrm/templates/feed/summary_card.html:26
#, python-format
msgid "Discover your stats for %(year)s!"
msgstr ""

View file

@ -4,10 +4,11 @@
},
"devDependencies": {
"eslint": "^7.23.0",
"prettier": "2.5.1",
"stylelint": "^13.12.0",
"stylelint-config-standard": "^21.0.0",
"stylelint-order": "^4.1.0",
"watch": "^1.0.2"
"watch": "^0.13.0"
},
"dependencies": {
"merge": "2.1.1",

View file

@ -1,6 +1,6 @@
celery==4.4.2
colorthief==0.2.1
Django==3.2.5
Django==3.2.10
django-imagekit==4.1.0
django-model-utils==4.0.0
environs==9.3.4

3382
yarn.lock

File diff suppressed because it is too large Load diff