diff --git a/bookwyrm/forms.py b/bookwyrm/forms.py index c112186ae..298f73da9 100644 --- a/bookwyrm/forms.py +++ b/bookwyrm/forms.py @@ -293,7 +293,13 @@ class AnnouncementForm(CustomForm): class ListForm(CustomForm): class Meta: model = models.List - fields = ["user", "name", "description", "curation", "privacy"] + fields = ["user", "name", "description", "curation", "privacy", "group"] + + +class GroupForm(CustomForm): + class Meta: + model = models.Group + fields = ["user", "privacy", "name", "description"] class ReportForm(CustomForm): diff --git a/bookwyrm/importers/goodreads_import.py b/bookwyrm/importers/goodreads_import.py index 7b577ea85..c62e65827 100644 --- a/bookwyrm/importers/goodreads_import.py +++ b/bookwyrm/importers/goodreads_import.py @@ -3,10 +3,10 @@ from . import Importer class GoodreadsImporter(Importer): - """GoodReads is the default importer, thus Importer follows its structure. + """Goodreads is the default importer, thus Importer follows its structure. For a more complete example of overriding see librarything_import.py""" - service = "GoodReads" + service = "Goodreads" def parse_fields(self, entry): """handle the specific fields in goodreads csvs""" diff --git a/bookwyrm/importers/importer.py b/bookwyrm/importers/importer.py index a10b4060c..6d898a2a3 100644 --- a/bookwyrm/importers/importer.py +++ b/bookwyrm/importers/importer.py @@ -1,4 +1,4 @@ -""" handle reading a csv from an external service, defaults are from GoodReads """ +""" handle reading a csv from an external service, defaults are from Goodreads """ import csv import logging diff --git a/bookwyrm/migrations/0107_auto_20211016_0639.py b/bookwyrm/migrations/0107_auto_20211016_0639.py new file mode 100644 index 000000000..61dffca34 --- /dev/null +++ b/bookwyrm/migrations/0107_auto_20211016_0639.py @@ -0,0 +1,871 @@ +# Generated by Django 3.2.5 on 2021-10-16 06:39 + +import bookwyrm.models.fields +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ("bookwyrm", "0106_user_preferred_language"), + ] + + operations = [ + migrations.CreateModel( + name="Group", + fields=[ + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("created_date", models.DateTimeField(auto_now_add=True)), + ("updated_date", models.DateTimeField(auto_now=True)), + ( + "remote_id", + bookwyrm.models.fields.RemoteIdField( + max_length=255, + null=True, + validators=[bookwyrm.models.fields.validate_remote_id], + ), + ), + ("name", bookwyrm.models.fields.CharField(max_length=100)), + ( + "description", + bookwyrm.models.fields.TextField(blank=True, null=True), + ), + ( + "privacy", + bookwyrm.models.fields.PrivacyField( + choices=[ + ("public", "Public"), + ("unlisted", "Unlisted"), + ("followers", "Followers"), + ("direct", "Direct"), + ], + default="public", + max_length=255, + ), + ), + ], + options={ + "abstract": False, + }, + ), + migrations.CreateModel( + name="GroupMember", + fields=[ + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("created_date", models.DateTimeField(auto_now_add=True)), + ("updated_date", models.DateTimeField(auto_now=True)), + ], + ), + migrations.CreateModel( + name="GroupMemberInvitation", + fields=[ + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("created_date", models.DateTimeField(auto_now_add=True)), + ], + ), + migrations.RemoveConstraint( + model_name="notification", + name="notification_type_valid", + ), + migrations.AlterField( + model_name="list", + name="curation", + field=bookwyrm.models.fields.CharField( + choices=[ + ("closed", "Closed"), + ("open", "Open"), + ("curated", "Curated"), + ("group", "Group"), + ], + default="closed", + max_length=255, + ), + ), + migrations.AlterField( + model_name="notification", + name="notification_type", + field=models.CharField( + choices=[ + ("FAVORITE", "Favorite"), + ("REPLY", "Reply"), + ("MENTION", "Mention"), + ("TAG", "Tag"), + ("FOLLOW", "Follow"), + ("FOLLOW_REQUEST", "Follow Request"), + ("BOOST", "Boost"), + ("IMPORT", "Import"), + ("ADD", "Add"), + ("REPORT", "Report"), + ("INVITE", "Invite"), + ("ACCEPT", "Accept"), + ("JOIN", "Join"), + ("LEAVE", "Leave"), + ("REMOVE", "Remove"), + ], + max_length=255, + ), + ), + migrations.AlterField( + model_name="user", + name="preferred_language", + field=models.CharField( + blank=True, + choices=[ + ("en-us", "English"), + ("de-de", "Deutsch (German)"), + ("es", "Español (Spanish)"), + ("fr-fr", "Français (French)"), + ("zh-hans", "简体中文 (Simplified Chinese)"), + ("zh-hant", "繁體中文 (Traditional Chinese)"), + ], + max_length=255, + null=True, + ), + ), + migrations.AlterField( + model_name="user", + name="preferred_timezone", + field=models.CharField( + choices=[ + ("Africa/Abidjan", "Africa/Abidjan"), + ("Africa/Accra", "Africa/Accra"), + ("Africa/Addis_Ababa", "Africa/Addis_Ababa"), + ("Africa/Algiers", "Africa/Algiers"), + ("Africa/Asmara", "Africa/Asmara"), + ("Africa/Asmera", "Africa/Asmera"), + ("Africa/Bamako", "Africa/Bamako"), + ("Africa/Bangui", "Africa/Bangui"), + ("Africa/Banjul", "Africa/Banjul"), + ("Africa/Bissau", "Africa/Bissau"), + ("Africa/Blantyre", "Africa/Blantyre"), + ("Africa/Brazzaville", "Africa/Brazzaville"), + ("Africa/Bujumbura", "Africa/Bujumbura"), + ("Africa/Cairo", "Africa/Cairo"), + ("Africa/Casablanca", "Africa/Casablanca"), + ("Africa/Ceuta", "Africa/Ceuta"), + ("Africa/Conakry", "Africa/Conakry"), + ("Africa/Dakar", "Africa/Dakar"), + ("Africa/Dar_es_Salaam", "Africa/Dar_es_Salaam"), + ("Africa/Djibouti", "Africa/Djibouti"), + ("Africa/Douala", "Africa/Douala"), + ("Africa/El_Aaiun", "Africa/El_Aaiun"), + ("Africa/Freetown", "Africa/Freetown"), + ("Africa/Gaborone", "Africa/Gaborone"), + ("Africa/Harare", "Africa/Harare"), + ("Africa/Johannesburg", "Africa/Johannesburg"), + ("Africa/Juba", "Africa/Juba"), + ("Africa/Kampala", "Africa/Kampala"), + ("Africa/Khartoum", "Africa/Khartoum"), + ("Africa/Kigali", "Africa/Kigali"), + ("Africa/Kinshasa", "Africa/Kinshasa"), + ("Africa/Lagos", "Africa/Lagos"), + ("Africa/Libreville", "Africa/Libreville"), + ("Africa/Lome", "Africa/Lome"), + ("Africa/Luanda", "Africa/Luanda"), + ("Africa/Lubumbashi", "Africa/Lubumbashi"), + ("Africa/Lusaka", "Africa/Lusaka"), + ("Africa/Malabo", "Africa/Malabo"), + ("Africa/Maputo", "Africa/Maputo"), + ("Africa/Maseru", "Africa/Maseru"), + ("Africa/Mbabane", "Africa/Mbabane"), + ("Africa/Mogadishu", "Africa/Mogadishu"), + ("Africa/Monrovia", "Africa/Monrovia"), + ("Africa/Nairobi", "Africa/Nairobi"), + ("Africa/Ndjamena", "Africa/Ndjamena"), + ("Africa/Niamey", "Africa/Niamey"), + ("Africa/Nouakchott", "Africa/Nouakchott"), + ("Africa/Ouagadougou", "Africa/Ouagadougou"), + ("Africa/Porto-Novo", "Africa/Porto-Novo"), + ("Africa/Sao_Tome", "Africa/Sao_Tome"), + ("Africa/Timbuktu", "Africa/Timbuktu"), + ("Africa/Tripoli", "Africa/Tripoli"), + ("Africa/Tunis", "Africa/Tunis"), + ("Africa/Windhoek", "Africa/Windhoek"), + ("America/Adak", "America/Adak"), + ("America/Anchorage", "America/Anchorage"), + ("America/Anguilla", "America/Anguilla"), + ("America/Antigua", "America/Antigua"), + ("America/Araguaina", "America/Araguaina"), + ( + "America/Argentina/Buenos_Aires", + "America/Argentina/Buenos_Aires", + ), + ("America/Argentina/Catamarca", "America/Argentina/Catamarca"), + ( + "America/Argentina/ComodRivadavia", + "America/Argentina/ComodRivadavia", + ), + ("America/Argentina/Cordoba", "America/Argentina/Cordoba"), + ("America/Argentina/Jujuy", "America/Argentina/Jujuy"), + ("America/Argentina/La_Rioja", "America/Argentina/La_Rioja"), + ("America/Argentina/Mendoza", "America/Argentina/Mendoza"), + ( + "America/Argentina/Rio_Gallegos", + "America/Argentina/Rio_Gallegos", + ), + ("America/Argentina/Salta", "America/Argentina/Salta"), + ("America/Argentina/San_Juan", "America/Argentina/San_Juan"), + ("America/Argentina/San_Luis", "America/Argentina/San_Luis"), + ("America/Argentina/Tucuman", "America/Argentina/Tucuman"), + ("America/Argentina/Ushuaia", "America/Argentina/Ushuaia"), + ("America/Aruba", "America/Aruba"), + ("America/Asuncion", "America/Asuncion"), + ("America/Atikokan", "America/Atikokan"), + ("America/Atka", "America/Atka"), + ("America/Bahia", "America/Bahia"), + ("America/Bahia_Banderas", "America/Bahia_Banderas"), + ("America/Barbados", "America/Barbados"), + ("America/Belem", "America/Belem"), + ("America/Belize", "America/Belize"), + ("America/Blanc-Sablon", "America/Blanc-Sablon"), + ("America/Boa_Vista", "America/Boa_Vista"), + ("America/Bogota", "America/Bogota"), + ("America/Boise", "America/Boise"), + ("America/Buenos_Aires", "America/Buenos_Aires"), + ("America/Cambridge_Bay", "America/Cambridge_Bay"), + ("America/Campo_Grande", "America/Campo_Grande"), + ("America/Cancun", "America/Cancun"), + ("America/Caracas", "America/Caracas"), + ("America/Catamarca", "America/Catamarca"), + ("America/Cayenne", "America/Cayenne"), + ("America/Cayman", "America/Cayman"), + ("America/Chicago", "America/Chicago"), + ("America/Chihuahua", "America/Chihuahua"), + ("America/Coral_Harbour", "America/Coral_Harbour"), + ("America/Cordoba", "America/Cordoba"), + ("America/Costa_Rica", "America/Costa_Rica"), + ("America/Creston", "America/Creston"), + ("America/Cuiaba", "America/Cuiaba"), + ("America/Curacao", "America/Curacao"), + ("America/Danmarkshavn", "America/Danmarkshavn"), + ("America/Dawson", "America/Dawson"), + ("America/Dawson_Creek", "America/Dawson_Creek"), + ("America/Denver", "America/Denver"), + ("America/Detroit", "America/Detroit"), + ("America/Dominica", "America/Dominica"), + ("America/Edmonton", "America/Edmonton"), + ("America/Eirunepe", "America/Eirunepe"), + ("America/El_Salvador", "America/El_Salvador"), + ("America/Ensenada", "America/Ensenada"), + ("America/Fort_Nelson", "America/Fort_Nelson"), + ("America/Fort_Wayne", "America/Fort_Wayne"), + ("America/Fortaleza", "America/Fortaleza"), + ("America/Glace_Bay", "America/Glace_Bay"), + ("America/Godthab", "America/Godthab"), + ("America/Goose_Bay", "America/Goose_Bay"), + ("America/Grand_Turk", "America/Grand_Turk"), + ("America/Grenada", "America/Grenada"), + ("America/Guadeloupe", "America/Guadeloupe"), + ("America/Guatemala", "America/Guatemala"), + ("America/Guayaquil", "America/Guayaquil"), + ("America/Guyana", "America/Guyana"), + ("America/Halifax", "America/Halifax"), + ("America/Havana", "America/Havana"), + ("America/Hermosillo", "America/Hermosillo"), + ("America/Indiana/Indianapolis", "America/Indiana/Indianapolis"), + ("America/Indiana/Knox", "America/Indiana/Knox"), + ("America/Indiana/Marengo", "America/Indiana/Marengo"), + ("America/Indiana/Petersburg", "America/Indiana/Petersburg"), + ("America/Indiana/Tell_City", "America/Indiana/Tell_City"), + ("America/Indiana/Vevay", "America/Indiana/Vevay"), + ("America/Indiana/Vincennes", "America/Indiana/Vincennes"), + ("America/Indiana/Winamac", "America/Indiana/Winamac"), + ("America/Indianapolis", "America/Indianapolis"), + ("America/Inuvik", "America/Inuvik"), + ("America/Iqaluit", "America/Iqaluit"), + ("America/Jamaica", "America/Jamaica"), + ("America/Jujuy", "America/Jujuy"), + ("America/Juneau", "America/Juneau"), + ("America/Kentucky/Louisville", "America/Kentucky/Louisville"), + ("America/Kentucky/Monticello", "America/Kentucky/Monticello"), + ("America/Knox_IN", "America/Knox_IN"), + ("America/Kralendijk", "America/Kralendijk"), + ("America/La_Paz", "America/La_Paz"), + ("America/Lima", "America/Lima"), + ("America/Los_Angeles", "America/Los_Angeles"), + ("America/Louisville", "America/Louisville"), + ("America/Lower_Princes", "America/Lower_Princes"), + ("America/Maceio", "America/Maceio"), + ("America/Managua", "America/Managua"), + ("America/Manaus", "America/Manaus"), + ("America/Marigot", "America/Marigot"), + ("America/Martinique", "America/Martinique"), + ("America/Matamoros", "America/Matamoros"), + ("America/Mazatlan", "America/Mazatlan"), + ("America/Mendoza", "America/Mendoza"), + ("America/Menominee", "America/Menominee"), + ("America/Merida", "America/Merida"), + ("America/Metlakatla", "America/Metlakatla"), + ("America/Mexico_City", "America/Mexico_City"), + ("America/Miquelon", "America/Miquelon"), + ("America/Moncton", "America/Moncton"), + ("America/Monterrey", "America/Monterrey"), + ("America/Montevideo", "America/Montevideo"), + ("America/Montreal", "America/Montreal"), + ("America/Montserrat", "America/Montserrat"), + ("America/Nassau", "America/Nassau"), + ("America/New_York", "America/New_York"), + ("America/Nipigon", "America/Nipigon"), + ("America/Nome", "America/Nome"), + ("America/Noronha", "America/Noronha"), + ("America/North_Dakota/Beulah", "America/North_Dakota/Beulah"), + ("America/North_Dakota/Center", "America/North_Dakota/Center"), + ( + "America/North_Dakota/New_Salem", + "America/North_Dakota/New_Salem", + ), + ("America/Nuuk", "America/Nuuk"), + ("America/Ojinaga", "America/Ojinaga"), + ("America/Panama", "America/Panama"), + ("America/Pangnirtung", "America/Pangnirtung"), + ("America/Paramaribo", "America/Paramaribo"), + ("America/Phoenix", "America/Phoenix"), + ("America/Port-au-Prince", "America/Port-au-Prince"), + ("America/Port_of_Spain", "America/Port_of_Spain"), + ("America/Porto_Acre", "America/Porto_Acre"), + ("America/Porto_Velho", "America/Porto_Velho"), + ("America/Puerto_Rico", "America/Puerto_Rico"), + ("America/Punta_Arenas", "America/Punta_Arenas"), + ("America/Rainy_River", "America/Rainy_River"), + ("America/Rankin_Inlet", "America/Rankin_Inlet"), + ("America/Recife", "America/Recife"), + ("America/Regina", "America/Regina"), + ("America/Resolute", "America/Resolute"), + ("America/Rio_Branco", "America/Rio_Branco"), + ("America/Rosario", "America/Rosario"), + ("America/Santa_Isabel", "America/Santa_Isabel"), + ("America/Santarem", "America/Santarem"), + ("America/Santiago", "America/Santiago"), + ("America/Santo_Domingo", "America/Santo_Domingo"), + ("America/Sao_Paulo", "America/Sao_Paulo"), + ("America/Scoresbysund", "America/Scoresbysund"), + ("America/Shiprock", "America/Shiprock"), + ("America/Sitka", "America/Sitka"), + ("America/St_Barthelemy", "America/St_Barthelemy"), + ("America/St_Johns", "America/St_Johns"), + ("America/St_Kitts", "America/St_Kitts"), + ("America/St_Lucia", "America/St_Lucia"), + ("America/St_Thomas", "America/St_Thomas"), + ("America/St_Vincent", "America/St_Vincent"), + ("America/Swift_Current", "America/Swift_Current"), + ("America/Tegucigalpa", "America/Tegucigalpa"), + ("America/Thule", "America/Thule"), + ("America/Thunder_Bay", "America/Thunder_Bay"), + ("America/Tijuana", "America/Tijuana"), + ("America/Toronto", "America/Toronto"), + ("America/Tortola", "America/Tortola"), + ("America/Vancouver", "America/Vancouver"), + ("America/Virgin", "America/Virgin"), + ("America/Whitehorse", "America/Whitehorse"), + ("America/Winnipeg", "America/Winnipeg"), + ("America/Yakutat", "America/Yakutat"), + ("America/Yellowknife", "America/Yellowknife"), + ("Antarctica/Casey", "Antarctica/Casey"), + ("Antarctica/Davis", "Antarctica/Davis"), + ("Antarctica/DumontDUrville", "Antarctica/DumontDUrville"), + ("Antarctica/Macquarie", "Antarctica/Macquarie"), + ("Antarctica/Mawson", "Antarctica/Mawson"), + ("Antarctica/McMurdo", "Antarctica/McMurdo"), + ("Antarctica/Palmer", "Antarctica/Palmer"), + ("Antarctica/Rothera", "Antarctica/Rothera"), + ("Antarctica/South_Pole", "Antarctica/South_Pole"), + ("Antarctica/Syowa", "Antarctica/Syowa"), + ("Antarctica/Troll", "Antarctica/Troll"), + ("Antarctica/Vostok", "Antarctica/Vostok"), + ("Arctic/Longyearbyen", "Arctic/Longyearbyen"), + ("Asia/Aden", "Asia/Aden"), + ("Asia/Almaty", "Asia/Almaty"), + ("Asia/Amman", "Asia/Amman"), + ("Asia/Anadyr", "Asia/Anadyr"), + ("Asia/Aqtau", "Asia/Aqtau"), + ("Asia/Aqtobe", "Asia/Aqtobe"), + ("Asia/Ashgabat", "Asia/Ashgabat"), + ("Asia/Ashkhabad", "Asia/Ashkhabad"), + ("Asia/Atyrau", "Asia/Atyrau"), + ("Asia/Baghdad", "Asia/Baghdad"), + ("Asia/Bahrain", "Asia/Bahrain"), + ("Asia/Baku", "Asia/Baku"), + ("Asia/Bangkok", "Asia/Bangkok"), + ("Asia/Barnaul", "Asia/Barnaul"), + ("Asia/Beirut", "Asia/Beirut"), + ("Asia/Bishkek", "Asia/Bishkek"), + ("Asia/Brunei", "Asia/Brunei"), + ("Asia/Calcutta", "Asia/Calcutta"), + ("Asia/Chita", "Asia/Chita"), + ("Asia/Choibalsan", "Asia/Choibalsan"), + ("Asia/Chongqing", "Asia/Chongqing"), + ("Asia/Chungking", "Asia/Chungking"), + ("Asia/Colombo", "Asia/Colombo"), + ("Asia/Dacca", "Asia/Dacca"), + ("Asia/Damascus", "Asia/Damascus"), + ("Asia/Dhaka", "Asia/Dhaka"), + ("Asia/Dili", "Asia/Dili"), + ("Asia/Dubai", "Asia/Dubai"), + ("Asia/Dushanbe", "Asia/Dushanbe"), + ("Asia/Famagusta", "Asia/Famagusta"), + ("Asia/Gaza", "Asia/Gaza"), + ("Asia/Harbin", "Asia/Harbin"), + ("Asia/Hebron", "Asia/Hebron"), + ("Asia/Ho_Chi_Minh", "Asia/Ho_Chi_Minh"), + ("Asia/Hong_Kong", "Asia/Hong_Kong"), + ("Asia/Hovd", "Asia/Hovd"), + ("Asia/Irkutsk", "Asia/Irkutsk"), + ("Asia/Istanbul", "Asia/Istanbul"), + ("Asia/Jakarta", "Asia/Jakarta"), + ("Asia/Jayapura", "Asia/Jayapura"), + ("Asia/Jerusalem", "Asia/Jerusalem"), + ("Asia/Kabul", "Asia/Kabul"), + ("Asia/Kamchatka", "Asia/Kamchatka"), + ("Asia/Karachi", "Asia/Karachi"), + ("Asia/Kashgar", "Asia/Kashgar"), + ("Asia/Kathmandu", "Asia/Kathmandu"), + ("Asia/Katmandu", "Asia/Katmandu"), + ("Asia/Khandyga", "Asia/Khandyga"), + ("Asia/Kolkata", "Asia/Kolkata"), + ("Asia/Krasnoyarsk", "Asia/Krasnoyarsk"), + ("Asia/Kuala_Lumpur", "Asia/Kuala_Lumpur"), + ("Asia/Kuching", "Asia/Kuching"), + ("Asia/Kuwait", "Asia/Kuwait"), + ("Asia/Macao", "Asia/Macao"), + ("Asia/Macau", "Asia/Macau"), + ("Asia/Magadan", "Asia/Magadan"), + ("Asia/Makassar", "Asia/Makassar"), + ("Asia/Manila", "Asia/Manila"), + ("Asia/Muscat", "Asia/Muscat"), + ("Asia/Nicosia", "Asia/Nicosia"), + ("Asia/Novokuznetsk", "Asia/Novokuznetsk"), + ("Asia/Novosibirsk", "Asia/Novosibirsk"), + ("Asia/Omsk", "Asia/Omsk"), + ("Asia/Oral", "Asia/Oral"), + ("Asia/Phnom_Penh", "Asia/Phnom_Penh"), + ("Asia/Pontianak", "Asia/Pontianak"), + ("Asia/Pyongyang", "Asia/Pyongyang"), + ("Asia/Qatar", "Asia/Qatar"), + ("Asia/Qostanay", "Asia/Qostanay"), + ("Asia/Qyzylorda", "Asia/Qyzylorda"), + ("Asia/Rangoon", "Asia/Rangoon"), + ("Asia/Riyadh", "Asia/Riyadh"), + ("Asia/Saigon", "Asia/Saigon"), + ("Asia/Sakhalin", "Asia/Sakhalin"), + ("Asia/Samarkand", "Asia/Samarkand"), + ("Asia/Seoul", "Asia/Seoul"), + ("Asia/Shanghai", "Asia/Shanghai"), + ("Asia/Singapore", "Asia/Singapore"), + ("Asia/Srednekolymsk", "Asia/Srednekolymsk"), + ("Asia/Taipei", "Asia/Taipei"), + ("Asia/Tashkent", "Asia/Tashkent"), + ("Asia/Tbilisi", "Asia/Tbilisi"), + ("Asia/Tehran", "Asia/Tehran"), + ("Asia/Tel_Aviv", "Asia/Tel_Aviv"), + ("Asia/Thimbu", "Asia/Thimbu"), + ("Asia/Thimphu", "Asia/Thimphu"), + ("Asia/Tokyo", "Asia/Tokyo"), + ("Asia/Tomsk", "Asia/Tomsk"), + ("Asia/Ujung_Pandang", "Asia/Ujung_Pandang"), + ("Asia/Ulaanbaatar", "Asia/Ulaanbaatar"), + ("Asia/Ulan_Bator", "Asia/Ulan_Bator"), + ("Asia/Urumqi", "Asia/Urumqi"), + ("Asia/Ust-Nera", "Asia/Ust-Nera"), + ("Asia/Vientiane", "Asia/Vientiane"), + ("Asia/Vladivostok", "Asia/Vladivostok"), + ("Asia/Yakutsk", "Asia/Yakutsk"), + ("Asia/Yangon", "Asia/Yangon"), + ("Asia/Yekaterinburg", "Asia/Yekaterinburg"), + ("Asia/Yerevan", "Asia/Yerevan"), + ("Atlantic/Azores", "Atlantic/Azores"), + ("Atlantic/Bermuda", "Atlantic/Bermuda"), + ("Atlantic/Canary", "Atlantic/Canary"), + ("Atlantic/Cape_Verde", "Atlantic/Cape_Verde"), + ("Atlantic/Faeroe", "Atlantic/Faeroe"), + ("Atlantic/Faroe", "Atlantic/Faroe"), + ("Atlantic/Jan_Mayen", "Atlantic/Jan_Mayen"), + ("Atlantic/Madeira", "Atlantic/Madeira"), + ("Atlantic/Reykjavik", "Atlantic/Reykjavik"), + ("Atlantic/South_Georgia", "Atlantic/South_Georgia"), + ("Atlantic/St_Helena", "Atlantic/St_Helena"), + ("Atlantic/Stanley", "Atlantic/Stanley"), + ("Australia/ACT", "Australia/ACT"), + ("Australia/Adelaide", "Australia/Adelaide"), + ("Australia/Brisbane", "Australia/Brisbane"), + ("Australia/Broken_Hill", "Australia/Broken_Hill"), + ("Australia/Canberra", "Australia/Canberra"), + ("Australia/Currie", "Australia/Currie"), + ("Australia/Darwin", "Australia/Darwin"), + ("Australia/Eucla", "Australia/Eucla"), + ("Australia/Hobart", "Australia/Hobart"), + ("Australia/LHI", "Australia/LHI"), + ("Australia/Lindeman", "Australia/Lindeman"), + ("Australia/Lord_Howe", "Australia/Lord_Howe"), + ("Australia/Melbourne", "Australia/Melbourne"), + ("Australia/NSW", "Australia/NSW"), + ("Australia/North", "Australia/North"), + ("Australia/Perth", "Australia/Perth"), + ("Australia/Queensland", "Australia/Queensland"), + ("Australia/South", "Australia/South"), + ("Australia/Sydney", "Australia/Sydney"), + ("Australia/Tasmania", "Australia/Tasmania"), + ("Australia/Victoria", "Australia/Victoria"), + ("Australia/West", "Australia/West"), + ("Australia/Yancowinna", "Australia/Yancowinna"), + ("Brazil/Acre", "Brazil/Acre"), + ("Brazil/DeNoronha", "Brazil/DeNoronha"), + ("Brazil/East", "Brazil/East"), + ("Brazil/West", "Brazil/West"), + ("CET", "CET"), + ("CST6CDT", "CST6CDT"), + ("Canada/Atlantic", "Canada/Atlantic"), + ("Canada/Central", "Canada/Central"), + ("Canada/Eastern", "Canada/Eastern"), + ("Canada/Mountain", "Canada/Mountain"), + ("Canada/Newfoundland", "Canada/Newfoundland"), + ("Canada/Pacific", "Canada/Pacific"), + ("Canada/Saskatchewan", "Canada/Saskatchewan"), + ("Canada/Yukon", "Canada/Yukon"), + ("Chile/Continental", "Chile/Continental"), + ("Chile/EasterIsland", "Chile/EasterIsland"), + ("Cuba", "Cuba"), + ("EET", "EET"), + ("EST", "EST"), + ("EST5EDT", "EST5EDT"), + ("Egypt", "Egypt"), + ("Eire", "Eire"), + ("Etc/GMT", "Etc/GMT"), + ("Etc/GMT+0", "Etc/GMT+0"), + ("Etc/GMT+1", "Etc/GMT+1"), + ("Etc/GMT+10", "Etc/GMT+10"), + ("Etc/GMT+11", "Etc/GMT+11"), + ("Etc/GMT+12", "Etc/GMT+12"), + ("Etc/GMT+2", "Etc/GMT+2"), + ("Etc/GMT+3", "Etc/GMT+3"), + ("Etc/GMT+4", "Etc/GMT+4"), + ("Etc/GMT+5", "Etc/GMT+5"), + ("Etc/GMT+6", "Etc/GMT+6"), + ("Etc/GMT+7", "Etc/GMT+7"), + ("Etc/GMT+8", "Etc/GMT+8"), + ("Etc/GMT+9", "Etc/GMT+9"), + ("Etc/GMT-0", "Etc/GMT-0"), + ("Etc/GMT-1", "Etc/GMT-1"), + ("Etc/GMT-10", "Etc/GMT-10"), + ("Etc/GMT-11", "Etc/GMT-11"), + ("Etc/GMT-12", "Etc/GMT-12"), + ("Etc/GMT-13", "Etc/GMT-13"), + ("Etc/GMT-14", "Etc/GMT-14"), + ("Etc/GMT-2", "Etc/GMT-2"), + ("Etc/GMT-3", "Etc/GMT-3"), + ("Etc/GMT-4", "Etc/GMT-4"), + ("Etc/GMT-5", "Etc/GMT-5"), + ("Etc/GMT-6", "Etc/GMT-6"), + ("Etc/GMT-7", "Etc/GMT-7"), + ("Etc/GMT-8", "Etc/GMT-8"), + ("Etc/GMT-9", "Etc/GMT-9"), + ("Etc/GMT0", "Etc/GMT0"), + ("Etc/Greenwich", "Etc/Greenwich"), + ("Etc/UCT", "Etc/UCT"), + ("Etc/UTC", "Etc/UTC"), + ("Etc/Universal", "Etc/Universal"), + ("Etc/Zulu", "Etc/Zulu"), + ("Europe/Amsterdam", "Europe/Amsterdam"), + ("Europe/Andorra", "Europe/Andorra"), + ("Europe/Astrakhan", "Europe/Astrakhan"), + ("Europe/Athens", "Europe/Athens"), + ("Europe/Belfast", "Europe/Belfast"), + ("Europe/Belgrade", "Europe/Belgrade"), + ("Europe/Berlin", "Europe/Berlin"), + ("Europe/Bratislava", "Europe/Bratislava"), + ("Europe/Brussels", "Europe/Brussels"), + ("Europe/Bucharest", "Europe/Bucharest"), + ("Europe/Budapest", "Europe/Budapest"), + ("Europe/Busingen", "Europe/Busingen"), + ("Europe/Chisinau", "Europe/Chisinau"), + ("Europe/Copenhagen", "Europe/Copenhagen"), + ("Europe/Dublin", "Europe/Dublin"), + ("Europe/Gibraltar", "Europe/Gibraltar"), + ("Europe/Guernsey", "Europe/Guernsey"), + ("Europe/Helsinki", "Europe/Helsinki"), + ("Europe/Isle_of_Man", "Europe/Isle_of_Man"), + ("Europe/Istanbul", "Europe/Istanbul"), + ("Europe/Jersey", "Europe/Jersey"), + ("Europe/Kaliningrad", "Europe/Kaliningrad"), + ("Europe/Kiev", "Europe/Kiev"), + ("Europe/Kirov", "Europe/Kirov"), + ("Europe/Lisbon", "Europe/Lisbon"), + ("Europe/Ljubljana", "Europe/Ljubljana"), + ("Europe/London", "Europe/London"), + ("Europe/Luxembourg", "Europe/Luxembourg"), + ("Europe/Madrid", "Europe/Madrid"), + ("Europe/Malta", "Europe/Malta"), + ("Europe/Mariehamn", "Europe/Mariehamn"), + ("Europe/Minsk", "Europe/Minsk"), + ("Europe/Monaco", "Europe/Monaco"), + ("Europe/Moscow", "Europe/Moscow"), + ("Europe/Nicosia", "Europe/Nicosia"), + ("Europe/Oslo", "Europe/Oslo"), + ("Europe/Paris", "Europe/Paris"), + ("Europe/Podgorica", "Europe/Podgorica"), + ("Europe/Prague", "Europe/Prague"), + ("Europe/Riga", "Europe/Riga"), + ("Europe/Rome", "Europe/Rome"), + ("Europe/Samara", "Europe/Samara"), + ("Europe/San_Marino", "Europe/San_Marino"), + ("Europe/Sarajevo", "Europe/Sarajevo"), + ("Europe/Saratov", "Europe/Saratov"), + ("Europe/Simferopol", "Europe/Simferopol"), + ("Europe/Skopje", "Europe/Skopje"), + ("Europe/Sofia", "Europe/Sofia"), + ("Europe/Stockholm", "Europe/Stockholm"), + ("Europe/Tallinn", "Europe/Tallinn"), + ("Europe/Tirane", "Europe/Tirane"), + ("Europe/Tiraspol", "Europe/Tiraspol"), + ("Europe/Ulyanovsk", "Europe/Ulyanovsk"), + ("Europe/Uzhgorod", "Europe/Uzhgorod"), + ("Europe/Vaduz", "Europe/Vaduz"), + ("Europe/Vatican", "Europe/Vatican"), + ("Europe/Vienna", "Europe/Vienna"), + ("Europe/Vilnius", "Europe/Vilnius"), + ("Europe/Volgograd", "Europe/Volgograd"), + ("Europe/Warsaw", "Europe/Warsaw"), + ("Europe/Zagreb", "Europe/Zagreb"), + ("Europe/Zaporozhye", "Europe/Zaporozhye"), + ("Europe/Zurich", "Europe/Zurich"), + ("GB", "GB"), + ("GB-Eire", "GB-Eire"), + ("GMT", "GMT"), + ("GMT+0", "GMT+0"), + ("GMT-0", "GMT-0"), + ("GMT0", "GMT0"), + ("Greenwich", "Greenwich"), + ("HST", "HST"), + ("Hongkong", "Hongkong"), + ("Iceland", "Iceland"), + ("Indian/Antananarivo", "Indian/Antananarivo"), + ("Indian/Chagos", "Indian/Chagos"), + ("Indian/Christmas", "Indian/Christmas"), + ("Indian/Cocos", "Indian/Cocos"), + ("Indian/Comoro", "Indian/Comoro"), + ("Indian/Kerguelen", "Indian/Kerguelen"), + ("Indian/Mahe", "Indian/Mahe"), + ("Indian/Maldives", "Indian/Maldives"), + ("Indian/Mauritius", "Indian/Mauritius"), + ("Indian/Mayotte", "Indian/Mayotte"), + ("Indian/Reunion", "Indian/Reunion"), + ("Iran", "Iran"), + ("Israel", "Israel"), + ("Jamaica", "Jamaica"), + ("Japan", "Japan"), + ("Kwajalein", "Kwajalein"), + ("Libya", "Libya"), + ("MET", "MET"), + ("MST", "MST"), + ("MST7MDT", "MST7MDT"), + ("Mexico/BajaNorte", "Mexico/BajaNorte"), + ("Mexico/BajaSur", "Mexico/BajaSur"), + ("Mexico/General", "Mexico/General"), + ("NZ", "NZ"), + ("NZ-CHAT", "NZ-CHAT"), + ("Navajo", "Navajo"), + ("PRC", "PRC"), + ("PST8PDT", "PST8PDT"), + ("Pacific/Apia", "Pacific/Apia"), + ("Pacific/Auckland", "Pacific/Auckland"), + ("Pacific/Bougainville", "Pacific/Bougainville"), + ("Pacific/Chatham", "Pacific/Chatham"), + ("Pacific/Chuuk", "Pacific/Chuuk"), + ("Pacific/Easter", "Pacific/Easter"), + ("Pacific/Efate", "Pacific/Efate"), + ("Pacific/Enderbury", "Pacific/Enderbury"), + ("Pacific/Fakaofo", "Pacific/Fakaofo"), + ("Pacific/Fiji", "Pacific/Fiji"), + ("Pacific/Funafuti", "Pacific/Funafuti"), + ("Pacific/Galapagos", "Pacific/Galapagos"), + ("Pacific/Gambier", "Pacific/Gambier"), + ("Pacific/Guadalcanal", "Pacific/Guadalcanal"), + ("Pacific/Guam", "Pacific/Guam"), + ("Pacific/Honolulu", "Pacific/Honolulu"), + ("Pacific/Johnston", "Pacific/Johnston"), + ("Pacific/Kanton", "Pacific/Kanton"), + ("Pacific/Kiritimati", "Pacific/Kiritimati"), + ("Pacific/Kosrae", "Pacific/Kosrae"), + ("Pacific/Kwajalein", "Pacific/Kwajalein"), + ("Pacific/Majuro", "Pacific/Majuro"), + ("Pacific/Marquesas", "Pacific/Marquesas"), + ("Pacific/Midway", "Pacific/Midway"), + ("Pacific/Nauru", "Pacific/Nauru"), + ("Pacific/Niue", "Pacific/Niue"), + ("Pacific/Norfolk", "Pacific/Norfolk"), + ("Pacific/Noumea", "Pacific/Noumea"), + ("Pacific/Pago_Pago", "Pacific/Pago_Pago"), + ("Pacific/Palau", "Pacific/Palau"), + ("Pacific/Pitcairn", "Pacific/Pitcairn"), + ("Pacific/Pohnpei", "Pacific/Pohnpei"), + ("Pacific/Ponape", "Pacific/Ponape"), + ("Pacific/Port_Moresby", "Pacific/Port_Moresby"), + ("Pacific/Rarotonga", "Pacific/Rarotonga"), + ("Pacific/Saipan", "Pacific/Saipan"), + ("Pacific/Samoa", "Pacific/Samoa"), + ("Pacific/Tahiti", "Pacific/Tahiti"), + ("Pacific/Tarawa", "Pacific/Tarawa"), + ("Pacific/Tongatapu", "Pacific/Tongatapu"), + ("Pacific/Truk", "Pacific/Truk"), + ("Pacific/Wake", "Pacific/Wake"), + ("Pacific/Wallis", "Pacific/Wallis"), + ("Pacific/Yap", "Pacific/Yap"), + ("Poland", "Poland"), + ("Portugal", "Portugal"), + ("ROC", "ROC"), + ("ROK", "ROK"), + ("Singapore", "Singapore"), + ("Turkey", "Turkey"), + ("UCT", "UCT"), + ("US/Alaska", "US/Alaska"), + ("US/Aleutian", "US/Aleutian"), + ("US/Arizona", "US/Arizona"), + ("US/Central", "US/Central"), + ("US/East-Indiana", "US/East-Indiana"), + ("US/Eastern", "US/Eastern"), + ("US/Hawaii", "US/Hawaii"), + ("US/Indiana-Starke", "US/Indiana-Starke"), + ("US/Michigan", "US/Michigan"), + ("US/Mountain", "US/Mountain"), + ("US/Pacific", "US/Pacific"), + ("US/Samoa", "US/Samoa"), + ("UTC", "UTC"), + ("Universal", "Universal"), + ("W-SU", "W-SU"), + ("WET", "WET"), + ("Zulu", "Zulu"), + ], + default="UTC", + max_length=255, + ), + ), + migrations.AddConstraint( + model_name="notification", + constraint=models.CheckConstraint( + check=models.Q( + ( + "notification_type__in", + [ + "FAVORITE", + "REPLY", + "MENTION", + "TAG", + "FOLLOW", + "FOLLOW_REQUEST", + "BOOST", + "IMPORT", + "ADD", + "REPORT", + "INVITE", + "ACCEPT", + "JOIN", + "LEAVE", + "REMOVE", + ], + ) + ), + name="notification_type_valid", + ), + ), + migrations.AddField( + model_name="groupmemberinvitation", + name="group", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="user_invitations", + to="bookwyrm.group", + ), + ), + migrations.AddField( + model_name="groupmemberinvitation", + name="user", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="group_invitations", + to=settings.AUTH_USER_MODEL, + ), + ), + migrations.AddField( + model_name="groupmember", + name="group", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="memberships", + to="bookwyrm.group", + ), + ), + migrations.AddField( + model_name="groupmember", + name="user", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="memberships", + to=settings.AUTH_USER_MODEL, + ), + ), + migrations.AddField( + model_name="group", + name="user", + field=bookwyrm.models.fields.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL + ), + ), + migrations.AddField( + model_name="list", + name="group", + field=models.ForeignKey( + blank=True, + default=None, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to="bookwyrm.group", + ), + ), + migrations.AddField( + model_name="notification", + name="related_group", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="notifications", + to="bookwyrm.group", + ), + ), + migrations.AddConstraint( + model_name="groupmemberinvitation", + constraint=models.UniqueConstraint( + fields=("group", "user"), name="unique_invitation" + ), + ), + migrations.AddConstraint( + model_name="groupmember", + constraint=models.UniqueConstraint( + fields=("group", "user"), name="unique_membership" + ), + ), + ] diff --git a/bookwyrm/migrations/0111_merge_0107_auto_20211016_0639_0110_auto_20211015_1734.py b/bookwyrm/migrations/0111_merge_0107_auto_20211016_0639_0110_auto_20211015_1734.py new file mode 100644 index 000000000..f5f316c13 --- /dev/null +++ b/bookwyrm/migrations/0111_merge_0107_auto_20211016_0639_0110_auto_20211015_1734.py @@ -0,0 +1,13 @@ +# Generated by Django 3.2.5 on 2021-10-16 19:30 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ("bookwyrm", "0107_auto_20211016_0639"), + ("bookwyrm", "0110_auto_20211015_1734"), + ] + + operations = [] diff --git a/bookwyrm/migrations/0112_auto_20211022_0844.py b/bookwyrm/migrations/0112_auto_20211022_0844.py new file mode 100644 index 000000000..246480b3a --- /dev/null +++ b/bookwyrm/migrations/0112_auto_20211022_0844.py @@ -0,0 +1,93 @@ +# Generated by Django 3.2.5 on 2021-10-22 08:44 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("bookwyrm", "0111_merge_0107_auto_20211016_0639_0110_auto_20211015_1734"), + ] + + operations = [ + migrations.RemoveConstraint( + model_name="notification", + name="notification_type_valid", + ), + migrations.AlterField( + model_name="notification", + name="notification_type", + field=models.CharField( + choices=[ + ("FAVORITE", "Favorite"), + ("REPLY", "Reply"), + ("MENTION", "Mention"), + ("TAG", "Tag"), + ("FOLLOW", "Follow"), + ("FOLLOW_REQUEST", "Follow Request"), + ("BOOST", "Boost"), + ("IMPORT", "Import"), + ("ADD", "Add"), + ("REPORT", "Report"), + ("INVITE", "Invite"), + ("ACCEPT", "Accept"), + ("JOIN", "Join"), + ("LEAVE", "Leave"), + ("REMOVE", "Remove"), + ("GROUP_PRIVACY", "Group Privacy"), + ("GROUP_NAME", "Group Name"), + ("GROUP_DESCRIPTION", "Group Description"), + ], + max_length=255, + ), + ), + migrations.AlterField( + model_name="user", + name="preferred_language", + field=models.CharField( + blank=True, + choices=[ + ("en-us", "English"), + ("de-de", "Deutsch (German)"), + ("es-es", "Español (Spanish)"), + ("fr-fr", "Français (French)"), + ("pt-br", "Português - Brasil (Brazilian Portuguese)"), + ("zh-hans", "简体中文 (Simplified Chinese)"), + ("zh-hant", "繁體中文 (Traditional Chinese)"), + ], + max_length=255, + null=True, + ), + ), + migrations.AddConstraint( + model_name="notification", + constraint=models.CheckConstraint( + check=models.Q( + ( + "notification_type__in", + [ + "FAVORITE", + "REPLY", + "MENTION", + "TAG", + "FOLLOW", + "FOLLOW_REQUEST", + "BOOST", + "IMPORT", + "ADD", + "REPORT", + "INVITE", + "ACCEPT", + "JOIN", + "LEAVE", + "REMOVE", + "GROUP_PRIVACY", + "GROUP_NAME", + "GROUP_DESCRIPTION", + ], + ) + ), + name="notification_type_valid", + ), + ), + ] diff --git a/bookwyrm/models/__init__.py b/bookwyrm/models/__init__.py index bffd62b45..c5ea44e0a 100644 --- a/bookwyrm/models/__init__.py +++ b/bookwyrm/models/__init__.py @@ -21,6 +21,8 @@ from .relationship import UserFollows, UserFollowRequest, UserBlocks from .report import Report, ReportComment from .federated_server import FederatedServer +from .group import Group, GroupMember, GroupMemberInvitation + from .import_job import ImportJob, ImportItem from .site import SiteSettings, SiteInvite diff --git a/bookwyrm/models/base_model.py b/bookwyrm/models/base_model.py index 07f6b0d5c..f62678f7e 100644 --- a/bookwyrm/models/base_model.py +++ b/bookwyrm/models/base_model.py @@ -78,7 +78,24 @@ class BookWyrmModel(models.Model): self.privacy in ["direct", "followers"] and self.mention_users.filter(id=viewer.id).first() ): + return + + # you can see groups of which you are a member + if ( + hasattr(self, "memberships") + and self.memberships.filter(user=viewer).exists() + ): + return + + # you can see objects which have a group of which you are a member + if hasattr(self, "group"): + if ( + hasattr(self.group, "memberships") + and self.group.memberships.filter(user=viewer).exists() + ): + return + raise Http404() def raise_not_editable(self, viewer): diff --git a/bookwyrm/models/group.py b/bookwyrm/models/group.py new file mode 100644 index 000000000..bd5b8d410 --- /dev/null +++ b/bookwyrm/models/group.py @@ -0,0 +1,182 @@ +""" do book related things with other users """ +from django.apps import apps +from django.db import models, IntegrityError, transaction +from django.db.models import Q +from bookwyrm.settings import DOMAIN +from .base_model import BookWyrmModel +from . import fields +from .relationship import UserBlocks + + +class Group(BookWyrmModel): + """A group of users""" + + name = fields.CharField(max_length=100) + user = fields.ForeignKey("User", on_delete=models.CASCADE) + description = fields.TextField(blank=True, null=True) + privacy = fields.PrivacyField() + + def get_remote_id(self): + """don't want the user to be in there in this case""" + return f"https://{DOMAIN}/group/{self.id}" + + @classmethod + def followers_filter(cls, queryset, viewer): + """Override filter for "followers" privacy level to allow non-following + group members to see the existence of group-curated lists""" + + return queryset.exclude( + ~Q( # user is not a group member + Q(user__followers=viewer) | Q(user=viewer) | Q(memberships__user=viewer) + ), + privacy="followers", # and the status of the group is followers only + ) + + @classmethod + def direct_filter(cls, queryset, viewer): + """Override filter for "direct" privacy level to allow group members + to see the existence of groups and group lists""" + + return queryset.exclude(~Q(memberships__user=viewer), privacy="direct") + + +class GroupMember(models.Model): + """Users who are members of a group""" + + created_date = models.DateTimeField(auto_now_add=True) + updated_date = models.DateTimeField(auto_now=True) + group = models.ForeignKey( + "Group", on_delete=models.CASCADE, related_name="memberships" + ) + user = models.ForeignKey( + "User", on_delete=models.CASCADE, related_name="memberships" + ) + + class Meta: + """Users can only have one membership per group""" + + constraints = [ + models.UniqueConstraint(fields=["group", "user"], name="unique_membership") + ] + + def save(self, *args, **kwargs): + """don't let a user invite someone who blocked them""" + # blocking in either direction is a no-go + if UserBlocks.objects.filter( + Q( + user_subject=self.group.user, + user_object=self.user, + ) + | Q( + user_subject=self.user, + user_object=self.group.user, + ) + ).exists(): + raise IntegrityError() + # accepts and requests are handled by the GroupInvitation model + super().save(*args, **kwargs) + + @classmethod + def from_request(cls, join_request): + """converts a join request into a member relationship""" + + # remove the invite + join_request.delete() + + # make a group member + return cls.objects.create( + user=join_request.user, + group=join_request.group, + ) + + @classmethod + def remove(cls, owner, user): + """remove a user from a group""" + + memberships = cls.objects.filter(group__user=owner, user=user).all() + for member in memberships: + member.delete() + + +class GroupMemberInvitation(models.Model): + """adding a user to a group requires manual confirmation""" + + created_date = models.DateTimeField(auto_now_add=True) + group = models.ForeignKey( + "Group", on_delete=models.CASCADE, related_name="user_invitations" + ) + user = models.ForeignKey( + "User", on_delete=models.CASCADE, related_name="group_invitations" + ) + + class Meta: + """Users can only have one outstanding invitation per group""" + + constraints = [ + models.UniqueConstraint(fields=["group", "user"], name="unique_invitation") + ] + + def save(self, *args, **kwargs): + """make sure the membership doesn't already exist""" + # if there's an invitation for a membership that already exists, accept it + # without changing the local database state + if GroupMember.objects.filter(user=self.user, group=self.group).exists(): + self.accept() + return + + # blocking in either direction is a no-go + if UserBlocks.objects.filter( + Q( + user_subject=self.group.user, + user_object=self.user, + ) + | Q( + user_subject=self.user, + user_object=self.group.user, + ) + ).exists(): + raise IntegrityError() + + # make an invitation + super().save(*args, **kwargs) + + # now send the invite + model = apps.get_model("bookwyrm.Notification", require_ready=True) + notification_type = "INVITE" + model.objects.create( + user=self.user, + related_user=self.group.user, + related_group=self.group, + notification_type=notification_type, + ) + + def accept(self): + """turn this request into the real deal""" + + with transaction.atomic(): + GroupMember.from_request(self) + + model = apps.get_model("bookwyrm.Notification", require_ready=True) + # tell the group owner + model.objects.create( + user=self.group.user, + related_user=self.user, + related_group=self.group, + notification_type="ACCEPT", + ) + + # let the other members know about it + for membership in self.group.memberships.all(): + member = membership.user + if member not in (self.user, self.group.user): + model.objects.create( + user=member, + related_user=self.user, + related_group=self.group, + notification_type="JOIN", + ) + + def reject(self): + """generate a Reject for this membership request""" + + self.delete() diff --git a/bookwyrm/models/list.py b/bookwyrm/models/list.py index 022a0d981..978a7a9b3 100644 --- a/bookwyrm/models/list.py +++ b/bookwyrm/models/list.py @@ -1,22 +1,20 @@ """ make a list of books!! """ from django.apps import apps from django.db import models +from django.db.models import Q from django.utils import timezone from bookwyrm import activitypub from bookwyrm.settings import DOMAIN + from .activitypub_mixin import CollectionItemMixin, OrderedCollectionMixin from .base_model import BookWyrmModel +from .group import GroupMember from . import fields - CurationType = models.TextChoices( "Curation", - [ - "closed", - "open", - "curated", - ], + ["closed", "open", "curated", "group"], ) @@ -32,6 +30,13 @@ class List(OrderedCollectionMixin, BookWyrmModel): curation = fields.CharField( max_length=255, default="closed", choices=CurationType.choices ) + group = models.ForeignKey( + "Group", + on_delete=models.SET_NULL, + default=None, + blank=True, + null=True, + ) books = models.ManyToManyField( "Edition", symmetrical=False, @@ -54,6 +59,52 @@ class List(OrderedCollectionMixin, BookWyrmModel): ordering = ("-updated_date",) + def raise_not_editable(self, viewer): + """the associated user OR the list owner can edit""" + if self.user == viewer: + return + # group members can edit items in group lists + is_group_member = GroupMember.objects.filter( + group=self.group, user=viewer + ).exists() + if is_group_member: + return + super().raise_not_editable(viewer) + + @classmethod + def followers_filter(cls, queryset, viewer): + """Override filter for "followers" privacy level to allow non-following + group members to see the existence of group lists""" + + return queryset.exclude( + ~Q( # user isn't following or group member + Q(user__followers=viewer) + | Q(user=viewer) + | Q(group__memberships__user=viewer) + ), + privacy="followers", # and the status (of the list) is followers only + ) + + @classmethod + def direct_filter(cls, queryset, viewer): + """Override filter for "direct" privacy level to allow + group members to see the existence of group lists""" + + return queryset.exclude( + ~Q( # user not self and not in the group if this is a group list + Q(user=viewer) | Q(group__memberships__user=viewer) + ), + privacy="direct", + ) + + @classmethod + def remove_from_group(cls, owner, user): + """remove a list from a group""" + + cls.objects.filter(group__user=owner, user=user).all().update( + group=None, curation="closed" + ) + class ListItem(CollectionItemMixin, BookWyrmModel): """ok""" @@ -82,9 +133,9 @@ class ListItem(CollectionItemMixin, BookWyrmModel): self.book_list.save(broadcast=False) list_owner = self.book_list.user + model = apps.get_model("bookwyrm.Notification", require_ready=True) # create a notification if somoene ELSE added to a local user's list if created and list_owner.local and list_owner != self.user: - model = apps.get_model("bookwyrm.Notification", require_ready=True) model.objects.create( user=list_owner, related_user=self.user, @@ -92,10 +143,26 @@ class ListItem(CollectionItemMixin, BookWyrmModel): notification_type="ADD", ) + if self.book_list.group: + for membership in self.book_list.group.memberships.all(): + if membership.user != self.user: + model.objects.create( + user=membership.user, + related_user=self.user, + related_list_item=self, + notification_type="ADD", + ) + def raise_not_deletable(self, viewer): """the associated user OR the list owner can delete""" if self.book_list.user == viewer: return + # group members can delete items in group lists + is_group_member = GroupMember.objects.filter( + group=self.book_list.group, user=viewer + ).exists() + if is_group_member: + return super().raise_not_deletable(viewer) class Meta: diff --git a/bookwyrm/models/notification.py b/bookwyrm/models/notification.py index a4968f61f..2f1aae4f3 100644 --- a/bookwyrm/models/notification.py +++ b/bookwyrm/models/notification.py @@ -4,10 +4,10 @@ from django.dispatch import receiver from .base_model import BookWyrmModel from . import Boost, Favorite, ImportJob, Report, Status, User - +# pylint: disable=line-too-long NotificationType = models.TextChoices( "NotificationType", - "FAVORITE REPLY MENTION TAG FOLLOW FOLLOW_REQUEST BOOST IMPORT ADD REPORT", + "FAVORITE REPLY MENTION TAG FOLLOW FOLLOW_REQUEST BOOST IMPORT ADD REPORT INVITE ACCEPT JOIN LEAVE REMOVE GROUP_PRIVACY GROUP_NAME GROUP_DESCRIPTION", ) @@ -19,6 +19,9 @@ class Notification(BookWyrmModel): related_user = models.ForeignKey( "User", on_delete=models.CASCADE, null=True, related_name="related_user" ) + related_group = models.ForeignKey( + "Group", on_delete=models.CASCADE, null=True, related_name="notifications" + ) related_status = models.ForeignKey("Status", on_delete=models.CASCADE, null=True) related_import = models.ForeignKey("ImportJob", on_delete=models.CASCADE, null=True) related_list_item = models.ForeignKey( @@ -37,6 +40,7 @@ class Notification(BookWyrmModel): user=self.user, related_book=self.related_book, related_user=self.related_user, + related_group=self.related_group, related_status=self.related_status, related_import=self.related_import, related_list_item=self.related_list_item, diff --git a/bookwyrm/static/js/bookwyrm.js b/bookwyrm/static/js/bookwyrm.js index f000fd082..2d5b88adc 100644 --- a/bookwyrm/static/js/bookwyrm.js +++ b/bookwyrm/static/js/bookwyrm.js @@ -28,6 +28,12 @@ let BookWyrm = new class { this.revealForm.bind(this)) ); + document.querySelectorAll('[data-hides]') + .forEach(button => button.addEventListener( + 'change', + this.hideForm.bind(this)) + ); + document.querySelectorAll('[data-back]') .forEach(button => button.addEventListener( 'click', @@ -119,8 +125,8 @@ let BookWyrm = new class { } /** - * Toggle form. - * + * Show form. + * * @param {Event} event * @return {undefined} */ @@ -128,7 +134,23 @@ let BookWyrm = new class { let trigger = event.currentTarget; let hidden = trigger.closest('.hidden-form').querySelectorAll('.is-hidden')[0]; - this.addRemoveClass(hidden, 'is-hidden', !hidden); + if (hidden) { + this.addRemoveClass(hidden, 'is-hidden', !hidden); + } + } + + /** + * Hide form. + * + * @param {Event} event + * @return {undefined} + */ + hideForm(event) { + let trigger = event.currentTarget; + let targetId = trigger.dataset.hides + let visible = document.getElementById(targetId) + + this.addRemoveClass(visible, 'is-hidden', true); } /** @@ -227,7 +249,7 @@ let BookWyrm = new class { } /** - * Check or uncheck a checbox. + * Check or uncheck a checkbox. * * @param {string} checkbox - id of the checkbox * @param {boolean} pressed - Is the trigger pressed? diff --git a/bookwyrm/suggested_users.py b/bookwyrm/suggested_users.py index aa53fcb25..86c181a28 100644 --- a/bookwyrm/suggested_users.py +++ b/bookwyrm/suggested_users.py @@ -81,7 +81,7 @@ class SuggestedUsers(RedisStore): """take a user out of someone's suggestions""" self.bulk_remove_objects_from_store([suggested_user], self.store_id(user)) - def get_suggestions(self, user): + def get_suggestions(self, user, local=False): """get suggestions""" values = self.get_store(self.store_id(user), withscores=True) results = [] @@ -97,8 +97,8 @@ class SuggestedUsers(RedisStore): logger.exception(err) continue user.mutuals = counts["mutuals"] - # user.shared_books = counts["shared_books"] - results.append(user) + if (local and user.local) or not local: + results.append(user) if len(results) >= 5: break return results diff --git a/bookwyrm/templates/author/author.html b/bookwyrm/templates/author/author.html index 8a15cd0f0..6a67b50b3 100644 --- a/bookwyrm/templates/author/author.html +++ b/bookwyrm/templates/author/author.html @@ -110,8 +110,14 @@ {% for book in books %}
{% include 'landing/small-book.html' with book=book %} + {% include 'snippets/shelve_button/shelve_button.html' with book=book %}
{% endfor %} + +
+ {% include 'snippets/pagination.html' with page=books %} +
+ {% endblock %} diff --git a/bookwyrm/templates/author/edit_author.html b/bookwyrm/templates/author/edit_author.html index 103341bfd..54d7f4f1c 100644 --- a/bookwyrm/templates/author/edit_author.html +++ b/bookwyrm/templates/author/edit_author.html @@ -12,7 +12,9 @@

{% trans "Added:" %} {{ author.created_date | naturaltime }}

{% trans "Updated:" %} {{ author.updated_date | naturaltime }}

+ {% if author.last_edited_by %}

{% trans "Last edited by:" %} {{ author.last_edited_by.display_name }}

+ {% endif %}
diff --git a/bookwyrm/templates/book/edit/edit_book.html b/bookwyrm/templates/book/edit/edit_book.html index ec7b08586..fc11208fd 100644 --- a/bookwyrm/templates/book/edit/edit_book.html +++ b/bookwyrm/templates/book/edit/edit_book.html @@ -108,7 +108,13 @@ {% if not confirm_mode %}
+ {% if book %} {% trans "Cancel" %} + {% else %} + + {% trans "Cancel" %} + + {% endif %}
{% endif %} diff --git a/bookwyrm/templates/book/publisher_info.html b/bookwyrm/templates/book/publisher_info.html index e0711fa82..b39bcf5ce 100644 --- a/bookwyrm/templates/book/publisher_info.html +++ b/bookwyrm/templates/book/publisher_info.html @@ -3,7 +3,7 @@ {% load i18n %} {% load humanize %} -{% firstof book.physical_format_detail book.physical_format as format %} +{% firstof book.physical_format_detail book.get_physical_format_display as format %} {% firstof book.physical_format book.physical_format_detail as format_property %} {% with pages=book.pages %} {% if format or pages %} @@ -18,7 +18,7 @@

{% if format and not pages %} - {% blocktrans %}{{ format }}{% endblocktrans %} + {{ format }} {% elif format and pages %} {% blocktrans %}{{ format }}, {{ pages }} pages{% endblocktrans %} {% elif pages %} diff --git a/bookwyrm/templates/discover/card-header.html b/bookwyrm/templates/discover/card-header.html new file mode 100644 index 000000000..0eb9a678c --- /dev/null +++ b/bookwyrm/templates/discover/card-header.html @@ -0,0 +1,26 @@ +{% load i18n %} +{% load utilities %} + +{% with user_path=status.user.local_path username=status.user.display_name book_path=status.book.local_poth book_title=book|book_title %} + +{% if status.status_type == 'GeneratedNote' %} + {{ status.content|safe }} +{% elif status.status_type == 'Rating' %} + {% blocktrans trimmed %} + {{ username }} rated {{ book_title }} + {% endblocktrans %} +{% elif status.status_type == 'Review' %} + {% blocktrans trimmed %} + {{ username }} reviewed {{ book_title }} + {% endblocktrans %} +{% elif status.status_type == 'Comment' %} + {% blocktrans trimmed %} + {{ username }} commented on {{ book_title }} + {% endblocktrans %} +{% elif status.status_type == 'Quotation' %} + {% blocktrans trimmed %} + {{ username }} quoted {{ book_title }} + {% endblocktrans %} +{% endif %} + +{% endwith %} diff --git a/bookwyrm/templates/discover/large-book.html b/bookwyrm/templates/discover/large-book.html index 6d80c3daf..1fa0afb92 100644 --- a/bookwyrm/templates/discover/large-book.html +++ b/bookwyrm/templates/discover/large-book.html @@ -36,23 +36,7 @@

- - {{ status.user.display_name }} - - - {% if status.status_type == 'GeneratedNote' %} - {{ status.content|safe }} - {% elif status.status_type == 'Rating' %} - {% trans "rated" %} - {% elif status.status_type == 'Review' %} - {% trans "reviewed" %} - {% elif status.status_type == 'Comment' %} - {% trans "commented on" %} - {% elif status.status_type == 'Quotation' %} - {% trans "quoted" %} - {% endif %} - - {{ book.title }} + {% include "discover/card-header.html" %}

diff --git a/bookwyrm/templates/discover/small-book.html b/bookwyrm/templates/discover/small-book.html index 5b2070188..76732ca14 100644 --- a/bookwyrm/templates/discover/small-book.html +++ b/bookwyrm/templates/discover/small-book.html @@ -22,23 +22,7 @@

- - {{ status.user.display_name }} - - - {% if status.status_type == 'GeneratedNote' %} - {{ status.content|safe }} - {% elif status.status_type == 'Rating' %} - {% trans "rated" %} - {% elif status.status_type == 'Review' %} - {% trans "reviewed" %} - {% elif status.status_type == 'Comment' %} - {% trans "commented on" %} - {% elif status.status_type == 'Quotation' %} - {% trans "quoted" %} - {% endif %} - - {{ book.title }} + {% include "discover/card-header.html" %}

{% if status.rating %}

diff --git a/bookwyrm/templates/email/invite/html_content.html b/bookwyrm/templates/email/invite/html_content.html index 358e23dc1..adc993b7b 100644 --- a/bookwyrm/templates/email/invite/html_content.html +++ b/bookwyrm/templates/email/invite/html_content.html @@ -12,6 +12,6 @@

{% url 'code-of-conduct' as coc_path %} {% url 'about' as about_path %} - {% blocktrans %}Learn more about this instance.{% endblocktrans %} + {% blocktrans %}Learn more about {{ site_name }}.{% endblocktrans %}

{% endblock %} diff --git a/bookwyrm/templates/email/invite/text_content.html b/bookwyrm/templates/email/invite/text_content.html index c3fcdc04e..26dcd1720 100644 --- a/bookwyrm/templates/email/invite/text_content.html +++ b/bookwyrm/templates/email/invite/text_content.html @@ -5,6 +5,6 @@ {{ invite_link }} -{% trans "Learn more about this instance:" %} https://{{ domain }}{% url 'about' %} +{% blocktrans %}Learn more about {{ site_name }}:{% endblocktrans %} https://{{ domain }}{% url 'about' %} {% endblock %} diff --git a/bookwyrm/templates/groups/create_form.html b/bookwyrm/templates/groups/create_form.html new file mode 100644 index 000000000..dbb8ad88b --- /dev/null +++ b/bookwyrm/templates/groups/create_form.html @@ -0,0 +1,12 @@ +{% extends 'components/inline_form.html' %} +{% load i18n %} + +{% block header %} +{% trans "Create Group" %} +{% endblock %} + +{% block form %} +
+ {% include 'groups/form.html' with group_form=group_form %} +
+{% endblock %} diff --git a/bookwyrm/templates/groups/created_text.html b/bookwyrm/templates/groups/created_text.html new file mode 100644 index 000000000..5e6ce513d --- /dev/null +++ b/bookwyrm/templates/groups/created_text.html @@ -0,0 +1,6 @@ +{% load i18n %} +{% spaceless %} + +{% blocktrans with username=group.user.display_name path=group.user.local_path %}Managed by {{ username }}{% endblocktrans %} + +{% endspaceless %} diff --git a/bookwyrm/templates/groups/delete_group_modal.html b/bookwyrm/templates/groups/delete_group_modal.html new file mode 100644 index 000000000..4bb87ed9b --- /dev/null +++ b/bookwyrm/templates/groups/delete_group_modal.html @@ -0,0 +1,21 @@ +{% extends 'components/modal.html' %} +{% load i18n %} + +{% block modal-title %}{% trans "Delete this group?" %}{% endblock %} + +{% block modal-body %} +{% trans "This action cannot be un-done" %} +{% endblock %} + +{% block modal-footer %} +
+ {% csrf_token %} + + + {% trans "Cancel" as button_text %} + {% include 'snippets/toggle/toggle_button.html' with text=button_text controls_text="delete_group" controls_uid=group.id %} +
+{% endblock %} + diff --git a/bookwyrm/templates/groups/edit_form.html b/bookwyrm/templates/groups/edit_form.html new file mode 100644 index 000000000..1c58dc77e --- /dev/null +++ b/bookwyrm/templates/groups/edit_form.html @@ -0,0 +1,13 @@ +{% extends 'components/inline_form.html' %} +{% load i18n %} + +{% block header %} +{% trans "Edit Group" %} +{% endblock %} + +{% block form %} +
+ {% include 'groups/form.html' %} +
+{% include "groups/delete_group_modal.html" with controls_text="delete_group" controls_uid=group.id %} +{% endblock %} diff --git a/bookwyrm/templates/groups/find_users.html b/bookwyrm/templates/groups/find_users.html new file mode 100644 index 000000000..377e3a0b3 --- /dev/null +++ b/bookwyrm/templates/groups/find_users.html @@ -0,0 +1,9 @@ +{% extends 'groups/group.html' %} +{% load i18n %} + +{% block searchresults %} +

+ {% trans "Add new members!" %} +

+ {% include 'groups/suggested_users.html' with suggested_users=suggested_users %} +{% endblock %} diff --git a/bookwyrm/templates/groups/form.html b/bookwyrm/templates/groups/form.html new file mode 100644 index 000000000..21c525ee0 --- /dev/null +++ b/bookwyrm/templates/groups/form.html @@ -0,0 +1,34 @@ +{% load i18n %} +{% csrf_token %} + +
+
+ +
+ + {{ group_form.name }} +
+
+ + {{ group_form.description }} +
+
+
+
+
+
+
+ {% include 'snippets/privacy_select_no_followers.html' with current=group.privacy %} +
+
+ +
+
+
+ {% if group.id %} +
+ {% trans "Delete group" as button_text %} + {% include 'snippets/toggle/toggle_button.html' with class="is-danger" text=button_text icon_with_text="x" controls_text="delete_group" controls_uid=group.id focus="modal_title_delete_group" %} +
+ {% endif %} +
diff --git a/bookwyrm/templates/groups/group.html b/bookwyrm/templates/groups/group.html new file mode 100644 index 000000000..f25a1f270 --- /dev/null +++ b/bookwyrm/templates/groups/group.html @@ -0,0 +1,82 @@ +{% extends 'groups/layout.html' %} +{% load i18n %} +{% load bookwyrm_tags %} +{% load markdown %} + +{% block panel %} + +
+
+ + {% if group.user == request.user %} +
+
+
+ +
+
+ +
+
+
+ {% endif %} + + {% block searchresults %} + {% endblock %} +
+ {% include "groups/members.html" with group=group %} +
+ +

Lists

+ {% if not lists %} +

{% trans "This group has no lists" %}

+ {% else %} + +
+ {% for list in lists %} +
+
+
+

+ {{ list.name }} {% include 'snippets/privacy-icons.html' with item=list %} +

+
+ + {% with list_books=list.listitem_set.all|slice:5 %} + {% if list_books %} + + {% endif %} + {% endwith %} + +
+
+ {% if list.description %} + {{ list.description|to_markdown|safe|truncatechars_html:30 }} + {% else %} +   + {% endif %} +
+

+ {% include 'lists/created_text.html' with list=list %} +

+
+
+
+ {% endfor %} +
+ + {% endif %} + {% include "snippets/pagination.html" with page=items %} +
+
+{% endblock %} diff --git a/bookwyrm/templates/groups/layout.html b/bookwyrm/templates/groups/layout.html new file mode 100644 index 000000000..f558f169a --- /dev/null +++ b/bookwyrm/templates/groups/layout.html @@ -0,0 +1,32 @@ +{% extends 'layout.html' %} +{% load i18n %} + +{% block title %}{{ group.name }}{% endblock %} + +{% block content %} +
+
+

{{ group.name }} {% include 'snippets/privacy-icons.html' with item=group %}

+

+ {% include 'groups/created_text.html' with group=group %} +

+
+
+ {% if request.user == group.user %} + {% trans "Edit group" as button_text %} + {% include 'snippets/toggle/open_button.html' with text=button_text icon_with_text="pencil" controls_text="edit_group" focus="edit_group_header" %} + {% endif %} +
+
+ +
+ {% include 'snippets/trimmed_text.html' with full=group.description %} +
+ +
+ {% include 'groups/edit_form.html' with controls_text="edit_group" %} +
+ +{% block panel %}{% endblock %} + +{% endblock %} diff --git a/bookwyrm/templates/groups/members.html b/bookwyrm/templates/groups/members.html new file mode 100644 index 000000000..719674d89 --- /dev/null +++ b/bookwyrm/templates/groups/members.html @@ -0,0 +1,47 @@ +{% load i18n %} +{% load utilities %} +{% load humanize %} +{% load bookwyrm_tags %} +{% load bookwyrm_group_tags %} + +

Group Members

+

{% trans "Members can add and remove books on a group's book lists" %}

+ +{% if group.user != request.user and group|is_member:request.user %} +
+ {% csrf_token %} + + + + +
+{% endif %} + +
+ {% for membership in group.memberships.all %} + {% with member=membership.user %} +
+ + {% include 'snippets/avatar.html' with user=member large=True %} + {{ member.display_name|truncatechars:10 }} + @{{ member|username|truncatechars:8 }} + + {% if group.user == member %} + + Manager + + {% endif %} + {% include 'snippets/remove_from_group_button.html' with user=member group=group %} + {% if request.user in member.following.all %} +

+ {% trans "Follows you" %} +

+ {% endif %} +
+ {% endwith %} + {% endfor %} +
diff --git a/bookwyrm/templates/groups/suggested_users.html b/bookwyrm/templates/groups/suggested_users.html new file mode 100644 index 000000000..64498eb85 --- /dev/null +++ b/bookwyrm/templates/groups/suggested_users.html @@ -0,0 +1,46 @@ +{% load i18n %} +{% load utilities %} +{% load humanize %} + +{% if suggested_users %} +
+ {% for user in suggested_users %} +
+ + {% include 'snippets/avatar.html' with user=user large=True %} + {{ user.display_name|truncatechars:10 }} + @{{ user|username|truncatechars:8 }} + + {% include 'snippets/add_to_group_button.html' with user=user group=group %} + {% if user.mutuals %} +

+ {% blocktrans trimmed with mutuals=user.mutuals|intcomma count counter=user.mutuals %} + {{ mutuals }} follower you follow + {% plural %} + {{ mutuals }} followers you follow{% endblocktrans %} +

+ {% elif user.shared_books %} +

+ {% blocktrans trimmed with shared_books=user.shared_books|intcomma count counter=user.shared_books %} + {{ shared_books }} book on your shelves + {% plural %} + {{ shared_books }} books on your shelves + {% endblocktrans %} +

+ {% elif request.user in user.following.all %} +

+ {% trans "Follows you" %} +

+ {% endif %} +
+ {% endfor %} +
+ {% else %} +

+ {% blocktrans trimmed %} + No potential members found for "{{ user_query }}" + {% endblocktrans %} +

+
+ +{% endif %} diff --git a/bookwyrm/templates/groups/user_groups.html b/bookwyrm/templates/groups/user_groups.html new file mode 100644 index 000000000..cc27ce42d --- /dev/null +++ b/bookwyrm/templates/groups/user_groups.html @@ -0,0 +1,35 @@ +{% load i18n %} +{% load markdown %} +{% load interaction %} + +
+ {% for group in groups %} +
+
+
+

+ {{ group.name }} {% include 'snippets/privacy-icons.html' with item=group %} +

+ {% if group.user == user %} +
+ {% trans "Manager" as text %} + + {{ text }} + +
+ {% endif %} +
+ +
+
+ {% if group.description %} + {{ group.description|to_markdown|safe|truncatechars_html:30 }} + {% else %} +   + {% endif %} +
+
+
+
+ {% endfor %} +
diff --git a/bookwyrm/templates/import/import.html b/bookwyrm/templates/import/import.html index cc296b75b..81f0daa54 100644 --- a/bookwyrm/templates/import/import.html +++ b/bookwyrm/templates/import/import.html @@ -22,8 +22,8 @@
+ {% include 'snippets/register_form.html' %}
diff --git a/bookwyrm/templates/login.html b/bookwyrm/templates/landing/login.html similarity index 79% rename from bookwyrm/templates/login.html rename to bookwyrm/templates/landing/login.html index 31e9cebb4..95baa06de 100644 --- a/bookwyrm/templates/login.html +++ b/bookwyrm/templates/landing/login.html @@ -14,19 +14,19 @@ {% if show_confirmed_email %}

{% trans "Success! Email address confirmed." %}

{% endif %} -
+ {% csrf_token %} {% if show_confirmed_email %}{% endif %}
- +
- {{ login_form.localname }} +
- +
- {{ login_form.password }} +
{% for error in login_form.password.errors %}

{{ error | escape }}

diff --git a/bookwyrm/templates/password_reset.html b/bookwyrm/templates/landing/password_reset.html similarity index 90% rename from bookwyrm/templates/password_reset.html rename to bookwyrm/templates/landing/password_reset.html index 57add722a..be1dccf81 100644 --- a/bookwyrm/templates/password_reset.html +++ b/bookwyrm/templates/landing/password_reset.html @@ -14,9 +14,9 @@ {% csrf_token %}
- +
- +
diff --git a/bookwyrm/templates/password_reset_request.html b/bookwyrm/templates/landing/password_reset_request.html similarity index 100% rename from bookwyrm/templates/password_reset_request.html rename to bookwyrm/templates/landing/password_reset_request.html diff --git a/bookwyrm/templates/layout.html b/bookwyrm/templates/layout.html index ce3eee2ba..f2d04f961 100644 --- a/bookwyrm/templates/layout.html +++ b/bookwyrm/templates/layout.html @@ -171,7 +171,7 @@
- +

{% trans "Forgot your password?" %}

@@ -227,7 +227,7 @@

- {% trans "About this instance" %} + {% blocktrans with site_name=site.name %}About {{ site_name }}{% endblocktrans %}

{% if site.admin_email %}

diff --git a/bookwyrm/templates/lists/create_form.html b/bookwyrm/templates/lists/create_form.html index 6a42a90a5..447c0f6bf 100644 --- a/bookwyrm/templates/lists/create_form.html +++ b/bookwyrm/templates/lists/create_form.html @@ -6,7 +6,7 @@ {% endblock %} {% block form %} - + {% include 'lists/form.html' %}

{% endblock %} diff --git a/bookwyrm/templates/lists/created_text.html b/bookwyrm/templates/lists/created_text.html index eee5a75f2..f5405b64a 100644 --- a/bookwyrm/templates/lists/created_text.html +++ b/bookwyrm/templates/lists/created_text.html @@ -1,7 +1,9 @@ {% load i18n %} {% spaceless %} -{% if list.curation != 'open' %} +{% if list.curation == 'group' %} +{% blocktrans with username=list.user.display_name userpath=list.user.local_path groupname=list.group.name grouppath=list.group.local_path %}Created by {{ username }} and managed by {{ groupname }}{% endblocktrans %} +{% elif list.curation != 'open' %} {% blocktrans with username=list.user.display_name path=list.user.local_path %}Created and curated by {{ username }}{% endblocktrans %} {% else %} {% blocktrans with username=list.user.display_name path=list.user.local_path %}Created by {{ username }}{% endblocktrans %} diff --git a/bookwyrm/templates/lists/form.html b/bookwyrm/templates/lists/form.html index 336f1a8f8..25dc01e63 100644 --- a/bookwyrm/templates/lists/form.html +++ b/bookwyrm/templates/lists/form.html @@ -1,5 +1,6 @@ {% load i18n %} {% csrf_token %} +{% load utilities %}
@@ -17,20 +18,50 @@
{% trans "List curation:" %} -
diff --git a/bookwyrm/templates/lists/layout.html b/bookwyrm/templates/lists/layout.html index 68abafc09..6e772221a 100644 --- a/bookwyrm/templates/lists/layout.html +++ b/bookwyrm/templates/lists/layout.html @@ -25,7 +25,9 @@
- {% include 'lists/edit_form.html' with controls_text="edit_list" %} + {% if request.user == list.user %} + {% include 'lists/edit_form.html' with controls_text="edit_list" user_groups=user_groups %} + {% endif %}
{% block panel %}{% endblock %} diff --git a/bookwyrm/templates/lists/list.html b/bookwyrm/templates/lists/list.html index dbee2f1f5..61e31c06e 100644 --- a/bookwyrm/templates/lists/list.html +++ b/bookwyrm/templates/lists/list.html @@ -1,6 +1,7 @@ {% extends 'lists/layout.html' %} {% load i18n %} {% load bookwyrm_tags %} +{% load bookwyrm_group_tags %} {% load markdown %} {% block panel %} @@ -16,7 +17,7 @@
{% if request.GET.updated %}
- {% if list.curation != "open" and request.user != list.user %} + {% if list.curation != "open" and request.user != list.user and not list.group|is_member:request.user %} {% trans "You successfully suggested a book for this list!" %} {% else %} {% trans "You successfully added a book to this list!" %} @@ -66,7 +67,7 @@

{% blocktrans with username=item.user.display_name user_path=item.user.local_path %}Added by {{ username }}{% endblocktrans %}

- {% if list.user == request.user %} + {% if list.user == request.user or list.group|is_member:request.user %} {% endif %} - {% if list.user == request.user or list.curation == 'open' and item.user == request.user %} + {% if list.user == request.user or list.curation == 'open' and item.user == request.user or list.group|is_member:request.user %} {% if request.user.is_authenticated and not list.curation == 'closed' or request.user == list.user %}

- {% if list.curation == 'open' or request.user == list.user %} + {% if list.curation == 'open' or request.user == list.user or list.group|is_member:request.user %} {% trans "Add Books" %} {% else %} {% trans "Suggest Books" %} @@ -178,7 +179,7 @@ {% csrf_token %} - +

diff --git a/bookwyrm/templates/lists/lists.html b/bookwyrm/templates/lists/lists.html index d909f5e81..49091bcf0 100644 --- a/bookwyrm/templates/lists/lists.html +++ b/bookwyrm/templates/lists/lists.html @@ -22,10 +22,11 @@ {% endif %} - +{% if request.user.is_authenticated %}
{% include 'lists/create_form.html' with controls_text="create_list" %}
+{% endif %} {% if request.user.is_authenticated %}
{% include 'shelf/create_shelf_form.html' with controls_text='create_shelf_form' %} diff --git a/bookwyrm/templates/snippets/add_to_group_button.html b/bookwyrm/templates/snippets/add_to_group_button.html new file mode 100644 index 000000000..2785d7c01 --- /dev/null +++ b/bookwyrm/templates/snippets/add_to_group_button.html @@ -0,0 +1,34 @@ +{% load i18n %} +{% load bookwyrm_group_tags %} +{% if request.user == user or not request.user == group.user or not request.user.is_authenticated %} +{% elif user in request.user.blocks.all %} +{% include 'snippets/block_button.html' with blocks=True %} +{% else %} + +
+
+
+ {% csrf_token %} + + + +
+
+ {% csrf_token %} + + + {% if not group|is_member:user %} + + {% else %} + + {% endif %} +
+
+
+{% endif %} diff --git a/bookwyrm/templates/snippets/join_invitation_buttons.html b/bookwyrm/templates/snippets/join_invitation_buttons.html new file mode 100644 index 000000000..46c4071d4 --- /dev/null +++ b/bookwyrm/templates/snippets/join_invitation_buttons.html @@ -0,0 +1,16 @@ +{% load i18n %} +{% load bookwyrm_group_tags %} +{% if group|is_invited:request.user %} +
+
+ {% csrf_token %} + + +
+
+ {% csrf_token %} + + +
+
+{% endif %} diff --git a/bookwyrm/templates/snippets/privacy_select_no_followers.html b/bookwyrm/templates/snippets/privacy_select_no_followers.html new file mode 100644 index 000000000..2c601e7ff --- /dev/null +++ b/bookwyrm/templates/snippets/privacy_select_no_followers.html @@ -0,0 +1,21 @@ +{% load i18n %} +{% load utilities %} +
+ {% firstof privacy_uuid 0|uuid as uuid %} + {% if not no_label %} + + {% endif %} + {% firstof current user.default_post_privacy "public" as privacy %} + +
+ diff --git a/bookwyrm/templates/snippets/remove_from_group_button.html b/bookwyrm/templates/snippets/remove_from_group_button.html new file mode 100644 index 000000000..1672e0388 --- /dev/null +++ b/bookwyrm/templates/snippets/remove_from_group_button.html @@ -0,0 +1,24 @@ +{% load i18n %} +{% load bookwyrm_group_tags %} +{% if request.user == user or not request.user == group.user or not request.user.is_authenticated %} +{% else %} +{% if user in request.user.blocks.all %} +{% include 'snippets/block_button.html' with blocks=True %} +
+{% endif %} +
+
+
+ {% csrf_token %} + + + + +
+
+
+{% endif %} diff --git a/bookwyrm/templates/user/groups.html b/bookwyrm/templates/user/groups.html new file mode 100644 index 000000000..6f3619fd3 --- /dev/null +++ b/bookwyrm/templates/user/groups.html @@ -0,0 +1,37 @@ +{% extends 'user/layout.html' %} +{% load i18n %} + +{% block header %} +
+
+

+ {% if is_self %} + {% trans "Your Groups" %} + {% else %} + {% blocktrans with username=user.display_name %}Groups: {{ username }}{% endblocktrans %} + {% endif %} +

+
+ {% if is_self %} +
+ {% trans "Create group" as button_text %} + {% include 'snippets/toggle/open_button.html' with controls_text="create_group" icon_with_text="plus" text=button_text %} +
+ {% endif %} +
+{% endblock %} + + +{% block panel %} +
+ +
+ {% include 'groups/create_form.html' with controls_text="create_group" %} +
+ + {% include 'groups/user_groups.html' with memberships=memberships %} +
+
+ {% include 'snippets/pagination.html' with page=user.memberships path=path %} +
+{% endblock %} diff --git a/bookwyrm/templates/user/layout.html b/bookwyrm/templates/user/layout.html index 8ca3bd180..d7557ae7b 100755 --- a/bookwyrm/templates/user/layout.html +++ b/bookwyrm/templates/user/layout.html @@ -4,6 +4,7 @@ {% load utilities %} {% load markdown %} {% load layout %} +{% load bookwyrm_group_tags %} {% block title %}{{ user.display_name }}{% endblock %} @@ -69,6 +70,12 @@ {% trans "Reading Goal" %} {% endif %} + {% if is_self or user|has_groups %} + {% url 'user-groups' user|username as url %} + + {% trans "Groups" %} + + {% endif %} {% if is_self or user.list_set.exists %} {% url 'user-lists' user|username as url %} diff --git a/bookwyrm/templatetags/bookwyrm_group_tags.py b/bookwyrm/templatetags/bookwyrm_group_tags.py new file mode 100644 index 000000000..fde7997e8 --- /dev/null +++ b/bookwyrm/templatetags/bookwyrm_group_tags.py @@ -0,0 +1,27 @@ +""" template filters """ +from django import template + +from bookwyrm import models + +register = template.Library() + + +@register.filter(name="has_groups") +def has_groups(user): + """whether or not the user has a pending invitation to join this group""" + + return models.GroupMember.objects.filter(user=user).exists() + + +@register.filter(name="is_member") +def is_member(group, user): + """whether or not the user is a member of this group""" + + return models.GroupMember.objects.filter(group=group, user=user).exists() + + +@register.filter(name="is_invited") +def is_invited(group, user): + """whether or not the user has a pending invitation to join this group""" + + return models.GroupMemberInvitation.objects.filter(group=group, user=user).exists() diff --git a/bookwyrm/tests/models/test_group.py b/bookwyrm/tests/models/test_group.py new file mode 100644 index 000000000..33341d192 --- /dev/null +++ b/bookwyrm/tests/models/test_group.py @@ -0,0 +1,126 @@ +""" testing models """ +from unittest.mock import patch +from django.test import TestCase + +from bookwyrm import models, settings + + +@patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay") +class Group(TestCase): + """some activitypub oddness ahead""" + + def setUp(self): + """Set up for tests""" + + with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"), patch( + "bookwyrm.activitystreams.populate_stream_task.delay" + ): + self.owner_user = models.User.objects.create_user( + "mouse", "mouse@mouse.mouse", "mouseword", local=True, localname="mouse" + ) + + with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"), patch( + "bookwyrm.activitystreams.populate_stream_task.delay" + ): + self.rat = models.User.objects.create_user( + "rat", "rat@rat.rat", "ratword", local=True, localname="rat" + ) + + with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"), patch( + "bookwyrm.activitystreams.populate_stream_task.delay" + ): + self.badger = models.User.objects.create_user( + "badger", + "badger@badger.badger", + "badgerword", + local=True, + localname="badger", + ) + + with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"), patch( + "bookwyrm.activitystreams.populate_stream_task.delay" + ): + self.capybara = models.User.objects.create_user( + "capybara", + "capybara@capybara.capybara", + "capybaraword", + local=True, + localname="capybara", + ) + + self.public_group = models.Group.objects.create( + name="Public Group", + description="Initial description", + user=self.owner_user, + privacy="public", + ) + + self.private_group = models.Group.objects.create( + name="Private Group", + description="Top secret", + user=self.owner_user, + privacy="direct", + ) + + self.followers_only_group = models.Group.objects.create( + name="Followers Group", + description="No strangers", + user=self.owner_user, + privacy="followers", + ) + + models.GroupMember.objects.create(group=self.private_group, user=self.badger) + models.GroupMember.objects.create( + group=self.followers_only_group, user=self.badger + ) + models.GroupMember.objects.create(group=self.public_group, user=self.capybara) + + def test_group_members_can_see_private_groups(self, _): + """direct privacy group should not be excluded from group listings for group members viewing""" + + rat_groups = models.Group.privacy_filter(self.rat).all() + badger_groups = models.Group.privacy_filter(self.badger).all() + + self.assertFalse(self.private_group in rat_groups) + self.assertTrue(self.private_group in badger_groups) + + def test_group_members_can_see_followers_only_lists(self, _): + """follower-only group booklists should not be excluded from group booklist listing for group members who do not follower list owner""" + + with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): + followers_list = models.List.objects.create( + name="Followers List", + curation="group", + privacy="followers", + group=self.public_group, + user=self.owner_user, + ) + + rat_lists = models.List.privacy_filter(self.rat).all() + badger_lists = models.List.privacy_filter(self.badger).all() + capybara_lists = models.List.privacy_filter(self.capybara).all() + + self.assertFalse(followers_list in rat_lists) + self.assertFalse(followers_list in badger_lists) + self.assertTrue(followers_list in capybara_lists) + + def test_group_members_can_see_private_lists(self, _): + """private group booklists should not be excluded from group booklist listing for group members""" + + with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): + + private_list = models.List.objects.create( + name="Private List", + privacy="direct", + curation="group", + group=self.public_group, + user=self.owner_user, + ) + + rat_lists = models.List.privacy_filter(self.rat).all() + badger_lists = models.List.privacy_filter(self.badger).all() + capybara_lists = models.List.privacy_filter(self.capybara).all() + + self.assertFalse(private_list in rat_lists) + self.assertFalse(private_list in badger_lists) + self.assertTrue(private_list in capybara_lists) diff --git a/bookwyrm/tests/test_suggested_users.py b/bookwyrm/tests/test_suggested_users.py index 6b794b5dc..f625ac10c 100644 --- a/bookwyrm/tests/test_suggested_users.py +++ b/bookwyrm/tests/test_suggested_users.py @@ -35,11 +35,18 @@ class SuggestedUsers(TestCase): rank = suggested_users.get_rank(annotated_user_mock) self.assertEqual(rank, 3) # 3.9642857142857144) - def test_store_id(self, *_): - """redis key generation""" + def test_store_id_from_obj(self, *_): + """redis key generation by user obj""" self.assertEqual( suggested_users.store_id(self.local_user), - "{:d}-suggestions".format(self.local_user.id), + f"{self.local_user.id}-suggestions", + ) + + def test_store_id_from_id(self, *_): + """redis key generation by user id""" + self.assertEqual( + suggested_users.store_id(self.local_user.id), + f"{self.local_user.id}-suggestions", ) def test_get_counts_from_rank(self, *_): @@ -69,21 +76,74 @@ class SuggestedUsers(TestCase): suggestable_user.followers.add(mutual_user) results = suggested_users.get_objects_for_store( - "{:d}-suggestions".format(self.local_user.id) + f"{self.local_user.id}-suggestions" ) self.assertEqual(results.count(), 1) match = results.first() self.assertEqual(match.id, suggestable_user.id) self.assertEqual(match.mutuals, 1) - def test_create_user_signal(self, *_): - """build suggestions for new users""" - with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay") as mock: - models.User.objects.create_user( - "nutria", "nutria@nu.tria", "password", local=True, localname="nutria" - ) + def test_get_stores_for_object(self, *_): + """possible follows""" + mutual_user = models.User.objects.create_user( + "rat", "rat@local.rat", "password", local=True, localname="rat" + ) + suggestable_user = models.User.objects.create_user( + "nutria", + "nutria@nutria.nutria", + "password", + local=True, + localname="nutria", + discoverable=True, + ) - self.assertEqual(mock.call_count, 1) + # you follow rat + mutual_user.followers.add(self.local_user) + # rat follows the suggested user + suggestable_user.followers.add(mutual_user) + + results = suggested_users.get_stores_for_object(self.local_user) + self.assertEqual(len(results), 1) + self.assertEqual(results[0], f"{suggestable_user.id}-suggestions") + + def test_get_users_for_object(self, *_): + """given a user, who might want to follow them""" + mutual_user = models.User.objects.create_user( + "rat", "rat@local.rat", "password", local=True, localname="rat" + ) + suggestable_user = models.User.objects.create_user( + "nutria", + "nutria@nutria.nutria", + "password", + local=True, + localname="nutria", + discoverable=True, + ) + # you follow rat + mutual_user.followers.add(self.local_user) + # rat follows the suggested user + suggestable_user.followers.add(mutual_user) + + results = suggested_users.get_users_for_object(self.local_user) + self.assertEqual(len(results), 1) + self.assertEqual(results[0], suggestable_user) + + def test_rerank_user_suggestions(self, *_): + """does it call the populate store function correctly""" + with patch( + "bookwyrm.suggested_users.SuggestedUsers.populate_store" + ) as store_mock: + suggested_users.rerank_user_suggestions(self.local_user) + args = store_mock.call_args[0] + self.assertEqual(args[0], f"{self.local_user.id}-suggestions") + + def test_get_suggestions(self, *_): + """load from store""" + with patch("bookwyrm.suggested_users.SuggestedUsers.get_store") as mock: + mock.return_value = [(self.local_user.id, 7.9)] + results = suggested_users.get_suggestions(self.local_user) + self.assertEqual(results[0], self.local_user) + self.assertEqual(results[0].mutuals, 7) def test_get_annotated_users(self, *_): """list of people you might know""" @@ -144,8 +204,8 @@ class SuggestedUsers(TestCase): ) for i in range(3): user = models.User.objects.create_user( - "{:d}@local.com".format(i), - "{:d}@nutria.com".format(i), + f"{i}@local.com", + f"{i}@nutria.com", "password", local=True, localname=i, @@ -175,3 +235,12 @@ class SuggestedUsers(TestCase): ) user_1_annotated = result.get(id=user_1.id) self.assertEqual(user_1_annotated.mutuals, 3) + + def test_create_user_signal(self, *_): + """build suggestions for new users""" + with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay") as mock: + models.User.objects.create_user( + "nutria", "nutria@nu.tria", "password", local=True, localname="nutria" + ) + + self.assertEqual(mock.call_count, 1) diff --git a/bookwyrm/tests/views/books/test_edit_book.py b/bookwyrm/tests/views/books/test_edit_book.py index 85bbc032c..7bf5708f8 100644 --- a/bookwyrm/tests/views/books/test_edit_book.py +++ b/bookwyrm/tests/views/books/test_edit_book.py @@ -58,6 +58,17 @@ class EditBookViews(TestCase): validate_html(result.render()) self.assertEqual(result.status_code, 200) + def test_edit_book_create_page(self): + """there are so many views, this just makes sure it LOADS""" + view = views.EditBook.as_view() + request = self.factory.get("") + request.user = self.local_user + request.user.is_superuser = True + result = view(request) + self.assertIsInstance(result, TemplateResponse) + validate_html(result.render()) + self.assertEqual(result.status_code, 200) + def test_edit_book(self): """lets a user edit a book""" view = views.EditBook.as_view() diff --git a/bookwyrm/tests/views/landing/__init__.py b/bookwyrm/tests/views/landing/__init__.py new file mode 100644 index 000000000..b6e690fd5 --- /dev/null +++ b/bookwyrm/tests/views/landing/__init__.py @@ -0,0 +1 @@ +from . import * diff --git a/bookwyrm/tests/views/test_invite.py b/bookwyrm/tests/views/landing/test_invite.py similarity index 94% rename from bookwyrm/tests/views/test_invite.py rename to bookwyrm/tests/views/landing/test_invite.py index c1e425c8c..a93821a9e 100644 --- a/bookwyrm/tests/views/test_invite.py +++ b/bookwyrm/tests/views/landing/test_invite.py @@ -8,6 +8,7 @@ from django.test.client import RequestFactory from bookwyrm import forms, models from bookwyrm import views +from bookwyrm.tests.validate_html import validate_html class InviteViews(TestCase): @@ -40,7 +41,7 @@ class InviteViews(TestCase): invite.return_value = True result = view(request, "hi") self.assertIsInstance(result, TemplateResponse) - result.render() + validate_html(result.render()) self.assertEqual(result.status_code, 200) def test_manage_invites(self): @@ -51,7 +52,7 @@ class InviteViews(TestCase): request.user.is_superuser = True result = view(request) self.assertIsInstance(result, TemplateResponse) - result.render() + validate_html(result.render()) self.assertEqual(result.status_code, 200) def test_manage_invites_post(self): @@ -67,7 +68,7 @@ class InviteViews(TestCase): result = view(request) self.assertIsInstance(result, TemplateResponse) - result.render() + validate_html(result.render()) self.assertEqual(result.status_code, 200) invite = models.SiteInvite.objects.get() @@ -83,7 +84,7 @@ class InviteViews(TestCase): request = self.factory.post("", form.data) result = view(request) - result.render() + validate_html(result.render()) req = models.InviteRequest.objects.get() self.assertEqual(req.email, "new@user.email") @@ -97,7 +98,7 @@ class InviteViews(TestCase): request = self.factory.post("", form.data) result = view(request) - result.render() + validate_html(result.render()) # no request created self.assertFalse(models.InviteRequest.objects.exists()) @@ -110,14 +111,14 @@ class InviteViews(TestCase): request.user.is_superuser = True result = view(request) self.assertIsInstance(result, TemplateResponse) - result.render() + validate_html(result.render()) self.assertEqual(result.status_code, 200) # now with data models.InviteRequest.objects.create(email="fish@example.com") result = view(request) self.assertIsInstance(result, TemplateResponse) - result.render() + validate_html(result.render()) self.assertEqual(result.status_code, 200) def test_manage_invite_requests_send(self): diff --git a/bookwyrm/tests/views/test_landing.py b/bookwyrm/tests/views/landing/test_landing.py similarity index 93% rename from bookwyrm/tests/views/test_landing.py rename to bookwyrm/tests/views/landing/test_landing.py index f3a50fbc2..829919177 100644 --- a/bookwyrm/tests/views/test_landing.py +++ b/bookwyrm/tests/views/landing/test_landing.py @@ -7,6 +7,7 @@ from django.test.client import RequestFactory from bookwyrm import models from bookwyrm import views +from bookwyrm.tests.validate_html import validate_html class LandingViews(TestCase): @@ -38,13 +39,13 @@ class LandingViews(TestCase): with patch("bookwyrm.activitystreams.ActivityStream.get_activity_stream"): result = view(request) self.assertEqual(result.status_code, 200) - result.render() + validate_html(result.render()) request.user = self.anonymous_user result = view(request) self.assertIsInstance(result, TemplateResponse) self.assertEqual(result.status_code, 200) - result.render() + validate_html(result.render()) def test_about_page(self): """there are so many views, this just makes sure it LOADS""" @@ -53,7 +54,7 @@ class LandingViews(TestCase): request.user = self.local_user result = view(request) self.assertIsInstance(result, TemplateResponse) - result.render() + validate_html(result.render()) self.assertEqual(result.status_code, 200) def test_landing(self): diff --git a/bookwyrm/tests/views/test_login.py b/bookwyrm/tests/views/landing/test_login.py similarity index 91% rename from bookwyrm/tests/views/test_login.py rename to bookwyrm/tests/views/landing/test_login.py index c37eaa514..0f86fb791 100644 --- a/bookwyrm/tests/views/test_login.py +++ b/bookwyrm/tests/views/landing/test_login.py @@ -7,6 +7,7 @@ from django.test import TestCase from django.test.client import RequestFactory from bookwyrm import forms, models, views +from bookwyrm.tests.validate_html import validate_html # pylint: disable=too-many-public-methods @@ -41,7 +42,7 @@ class LoginViews(TestCase): result = login(request) self.assertIsInstance(result, TemplateResponse) - result.render() + validate_html(result.render()) self.assertEqual(result.status_code, 200) request.user = self.local_user @@ -58,7 +59,7 @@ class LoginViews(TestCase): request = self.factory.post("", form.data) request.user = self.anonymous_user - with patch("bookwyrm.views.login.login"): + with patch("bookwyrm.views.landing.login.login"): result = view(request) self.assertEqual(result.url, "/") self.assertEqual(result.status_code, 302) @@ -72,7 +73,7 @@ class LoginViews(TestCase): request = self.factory.post("", form.data) request.user = self.anonymous_user - with patch("bookwyrm.views.login.login"): + with patch("bookwyrm.views.landing.login.login"): result = view(request) self.assertEqual(result.url, "/") self.assertEqual(result.status_code, 302) @@ -86,7 +87,7 @@ class LoginViews(TestCase): request = self.factory.post("", form.data) request.user = self.anonymous_user - with patch("bookwyrm.views.login.login"): + with patch("bookwyrm.views.landing.login.login"): result = view(request) self.assertEqual(result.url, "/") self.assertEqual(result.status_code, 302) @@ -100,9 +101,9 @@ class LoginViews(TestCase): request = self.factory.post("", form.data) request.user = self.anonymous_user - with patch("bookwyrm.views.login.login"): + with patch("bookwyrm.views.landing.login.login"): result = view(request) - result.render() + validate_html(result.render()) self.assertEqual(result.status_code, 200) self.assertEqual( result.context_data["login_form"].non_field_errors, diff --git a/bookwyrm/tests/views/test_password.py b/bookwyrm/tests/views/landing/test_password.py similarity index 69% rename from bookwyrm/tests/views/test_password.py rename to bookwyrm/tests/views/landing/test_password.py index 47d8bb27b..f01565ef7 100644 --- a/bookwyrm/tests/views/test_password.py +++ b/bookwyrm/tests/views/landing/test_password.py @@ -1,12 +1,16 @@ """ test for app action functionality """ +from datetime import timedelta from unittest.mock import patch from django.contrib.auth.models import AnonymousUser +from django.core.exceptions import PermissionDenied from django.template.response import TemplateResponse from django.test import TestCase from django.test.client import RequestFactory +from django.utils import timezone from bookwyrm import models, views +from bookwyrm.tests.validate_html import validate_html class PasswordViews(TestCase): @@ -37,7 +41,7 @@ class PasswordViews(TestCase): result = view(request) self.assertIsInstance(result, TemplateResponse) - result.render() + validate_html(result.render()) self.assertEqual(result.status_code, 200) def test_password_reset_request_post(self): @@ -47,13 +51,13 @@ class PasswordViews(TestCase): view = views.PasswordResetRequest.as_view() resp = view(request) self.assertEqual(resp.status_code, 200) - resp.render() + validate_html(resp.render()) request = self.factory.post("", {"email": "mouse@mouse.com"}) request.user = self.anonymous_user with patch("bookwyrm.emailing.send_email.delay"): resp = view(request) - resp.render() + validate_html(resp.render()) self.assertEqual(models.PasswordReset.objects.get().user, self.local_user) @@ -65,15 +69,43 @@ class PasswordViews(TestCase): request.user = self.anonymous_user result = view(request, code.code) self.assertIsInstance(result, TemplateResponse) - result.render() + validate_html(result.render()) self.assertEqual(result.status_code, 200) + def test_password_reset_nonexistant_code(self): + """there are so many views, this just makes sure it LOADS""" + view = views.PasswordReset.as_view() + request = self.factory.get("") + request.user = self.anonymous_user + with self.assertRaises(PermissionDenied): + view(request, "beep") + + def test_password_reset_invalid_code(self): + """there are so many views, this just makes sure it LOADS""" + view = views.PasswordReset.as_view() + code = models.PasswordReset.objects.create( + user=self.local_user, expiry=timezone.now() - timedelta(days=2) + ) + request = self.factory.get("") + request.user = self.anonymous_user + with self.assertRaises(PermissionDenied): + view(request, code.code) + + def test_password_reset_logged_in(self): + """redirect logged in users""" + view = views.PasswordReset.as_view() + code = models.PasswordReset.objects.create(user=self.local_user) + request = self.factory.get("") + request.user = self.local_user + result = view(request, code.code) + self.assertEqual(result.status_code, 302) + def test_password_reset_post(self): """reset from code""" view = views.PasswordReset.as_view() code = models.PasswordReset.objects.create(user=self.local_user) request = self.factory.post("", {"password": "hi", "confirm-password": "hi"}) - with patch("bookwyrm.views.password.login"): + with patch("bookwyrm.views.landing.password.login"): resp = view(request, code.code) self.assertEqual(resp.status_code, 302) self.assertFalse(models.PasswordReset.objects.exists()) @@ -84,7 +116,7 @@ class PasswordViews(TestCase): models.PasswordReset.objects.create(user=self.local_user) request = self.factory.post("", {"password": "hi", "confirm-password": "hi"}) resp = view(request, "jhgdkfjgdf") - resp.render() + validate_html(resp.render()) self.assertTrue(models.PasswordReset.objects.exists()) def test_password_reset_mismatch(self): @@ -93,5 +125,5 @@ class PasswordViews(TestCase): code = models.PasswordReset.objects.create(user=self.local_user) request = self.factory.post("", {"password": "hi", "confirm-password": "hihi"}) resp = view(request, code.code) - resp.render() + validate_html(resp.render()) self.assertTrue(models.PasswordReset.objects.exists()) diff --git a/bookwyrm/tests/views/test_register.py b/bookwyrm/tests/views/landing/test_register.py similarity index 77% rename from bookwyrm/tests/views/test_register.py rename to bookwyrm/tests/views/landing/test_register.py index 764081ef9..99f38da21 100644 --- a/bookwyrm/tests/views/test_register.py +++ b/bookwyrm/tests/views/landing/test_register.py @@ -10,6 +10,7 @@ from django.test.client import RequestFactory from bookwyrm import models, views from bookwyrm.settings import DOMAIN +from bookwyrm.tests.validate_html import validate_html # pylint: disable=too-many-public-methods @@ -38,6 +39,13 @@ class RegisterViews(TestCase): id=1, require_confirm_email=False ) + def test_get_redirect(self, *_): + """there's no dedicated registration page""" + view = views.Register.as_view() + request = self.factory.get("register/") + response = view(request) + self.assertEqual(response.status_code, 302) + def test_register(self, *_): """create a user""" view = views.Register.as_view() @@ -50,12 +58,12 @@ class RegisterViews(TestCase): "email": "aa@bb.cccc", }, ) - with patch("bookwyrm.views.register.login"): + with patch("bookwyrm.views.landing.register.login"): response = view(request) self.assertEqual(models.User.objects.count(), 2) self.assertEqual(response.status_code, 302) nutria = models.User.objects.last() - self.assertEqual(nutria.username, "nutria-user.user_nutria@%s" % DOMAIN) + self.assertEqual(nutria.username, f"nutria-user.user_nutria@{DOMAIN}") self.assertEqual(nutria.localname, "nutria-user.user_nutria") self.assertEqual(nutria.local, True) @@ -75,11 +83,11 @@ class RegisterViews(TestCase): "email": "aa@bb.cccc", }, ) - with patch("bookwyrm.views.register.login"): + with patch("bookwyrm.views.landing.register.login"): response = view(request) self.assertEqual(response.status_code, 302) nutria = models.User.objects.get(localname="nutria") - self.assertEqual(nutria.username, "nutria@%s" % DOMAIN) + self.assertEqual(nutria.username, f"nutria@{DOMAIN}") self.assertEqual(nutria.local, True) self.assertFalse(nutria.is_active) @@ -93,12 +101,12 @@ class RegisterViews(TestCase): "register/", {"localname": "nutria ", "password": "mouseword", "email": "aa@bb.ccc"}, ) - with patch("bookwyrm.views.register.login"): + with patch("bookwyrm.views.landing.register.login"): response = view(request) self.assertEqual(models.User.objects.count(), 2) self.assertEqual(response.status_code, 302) nutria = models.User.objects.last() - self.assertEqual(nutria.username, "nutria@%s" % DOMAIN) + self.assertEqual(nutria.username, f"nutria@{DOMAIN}") self.assertEqual(nutria.localname, "nutria") self.assertEqual(nutria.local, True) @@ -111,7 +119,43 @@ class RegisterViews(TestCase): ) response = view(request) self.assertEqual(models.User.objects.count(), 1) - response.render() + validate_html(response.render()) + + def test_register_error_and_invite(self, *_): + """redirect to the invite page""" + view = views.Register.as_view() + self.settings.allow_registration = False + self.settings.save() + models.SiteInvite.objects.create( + code="testcode", user=self.local_user, use_limit=1 + ) + self.assertEqual(models.SiteInvite.objects.get().times_used, 0) + + request = self.factory.post( + "register/", + { + "localname": "nutria", + "password": "mouseword", + "email": "", + "invite_code": "testcode", + }, + ) + with patch("bookwyrm.views.landing.register.login"): + response = view(request) + response = view(request) + validate_html(response.render()) + + def test_register_username_in_use(self, *_): + """that username is taken""" + view = views.Register.as_view() + self.assertEqual(models.User.objects.count(), 1) + request = self.factory.post( + "register/", + {"localname": "mouse", "password": "mouseword", "email": "aa@bb.ccc"}, + ) + response = view(request) + self.assertEqual(models.User.objects.count(), 1) + validate_html(response.render()) def test_register_invalid_username(self, *_): """gotta have an email""" @@ -123,7 +167,7 @@ class RegisterViews(TestCase): ) response = view(request) self.assertEqual(models.User.objects.count(), 1) - response.render() + validate_html(response.render()) request = self.factory.post( "register/", @@ -131,7 +175,7 @@ class RegisterViews(TestCase): ) response = view(request) self.assertEqual(models.User.objects.count(), 1) - response.render() + validate_html(response.render()) request = self.factory.post( "register/", @@ -139,7 +183,7 @@ class RegisterViews(TestCase): ) response = view(request) self.assertEqual(models.User.objects.count(), 1) - response.render() + validate_html(response.render()) def test_register_closed_instance(self, *_): """you can't just register""" @@ -172,7 +216,7 @@ class RegisterViews(TestCase): "register/", {"localname": "nutria ", "password": "mouseword", "email": "aa@bleep.com"}, ) - with patch("bookwyrm.views.register.login"): + with patch("bookwyrm.views.landing.register.login"): result = view(request) self.assertEqual(result.status_code, 302) self.assertTrue(models.User.objects.filter(email="aa@bleep.com").exists()) @@ -196,7 +240,7 @@ class RegisterViews(TestCase): "invite_code": "testcode", }, ) - with patch("bookwyrm.views.register.login"): + with patch("bookwyrm.views.landing.register.login"): response = view(request) self.assertEqual(models.User.objects.count(), 2) self.assertEqual(response.status_code, 302) @@ -277,7 +321,7 @@ class RegisterViews(TestCase): result = view(request, "abcde") self.assertIsInstance(result, TemplateResponse) - result.render() + validate_html(result.render()) self.assertEqual(result.status_code, 200) self.assertFalse(self.local_user.is_active) self.assertEqual(self.local_user.deactivation_reason, "pending") @@ -293,10 +337,32 @@ class RegisterViews(TestCase): result = login(request) self.assertIsInstance(result, TemplateResponse) - result.render() + validate_html(result.render()) self.assertEqual(result.status_code, 200) request.user = self.local_user result = login(request) self.assertEqual(result.url, "/") self.assertEqual(result.status_code, 302) + + def test_confirm_email_post(self, *_): + """send the email""" + self.settings.require_confirm_email = True + self.settings.save() + view = views.ConfirmEmail.as_view() + models.SiteInvite.objects.create( + code="testcode", user=self.local_user, use_limit=1 + ) + request = self.factory.post("", {"code": "testcode"}) + request.user = self.anonymous_user + + result = view(request) + validate_html(result.render()) + + def test_resend_link(self, *_): + """try again""" + request = self.factory.post("", {"email": "mouse@mouse.com"}) + request.user = self.anonymous_user + with patch("bookwyrm.emailing.send_email.delay") as mock: + views.resend_link(request) + self.assertEqual(mock.call_count, 1) diff --git a/bookwyrm/tests/views/shelf/__init__.py b/bookwyrm/tests/views/shelf/__init__.py new file mode 100644 index 000000000..b6e690fd5 --- /dev/null +++ b/bookwyrm/tests/views/shelf/__init__.py @@ -0,0 +1 @@ +from . import * diff --git a/bookwyrm/tests/views/shelf/test_shelf.py b/bookwyrm/tests/views/shelf/test_shelf.py new file mode 100644 index 000000000..71df3631f --- /dev/null +++ b/bookwyrm/tests/views/shelf/test_shelf.py @@ -0,0 +1,165 @@ +""" test for app action functionality """ +from unittest.mock import patch + +from django.contrib.auth.models import AnonymousUser +from django.template.response import TemplateResponse +from django.test import TestCase +from django.test.client import RequestFactory + +from bookwyrm import models, views +from bookwyrm.activitypub import ActivitypubResponse +from bookwyrm.tests.validate_html import validate_html + + +@patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay") +@patch("bookwyrm.suggested_users.rerank_suggestions_task.delay") +@patch("bookwyrm.activitystreams.populate_stream_task.delay") +@patch("bookwyrm.activitystreams.add_book_statuses_task.delay") +@patch("bookwyrm.activitystreams.remove_book_statuses_task.delay") +class ShelfViews(TestCase): + """tag views""" + + def setUp(self): + """we need basic test data and mocks""" + self.factory = RequestFactory() + with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"), patch( + "bookwyrm.activitystreams.populate_stream_task.delay" + ): + self.local_user = models.User.objects.create_user( + "mouse@local.com", + "mouse@mouse.com", + "mouseword", + local=True, + localname="mouse", + remote_id="https://example.com/users/mouse", + ) + self.work = models.Work.objects.create(title="Test Work") + self.book = models.Edition.objects.create( + title="Example Edition", + remote_id="https://example.com/book/1", + parent_work=self.work, + ) + with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): + self.shelf = models.Shelf.objects.create( + name="Test Shelf", identifier="test-shelf", user=self.local_user + ) + models.SiteSettings.objects.create() + + self.anonymous_user = AnonymousUser + self.anonymous_user.is_authenticated = False + + def test_shelf_page_all_books(self, *_): + """there are so many views, this just makes sure it LOADS""" + 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 = False + 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() + request = self.factory.get("") + request.user = self.anonymous_user + with patch("bookwyrm.views.shelf.shelf.is_api_request") as is_api: + is_api.return_value = False + 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_sorted(self, *_): + """there are so many views, this just makes sure it LOADS""" + view = views.Shelf.as_view() + shelf = self.local_user.shelf_set.first() + request = self.factory.get("", {"sort": "author"}) + request.user = self.local_user + with patch("bookwyrm.views.shelf.shelf.is_api_request") as is_api: + is_api.return_value = False + result = view(request, self.local_user.username, shelf.identifier) + self.assertIsInstance(result, TemplateResponse) + validate_html(result.render()) + self.assertEqual(result.status_code, 200) + + def test_shelf_page(self, *_): + """there are so many views, this just makes sure it LOADS""" + view = views.Shelf.as_view() + shelf = self.local_user.shelf_set.first() + 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 = False + result = view(request, self.local_user.username, shelf.identifier) + self.assertIsInstance(result, TemplateResponse) + validate_html(result.render()) + self.assertEqual(result.status_code, 200) + + with patch("bookwyrm.views.shelf.shelf.is_api_request") as is_api: + is_api.return_value = True + result = view(request, self.local_user.username, shelf.identifier) + self.assertIsInstance(result, ActivitypubResponse) + self.assertEqual(result.status_code, 200) + + request = self.factory.get("/?page=1") + 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, shelf.identifier) + self.assertIsInstance(result, ActivitypubResponse) + self.assertEqual(result.status_code, 200) + + def test_edit_shelf_privacy(self, *_): + """set name or privacy on shelf""" + view = views.Shelf.as_view() + shelf = self.local_user.shelf_set.get(identifier="to-read") + self.assertEqual(shelf.privacy, "public") + + request = self.factory.post( + "", + { + "privacy": "unlisted", + "user": self.local_user.id, + "name": "To Read", + }, + ) + request.user = self.local_user + view(request, self.local_user.username, shelf.identifier) + shelf.refresh_from_db() + + self.assertEqual(shelf.privacy, "unlisted") + + def test_edit_shelf_name(self, *_): + """change the name of an editable shelf""" + view = views.Shelf.as_view() + shelf = models.Shelf.objects.create(name="Test Shelf", user=self.local_user) + self.assertEqual(shelf.privacy, "public") + + request = self.factory.post( + "", {"privacy": "public", "user": self.local_user.id, "name": "cool name"} + ) + request.user = self.local_user + with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): + view(request, request.user.username, shelf.identifier) + shelf.refresh_from_db() + + self.assertEqual(shelf.name, "cool name") + self.assertEqual(shelf.identifier, f"testshelf-{shelf.id}") + + def test_edit_shelf_name_not_editable(self, *_): + """can't change the name of an non-editable shelf""" + view = views.Shelf.as_view() + shelf = self.local_user.shelf_set.get(identifier="to-read") + self.assertEqual(shelf.privacy, "public") + + request = self.factory.post( + "", {"privacy": "public", "user": self.local_user.id, "name": "cool name"} + ) + request.user = self.local_user + with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): + view(request, request.user.username, shelf.identifier) + + self.assertEqual(shelf.name, "To Read") diff --git a/bookwyrm/tests/views/test_shelf.py b/bookwyrm/tests/views/shelf/test_shelf_actions.py similarity index 70% rename from bookwyrm/tests/views/test_shelf.py rename to bookwyrm/tests/views/shelf/test_shelf_actions.py index b78e241cc..3efae0f45 100644 --- a/bookwyrm/tests/views/test_shelf.py +++ b/bookwyrm/tests/views/shelf/test_shelf_actions.py @@ -3,13 +3,10 @@ import json from unittest.mock import patch from django.core.exceptions import PermissionDenied -from django.template.response import TemplateResponse from django.test import TestCase from django.test.client import RequestFactory from bookwyrm import forms, models, views -from bookwyrm.activitypub import ActivitypubResponse -from bookwyrm.tests.validate_html import validate_html @patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay") @@ -17,7 +14,7 @@ from bookwyrm.tests.validate_html import validate_html @patch("bookwyrm.activitystreams.populate_stream_task.delay") @patch("bookwyrm.activitystreams.add_book_statuses_task.delay") @patch("bookwyrm.activitystreams.remove_book_statuses_task.delay") -class ShelfViews(TestCase): +class ShelfActionViews(TestCase): """tag views""" def setUp(self): @@ -46,85 +43,6 @@ class ShelfViews(TestCase): ) models.SiteSettings.objects.create() - def test_shelf_page(self, *_): - """there are so many views, this just makes sure it LOADS""" - view = views.Shelf.as_view() - shelf = self.local_user.shelf_set.first() - request = self.factory.get("") - request.user = self.local_user - with patch("bookwyrm.views.shelf.is_api_request") as is_api: - is_api.return_value = False - result = view(request, self.local_user.username, shelf.identifier) - self.assertIsInstance(result, TemplateResponse) - validate_html(result.render()) - self.assertEqual(result.status_code, 200) - - with patch("bookwyrm.views.shelf.is_api_request") as is_api: - is_api.return_value = True - result = view(request, self.local_user.username, shelf.identifier) - self.assertIsInstance(result, ActivitypubResponse) - self.assertEqual(result.status_code, 200) - - request = self.factory.get("/?page=1") - request.user = self.local_user - with patch("bookwyrm.views.shelf.is_api_request") as is_api: - is_api.return_value = True - result = view(request, self.local_user.username, shelf.identifier) - self.assertIsInstance(result, ActivitypubResponse) - self.assertEqual(result.status_code, 200) - - def test_edit_shelf_privacy(self, *_): - """set name or privacy on shelf""" - view = views.Shelf.as_view() - shelf = self.local_user.shelf_set.get(identifier="to-read") - self.assertEqual(shelf.privacy, "public") - - request = self.factory.post( - "", - { - "privacy": "unlisted", - "user": self.local_user.id, - "name": "To Read", - }, - ) - request.user = self.local_user - view(request, self.local_user.username, shelf.identifier) - shelf.refresh_from_db() - - self.assertEqual(shelf.privacy, "unlisted") - - def test_edit_shelf_name(self, *_): - """change the name of an editable shelf""" - view = views.Shelf.as_view() - shelf = models.Shelf.objects.create(name="Test Shelf", user=self.local_user) - self.assertEqual(shelf.privacy, "public") - - request = self.factory.post( - "", {"privacy": "public", "user": self.local_user.id, "name": "cool name"} - ) - request.user = self.local_user - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): - view(request, request.user.username, shelf.identifier) - shelf.refresh_from_db() - - self.assertEqual(shelf.name, "cool name") - self.assertEqual(shelf.identifier, f"testshelf-{shelf.id}") - - def test_edit_shelf_name_not_editable(self, *_): - """can't change the name of an non-editable shelf""" - view = views.Shelf.as_view() - shelf = self.local_user.shelf_set.get(identifier="to-read") - self.assertEqual(shelf.privacy, "public") - - request = self.factory.post( - "", {"privacy": "public", "user": self.local_user.id, "name": "cool name"} - ) - request.user = self.local_user - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): - view(request, request.user.username, shelf.identifier) - - self.assertEqual(shelf.name, "To Read") - def test_shelve(self, *_): """shelve a book""" request = self.factory.post( @@ -182,6 +100,30 @@ class ShelfViews(TestCase): # make sure the book is on the shelf self.assertEqual(shelf.books.get(), self.book) + def test_shelve_read_with_change_shelf(self, *_): + """special behavior for the read shelf""" + previous_shelf = models.Shelf.objects.get(identifier="reading") + models.ShelfBook.objects.create( + shelf=previous_shelf, user=self.local_user, book=self.book + ) + shelf = models.Shelf.objects.get(identifier="read") + + request = self.factory.post( + "", + { + "book": self.book.id, + "shelf": shelf.identifier, + "change-shelf-from": previous_shelf.identifier, + }, + ) + request.user = self.local_user + + with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): + views.shelve(request) + # make sure the book is on the shelf + self.assertEqual(shelf.books.get(), self.book) + self.assertEqual(list(previous_shelf.books.all()), []) + def test_unshelve(self, *_): """remove a book from a shelf""" with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): diff --git a/bookwyrm/tests/views/test_author.py b/bookwyrm/tests/views/test_author.py index 7a0065c9e..ccbfe5493 100644 --- a/bookwyrm/tests/views/test_author.py +++ b/bookwyrm/tests/views/test_author.py @@ -1,6 +1,7 @@ """ test for app action functionality """ from unittest.mock import patch -from django.contrib.auth.models import Group, Permission + +from django.contrib.auth.models import AnonymousUser, Group, Permission from django.contrib.contenttypes.models import ContentType from django.core.exceptions import PermissionDenied from django.template.response import TemplateResponse @@ -9,6 +10,7 @@ from django.test.client import RequestFactory from bookwyrm import forms, models, views from bookwyrm.activitypub import ActivitypubResponse +from bookwyrm.tests.validate_html import validate_html class AuthorViews(TestCase): @@ -43,6 +45,8 @@ class AuthorViews(TestCase): parent_work=self.work, ) + self.anonymous_user = AnonymousUser + self.anonymous_user.is_authenticated = False models.SiteSettings.objects.create() def test_author_page(self): @@ -50,15 +54,33 @@ class AuthorViews(TestCase): view = views.Author.as_view() author = models.Author.objects.create(name="Jessica") request = self.factory.get("") + request.user = self.local_user with patch("bookwyrm.views.author.is_api_request") as is_api: is_api.return_value = False result = view(request, author.id) self.assertIsInstance(result, TemplateResponse) - result.render() - self.assertEqual(result.status_code, 200) + validate_html(result.render()) self.assertEqual(result.status_code, 200) + def test_author_page_logged_out(self): + """there are so many views, this just makes sure it LOADS""" + view = views.Author.as_view() + author = models.Author.objects.create(name="Jessica") request = self.factory.get("") + request.user = self.anonymous_user + with patch("bookwyrm.views.author.is_api_request") as is_api: + is_api.return_value = False + result = view(request, author.id) + self.assertIsInstance(result, TemplateResponse) + validate_html(result.render()) + self.assertEqual(result.status_code, 200) + + def test_author_page_api_response(self): + """there are so many views, this just makes sure it LOADS""" + view = views.Author.as_view() + author = models.Author.objects.create(name="Jessica") + request = self.factory.get("") + request.user = self.local_user with patch("bookwyrm.views.author.is_api_request") as is_api: is_api.return_value = True result = view(request, author.id) @@ -75,8 +97,7 @@ class AuthorViews(TestCase): result = view(request, author.id) self.assertIsInstance(result, TemplateResponse) - result.render() - self.assertEqual(result.status_code, 200) + validate_html(result.render()) self.assertEqual(result.status_code, 200) def test_edit_author(self): @@ -125,5 +146,5 @@ class AuthorViews(TestCase): resp = view(request, author.id) author.refresh_from_db() self.assertEqual(author.name, "Test Author") - resp.render() + validate_html(resp.render()) self.assertEqual(resp.status_code, 200) diff --git a/bookwyrm/tests/views/test_group.py b/bookwyrm/tests/views/test_group.py new file mode 100644 index 000000000..c7e0a0f76 --- /dev/null +++ b/bookwyrm/tests/views/test_group.py @@ -0,0 +1,119 @@ +""" test for app action functionality """ +from unittest.mock import patch +from django.contrib.auth import decorators + +from django.template.response import TemplateResponse +from django.test import TestCase +from django.test.client import RequestFactory + +from bookwyrm import models, views, forms +from bookwyrm.tests.validate_html import validate_html + + +@patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay") +class GroupViews(TestCase): + """view group and edit details""" + + def setUp(self): + """we need basic test data and mocks""" + self.factory = RequestFactory() + with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"), patch( + "bookwyrm.activitystreams.populate_stream_task.delay" + ): + self.local_user = models.User.objects.create_user( + "mouse@local.com", + "mouse@mouse.mouse", + "password", + local=True, + localname="mouse", + ) + + self.testgroup = models.Group.objects.create( + name="Test Group", + description="Initial description", + user=self.local_user, + privacy="public", + ) + self.membership = models.GroupMember.objects.create( + group=self.testgroup, user=self.local_user + ) + + models.SiteSettings.objects.create() + + def test_group_get(self, _): + """there are so many views, this just makes sure it LOADS""" + view = views.Group.as_view() + request = self.factory.get("") + request.user = self.local_user + result = view(request, group_id=self.testgroup.id) + self.assertIsInstance(result, TemplateResponse) + validate_html(result.render()) + self.assertEqual(result.status_code, 200) + + def test_usergroups_get(self, _): + """there are so many views, this just makes sure it LOADS""" + view = views.UserGroups.as_view() + request = self.factory.get("") + request.user = self.local_user + result = view(request, username="mouse@local.com") + self.assertIsInstance(result, TemplateResponse) + validate_html(result.render()) + self.assertEqual(result.status_code, 200) + + @patch("bookwyrm.suggested_users.SuggestedUsers.get_suggestions") + def test_findusers_get(self, *_): + """there are so many views, this just makes sure it LOADS""" + view = views.FindUsers.as_view() + request = self.factory.get("") + request.user = self.local_user + result = view(request, group_id=self.testgroup.id) + self.assertIsInstance(result, TemplateResponse) + validate_html(result.render()) + self.assertEqual(result.status_code, 200) + + def test_group_create(self, _): + """create group view""" + view = views.UserGroups.as_view() + request = self.factory.post( + "", + { + "name": "A group", + "description": "wowzers", + "privacy": "unlisted", + "user": self.local_user.id, + }, + ) + request.user = self.local_user + result = view(request, "username") + + self.assertEqual(result.status_code, 302) + new_group = models.Group.objects.filter(name="A group").get() + self.assertEqual(new_group.description, "wowzers") + self.assertEqual(new_group.privacy, "unlisted") + self.assertTrue( + models.GroupMember.objects.filter( + group=new_group, user=self.local_user + ).exists() + ) + + def test_group_edit(self, _): + """test editing a "group" database entry""" + + view = views.Group.as_view() + request = self.factory.post( + "", + { + "name": "Updated Group name", + "description": "wow", + "privacy": "direct", + "user": self.local_user.id, + }, + ) + request.user = self.local_user + + result = view(request, group_id=self.testgroup.id) + self.assertEqual(result.status_code, 302) + self.testgroup.refresh_from_db() + self.assertEqual(self.testgroup.name, "Updated Group name") + self.assertEqual(self.testgroup.description, "wow") + self.assertEqual(self.testgroup.privacy, "direct") diff --git a/bookwyrm/urls.py b/bookwyrm/urls.py index e6c9ad0b4..839d783fd 100644 --- a/bookwyrm/urls.py +++ b/bookwyrm/urls.py @@ -253,6 +253,33 @@ urlpatterns = [ name="user-following", ), re_path(r"^hide-suggestions/?$", views.hide_suggestions, name="hide-suggestions"), + # groups + re_path(rf"{USER_PATH}/groups/?$", views.UserGroups.as_view(), name="user-groups"), + re_path( + r"^group/(?P\d+)(.json)?/?$", views.Group.as_view(), name="group" + ), + re_path( + r"^group/delete/(?P\d+)/?$", views.delete_group, name="delete-group" + ), + re_path( + r"^group/(?P\d+)/add-users/?$", + views.FindUsers.as_view(), + name="group-find-users", + ), + re_path(r"^add-group-member/?$", views.invite_member, name="invite-group-member"), + re_path( + r"^remove-group-member/?$", views.remove_member, name="remove-group-member" + ), + re_path( + r"^accept-group-invitation/?$", + views.accept_membership, + name="accept-group-invitation", + ), + re_path( + r"^reject-group-invitation/?$", + views.reject_membership, + name="reject-group-invitation", + ), # lists re_path(rf"{USER_PATH}/lists/?$", views.UserLists.as_view(), name="user-lists"), re_path(r"^list/?$", views.Lists.as_view(), name="lists"), diff --git a/bookwyrm/views/__init__.py b/bookwyrm/views/__init__.py index 0ac231996..e1dd83557 100644 --- a/bookwyrm/views/__init__.py +++ b/bookwyrm/views/__init__.py @@ -32,6 +32,17 @@ from .books.books import Book, upload_cover, add_description, resolve_book from .books.edit_book import EditBook, ConfirmEditBook from .books.editions import Editions, switch_edition +# landing +from .landing.landing import About, Home, Landing +from .landing.login import Login, Logout +from .landing.register import Register, ConfirmEmail, ConfirmEmailCode, resend_link +from .landing.password import PasswordResetRequest, PasswordReset + +# shelves +from .shelf.shelf import Shelf +from .shelf.shelf_actions import create_shelf, delete_shelf +from .shelf.shelf_actions import shelve, unshelve + # misc views from .author import Author, EditAuthor from .directory import Directory @@ -41,25 +52,28 @@ from .follow import follow, unfollow from .follow import accept_follow_request, delete_follow_request from .get_started import GetStartedBooks, GetStartedProfile, GetStartedUsers from .goal import Goal, hide_goal +from .group import ( + Group, + UserGroups, + FindUsers, + delete_group, + invite_member, + remove_member, + accept_membership, + reject_membership, +) from .import_data import Import, ImportStatus from .inbox import Inbox from .interaction import Favorite, Unfavorite, Boost, Unboost from .isbn import Isbn -from .landing import About, Home, Landing from .list import Lists, SavedLists, List, Curate, UserLists from .list import save_list, unsave_list, delete_list -from .login import Login, Logout from .notifications import Notifications from .outbox import Outbox from .reading import create_readthrough, delete_readthrough, delete_progressupdate from .reading import ReadingStatus -from .register import Register, ConfirmEmail, ConfirmEmailCode, resend_link from .rss_feed import RssFeed -from .password import PasswordResetRequest, PasswordReset from .search import Search -from .shelf import Shelf -from .shelf import create_shelf, delete_shelf -from .shelf import shelve, unshelve from .status import CreateStatus, EditStatus, DeleteStatus, update_progress from .status import edit_readthrough from .updates import get_notification_count, get_unread_status_count diff --git a/bookwyrm/views/admin/invite.py b/bookwyrm/views/admin/invite.py index 8a3db61a9..8fd68b705 100644 --- a/bookwyrm/views/admin/invite.py +++ b/bookwyrm/views/admin/invite.py @@ -81,7 +81,7 @@ class Invite(View): "invite": invite, "valid": invite.valid() if invite else True, } - return TemplateResponse(request, "invite.html", data) + return TemplateResponse(request, "landing/invite.html", data) # post handling is in views.register.Register diff --git a/bookwyrm/views/author.py b/bookwyrm/views/author.py index e1e9247de..6c3ee36ff 100644 --- a/bookwyrm/views/author.py +++ b/bookwyrm/views/author.py @@ -1,6 +1,7 @@ """ the good people stuff! the authors! """ from django.contrib.auth.decorators import login_required, permission_required -from django.db.models import Q +from django.core.paginator import Paginator +from django.db.models import OuterRef, Subquery, F, Q from django.shortcuts import get_object_or_404, redirect from django.template.response import TemplateResponse from django.utils.decorators import method_decorator @@ -8,7 +9,8 @@ from django.views import View from bookwyrm import forms, models from bookwyrm.activitypub import ActivitypubResponse -from .helpers import is_api_request +from bookwyrm.settings import PAGE_LENGTH +from bookwyrm.views.helpers import is_api_request # pylint: disable= no-self-use @@ -22,12 +24,27 @@ class Author(View): if is_api_request(request): return ActivitypubResponse(author.to_activity()) - books = models.Work.objects.filter( - Q(authors=author) | Q(editions__authors=author) - ).distinct() + default_editions = models.Edition.objects.filter( + parent_work=OuterRef("parent_work") + ).order_by("-edition_rank") + + books = ( + models.Edition.viewer_aware_objects(request.user) + .filter(Q(authors=author) | Q(parent_work__authors=author)) + .annotate(default_id=Subquery(default_editions.values("id")[:1])) + .filter(default_id=F("id")) + .order_by("-first_published_date", "-published_date", "-created_date") + .prefetch_related("authors") + ) + + paginated = Paginator(books, PAGE_LENGTH) + page = paginated.get_page(request.GET.get("page")) data = { "author": author, - "books": [b.default_edition for b in books], + "books": page, + "page_range": paginated.get_elided_page_range( + page.number, on_each_side=2, on_ends=1 + ), } return TemplateResponse(request, "author/author.html", data) diff --git a/bookwyrm/views/group.py b/bookwyrm/views/group.py new file mode 100644 index 000000000..9ee99bffa --- /dev/null +++ b/bookwyrm/views/group.py @@ -0,0 +1,311 @@ +"""group views""" +from django.apps import apps +from django.contrib.auth.decorators import login_required +from django.db import IntegrityError +from django.core.paginator import Paginator +from django.http import HttpResponseBadRequest +from django.shortcuts import get_object_or_404, redirect +from django.template.response import TemplateResponse +from django.utils.decorators import method_decorator +from django.views import View +from django.views.decorators.http import require_POST +from django.contrib.postgres.search import TrigramSimilarity +from django.db.models.functions import Greatest + +from bookwyrm import forms, models +from bookwyrm.suggested_users import suggested_users +from .helpers import get_user_from_username + +# pylint: disable=no-self-use +class Group(View): + """group page""" + + def get(self, request, group_id): + """display a group""" + + group = get_object_or_404(models.Group, id=group_id) + group.raise_visible_to_user(request.user) + lists = ( + models.List.privacy_filter(request.user) + .filter(group=group) + .order_by("-updated_date") + ) + + data = { + "group": group, + "lists": lists, + "group_form": forms.GroupForm(instance=group), + "path": "/group", + } + return TemplateResponse(request, "groups/group.html", data) + + @method_decorator(login_required, name="dispatch") + def post(self, request, group_id): + """edit a group""" + user_group = get_object_or_404(models.Group, id=group_id) + form = forms.GroupForm(request.POST, instance=user_group) + if not form.is_valid(): + return redirect("group", user_group.id) + user_group = form.save() + + # let the other members know something about the group changed + memberships = models.GroupMember.objects.filter(group=user_group) + model = apps.get_model("bookwyrm.Notification", require_ready=True) + for field in form.changed_data: + notification_type = ( + "GROUP_PRIVACY" + if field == "privacy" + else "GROUP_NAME" + if field == "name" + else "GROUP_DESCRIPTION" + if field == "description" + else None + ) + if notification_type: + for membership in memberships: + member = membership.user + if member != request.user: + model.objects.create( + user=member, + related_user=request.user, + related_group=user_group, + notification_type=notification_type, + ) + + return redirect("group", user_group.id) + + +@method_decorator(login_required, name="dispatch") +class UserGroups(View): + """a user's groups page""" + + def get(self, request, username): + """display a group""" + user = get_user_from_username(request.user, username) + groups = ( + models.Group.privacy_filter(request.user) + .filter(memberships__user=user) + .order_by("-updated_date") + ) + paginated = Paginator(groups, 12) + + data = { + "groups": paginated.get_page(request.GET.get("page")), + "is_self": request.user.id == user.id, + "user": user, + "group_form": forms.GroupForm(), + "path": user.local_path + "/group", + } + return TemplateResponse(request, "user/groups.html", data) + + @method_decorator(login_required, name="dispatch") + # pylint: disable=unused-argument + def post(self, request, username): + """create a user group""" + form = forms.GroupForm(request.POST) + if not form.is_valid(): + return redirect(request.user.local_path + "/groups") + group = form.save() + # add the creator as a group member + models.GroupMember.objects.create(group=group, user=request.user) + return redirect("group", group.id) + + +@method_decorator(login_required, name="dispatch") +class FindUsers(View): + """find friends to add to your group""" + + # this is mostly borrowed from the Get Started friend finder + + def get(self, request, group_id): + """basic profile info""" + user_query = request.GET.get("user_query") + group = get_object_or_404(models.Group, id=group_id) + + if not group: + return HttpResponseBadRequest() + + if not group.user == request.user: + return HttpResponseBadRequest() + + user_results = ( + models.User.viewer_aware_objects(request.user) + .exclude( + memberships__in=group.memberships.all() + ) # don't suggest users who are already members + .annotate( + similarity=Greatest( + TrigramSimilarity("username", user_query), + TrigramSimilarity("localname", user_query), + ) + ) + .filter(similarity__gt=0.5, local=True) + .order_by("-similarity")[:5] + ) + data = {"no_results": not user_results} + + if user_results.count() < 5: + user_results = list(user_results) + suggested_users.get_suggestions( + request.user, local=True + ) + + data = { + "suggested_users": user_results, + "group": group, + "group_form": forms.GroupForm(instance=group), + "user_query": user_query, + "requestor_is_manager": request.user == group.user, + } + return TemplateResponse(request, "groups/find_users.html", data) + + +@require_POST +@login_required +def delete_group(request, group_id): + """delete a group""" + group = get_object_or_404(models.Group, id=group_id) + + # only the owner can delete a group + group.raise_not_deletable(request.user) + + # deal with any group lists + models.List.objects.filter(group=group).update(curation="closed", group=None) + + group.delete() + return redirect(request.user.local_path + "/groups") + + +@require_POST +@login_required +def invite_member(request): + """invite a member to the group""" + + group = get_object_or_404(models.Group, id=request.POST.get("group")) + if not group: + return HttpResponseBadRequest() + + user = get_user_from_username(request.user, request.POST["user"]) + if not user: + return HttpResponseBadRequest() + + if not group.user == request.user: + return HttpResponseBadRequest() + + try: + models.GroupMemberInvitation.objects.create(user=user, group=group) + + except IntegrityError: + pass + + return redirect(user.local_path) + + +@require_POST +@login_required +def remove_member(request): + """remove a member from the group""" + + group = get_object_or_404(models.Group, id=request.POST.get("group")) + if not group: + return HttpResponseBadRequest() + + user = get_user_from_username(request.user, request.POST["user"]) + if not user: + return HttpResponseBadRequest() + + # you can't be removed from your own group + if request.POST["user"] == group.user: + return HttpResponseBadRequest() + + is_member = models.GroupMember.objects.filter(group=group, user=user).exists() + is_invited = models.GroupMemberInvitation.objects.filter( + group=group, user=user + ).exists() + + if is_invited: + try: + invitation = models.GroupMemberInvitation.objects.get( + user=user, group=group + ) + + invitation.reject() + + except IntegrityError: + pass + + if is_member: + + try: + models.List.remove_from_group(group.user, user) + models.GroupMember.remove(group.user, user) + + except IntegrityError: + pass + + memberships = models.GroupMember.objects.filter(group=group) + model = apps.get_model("bookwyrm.Notification", require_ready=True) + notification_type = "LEAVE" if user == request.user else "REMOVE" + # let the other members know about it + for membership in memberships: + member = membership.user + if member != request.user: + model.objects.create( + user=member, + related_user=user, + related_group=group, + notification_type=notification_type, + ) + + # let the user (now ex-member) know as well, if they were removed + if notification_type == "REMOVE": + model.objects.create( + user=user, + related_group=group, + notification_type=notification_type, + ) + + return redirect(group.local_path) + + +@require_POST +@login_required +def accept_membership(request): + """accept an invitation to join a group""" + + group = models.Group.objects.get(id=request.POST["group"]) + if not group: + return HttpResponseBadRequest() + + invite = models.GroupMemberInvitation.objects.get(group=group, user=request.user) + if not invite: + return HttpResponseBadRequest() + + try: + invite.accept() + + except IntegrityError: + pass + + return redirect(group.local_path) + + +@require_POST +@login_required +def reject_membership(request): + """reject an invitation to join a group""" + + group = models.Group.objects.get(id=request.POST["group"]) + if not group: + return HttpResponseBadRequest() + + invite = models.GroupMemberInvitation.objects.get(group=group, user=request.user) + if not invite: + return HttpResponseBadRequest() + + try: + invite.reject() + + except IntegrityError: + pass + + return redirect(request.user.local_path) diff --git a/bookwyrm/views/import_data.py b/bookwyrm/views/import_data.py index fe54c39a9..5e113be88 100644 --- a/bookwyrm/views/import_data.py +++ b/bookwyrm/views/import_data.py @@ -51,7 +51,7 @@ class Import(View): elif source == "Storygraph": importer = StorygraphImporter() else: - # Default : GoodReads + # Default : Goodreads importer = GoodreadsImporter() try: diff --git a/bookwyrm/views/landing/__init__.py b/bookwyrm/views/landing/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/bookwyrm/views/landing.py b/bookwyrm/views/landing/landing.py similarity index 93% rename from bookwyrm/views/landing.py rename to bookwyrm/views/landing/landing.py index 6f480b70f..c8bba0664 100644 --- a/bookwyrm/views/landing.py +++ b/bookwyrm/views/landing/landing.py @@ -3,8 +3,8 @@ from django.template.response import TemplateResponse from django.views import View from bookwyrm import forms -from .feed import Feed -from . import helpers +from bookwyrm.views import helpers +from bookwyrm.views.feed import Feed # pylint: disable= no-self-use diff --git a/bookwyrm/views/login.py b/bookwyrm/views/landing/login.py similarity index 95% rename from bookwyrm/views/login.py rename to bookwyrm/views/landing/login.py index 91fda35a3..5c25e30e2 100644 --- a/bookwyrm/views/login.py +++ b/bookwyrm/views/landing/login.py @@ -29,7 +29,7 @@ class Login(View): "login_form": forms.LoginForm(), "register_form": forms.RegisterForm(), } - return TemplateResponse(request, "login.html", data) + return TemplateResponse(request, "landing/login.html", data) @sensitive_variables("password") @method_decorator(sensitive_post_parameters("password")) @@ -69,7 +69,7 @@ class Login(View): login_form.non_field_errors = _("Username or password are incorrect") register_form = forms.RegisterForm() data = {"login_form": login_form, "register_form": register_form} - return TemplateResponse(request, "login.html", data) + return TemplateResponse(request, "landing/login.html", data) @method_decorator(login_required, name="dispatch") diff --git a/bookwyrm/views/password.py b/bookwyrm/views/landing/password.py similarity index 83% rename from bookwyrm/views/password.py rename to bookwyrm/views/landing/password.py index d3104ad46..90713e29d 100644 --- a/bookwyrm/views/password.py +++ b/bookwyrm/views/landing/password.py @@ -18,7 +18,7 @@ class PasswordResetRequest(View): """password reset page""" return TemplateResponse( request, - "password_reset_request.html", + "landing/password_reset_request.html", ) def post(self, request): @@ -30,7 +30,9 @@ class PasswordResetRequest(View): ) except models.User.DoesNotExist: data = {"error": _("No user with that email address was found.")} - return TemplateResponse(request, "password_reset_request.html", data) + return TemplateResponse( + request, "landing/password_reset_request.html", data + ) # remove any existing password reset cods for this user models.PasswordReset.objects.filter(user=user).all().delete() @@ -39,7 +41,7 @@ class PasswordResetRequest(View): code = models.PasswordReset.objects.create(user=user) password_reset_email(code) data = {"message": _(f"A password reset link was sent to {email}")} - return TemplateResponse(request, "password_reset_request.html", data) + return TemplateResponse(request, "landing/password_reset_request.html", data) class PasswordReset(View): @@ -56,7 +58,7 @@ class PasswordReset(View): except models.PasswordReset.DoesNotExist: raise PermissionDenied() - return TemplateResponse(request, "password_reset.html", {"code": code}) + return TemplateResponse(request, "landing/password_reset.html", {"code": code}) def post(self, request, code): """allow a user to change their password through an emailed token""" @@ -64,7 +66,7 @@ class PasswordReset(View): reset_code = models.PasswordReset.objects.get(code=code) except models.PasswordReset.DoesNotExist: data = {"errors": ["Invalid password reset link"]} - return TemplateResponse(request, "password_reset.html", data) + return TemplateResponse(request, "landing/password_reset.html", data) user = reset_code.user @@ -73,7 +75,7 @@ class PasswordReset(View): if new_password != confirm_password: data = {"errors": ["Passwords do not match"]} - return TemplateResponse(request, "password_reset.html", data) + return TemplateResponse(request, "landing/password_reset.html", data) user.set_password(new_password) user.save(broadcast=False, update_fields=["password"]) diff --git a/bookwyrm/views/register.py b/bookwyrm/views/landing/register.py similarity index 97% rename from bookwyrm/views/register.py rename to bookwyrm/views/landing/register.py index dd8249203..b91d1b5a9 100644 --- a/bookwyrm/views/register.py +++ b/bookwyrm/views/landing/register.py @@ -65,8 +65,8 @@ class Register(View): "valid": invite.valid() if invite else True, } if invite: - return TemplateResponse(request, "invite.html", data) - return TemplateResponse(request, "login.html", data) + return TemplateResponse(request, "landing/invite.html", data) + return TemplateResponse(request, "landing/login.html", data) username = f"{localname}@{DOMAIN}" user = models.User.objects.create_user( diff --git a/bookwyrm/views/list.py b/bookwyrm/views/list.py index b01a0e0e3..97eaf9d6f 100644 --- a/bookwyrm/views/list.py +++ b/bookwyrm/views/list.py @@ -40,7 +40,6 @@ class Lists(View): .order_by("-updated_date") .distinct() ) - paginated = Paginator(lists, 12) data = { "lists": paginated.get_page(request.GET.get("page")), @@ -57,6 +56,10 @@ class Lists(View): if not form.is_valid(): return redirect("lists") book_list = form.save() + # list should not have a group if it is not group curated + if not book_list.curation == "group": + book_list.group = None + book_list.save(broadcast=False) return redirect(book_list.local_path) @@ -181,7 +184,6 @@ class List(View): return TemplateResponse(request, "lists/list.html", data) @method_decorator(login_required, name="dispatch") - # pylint: disable=unused-argument def post(self, request, list_id): """edit a list""" book_list = get_object_or_404(models.List, id=list_id) @@ -191,6 +193,10 @@ class List(View): if not form.is_valid(): return redirect("list", book_list.id) book_list = form.save() + if not book_list.curation == "group": + book_list.group = None + book_list.save(broadcast=False) + return redirect(book_list.local_path) @@ -275,12 +281,22 @@ def delete_list(request, list_id): def add_book(request): """put a book on a list""" book_list = get_object_or_404(models.List, id=request.POST.get("list")) + is_group_member = False + if book_list.curation == "group": + is_group_member = models.GroupMember.objects.filter( + group=book_list.group, user=request.user + ).exists() + book_list.raise_visible_to_user(request.user) book = get_object_or_404(models.Edition, id=request.POST.get("book")) # do you have permission to add to the list? try: - if request.user == book_list.user or book_list.curation == "open": + if ( + request.user == book_list.user + or is_group_member + or book_list.curation == "open" + ): # add the book at the latest order of approved books, before pending books order_max = ( book_list.listitem_set.filter(approved=True).aggregate(Max("order"))[ @@ -323,14 +339,17 @@ def add_book(request): @login_required def remove_book(request, list_id): """remove a book from a list""" + book_list = get_object_or_404(models.List, id=list_id) item = get_object_or_404(models.ListItem, id=request.POST.get("item")) + item.raise_not_deletable(request.user) with transaction.atomic(): deleted_order = item.order item.delete() normalize_book_list_ordering(book_list.id, start=deleted_order) + return redirect("list", list_id) diff --git a/bookwyrm/views/preferences/block.py b/bookwyrm/views/preferences/block.py index 1eccf4612..2ccd3c065 100644 --- a/bookwyrm/views/preferences/block.py +++ b/bookwyrm/views/preferences/block.py @@ -23,6 +23,10 @@ class Block(View): models.UserBlocks.objects.create( user_subject=request.user, user_object=to_block ) + # remove the blocked users's lists from the groups + models.List.remove_from_group(request.user, to_block) + # remove the blocked user from all blocker's owned groups + models.GroupMember.remove(request.user, to_block) return redirect("prefs-block") diff --git a/bookwyrm/views/shelf/__init__.py b/bookwyrm/views/shelf/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/bookwyrm/views/shelf.py b/bookwyrm/views/shelf/shelf.py similarity index 60% rename from bookwyrm/views/shelf.py rename to bookwyrm/views/shelf/shelf.py index 0b830d906..f8cffe93f 100644 --- a/bookwyrm/views/shelf.py +++ b/bookwyrm/views/shelf/shelf.py @@ -1,7 +1,6 @@ """ shelf views """ from collections import namedtuple -from django.db import IntegrityError, transaction from django.db.models import OuterRef, Subquery, F from django.contrib.auth.decorators import login_required from django.core.paginator import Paginator @@ -11,12 +10,11 @@ from django.template.response import TemplateResponse from django.utils.decorators import method_decorator from django.utils.translation import gettext_lazy as _ from django.views import View -from django.views.decorators.http import require_POST from bookwyrm import forms, models from bookwyrm.activitypub import ActivitypubResponse from bookwyrm.settings import PAGE_LENGTH -from .helpers import is_api_request, get_user_from_username +from bookwyrm.views.helpers import is_api_request, get_user_from_username # pylint: disable=no-self-use @@ -128,102 +126,6 @@ class Shelf(View): return redirect(shelf.local_path) -@login_required -@require_POST -def create_shelf(request): - """user generated shelves""" - form = forms.ShelfForm(request.POST) - if not form.is_valid(): - return redirect(request.headers.get("Referer", "/")) - - shelf = form.save() - return redirect(shelf.local_path) - - -@login_required -@require_POST -def delete_shelf(request, shelf_id): - """user generated shelves""" - shelf = get_object_or_404(models.Shelf, id=shelf_id) - shelf.raise_not_deletable(request.user) - - shelf.delete() - return redirect("user-shelves", request.user.localname) - - -@login_required -@require_POST -@transaction.atomic -def shelve(request): - """put a book on a user's shelf""" - book = get_object_or_404(models.Edition, id=request.POST.get("book")) - desired_shelf = get_object_or_404( - request.user.shelf_set, identifier=request.POST.get("shelf") - ) - - # first we need to remove from the specified shelf - change_from_current_identifier = request.POST.get("change-shelf-from") - if change_from_current_identifier: - # find the shelfbook obj and delete it - get_object_or_404( - models.ShelfBook, - book=book, - user=request.user, - shelf__identifier=change_from_current_identifier, - ).delete() - - # A book can be on multiple shelves, but only on one read status shelf at a time - if desired_shelf.identifier in models.Shelf.READ_STATUS_IDENTIFIERS: - # figure out where state shelf it's currently on (if any) - current_read_status_shelfbook = ( - models.ShelfBook.objects.select_related("shelf") - .filter( - shelf__identifier__in=models.Shelf.READ_STATUS_IDENTIFIERS, - user=request.user, - book=book, - ) - .first() - ) - if current_read_status_shelfbook is not None: - if ( - current_read_status_shelfbook.shelf.identifier - != desired_shelf.identifier - ): - current_read_status_shelfbook.delete() - else: # It is already on the shelf - return redirect(request.headers.get("Referer", "/")) - - # create the new shelf-book entry - models.ShelfBook.objects.create( - book=book, shelf=desired_shelf, user=request.user - ) - else: - # we're putting it on a custom shelf - try: - models.ShelfBook.objects.create( - book=book, shelf=desired_shelf, user=request.user - ) - # The book is already on this shelf. - # Might be good to alert, or reject the action? - except IntegrityError: - pass - return redirect(request.headers.get("Referer", "/")) - - -@login_required -@require_POST -def unshelve(request): - """put a on a user's shelf""" - book = get_object_or_404(models.Edition, id=request.POST.get("book")) - shelf_book = get_object_or_404( - models.ShelfBook, book=book, shelf__id=request.POST["shelf"] - ) - shelf_book.raise_not_deletable(request.user) - - shelf_book.delete() - return redirect(request.headers.get("Referer", "/")) - - def sort_books(books, sort): """Books in shelf sorting""" sort_fields = [ diff --git a/bookwyrm/views/shelf/shelf_actions.py b/bookwyrm/views/shelf/shelf_actions.py new file mode 100644 index 000000000..702b72c13 --- /dev/null +++ b/bookwyrm/views/shelf/shelf_actions.py @@ -0,0 +1,103 @@ +""" shelf views """ +from django.db import IntegrityError, transaction +from django.contrib.auth.decorators import login_required +from django.shortcuts import get_object_or_404, redirect +from django.views.decorators.http import require_POST + +from bookwyrm import forms, models + + +@login_required +@require_POST +def create_shelf(request): + """user generated shelves""" + form = forms.ShelfForm(request.POST) + if not form.is_valid(): + return redirect(request.headers.get("Referer", "/")) + + shelf = form.save() + return redirect(shelf.local_path) + + +@login_required +@require_POST +def delete_shelf(request, shelf_id): + """user generated shelves""" + shelf = get_object_or_404(models.Shelf, id=shelf_id) + shelf.raise_not_deletable(request.user) + + shelf.delete() + return redirect("user-shelves", request.user.localname) + + +@login_required +@require_POST +@transaction.atomic +def shelve(request): + """put a book on a user's shelf""" + book = get_object_or_404(models.Edition, id=request.POST.get("book")) + desired_shelf = get_object_or_404( + request.user.shelf_set, identifier=request.POST.get("shelf") + ) + + # first we need to remove from the specified shelf + change_from_current_identifier = request.POST.get("change-shelf-from") + if change_from_current_identifier: + # find the shelfbook obj and delete it + get_object_or_404( + models.ShelfBook, + book=book, + user=request.user, + shelf__identifier=change_from_current_identifier, + ).delete() + + # A book can be on multiple shelves, but only on one read status shelf at a time + if desired_shelf.identifier in models.Shelf.READ_STATUS_IDENTIFIERS: + # figure out where state shelf it's currently on (if any) + current_read_status_shelfbook = ( + models.ShelfBook.objects.select_related("shelf") + .filter( + shelf__identifier__in=models.Shelf.READ_STATUS_IDENTIFIERS, + user=request.user, + book=book, + ) + .first() + ) + if current_read_status_shelfbook is not None: + if ( + current_read_status_shelfbook.shelf.identifier + != desired_shelf.identifier + ): + current_read_status_shelfbook.delete() + else: # It is already on the shelf + return redirect(request.headers.get("Referer", "/")) + + # create the new shelf-book entry + models.ShelfBook.objects.create( + book=book, shelf=desired_shelf, user=request.user + ) + else: + # we're putting it on a custom shelf + try: + models.ShelfBook.objects.create( + book=book, shelf=desired_shelf, user=request.user + ) + # The book is already on this shelf. + # Might be good to alert, or reject the action? + except IntegrityError: + pass + return redirect(request.headers.get("Referer", "/")) + + +@login_required +@require_POST +def unshelve(request): + """remove a book from a user's shelf""" + book = get_object_or_404(models.Edition, id=request.POST.get("book")) + shelf_book = get_object_or_404( + models.ShelfBook, book=book, shelf__id=request.POST["shelf"] + ) + shelf_book.raise_not_deletable(request.user) + + shelf_book.delete() + return redirect(request.headers.get("Referer", "/")) diff --git a/bookwyrm/views/user.py b/bookwyrm/views/user.py index 0d8d385b1..b7ab1d3cf 100644 --- a/bookwyrm/views/user.py +++ b/bookwyrm/views/user.py @@ -137,6 +137,25 @@ class Following(View): return TemplateResponse(request, "user/relationships/following.html", data) +class Groups(View): + """list of user's groups view""" + + def get(self, request, username): + """list of groups""" + user = get_user_from_username(request.user, username) + + paginated = Paginator( + models.Group.memberships.filter(user=user).order_by("-created_date"), + PAGE_LENGTH, + ) + data = { + "user": user, + "is_self": request.user.id == user.id, + "group_list": paginated.get_page(request.GET.get("page")), + } + return TemplateResponse(request, "user/groups.html", data) + + @require_POST @login_required def hide_suggestions(request): diff --git a/locale/de_DE/LC_MESSAGES/django.mo b/locale/de_DE/LC_MESSAGES/django.mo index 682f599d5..516685a0e 100644 Binary files a/locale/de_DE/LC_MESSAGES/django.mo and b/locale/de_DE/LC_MESSAGES/django.mo differ diff --git a/locale/de_DE/LC_MESSAGES/django.po b/locale/de_DE/LC_MESSAGES/django.po index de0fd4a48..8fd930fee 100644 --- a/locale/de_DE/LC_MESSAGES/django.po +++ b/locale/de_DE/LC_MESSAGES/django.po @@ -2,8 +2,8 @@ msgid "" msgstr "" "Project-Id-Version: bookwyrm\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-10-06 23:57+0000\n" -"PO-Revision-Date: 2021-10-13 13:32\n" +"POT-Creation-Date: 2021-10-15 22:03+0000\n" +"PO-Revision-Date: 2021-10-20 17:38\n" "Last-Translator: Mouse Reeve \n" "Language-Team: German\n" "Language: de\n" @@ -54,8 +54,8 @@ msgstr "Reihenfolge der Liste" msgid "Book Title" msgstr "Buchtitel" -#: bookwyrm/forms.py:328 bookwyrm/templates/shelf/shelf.html:134 -#: bookwyrm/templates/shelf/shelf.html:165 +#: bookwyrm/forms.py:328 bookwyrm/templates/shelf/shelf.html:136 +#: bookwyrm/templates/shelf/shelf.html:168 #: bookwyrm/templates/snippets/create_status/review.html:33 msgid "Rating" msgstr "Bewertung" @@ -90,11 +90,11 @@ msgstr "Selbstlöschung" #: bookwyrm/models/base_model.py:19 msgid "Moderator suspension" -msgstr "Moderator suspendieren" +msgstr "Moderator*in suspendieren" #: bookwyrm/models/base_model.py:20 msgid "Moderator deletion" -msgstr "Moderatoren löschen" +msgstr "Moderator*in löschen" #: bookwyrm/models/base_model.py:21 msgid "Domain block" @@ -151,45 +151,49 @@ msgstr "Username" msgid "A user with that username already exists." msgstr "Dieser Benutzename ist bereits vergeben." -#: bookwyrm/settings.py:117 +#: bookwyrm/settings.py:118 msgid "Home Timeline" msgstr "Start-Zeitleiste" -#: bookwyrm/settings.py:117 +#: bookwyrm/settings.py:118 msgid "Home" msgstr "Startseite" -#: bookwyrm/settings.py:118 +#: bookwyrm/settings.py:119 msgid "Books Timeline" msgstr "Bücher-Zeitachse" -#: bookwyrm/settings.py:118 bookwyrm/templates/search/layout.html:21 +#: bookwyrm/settings.py:119 bookwyrm/templates/search/layout.html:21 #: bookwyrm/templates/search/layout.html:42 #: bookwyrm/templates/user/layout.html:81 msgid "Books" msgstr "Bücher" -#: bookwyrm/settings.py:164 +#: bookwyrm/settings.py:165 msgid "English" msgstr "English (Englisch)" -#: bookwyrm/settings.py:165 +#: bookwyrm/settings.py:166 msgid "Deutsch (German)" msgstr "Deutsch" -#: bookwyrm/settings.py:166 +#: bookwyrm/settings.py:167 msgid "Español (Spanish)" msgstr "Español (Spanisch)" -#: bookwyrm/settings.py:167 +#: bookwyrm/settings.py:168 msgid "Français (French)" msgstr "Français (Französisch)" -#: bookwyrm/settings.py:168 +#: bookwyrm/settings.py:169 +msgid "Português - Brasil (Brazilian Portuguese)" +msgstr "Português (Portugiesisch)" + +#: bookwyrm/settings.py:170 msgid "简体中文 (Simplified Chinese)" msgstr "简体中文 (Vereinfachtes Chinesisch)" -#: bookwyrm/settings.py:169 +#: bookwyrm/settings.py:171 msgid "繁體中文 (Traditional Chinese)" msgstr "繁體中文 (Chinesisch, traditionell)" @@ -292,7 +296,7 @@ msgstr "Name:" #: bookwyrm/templates/book/edit/edit_book_form.html:79 #: bookwyrm/templates/book/edit/edit_book_form.html:124 msgid "Separate multiple values with commas." -msgstr "Geben Sie mehrere Werte durch Kommas getrennt ein." +msgstr "Mehrere Werte durch Kommas getrennt eingeben." #: bookwyrm/templates/author/edit_author.html:50 msgid "Bio:" @@ -316,20 +320,20 @@ msgstr "Autor*innenidentifikatoren" #: bookwyrm/templates/author/edit_author.html:81 msgid "Openlibrary key:" -msgstr "Openlibrary Schlüssel:" +msgstr "Openlibrary-Schlüssel:" #: bookwyrm/templates/author/edit_author.html:89 #: bookwyrm/templates/book/edit/edit_book_form.html:224 msgid "Inventaire ID:" -msgstr "Inventaire ID:" +msgstr "Inventaire-ID:" #: bookwyrm/templates/author/edit_author.html:97 msgid "Librarything key:" -msgstr "Librarything Schlüssel:" +msgstr "Librarything-Schlüssel:" #: bookwyrm/templates/author/edit_author.html:105 msgid "Goodreads key:" -msgstr "Goodreads Schlüssel:" +msgstr "Goodreads-Schlüssel:" #: bookwyrm/templates/author/edit_author.html:116 #: bookwyrm/templates/book/book.html:140 @@ -378,7 +382,7 @@ msgstr "Cover hinzufügen" #: bookwyrm/templates/book/book.html:77 msgid "Failed to load cover" -msgstr "Fehler beim Laden des Covers" +msgstr "Fehler beim Laden des Titelbilds" #: bookwyrm/templates/book/book.html:117 #, python-format @@ -430,11 +434,11 @@ msgstr "Du hast keine Leseaktivität für dieses Buch." #: bookwyrm/templates/book/book.html:218 msgid "Reviews" -msgstr "Rezensionen" +msgstr "Besprechungen" #: bookwyrm/templates/book/book.html:223 msgid "Your reviews" -msgstr "Deine Rezensionen" +msgstr "Deine Besprechungen" #: bookwyrm/templates/book/book.html:229 msgid "Your comments" @@ -519,7 +523,7 @@ msgstr "Existiert \"%(name)s\" bereits als Autor:in?" #: bookwyrm/templates/book/edit/edit_book.html:64 #, python-format msgid "Author of %(book_title)s" -msgstr "Autor von %(book_title)s" +msgstr "Autor*in von %(book_title)s" #: bookwyrm/templates/book/edit/edit_book.html:68 msgid "This is a new author" @@ -587,7 +591,7 @@ msgstr "Veröffentlichungsdatum:" #: bookwyrm/templates/book/edit/edit_book_form.html:104 msgid "Authors" -msgstr "Autoren" +msgstr "Autor*innen" #: bookwyrm/templates/book/edit/edit_book_form.html:112 #, python-format @@ -597,11 +601,11 @@ msgstr "%(name)s entfernen" #: bookwyrm/templates/book/edit/edit_book_form.html:115 #, python-format msgid "Author page for %(name)s" -msgstr "Autorenseite für %(name)s" +msgstr "Autor*innenseite für %(name)s" #: bookwyrm/templates/book/edit/edit_book_form.html:122 msgid "Add Authors:" -msgstr "Autoren hinzufügen:" +msgstr "Autor*innen hinzufügen:" #: bookwyrm/templates/book/edit/edit_book_form.html:123 msgid "John Doe, Jane Smith" @@ -610,7 +614,7 @@ msgstr "Max Mustermann, Maria Musterfrau" #: bookwyrm/templates/book/edit/edit_book_form.html:132 #: bookwyrm/templates/shelf/shelf.html:127 msgid "Cover" -msgstr "Einband" +msgstr "Titelbild" #: bookwyrm/templates/book/edit/edit_book_form.html:161 msgid "Physical Properties" @@ -643,7 +647,7 @@ msgstr "ISBN 10:" #: bookwyrm/templates/book/edit/edit_book_form.html:216 msgid "Openlibrary ID:" -msgstr "OpenLibrary ID:" +msgstr "OpenLibrary-ID:" #: bookwyrm/templates/book/editions/editions.html:4 #, python-format @@ -669,11 +673,6 @@ msgstr "Sprache" msgid "Search editions" msgstr "Ausgaben suchen" -#: bookwyrm/templates/book/publisher_info.html:21 -#, python-format -msgid "%(format)s" -msgstr "Formate: %(format)s" - #: bookwyrm/templates/book/publisher_info.html:23 #, python-format msgid "%(format)s, %(pages)s pages" @@ -687,7 +686,7 @@ msgstr "%(pages)s Seiten" #: bookwyrm/templates/book/publisher_info.html:38 #, python-format msgid "%(languages)s language" -msgstr "%(languages)s Sprache" +msgstr "%(languages)s-sprachig" #: bookwyrm/templates/book/publisher_info.html:65 #, python-format @@ -697,7 +696,7 @@ msgstr "Am %(date)s von %(publisher)s veröffentlicht." #: bookwyrm/templates/book/publisher_info.html:67 #, python-format msgid "Published %(date)s" -msgstr "Erscheinungsdatum %(date)s" +msgstr "Erschienen am %(date)s" #: bookwyrm/templates/book/publisher_info.html:69 #, python-format @@ -753,8 +752,8 @@ msgid "Help" msgstr "Hilfe" #: bookwyrm/templates/compose.html:5 bookwyrm/templates/compose.html:8 -msgid "Compose status" -msgstr "Status verfassen" +msgid "Edit status" +msgstr "Status bearbeiten" #: bookwyrm/templates/confirm_email/confirm_email.html:4 msgid "Confirm email" @@ -762,7 +761,7 @@ msgstr "E-Mail bestätigen" #: bookwyrm/templates/confirm_email/confirm_email.html:7 msgid "Confirm your email address" -msgstr "Bestätigen Sie Ihre E-Mail-Adresse" +msgstr "Bestätige deine E-Mail-Adresse" #: bookwyrm/templates/confirm_email/confirm_email.html:13 msgid "A confirmation code has been sent to the email address you used to register your account." @@ -854,13 +853,13 @@ msgstr "Empfohlen" #: bookwyrm/templates/user/user_preview.html:16 #: bookwyrm/templates/user/user_preview.html:17 msgid "Locked account" -msgstr "Gesperrter Account" +msgstr "Gesperrtes Benutzer*inkonto" #: bookwyrm/templates/directory/user_card.html:40 msgid "follower you follow" msgid_plural "followers you follow" -msgstr[0] "gegenseitiger Follower" -msgstr[1] "gegenseitige Followers" +msgstr[0] "Follower*in, dem*der du folgst" +msgstr[1] "Follower*innen, denen du folgst" #: bookwyrm/templates/directory/user_card.html:47 msgid "book on your shelves" @@ -878,7 +877,7 @@ msgstr "zuletzt aktiv" #: bookwyrm/templates/directory/user_type_filter.html:5 msgid "User type" -msgstr "Art der Benutzer*innen" +msgstr "Benutzer*in-Typ" #: bookwyrm/templates/directory/user_type_filter.html:8 msgid "BookWyrm users" @@ -888,6 +887,26 @@ msgstr "Bookwyrmnutzer*innen" msgid "All known users" msgstr "Alle bekannten Nutzer*innen" +#: bookwyrm/templates/discover/card-header.html:9 +#, python-format +msgid "%(username)s rated %(book_title)s" +msgstr "%(username)s hat %(book_title)s bewertet" + +#: bookwyrm/templates/discover/card-header.html:13 +#, python-format +msgid "%(username)s reviewed %(book_title)s" +msgstr "%(username)s hat %(book_title)s besprochen" + +#: bookwyrm/templates/discover/card-header.html:17 +#, python-format +msgid "%(username)s commented on %(book_title)s" +msgstr "%(username)s hat %(book_title)s kommentiert" + +#: bookwyrm/templates/discover/card-header.html:21 +#, python-format +msgid "%(username)s quoted %(book_title)s" +msgstr "%(username)s hat %(book_title)s zitiert" + #: bookwyrm/templates/discover/discover.html:4 #: bookwyrm/templates/discover/discover.html:10 #: bookwyrm/templates/layout.html:78 @@ -899,28 +918,8 @@ msgstr "Entdecken" msgid "See what's new in the local %(site_name)s community" msgstr "Schau, was in der %(site_name)s Community neu ist" -#: bookwyrm/templates/discover/large-book.html:46 -#: bookwyrm/templates/discover/small-book.html:32 -msgid "rated" -msgstr "bewertet" - -#: bookwyrm/templates/discover/large-book.html:48 -#: bookwyrm/templates/discover/small-book.html:34 -msgid "reviewed" -msgstr "bewertete" - -#: bookwyrm/templates/discover/large-book.html:50 -#: bookwyrm/templates/discover/small-book.html:36 -msgid "commented on" -msgstr "kommentierte" - #: bookwyrm/templates/discover/large-book.html:52 -#: bookwyrm/templates/discover/small-book.html:38 -msgid "quoted" -msgstr "zitierte" - -#: bookwyrm/templates/discover/large-book.html:68 -#: bookwyrm/templates/discover/small-book.html:52 +#: bookwyrm/templates/discover/small-book.html:36 msgid "View status" msgstr "Status ansehen" @@ -932,7 +931,7 @@ msgstr "Als letzten Schritt bevor du %(site_name)s beitrittst - bestätige bitte #: bookwyrm/templates/email/confirm/html_content.html:11 msgid "Confirm Email" -msgstr "E-Mail bestätigen" +msgstr "E-Mail-Adresse bestätigen" #: bookwyrm/templates/email/confirm/html_content.html:15 #, python-format @@ -966,7 +965,7 @@ msgstr "E-Mail Einstellungen" #: bookwyrm/templates/email/invite/subject.html:2 #, python-format msgid "You're invited to join %(site_name)s!" -msgstr "Du bist eingeladen %(site_name)s beizutreten!" +msgstr "Du bist eingeladen, %(site_name)s beizutreten!" #: bookwyrm/templates/email/invite/html_content.html:9 msgid "Join Now" @@ -974,8 +973,8 @@ msgstr "Tritt jetzt bei" #: bookwyrm/templates/email/invite/html_content.html:15 #, python-format -msgid "Learn more about this instance." -msgstr "Erfahre mehr über diese Instanz." +msgid "Learn more about %(site_name)s." +msgstr "Erfahre mehr über %(site_name)s." #: bookwyrm/templates/email/invite/text_content.html:4 #, python-format @@ -983,8 +982,9 @@ msgid "You're invited to join %(site_name)s! Click the link below to create an a msgstr "Du bist eingeladen, %(site_name)s beizutreten! Klicke auf den Link unten um einen Account zu erstellen." #: bookwyrm/templates/email/invite/text_content.html:8 -msgid "Learn more about this instance:" -msgstr "Lerne mehr über diese Instanz:" +#, python-format +msgid "Learn more about %(site_name)s:" +msgstr "Erfahre mehr über %(site_name)s:" #: bookwyrm/templates/email/password_reset/html_content.html:6 #: bookwyrm/templates/email/password_reset/text_content.html:4 @@ -1070,7 +1070,7 @@ msgstr "Zu lesen" #: bookwyrm/templates/feed/layout.html:26 #: bookwyrm/templates/shelf/shelf.html:40 msgid "Currently Reading" -msgstr "Gegenwärtiger Lesestoff" +msgstr "Lese ich gerade" #: bookwyrm/templates/feed/layout.html:27 #: bookwyrm/templates/shelf/shelf.html:42 @@ -1134,7 +1134,7 @@ msgstr "Empfohlene Bücher" #: bookwyrm/templates/get_started/books.html:46 #, python-format msgid "Popular on %(site_name)s" -msgstr "Beliebt auf %(site_name)s" +msgstr "Auf %(site_name)s beliebt" #: bookwyrm/templates/get_started/books.html:58 #: bookwyrm/templates/lists/list.html:154 @@ -1179,7 +1179,7 @@ msgstr "Schritt überspringen" #: bookwyrm/templates/get_started/layout.html:49 msgid "Finish" -msgstr "Fertig stellen" +msgstr "Fertigstellen" #: bookwyrm/templates/get_started/profile.html:15 #: bookwyrm/templates/preferences/edit_user.html:42 @@ -1216,7 +1216,7 @@ msgstr "Dein Account wird im Verzeichnis gezeigt und möglicherweise anderen Use #: bookwyrm/templates/get_started/users.html:11 msgid "Search for a user" -msgstr "Nach Benutzer*in suchen" +msgstr "Benutzer*in suchen" #: bookwyrm/templates/get_started/users.html:13 #, python-format @@ -1265,7 +1265,7 @@ msgstr "Importstatus" #: bookwyrm/templates/import/import_status.html:11 msgid "Back to imports" -msgstr "" +msgstr "Zurück zu Importen" #: bookwyrm/templates/import/import_status.html:15 msgid "Import started:" @@ -1299,7 +1299,7 @@ msgstr "Zum Ende der Liste springen, um die %(failed_count)s Einträge, deren Im #: bookwyrm/templates/import/import_status.html:62 #, python-format msgid "Line %(index)s: %(title)s by %(author)s" -msgstr "" +msgstr "Zeile %(index)s: %(title)s von %(author)s" #: bookwyrm/templates/import/import_status.html:82 msgid "Select all" @@ -1315,7 +1315,7 @@ msgstr "Erfolgreich importiert" #: bookwyrm/templates/import/import_status.html:114 msgid "Import Progress" -msgstr "" +msgstr "Import-Fortschritt" #: bookwyrm/templates/import/import_status.html:119 msgid "Book" @@ -1323,13 +1323,13 @@ msgstr "Buch" #: bookwyrm/templates/import/import_status.html:122 #: bookwyrm/templates/shelf/shelf.html:128 -#: bookwyrm/templates/shelf/shelf.html:148 +#: bookwyrm/templates/shelf/shelf.html:150 msgid "Title" msgstr "Titel" #: bookwyrm/templates/import/import_status.html:125 #: bookwyrm/templates/shelf/shelf.html:129 -#: bookwyrm/templates/shelf/shelf.html:151 +#: bookwyrm/templates/shelf/shelf.html:153 msgid "Author" msgstr "Autor*in" @@ -1338,8 +1338,8 @@ msgid "Imported" msgstr "Importiert" #: bookwyrm/templates/import/tooltip.html:6 -msgid "You can download your GoodReads data from the Import/Export page of your GoodReads account." -msgstr "Du kannst dir deine GoodReads Daten von Import/Export page in deinem GoodReads Account runterladen." +msgid "You can download your Goodreads data from the Import/Export page of your Goodreads account." +msgstr "Du kannst deine Goodreads-Daten von der Import/Export-Seite deines Goodreads-Kontos downloaden." #: bookwyrm/templates/invite.html:4 bookwyrm/templates/invite.html:8 #: bookwyrm/templates/login.html:49 @@ -1354,7 +1354,7 @@ msgstr "Zugiff verweigert" msgid "Sorry! This invite code is no longer valid." msgstr "Sorry! Dieser Einladecode ist mehr gültig." -#: bookwyrm/templates/landing/about.html:7 +#: bookwyrm/templates/landing/about.html:7 bookwyrm/templates/layout.html:230 #, python-format msgid "About %(site_name)s" msgstr "Über %(site_name)s" @@ -1383,7 +1383,7 @@ msgstr "Freundlich" #: bookwyrm/templates/landing/layout.html:29 msgid "Anti-Corporate" -msgstr "Unkommerziell" +msgstr "Nichtkommerziell" #: bookwyrm/templates/landing/layout.html:45 #, python-format @@ -1397,7 +1397,7 @@ msgstr "Einladung beantragen" #: bookwyrm/templates/landing/layout.html:49 #, python-format msgid "%(name)s registration is closed" -msgstr "%(name)s Registrierung ist geschlossen" +msgstr "%(name)s erlaubt keine Selbtregistrierung" #: bookwyrm/templates/landing/layout.html:60 msgid "Thank you! Your request has been received." @@ -1410,11 +1410,11 @@ msgstr "Dein Account" #: bookwyrm/templates/layout.html:13 #, python-format msgid "%(site_name)s search" -msgstr "%(site_name)s Suche" +msgstr "%(site_name)s-Suche" #: bookwyrm/templates/layout.html:43 msgid "Search for a book, user, or list" -msgstr "Nach einem Buch, einem Benutzer oder einer Liste suchen" +msgstr "Nach einem Buch, einem*r Benutzer*in oder einer Liste suchen" #: bookwyrm/templates/layout.html:61 bookwyrm/templates/layout.html:62 msgid "Main navigation menu" @@ -1442,7 +1442,7 @@ msgstr "Einladungen" #: bookwyrm/templates/layout.html:132 msgid "Admin" -msgstr "Administrator" +msgstr "Administration" #: bookwyrm/templates/layout.html:139 msgid "Log out" @@ -1458,7 +1458,7 @@ msgstr "Benachrichtigungen" #: bookwyrm/templates/login.html:21 #: bookwyrm/templates/snippets/register_form.html:4 msgid "Username:" -msgstr "Benutzername:" +msgstr "Benutzer*inname:" #: bookwyrm/templates/layout.html:175 msgid "password" @@ -1479,16 +1479,12 @@ msgstr "Beitreten" #: bookwyrm/templates/layout.html:221 msgid "Successfully posted status" -msgstr "Status erfolgreich veröffentlicht" +msgstr "Status veröffentlicht" #: bookwyrm/templates/layout.html:222 msgid "Error posting status" msgstr "Fehler beim veröffentlichen des Status" -#: bookwyrm/templates/layout.html:230 -msgid "About this instance" -msgstr "Über diese Instanz" - #: bookwyrm/templates/layout.html:234 msgid "Contact site admin" msgstr "Admin kontaktieren" @@ -1709,7 +1705,7 @@ msgstr "%(book_title)s zu deiner Liste \" #: bookwyrm/templates/notifications/items/add.html:31 #, python-format msgid "suggested adding %(book_title)s to your list \"%(list_name)s\"" -msgstr "%(book_title)s zu deiner Liste \"%(list_name)s\" vorgeschlagen" +msgstr "hat vorgeschlagen, %(book_title)s zu deiner Liste „%(list_name)s“ hinzuzufügen" #: bookwyrm/templates/notifications/items/boost.html:19 #, python-format @@ -1733,22 +1729,22 @@ msgstr "hat deinen Status geteilt" #: bookwyrm/templates/notifications/items/fav.html:19 #, python-format -msgid "favorited your review of %(book_title)s" -msgstr "hat deine Bewertung von %(book_title)s favorisiert" +msgid "liked your review of %(book_title)s" +msgstr "hat deine Besprechung von %(book_title)s favorisiert" #: bookwyrm/templates/notifications/items/fav.html:25 #, python-format -msgid "favorited your comment on%(book_title)s" -msgstr "favorisierte deinen Kommentar zu%(book_title)s" +msgid "liked your comment on%(book_title)s" +msgstr "hat deinen Kommentar zu %(book_title)s favorisiert" #: bookwyrm/templates/notifications/items/fav.html:31 #, python-format -msgid "favorited your quote from %(book_title)s" -msgstr " hat dein Zitat aus %(book_title)s favorisiert" +msgid "liked your quote from %(book_title)s" +msgstr "hat dein Zitat aus %(book_title)s favorisiert" #: bookwyrm/templates/notifications/items/fav.html:37 #, python-format -msgid "favorited your status" +msgid "liked your status" msgstr "hat deinen Status favorisiert" #: bookwyrm/templates/notifications/items/follow.html:15 @@ -1815,7 +1811,7 @@ msgstr "Benachrichtigungen löschen" #: bookwyrm/templates/notifications/notifications_page.html:29 msgid "All" -msgstr "Alle" +msgstr "Alle(s)" #: bookwyrm/templates/notifications/notifications_page.html:33 msgid "Mentions" @@ -1866,7 +1862,7 @@ msgstr "Neues Passwort:" #: bookwyrm/templates/preferences/layout.html:24 #: bookwyrm/templates/settings/users/delete_user_form.html:23 msgid "Delete Account" -msgstr "Account löschen" +msgstr "Benutzer*inkonto löschen" #: bookwyrm/templates/preferences/delete_user.html:12 msgid "Permanently delete account" @@ -1900,7 +1896,7 @@ msgstr "Privatsphäre" #: bookwyrm/templates/preferences/edit_user.html:72 msgid "Show reading goal prompt in feed:" -msgstr "Zeige Lesezielabfrage im Feed:" +msgstr "Zeige Leseziel-Abfrage im Feed:" #: bookwyrm/templates/preferences/edit_user.html:76 msgid "Show suggested users:" @@ -1921,7 +1917,7 @@ msgstr "Voreinstellung für Beitragssichtbarkeit:" #: bookwyrm/templates/preferences/layout.html:11 msgid "Account" -msgstr "Account" +msgstr "Benutzer*inkonto" #: bookwyrm/templates/preferences/layout.html:27 msgid "Relationships" @@ -1940,7 +1936,7 @@ msgstr "\"%(book_title)s\" beginnen" #: bookwyrm/templates/reading_progress/want.html:5 #, python-format msgid "Want to Read \"%(book_title)s\"" -msgstr "" +msgstr "\"%(book_title)s\" auf Leseliste setzen" #: bookwyrm/templates/search/book.html:47 #: bookwyrm/templates/settings/reports/reports.html:25 @@ -1954,7 +1950,7 @@ msgstr "Buch importieren" #: bookwyrm/templates/search/book.html:107 msgid "Load results from other catalogues" -msgstr "" +msgstr "Ergebnisse aus anderen Katalogen laden" #: bookwyrm/templates/search/book.html:111 msgid "Manually add book" @@ -1970,7 +1966,7 @@ msgstr "Suchanfrage" #: bookwyrm/templates/search/layout.html:19 msgid "Search type" -msgstr "" +msgstr "Suchart" #: bookwyrm/templates/search/layout.html:23 #: bookwyrm/templates/search/layout.html:46 @@ -1985,7 +1981,7 @@ msgstr "Benutzer*innen" #: bookwyrm/templates/search/layout.html:58 #, python-format msgid "No results found for \"%(query)s\"" -msgstr "Für \"%(query)s\" wurden keine Ergebnisse gefunden" +msgstr "Keine Ergebnisse für „%(query)s“ gefunden" #: bookwyrm/templates/settings/announcements/announcement.html:3 #: bookwyrm/templates/settings/announcements/announcement.html:6 @@ -2018,18 +2014,18 @@ msgstr "Nein" #: bookwyrm/templates/settings/announcements/announcement_form.html:40 #: bookwyrm/templates/settings/dashboard/dashboard.html:71 msgid "Start date:" -msgstr "" +msgstr "Startdatum:" #: bookwyrm/templates/settings/announcements/announcement.html:54 #: bookwyrm/templates/settings/announcements/announcement_form.html:49 #: bookwyrm/templates/settings/dashboard/dashboard.html:77 msgid "End date:" -msgstr "" +msgstr "Enddatum:" #: bookwyrm/templates/settings/announcements/announcement.html:60 #: bookwyrm/templates/settings/announcements/announcement_form.html:58 msgid "Active:" -msgstr "" +msgstr "Aktiv:" #: bookwyrm/templates/settings/announcements/announcement_form.html:8 #: bookwyrm/templates/settings/announcements/announcements.html:8 @@ -2046,7 +2042,7 @@ msgstr "Inhalt:" #: bookwyrm/templates/settings/announcements/announcement_form.html:30 msgid "Event date:" -msgstr "" +msgstr "Ereignisdatum:" #: bookwyrm/templates/settings/announcements/announcements.html:3 #: bookwyrm/templates/settings/announcements/announcements.html:5 @@ -2065,11 +2061,11 @@ msgstr "Vorschau" #: bookwyrm/templates/settings/announcements/announcements.html:30 msgid "Start date" -msgstr "" +msgstr "Startdatum" #: bookwyrm/templates/settings/announcements/announcements.html:34 msgid "End date" -msgstr "" +msgstr "Enddatum" #: bookwyrm/templates/settings/announcements/announcements.html:38 #: bookwyrm/templates/settings/federation/instance_list.html:46 @@ -2082,11 +2078,11 @@ msgstr "Status" #: bookwyrm/templates/settings/announcements/announcements.html:48 msgid "active" -msgstr "" +msgstr "aktiv" #: bookwyrm/templates/settings/announcements/announcements.html:48 msgid "inactive" -msgstr "" +msgstr "inaktiv" #: bookwyrm/templates/settings/announcements/announcements.html:52 msgid "No announcements found" @@ -2101,7 +2097,7 @@ msgstr "Übersicht" #: bookwyrm/templates/settings/dashboard/dashboard.html:15 #: bookwyrm/templates/settings/dashboard/dashboard.html:100 msgid "Total users" -msgstr "" +msgstr "Benutzer*innen insgesamt" #: bookwyrm/templates/settings/dashboard/dashboard.html:21 #: bookwyrm/templates/settings/dashboard/user_chart.html:16 @@ -2110,12 +2106,12 @@ msgstr "Diesen Monat aktiv" #: bookwyrm/templates/settings/dashboard/dashboard.html:27 msgid "Statuses" -msgstr "Statusse" +msgstr "Statusmeldungen" #: bookwyrm/templates/settings/dashboard/dashboard.html:33 #: bookwyrm/templates/settings/dashboard/works_chart.html:11 msgid "Works" -msgstr "" +msgstr "Werke" #: bookwyrm/templates/settings/dashboard/dashboard.html:43 #, python-format @@ -2149,11 +2145,11 @@ msgstr "Wochen" #: bookwyrm/templates/settings/dashboard/dashboard.html:106 msgid "User signup activity" -msgstr "" +msgstr "Neuanmeldungen" #: bookwyrm/templates/settings/dashboard/dashboard.html:112 msgid "Status activity" -msgstr "" +msgstr "Statusaktivitäten" #: bookwyrm/templates/settings/dashboard/dashboard.html:118 msgid "Works created" @@ -2161,15 +2157,15 @@ msgstr "Erstellte Werke" #: bookwyrm/templates/settings/dashboard/registration_chart.html:10 msgid "Registrations" -msgstr "Anmeldungen" +msgstr "Registrierungen" #: bookwyrm/templates/settings/dashboard/status_chart.html:11 msgid "Statuses posted" -msgstr "Statusse veröffentlicht" +msgstr "Statusmeldungen veröffentlicht" #: bookwyrm/templates/settings/dashboard/user_chart.html:11 msgid "Total" -msgstr "" +msgstr "Gesamt" #: bookwyrm/templates/settings/email_blocklist/domain_form.html:5 #: bookwyrm/templates/settings/email_blocklist/email_blocklist.html:10 @@ -2178,13 +2174,13 @@ msgstr "Domain hinzufügen" #: bookwyrm/templates/settings/email_blocklist/domain_form.html:11 msgid "Domain:" -msgstr "" +msgstr "Domain:" #: bookwyrm/templates/settings/email_blocklist/email_blocklist.html:5 #: bookwyrm/templates/settings/email_blocklist/email_blocklist.html:7 #: bookwyrm/templates/settings/layout.html:59 msgid "Email Blocklist" -msgstr "E-Mail-Blockliste" +msgstr "E-Mail-Sperrliste" #: bookwyrm/templates/settings/email_blocklist/email_blocklist.html:18 msgid "When someone tries to register with an email from this domain, no account will be created. The registration process will appear to have worked." @@ -2192,12 +2188,12 @@ msgstr "Wenn sich jemand mit einer E-Mail-Adresse von dieser Domain zu registrie #: bookwyrm/templates/settings/email_blocklist/email_blocklist.html:25 msgid "Domain" -msgstr "" +msgstr "Domain" #: bookwyrm/templates/settings/email_blocklist/email_blocklist.html:29 #: bookwyrm/templates/settings/ip_blocklist/ip_blocklist.html:27 msgid "Options" -msgstr "" +msgstr "Optionen" #: bookwyrm/templates/settings/email_blocklist/email_blocklist.html:38 #, python-format @@ -2208,7 +2204,7 @@ msgstr[1] "%(display_count)s Benutzer*innen" #: bookwyrm/templates/settings/email_blocklist/email_blocklist.html:59 msgid "No email domains currently blocked" -msgstr "" +msgstr "Derzeit sind keine E-Mail-Domains gesperrt" #: bookwyrm/templates/settings/federation/edit_instance.html:3 #: bookwyrm/templates/settings/federation/edit_instance.html:6 @@ -2223,12 +2219,12 @@ msgstr "Instanz hinzufügen" #: bookwyrm/templates/settings/federation/edit_instance.html:7 #: bookwyrm/templates/settings/federation/instance_blocklist.html:7 msgid "Back to instance list" -msgstr "Zurück zur Instanzliste" +msgstr "Zurück zur Instanzenliste" #: bookwyrm/templates/settings/federation/edit_instance.html:16 #: bookwyrm/templates/settings/federation/instance_blocklist.html:16 msgid "Import block list" -msgstr "Blockliste importieren" +msgstr "Sperrliste importieren" #: bookwyrm/templates/settings/federation/edit_instance.html:30 msgid "Instance:" @@ -2254,7 +2250,7 @@ msgstr "Version:" #: bookwyrm/templates/settings/federation/edit_instance.html:70 msgid "Notes:" -msgstr "Hinweise:" +msgstr "Anmerkungen:" #: bookwyrm/templates/settings/federation/instance.html:19 msgid "Details" @@ -2267,7 +2263,7 @@ msgstr "Aktivität" #: bookwyrm/templates/settings/federation/instance.html:38 msgid "Users:" -msgstr "Benutzer:" +msgstr "Benutzer*innen:" #: bookwyrm/templates/settings/federation/instance.html:41 #: bookwyrm/templates/settings/federation/instance.html:47 @@ -2281,55 +2277,56 @@ msgstr "Meldungen:" #: bookwyrm/templates/settings/federation/instance.html:50 msgid "Followed by us:" -msgstr "" +msgstr "Folgen wir:" #: bookwyrm/templates/settings/federation/instance.html:55 msgid "Followed by them:" -msgstr "" +msgstr "Folgen:" #: bookwyrm/templates/settings/federation/instance.html:60 msgid "Blocked by us:" -msgstr "" +msgstr "Von uns gesperrt:" #: bookwyrm/templates/settings/federation/instance.html:72 #: bookwyrm/templates/settings/users/user_info.html:110 msgid "Notes" -msgstr "Hinweise" +msgstr "Anmerkungen" #: bookwyrm/templates/settings/federation/instance.html:75 +#: bookwyrm/templates/snippets/status/status_options.html:24 msgid "Edit" msgstr "Ändern" #: bookwyrm/templates/settings/federation/instance.html:79 msgid "No notes" -msgstr "" +msgstr "Keine Anmerkungen" #: bookwyrm/templates/settings/federation/instance.html:94 #: bookwyrm/templates/settings/users/user_moderation_actions.html:8 msgid "Actions" -msgstr "" +msgstr "Aktionen" #: bookwyrm/templates/settings/federation/instance.html:98 #: bookwyrm/templates/snippets/block_button.html:5 msgid "Block" -msgstr "Blockieren" +msgstr "Sperren" #: bookwyrm/templates/settings/federation/instance.html:99 msgid "All users from this instance will be deactivated." -msgstr "Alle Benutzer dieser Instanz werden deaktiviert." +msgstr "Alle Benutzer*innen dieser Instanz werden deaktiviert." #: bookwyrm/templates/settings/federation/instance.html:104 #: bookwyrm/templates/snippets/block_button.html:10 msgid "Un-block" -msgstr "Entblocken" +msgstr "Entsperren" #: bookwyrm/templates/settings/federation/instance.html:105 msgid "All users from this instance will be re-activated." -msgstr "Alle Benutzer dieser Instanz werden wieder aktiviert." +msgstr "Alle Benutzer*innen dieser Instanz werden wieder aktiviert." #: bookwyrm/templates/settings/federation/instance_blocklist.html:6 msgid "Import Blocklist" -msgstr "Blockliste importieren" +msgstr "Sperrliste importieren" #: bookwyrm/templates/settings/federation/instance_blocklist.html:26 #: bookwyrm/templates/snippets/goal_progress.html:7 @@ -2338,7 +2335,7 @@ msgstr "Erfolg!" #: bookwyrm/templates/settings/federation/instance_blocklist.html:30 msgid "Successfully blocked:" -msgstr "Erfolgreich blockiert:" +msgstr "Erfolgreich gesperrt:" #: bookwyrm/templates/settings/federation/instance_blocklist.html:32 msgid "Failed:" @@ -2380,7 +2377,7 @@ msgstr "Datum der Anfrage" #: bookwyrm/templates/settings/invites/manage_invite_requests.html:39 msgid "Date accepted" -msgstr "Datum der Annahme" +msgstr "Datum der Bestätigung" #: bookwyrm/templates/settings/invites/manage_invite_requests.html:42 msgid "Email" @@ -2397,7 +2394,7 @@ msgstr "Keine Anfragen" #: bookwyrm/templates/settings/invites/manage_invite_requests.html:59 #: bookwyrm/templates/settings/invites/status_filter.html:16 msgid "Accepted" -msgstr "Akzeptiert" +msgstr "Bestätigt" #: bookwyrm/templates/settings/invites/manage_invite_requests.html:61 #: bookwyrm/templates/settings/invites/status_filter.html:12 @@ -2427,7 +2424,7 @@ msgstr "Un-ignorieren" #: bookwyrm/templates/settings/invites/manage_invite_requests.html:108 msgid "Back to pending requests" -msgstr "Keine ausstehenden Anfragen" +msgstr "Zurück zu ausstehenden Anfragen" #: bookwyrm/templates/settings/invites/manage_invite_requests.html:110 msgid "View ignored requests" @@ -2476,7 +2473,7 @@ msgstr "IP-Adresse hinzufügen" #: bookwyrm/templates/settings/ip_blocklist/ip_address_form.html:11 msgid "Use IP address blocks with caution, and consider using blocks only temporarily, as IP addresses are often shared or change hands. If you block your own IP, you will not be able to access this page." -msgstr "" +msgstr "Lass bei der Sperrung von IP-Adressen Vorsicht walten. Erwäge, IP-Adressen nur vorübergehend zu sperren, da sie oft geteilt werden oder neu zugeordnet werden. Wenn du deine eigene IP blockierst, kannst du nicht mehr auf diese Seite zugreifen." #: bookwyrm/templates/settings/ip_blocklist/ip_address_form.html:18 msgid "IP Address:" @@ -2486,27 +2483,27 @@ msgstr "IP-Adresse:" #: bookwyrm/templates/settings/ip_blocklist/ip_blocklist.html:7 #: bookwyrm/templates/settings/layout.html:63 msgid "IP Address Blocklist" -msgstr "" +msgstr "IP-Addressen-Sperrliste" #: bookwyrm/templates/settings/ip_blocklist/ip_blocklist.html:18 msgid "Any traffic from this IP address will get a 404 response when trying to access any part of the application." -msgstr "" +msgstr "Jeder Datenverkehr von dieser IP-Adresse erhält eine 404-Antwort, wenn versucht wird, auf einen beliebigen Teil der Anwendung zuzugreifen." #: bookwyrm/templates/settings/ip_blocklist/ip_blocklist.html:24 msgid "Address" -msgstr "" +msgstr "Adresse" #: bookwyrm/templates/settings/ip_blocklist/ip_blocklist.html:46 msgid "No IP addresses currently blocked" -msgstr "" +msgstr "Derzeit sind keine IP-Adressen gesperrt" #: bookwyrm/templates/settings/ip_blocklist/ip_tooltip.html:6 msgid "You can block IP ranges using CIDR syntax." -msgstr "" +msgstr "Du kannst IP-Bereiche mittels CIDR-Syntax blockieren." #: bookwyrm/templates/settings/layout.html:4 msgid "Administration" -msgstr "" +msgstr "Administration" #: bookwyrm/templates/settings/layout.html:29 msgid "Manage Users" @@ -2514,7 +2511,7 @@ msgstr "Nutzer*innen verwalten" #: bookwyrm/templates/settings/layout.html:51 msgid "Moderation" -msgstr "" +msgstr "Moderation" #: bookwyrm/templates/settings/layout.html:55 #: bookwyrm/templates/settings/reports/reports.html:8 @@ -2554,7 +2551,7 @@ msgstr "Kommentieren" #: bookwyrm/templates/settings/reports/report.html:46 msgid "Reported statuses" -msgstr "" +msgstr "Gemeldete Statusmeldungen" #: bookwyrm/templates/settings/reports/report.html:48 msgid "No statuses reported" @@ -2562,7 +2559,7 @@ msgstr "Keine Beiträge gemeldet" #: bookwyrm/templates/settings/reports/report.html:54 msgid "Status has been deleted" -msgstr "" +msgstr "Statusmeldung gelöscht" #: bookwyrm/templates/settings/reports/report_preview.html:13 msgid "No notes provided" @@ -2593,11 +2590,11 @@ msgstr "Meldungen: %(instance_name)s" #: bookwyrm/templates/settings/reports/reports.html:28 msgid "Resolved" -msgstr "" +msgstr "Behoben" #: bookwyrm/templates/settings/reports/reports.html:37 msgid "No reports found." -msgstr "" +msgstr "Keine Meldungen gefunden." #: bookwyrm/templates/settings/site.html:10 #: bookwyrm/templates/settings/site.html:21 @@ -2625,7 +2622,7 @@ msgstr "Instanzname" #: bookwyrm/templates/settings/site.html:28 msgid "Tagline:" -msgstr "" +msgstr "Motto:" #: bookwyrm/templates/settings/site.html:32 msgid "Instance description:" @@ -2633,15 +2630,15 @@ msgstr "Instanzbeschreibung" #: bookwyrm/templates/settings/site.html:36 msgid "Short description:" -msgstr "" +msgstr "Kurzbeschreibung:" #: bookwyrm/templates/settings/site.html:37 -msgid "Used when the instance is previewed on joinbookwyrm.com. Does not support html or markdown." -msgstr "" +msgid "Used when the instance is previewed on joinbookwyrm.com. Does not support HTML or Markdown." +msgstr "Wird verwendet, wenn die Instanz auf joinbookwyrm.com in der Vorschau angezeigt wird. Unterstützt weder HTML noch Markdown." #: bookwyrm/templates/settings/site.html:41 msgid "Code of conduct:" -msgstr "" +msgstr "Verhaltenskodex:" #: bookwyrm/templates/settings/site.html:45 msgid "Privacy Policy:" @@ -2669,7 +2666,7 @@ msgstr "Unterstützungstitel" #: bookwyrm/templates/settings/site.html:85 msgid "Admin email:" -msgstr "" +msgstr "E-Mail-Adresse des*r Administrator*in:" #: bookwyrm/templates/settings/site.html:89 msgid "Additional info:" @@ -2677,11 +2674,11 @@ msgstr "Zusätzliche Info:" #: bookwyrm/templates/settings/site.html:103 msgid "Allow registration" -msgstr "" +msgstr "Selbstregistrierung zulassen" #: bookwyrm/templates/settings/site.html:109 msgid "Allow invite requests" -msgstr "" +msgstr "Einladungsanfragen zulassen" #: bookwyrm/templates/settings/site.html:115 msgid "Require users to confirm email address" @@ -2697,7 +2694,7 @@ msgstr "Registrierungen geschlossen text" #: bookwyrm/templates/settings/site.html:124 msgid "Invite request text:" -msgstr "" +msgstr "Hinweis für Einladungsanfragen:" #: bookwyrm/templates/settings/users/delete_user_form.html:5 #: bookwyrm/templates/settings/users/user_moderation_actions.html:31 @@ -2715,21 +2712,21 @@ msgstr "Dein Passwort:" #: bookwyrm/templates/settings/users/user.html:7 msgid "Back to users" -msgstr "" +msgstr "Zurück zu Benutzer*innen" #: bookwyrm/templates/settings/users/user_admin.html:7 #, python-format msgid "Users: %(instance_name)s" -msgstr "" +msgstr "Benutzer*innen: %(instance_name)s" #: bookwyrm/templates/settings/users/user_admin.html:22 #: bookwyrm/templates/settings/users/username_filter.html:5 msgid "Username" -msgstr "Benutzername" +msgstr "Benutzer*inname" #: bookwyrm/templates/settings/users/user_admin.html:26 msgid "Date Added" -msgstr "" +msgstr "Hinzugefügt am" #: bookwyrm/templates/settings/users/user_admin.html:30 msgid "Last Active" @@ -2737,12 +2734,12 @@ msgstr "Zuletzt aktiv" #: bookwyrm/templates/settings/users/user_admin.html:38 msgid "Remote instance" -msgstr "" +msgstr "Entfernte Instanz" #: bookwyrm/templates/settings/users/user_admin.html:47 #: bookwyrm/templates/settings/users/user_info.html:24 msgid "Active" -msgstr "" +msgstr "Aktiv" #: bookwyrm/templates/settings/users/user_admin.html:47 #: bookwyrm/templates/settings/users/user_info.html:28 @@ -2756,7 +2753,7 @@ msgstr "Nicht gesetzt" #: bookwyrm/templates/settings/users/user_info.html:16 msgid "View user profile" -msgstr "" +msgstr "Benutzer*inprofil anzeigen" #: bookwyrm/templates/settings/users/user_info.html:36 msgid "Local" @@ -2764,11 +2761,11 @@ msgstr "Lokal" #: bookwyrm/templates/settings/users/user_info.html:38 msgid "Remote" -msgstr "" +msgstr "Entfernt" #: bookwyrm/templates/settings/users/user_info.html:47 msgid "User details" -msgstr "Benutzerdetails" +msgstr "Benutzer*indetails" #: bookwyrm/templates/settings/users/user_info.html:51 msgid "Email:" @@ -2776,19 +2773,19 @@ msgstr "E-Mail:" #: bookwyrm/templates/settings/users/user_info.html:61 msgid "(View reports)" -msgstr "" +msgstr "(Meldungen anzeigen)" #: bookwyrm/templates/settings/users/user_info.html:67 msgid "Blocked by count:" -msgstr "" +msgstr "Gesperrt durch (Anzahl):" #: bookwyrm/templates/settings/users/user_info.html:70 msgid "Last active date:" -msgstr "" +msgstr "Zuletzt aktiv:" #: bookwyrm/templates/settings/users/user_info.html:73 msgid "Manually approved followers:" -msgstr "" +msgstr "Manuell zugelassene Follower*innen:" #: bookwyrm/templates/settings/users/user_info.html:76 msgid "Discoverable:" @@ -2796,11 +2793,11 @@ msgstr "Entdeckbar:" #: bookwyrm/templates/settings/users/user_info.html:80 msgid "Deactivation reason:" -msgstr "" +msgstr "Grund der Deaktivierung:" #: bookwyrm/templates/settings/users/user_info.html:95 msgid "Instance details" -msgstr "" +msgstr "Instanzdetails" #: bookwyrm/templates/settings/users/user_info.html:117 msgid "View instance" @@ -2811,22 +2808,22 @@ msgid "Permanently deleted" msgstr "Permanent gelöscht" #: bookwyrm/templates/settings/users/user_moderation_actions.html:13 -#: bookwyrm/templates/snippets/status/status_options.html:35 +#: bookwyrm/templates/snippets/status/status_options.html:32 #: bookwyrm/templates/snippets/user_options.html:13 msgid "Send direct message" msgstr "Direktnachricht senden" #: bookwyrm/templates/settings/users/user_moderation_actions.html:20 msgid "Suspend user" -msgstr "" +msgstr "Benutzer*in vorläufig sperren" #: bookwyrm/templates/settings/users/user_moderation_actions.html:25 msgid "Un-suspend user" -msgstr "" +msgstr "Vorläufige Sperre für Benutzer*in aufheben" #: bookwyrm/templates/settings/users/user_moderation_actions.html:47 msgid "Access level:" -msgstr "" +msgstr "Zugriffsstufe:" #: bookwyrm/templates/shelf/create_shelf_form.html:5 msgid "Create Shelf" @@ -2864,36 +2861,36 @@ msgstr "Regal bearbeiten" msgid "Delete shelf" msgstr "Regal löschen" -#: bookwyrm/templates/shelf/shelf.html:130 -#: bookwyrm/templates/shelf/shelf.html:154 +#: bookwyrm/templates/shelf/shelf.html:132 +#: bookwyrm/templates/shelf/shelf.html:158 msgid "Shelved" msgstr "Ins Regal gestellt" -#: bookwyrm/templates/shelf/shelf.html:131 -#: bookwyrm/templates/shelf/shelf.html:158 +#: bookwyrm/templates/shelf/shelf.html:133 +#: bookwyrm/templates/shelf/shelf.html:161 msgid "Started" msgstr "Gestartet" -#: bookwyrm/templates/shelf/shelf.html:132 -#: bookwyrm/templates/shelf/shelf.html:161 +#: bookwyrm/templates/shelf/shelf.html:134 +#: bookwyrm/templates/shelf/shelf.html:164 msgid "Finished" msgstr "Abgeschlossen" -#: bookwyrm/templates/shelf/shelf.html:187 +#: bookwyrm/templates/shelf/shelf.html:190 msgid "This shelf is empty." msgstr "Dieses Regal ist leer." #: bookwyrm/templates/snippets/announcement.html:31 #, python-format msgid "Posted by %(username)s" -msgstr "" +msgstr "Von %(username)s veröffentlicht" #: bookwyrm/templates/snippets/authors.html:22 #, python-format msgid "and %(remainder_count_display)s other" msgid_plural "and %(remainder_count_display)s others" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "und %(remainder_count_display)s Andere*r" +msgstr[1] "und %(remainder_count_display)s Andere" #: bookwyrm/templates/snippets/book_cover.html:61 msgid "No cover" @@ -2902,17 +2899,17 @@ msgstr "Kein Titelbild" #: bookwyrm/templates/snippets/book_titleby.html:6 #, python-format msgid "%(title)s by" -msgstr "" +msgstr "%(title)s von" #: bookwyrm/templates/snippets/boost_button.html:20 #: bookwyrm/templates/snippets/boost_button.html:21 msgid "Boost" -msgstr "" +msgstr "Teilen" #: bookwyrm/templates/snippets/boost_button.html:33 #: bookwyrm/templates/snippets/boost_button.html:34 msgid "Un-boost" -msgstr "" +msgstr "Teilen zurücknehmen" #: bookwyrm/templates/snippets/create_status.html:17 msgid "Review" @@ -2926,22 +2923,22 @@ msgstr "Zitieren" msgid "Some thoughts on the book" msgstr "Ein paar Gedanken zum Buch" -#: bookwyrm/templates/snippets/create_status/comment.html:26 +#: bookwyrm/templates/snippets/create_status/comment.html:27 #: bookwyrm/templates/snippets/reading_modals/progress_update_modal.html:15 msgid "Progress:" msgstr "Fortschritt:" -#: bookwyrm/templates/snippets/create_status/comment.html:52 +#: bookwyrm/templates/snippets/create_status/comment.html:53 #: bookwyrm/templates/snippets/progress_field.html:18 msgid "pages" msgstr "Seiten" -#: bookwyrm/templates/snippets/create_status/comment.html:58 +#: bookwyrm/templates/snippets/create_status/comment.html:59 #: bookwyrm/templates/snippets/progress_field.html:23 msgid "percent" msgstr "Prozent" -#: bookwyrm/templates/snippets/create_status/comment.html:65 +#: bookwyrm/templates/snippets/create_status/comment.html:66 #, python-format msgid "of %(pages)s pages" msgstr "von %(pages)s Seiten" @@ -2955,11 +2952,11 @@ msgstr "Antwort" #: bookwyrm/templates/snippets/create_status/content_field.html:17 msgid "Content" -msgstr "" +msgstr "Inhalt" #: bookwyrm/templates/snippets/create_status/content_warning_field.html:10 msgid "Content warning:" -msgstr "" +msgstr "Inhaltswarnung:" #: bookwyrm/templates/snippets/create_status/content_warning_field.html:18 msgid "Spoilers ahead!" @@ -2969,7 +2966,7 @@ msgstr "Spoileralarm!" msgid "Include spoiler alert" msgstr "Spoileralarm aktivieren" -#: bookwyrm/templates/snippets/create_status/layout.html:41 +#: bookwyrm/templates/snippets/create_status/layout.html:48 #: bookwyrm/templates/snippets/reading_modals/form.html:7 msgid "Comment:" msgstr "Kommentar:" @@ -2992,11 +2989,11 @@ msgstr "Zitat:" #: bookwyrm/templates/snippets/create_status/quotation.html:25 #, python-format msgid "An excerpt from '%(book_title)s'" -msgstr "" +msgstr "Ein Auszug aus „%(book_title)s“" #: bookwyrm/templates/snippets/create_status/quotation.html:32 msgid "Position:" -msgstr "" +msgstr "Position:" #: bookwyrm/templates/snippets/create_status/quotation.html:45 msgid "On page:" @@ -3004,16 +3001,16 @@ msgstr "Auf Seite:" #: bookwyrm/templates/snippets/create_status/quotation.html:51 msgid "At percent:" -msgstr "" +msgstr "Bei Prozentsatz:" #: bookwyrm/templates/snippets/create_status/review.html:25 #, python-format msgid "Your review of '%(book_title)s'" -msgstr "Deine Rezension von '%(book_title)s'" +msgstr "Deine Besprechung von „%(book_title)s“" #: bookwyrm/templates/snippets/create_status/review.html:40 msgid "Review:" -msgstr "" +msgstr "Besprechung:" #: bookwyrm/templates/snippets/delete_readthrough_modal.html:4 msgid "Delete these read dates?" @@ -3032,7 +3029,7 @@ msgstr "Favorisieren" #: bookwyrm/templates/snippets/fav_button.html:30 #: bookwyrm/templates/snippets/fav_button.html:31 msgid "Un-like" -msgstr "" +msgstr "Favorisierung zurücknehmen" #: bookwyrm/templates/snippets/filters_panel/filters_panel.html:7 msgid "Show filters" @@ -3048,12 +3045,12 @@ msgstr "Filter anwenden" #: bookwyrm/templates/snippets/filters_panel/filters_panel.html:26 msgid "Clear filters" -msgstr "" +msgstr "Filter zurücksetzen" #: bookwyrm/templates/snippets/follow_button.html:14 #, python-format msgid "Follow @%(username)s" -msgstr "" +msgstr "@%(username)s folgen" #: bookwyrm/templates/snippets/follow_button.html:16 msgid "Follow" @@ -3061,12 +3058,12 @@ msgstr "Folgen" #: bookwyrm/templates/snippets/follow_button.html:25 msgid "Undo follow request" -msgstr "" +msgstr "Folgeanfrage zurücknehmen" #: bookwyrm/templates/snippets/follow_button.html:30 #, python-format msgid "Unfollow @%(username)s" -msgstr "" +msgstr "@%(username)s entfolgen" #: bookwyrm/templates/snippets/follow_button.html:32 msgid "Unfollow" @@ -3085,16 +3082,16 @@ msgstr "Kein Rating" #, python-format msgid "%(half_rating)s star" msgid_plural "%(half_rating)s stars" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "%(half_rating)s Stern" +msgstr[1] "%(half_rating)s Sterne" #: bookwyrm/templates/snippets/form_rate_stars.html:64 #: bookwyrm/templates/snippets/stars.html:7 #, python-format msgid "%(rating)s star" msgid_plural "%(rating)s stars" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "%(rating)s Stern" +msgstr[1] "%(rating)s Sterne" #: bookwyrm/templates/snippets/generated_status/goal.html:2 #, python-format @@ -3107,15 +3104,15 @@ msgstr[1] "Setze das Ziel, %(year)s %(counter)s Bücher zu lesen" #, python-format msgid "rated %(title)s: %(display_rating)s star" msgid_plural "rated %(title)s: %(display_rating)s stars" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "hat %(title)s mit %(display_rating)s Stern bewertet" +msgstr[1] "hat %(title)s mit %(display_rating)s Sternen bewertet" #: bookwyrm/templates/snippets/generated_status/review_pure_name.html:4 #, python-format msgid "Review of \"%(book_title)s\" (%(display_rating)s star): %(review_title)s" msgid_plural "Review of \"%(book_title)s\" (%(display_rating)s stars): %(review_title)s" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "Besprechung von „%(book_title)s“ (%(display_rating)s Stern): %(review_title)s" +msgstr[1] "Besprechung von „%(book_title)s“ (%(display_rating)s Sterne): %(review_title)s" #: bookwyrm/templates/snippets/generated_status/review_pure_name.html:8 #, python-format @@ -3163,15 +3160,15 @@ msgstr "Du hast %(read_count)s von %(goal_count)s Büchern< msgid "%(username)s has read %(read_count)s of %(goal_count)s books." msgstr "%(username)s hat %(read_count)s von %(goal_count)s Büchern gelesen." -#: bookwyrm/templates/snippets/page_text.html:4 +#: bookwyrm/templates/snippets/page_text.html:8 #, python-format msgid "page %(page)s of %(total_pages)s" -msgstr "" +msgstr "Seite %(page)s von %(total_pages)s" -#: bookwyrm/templates/snippets/page_text.html:6 +#: bookwyrm/templates/snippets/page_text.html:14 #, python-format msgid "page %(page)s" -msgstr "" +msgstr "Seite %(page)s" #: bookwyrm/templates/snippets/pagination.html:12 msgid "Previous" @@ -3213,7 +3210,7 @@ msgstr "Raten" #: bookwyrm/templates/snippets/rate_action.html:19 msgid "Rate" -msgstr "" +msgstr "Bewerten" #: bookwyrm/templates/snippets/reading_modals/finish_reading_modal.html:6 #, python-format @@ -3233,12 +3230,12 @@ msgstr "Zu Ende gelesen" #: bookwyrm/templates/snippets/reading_modals/form.html:9 msgid "(Optional)" -msgstr "" +msgstr "(Optional)" #: bookwyrm/templates/snippets/reading_modals/progress_update_modal.html:5 #: bookwyrm/templates/snippets/shelve_button/shelve_button_dropdown_options.html:50 msgid "Update progress" -msgstr "" +msgstr "Update-Fortschritt" #: bookwyrm/templates/snippets/reading_modals/start_reading_modal.html:6 #, python-format @@ -3260,12 +3257,12 @@ msgstr "Registrieren" #: bookwyrm/templates/snippets/report_button.html:6 msgid "Report" -msgstr "" +msgstr "Melden" #: bookwyrm/templates/snippets/report_modal.html:6 #, python-format msgid "Report @%(username)s" -msgstr "" +msgstr "@%(username)s melden" #: bookwyrm/templates/snippets/report_modal.html:23 #, python-format @@ -3274,11 +3271,11 @@ msgstr "Diese Meldung wird an die Moderator:innen von %(site_name)s weitergeleti #: bookwyrm/templates/snippets/report_modal.html:24 msgid "More info about this report:" -msgstr "" +msgstr "Weitere Angeben zu dieser Meldung:" #: bookwyrm/templates/snippets/shelf_selector.html:4 msgid "Move book" -msgstr "" +msgstr "Buch verschieben" #: bookwyrm/templates/snippets/shelve_button/shelve_button_dropdown.html:5 msgid "More shelves" @@ -3297,7 +3294,7 @@ msgstr "Auf Leseliste setzen" #: bookwyrm/templates/snippets/shelve_button/shelve_button_dropdown_options.html:62 #, python-format msgid "Remove from %(name)s" -msgstr "" +msgstr "Aus %(name)s entfernen" #: bookwyrm/templates/snippets/shelve_button/shelve_button_options.html:30 msgid "Finish reading" @@ -3305,21 +3302,21 @@ msgstr "Lesen abschließen" #: bookwyrm/templates/snippets/status/content_status.html:72 msgid "Content warning" -msgstr "" +msgstr "Inhaltswarnung" #: bookwyrm/templates/snippets/status/content_status.html:79 msgid "Show status" -msgstr "" +msgstr "Status anzeigen" #: bookwyrm/templates/snippets/status/content_status.html:101 #, python-format msgid "(Page %(page)s)" -msgstr "" +msgstr "(Seite %(page)s)" #: bookwyrm/templates/snippets/status/content_status.html:103 #, python-format msgid "(%(percent)s%%)" -msgstr "" +msgstr "(%(percent)s%%)" #: bookwyrm/templates/snippets/status/content_status.html:125 msgid "Open image in new window" @@ -3327,47 +3324,52 @@ msgstr "Bild in neuem Fenster öffnen" #: bookwyrm/templates/snippets/status/content_status.html:144 msgid "Hide status" -msgstr "" +msgstr "Status ausblenden" + +#: bookwyrm/templates/snippets/status/header.html:45 +#, python-format +msgid "edited %(date)s" +msgstr "%(date)s bearbeitet" #: bookwyrm/templates/snippets/status/headers/comment.html:2 #, python-format msgid "commented on %(book)s" -msgstr "" +msgstr "hat %(book)s kommentiert" #: bookwyrm/templates/snippets/status/headers/note.html:15 #, python-format msgid "replied to %(username)s's status" -msgstr "" +msgstr "hat auf die Statusmeldung von %(username)s geantwortet" #: bookwyrm/templates/snippets/status/headers/quotation.html:2 #, python-format msgid "quoted %(book)s" -msgstr "" +msgstr "hat %(book)s zitiert" #: bookwyrm/templates/snippets/status/headers/rating.html:3 #, python-format msgid "rated %(book)s:" -msgstr "" +msgstr "hat %(book)s bewertet:" #: bookwyrm/templates/snippets/status/headers/read.html:7 #, python-format msgid "finished reading %(book)s" -msgstr "" +msgstr "hat %(book)s ausgelesen" #: bookwyrm/templates/snippets/status/headers/reading.html:7 #, python-format msgid "started reading %(book)s" -msgstr "" +msgstr "hat angefangen, %(book)s zu lesen" #: bookwyrm/templates/snippets/status/headers/review.html:3 #, python-format msgid "reviewed %(book)s" -msgstr "" +msgstr "hat %(book)s besprochen" #: bookwyrm/templates/snippets/status/headers/to_read.html:7 #, python-format msgid "%(username)s wants to read %(book)s" -msgstr "" +msgstr "%(username)s hat %(book)s auf die Leseliste gesetzt" #: bookwyrm/templates/snippets/status/layout.html:24 #: bookwyrm/templates/snippets/status/status_options.html:17 @@ -3393,28 +3395,24 @@ msgstr "teilt" msgid "More options" msgstr "Mehr Optionen" -#: bookwyrm/templates/snippets/status/status_options.html:26 -msgid "Delete & re-draft" -msgstr "" - #: bookwyrm/templates/snippets/suggested_users.html:16 #, python-format msgid "%(mutuals)s follower you follow" msgid_plural "%(mutuals)s followers you follow" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "%(mutuals)s Follower*in, der*die du folgst" +msgstr[1] "%(mutuals)s Follower*innen, denen du folgst" #: bookwyrm/templates/snippets/suggested_users.html:23 #, python-format msgid "%(shared_books)s book on your shelves" msgid_plural "%(shared_books)s books on your shelves" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "%(shared_books)s Buch in deinen Regalen" +msgstr[1] "%(shared_books)s Bücher in deinen Regalen" #: bookwyrm/templates/snippets/suggested_users.html:31 #: bookwyrm/templates/user/user_preview.html:36 msgid "Follows you" -msgstr "" +msgstr "Folgt dir" #: bookwyrm/templates/snippets/switch_edition_button.html:5 msgid "Switch to this edition" @@ -3422,11 +3420,11 @@ msgstr "Zu dieser Edition wechseln" #: bookwyrm/templates/snippets/table-sort-header.html:6 msgid "Sorted ascending" -msgstr "" +msgstr "Aufsteigend sortiert" #: bookwyrm/templates/snippets/table-sort-header.html:10 msgid "Sorted descending" -msgstr "" +msgstr "Absteigend sortiert" #: bookwyrm/templates/snippets/trimmed_text.html:17 msgid "Show more" @@ -3439,7 +3437,7 @@ msgstr "Weniger anzeigen" #: bookwyrm/templates/user/books_header.html:5 #, python-format msgid "%(username)s's books" -msgstr "" +msgstr "Bücher von %(username)s" #: bookwyrm/templates/user/goal.html:8 #, python-format @@ -3508,7 +3506,7 @@ msgstr "Profil bearbeiten" #: bookwyrm/templates/user/user.html:33 #, python-format msgid "View all %(size)s" -msgstr "" +msgstr "Alle %(size)s anzeigen" #: bookwyrm/templates/user/user.html:46 msgid "View all books" @@ -3520,7 +3518,7 @@ msgstr "Nutzer*innenaktivität" #: bookwyrm/templates/user/user.html:63 msgid "RSS feed" -msgstr "" +msgstr "RSS-Feed" #: bookwyrm/templates/user/user.html:74 msgid "No activities yet!" @@ -3547,21 +3545,21 @@ msgstr "Folgt %(counter)s" #, python-format msgid "%(mutuals_display)s follower you follow" msgid_plural "%(mutuals_display)s followers you follow" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "%(mutuals_display)s Follower*in, der*die du folgst" +msgstr[1] "%(mutuals_display)s Follower*innen, denen du folgst" #: bookwyrm/templates/user/user_preview.html:38 msgid "No followers you follow" -msgstr "" +msgstr "Keine Follower*innen, denen du folgst" #: bookwyrm/templates/widgets/clearable_file_input_with_warning.html:28 msgid "File exceeds maximum size: 10MB" -msgstr "" +msgstr "Datei überschreitet die maximale Größe von 10MB" #: bookwyrm/templatetags/utilities.py:31 #, python-format msgid "%(title)s: %(subtitle)s" -msgstr "" +msgstr "%(title)s: %(subtitle)s" #: bookwyrm/views/import_data.py:67 msgid "Not a valid csv file" @@ -3573,7 +3571,7 @@ msgstr "Username oder Passwort sind falsch" #: bookwyrm/views/password.py:32 msgid "No user with that email address was found." -msgstr "" +msgstr "Es wurde kein*e Benutzer*in mit dieser E-Mail-Adresse gefunden." #: bookwyrm/views/password.py:41 #, python-brace-format diff --git a/locale/en_US/LC_MESSAGES/django.po b/locale/en_US/LC_MESSAGES/django.po index b67d50c53..7d8fc801a 100644 --- a/locale/en_US/LC_MESSAGES/django.po +++ b/locale/en_US/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: 0.0.1\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-10-06 23:57+0000\n" +"POT-Creation-Date: 2021-10-24 14:09+0000\n" "PO-Revision-Date: 2021-02-28 17:19-0800\n" "Last-Translator: Mouse Reeve \n" "Language-Team: English \n" @@ -47,29 +47,29 @@ msgstr "" msgid "Unlimited" msgstr "" -#: bookwyrm/forms.py:326 +#: bookwyrm/forms.py:332 msgid "List Order" msgstr "" -#: bookwyrm/forms.py:327 +#: bookwyrm/forms.py:333 msgid "Book Title" msgstr "" -#: bookwyrm/forms.py:328 bookwyrm/templates/shelf/shelf.html:134 -#: bookwyrm/templates/shelf/shelf.html:165 +#: bookwyrm/forms.py:334 bookwyrm/templates/shelf/shelf.html:149 +#: bookwyrm/templates/shelf/shelf.html:181 #: bookwyrm/templates/snippets/create_status/review.html:33 msgid "Rating" msgstr "" -#: bookwyrm/forms.py:330 bookwyrm/templates/lists/list.html:109 +#: bookwyrm/forms.py:336 bookwyrm/templates/lists/list.html:110 msgid "Sort By" msgstr "" -#: bookwyrm/forms.py:334 +#: bookwyrm/forms.py:340 msgid "Ascending" msgstr "" -#: bookwyrm/forms.py:335 +#: bookwyrm/forms.py:341 msgid "Descending" msgstr "" @@ -152,45 +152,49 @@ msgstr "" msgid "A user with that username already exists." msgstr "" -#: bookwyrm/settings.py:117 +#: bookwyrm/settings.py:118 msgid "Home Timeline" msgstr "" -#: bookwyrm/settings.py:117 +#: bookwyrm/settings.py:118 msgid "Home" msgstr "" -#: bookwyrm/settings.py:118 +#: bookwyrm/settings.py:119 msgid "Books Timeline" msgstr "" -#: bookwyrm/settings.py:118 bookwyrm/templates/search/layout.html:21 +#: bookwyrm/settings.py:119 bookwyrm/templates/search/layout.html:21 #: bookwyrm/templates/search/layout.html:42 -#: bookwyrm/templates/user/layout.html:81 +#: bookwyrm/templates/user/layout.html:88 msgid "Books" msgstr "" -#: bookwyrm/settings.py:164 +#: bookwyrm/settings.py:165 msgid "English" msgstr "" -#: bookwyrm/settings.py:165 +#: bookwyrm/settings.py:166 msgid "Deutsch (German)" msgstr "" -#: bookwyrm/settings.py:166 +#: bookwyrm/settings.py:167 msgid "Español (Spanish)" msgstr "" -#: bookwyrm/settings.py:167 +#: bookwyrm/settings.py:168 msgid "Français (French)" msgstr "" -#: bookwyrm/settings.py:168 +#: bookwyrm/settings.py:169 +msgid "Português - Brasil (Brazilian Portuguese)" +msgstr "" + +#: bookwyrm/settings.py:170 msgid "简体中文 (Simplified Chinese)" msgstr "" -#: bookwyrm/settings.py:169 +#: bookwyrm/settings.py:171 msgid "繁體中文 (Traditional Chinese)" msgstr "" @@ -220,7 +224,7 @@ msgid "Edit Author" msgstr "" #: bookwyrm/templates/author/author.html:34 -#: bookwyrm/templates/author/edit_author.html:41 +#: bookwyrm/templates/author/edit_author.html:43 msgid "Aliases:" msgstr "" @@ -273,71 +277,72 @@ msgstr "" msgid "Updated:" msgstr "" -#: bookwyrm/templates/author/edit_author.html:15 +#: bookwyrm/templates/author/edit_author.html:16 #: bookwyrm/templates/book/edit/edit_book.html:25 msgid "Last edited by:" msgstr "" -#: bookwyrm/templates/author/edit_author.html:31 +#: bookwyrm/templates/author/edit_author.html:33 #: bookwyrm/templates/book/edit/edit_book_form.html:15 msgid "Metadata" msgstr "" -#: bookwyrm/templates/author/edit_author.html:33 -#: bookwyrm/templates/lists/form.html:8 bookwyrm/templates/shelf/form.html:9 +#: bookwyrm/templates/author/edit_author.html:35 +#: bookwyrm/templates/lists/form.html:9 bookwyrm/templates/shelf/form.html:9 msgid "Name:" msgstr "" -#: bookwyrm/templates/author/edit_author.html:43 +#: bookwyrm/templates/author/edit_author.html:45 #: bookwyrm/templates/book/edit/edit_book_form.html:65 #: bookwyrm/templates/book/edit/edit_book_form.html:79 #: bookwyrm/templates/book/edit/edit_book_form.html:124 msgid "Separate multiple values with commas." msgstr "" -#: bookwyrm/templates/author/edit_author.html:50 +#: bookwyrm/templates/author/edit_author.html:52 msgid "Bio:" msgstr "" -#: bookwyrm/templates/author/edit_author.html:57 +#: bookwyrm/templates/author/edit_author.html:59 msgid "Wikipedia link:" msgstr "" -#: bookwyrm/templates/author/edit_author.html:63 +#: bookwyrm/templates/author/edit_author.html:65 msgid "Birth date:" msgstr "" -#: bookwyrm/templates/author/edit_author.html:71 +#: bookwyrm/templates/author/edit_author.html:73 msgid "Death date:" msgstr "" -#: bookwyrm/templates/author/edit_author.html:79 +#: bookwyrm/templates/author/edit_author.html:81 msgid "Author Identifiers" msgstr "" -#: bookwyrm/templates/author/edit_author.html:81 +#: bookwyrm/templates/author/edit_author.html:83 msgid "Openlibrary key:" msgstr "" -#: bookwyrm/templates/author/edit_author.html:89 +#: bookwyrm/templates/author/edit_author.html:91 #: bookwyrm/templates/book/edit/edit_book_form.html:224 msgid "Inventaire ID:" msgstr "" -#: bookwyrm/templates/author/edit_author.html:97 +#: bookwyrm/templates/author/edit_author.html:99 msgid "Librarything key:" msgstr "" -#: bookwyrm/templates/author/edit_author.html:105 +#: bookwyrm/templates/author/edit_author.html:107 msgid "Goodreads key:" msgstr "" -#: bookwyrm/templates/author/edit_author.html:116 +#: bookwyrm/templates/author/edit_author.html:118 #: bookwyrm/templates/book/book.html:140 #: bookwyrm/templates/book/edit/edit_book.html:110 #: bookwyrm/templates/book/readthrough.html:76 +#: bookwyrm/templates/groups/form.html:24 #: bookwyrm/templates/lists/bookmark_button.html:15 -#: bookwyrm/templates/lists/form.html:44 +#: bookwyrm/templates/lists/form.html:75 #: bookwyrm/templates/preferences/edit_user.html:124 #: bookwyrm/templates/settings/announcements/announcement_form.html:69 #: bookwyrm/templates/settings/federation/edit_instance.html:74 @@ -349,11 +354,13 @@ msgstr "" msgid "Save" msgstr "" -#: bookwyrm/templates/author/edit_author.html:117 +#: bookwyrm/templates/author/edit_author.html:119 #: bookwyrm/templates/book/book.html:141 bookwyrm/templates/book/book.html:190 #: bookwyrm/templates/book/cover_modal.html:32 -#: bookwyrm/templates/book/edit/edit_book.html:111 +#: bookwyrm/templates/book/edit/edit_book.html:112 +#: bookwyrm/templates/book/edit/edit_book.html:115 #: bookwyrm/templates/book/readthrough.html:77 +#: bookwyrm/templates/groups/delete_group_modal.html:17 #: bookwyrm/templates/lists/delete_list_modal.html:17 #: bookwyrm/templates/settings/federation/instance.html:88 #: bookwyrm/templates/snippets/delete_readthrough_modal.html:17 @@ -394,7 +401,7 @@ msgstr "" #: bookwyrm/templates/book/book.html:136 #: bookwyrm/templates/book/edit/edit_book_form.html:34 -#: bookwyrm/templates/lists/form.html:12 bookwyrm/templates/shelf/form.html:17 +#: bookwyrm/templates/lists/form.html:13 bookwyrm/templates/shelf/form.html:17 msgid "Description:" msgstr "" @@ -457,7 +464,7 @@ msgstr "" #: bookwyrm/templates/lists/lists.html:5 bookwyrm/templates/lists/lists.html:12 #: bookwyrm/templates/search/layout.html:25 #: bookwyrm/templates/search/layout.html:50 -#: bookwyrm/templates/user/layout.html:75 +#: bookwyrm/templates/user/layout.html:82 msgid "Lists" msgstr "" @@ -467,7 +474,7 @@ msgstr "" #: bookwyrm/templates/book/book.html:315 #: bookwyrm/templates/book/cover_modal.html:31 -#: bookwyrm/templates/lists/list.html:181 +#: bookwyrm/templates/lists/list.html:182 #: bookwyrm/templates/settings/email_blocklist/domain_form.html:26 #: bookwyrm/templates/settings/ip_blocklist/ip_address_form.html:32 msgid "Add" @@ -540,7 +547,9 @@ msgid "This is a new work" msgstr "" #: bookwyrm/templates/book/edit/edit_book.html:97 -#: bookwyrm/templates/password_reset.html:30 +#: bookwyrm/templates/groups/members.html:16 +#: bookwyrm/templates/landing/password_reset.html:30 +#: bookwyrm/templates/snippets/remove_from_group_button.html:16 msgid "Confirm" msgstr "" @@ -609,7 +618,7 @@ msgid "John Doe, Jane Smith" msgstr "" #: bookwyrm/templates/book/edit/edit_book_form.html:132 -#: bookwyrm/templates/shelf/shelf.html:127 +#: bookwyrm/templates/shelf/shelf.html:140 msgid "Cover" msgstr "" @@ -670,11 +679,6 @@ msgstr "" msgid "Search editions" msgstr "" -#: bookwyrm/templates/book/publisher_info.html:21 -#, python-format -msgid "%(format)s" -msgstr "" - #: bookwyrm/templates/book/publisher_info.html:23 #, python-format msgid "%(format)s, %(pages)s pages" @@ -754,7 +758,7 @@ msgid "Help" msgstr "" #: bookwyrm/templates/compose.html:5 bookwyrm/templates/compose.html:8 -msgid "Compose status" +msgid "Edit status" msgstr "" #: bookwyrm/templates/confirm_email/confirm_email.html:4 @@ -795,7 +799,7 @@ msgstr "" #: bookwyrm/templates/confirm_email/resend_form.html:11 #: bookwyrm/templates/landing/layout.html:67 -#: bookwyrm/templates/password_reset_request.html:18 +#: bookwyrm/templates/landing/password_reset_request.html:18 #: bookwyrm/templates/preferences/edit_user.html:56 #: bookwyrm/templates/snippets/register_form.html:13 msgid "Email address:" @@ -889,6 +893,26 @@ msgstr "" msgid "All known users" msgstr "" +#: bookwyrm/templates/discover/card-header.html:9 +#, python-format +msgid "%(username)s rated %(book_title)s" +msgstr "" + +#: bookwyrm/templates/discover/card-header.html:13 +#, python-format +msgid "%(username)s reviewed %(book_title)s" +msgstr "" + +#: bookwyrm/templates/discover/card-header.html:17 +#, python-format +msgid "%(username)s commented on %(book_title)s" +msgstr "" + +#: bookwyrm/templates/discover/card-header.html:21 +#, python-format +msgid "%(username)s quoted %(book_title)s" +msgstr "" + #: bookwyrm/templates/discover/discover.html:4 #: bookwyrm/templates/discover/discover.html:10 #: bookwyrm/templates/layout.html:78 @@ -900,28 +924,8 @@ msgstr "" msgid "See what's new in the local %(site_name)s community" msgstr "" -#: bookwyrm/templates/discover/large-book.html:46 -#: bookwyrm/templates/discover/small-book.html:32 -msgid "rated" -msgstr "" - -#: bookwyrm/templates/discover/large-book.html:48 -#: bookwyrm/templates/discover/small-book.html:34 -msgid "reviewed" -msgstr "" - -#: bookwyrm/templates/discover/large-book.html:50 -#: bookwyrm/templates/discover/small-book.html:36 -msgid "commented on" -msgstr "" - #: bookwyrm/templates/discover/large-book.html:52 -#: bookwyrm/templates/discover/small-book.html:38 -msgid "quoted" -msgstr "" - -#: bookwyrm/templates/discover/large-book.html:68 -#: bookwyrm/templates/discover/small-book.html:52 +#: bookwyrm/templates/discover/small-book.html:36 msgid "View status" msgstr "" @@ -975,7 +979,7 @@ msgstr "" #: bookwyrm/templates/email/invite/html_content.html:15 #, python-format -msgid "Learn more about this instance." +msgid "Learn more about %(site_name)s." msgstr "" #: bookwyrm/templates/email/invite/text_content.html:4 @@ -984,7 +988,8 @@ msgid "You're invited to join %(site_name)s! Click the link below to create an a msgstr "" #: bookwyrm/templates/email/invite/text_content.html:8 -msgid "Learn more about this instance:" +#, python-format +msgid "Learn more about %(site_name)s:" msgstr "" #: bookwyrm/templates/email/password_reset/html_content.html:6 @@ -994,10 +999,10 @@ msgid "You requested to reset your %(site_name)s password. Click the link below msgstr "" #: bookwyrm/templates/email/password_reset/html_content.html:9 -#: bookwyrm/templates/password_reset.html:4 -#: bookwyrm/templates/password_reset.html:10 -#: bookwyrm/templates/password_reset_request.html:4 -#: bookwyrm/templates/password_reset_request.html:10 +#: bookwyrm/templates/landing/password_reset.html:4 +#: bookwyrm/templates/landing/password_reset.html:10 +#: bookwyrm/templates/landing/password_reset_request.html:4 +#: bookwyrm/templates/landing/password_reset_request.html:10 msgid "Reset Password" msgstr "" @@ -1103,7 +1108,7 @@ msgid "What are you reading?" msgstr "" #: bookwyrm/templates/get_started/books.html:9 -#: bookwyrm/templates/layout.html:45 bookwyrm/templates/lists/list.html:137 +#: bookwyrm/templates/layout.html:45 bookwyrm/templates/lists/list.html:138 msgid "Search for a book" msgstr "" @@ -1121,8 +1126,9 @@ msgstr "" #: bookwyrm/templates/get_started/books.html:17 #: bookwyrm/templates/get_started/users.html:18 #: bookwyrm/templates/get_started/users.html:19 -#: bookwyrm/templates/layout.html:51 bookwyrm/templates/layout.html:52 -#: bookwyrm/templates/lists/list.html:141 +#: bookwyrm/templates/groups/group.html:19 +#: bookwyrm/templates/groups/group.html:20 bookwyrm/templates/layout.html:51 +#: bookwyrm/templates/layout.html:52 bookwyrm/templates/lists/list.html:142 #: bookwyrm/templates/search/layout.html:4 #: bookwyrm/templates/search/layout.html:9 msgid "Search" @@ -1138,7 +1144,7 @@ msgid "Popular on %(site_name)s" msgstr "" #: bookwyrm/templates/get_started/books.html:58 -#: bookwyrm/templates/lists/list.html:154 +#: bookwyrm/templates/lists/list.html:155 msgid "No books found" msgstr "" @@ -1224,9 +1230,110 @@ msgstr "" msgid "No users found for \"%(query)s\"" msgstr "" +#: bookwyrm/templates/groups/create_form.html:5 +msgid "Create Group" +msgstr "" + +#: bookwyrm/templates/groups/created_text.html:4 +#, python-format +msgid "Managed by %(username)s" +msgstr "" + +#: bookwyrm/templates/groups/delete_group_modal.html:4 +msgid "Delete this group?" +msgstr "" + +#: bookwyrm/templates/groups/delete_group_modal.html:7 +#: bookwyrm/templates/lists/delete_list_modal.html:7 +msgid "This action cannot be un-done" +msgstr "" + +#: bookwyrm/templates/groups/delete_group_modal.html:15 +#: bookwyrm/templates/lists/delete_list_modal.html:15 +#: bookwyrm/templates/settings/announcements/announcement.html:20 +#: bookwyrm/templates/settings/email_blocklist/email_blocklist.html:49 +#: bookwyrm/templates/settings/ip_blocklist/ip_blocklist.html:36 +#: bookwyrm/templates/snippets/delete_readthrough_modal.html:15 +#: bookwyrm/templates/snippets/follow_request_buttons.html:12 +#: bookwyrm/templates/snippets/join_invitation_buttons.html:13 +msgid "Delete" +msgstr "" + +#: bookwyrm/templates/groups/edit_form.html:5 +msgid "Edit Group" +msgstr "" + +#: bookwyrm/templates/groups/find_users.html:6 +msgid "Add new members!" +msgstr "" + +#: bookwyrm/templates/groups/form.html:8 +msgid "Group Name:" +msgstr "" + +#: bookwyrm/templates/groups/form.html:12 +msgid "Group Description:" +msgstr "" + +#: bookwyrm/templates/groups/form.html:30 +msgid "Delete group" +msgstr "" + +#: bookwyrm/templates/groups/group.html:15 +msgid "Search to add a user" +msgstr "" + +#: bookwyrm/templates/groups/group.html:36 +msgid "This group has no lists" +msgstr "" + +#: bookwyrm/templates/groups/layout.html:16 +msgid "Edit group" +msgstr "" + +#: bookwyrm/templates/groups/members.html:8 +msgid "Members can add and remove books on a group's book lists" +msgstr "" + +#: bookwyrm/templates/groups/members.html:19 +msgid "Leave group" +msgstr "" + +#: bookwyrm/templates/groups/members.html:41 +#: bookwyrm/templates/groups/suggested_users.html:32 +#: bookwyrm/templates/snippets/suggested_users.html:31 +#: bookwyrm/templates/user/user_preview.html:36 +msgid "Follows you" +msgstr "" + +#: bookwyrm/templates/groups/suggested_users.html:17 +#: bookwyrm/templates/snippets/suggested_users.html:16 +#, python-format +msgid "%(mutuals)s follower you follow" +msgid_plural "%(mutuals)s followers you follow" +msgstr[0] "" +msgstr[1] "" + +#: bookwyrm/templates/groups/suggested_users.html:24 +#: bookwyrm/templates/snippets/suggested_users.html:23 +#, python-format +msgid "%(shared_books)s book on your shelves" +msgid_plural "%(shared_books)s books on your shelves" +msgstr[0] "" +msgstr[1] "" + +#: bookwyrm/templates/groups/suggested_users.html:40 +#, python-format +msgid "No potential members found for \"%(user_query)s\"" +msgstr "" + +#: bookwyrm/templates/groups/user_groups.html:15 +msgid "Manager" +msgstr "" + #: bookwyrm/templates/import/import.html:5 #: bookwyrm/templates/import/import.html:9 -#: bookwyrm/templates/shelf/shelf.html:57 +#: bookwyrm/templates/shelf/shelf.html:61 msgid "Import Books" msgstr "" @@ -1323,14 +1430,14 @@ msgid "Book" msgstr "" #: bookwyrm/templates/import/import_status.html:122 -#: bookwyrm/templates/shelf/shelf.html:128 -#: bookwyrm/templates/shelf/shelf.html:148 +#: bookwyrm/templates/shelf/shelf.html:141 +#: bookwyrm/templates/shelf/shelf.html:163 msgid "Title" msgstr "" #: bookwyrm/templates/import/import_status.html:125 -#: bookwyrm/templates/shelf/shelf.html:129 -#: bookwyrm/templates/shelf/shelf.html:151 +#: bookwyrm/templates/shelf/shelf.html:142 +#: bookwyrm/templates/shelf/shelf.html:166 msgid "Author" msgstr "" @@ -1339,23 +1446,10 @@ msgid "Imported" msgstr "" #: bookwyrm/templates/import/tooltip.html:6 -msgid "You can download your GoodReads data from the Import/Export page of your GoodReads account." +msgid "You can download your Goodreads data from the Import/Export page of your Goodreads account." msgstr "" -#: bookwyrm/templates/invite.html:4 bookwyrm/templates/invite.html:8 -#: bookwyrm/templates/login.html:49 -msgid "Create an Account" -msgstr "" - -#: bookwyrm/templates/invite.html:21 -msgid "Permission Denied" -msgstr "" - -#: bookwyrm/templates/invite.html:22 -msgid "Sorry! This invite code is no longer valid." -msgstr "" - -#: bookwyrm/templates/landing/about.html:7 +#: bookwyrm/templates/landing/about.html:7 bookwyrm/templates/layout.html:230 #, python-format msgid "About %(site_name)s" msgstr "" @@ -1370,6 +1464,20 @@ msgstr "" msgid "Privacy Policy" msgstr "" +#: bookwyrm/templates/landing/invite.html:4 +#: bookwyrm/templates/landing/invite.html:8 +#: bookwyrm/templates/landing/login.html:49 +msgid "Create an Account" +msgstr "" + +#: bookwyrm/templates/landing/invite.html:21 +msgid "Permission Denied" +msgstr "" + +#: bookwyrm/templates/landing/invite.html:22 +msgid "Sorry! This invite code is no longer valid." +msgstr "" + #: bookwyrm/templates/landing/landing.html:6 msgid "Recent Books" msgstr "" @@ -1408,6 +1516,53 @@ msgstr "" msgid "Your Account" msgstr "" +#: bookwyrm/templates/landing/login.html:4 +msgid "Login" +msgstr "" + +#: bookwyrm/templates/landing/login.html:7 +#: bookwyrm/templates/landing/login.html:37 bookwyrm/templates/layout.html:179 +msgid "Log in" +msgstr "" + +#: bookwyrm/templates/landing/login.html:15 +msgid "Success! Email address confirmed." +msgstr "" + +#: bookwyrm/templates/landing/login.html:21 bookwyrm/templates/layout.html:170 +#: bookwyrm/templates/snippets/register_form.html:4 +msgid "Username:" +msgstr "" + +#: bookwyrm/templates/landing/login.html:27 +#: bookwyrm/templates/landing/password_reset.html:17 +#: bookwyrm/templates/layout.html:174 +#: bookwyrm/templates/snippets/register_form.html:22 +msgid "Password:" +msgstr "" + +#: bookwyrm/templates/landing/login.html:40 bookwyrm/templates/layout.html:176 +msgid "Forgot your password?" +msgstr "" + +#: bookwyrm/templates/landing/login.html:62 +msgid "More about this site" +msgstr "" + +#: bookwyrm/templates/landing/password_reset.html:23 +#: bookwyrm/templates/preferences/change_password.html:18 +#: bookwyrm/templates/preferences/delete_user.html:20 +msgid "Confirm password:" +msgstr "" + +#: bookwyrm/templates/landing/password_reset_request.html:14 +msgid "A link to reset your password will be sent to your email address" +msgstr "" + +#: bookwyrm/templates/landing/password_reset_request.html:28 +msgid "Reset password" +msgstr "" + #: bookwyrm/templates/layout.html:13 #, python-format msgid "%(site_name)s search" @@ -1455,25 +1610,10 @@ msgstr "" msgid "Notifications" msgstr "" -#: bookwyrm/templates/layout.html:170 bookwyrm/templates/layout.html:174 -#: bookwyrm/templates/login.html:21 -#: bookwyrm/templates/snippets/register_form.html:4 -msgid "Username:" -msgstr "" - #: bookwyrm/templates/layout.html:175 msgid "password" msgstr "" -#: bookwyrm/templates/layout.html:176 bookwyrm/templates/login.html:40 -msgid "Forgot your password?" -msgstr "" - -#: bookwyrm/templates/layout.html:179 bookwyrm/templates/login.html:7 -#: bookwyrm/templates/login.html:37 -msgid "Log in" -msgstr "" - #: bookwyrm/templates/layout.html:187 msgid "Join" msgstr "" @@ -1486,10 +1626,6 @@ msgstr "" msgid "Error posting status" msgstr "" -#: bookwyrm/templates/layout.html:230 -msgid "About this instance" -msgstr "" - #: bookwyrm/templates/layout.html:234 msgid "Contact site admin" msgstr "" @@ -1518,11 +1654,16 @@ msgstr "" #: bookwyrm/templates/lists/created_text.html:5 #, python-format -msgid "Created and curated by %(username)s" +msgid "Created by %(username)s and managed by %(groupname)s" msgstr "" #: bookwyrm/templates/lists/created_text.html:7 #, python-format +msgid "Created and curated by %(username)s" +msgstr "" + +#: bookwyrm/templates/lists/created_text.html:9 +#, python-format msgid "Created by %(username)s" msgstr "" @@ -1554,118 +1695,130 @@ msgstr "" msgid "Delete this list?" msgstr "" -#: bookwyrm/templates/lists/delete_list_modal.html:7 -msgid "This action cannot be un-done" -msgstr "" - -#: bookwyrm/templates/lists/delete_list_modal.html:15 -#: bookwyrm/templates/settings/announcements/announcement.html:20 -#: bookwyrm/templates/settings/email_blocklist/email_blocklist.html:49 -#: bookwyrm/templates/settings/ip_blocklist/ip_blocklist.html:36 -#: bookwyrm/templates/snippets/delete_readthrough_modal.html:15 -#: bookwyrm/templates/snippets/follow_request_buttons.html:12 -msgid "Delete" -msgstr "" - #: bookwyrm/templates/lists/edit_form.html:5 #: bookwyrm/templates/lists/layout.html:16 msgid "Edit List" msgstr "" -#: bookwyrm/templates/lists/form.html:18 +#: bookwyrm/templates/lists/form.html:19 msgid "List curation:" msgstr "" -#: bookwyrm/templates/lists/form.html:21 +#: bookwyrm/templates/lists/form.html:22 msgid "Closed" msgstr "" -#: bookwyrm/templates/lists/form.html:22 +#: bookwyrm/templates/lists/form.html:23 msgid "Only you can add and remove books to this list" msgstr "" -#: bookwyrm/templates/lists/form.html:26 +#: bookwyrm/templates/lists/form.html:27 msgid "Curated" msgstr "" -#: bookwyrm/templates/lists/form.html:27 +#: bookwyrm/templates/lists/form.html:28 msgid "Anyone can suggest books, subject to your approval" msgstr "" -#: bookwyrm/templates/lists/form.html:31 +#: bookwyrm/templates/lists/form.html:32 msgctxt "curation type" msgid "Open" msgstr "" -#: bookwyrm/templates/lists/form.html:32 +#: bookwyrm/templates/lists/form.html:33 msgid "Anyone can add books to this list" msgstr "" -#: bookwyrm/templates/lists/form.html:50 +#: bookwyrm/templates/lists/form.html:37 +msgid "Group" +msgstr "" + +#: bookwyrm/templates/lists/form.html:38 +msgid "Group members can add to and remove from this list" +msgstr "" + +#: bookwyrm/templates/lists/form.html:41 +msgid "Select Group" +msgstr "" + +#: bookwyrm/templates/lists/form.html:45 +msgid "Select a group" +msgstr "" + +#: bookwyrm/templates/lists/form.html:56 +msgid "You don't have any Groups yet!" +msgstr "" + +#: bookwyrm/templates/lists/form.html:58 +msgid "Create a Group" +msgstr "" + +#: bookwyrm/templates/lists/form.html:81 msgid "Delete list" msgstr "" -#: bookwyrm/templates/lists/list.html:20 +#: bookwyrm/templates/lists/list.html:21 msgid "You successfully suggested a book for this list!" msgstr "" -#: bookwyrm/templates/lists/list.html:22 +#: bookwyrm/templates/lists/list.html:23 msgid "You successfully added a book to this list!" msgstr "" -#: bookwyrm/templates/lists/list.html:28 +#: bookwyrm/templates/lists/list.html:29 msgid "This list is currently empty" msgstr "" -#: bookwyrm/templates/lists/list.html:66 +#: bookwyrm/templates/lists/list.html:67 #, python-format msgid "Added by %(username)s" msgstr "" -#: bookwyrm/templates/lists/list.html:75 +#: bookwyrm/templates/lists/list.html:76 msgid "List position" msgstr "" -#: bookwyrm/templates/lists/list.html:81 +#: bookwyrm/templates/lists/list.html:82 msgid "Set" msgstr "" -#: bookwyrm/templates/lists/list.html:91 +#: bookwyrm/templates/lists/list.html:92 +#: bookwyrm/templates/snippets/remove_from_group_button.html:19 #: bookwyrm/templates/snippets/shelf_selector.html:26 msgid "Remove" msgstr "" -#: bookwyrm/templates/lists/list.html:105 -#: bookwyrm/templates/lists/list.html:122 +#: bookwyrm/templates/lists/list.html:106 +#: bookwyrm/templates/lists/list.html:123 msgid "Sort List" msgstr "" -#: bookwyrm/templates/lists/list.html:115 +#: bookwyrm/templates/lists/list.html:116 msgid "Direction" msgstr "" -#: bookwyrm/templates/lists/list.html:129 +#: bookwyrm/templates/lists/list.html:130 msgid "Add Books" msgstr "" -#: bookwyrm/templates/lists/list.html:131 +#: bookwyrm/templates/lists/list.html:132 msgid "Suggest Books" msgstr "" -#: bookwyrm/templates/lists/list.html:142 +#: bookwyrm/templates/lists/list.html:143 msgid "search" msgstr "" -#: bookwyrm/templates/lists/list.html:148 +#: bookwyrm/templates/lists/list.html:149 msgid "Clear search" msgstr "" -#: bookwyrm/templates/lists/list.html:153 +#: bookwyrm/templates/lists/list.html:154 #, python-format msgid "No books found matching the query \"%(query)s\"" msgstr "" -#: bookwyrm/templates/lists/list.html:181 +#: bookwyrm/templates/lists/list.html:182 msgid "Suggest" msgstr "" @@ -1677,29 +1830,17 @@ msgstr "" msgid "Your Lists" msgstr "" -#: bookwyrm/templates/lists/lists.html:35 +#: bookwyrm/templates/lists/lists.html:36 msgid "All Lists" msgstr "" -#: bookwyrm/templates/lists/lists.html:39 +#: bookwyrm/templates/lists/lists.html:40 msgid "Saved Lists" msgstr "" -#: bookwyrm/templates/login.html:4 -msgid "Login" -msgstr "" - -#: bookwyrm/templates/login.html:15 -msgid "Success! Email address confirmed." -msgstr "" - -#: bookwyrm/templates/login.html:27 bookwyrm/templates/password_reset.html:17 -#: bookwyrm/templates/snippets/register_form.html:22 -msgid "Password:" -msgstr "" - -#: bookwyrm/templates/login.html:62 -msgid "More about this site" +#: bookwyrm/templates/notifications/items/accept.html:16 +#, python-format +msgid "accepted your invitation to join group \"%(group_name)s\"" msgstr "" #: bookwyrm/templates/notifications/items/add.html:24 @@ -1734,22 +1875,22 @@ msgstr "" #: bookwyrm/templates/notifications/items/fav.html:19 #, python-format -msgid "favorited your review of %(book_title)s" +msgid "liked your review of %(book_title)s" msgstr "" #: bookwyrm/templates/notifications/items/fav.html:25 #, python-format -msgid "favorited your comment on%(book_title)s" +msgid "liked your comment on %(book_title)s" msgstr "" #: bookwyrm/templates/notifications/items/fav.html:31 #, python-format -msgid "favorited your quote from %(book_title)s" +msgid "liked your quote from %(book_title)s" msgstr "" #: bookwyrm/templates/notifications/items/fav.html:37 #, python-format -msgid "favorited your status" +msgid "liked your status" msgstr "" #: bookwyrm/templates/notifications/items/follow.html:15 @@ -1765,6 +1906,21 @@ msgstr "" msgid "Your import completed." msgstr "" +#: bookwyrm/templates/notifications/items/invite.html:15 +#, python-format +msgid "invited you to join the group \"%(group_name)s\"" +msgstr "" + +#: bookwyrm/templates/notifications/items/join.html:16 +#, python-format +msgid "has joined your group \"%(group_name)s\"" +msgstr "" + +#: bookwyrm/templates/notifications/items/leave.html:16 +#, python-format +msgid "has left your group \"%(group_name)s\"" +msgstr "" + #: bookwyrm/templates/notifications/items/mention.html:20 #, python-format msgid "mentioned you in a review of %(book_title)s" @@ -1785,6 +1941,16 @@ msgstr "" msgid "mentioned you in a status" msgstr "" +#: bookwyrm/templates/notifications/items/remove.html:17 +#, python-format +msgid "has been removed from your group \"%(group_name)s\"" +msgstr "" + +#: bookwyrm/templates/notifications/items/remove.html:23 +#, python-format +msgid "You have been removed from the \"%(group_name)s\" group" +msgstr "" + #: bookwyrm/templates/notifications/items/reply.html:21 #, python-format msgid "replied to your review of %(book_title)s" @@ -1810,6 +1976,21 @@ msgstr "" msgid "A new report needs moderation." msgstr "" +#: bookwyrm/templates/notifications/items/update.html:16 +#, python-format +msgid "has changed the privacy level for %(group_name)s" +msgstr "" + +#: bookwyrm/templates/notifications/items/update.html:20 +#, python-format +msgid "has changed the name of %(group_name)s" +msgstr "" + +#: bookwyrm/templates/notifications/items/update.html:24 +#, python-format +msgid "has changed the description of %(group_name)s" +msgstr "" + #: bookwyrm/templates/notifications/notifications_page.html:18 msgid "Delete notifications" msgstr "" @@ -1826,20 +2007,6 @@ msgstr "" msgid "You're all caught up!" msgstr "" -#: bookwyrm/templates/password_reset.html:23 -#: bookwyrm/templates/preferences/change_password.html:18 -#: bookwyrm/templates/preferences/delete_user.html:20 -msgid "Confirm password:" -msgstr "" - -#: bookwyrm/templates/password_reset_request.html:14 -msgid "A link to reset your password will be sent to your email address" -msgstr "" - -#: bookwyrm/templates/password_reset_request.html:28 -msgid "Reset password" -msgstr "" - #: bookwyrm/templates/preferences/blocks.html:4 #: bookwyrm/templates/preferences/blocks.html:7 #: bookwyrm/templates/preferences/layout.html:31 @@ -2262,7 +2429,7 @@ msgid "Details" msgstr "" #: bookwyrm/templates/settings/federation/instance.html:35 -#: bookwyrm/templates/user/layout.html:63 +#: bookwyrm/templates/user/layout.html:64 msgid "Activity" msgstr "" @@ -2298,6 +2465,7 @@ msgid "Notes" msgstr "" #: bookwyrm/templates/settings/federation/instance.html:75 +#: bookwyrm/templates/snippets/status/status_options.html:24 msgid "Edit" msgstr "" @@ -2637,7 +2805,7 @@ msgid "Short description:" msgstr "" #: bookwyrm/templates/settings/site.html:37 -msgid "Used when the instance is previewed on joinbookwyrm.com. Does not support html or markdown." +msgid "Used when the instance is previewed on joinbookwyrm.com. Does not support HTML or Markdown." msgstr "" #: bookwyrm/templates/settings/site.html:41 @@ -2812,7 +2980,7 @@ msgid "Permanently deleted" msgstr "" #: bookwyrm/templates/settings/users/user_moderation_actions.html:13 -#: bookwyrm/templates/snippets/status/status_options.html:35 +#: bookwyrm/templates/snippets/status/status_options.html:32 #: bookwyrm/templates/snippets/user_options.html:13 msgid "Send direct message" msgstr "" @@ -2837,53 +3005,66 @@ msgstr "" msgid "Edit Shelf" msgstr "" -#: bookwyrm/templates/shelf/shelf.html:28 bookwyrm/views/shelf.py:55 +#: bookwyrm/templates/shelf/shelf.html:28 bookwyrm/views/shelf/shelf.py:53 msgid "All books" msgstr "" -#: bookwyrm/templates/shelf/shelf.html:55 +#: bookwyrm/templates/shelf/shelf.html:69 msgid "Create shelf" msgstr "" -#: bookwyrm/templates/shelf/shelf.html:77 +#: bookwyrm/templates/shelf/shelf.html:90 #, python-format msgid "%(formatted_count)s book" msgid_plural "%(formatted_count)s books" msgstr[0] "" msgstr[1] "" -#: bookwyrm/templates/shelf/shelf.html:84 +#: bookwyrm/templates/shelf/shelf.html:97 #, python-format msgid "(showing %(start)s-%(end)s)" msgstr "" -#: bookwyrm/templates/shelf/shelf.html:96 +#: bookwyrm/templates/shelf/shelf.html:109 msgid "Edit shelf" msgstr "" -#: bookwyrm/templates/shelf/shelf.html:104 +#: bookwyrm/templates/shelf/shelf.html:117 msgid "Delete shelf" msgstr "" -#: bookwyrm/templates/shelf/shelf.html:130 -#: bookwyrm/templates/shelf/shelf.html:154 +#: bookwyrm/templates/shelf/shelf.html:145 +#: bookwyrm/templates/shelf/shelf.html:171 msgid "Shelved" msgstr "" -#: bookwyrm/templates/shelf/shelf.html:131 -#: bookwyrm/templates/shelf/shelf.html:158 +#: bookwyrm/templates/shelf/shelf.html:146 +#: bookwyrm/templates/shelf/shelf.html:174 msgid "Started" msgstr "" -#: bookwyrm/templates/shelf/shelf.html:132 -#: bookwyrm/templates/shelf/shelf.html:161 +#: bookwyrm/templates/shelf/shelf.html:147 +#: bookwyrm/templates/shelf/shelf.html:177 msgid "Finished" msgstr "" -#: bookwyrm/templates/shelf/shelf.html:187 +#: bookwyrm/templates/shelf/shelf.html:203 msgid "This shelf is empty." msgstr "" +#: bookwyrm/templates/snippets/add_to_group_button.html:15 +msgid "Invite" +msgstr "" + +#: bookwyrm/templates/snippets/add_to_group_button.html:24 +msgid "Uninvite" +msgstr "" + +#: bookwyrm/templates/snippets/add_to_group_button.html:28 +#, python-format +msgid "Remove @%(username)s" +msgstr "" + #: bookwyrm/templates/snippets/announcement.html:31 #, python-format msgid "Posted by %(username)s" @@ -2927,22 +3108,22 @@ msgstr "" msgid "Some thoughts on the book" msgstr "" -#: bookwyrm/templates/snippets/create_status/comment.html:26 +#: bookwyrm/templates/snippets/create_status/comment.html:27 #: bookwyrm/templates/snippets/reading_modals/progress_update_modal.html:15 msgid "Progress:" msgstr "" -#: bookwyrm/templates/snippets/create_status/comment.html:52 +#: bookwyrm/templates/snippets/create_status/comment.html:53 #: bookwyrm/templates/snippets/progress_field.html:18 msgid "pages" msgstr "" -#: bookwyrm/templates/snippets/create_status/comment.html:58 +#: bookwyrm/templates/snippets/create_status/comment.html:59 #: bookwyrm/templates/snippets/progress_field.html:23 msgid "percent" msgstr "" -#: bookwyrm/templates/snippets/create_status/comment.html:65 +#: bookwyrm/templates/snippets/create_status/comment.html:66 #, python-format msgid "of %(pages)s pages" msgstr "" @@ -2970,7 +3151,7 @@ msgstr "" msgid "Include spoiler alert" msgstr "" -#: bookwyrm/templates/snippets/create_status/layout.html:41 +#: bookwyrm/templates/snippets/create_status/layout.html:48 #: bookwyrm/templates/snippets/reading_modals/form.html:7 msgid "Comment:" msgstr "" @@ -2979,6 +3160,7 @@ msgstr "" #: bookwyrm/templates/snippets/privacy-icons.html:15 #: bookwyrm/templates/snippets/privacy-icons.html:16 #: bookwyrm/templates/snippets/privacy_select.html:20 +#: bookwyrm/templates/snippets/privacy_select_no_followers.html:17 msgid "Private" msgstr "" @@ -3074,6 +3256,7 @@ msgid "Unfollow" msgstr "" #: bookwyrm/templates/snippets/follow_request_buttons.html:7 +#: bookwyrm/templates/snippets/join_invitation_buttons.html:8 msgid "Accept" msgstr "" @@ -3164,12 +3347,12 @@ msgstr "" msgid "%(username)s has read %(read_count)s of %(goal_count)s books." msgstr "" -#: bookwyrm/templates/snippets/page_text.html:4 +#: bookwyrm/templates/snippets/page_text.html:8 #, python-format msgid "page %(page)s of %(total_pages)s" msgstr "" -#: bookwyrm/templates/snippets/page_text.html:6 +#: bookwyrm/templates/snippets/page_text.html:14 #, python-format msgid "page %(page)s" msgstr "" @@ -3185,12 +3368,14 @@ msgstr "" #: bookwyrm/templates/snippets/privacy-icons.html:3 #: bookwyrm/templates/snippets/privacy-icons.html:4 #: bookwyrm/templates/snippets/privacy_select.html:11 +#: bookwyrm/templates/snippets/privacy_select_no_followers.html:11 msgid "Public" msgstr "" #: bookwyrm/templates/snippets/privacy-icons.html:7 #: bookwyrm/templates/snippets/privacy-icons.html:8 #: bookwyrm/templates/snippets/privacy_select.html:14 +#: bookwyrm/templates/snippets/privacy_select_no_followers.html:14 msgid "Unlisted" msgstr "" @@ -3199,6 +3384,7 @@ msgid "Followers-only" msgstr "" #: bookwyrm/templates/snippets/privacy_select.html:6 +#: bookwyrm/templates/snippets/privacy_select_no_followers.html:6 msgid "Post privacy" msgstr "" @@ -3330,6 +3516,11 @@ msgstr "" msgid "Hide status" msgstr "" +#: bookwyrm/templates/snippets/status/header.html:45 +#, python-format +msgid "edited %(date)s" +msgstr "" + #: bookwyrm/templates/snippets/status/headers/comment.html:2 #, python-format msgid "commented on %(book)s" @@ -3394,29 +3585,6 @@ msgstr "" msgid "More options" msgstr "" -#: bookwyrm/templates/snippets/status/status_options.html:26 -msgid "Delete & re-draft" -msgstr "" - -#: bookwyrm/templates/snippets/suggested_users.html:16 -#, python-format -msgid "%(mutuals)s follower you follow" -msgid_plural "%(mutuals)s followers you follow" -msgstr[0] "" -msgstr[1] "" - -#: bookwyrm/templates/snippets/suggested_users.html:23 -#, python-format -msgid "%(shared_books)s book on your shelves" -msgid_plural "%(shared_books)s books on your shelves" -msgstr[0] "" -msgstr[1] "" - -#: bookwyrm/templates/snippets/suggested_users.html:31 -#: bookwyrm/templates/user/user_preview.html:36 -msgid "Follows you" -msgstr "" - #: bookwyrm/templates/snippets/switch_edition_button.html:5 msgid "Switch to this edition" msgstr "" @@ -3466,18 +3634,35 @@ msgstr "" msgid "%(username)s's %(year)s Books" msgstr "" -#: bookwyrm/templates/user/layout.html:18 bookwyrm/templates/user/user.html:10 +#: bookwyrm/templates/user/groups.html:9 +msgid "Your Groups" +msgstr "" + +#: bookwyrm/templates/user/groups.html:11 +#, python-format +msgid "Groups: %(username)s" +msgstr "" + +#: bookwyrm/templates/user/groups.html:17 +msgid "Create group" +msgstr "" + +#: bookwyrm/templates/user/layout.html:19 bookwyrm/templates/user/user.html:10 msgid "User Profile" msgstr "" -#: bookwyrm/templates/user/layout.html:44 +#: bookwyrm/templates/user/layout.html:45 msgid "Follow Requests" msgstr "" -#: bookwyrm/templates/user/layout.html:69 +#: bookwyrm/templates/user/layout.html:70 msgid "Reading Goal" msgstr "" +#: bookwyrm/templates/user/layout.html:76 +msgid "Groups" +msgstr "" + #: bookwyrm/templates/user/lists.html:11 #, python-format msgid "Lists: %(username)s" @@ -3568,15 +3753,15 @@ msgstr "" msgid "Not a valid csv file" msgstr "" -#: bookwyrm/views/login.py:69 +#: bookwyrm/views/landing/login.py:69 msgid "Username or password are incorrect" msgstr "" -#: bookwyrm/views/password.py:32 +#: bookwyrm/views/landing/password.py:32 msgid "No user with that email address was found." msgstr "" -#: bookwyrm/views/password.py:41 +#: bookwyrm/views/landing/password.py:43 #, python-brace-format msgid "A password reset link was sent to {email}" msgstr "" diff --git a/locale/es_ES/LC_MESSAGES/django.mo b/locale/es_ES/LC_MESSAGES/django.mo index 93c9caed1..7df750310 100644 Binary files a/locale/es_ES/LC_MESSAGES/django.mo and b/locale/es_ES/LC_MESSAGES/django.mo differ diff --git a/locale/es_ES/LC_MESSAGES/django.po b/locale/es_ES/LC_MESSAGES/django.po index d066ed1a9..ab71feb8a 100644 --- a/locale/es_ES/LC_MESSAGES/django.po +++ b/locale/es_ES/LC_MESSAGES/django.po @@ -2,8 +2,8 @@ msgid "" msgstr "" "Project-Id-Version: bookwyrm\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-10-06 23:57+0000\n" -"PO-Revision-Date: 2021-10-08 00:04\n" +"POT-Creation-Date: 2021-10-15 22:03+0000\n" +"PO-Revision-Date: 2021-10-21 21:00\n" "Last-Translator: Mouse Reeve \n" "Language-Team: Spanish\n" "Language: es\n" @@ -54,8 +54,8 @@ msgstr "Orden de la lista" msgid "Book Title" msgstr "Título" -#: bookwyrm/forms.py:328 bookwyrm/templates/shelf/shelf.html:134 -#: bookwyrm/templates/shelf/shelf.html:165 +#: bookwyrm/forms.py:328 bookwyrm/templates/shelf/shelf.html:136 +#: bookwyrm/templates/shelf/shelf.html:168 #: bookwyrm/templates/snippets/create_status/review.html:33 msgid "Rating" msgstr "Calificación" @@ -151,45 +151,49 @@ msgstr "nombre de usuario" msgid "A user with that username already exists." msgstr "Ya existe un usuario con ese nombre." -#: bookwyrm/settings.py:117 +#: bookwyrm/settings.py:118 msgid "Home Timeline" msgstr "Línea temporal de hogar" -#: bookwyrm/settings.py:117 +#: bookwyrm/settings.py:118 msgid "Home" msgstr "Hogar" -#: bookwyrm/settings.py:118 +#: bookwyrm/settings.py:119 msgid "Books Timeline" msgstr "Línea temporal de libros" -#: bookwyrm/settings.py:118 bookwyrm/templates/search/layout.html:21 +#: bookwyrm/settings.py:119 bookwyrm/templates/search/layout.html:21 #: bookwyrm/templates/search/layout.html:42 #: bookwyrm/templates/user/layout.html:81 msgid "Books" msgstr "Libros" -#: bookwyrm/settings.py:164 +#: bookwyrm/settings.py:165 msgid "English" msgstr "Inglés" -#: bookwyrm/settings.py:165 +#: bookwyrm/settings.py:166 msgid "Deutsch (German)" msgstr "Deutsch (Alemán)" -#: bookwyrm/settings.py:166 +#: bookwyrm/settings.py:167 msgid "Español (Spanish)" msgstr "Español" -#: bookwyrm/settings.py:167 +#: bookwyrm/settings.py:168 msgid "Français (French)" msgstr "Français (Francés)" -#: bookwyrm/settings.py:168 +#: bookwyrm/settings.py:169 +msgid "Português - Brasil (Brazilian Portuguese)" +msgstr "Português - Brasil (Portugués Brasileño)" + +#: bookwyrm/settings.py:170 msgid "简体中文 (Simplified Chinese)" msgstr "简体中文 (Chino simplificado)" -#: bookwyrm/settings.py:169 +#: bookwyrm/settings.py:171 msgid "繁體中文 (Traditional Chinese)" msgstr "繁體中文 (Chino tradicional)" @@ -221,7 +225,7 @@ msgstr "Editar Autor/Autora" #: bookwyrm/templates/author/author.html:34 #: bookwyrm/templates/author/edit_author.html:41 msgid "Aliases:" -msgstr "" +msgstr "Alias:" #: bookwyrm/templates/author/author.html:45 msgid "Born:" @@ -233,7 +237,7 @@ msgstr "Muerto:" #: bookwyrm/templates/author/author.html:61 msgid "Wikipedia" -msgstr "" +msgstr "Wikipedia" #: bookwyrm/templates/author/author.html:69 #: bookwyrm/templates/book/book.html:94 @@ -296,7 +300,7 @@ msgstr "Separar varios valores con comas." #: bookwyrm/templates/author/edit_author.html:50 msgid "Bio:" -msgstr "" +msgstr "Biografía:" #: bookwyrm/templates/author/edit_author.html:57 msgid "Wikipedia link:" @@ -484,7 +488,7 @@ msgstr "Número OCLC:" #: bookwyrm/templates/book/book_identifiers.html:22 #: bookwyrm/templates/book/edit/edit_book_form.html:240 msgid "ASIN:" -msgstr "" +msgstr "ASIN:" #: bookwyrm/templates/book/cover_modal.html:17 #: bookwyrm/templates/book/edit/edit_book_form.html:143 @@ -571,7 +575,7 @@ msgstr "Idiomas:" #: bookwyrm/templates/book/edit/edit_book_form.html:74 msgid "Publication" -msgstr "" +msgstr "Publicación" #: bookwyrm/templates/book/edit/edit_book_form.html:77 msgid "Publisher:" @@ -635,11 +639,11 @@ msgstr "Identificadores de libro" #: bookwyrm/templates/book/edit/edit_book_form.html:200 msgid "ISBN 13:" -msgstr "" +msgstr "ISBN 13:" #: bookwyrm/templates/book/edit/edit_book_form.html:208 msgid "ISBN 10:" -msgstr "" +msgstr "ISBN 10:" #: bookwyrm/templates/book/edit/edit_book_form.html:216 msgid "Openlibrary ID:" @@ -669,11 +673,6 @@ msgstr "Idioma:" msgid "Search editions" msgstr "Buscar ediciones" -#: bookwyrm/templates/book/publisher_info.html:21 -#, python-format -msgid "%(format)s" -msgstr "" - #: bookwyrm/templates/book/publisher_info.html:23 #, python-format msgid "%(format)s, %(pages)s pages" @@ -753,8 +752,8 @@ msgid "Help" msgstr "Ayuda" #: bookwyrm/templates/compose.html:5 bookwyrm/templates/compose.html:8 -msgid "Compose status" -msgstr "Componer status" +msgid "Edit status" +msgstr "Editar estado" #: bookwyrm/templates/confirm_email/confirm_email.html:4 msgid "Confirm email" @@ -888,6 +887,26 @@ msgstr "Usuarios de BookWyrm" msgid "All known users" msgstr "Todos los usuarios conocidos" +#: bookwyrm/templates/discover/card-header.html:9 +#, python-format +msgid "%(username)s rated %(book_title)s" +msgstr "%(username)s calificó %(book_title)s" + +#: bookwyrm/templates/discover/card-header.html:13 +#, python-format +msgid "%(username)s reviewed %(book_title)s" +msgstr "%(username)s reseñó %(book_title)s" + +#: bookwyrm/templates/discover/card-header.html:17 +#, python-format +msgid "%(username)s commented on %(book_title)s" +msgstr "%(username)s comentó en %(book_title)s" + +#: bookwyrm/templates/discover/card-header.html:21 +#, python-format +msgid "%(username)s quoted %(book_title)s" +msgstr "%(username)s citó %(book_title)s" + #: bookwyrm/templates/discover/discover.html:4 #: bookwyrm/templates/discover/discover.html:10 #: bookwyrm/templates/layout.html:78 @@ -899,28 +918,8 @@ msgstr "Descubrir" msgid "See what's new in the local %(site_name)s community" msgstr "Ver que es nuevo en la comunidad local de %(site_name)s" -#: bookwyrm/templates/discover/large-book.html:46 -#: bookwyrm/templates/discover/small-book.html:32 -msgid "rated" -msgstr "calificó" - -#: bookwyrm/templates/discover/large-book.html:48 -#: bookwyrm/templates/discover/small-book.html:34 -msgid "reviewed" -msgstr "reseñó" - -#: bookwyrm/templates/discover/large-book.html:50 -#: bookwyrm/templates/discover/small-book.html:36 -msgid "commented on" -msgstr "comentó en" - #: bookwyrm/templates/discover/large-book.html:52 -#: bookwyrm/templates/discover/small-book.html:38 -msgid "quoted" -msgstr "citó" - -#: bookwyrm/templates/discover/large-book.html:68 -#: bookwyrm/templates/discover/small-book.html:52 +#: bookwyrm/templates/discover/small-book.html:36 msgid "View status" msgstr "Ver status" @@ -974,8 +973,8 @@ msgstr "Únete ahora" #: bookwyrm/templates/email/invite/html_content.html:15 #, python-format -msgid "Learn more about this instance." -msgstr "Aprenda más sobre esta instancia." +msgid "Learn more about %(site_name)s." +msgstr "Más información sobre %(site_name)s." #: bookwyrm/templates/email/invite/text_content.html:4 #, python-format @@ -983,8 +982,9 @@ msgid "You're invited to join %(site_name)s! Click the link below to create an a msgstr "Estás invitado a unirte con %(site_name)s! Haz clic en el enlace a continuación para crear una cuenta." #: bookwyrm/templates/email/invite/text_content.html:8 -msgid "Learn more about this instance:" -msgstr "Aprende más sobre esta intancia:" +#, python-format +msgid "Learn more about %(site_name)s:" +msgstr "Más información sobre %(site_name)s:" #: bookwyrm/templates/email/password_reset/html_content.html:6 #: bookwyrm/templates/email/password_reset/text_content.html:4 @@ -1198,7 +1198,7 @@ msgstr "Un poco sobre ti" #: bookwyrm/templates/get_started/profile.html:32 #: bookwyrm/templates/preferences/edit_user.html:27 msgid "Avatar:" -msgstr "" +msgstr "Avatar:" #: bookwyrm/templates/get_started/profile.html:42 #: bookwyrm/templates/preferences/edit_user.html:110 @@ -1323,13 +1323,13 @@ msgstr "Libro" #: bookwyrm/templates/import/import_status.html:122 #: bookwyrm/templates/shelf/shelf.html:128 -#: bookwyrm/templates/shelf/shelf.html:148 +#: bookwyrm/templates/shelf/shelf.html:150 msgid "Title" msgstr "Título" #: bookwyrm/templates/import/import_status.html:125 #: bookwyrm/templates/shelf/shelf.html:129 -#: bookwyrm/templates/shelf/shelf.html:151 +#: bookwyrm/templates/shelf/shelf.html:153 msgid "Author" msgstr "Autor/Autora" @@ -1338,8 +1338,8 @@ msgid "Imported" msgstr "Importado" #: bookwyrm/templates/import/tooltip.html:6 -msgid "You can download your GoodReads data from the Import/Export page of your GoodReads account." -msgstr "Puedes descargar tus datos de GoodReads de la Página de Exportación/Importación de tu cuenta de GoodReads." +msgid "You can download your Goodreads data from the Import/Export page of your Goodreads account." +msgstr "Puede descargar sus datos de Goodreads desde la página de Importación/Exportación de su cuenta de Goodreads." #: bookwyrm/templates/invite.html:4 bookwyrm/templates/invite.html:8 #: bookwyrm/templates/login.html:49 @@ -1354,7 +1354,7 @@ msgstr "Permiso denegado" msgid "Sorry! This invite code is no longer valid." msgstr "¡Disculpa! Este código de invitación no queda válido." -#: bookwyrm/templates/landing/about.html:7 +#: bookwyrm/templates/landing/about.html:7 bookwyrm/templates/layout.html:230 #, python-format msgid "About %(site_name)s" msgstr "Sobre %(site_name)s" @@ -1442,7 +1442,7 @@ msgstr "Invitaciones" #: bookwyrm/templates/layout.html:132 msgid "Admin" -msgstr "" +msgstr "Administrador" #: bookwyrm/templates/layout.html:139 msgid "Log out" @@ -1485,10 +1485,6 @@ msgstr "Status publicado exitosamente" msgid "Error posting status" msgstr "Error en publicar status" -#: bookwyrm/templates/layout.html:230 -msgid "About this instance" -msgstr "Sobre esta instancia" - #: bookwyrm/templates/layout.html:234 msgid "Contact site admin" msgstr "Contactarse con administradores del sitio" @@ -1594,7 +1590,7 @@ msgstr "Cualquier usuario puede sugerir libros, en cuanto lo hayas aprobado" #: bookwyrm/templates/lists/form.html:31 msgctxt "curation type" msgid "Open" -msgstr "" +msgstr "Abrir" #: bookwyrm/templates/lists/form.html:32 msgid "Anyone can add books to this list" @@ -1704,12 +1700,12 @@ msgstr "Más sobre este sitio" #: bookwyrm/templates/notifications/items/add.html:24 #, python-format msgid "added %(book_title)s to your list \"%(list_name)s\"" -msgstr "" +msgstr "agregó %(book_title)s a su lista «%(list_name)s»" #: bookwyrm/templates/notifications/items/add.html:31 #, python-format msgid "suggested adding %(book_title)s to your list \"%(list_name)s\"" -msgstr "" +msgstr "sugirió agregar %(book_title)s a su lista «%(list_name)s»" #: bookwyrm/templates/notifications/items/boost.html:19 #, python-format @@ -1733,23 +1729,23 @@ msgstr "respaldó tu status" #: bookwyrm/templates/notifications/items/fav.html:19 #, python-format -msgid "favorited your review of %(book_title)s" +msgid "liked your review of %(book_title)s" msgstr "le gustó tu reseña de %(book_title)s" #: bookwyrm/templates/notifications/items/fav.html:25 #, python-format -msgid "favorited your comment on%(book_title)s" -msgstr "" +msgid "liked your comment on%(book_title)s" +msgstr "le gustó tu comentario sobre %(book_title)s" #: bookwyrm/templates/notifications/items/fav.html:31 #, python-format -msgid "favorited your quote from %(book_title)s" +msgid "liked your quote from %(book_title)s" msgstr "le gustó tu cita de %(book_title)s" #: bookwyrm/templates/notifications/items/fav.html:37 #, python-format -msgid "favorited your status" -msgstr "le gustó tu status" +msgid "liked your status" +msgstr "le gustó tu estado" #: bookwyrm/templates/notifications/items/follow.html:15 msgid "followed you" @@ -2004,7 +2000,7 @@ msgstr "Editar anuncio" #: bookwyrm/templates/settings/announcements/announcement.html:35 msgid "Visible:" -msgstr "" +msgstr "Visible:" #: bookwyrm/templates/settings/announcements/announcement.html:38 msgid "True" @@ -2078,7 +2074,7 @@ msgstr "Fecha final" #: bookwyrm/templates/settings/users/user_admin.html:34 #: bookwyrm/templates/settings/users/user_info.html:20 msgid "Status" -msgstr "" +msgstr "Estado" #: bookwyrm/templates/settings/announcements/announcements.html:48 msgid "active" @@ -2110,7 +2106,7 @@ msgstr "Activos este mes" #: bookwyrm/templates/settings/dashboard/dashboard.html:27 msgid "Statuses" -msgstr "" +msgstr "Estados" #: bookwyrm/templates/settings/dashboard/dashboard.html:33 #: bookwyrm/templates/settings/dashboard/works_chart.html:11 @@ -2157,11 +2153,11 @@ msgstr "Actividad de status" #: bookwyrm/templates/settings/dashboard/dashboard.html:118 msgid "Works created" -msgstr "" +msgstr "Obras creadas" #: bookwyrm/templates/settings/dashboard/registration_chart.html:10 msgid "Registrations" -msgstr "" +msgstr "Inscripciones" #: bookwyrm/templates/settings/dashboard/status_chart.html:11 msgid "Statuses posted" @@ -2238,13 +2234,13 @@ msgstr "Instancia:" #: bookwyrm/templates/settings/federation/instance.html:28 #: bookwyrm/templates/settings/users/user_info.html:106 msgid "Status:" -msgstr "" +msgstr "Estado:" #: bookwyrm/templates/settings/federation/edit_instance.html:52 #: bookwyrm/templates/settings/federation/instance.html:22 #: bookwyrm/templates/settings/users/user_info.html:100 msgid "Software:" -msgstr "" +msgstr "Software:" #: bookwyrm/templates/settings/federation/edit_instance.html:61 #: bookwyrm/templates/settings/federation/instance.html:25 @@ -2297,6 +2293,7 @@ msgid "Notes" msgstr "Notas" #: bookwyrm/templates/settings/federation/instance.html:75 +#: bookwyrm/templates/snippets/status/status_options.html:24 msgid "Edit" msgstr "Editar" @@ -2357,7 +2354,7 @@ msgstr "Nombre de instancia" #: bookwyrm/templates/settings/federation/instance_list.html:40 msgid "Software" -msgstr "" +msgstr "Software" #: bookwyrm/templates/settings/federation/instance_list.html:63 msgid "No instances found" @@ -2636,8 +2633,8 @@ msgid "Short description:" msgstr "Descripción corta:" #: bookwyrm/templates/settings/site.html:37 -msgid "Used when the instance is previewed on joinbookwyrm.com. Does not support html or markdown." -msgstr "Utilizado cuando la instancia se ve de una vista previa en joinbookwyrm.com. No es compatible con html o markdown." +msgid "Used when the instance is previewed on joinbookwyrm.com. Does not support HTML or Markdown." +msgstr "Se utiliza cuando se obtiene una vista previa de la instancia en joinbookwyrm.com. No es compatible con HTML ni Markdown." #: bookwyrm/templates/settings/site.html:41 msgid "Code of conduct:" @@ -2649,7 +2646,7 @@ msgstr "Política de privacidad:" #: bookwyrm/templates/settings/site.html:57 msgid "Logo:" -msgstr "" +msgstr "Logo:" #: bookwyrm/templates/settings/site.html:61 msgid "Logo small:" @@ -2657,7 +2654,7 @@ msgstr "Logo pequeño:" #: bookwyrm/templates/settings/site.html:65 msgid "Favicon:" -msgstr "" +msgstr "Favicon:" #: bookwyrm/templates/settings/site.html:77 msgid "Support link:" @@ -2760,7 +2757,7 @@ msgstr "Ver perfil de usuario" #: bookwyrm/templates/settings/users/user_info.html:36 msgid "Local" -msgstr "" +msgstr "Local" #: bookwyrm/templates/settings/users/user_info.html:38 msgid "Remote" @@ -2811,7 +2808,7 @@ msgid "Permanently deleted" msgstr "Eliminado permanentemente" #: bookwyrm/templates/settings/users/user_moderation_actions.html:13 -#: bookwyrm/templates/snippets/status/status_options.html:35 +#: bookwyrm/templates/snippets/status/status_options.html:32 #: bookwyrm/templates/snippets/user_options.html:13 msgid "Send direct message" msgstr "Enviar mensaje directo" @@ -2848,8 +2845,8 @@ msgstr "Crear estante" #, python-format msgid "%(formatted_count)s book" msgid_plural "%(formatted_count)s books" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "%(formatted_count)s libro" +msgstr[1] "%(formatted_count)s libros" #: bookwyrm/templates/shelf/shelf.html:84 #, python-format @@ -2864,22 +2861,22 @@ msgstr "Editar estante" msgid "Delete shelf" msgstr "Eliminar estante" -#: bookwyrm/templates/shelf/shelf.html:130 -#: bookwyrm/templates/shelf/shelf.html:154 +#: bookwyrm/templates/shelf/shelf.html:132 +#: bookwyrm/templates/shelf/shelf.html:158 msgid "Shelved" msgstr "Archivado" -#: bookwyrm/templates/shelf/shelf.html:131 -#: bookwyrm/templates/shelf/shelf.html:158 +#: bookwyrm/templates/shelf/shelf.html:133 +#: bookwyrm/templates/shelf/shelf.html:161 msgid "Started" msgstr "Empezado" -#: bookwyrm/templates/shelf/shelf.html:132 -#: bookwyrm/templates/shelf/shelf.html:161 +#: bookwyrm/templates/shelf/shelf.html:134 +#: bookwyrm/templates/shelf/shelf.html:164 msgid "Finished" msgstr "Terminado" -#: bookwyrm/templates/shelf/shelf.html:187 +#: bookwyrm/templates/shelf/shelf.html:190 msgid "This shelf is empty." msgstr "Este estante está vacio." @@ -2892,8 +2889,8 @@ msgstr "Publicado por %(username)s" #, python-format msgid "and %(remainder_count_display)s other" msgid_plural "and %(remainder_count_display)s others" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "y %(remainder_count_display)s otro" +msgstr[1] "y %(remainder_count_display)s otros" #: bookwyrm/templates/snippets/book_cover.html:61 msgid "No cover" @@ -2926,22 +2923,22 @@ msgstr "Cita" msgid "Some thoughts on the book" msgstr "Algunos pensamientos sobre el libro" -#: bookwyrm/templates/snippets/create_status/comment.html:26 +#: bookwyrm/templates/snippets/create_status/comment.html:27 #: bookwyrm/templates/snippets/reading_modals/progress_update_modal.html:15 msgid "Progress:" msgstr "Progreso:" -#: bookwyrm/templates/snippets/create_status/comment.html:52 +#: bookwyrm/templates/snippets/create_status/comment.html:53 #: bookwyrm/templates/snippets/progress_field.html:18 msgid "pages" msgstr "páginas" -#: bookwyrm/templates/snippets/create_status/comment.html:58 +#: bookwyrm/templates/snippets/create_status/comment.html:59 #: bookwyrm/templates/snippets/progress_field.html:23 msgid "percent" msgstr "por ciento" -#: bookwyrm/templates/snippets/create_status/comment.html:65 +#: bookwyrm/templates/snippets/create_status/comment.html:66 #, python-format msgid "of %(pages)s pages" msgstr "de %(pages)s páginas" @@ -2969,7 +2966,7 @@ msgstr "¡Advertencia, ya vienen spoilers!" msgid "Include spoiler alert" msgstr "Incluir alerta de spoiler" -#: bookwyrm/templates/snippets/create_status/layout.html:41 +#: bookwyrm/templates/snippets/create_status/layout.html:48 #: bookwyrm/templates/snippets/reading_modals/form.html:7 msgid "Comment:" msgstr "Comentario:" @@ -3163,12 +3160,12 @@ msgstr "Has leído %(read_count)s de %(goal_count)s libros< msgid "%(username)s has read %(read_count)s of %(goal_count)s books." msgstr "%(username)s ha leído %(read_count)s de %(goal_count)s libros." -#: bookwyrm/templates/snippets/page_text.html:4 +#: bookwyrm/templates/snippets/page_text.html:8 #, python-format msgid "page %(page)s of %(total_pages)s" msgstr "página %(page)s de %(total_pages)s" -#: bookwyrm/templates/snippets/page_text.html:6 +#: bookwyrm/templates/snippets/page_text.html:14 #, python-format msgid "page %(page)s" msgstr "página %(page)s" @@ -3319,7 +3316,7 @@ msgstr "(Página %(page)s)" #: bookwyrm/templates/snippets/status/content_status.html:103 #, python-format msgid "(%(percent)s%%)" -msgstr "" +msgstr "(%(percent)s%%)" #: bookwyrm/templates/snippets/status/content_status.html:125 msgid "Open image in new window" @@ -3329,6 +3326,11 @@ msgstr "Abrir imagen en una nueva ventana" msgid "Hide status" msgstr "Ocultar status" +#: bookwyrm/templates/snippets/status/header.html:45 +#, python-format +msgid "edited %(date)s" +msgstr "editado %(date)s" + #: bookwyrm/templates/snippets/status/headers/comment.html:2 #, python-format msgid "commented on %(book)s" @@ -3393,10 +3395,6 @@ msgstr "respaldó" msgid "More options" msgstr "Más opciones" -#: bookwyrm/templates/snippets/status/status_options.html:26 -msgid "Delete & re-draft" -msgstr "Eliminar y recomponer" - #: bookwyrm/templates/snippets/suggested_users.html:16 #, python-format msgid "%(mutuals)s follower you follow" @@ -3561,7 +3559,7 @@ msgstr "Archivo excede el tamaño máximo: 10MB" #: bookwyrm/templatetags/utilities.py:31 #, python-format msgid "%(title)s: %(subtitle)s" -msgstr "" +msgstr "%(title)s: %(subtitle)s" #: bookwyrm/views/import_data.py:67 msgid "Not a valid csv file" diff --git a/locale/fr_FR/LC_MESSAGES/django.mo b/locale/fr_FR/LC_MESSAGES/django.mo index a0d40fae6..5e8dfa664 100644 Binary files a/locale/fr_FR/LC_MESSAGES/django.mo and b/locale/fr_FR/LC_MESSAGES/django.mo differ diff --git a/locale/fr_FR/LC_MESSAGES/django.po b/locale/fr_FR/LC_MESSAGES/django.po index 699f8e8ba..141bd894b 100644 --- a/locale/fr_FR/LC_MESSAGES/django.po +++ b/locale/fr_FR/LC_MESSAGES/django.po @@ -2,8 +2,8 @@ msgid "" msgstr "" "Project-Id-Version: bookwyrm\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-10-06 23:57+0000\n" -"PO-Revision-Date: 2021-10-08 10:19\n" +"POT-Creation-Date: 2021-10-15 22:03+0000\n" +"PO-Revision-Date: 2021-10-16 14:36\n" "Last-Translator: Mouse Reeve \n" "Language-Team: French\n" "Language: fr\n" @@ -54,8 +54,8 @@ msgstr "Ordre de la liste" msgid "Book Title" msgstr "Titre du livre" -#: bookwyrm/forms.py:328 bookwyrm/templates/shelf/shelf.html:134 -#: bookwyrm/templates/shelf/shelf.html:165 +#: bookwyrm/forms.py:328 bookwyrm/templates/shelf/shelf.html:136 +#: bookwyrm/templates/shelf/shelf.html:168 #: bookwyrm/templates/snippets/create_status/review.html:33 msgid "Rating" msgstr "Note" @@ -151,45 +151,49 @@ msgstr "nom du compte :" msgid "A user with that username already exists." msgstr "Ce nom est déjà associé à un compte." -#: bookwyrm/settings.py:117 +#: bookwyrm/settings.py:118 msgid "Home Timeline" msgstr "Mon fil d’actualité" -#: bookwyrm/settings.py:117 +#: bookwyrm/settings.py:118 msgid "Home" msgstr "Accueil" -#: bookwyrm/settings.py:118 +#: bookwyrm/settings.py:119 msgid "Books Timeline" msgstr "" -#: bookwyrm/settings.py:118 bookwyrm/templates/search/layout.html:21 +#: bookwyrm/settings.py:119 bookwyrm/templates/search/layout.html:21 #: bookwyrm/templates/search/layout.html:42 #: bookwyrm/templates/user/layout.html:81 msgid "Books" msgstr "Livres" -#: bookwyrm/settings.py:164 +#: bookwyrm/settings.py:165 msgid "English" msgstr "English" -#: bookwyrm/settings.py:165 +#: bookwyrm/settings.py:166 msgid "Deutsch (German)" msgstr "Deutsch" -#: bookwyrm/settings.py:166 +#: bookwyrm/settings.py:167 msgid "Español (Spanish)" msgstr "Español" -#: bookwyrm/settings.py:167 +#: bookwyrm/settings.py:168 msgid "Français (French)" msgstr "Français" -#: bookwyrm/settings.py:168 +#: bookwyrm/settings.py:169 +msgid "Português - Brasil (Brazilian Portuguese)" +msgstr "" + +#: bookwyrm/settings.py:170 msgid "简体中文 (Simplified Chinese)" msgstr "简化字" -#: bookwyrm/settings.py:169 +#: bookwyrm/settings.py:171 msgid "繁體中文 (Traditional Chinese)" msgstr "Infos supplémentaires :" @@ -669,11 +673,6 @@ msgstr "Langue :" msgid "Search editions" msgstr "Rechercher des éditions" -#: bookwyrm/templates/book/publisher_info.html:21 -#, python-format -msgid "%(format)s" -msgstr "%(format)s" - #: bookwyrm/templates/book/publisher_info.html:23 #, python-format msgid "%(format)s, %(pages)s pages" @@ -753,8 +752,8 @@ msgid "Help" msgstr "Aide" #: bookwyrm/templates/compose.html:5 bookwyrm/templates/compose.html:8 -msgid "Compose status" -msgstr "Rédiger un statut" +msgid "Edit status" +msgstr "" #: bookwyrm/templates/confirm_email/confirm_email.html:4 msgid "Confirm email" @@ -888,6 +887,26 @@ msgstr "Comptes BookWyrm" msgid "All known users" msgstr "Tous les comptes connus" +#: bookwyrm/templates/discover/card-header.html:9 +#, python-format +msgid "%(username)s rated %(book_title)s" +msgstr "" + +#: bookwyrm/templates/discover/card-header.html:13 +#, python-format +msgid "%(username)s reviewed %(book_title)s" +msgstr "" + +#: bookwyrm/templates/discover/card-header.html:17 +#, python-format +msgid "%(username)s commented on %(book_title)s" +msgstr "" + +#: bookwyrm/templates/discover/card-header.html:21 +#, python-format +msgid "%(username)s quoted %(book_title)s" +msgstr "" + #: bookwyrm/templates/discover/discover.html:4 #: bookwyrm/templates/discover/discover.html:10 #: bookwyrm/templates/layout.html:78 @@ -899,28 +918,8 @@ msgstr "Découvrir" msgid "See what's new in the local %(site_name)s community" msgstr "Voir les nouveautés de la communauté locale %(site_name)s" -#: bookwyrm/templates/discover/large-book.html:46 -#: bookwyrm/templates/discover/small-book.html:32 -msgid "rated" -msgstr "a noté" - -#: bookwyrm/templates/discover/large-book.html:48 -#: bookwyrm/templates/discover/small-book.html:34 -msgid "reviewed" -msgstr "a écrit une critique de" - -#: bookwyrm/templates/discover/large-book.html:50 -#: bookwyrm/templates/discover/small-book.html:36 -msgid "commented on" -msgstr "a commenté" - #: bookwyrm/templates/discover/large-book.html:52 -#: bookwyrm/templates/discover/small-book.html:38 -msgid "quoted" -msgstr "a cité" - -#: bookwyrm/templates/discover/large-book.html:68 -#: bookwyrm/templates/discover/small-book.html:52 +#: bookwyrm/templates/discover/small-book.html:36 msgid "View status" msgstr "Afficher tous les status" @@ -974,8 +973,8 @@ msgstr "S’enregistrer maintenant" #: bookwyrm/templates/email/invite/html_content.html:15 #, python-format -msgid "Learn more about this instance." -msgstr "En savoir plus sur cette instance." +msgid "Learn more about %(site_name)s." +msgstr "" #: bookwyrm/templates/email/invite/text_content.html:4 #, python-format @@ -983,8 +982,9 @@ msgid "You're invited to join %(site_name)s! Click the link below to create an a msgstr "Vous avez reçu une invitation à rejoindre %(site_name)s ! Cliquez le lien suivant pour créer un compte." #: bookwyrm/templates/email/invite/text_content.html:8 -msgid "Learn more about this instance:" -msgstr "En savoir plus sur cete instance :" +#, python-format +msgid "Learn more about %(site_name)s:" +msgstr "" #: bookwyrm/templates/email/password_reset/html_content.html:6 #: bookwyrm/templates/email/password_reset/text_content.html:4 @@ -1323,13 +1323,13 @@ msgstr "Livre" #: bookwyrm/templates/import/import_status.html:122 #: bookwyrm/templates/shelf/shelf.html:128 -#: bookwyrm/templates/shelf/shelf.html:148 +#: bookwyrm/templates/shelf/shelf.html:150 msgid "Title" msgstr "Titre" #: bookwyrm/templates/import/import_status.html:125 #: bookwyrm/templates/shelf/shelf.html:129 -#: bookwyrm/templates/shelf/shelf.html:151 +#: bookwyrm/templates/shelf/shelf.html:153 msgid "Author" msgstr "Auteur/autrice" @@ -1338,8 +1338,8 @@ msgid "Imported" msgstr "Importé" #: bookwyrm/templates/import/tooltip.html:6 -msgid "You can download your GoodReads data from the Import/Export page of your GoodReads account." -msgstr "Vous pouvez télécharger vos données GoodReads depuis la page Importation/Exportation de votre compte GoodRead." +msgid "You can download your Goodreads data from the Import/Export page of your Goodreads account." +msgstr "" #: bookwyrm/templates/invite.html:4 bookwyrm/templates/invite.html:8 #: bookwyrm/templates/login.html:49 @@ -1354,7 +1354,7 @@ msgstr "Autorisation refusée" msgid "Sorry! This invite code is no longer valid." msgstr "Cette invitation n’est plus valide ; désolé !" -#: bookwyrm/templates/landing/about.html:7 +#: bookwyrm/templates/landing/about.html:7 bookwyrm/templates/layout.html:230 #, python-format msgid "About %(site_name)s" msgstr "À propos de %(site_name)s" @@ -1485,10 +1485,6 @@ msgstr "Publié !" msgid "Error posting status" msgstr "Erreur lors de la publication" -#: bookwyrm/templates/layout.html:230 -msgid "About this instance" -msgstr "À propos de cette instance" - #: bookwyrm/templates/layout.html:234 msgid "Contact site admin" msgstr "Contacter l’administrateur du site" @@ -1733,23 +1729,23 @@ msgstr "a partagé votre statut" #: bookwyrm/templates/notifications/items/fav.html:19 #, python-format -msgid "favorited your review of %(book_title)s" -msgstr "a ajouté votre critique de %(book_title)s à ses favoris" +msgid "liked your review of %(book_title)s" +msgstr "" #: bookwyrm/templates/notifications/items/fav.html:25 #, python-format -msgid "favorited your comment on%(book_title)s" +msgid "liked your comment on%(book_title)s" msgstr "" #: bookwyrm/templates/notifications/items/fav.html:31 #, python-format -msgid "favorited your quote from %(book_title)s" -msgstr "a ajouté votre citation de %(book_title)s à ses favoris" +msgid "liked your quote from %(book_title)s" +msgstr "" #: bookwyrm/templates/notifications/items/fav.html:37 #, python-format -msgid "favorited your status" -msgstr "a ajouté votre statut à ses favoris" +msgid "liked your status" +msgstr "" #: bookwyrm/templates/notifications/items/follow.html:15 msgid "followed you" @@ -1904,7 +1900,7 @@ msgstr "" #: bookwyrm/templates/preferences/edit_user.html:76 msgid "Show suggested users:" -msgstr "" +msgstr "Afficher les utilisateurs suggérés :" #: bookwyrm/templates/preferences/edit_user.html:85 #, python-format @@ -1917,7 +1913,7 @@ msgstr "Fuseau horaire préféré" #: bookwyrm/templates/preferences/edit_user.html:116 msgid "Default post privacy:" -msgstr "" +msgstr "Niveau de confidentialité des messages par défaut :" #: bookwyrm/templates/preferences/layout.html:11 msgid "Account" @@ -1930,12 +1926,12 @@ msgstr "Relations" #: bookwyrm/templates/reading_progress/finish.html:5 #, python-format msgid "Finish \"%(book_title)s\"" -msgstr "" +msgstr "Terminer \"%(book_title)s\"" #: bookwyrm/templates/reading_progress/start.html:5 #, python-format msgid "Start \"%(book_title)s\"" -msgstr "" +msgstr "Commencer \"%(book_title)s\"" #: bookwyrm/templates/reading_progress/want.html:5 #, python-format @@ -2038,11 +2034,11 @@ msgstr "Ajouter une annonce" #: bookwyrm/templates/settings/announcements/announcement_form.html:16 msgid "Preview:" -msgstr "" +msgstr "Aperçu :" #: bookwyrm/templates/settings/announcements/announcement_form.html:23 msgid "Content:" -msgstr "" +msgstr "Contenu :" #: bookwyrm/templates/settings/announcements/announcement_form.html:30 msgid "Event date:" @@ -2141,11 +2137,11 @@ msgstr "" #: bookwyrm/templates/settings/dashboard/dashboard.html:87 msgid "Days" -msgstr "" +msgstr "Jours" #: bookwyrm/templates/settings/dashboard/dashboard.html:88 msgid "Weeks" -msgstr "" +msgstr "Semaines" #: bookwyrm/templates/settings/dashboard/dashboard.html:106 msgid "User signup activity" @@ -2161,7 +2157,7 @@ msgstr "" #: bookwyrm/templates/settings/dashboard/registration_chart.html:10 msgid "Registrations" -msgstr "" +msgstr "Inscriptions" #: bookwyrm/templates/settings/dashboard/status_chart.html:11 msgid "Statuses posted" @@ -2188,7 +2184,7 @@ msgstr "" #: bookwyrm/templates/settings/email_blocklist/email_blocklist.html:18 msgid "When someone tries to register with an email from this domain, no account will be created. The registration process will appear to have worked." -msgstr "" +msgstr "Quand quelqu'un essaiera de s'inscrire avec un e-mail de ce domaine, aucun compte ne sera créé. Le processus d'inscription semblera avoir fonctionné." #: bookwyrm/templates/settings/email_blocklist/email_blocklist.html:25 msgid "Domain" @@ -2197,14 +2193,14 @@ msgstr "" #: bookwyrm/templates/settings/email_blocklist/email_blocklist.html:29 #: bookwyrm/templates/settings/ip_blocklist/ip_blocklist.html:27 msgid "Options" -msgstr "" +msgstr "Options" #: bookwyrm/templates/settings/email_blocklist/email_blocklist.html:38 #, python-format msgid "%(display_count)s user" msgid_plural "%(display_count)s users" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "%(display_count)s utilisateur" +msgstr[1] "%(display_count)s utilisateurs" #: bookwyrm/templates/settings/email_blocklist/email_blocklist.html:59 msgid "No email domains currently blocked" @@ -2297,6 +2293,7 @@ msgid "Notes" msgstr "Remarques" #: bookwyrm/templates/settings/federation/instance.html:75 +#: bookwyrm/templates/snippets/status/status_options.html:24 msgid "Edit" msgstr "Modifier" @@ -2361,7 +2358,7 @@ msgstr "Logiciel" #: bookwyrm/templates/settings/federation/instance_list.html:63 msgid "No instances found" -msgstr "" +msgstr "Aucune instance trouvée" #: bookwyrm/templates/settings/invites/manage_invite_requests.html:4 #: bookwyrm/templates/settings/invites/manage_invite_requests.html:11 @@ -2472,33 +2469,33 @@ msgstr "Aucune invitation active" #: bookwyrm/templates/settings/ip_blocklist/ip_address_form.html:5 #: bookwyrm/templates/settings/ip_blocklist/ip_blocklist.html:10 msgid "Add IP address" -msgstr "" +msgstr "Ajouter une adresse IP" #: bookwyrm/templates/settings/ip_blocklist/ip_address_form.html:11 msgid "Use IP address blocks with caution, and consider using blocks only temporarily, as IP addresses are often shared or change hands. If you block your own IP, you will not be able to access this page." -msgstr "" +msgstr "Bloquez des adresses IP avec précaution, voire de façon temporaire, car les adresses IP sont souvent partagées, ou changent de main. Si vous bloquez votre propre adresse IP, vous ne pourrez plus accéder à cette page." #: bookwyrm/templates/settings/ip_blocklist/ip_address_form.html:18 msgid "IP Address:" -msgstr "" +msgstr "Adresse IP :" #: bookwyrm/templates/settings/ip_blocklist/ip_blocklist.html:5 #: bookwyrm/templates/settings/ip_blocklist/ip_blocklist.html:7 #: bookwyrm/templates/settings/layout.html:63 msgid "IP Address Blocklist" -msgstr "" +msgstr "Liste des adresses IP bloquées" #: bookwyrm/templates/settings/ip_blocklist/ip_blocklist.html:18 msgid "Any traffic from this IP address will get a 404 response when trying to access any part of the application." -msgstr "" +msgstr "Tout trafic provenant de cette adresse IP obtiendra une réponse 404 en essayant d'accéder à n'importe quelle partie de l'application." #: bookwyrm/templates/settings/ip_blocklist/ip_blocklist.html:24 msgid "Address" -msgstr "" +msgstr "Adresse" #: bookwyrm/templates/settings/ip_blocklist/ip_blocklist.html:46 msgid "No IP addresses currently blocked" -msgstr "" +msgstr "Aucune adresse IP n'est actuellement bloquée" #: bookwyrm/templates/settings/ip_blocklist/ip_tooltip.html:6 msgid "You can block IP ranges using CIDR syntax." @@ -2636,8 +2633,8 @@ msgid "Short description:" msgstr "Description courte :" #: bookwyrm/templates/settings/site.html:37 -msgid "Used when the instance is previewed on joinbookwyrm.com. Does not support html or markdown." -msgstr "Utilisé lorsque l'instance est prévisualisée sur joinbookwyrm.com. Ne supporte pas html ou markdown." +msgid "Used when the instance is previewed on joinbookwyrm.com. Does not support HTML or Markdown." +msgstr "" #: bookwyrm/templates/settings/site.html:41 msgid "Code of conduct:" @@ -2772,7 +2769,7 @@ msgstr "Détails du compte" #: bookwyrm/templates/settings/users/user_info.html:51 msgid "Email:" -msgstr "Email:" +msgstr "Email :" #: bookwyrm/templates/settings/users/user_info.html:61 msgid "(View reports)" @@ -2811,7 +2808,7 @@ msgid "Permanently deleted" msgstr "Supprimé définitivement" #: bookwyrm/templates/settings/users/user_moderation_actions.html:13 -#: bookwyrm/templates/snippets/status/status_options.html:35 +#: bookwyrm/templates/snippets/status/status_options.html:32 #: bookwyrm/templates/snippets/user_options.html:13 msgid "Send direct message" msgstr "Envoyer un message direct" @@ -2864,22 +2861,22 @@ msgstr "Modifier l’étagère" msgid "Delete shelf" msgstr "Supprimer l’étagère" -#: bookwyrm/templates/shelf/shelf.html:130 -#: bookwyrm/templates/shelf/shelf.html:154 +#: bookwyrm/templates/shelf/shelf.html:132 +#: bookwyrm/templates/shelf/shelf.html:158 msgid "Shelved" msgstr "Date d’ajout" -#: bookwyrm/templates/shelf/shelf.html:131 -#: bookwyrm/templates/shelf/shelf.html:158 +#: bookwyrm/templates/shelf/shelf.html:133 +#: bookwyrm/templates/shelf/shelf.html:161 msgid "Started" msgstr "Commencé" -#: bookwyrm/templates/shelf/shelf.html:132 -#: bookwyrm/templates/shelf/shelf.html:161 +#: bookwyrm/templates/shelf/shelf.html:134 +#: bookwyrm/templates/shelf/shelf.html:164 msgid "Finished" msgstr "Terminé" -#: bookwyrm/templates/shelf/shelf.html:187 +#: bookwyrm/templates/shelf/shelf.html:190 msgid "This shelf is empty." msgstr "Cette étagère est vide" @@ -2892,8 +2889,8 @@ msgstr "Publiée par %(username)s" #, python-format msgid "and %(remainder_count_display)s other" msgid_plural "and %(remainder_count_display)s others" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "et %(remainder_count_display)s autre" +msgstr[1] "et %(remainder_count_display)s autres" #: bookwyrm/templates/snippets/book_cover.html:61 msgid "No cover" @@ -2926,22 +2923,22 @@ msgstr "Citation" msgid "Some thoughts on the book" msgstr "" -#: bookwyrm/templates/snippets/create_status/comment.html:26 +#: bookwyrm/templates/snippets/create_status/comment.html:27 #: bookwyrm/templates/snippets/reading_modals/progress_update_modal.html:15 msgid "Progress:" msgstr "Progression :" -#: bookwyrm/templates/snippets/create_status/comment.html:52 +#: bookwyrm/templates/snippets/create_status/comment.html:53 #: bookwyrm/templates/snippets/progress_field.html:18 msgid "pages" msgstr "pages" -#: bookwyrm/templates/snippets/create_status/comment.html:58 +#: bookwyrm/templates/snippets/create_status/comment.html:59 #: bookwyrm/templates/snippets/progress_field.html:23 msgid "percent" msgstr "pourcent" -#: bookwyrm/templates/snippets/create_status/comment.html:65 +#: bookwyrm/templates/snippets/create_status/comment.html:66 #, python-format msgid "of %(pages)s pages" msgstr "sur %(pages)s pages" @@ -2969,7 +2966,7 @@ msgstr "Attention spoilers !" msgid "Include spoiler alert" msgstr "Afficher une alerte spoiler" -#: bookwyrm/templates/snippets/create_status/layout.html:41 +#: bookwyrm/templates/snippets/create_status/layout.html:48 #: bookwyrm/templates/snippets/reading_modals/form.html:7 msgid "Comment:" msgstr "Commentaire :" @@ -3163,12 +3160,12 @@ msgstr "Vous avez lu %(read_count)s sur %(goal_count)s livr msgid "%(username)s has read %(read_count)s of %(goal_count)s books." msgstr "%(username)s a lu %(read_count)s sur %(goal_count)s livres." -#: bookwyrm/templates/snippets/page_text.html:4 +#: bookwyrm/templates/snippets/page_text.html:8 #, python-format msgid "page %(page)s of %(total_pages)s" msgstr "page %(page)s sur %(total_pages)s pages" -#: bookwyrm/templates/snippets/page_text.html:6 +#: bookwyrm/templates/snippets/page_text.html:14 #, python-format msgid "page %(page)s" msgstr "page %(page)s" @@ -3329,10 +3326,15 @@ msgstr "Ouvrir l’image dans une nouvelle fenêtre" msgid "Hide status" msgstr "" +#: bookwyrm/templates/snippets/status/header.html:45 +#, python-format +msgid "edited %(date)s" +msgstr "" + #: bookwyrm/templates/snippets/status/headers/comment.html:2 #, python-format msgid "commented on %(book)s" -msgstr "" +msgstr "a commenté %(book)s" #: bookwyrm/templates/snippets/status/headers/note.html:15 #, python-format @@ -3342,32 +3344,32 @@ msgstr "a répondu au statut de %(book)s" -msgstr "" +msgstr "a cité un passage de %(book)s" #: bookwyrm/templates/snippets/status/headers/rating.html:3 #, python-format msgid "rated %(book)s:" -msgstr "" +msgstr "a noté %(book)s :" #: bookwyrm/templates/snippets/status/headers/read.html:7 #, python-format msgid "finished reading %(book)s" -msgstr "" +msgstr "a terminé %(book)s" #: bookwyrm/templates/snippets/status/headers/reading.html:7 #, python-format msgid "started reading %(book)s" -msgstr "" +msgstr "a commencé %(book)s" #: bookwyrm/templates/snippets/status/headers/review.html:3 #, python-format msgid "reviewed %(book)s" -msgstr "" +msgstr "a critiqué %(book)s" #: bookwyrm/templates/snippets/status/headers/to_read.html:7 #, python-format msgid "%(username)s wants to read %(book)s" -msgstr "" +msgstr "%(username)s veut lire %(book)s" #: bookwyrm/templates/snippets/status/layout.html:24 #: bookwyrm/templates/snippets/status/status_options.html:17 @@ -3393,10 +3395,6 @@ msgstr "a partagé" msgid "More options" msgstr "Plus d’options" -#: bookwyrm/templates/snippets/status/status_options.html:26 -msgid "Delete & re-draft" -msgstr "Supprimer & recommencer la rédaction" - #: bookwyrm/templates/snippets/suggested_users.html:16 #, python-format msgid "%(mutuals)s follower you follow" @@ -3414,7 +3412,7 @@ msgstr[1] "%(shared_books)s livres sur vos étagères" #: bookwyrm/templates/snippets/suggested_users.html:31 #: bookwyrm/templates/user/user_preview.html:36 msgid "Follows you" -msgstr "" +msgstr "Vous suit" #: bookwyrm/templates/snippets/switch_edition_button.html:5 msgid "Switch to this edition" @@ -3552,7 +3550,7 @@ msgstr[1] "%(mutuals_display)s abonné(e)s que vous suivez" #: bookwyrm/templates/user/user_preview.html:38 msgid "No followers you follow" -msgstr "" +msgstr "Aucun·e abonné·e que vous suivez" #: bookwyrm/templates/widgets/clearable_file_input_with_warning.html:28 msgid "File exceeds maximum size: 10MB" @@ -3569,7 +3567,7 @@ msgstr "Fichier CSV non valide" #: bookwyrm/views/login.py:69 msgid "Username or password are incorrect" -msgstr "" +msgstr "Identifiant ou mot de passe incorrect" #: bookwyrm/views/password.py:32 msgid "No user with that email address was found." diff --git a/locale/pt_BR/LC_MESSAGES/django.mo b/locale/pt_BR/LC_MESSAGES/django.mo index 1942ada36..1ebe52800 100644 Binary files a/locale/pt_BR/LC_MESSAGES/django.mo and b/locale/pt_BR/LC_MESSAGES/django.mo differ diff --git a/locale/pt_BR/LC_MESSAGES/django.po b/locale/pt_BR/LC_MESSAGES/django.po index d3add60c7..2e729a2f2 100644 --- a/locale/pt_BR/LC_MESSAGES/django.po +++ b/locale/pt_BR/LC_MESSAGES/django.po @@ -2,8 +2,8 @@ msgid "" msgstr "" "Project-Id-Version: bookwyrm\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-10-06 23:57+0000\n" -"PO-Revision-Date: 2021-10-13 16:42\n" +"POT-Creation-Date: 2021-10-15 22:03+0000\n" +"PO-Revision-Date: 2021-10-22 13:31\n" "Last-Translator: Mouse Reeve \n" "Language-Team: Portuguese, Brazilian\n" "Language: pt\n" @@ -23,15 +23,15 @@ msgstr "Já existe um usuário com este endereço de e-mail." #: bookwyrm/forms.py:256 msgid "One Day" -msgstr "Um Dia" +msgstr "Um dia" #: bookwyrm/forms.py:257 msgid "One Week" -msgstr "Uma Semana" +msgstr "Uma semana" #: bookwyrm/forms.py:258 msgid "One Month" -msgstr "Um Mês" +msgstr "Um mês" #: bookwyrm/forms.py:259 msgid "Does Not Expire" @@ -48,14 +48,14 @@ msgstr "Ilimitado" #: bookwyrm/forms.py:326 msgid "List Order" -msgstr "Ordem da lista" +msgstr "Ordem de inserção" #: bookwyrm/forms.py:327 msgid "Book Title" msgstr "Título do livro" -#: bookwyrm/forms.py:328 bookwyrm/templates/shelf/shelf.html:134 -#: bookwyrm/templates/shelf/shelf.html:165 +#: bookwyrm/forms.py:328 bookwyrm/templates/shelf/shelf.html:136 +#: bookwyrm/templates/shelf/shelf.html:168 #: bookwyrm/templates/snippets/create_status/review.html:33 msgid "Rating" msgstr "Avaliação" @@ -151,45 +151,49 @@ msgstr "nome de usuário" msgid "A user with that username already exists." msgstr "Já existe um usuário com este nome." -#: bookwyrm/settings.py:117 +#: bookwyrm/settings.py:118 msgid "Home Timeline" msgstr "Linha do tempo" -#: bookwyrm/settings.py:117 +#: bookwyrm/settings.py:118 msgid "Home" msgstr "Página inicial" -#: bookwyrm/settings.py:118 +#: bookwyrm/settings.py:119 msgid "Books Timeline" msgstr "Linha do tempo dos livros" -#: bookwyrm/settings.py:118 bookwyrm/templates/search/layout.html:21 +#: bookwyrm/settings.py:119 bookwyrm/templates/search/layout.html:21 #: bookwyrm/templates/search/layout.html:42 #: bookwyrm/templates/user/layout.html:81 msgid "Books" msgstr "Livros" -#: bookwyrm/settings.py:164 +#: bookwyrm/settings.py:165 msgid "English" msgstr "English (Inglês)" -#: bookwyrm/settings.py:165 +#: bookwyrm/settings.py:166 msgid "Deutsch (German)" msgstr "Deutsch (Alemão)" -#: bookwyrm/settings.py:166 +#: bookwyrm/settings.py:167 msgid "Español (Spanish)" msgstr "Español (Espanhol)" -#: bookwyrm/settings.py:167 +#: bookwyrm/settings.py:168 msgid "Français (French)" msgstr "Français (Francês)" -#: bookwyrm/settings.py:168 +#: bookwyrm/settings.py:169 +msgid "Português - Brasil (Brazilian Portuguese)" +msgstr "Português - Brasil (Brazilian Portuguese)" + +#: bookwyrm/settings.py:170 msgid "简体中文 (Simplified Chinese)" msgstr "简体中文 (Chinês simplificado)" -#: bookwyrm/settings.py:169 +#: bookwyrm/settings.py:171 msgid "繁體中文 (Traditional Chinese)" msgstr "繁體中文 (Chinês tradicional)" @@ -669,11 +673,6 @@ msgstr "Idioma:" msgid "Search editions" msgstr "Procurar edições" -#: bookwyrm/templates/book/publisher_info.html:21 -#, python-format -msgid "%(format)s" -msgstr "%(format)s" - #: bookwyrm/templates/book/publisher_info.html:23 #, python-format msgid "%(format)s, %(pages)s pages" @@ -753,8 +752,8 @@ msgid "Help" msgstr "Ajuda" #: bookwyrm/templates/compose.html:5 bookwyrm/templates/compose.html:8 -msgid "Compose status" -msgstr "Escrever publicação" +msgid "Edit status" +msgstr "Editar publicação" #: bookwyrm/templates/confirm_email/confirm_email.html:4 msgid "Confirm email" @@ -888,6 +887,26 @@ msgstr "Usuários da BookWyrm" msgid "All known users" msgstr "Todos os usuários conhecidos" +#: bookwyrm/templates/discover/card-header.html:9 +#, python-format +msgid "%(username)s rated %(book_title)s" +msgstr "%(username)s avaliou %(book_title)s" + +#: bookwyrm/templates/discover/card-header.html:13 +#, python-format +msgid "%(username)s reviewed %(book_title)s" +msgstr "%(username)s resenhou %(book_title)s" + +#: bookwyrm/templates/discover/card-header.html:17 +#, python-format +msgid "%(username)s commented on %(book_title)s" +msgstr "%(username)s comentou sobre %(book_title)s" + +#: bookwyrm/templates/discover/card-header.html:21 +#, python-format +msgid "%(username)s quoted %(book_title)s" +msgstr "%(username)s citou %(book_title)s" + #: bookwyrm/templates/discover/discover.html:4 #: bookwyrm/templates/discover/discover.html:10 #: bookwyrm/templates/layout.html:78 @@ -899,28 +918,8 @@ msgstr "Explorar" msgid "See what's new in the local %(site_name)s community" msgstr "Veja as novidades da comunidade %(site_name)s local" -#: bookwyrm/templates/discover/large-book.html:46 -#: bookwyrm/templates/discover/small-book.html:32 -msgid "rated" -msgstr "avaliado" - -#: bookwyrm/templates/discover/large-book.html:48 -#: bookwyrm/templates/discover/small-book.html:34 -msgid "reviewed" -msgstr "resenhado" - -#: bookwyrm/templates/discover/large-book.html:50 -#: bookwyrm/templates/discover/small-book.html:36 -msgid "commented on" -msgstr "comentado" - #: bookwyrm/templates/discover/large-book.html:52 -#: bookwyrm/templates/discover/small-book.html:38 -msgid "quoted" -msgstr "citou" - -#: bookwyrm/templates/discover/large-book.html:68 -#: bookwyrm/templates/discover/small-book.html:52 +#: bookwyrm/templates/discover/small-book.html:36 msgid "View status" msgstr "Ver publicação" @@ -937,7 +936,7 @@ msgstr "Confirmar email" #: bookwyrm/templates/email/confirm/html_content.html:15 #, python-format msgid "Or enter the code \"%(confirmation_code)s\" at login." -msgstr "Ou insira o código \"%(confirmation_code)s\" ao fazer login." +msgstr "Ou insira o código \"%(confirmation_code)s\" quando entrar." #: bookwyrm/templates/email/confirm/subject.html:2 msgid "Please confirm your email" @@ -946,7 +945,7 @@ msgstr "Por favor, confirme seu email" #: bookwyrm/templates/email/confirm/text_content.html:10 #, python-format msgid "Or enter the code \"%(confirmation_code)s\" at login." -msgstr "Ou insira o código \"%(confirmation_code)s\" ao fazer login." +msgstr "Ou insira o código \"%(confirmation_code)s\" quando entrar." #: bookwyrm/templates/email/html_layout.html:15 #: bookwyrm/templates/email/text_layout.html:2 @@ -974,8 +973,8 @@ msgstr "Inscreva-se" #: bookwyrm/templates/email/invite/html_content.html:15 #, python-format -msgid "Learn more about this instance." -msgstr "Saiba mais sobre esta instância." +msgid "Learn more about %(site_name)s." +msgstr "Saiba mais sobre %(site_name)s." #: bookwyrm/templates/email/invite/text_content.html:4 #, python-format @@ -983,14 +982,15 @@ msgid "You're invited to join %(site_name)s! Click the link below to create an a msgstr "Você recebeu um convite para juntar-se a %(site_name)s! Clique no link abaixo para criar uma conta." #: bookwyrm/templates/email/invite/text_content.html:8 -msgid "Learn more about this instance:" -msgstr "Saiba mais sobre esta instância:" +#, python-format +msgid "Learn more about %(site_name)s:" +msgstr "Saiba mais sobre %(site_name)s:" #: bookwyrm/templates/email/password_reset/html_content.html:6 #: bookwyrm/templates/email/password_reset/text_content.html:4 #, python-format msgid "You requested to reset your %(site_name)s password. Click the link below to set a new password and log in to your account." -msgstr "Você solicitou a redefinição de sua senha no %(site_name)s. Clique no link abaixo para definir uma nova senha e fazer login." +msgstr "Você solicitou a redefinição de sua senha no %(site_name)s. Clique no link abaixo para definir uma nova senha e entrar no site." #: bookwyrm/templates/email/password_reset/html_content.html:9 #: bookwyrm/templates/password_reset.html:4 @@ -1189,7 +1189,7 @@ msgstr "Nome de exibição:" #: bookwyrm/templates/get_started/profile.html:22 #: bookwyrm/templates/preferences/edit_user.html:49 msgid "Summary:" -msgstr "Descrição:" +msgstr "Bio:" #: bookwyrm/templates/get_started/profile.html:23 msgid "A little bit about you" @@ -1323,13 +1323,13 @@ msgstr "Livro" #: bookwyrm/templates/import/import_status.html:122 #: bookwyrm/templates/shelf/shelf.html:128 -#: bookwyrm/templates/shelf/shelf.html:148 +#: bookwyrm/templates/shelf/shelf.html:150 msgid "Title" msgstr "Título" #: bookwyrm/templates/import/import_status.html:125 #: bookwyrm/templates/shelf/shelf.html:129 -#: bookwyrm/templates/shelf/shelf.html:151 +#: bookwyrm/templates/shelf/shelf.html:153 msgid "Author" msgstr "Autor" @@ -1338,7 +1338,7 @@ msgid "Imported" msgstr "Importado" #: bookwyrm/templates/import/tooltip.html:6 -msgid "You can download your GoodReads data from the Import/Export page of your GoodReads account." +msgid "You can download your Goodreads data from the Import/Export page of your Goodreads account." msgstr "Você pode baixar seus dados do Goodreads na página de Importar/Exportar da sua conta." #: bookwyrm/templates/invite.html:4 bookwyrm/templates/invite.html:8 @@ -1354,7 +1354,7 @@ msgstr "Permissão negada" msgid "Sorry! This invite code is no longer valid." msgstr "Desculpe! Este convite não é mais válido." -#: bookwyrm/templates/landing/about.html:7 +#: bookwyrm/templates/landing/about.html:7 bookwyrm/templates/layout.html:230 #, python-format msgid "About %(site_name)s" msgstr "Sobre %(site_name)s" @@ -1422,7 +1422,7 @@ msgstr "Menu de navegação principal" #: bookwyrm/templates/layout.html:72 msgid "Feed" -msgstr "Feed" +msgstr "Novidades" #: bookwyrm/templates/layout.html:106 msgid "Your Books" @@ -1466,12 +1466,12 @@ msgstr "senha" #: bookwyrm/templates/layout.html:176 bookwyrm/templates/login.html:40 msgid "Forgot your password?" -msgstr "Esqueceu a senha?" +msgstr "Esqueceu sua senha?" #: bookwyrm/templates/layout.html:179 bookwyrm/templates/login.html:7 #: bookwyrm/templates/login.html:37 msgid "Log in" -msgstr "Fazer login" +msgstr "Entrar" #: bookwyrm/templates/layout.html:187 msgid "Join" @@ -1485,10 +1485,6 @@ msgstr "Publicação feita com sucesso" msgid "Error posting status" msgstr "Erro ao publicar" -#: bookwyrm/templates/layout.html:230 -msgid "About this instance" -msgstr "Sobre esta instância" - #: bookwyrm/templates/layout.html:234 msgid "Contact site admin" msgstr "Contatar administração" @@ -1500,7 +1496,7 @@ msgstr "Documentação" #: bookwyrm/templates/layout.html:245 #, python-format msgid "Support %(site_name)s on %(support_title)s" -msgstr "Apoie a instância %(site_name)s em %(support_title)s" +msgstr "Apoie a instância %(site_name)s: %(support_title)s" #: bookwyrm/templates/layout.html:249 msgid "BookWyrm's source code is freely available. You can contribute or report issues on GitHub." @@ -1733,22 +1729,22 @@ msgstr "compartilhou sua publicação" #: bookwyrm/templates/notifications/items/fav.html:19 #, python-format -msgid "favorited your review of %(book_title)s" +msgid "liked your review of %(book_title)s" msgstr "curtiu sua resenha de %(book_title)s" #: bookwyrm/templates/notifications/items/fav.html:25 #, python-format -msgid "favorited your comment on%(book_title)s" -msgstr "curtiu seu comentário sobre%(book_title)s" +msgid "liked your comment on%(book_title)s" +msgstr "curtiu seu comentário sobre %(book_title)s" #: bookwyrm/templates/notifications/items/fav.html:31 #, python-format -msgid "favorited your quote from %(book_title)s" +msgid "liked your quote from %(book_title)s" msgstr "curtiu sua citação de %(book_title)s" #: bookwyrm/templates/notifications/items/fav.html:37 #, python-format -msgid "favorited your status" +msgid "liked your status" msgstr "curtiu sua publicação" #: bookwyrm/templates/notifications/items/follow.html:15 @@ -1900,7 +1896,7 @@ msgstr "Privacidade" #: bookwyrm/templates/preferences/edit_user.html:72 msgid "Show reading goal prompt in feed:" -msgstr "Mostrar sugestão de meta de leitura no feed:" +msgstr "Mostrar sugestão de meta de leitura:" #: bookwyrm/templates/preferences/edit_user.html:76 msgid "Show suggested users:" @@ -1962,7 +1958,7 @@ msgstr "Adicionar livro manualmente" #: bookwyrm/templates/search/book.html:116 msgid "Log in to import or add books." -msgstr "Faça login para importar ou adicionar livros." +msgstr "Entre para importar ou adicionar livros." #: bookwyrm/templates/search/layout.html:16 msgid "Search query" @@ -2115,7 +2111,7 @@ msgstr "Publicações" #: bookwyrm/templates/settings/dashboard/dashboard.html:33 #: bookwyrm/templates/settings/dashboard/works_chart.html:11 msgid "Works" -msgstr "" +msgstr "Obras" #: bookwyrm/templates/settings/dashboard/dashboard.html:43 #, python-format @@ -2157,7 +2153,7 @@ msgstr "Publicações" #: bookwyrm/templates/settings/dashboard/dashboard.html:118 msgid "Works created" -msgstr "" +msgstr "Obras criadas" #: bookwyrm/templates/settings/dashboard/registration_chart.html:10 msgid "Registrations" @@ -2297,6 +2293,7 @@ msgid "Notes" msgstr "Notas" #: bookwyrm/templates/settings/federation/instance.html:75 +#: bookwyrm/templates/snippets/status/status_options.html:24 msgid "Edit" msgstr "Editar" @@ -2550,7 +2547,7 @@ msgstr "Comentários da moderação" #: bookwyrm/templates/settings/reports/report.html:41 #: bookwyrm/templates/snippets/create_status.html:28 msgid "Comment" -msgstr "Comentário" +msgstr "Comentar" #: bookwyrm/templates/settings/reports/report.html:46 msgid "Reported statuses" @@ -2636,8 +2633,8 @@ msgid "Short description:" msgstr "Descrição curta:" #: bookwyrm/templates/settings/site.html:37 -msgid "Used when the instance is previewed on joinbookwyrm.com. Does not support html or markdown." -msgstr "Mostrado quando a instância é vista em joinbookwyrm.com. Não compatível com HTML ou markdown." +msgid "Used when the instance is previewed on joinbookwyrm.com. Does not support HTML or Markdown." +msgstr "Mostrado quando a instância é vista em joinbookwyrm.com. Não é compatível com HTML ou Markdown." #: bookwyrm/templates/settings/site.html:41 msgid "Code of conduct:" @@ -2811,7 +2808,7 @@ msgid "Permanently deleted" msgstr "Excluído permanentemente" #: bookwyrm/templates/settings/users/user_moderation_actions.html:13 -#: bookwyrm/templates/snippets/status/status_options.html:35 +#: bookwyrm/templates/snippets/status/status_options.html:32 #: bookwyrm/templates/snippets/user_options.html:13 msgid "Send direct message" msgstr "Enviar mensagem direta" @@ -2864,22 +2861,22 @@ msgstr "Editar estante" msgid "Delete shelf" msgstr "Excluir estante" -#: bookwyrm/templates/shelf/shelf.html:130 -#: bookwyrm/templates/shelf/shelf.html:154 +#: bookwyrm/templates/shelf/shelf.html:132 +#: bookwyrm/templates/shelf/shelf.html:158 msgid "Shelved" msgstr "Adicionado" -#: bookwyrm/templates/shelf/shelf.html:131 -#: bookwyrm/templates/shelf/shelf.html:158 +#: bookwyrm/templates/shelf/shelf.html:133 +#: bookwyrm/templates/shelf/shelf.html:161 msgid "Started" msgstr "Iniciado" -#: bookwyrm/templates/shelf/shelf.html:132 -#: bookwyrm/templates/shelf/shelf.html:161 +#: bookwyrm/templates/shelf/shelf.html:134 +#: bookwyrm/templates/shelf/shelf.html:164 msgid "Finished" msgstr "Terminado" -#: bookwyrm/templates/shelf/shelf.html:187 +#: bookwyrm/templates/shelf/shelf.html:190 msgid "This shelf is empty." msgstr "Esta estante está vazia." @@ -2926,22 +2923,22 @@ msgstr "Citar" msgid "Some thoughts on the book" msgstr "Algumas ideias sobre o livro" -#: bookwyrm/templates/snippets/create_status/comment.html:26 +#: bookwyrm/templates/snippets/create_status/comment.html:27 #: bookwyrm/templates/snippets/reading_modals/progress_update_modal.html:15 msgid "Progress:" msgstr "Progresso:" -#: bookwyrm/templates/snippets/create_status/comment.html:52 +#: bookwyrm/templates/snippets/create_status/comment.html:53 #: bookwyrm/templates/snippets/progress_field.html:18 msgid "pages" msgstr "páginas" -#: bookwyrm/templates/snippets/create_status/comment.html:58 +#: bookwyrm/templates/snippets/create_status/comment.html:59 #: bookwyrm/templates/snippets/progress_field.html:23 msgid "percent" msgstr "porcento" -#: bookwyrm/templates/snippets/create_status/comment.html:65 +#: bookwyrm/templates/snippets/create_status/comment.html:66 #, python-format msgid "of %(pages)s pages" msgstr "de %(pages)s páginas" @@ -2969,7 +2966,7 @@ msgstr "Alerta de spoiler!" msgid "Include spoiler alert" msgstr "Incluir alerta de spoiler" -#: bookwyrm/templates/snippets/create_status/layout.html:41 +#: bookwyrm/templates/snippets/create_status/layout.html:48 #: bookwyrm/templates/snippets/reading_modals/form.html:7 msgid "Comment:" msgstr "Comentário:" @@ -3142,7 +3139,7 @@ msgstr "Privacidade da meta:" #: bookwyrm/templates/snippets/goal_form.html:33 #: bookwyrm/templates/snippets/reading_modals/layout.html:13 msgid "Post to feed" -msgstr "Publicar no feed" +msgstr "Publicar" #: bookwyrm/templates/snippets/goal_form.html:37 msgid "Set goal" @@ -3151,7 +3148,7 @@ msgstr "Definir meta" #: bookwyrm/templates/snippets/goal_progress.html:9 #, python-format msgid "%(percent)s%% complete!" -msgstr "%(percent)s%% completo!" +msgstr "%(percent)s%% lá!" #: bookwyrm/templates/snippets/goal_progress.html:12 #, python-format @@ -3163,12 +3160,12 @@ msgstr "Você leu %(read_count)s de %(goal_count)s livros%(read_count)s of %(goal_count)s books." msgstr "%(username)s leu %(read_count)s de %(goal_count)s livros." -#: bookwyrm/templates/snippets/page_text.html:4 +#: bookwyrm/templates/snippets/page_text.html:8 #, python-format msgid "page %(page)s of %(total_pages)s" msgstr "página %(page)s de %(total_pages)s" -#: bookwyrm/templates/snippets/page_text.html:6 +#: bookwyrm/templates/snippets/page_text.html:14 #, python-format msgid "page %(page)s" msgstr "página %(page)s" @@ -3256,7 +3253,7 @@ msgstr "Progresso" #: bookwyrm/templates/snippets/register_form.html:32 msgid "Sign Up" -msgstr "Se cadastrar" +msgstr "Cadastrar" #: bookwyrm/templates/snippets/report_button.html:6 msgid "Report" @@ -3329,6 +3326,11 @@ msgstr "Abrir imagem em nova janela" msgid "Hide status" msgstr "Esconder publicação" +#: bookwyrm/templates/snippets/status/header.html:45 +#, python-format +msgid "edited %(date)s" +msgstr "editado em %(date)s" + #: bookwyrm/templates/snippets/status/headers/comment.html:2 #, python-format msgid "commented on %(book)s" @@ -3393,10 +3395,6 @@ msgstr "compartilhado" msgid "More options" msgstr "Mais opções" -#: bookwyrm/templates/snippets/status/status_options.html:26 -msgid "Delete & re-draft" -msgstr "Excluir e rascunhar" - #: bookwyrm/templates/snippets/suggested_users.html:16 #, python-format msgid "%(mutuals)s follower you follow" diff --git a/locale/zh_Hans/LC_MESSAGES/django.mo b/locale/zh_Hans/LC_MESSAGES/django.mo index 9eaced00b..1d1227f80 100644 Binary files a/locale/zh_Hans/LC_MESSAGES/django.mo and b/locale/zh_Hans/LC_MESSAGES/django.mo differ diff --git a/locale/zh_Hans/LC_MESSAGES/django.po b/locale/zh_Hans/LC_MESSAGES/django.po index 7bbcbcc54..2a0707359 100644 --- a/locale/zh_Hans/LC_MESSAGES/django.po +++ b/locale/zh_Hans/LC_MESSAGES/django.po @@ -2,8 +2,8 @@ msgid "" msgstr "" "Project-Id-Version: bookwyrm\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-10-06 23:57+0000\n" -"PO-Revision-Date: 2021-10-08 00:03\n" +"POT-Creation-Date: 2021-10-15 22:03+0000\n" +"PO-Revision-Date: 2021-10-16 14:36\n" "Last-Translator: Mouse Reeve \n" "Language-Team: Chinese Simplified\n" "Language: zh\n" @@ -40,7 +40,7 @@ msgstr "永不失效" #: bookwyrm/forms.py:263 #, python-brace-format msgid "{i} uses" -msgstr "" +msgstr "{i} 次使用" #: bookwyrm/forms.py:264 msgid "Unlimited" @@ -54,8 +54,8 @@ msgstr "列表顺序" msgid "Book Title" msgstr "书名" -#: bookwyrm/forms.py:328 bookwyrm/templates/shelf/shelf.html:134 -#: bookwyrm/templates/shelf/shelf.html:165 +#: bookwyrm/forms.py:328 bookwyrm/templates/shelf/shelf.html:136 +#: bookwyrm/templates/shelf/shelf.html:168 #: bookwyrm/templates/snippets/create_status/review.html:33 msgid "Rating" msgstr "评价" @@ -74,51 +74,51 @@ msgstr "降序" #: bookwyrm/importers/importer.py:75 msgid "Error loading book" -msgstr "" +msgstr "加载书籍时出错" #: bookwyrm/importers/importer.py:88 msgid "Could not find a match for book" -msgstr "" +msgstr "找不到匹配的书" #: bookwyrm/models/base_model.py:17 msgid "Pending" -msgstr "" +msgstr "待处理" #: bookwyrm/models/base_model.py:18 msgid "Self deletion" -msgstr "" +msgstr "自我删除" #: bookwyrm/models/base_model.py:19 msgid "Moderator suspension" -msgstr "" +msgstr "仲裁员停用" #: bookwyrm/models/base_model.py:20 msgid "Moderator deletion" -msgstr "" +msgstr "仲裁员删除" #: bookwyrm/models/base_model.py:21 msgid "Domain block" -msgstr "" +msgstr "域名屏蔽" #: bookwyrm/models/book.py:232 msgid "Audiobook" -msgstr "" +msgstr "有声书籍" #: bookwyrm/models/book.py:233 msgid "eBook" -msgstr "" +msgstr "电子书" #: bookwyrm/models/book.py:234 msgid "Graphic novel" -msgstr "" +msgstr "图像小说" #: bookwyrm/models/book.py:235 msgid "Hardcover" -msgstr "" +msgstr "硬封面" #: bookwyrm/models/book.py:236 msgid "Paperback" -msgstr "" +msgstr "平装" #: bookwyrm/models/federated_server.py:11 #: bookwyrm/templates/settings/federation/edit_instance.html:42 @@ -151,45 +151,49 @@ msgstr "用户名" msgid "A user with that username already exists." msgstr "已经存在使用该用户名的用户。" -#: bookwyrm/settings.py:117 +#: bookwyrm/settings.py:118 msgid "Home Timeline" msgstr "主页时间线" -#: bookwyrm/settings.py:117 +#: bookwyrm/settings.py:118 msgid "Home" msgstr "主页" -#: bookwyrm/settings.py:118 +#: bookwyrm/settings.py:119 msgid "Books Timeline" msgstr "书目时间线" -#: bookwyrm/settings.py:118 bookwyrm/templates/search/layout.html:21 +#: bookwyrm/settings.py:119 bookwyrm/templates/search/layout.html:21 #: bookwyrm/templates/search/layout.html:42 #: bookwyrm/templates/user/layout.html:81 msgid "Books" msgstr "书目" -#: bookwyrm/settings.py:164 +#: bookwyrm/settings.py:165 msgid "English" msgstr "English(英语)" -#: bookwyrm/settings.py:165 +#: bookwyrm/settings.py:166 msgid "Deutsch (German)" msgstr "Deutsch(德语)" -#: bookwyrm/settings.py:166 +#: bookwyrm/settings.py:167 msgid "Español (Spanish)" msgstr "Español(西班牙语)" -#: bookwyrm/settings.py:167 +#: bookwyrm/settings.py:168 msgid "Français (French)" msgstr "Français(法语)" -#: bookwyrm/settings.py:168 +#: bookwyrm/settings.py:169 +msgid "Português - Brasil (Brazilian Portuguese)" +msgstr "" + +#: bookwyrm/settings.py:170 msgid "简体中文 (Simplified Chinese)" msgstr "简体中文" -#: bookwyrm/settings.py:169 +#: bookwyrm/settings.py:171 msgid "繁體中文 (Traditional Chinese)" msgstr "繁體中文(繁体中文)" @@ -570,7 +574,7 @@ msgstr "语言:" #: bookwyrm/templates/book/edit/edit_book_form.html:74 msgid "Publication" -msgstr "" +msgstr "出版" #: bookwyrm/templates/book/edit/edit_book_form.html:77 msgid "Publisher:" @@ -622,7 +626,7 @@ msgstr "格式:" #: bookwyrm/templates/book/edit/edit_book_form.html:177 msgid "Format details:" -msgstr "" +msgstr "装订细节:" #: bookwyrm/templates/book/edit/edit_book_form.html:187 msgid "Pages:" @@ -666,12 +670,7 @@ msgstr "语言:" #: bookwyrm/templates/book/editions/search_filter.html:5 msgid "Search editions" -msgstr "" - -#: bookwyrm/templates/book/publisher_info.html:21 -#, python-format -msgid "%(format)s" -msgstr "%(format)s" +msgstr "搜索版本" #: bookwyrm/templates/book/publisher_info.html:23 #, python-format @@ -749,11 +748,11 @@ msgstr "关闭" #: bookwyrm/templates/components/tooltip.html:3 msgid "Help" -msgstr "" +msgstr "帮助" #: bookwyrm/templates/compose.html:5 bookwyrm/templates/compose.html:8 -msgid "Compose status" -msgstr "撰写状态" +msgid "Edit status" +msgstr "" #: bookwyrm/templates/confirm_email/confirm_email.html:4 msgid "Confirm email" @@ -885,6 +884,26 @@ msgstr "BookWyrm 用户" msgid "All known users" msgstr "所有已知用户" +#: bookwyrm/templates/discover/card-header.html:9 +#, python-format +msgid "%(username)s rated %(book_title)s" +msgstr "" + +#: bookwyrm/templates/discover/card-header.html:13 +#, python-format +msgid "%(username)s reviewed %(book_title)s" +msgstr "" + +#: bookwyrm/templates/discover/card-header.html:17 +#, python-format +msgid "%(username)s commented on %(book_title)s" +msgstr "" + +#: bookwyrm/templates/discover/card-header.html:21 +#, python-format +msgid "%(username)s quoted %(book_title)s" +msgstr "" + #: bookwyrm/templates/discover/discover.html:4 #: bookwyrm/templates/discover/discover.html:10 #: bookwyrm/templates/layout.html:78 @@ -896,28 +915,8 @@ msgstr "发现" msgid "See what's new in the local %(site_name)s community" msgstr "看看本地 %(site_name)s 社区的新消息" -#: bookwyrm/templates/discover/large-book.html:46 -#: bookwyrm/templates/discover/small-book.html:32 -msgid "rated" -msgstr "评价了" - -#: bookwyrm/templates/discover/large-book.html:48 -#: bookwyrm/templates/discover/small-book.html:34 -msgid "reviewed" -msgstr "写了书评给" - -#: bookwyrm/templates/discover/large-book.html:50 -#: bookwyrm/templates/discover/small-book.html:36 -msgid "commented on" -msgstr "评论了" - #: bookwyrm/templates/discover/large-book.html:52 -#: bookwyrm/templates/discover/small-book.html:38 -msgid "quoted" -msgstr "引用了" - -#: bookwyrm/templates/discover/large-book.html:68 -#: bookwyrm/templates/discover/small-book.html:52 +#: bookwyrm/templates/discover/small-book.html:36 msgid "View status" msgstr "浏览状态" @@ -971,8 +970,8 @@ msgstr "立即加入" #: bookwyrm/templates/email/invite/html_content.html:15 #, python-format -msgid "Learn more about this instance." -msgstr "了解更多 有关本实例的信息。" +msgid "Learn more about %(site_name)s." +msgstr "" #: bookwyrm/templates/email/invite/text_content.html:4 #, python-format @@ -980,8 +979,9 @@ msgid "You're invited to join %(site_name)s! Click the link below to create an a msgstr "你受邀请加入 %(site_name)s!点击下面的连接来创建帐号。" #: bookwyrm/templates/email/invite/text_content.html:8 -msgid "Learn more about this instance:" -msgstr "了解更多有关本实例的信息:" +#, python-format +msgid "Learn more about %(site_name)s:" +msgstr "" #: bookwyrm/templates/email/password_reset/html_content.html:6 #: bookwyrm/templates/email/password_reset/text_content.html:4 @@ -1083,11 +1083,11 @@ msgstr "可以关注的人" #: bookwyrm/templates/feed/suggested_users.html:9 msgid "Don't show suggested users" -msgstr "" +msgstr "不显示推荐用户" #: bookwyrm/templates/feed/suggested_users.html:14 msgid "View directory" -msgstr "" +msgstr "查看目录" #: bookwyrm/templates/get_started/book_preview.html:6 #, python-format @@ -1320,13 +1320,13 @@ msgstr "书目" #: bookwyrm/templates/import/import_status.html:122 #: bookwyrm/templates/shelf/shelf.html:128 -#: bookwyrm/templates/shelf/shelf.html:148 +#: bookwyrm/templates/shelf/shelf.html:150 msgid "Title" msgstr "标题" #: bookwyrm/templates/import/import_status.html:125 #: bookwyrm/templates/shelf/shelf.html:129 -#: bookwyrm/templates/shelf/shelf.html:151 +#: bookwyrm/templates/shelf/shelf.html:153 msgid "Author" msgstr "作者" @@ -1335,7 +1335,7 @@ msgid "Imported" msgstr "已导入" #: bookwyrm/templates/import/tooltip.html:6 -msgid "You can download your GoodReads data from the Import/Export page of your GoodReads account." +msgid "You can download your Goodreads data from the Import/Export page of your Goodreads account." msgstr "" #: bookwyrm/templates/invite.html:4 bookwyrm/templates/invite.html:8 @@ -1351,7 +1351,7 @@ msgstr "没有权限" msgid "Sorry! This invite code is no longer valid." msgstr "抱歉!此邀请码已不再有效。" -#: bookwyrm/templates/landing/about.html:7 +#: bookwyrm/templates/landing/about.html:7 bookwyrm/templates/layout.html:230 #, python-format msgid "About %(site_name)s" msgstr "关于 %(site_name)s" @@ -1394,7 +1394,7 @@ msgstr "请求邀请" #: bookwyrm/templates/landing/layout.html:49 #, python-format msgid "%(name)s registration is closed" -msgstr "" +msgstr "%(name)s 注册已关闭" #: bookwyrm/templates/landing/layout.html:60 msgid "Thank you! Your request has been received." @@ -1407,11 +1407,11 @@ msgstr "你的帐号" #: bookwyrm/templates/layout.html:13 #, python-format msgid "%(site_name)s search" -msgstr "" +msgstr "%(site_name)s 搜索" #: bookwyrm/templates/layout.html:43 msgid "Search for a book, user, or list" -msgstr "" +msgstr "搜索书籍、用户或列表" #: bookwyrm/templates/layout.html:61 bookwyrm/templates/layout.html:62 msgid "Main navigation menu" @@ -1476,15 +1476,11 @@ msgstr "加入" #: bookwyrm/templates/layout.html:221 msgid "Successfully posted status" -msgstr "" +msgstr "成功发布的状态" #: bookwyrm/templates/layout.html:222 msgid "Error posting status" -msgstr "" - -#: bookwyrm/templates/layout.html:230 -msgid "About this instance" -msgstr "关于本实例" +msgstr "发布状态时出错" #: bookwyrm/templates/layout.html:234 msgid "Contact site admin" @@ -1505,7 +1501,7 @@ msgstr "BookWyrm 是开源软件。你可以在 %(book_title)s to your list \"%(list_name)s\"" -msgstr "" +msgstr "添加了 %(book_title)s 到你的列表 “%(list_name)s”" #: bookwyrm/templates/notifications/items/add.html:31 #, python-format msgid "suggested adding %(book_title)s to your list \"%(list_name)s\"" -msgstr "" +msgstr "建议了添加 %(book_title)s 到你的列表 “%(list_name)s” 中" #: bookwyrm/templates/notifications/items/boost.html:19 #, python-format @@ -1730,23 +1726,23 @@ msgstr "转发了你的 状态" #: bookwyrm/templates/notifications/items/fav.html:19 #, python-format -msgid "favorited your review of %(book_title)s" -msgstr "喜欢了你 %(book_title)s 的书评" +msgid "liked your review of %(book_title)s" +msgstr "" #: bookwyrm/templates/notifications/items/fav.html:25 #, python-format -msgid "favorited your comment on%(book_title)s" +msgid "liked your comment on%(book_title)s" msgstr "" #: bookwyrm/templates/notifications/items/fav.html:31 #, python-format -msgid "favorited your quote from %(book_title)s" -msgstr "喜欢了你 来自 %(book_title)s 的引用" +msgid "liked your quote from %(book_title)s" +msgstr "" #: bookwyrm/templates/notifications/items/fav.html:37 #, python-format -msgid "favorited your status" -msgstr "喜欢了你的 状态" +msgid "liked your status" +msgstr "" #: bookwyrm/templates/notifications/items/follow.html:15 msgid "followed you" @@ -1888,20 +1884,20 @@ msgstr "个人资料" #: bookwyrm/templates/preferences/edit_user.html:13 #: bookwyrm/templates/preferences/edit_user.html:68 msgid "Display preferences" -msgstr "" +msgstr "显示偏好" #: bookwyrm/templates/preferences/edit_user.html:14 #: bookwyrm/templates/preferences/edit_user.html:106 msgid "Privacy" -msgstr "" +msgstr "隐私" #: bookwyrm/templates/preferences/edit_user.html:72 msgid "Show reading goal prompt in feed:" -msgstr "" +msgstr "在状态流中显示阅读目标提示:" #: bookwyrm/templates/preferences/edit_user.html:76 msgid "Show suggested users:" -msgstr "" +msgstr "显示推荐用户:" #: bookwyrm/templates/preferences/edit_user.html:85 #, python-format @@ -2035,15 +2031,15 @@ msgstr "创建公告" #: bookwyrm/templates/settings/announcements/announcement_form.html:16 msgid "Preview:" -msgstr "" +msgstr "预览:" #: bookwyrm/templates/settings/announcements/announcement_form.html:23 msgid "Content:" -msgstr "" +msgstr "内容:" #: bookwyrm/templates/settings/announcements/announcement_form.html:30 msgid "Event date:" -msgstr "" +msgstr "事件日期:" #: bookwyrm/templates/settings/announcements/announcements.html:3 #: bookwyrm/templates/settings/announcements/announcements.html:5 @@ -2087,122 +2083,122 @@ msgstr "停用" #: bookwyrm/templates/settings/announcements/announcements.html:52 msgid "No announcements found" -msgstr "" +msgstr "未找到公告" #: bookwyrm/templates/settings/dashboard/dashboard.html:6 #: bookwyrm/templates/settings/dashboard/dashboard.html:8 #: bookwyrm/templates/settings/layout.html:26 msgid "Dashboard" -msgstr "" +msgstr "仪表盘" #: bookwyrm/templates/settings/dashboard/dashboard.html:15 #: bookwyrm/templates/settings/dashboard/dashboard.html:100 msgid "Total users" -msgstr "" +msgstr "用户总数" #: bookwyrm/templates/settings/dashboard/dashboard.html:21 #: bookwyrm/templates/settings/dashboard/user_chart.html:16 msgid "Active this month" -msgstr "" +msgstr "今月活跃" #: bookwyrm/templates/settings/dashboard/dashboard.html:27 msgid "Statuses" -msgstr "" +msgstr "状态" #: bookwyrm/templates/settings/dashboard/dashboard.html:33 #: bookwyrm/templates/settings/dashboard/works_chart.html:11 msgid "Works" -msgstr "" +msgstr "作品" #: bookwyrm/templates/settings/dashboard/dashboard.html:43 #, python-format msgid "%(display_count)s open report" msgid_plural "%(display_count)s open reports" -msgstr[0] "" +msgstr[0] "%(display_count)s 条待处理报告" #: bookwyrm/templates/settings/dashboard/dashboard.html:54 #, python-format msgid "%(display_count)s invite request" msgid_plural "%(display_count)s invite requests" -msgstr[0] "" +msgstr[0] "%(display_count)s 条邀请请求" #: bookwyrm/templates/settings/dashboard/dashboard.html:65 msgid "Instance Activity" -msgstr "" +msgstr "实例活动" #: bookwyrm/templates/settings/dashboard/dashboard.html:83 msgid "Interval:" -msgstr "" +msgstr "区段:" #: bookwyrm/templates/settings/dashboard/dashboard.html:87 msgid "Days" -msgstr "" +msgstr "天" #: bookwyrm/templates/settings/dashboard/dashboard.html:88 msgid "Weeks" -msgstr "" +msgstr "周" #: bookwyrm/templates/settings/dashboard/dashboard.html:106 msgid "User signup activity" -msgstr "" +msgstr "用户注册活动" #: bookwyrm/templates/settings/dashboard/dashboard.html:112 msgid "Status activity" -msgstr "" +msgstr "状态动态" #: bookwyrm/templates/settings/dashboard/dashboard.html:118 msgid "Works created" -msgstr "" +msgstr "创建的作品" #: bookwyrm/templates/settings/dashboard/registration_chart.html:10 msgid "Registrations" -msgstr "" +msgstr "注册" #: bookwyrm/templates/settings/dashboard/status_chart.html:11 msgid "Statuses posted" -msgstr "" +msgstr "发布的状态" #: bookwyrm/templates/settings/dashboard/user_chart.html:11 msgid "Total" -msgstr "" +msgstr "总数" #: bookwyrm/templates/settings/email_blocklist/domain_form.html:5 #: bookwyrm/templates/settings/email_blocklist/email_blocklist.html:10 msgid "Add domain" -msgstr "" +msgstr "添加域名" #: bookwyrm/templates/settings/email_blocklist/domain_form.html:11 msgid "Domain:" -msgstr "" +msgstr "域名:" #: bookwyrm/templates/settings/email_blocklist/email_blocklist.html:5 #: bookwyrm/templates/settings/email_blocklist/email_blocklist.html:7 #: bookwyrm/templates/settings/layout.html:59 msgid "Email Blocklist" -msgstr "" +msgstr "邮件屏蔽列表" #: bookwyrm/templates/settings/email_blocklist/email_blocklist.html:18 msgid "When someone tries to register with an email from this domain, no account will be created. The registration process will appear to have worked." -msgstr "" +msgstr "当有人试图使用此域名的电子邮件注册时,帐户将不会被创建,但注册过程看起来会像是成功了的样子。" #: bookwyrm/templates/settings/email_blocklist/email_blocklist.html:25 msgid "Domain" -msgstr "" +msgstr "域名" #: bookwyrm/templates/settings/email_blocklist/email_blocklist.html:29 #: bookwyrm/templates/settings/ip_blocklist/ip_blocklist.html:27 msgid "Options" -msgstr "" +msgstr "选项" #: bookwyrm/templates/settings/email_blocklist/email_blocklist.html:38 #, python-format msgid "%(display_count)s user" msgid_plural "%(display_count)s users" -msgstr[0] "" +msgstr[0] "%(display_count)s 名用户" #: bookwyrm/templates/settings/email_blocklist/email_blocklist.html:59 msgid "No email domains currently blocked" -msgstr "" +msgstr "目前没有屏蔽邮件域名" #: bookwyrm/templates/settings/federation/edit_instance.html:3 #: bookwyrm/templates/settings/federation/edit_instance.html:6 @@ -2291,12 +2287,13 @@ msgid "Notes" msgstr "备注" #: bookwyrm/templates/settings/federation/instance.html:75 +#: bookwyrm/templates/snippets/status/status_options.html:24 msgid "Edit" msgstr "编辑" #: bookwyrm/templates/settings/federation/instance.html:79 msgid "No notes" -msgstr "" +msgstr "没有备注" #: bookwyrm/templates/settings/federation/instance.html:94 #: bookwyrm/templates/settings/users/user_moderation_actions.html:8 @@ -2355,7 +2352,7 @@ msgstr "软件" #: bookwyrm/templates/settings/federation/instance_list.html:63 msgid "No instances found" -msgstr "" +msgstr "未找到实例" #: bookwyrm/templates/settings/invites/manage_invite_requests.html:4 #: bookwyrm/templates/settings/invites/manage_invite_requests.html:11 @@ -2466,37 +2463,37 @@ msgstr "无有效的邀请" #: bookwyrm/templates/settings/ip_blocklist/ip_address_form.html:5 #: bookwyrm/templates/settings/ip_blocklist/ip_blocklist.html:10 msgid "Add IP address" -msgstr "" +msgstr "添加 IP 地址" #: bookwyrm/templates/settings/ip_blocklist/ip_address_form.html:11 msgid "Use IP address blocks with caution, and consider using blocks only temporarily, as IP addresses are often shared or change hands. If you block your own IP, you will not be able to access this page." -msgstr "" +msgstr "请谨慎使用 IP 地址屏蔽,并尽可能只使用暂时的屏蔽,因为 IP 地址常常被共享或变更。 如果您屏蔽了自己的 IP,您将无法访问此页面。" #: bookwyrm/templates/settings/ip_blocklist/ip_address_form.html:18 msgid "IP Address:" -msgstr "" +msgstr "IP 地址:" #: bookwyrm/templates/settings/ip_blocklist/ip_blocklist.html:5 #: bookwyrm/templates/settings/ip_blocklist/ip_blocklist.html:7 #: bookwyrm/templates/settings/layout.html:63 msgid "IP Address Blocklist" -msgstr "" +msgstr "IP 地址屏蔽列表" #: bookwyrm/templates/settings/ip_blocklist/ip_blocklist.html:18 msgid "Any traffic from this IP address will get a 404 response when trying to access any part of the application." -msgstr "" +msgstr "从此 IP 地址的所有尝试访问此应用的流量都将会收到 404 的答复。" #: bookwyrm/templates/settings/ip_blocklist/ip_blocklist.html:24 msgid "Address" -msgstr "" +msgstr "地址" #: bookwyrm/templates/settings/ip_blocklist/ip_blocklist.html:46 msgid "No IP addresses currently blocked" -msgstr "" +msgstr "目前没有屏蔽 IP 地址" #: bookwyrm/templates/settings/ip_blocklist/ip_tooltip.html:6 msgid "You can block IP ranges using CIDR syntax." -msgstr "" +msgstr "你可以使用 CIDR 语法来进行 IP 段的屏蔽。" #: bookwyrm/templates/settings/layout.html:4 msgid "Administration" @@ -2508,7 +2505,7 @@ msgstr "管理用户" #: bookwyrm/templates/settings/layout.html:51 msgid "Moderation" -msgstr "" +msgstr "仲裁" #: bookwyrm/templates/settings/layout.html:55 #: bookwyrm/templates/settings/reports/reports.html:8 @@ -2627,10 +2624,10 @@ msgstr "实例描述:" #: bookwyrm/templates/settings/site.html:36 msgid "Short description:" -msgstr "" +msgstr "简要描述:" #: bookwyrm/templates/settings/site.html:37 -msgid "Used when the instance is previewed on joinbookwyrm.com. Does not support html or markdown." +msgid "Used when the instance is previewed on joinbookwyrm.com. Does not support HTML or Markdown." msgstr "" #: bookwyrm/templates/settings/site.html:41 @@ -2691,21 +2688,21 @@ msgstr "注册关闭文字:" #: bookwyrm/templates/settings/site.html:124 msgid "Invite request text:" -msgstr "" +msgstr "邀请请求文本:" #: bookwyrm/templates/settings/users/delete_user_form.html:5 #: bookwyrm/templates/settings/users/user_moderation_actions.html:31 msgid "Permanently delete user" -msgstr "" +msgstr "永久删除用户" #: bookwyrm/templates/settings/users/delete_user_form.html:12 #, python-format msgid "Are you sure you want to delete %(username)s's account? This action cannot be undone. To proceed, please enter your password to confirm deletion." -msgstr "" +msgstr "你确定要删除 %(username)s 的帐号吗?此操作不能被撤销。请输入你的密码确认删除操作以继续。" #: bookwyrm/templates/settings/users/delete_user_form.html:17 msgid "Your password:" -msgstr "" +msgstr "你的密码:" #: bookwyrm/templates/settings/users/user.html:7 msgid "Back to users" @@ -2758,7 +2755,7 @@ msgstr "本站" #: bookwyrm/templates/settings/users/user_info.html:38 msgid "Remote" -msgstr "" +msgstr "远端" #: bookwyrm/templates/settings/users/user_info.html:47 msgid "User details" @@ -2766,31 +2763,31 @@ msgstr "用户详情" #: bookwyrm/templates/settings/users/user_info.html:51 msgid "Email:" -msgstr "" +msgstr "邮箱:" #: bookwyrm/templates/settings/users/user_info.html:61 msgid "(View reports)" -msgstr "" +msgstr "(查看报告)" #: bookwyrm/templates/settings/users/user_info.html:67 msgid "Blocked by count:" -msgstr "" +msgstr "被屏蔽次数:" #: bookwyrm/templates/settings/users/user_info.html:70 msgid "Last active date:" -msgstr "" +msgstr "最后活跃日期:" #: bookwyrm/templates/settings/users/user_info.html:73 msgid "Manually approved followers:" -msgstr "" +msgstr "手动批准关注者:" #: bookwyrm/templates/settings/users/user_info.html:76 msgid "Discoverable:" -msgstr "" +msgstr "可发现:" #: bookwyrm/templates/settings/users/user_info.html:80 msgid "Deactivation reason:" -msgstr "" +msgstr "停用原因:" #: bookwyrm/templates/settings/users/user_info.html:95 msgid "Instance details" @@ -2805,7 +2802,7 @@ msgid "Permanently deleted" msgstr "已永久删除" #: bookwyrm/templates/settings/users/user_moderation_actions.html:13 -#: bookwyrm/templates/snippets/status/status_options.html:35 +#: bookwyrm/templates/snippets/status/status_options.html:32 #: bookwyrm/templates/snippets/user_options.html:13 msgid "Send direct message" msgstr "发送私信" @@ -2842,12 +2839,12 @@ msgstr "创建书架" #, python-format msgid "%(formatted_count)s book" msgid_plural "%(formatted_count)s books" -msgstr[0] "" +msgstr[0] "%(formatted_count)s 本书籍" #: bookwyrm/templates/shelf/shelf.html:84 #, python-format msgid "(showing %(start)s-%(end)s)" -msgstr "" +msgstr "(正在显示 %(start)s 到 %(end)s)" #: bookwyrm/templates/shelf/shelf.html:96 msgid "Edit shelf" @@ -2857,22 +2854,22 @@ msgstr "编辑书架" msgid "Delete shelf" msgstr "删除书架" -#: bookwyrm/templates/shelf/shelf.html:130 -#: bookwyrm/templates/shelf/shelf.html:154 +#: bookwyrm/templates/shelf/shelf.html:132 +#: bookwyrm/templates/shelf/shelf.html:158 msgid "Shelved" msgstr "上架时间" -#: bookwyrm/templates/shelf/shelf.html:131 -#: bookwyrm/templates/shelf/shelf.html:158 +#: bookwyrm/templates/shelf/shelf.html:133 +#: bookwyrm/templates/shelf/shelf.html:161 msgid "Started" msgstr "开始时间" -#: bookwyrm/templates/shelf/shelf.html:132 -#: bookwyrm/templates/shelf/shelf.html:161 +#: bookwyrm/templates/shelf/shelf.html:134 +#: bookwyrm/templates/shelf/shelf.html:164 msgid "Finished" msgstr "完成时间" -#: bookwyrm/templates/shelf/shelf.html:187 +#: bookwyrm/templates/shelf/shelf.html:190 msgid "This shelf is empty." msgstr "此书架是空的。" @@ -2918,22 +2915,22 @@ msgstr "引用" msgid "Some thoughts on the book" msgstr "对书的一些看法" -#: bookwyrm/templates/snippets/create_status/comment.html:26 +#: bookwyrm/templates/snippets/create_status/comment.html:27 #: bookwyrm/templates/snippets/reading_modals/progress_update_modal.html:15 msgid "Progress:" msgstr "进度:" -#: bookwyrm/templates/snippets/create_status/comment.html:52 +#: bookwyrm/templates/snippets/create_status/comment.html:53 #: bookwyrm/templates/snippets/progress_field.html:18 msgid "pages" msgstr "页数" -#: bookwyrm/templates/snippets/create_status/comment.html:58 +#: bookwyrm/templates/snippets/create_status/comment.html:59 #: bookwyrm/templates/snippets/progress_field.html:23 msgid "percent" msgstr "百分比" -#: bookwyrm/templates/snippets/create_status/comment.html:65 +#: bookwyrm/templates/snippets/create_status/comment.html:66 #, python-format msgid "of %(pages)s pages" msgstr "全书 %(pages)s 页" @@ -2951,7 +2948,7 @@ msgstr "内容" #: bookwyrm/templates/snippets/create_status/content_warning_field.html:10 msgid "Content warning:" -msgstr "" +msgstr "内容警告:" #: bookwyrm/templates/snippets/create_status/content_warning_field.html:18 msgid "Spoilers ahead!" @@ -2961,7 +2958,7 @@ msgstr "前有剧透!" msgid "Include spoiler alert" msgstr "加入剧透警告" -#: bookwyrm/templates/snippets/create_status/layout.html:41 +#: bookwyrm/templates/snippets/create_status/layout.html:48 #: bookwyrm/templates/snippets/reading_modals/form.html:7 msgid "Comment:" msgstr "评论:" @@ -2988,15 +2985,15 @@ msgstr "摘自《%(book_title)s》的节录" #: bookwyrm/templates/snippets/create_status/quotation.html:32 msgid "Position:" -msgstr "" +msgstr "位置:" #: bookwyrm/templates/snippets/create_status/quotation.html:45 msgid "On page:" -msgstr "" +msgstr "页码:" #: bookwyrm/templates/snippets/create_status/quotation.html:51 msgid "At percent:" -msgstr "" +msgstr "百分比:" #: bookwyrm/templates/snippets/create_status/review.html:25 #, python-format @@ -3077,7 +3074,7 @@ msgstr "没有评价" #, python-format msgid "%(half_rating)s star" msgid_plural "%(half_rating)s stars" -msgstr[0] "" +msgstr[0] "%(half_rating)s 星" #: bookwyrm/templates/snippets/form_rate_stars.html:64 #: bookwyrm/templates/snippets/stars.html:7 @@ -3150,12 +3147,12 @@ msgstr "你已经阅读了 %(goal_count)s 本书中的 %(re msgid "%(username)s has read %(read_count)s of %(goal_count)s books." msgstr "%(username)s 已经阅读了 %(goal_count)s 本书中的 %(read_count)s 本。" -#: bookwyrm/templates/snippets/page_text.html:4 +#: bookwyrm/templates/snippets/page_text.html:8 #, python-format msgid "page %(page)s of %(total_pages)s" msgstr "%(total_pages)s 页中的第 %(page)s 页" -#: bookwyrm/templates/snippets/page_text.html:6 +#: bookwyrm/templates/snippets/page_text.html:14 #, python-format msgid "page %(page)s" msgstr "第 %(page)s 页" @@ -3220,7 +3217,7 @@ msgstr "已完成阅读" #: bookwyrm/templates/snippets/reading_modals/form.html:9 msgid "(Optional)" -msgstr "" +msgstr "(可选)" #: bookwyrm/templates/snippets/reading_modals/progress_update_modal.html:5 #: bookwyrm/templates/snippets/shelve_button/shelve_button_dropdown_options.html:50 @@ -3292,21 +3289,21 @@ msgstr "完成阅读" #: bookwyrm/templates/snippets/status/content_status.html:72 msgid "Content warning" -msgstr "" +msgstr "内容警告" #: bookwyrm/templates/snippets/status/content_status.html:79 msgid "Show status" -msgstr "" +msgstr "显示状态" #: bookwyrm/templates/snippets/status/content_status.html:101 #, python-format msgid "(Page %(page)s)" -msgstr "" +msgstr "(第 %(page)s 页)" #: bookwyrm/templates/snippets/status/content_status.html:103 #, python-format msgid "(%(percent)s%%)" -msgstr "" +msgstr "(%(percent)s%%)" #: bookwyrm/templates/snippets/status/content_status.html:125 msgid "Open image in new window" @@ -3314,6 +3311,11 @@ msgstr "在新窗口中打开图像" #: bookwyrm/templates/snippets/status/content_status.html:144 msgid "Hide status" +msgstr "隐藏状态" + +#: bookwyrm/templates/snippets/status/header.html:45 +#, python-format +msgid "edited %(date)s" msgstr "" #: bookwyrm/templates/snippets/status/headers/comment.html:2 @@ -3380,10 +3382,6 @@ msgstr "转发了" msgid "More options" msgstr "更多选项" -#: bookwyrm/templates/snippets/status/status_options.html:26 -msgid "Delete & re-draft" -msgstr "删除并重新起草" - #: bookwyrm/templates/snippets/suggested_users.html:16 #, python-format msgid "%(mutuals)s follower you follow" @@ -3566,5 +3564,5 @@ msgstr "密码重置连接已发送给 {email}" #: bookwyrm/views/rss_feed.py:35 #, python-brace-format msgid "Status updates from {obj.display_name}" -msgstr "" +msgstr "{obj.display_name} 的状态更新" diff --git a/locale/zh_Hant/LC_MESSAGES/django.mo b/locale/zh_Hant/LC_MESSAGES/django.mo index 97c0a9fb7..0bd0ad46e 100644 Binary files a/locale/zh_Hant/LC_MESSAGES/django.mo and b/locale/zh_Hant/LC_MESSAGES/django.mo differ diff --git a/locale/zh_Hant/LC_MESSAGES/django.po b/locale/zh_Hant/LC_MESSAGES/django.po index bcfcdf06b..7b9d3b778 100644 --- a/locale/zh_Hant/LC_MESSAGES/django.po +++ b/locale/zh_Hant/LC_MESSAGES/django.po @@ -2,8 +2,8 @@ msgid "" msgstr "" "Project-Id-Version: bookwyrm\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-10-06 23:57+0000\n" -"PO-Revision-Date: 2021-10-08 00:03\n" +"POT-Creation-Date: 2021-10-15 22:03+0000\n" +"PO-Revision-Date: 2021-10-16 14:36\n" "Last-Translator: Mouse Reeve \n" "Language-Team: Chinese Traditional\n" "Language: zh\n" @@ -54,8 +54,8 @@ msgstr "列表順序" msgid "Book Title" msgstr "書名" -#: bookwyrm/forms.py:328 bookwyrm/templates/shelf/shelf.html:134 -#: bookwyrm/templates/shelf/shelf.html:165 +#: bookwyrm/forms.py:328 bookwyrm/templates/shelf/shelf.html:136 +#: bookwyrm/templates/shelf/shelf.html:168 #: bookwyrm/templates/snippets/create_status/review.html:33 msgid "Rating" msgstr "評價" @@ -151,45 +151,49 @@ msgstr "使用者名稱" msgid "A user with that username already exists." msgstr "已經存在使用該名稱的使用者。" -#: bookwyrm/settings.py:117 +#: bookwyrm/settings.py:118 msgid "Home Timeline" msgstr "主頁時間線" -#: bookwyrm/settings.py:117 +#: bookwyrm/settings.py:118 msgid "Home" msgstr "主頁" -#: bookwyrm/settings.py:118 +#: bookwyrm/settings.py:119 msgid "Books Timeline" msgstr "" -#: bookwyrm/settings.py:118 bookwyrm/templates/search/layout.html:21 +#: bookwyrm/settings.py:119 bookwyrm/templates/search/layout.html:21 #: bookwyrm/templates/search/layout.html:42 #: bookwyrm/templates/user/layout.html:81 msgid "Books" msgstr "書目" -#: bookwyrm/settings.py:164 +#: bookwyrm/settings.py:165 msgid "English" msgstr "English(英語)" -#: bookwyrm/settings.py:165 +#: bookwyrm/settings.py:166 msgid "Deutsch (German)" msgstr "Deutsch(德語)" -#: bookwyrm/settings.py:166 +#: bookwyrm/settings.py:167 msgid "Español (Spanish)" msgstr "Español(西班牙語)" -#: bookwyrm/settings.py:167 +#: bookwyrm/settings.py:168 msgid "Français (French)" msgstr "Français(法語)" -#: bookwyrm/settings.py:168 +#: bookwyrm/settings.py:169 +msgid "Português - Brasil (Brazilian Portuguese)" +msgstr "" + +#: bookwyrm/settings.py:170 msgid "简体中文 (Simplified Chinese)" msgstr "簡體中文" -#: bookwyrm/settings.py:169 +#: bookwyrm/settings.py:171 msgid "繁體中文 (Traditional Chinese)" msgstr "繁體中文" @@ -668,11 +672,6 @@ msgstr "語言:" msgid "Search editions" msgstr "" -#: bookwyrm/templates/book/publisher_info.html:21 -#, python-format -msgid "%(format)s" -msgstr "%(format)s" - #: bookwyrm/templates/book/publisher_info.html:23 #, python-format msgid "%(format)s, %(pages)s pages" @@ -752,8 +751,8 @@ msgid "Help" msgstr "" #: bookwyrm/templates/compose.html:5 bookwyrm/templates/compose.html:8 -msgid "Compose status" -msgstr "撰寫狀態" +msgid "Edit status" +msgstr "" #: bookwyrm/templates/confirm_email/confirm_email.html:4 msgid "Confirm email" @@ -885,6 +884,26 @@ msgstr "BookWyrm 使用者" msgid "All known users" msgstr "所有已知使用者" +#: bookwyrm/templates/discover/card-header.html:9 +#, python-format +msgid "%(username)s rated %(book_title)s" +msgstr "" + +#: bookwyrm/templates/discover/card-header.html:13 +#, python-format +msgid "%(username)s reviewed %(book_title)s" +msgstr "" + +#: bookwyrm/templates/discover/card-header.html:17 +#, python-format +msgid "%(username)s commented on %(book_title)s" +msgstr "" + +#: bookwyrm/templates/discover/card-header.html:21 +#, python-format +msgid "%(username)s quoted %(book_title)s" +msgstr "" + #: bookwyrm/templates/discover/discover.html:4 #: bookwyrm/templates/discover/discover.html:10 #: bookwyrm/templates/layout.html:78 @@ -896,28 +915,8 @@ msgstr "" msgid "See what's new in the local %(site_name)s community" msgstr "" -#: bookwyrm/templates/discover/large-book.html:46 -#: bookwyrm/templates/discover/small-book.html:32 -msgid "rated" -msgstr "評價了" - -#: bookwyrm/templates/discover/large-book.html:48 -#: bookwyrm/templates/discover/small-book.html:34 -msgid "reviewed" -msgstr "寫了書評給" - -#: bookwyrm/templates/discover/large-book.html:50 -#: bookwyrm/templates/discover/small-book.html:36 -msgid "commented on" -msgstr "評論了" - #: bookwyrm/templates/discover/large-book.html:52 -#: bookwyrm/templates/discover/small-book.html:38 -msgid "quoted" -msgstr "引用了" - -#: bookwyrm/templates/discover/large-book.html:68 -#: bookwyrm/templates/discover/small-book.html:52 +#: bookwyrm/templates/discover/small-book.html:36 msgid "View status" msgstr "" @@ -971,8 +970,8 @@ msgstr "立即加入" #: bookwyrm/templates/email/invite/html_content.html:15 #, python-format -msgid "Learn more about this instance." -msgstr "瞭解更多 有關本實例的資訊。" +msgid "Learn more about %(site_name)s." +msgstr "" #: bookwyrm/templates/email/invite/text_content.html:4 #, python-format @@ -980,8 +979,9 @@ msgid "You're invited to join %(site_name)s! Click the link below to create an a msgstr "你受邀請加入 %(site_name)s!點選下面的連結來建立帳號。" #: bookwyrm/templates/email/invite/text_content.html:8 -msgid "Learn more about this instance:" -msgstr "瞭解更多有關本實例的資訊:" +#, python-format +msgid "Learn more about %(site_name)s:" +msgstr "" #: bookwyrm/templates/email/password_reset/html_content.html:6 #: bookwyrm/templates/email/password_reset/text_content.html:4 @@ -1320,13 +1320,13 @@ msgstr "書目" #: bookwyrm/templates/import/import_status.html:122 #: bookwyrm/templates/shelf/shelf.html:128 -#: bookwyrm/templates/shelf/shelf.html:148 +#: bookwyrm/templates/shelf/shelf.html:150 msgid "Title" msgstr "標題" #: bookwyrm/templates/import/import_status.html:125 #: bookwyrm/templates/shelf/shelf.html:129 -#: bookwyrm/templates/shelf/shelf.html:151 +#: bookwyrm/templates/shelf/shelf.html:153 msgid "Author" msgstr "作者" @@ -1335,7 +1335,7 @@ msgid "Imported" msgstr "已匯入" #: bookwyrm/templates/import/tooltip.html:6 -msgid "You can download your GoodReads data from the Import/Export page of your GoodReads account." +msgid "You can download your Goodreads data from the Import/Export page of your Goodreads account." msgstr "" #: bookwyrm/templates/invite.html:4 bookwyrm/templates/invite.html:8 @@ -1351,7 +1351,7 @@ msgstr "沒有權限" msgid "Sorry! This invite code is no longer valid." msgstr "抱歉!此邀請碼已不再有效。" -#: bookwyrm/templates/landing/about.html:7 +#: bookwyrm/templates/landing/about.html:7 bookwyrm/templates/layout.html:230 #, python-format msgid "About %(site_name)s" msgstr "關於 %(site_name)s" @@ -1482,10 +1482,6 @@ msgstr "" msgid "Error posting status" msgstr "" -#: bookwyrm/templates/layout.html:230 -msgid "About this instance" -msgstr "關於本實例" - #: bookwyrm/templates/layout.html:234 msgid "Contact site admin" msgstr "聯絡網站管理員" @@ -1730,23 +1726,23 @@ msgstr "轉發了你的 狀態" #: bookwyrm/templates/notifications/items/fav.html:19 #, python-format -msgid "favorited your review of %(book_title)s" -msgstr "喜歡了你 %(book_title)s 的書評" +msgid "liked your review of %(book_title)s" +msgstr "" #: bookwyrm/templates/notifications/items/fav.html:25 #, python-format -msgid "favorited your comment on%(book_title)s" +msgid "liked your comment on%(book_title)s" msgstr "" #: bookwyrm/templates/notifications/items/fav.html:31 #, python-format -msgid "favorited your quote from %(book_title)s" -msgstr "喜歡了你 來自 %(book_title)s 的引用" +msgid "liked your quote from %(book_title)s" +msgstr "" #: bookwyrm/templates/notifications/items/fav.html:37 #, python-format -msgid "favorited your status" -msgstr "喜歡了你的 狀態" +msgid "liked your status" +msgstr "" #: bookwyrm/templates/notifications/items/follow.html:15 msgid "followed you" @@ -2291,6 +2287,7 @@ msgid "Notes" msgstr "備註" #: bookwyrm/templates/settings/federation/instance.html:75 +#: bookwyrm/templates/snippets/status/status_options.html:24 msgid "Edit" msgstr "編輯" @@ -2630,7 +2627,7 @@ msgid "Short description:" msgstr "" #: bookwyrm/templates/settings/site.html:37 -msgid "Used when the instance is previewed on joinbookwyrm.com. Does not support html or markdown." +msgid "Used when the instance is previewed on joinbookwyrm.com. Does not support HTML or Markdown." msgstr "" #: bookwyrm/templates/settings/site.html:41 @@ -2805,7 +2802,7 @@ msgid "Permanently deleted" msgstr "" #: bookwyrm/templates/settings/users/user_moderation_actions.html:13 -#: bookwyrm/templates/snippets/status/status_options.html:35 +#: bookwyrm/templates/snippets/status/status_options.html:32 #: bookwyrm/templates/snippets/user_options.html:13 msgid "Send direct message" msgstr "發送私信" @@ -2857,22 +2854,22 @@ msgstr "編輯書架" msgid "Delete shelf" msgstr "刪除書架" -#: bookwyrm/templates/shelf/shelf.html:130 -#: bookwyrm/templates/shelf/shelf.html:154 +#: bookwyrm/templates/shelf/shelf.html:132 +#: bookwyrm/templates/shelf/shelf.html:158 msgid "Shelved" msgstr "上架時間" -#: bookwyrm/templates/shelf/shelf.html:131 -#: bookwyrm/templates/shelf/shelf.html:158 +#: bookwyrm/templates/shelf/shelf.html:133 +#: bookwyrm/templates/shelf/shelf.html:161 msgid "Started" msgstr "開始時間" -#: bookwyrm/templates/shelf/shelf.html:132 -#: bookwyrm/templates/shelf/shelf.html:161 +#: bookwyrm/templates/shelf/shelf.html:134 +#: bookwyrm/templates/shelf/shelf.html:164 msgid "Finished" msgstr "完成時間" -#: bookwyrm/templates/shelf/shelf.html:187 +#: bookwyrm/templates/shelf/shelf.html:190 msgid "This shelf is empty." msgstr "此書架是空的。" @@ -2918,22 +2915,22 @@ msgstr "引用" msgid "Some thoughts on the book" msgstr "" -#: bookwyrm/templates/snippets/create_status/comment.html:26 +#: bookwyrm/templates/snippets/create_status/comment.html:27 #: bookwyrm/templates/snippets/reading_modals/progress_update_modal.html:15 msgid "Progress:" msgstr "進度:" -#: bookwyrm/templates/snippets/create_status/comment.html:52 +#: bookwyrm/templates/snippets/create_status/comment.html:53 #: bookwyrm/templates/snippets/progress_field.html:18 msgid "pages" msgstr "頁數" -#: bookwyrm/templates/snippets/create_status/comment.html:58 +#: bookwyrm/templates/snippets/create_status/comment.html:59 #: bookwyrm/templates/snippets/progress_field.html:23 msgid "percent" msgstr "百分比" -#: bookwyrm/templates/snippets/create_status/comment.html:65 +#: bookwyrm/templates/snippets/create_status/comment.html:66 #, python-format msgid "of %(pages)s pages" msgstr "全書 %(pages)s 頁" @@ -2961,7 +2958,7 @@ msgstr "前有劇透!" msgid "Include spoiler alert" msgstr "加入劇透警告" -#: bookwyrm/templates/snippets/create_status/layout.html:41 +#: bookwyrm/templates/snippets/create_status/layout.html:48 #: bookwyrm/templates/snippets/reading_modals/form.html:7 msgid "Comment:" msgstr "評論:" @@ -3150,12 +3147,12 @@ msgstr "你已經閱讀了 %(goal_count)s 本書中的 %(re msgid "%(username)s has read %(read_count)s of %(goal_count)s books." msgstr "%(username)s 已經閱讀了 %(goal_count)s 本書中的 %(read_count)s 本。" -#: bookwyrm/templates/snippets/page_text.html:4 +#: bookwyrm/templates/snippets/page_text.html:8 #, python-format msgid "page %(page)s of %(total_pages)s" msgstr "%(total_pages)s 頁中的第 %(page)s 頁" -#: bookwyrm/templates/snippets/page_text.html:6 +#: bookwyrm/templates/snippets/page_text.html:14 #, python-format msgid "page %(page)s" msgstr "第 %(page)s 頁" @@ -3316,6 +3313,11 @@ msgstr "在新視窗中開啟圖片" msgid "Hide status" msgstr "" +#: bookwyrm/templates/snippets/status/header.html:45 +#, python-format +msgid "edited %(date)s" +msgstr "" + #: bookwyrm/templates/snippets/status/headers/comment.html:2 #, python-format msgid "commented on %(book)s" @@ -3380,10 +3382,6 @@ msgstr "轉發了" msgid "More options" msgstr "更多選項" -#: bookwyrm/templates/snippets/status/status_options.html:26 -msgid "Delete & re-draft" -msgstr "刪除並重新起草" - #: bookwyrm/templates/snippets/suggested_users.html:16 #, python-format msgid "%(mutuals)s follower you follow" diff --git a/requirements.txt b/requirements.txt index 93bb4073d..63bed4e6c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,7 +3,7 @@ colorthief==0.2.1 Django==3.2.5 django-imagekit==4.0.2 django-model-utils==4.0.0 -environs==7.2.0 +environs==9.3.4 flower==0.9.4 gunicorn==20.0.4 Markdown==3.3.3