more i18n fixes

This commit is contained in:
Your Name 2024-05-26 22:57:49 -04:00 committed by Henri Dickson
parent 9f5b6c26c8
commit 76a1114bf7
22 changed files with 341 additions and 208 deletions

View file

@ -159,13 +159,14 @@ class Edition(Item):
return [(i.value, i.label) for i in id_types]
@classmethod
def lookup_id_cleanup(cls, lookup_id_type, lookup_id_value):
def lookup_id_cleanup(cls, lookup_id_type: str | IdType, lookup_id_value: str):
if lookup_id_type in [IdType.ASIN.value, IdType.ISBN.value]:
return detect_isbn_asin(lookup_id_value)
return super().lookup_id_cleanup(lookup_id_type, lookup_id_value)
def merge_to(self, to_item):
def merge_to(self, to_item: "Edition | None"):
super().merge_to(to_item)
if to_item:
for work in self.works.all():
to_item.works.add(work)
self.works.clear()
@ -278,8 +279,9 @@ class Work(Item):
]
return [(i.value, i.label) for i in id_types]
def merge_to(self, to_item):
def merge_to(self, to_item: "Work | None"):
super().merge_to(to_item)
if to_item:
for edition in self.editions.all():
to_item.editions.add(edition)
self.editions.clear()
@ -288,7 +290,7 @@ class Work(Item):
and self.title != to_item.title
and self.title not in to_item.other_title
):
to_item.other_title += [self.title]
to_item.other_title += [self.title] # type: ignore
to_item.save()
def delete(self, using=None, soft=True, *args, **kwargs):

View file

@ -50,14 +50,15 @@ def is_asin(asin):
return re.match(r"^B[A-Z0-9]{9}$", asin) is not None
def detect_isbn_asin(s):
def detect_isbn_asin(s: str) -> tuple[IdType, str] | tuple[None, None]:
if not s:
return None, None
n = re.sub(r"[^0-9A-Z]", "", s.upper())
if is_isbn_13(n):
return IdType.ISBN, n
if is_isbn_10(n):
return IdType.ISBN, isbn_10_to_13(n)
v = isbn_10_to_13(n)
return (IdType.ISBN, v) if v else (None, None)
if is_asin(n):
return IdType.ASIN, n
return None, None

View file

@ -1,7 +1,7 @@
import re
import uuid
from functools import cached_property
from typing import TYPE_CHECKING, cast
from typing import TYPE_CHECKING, Any, Iterable, Type, cast
from auditlog.context import disable_auditlog
from auditlog.models import AuditlogHistoryField, LogEntry
@ -9,6 +9,7 @@ from django.conf import settings
from django.contrib.contenttypes.models import ContentType
from django.core.files.uploadedfile import SimpleUploadedFile
from django.db import connection, models
from django.db.models import QuerySet
from django.utils import timezone
from django.utils.baseconv import base62
from django.utils.translation import gettext_lazy as _
@ -145,26 +146,26 @@ class AvailableItemCategory(models.TextChoices):
# class SubItemType(models.TextChoices):
# Season = "season", _("剧集分季")
# Episode = "episode", _("剧集分集")
# Version = "version", _("版本")
# Season = "season", _("season")
# Episode = "episode", _("episode")
# Version = "production", _("production")
# class CreditType(models.TextChoices):
# Author = 'author', _('作者')
# Translater = 'translater', _('译者')
# Producer = 'producer', _('出品人')
# Director = 'director', _('电影')
# Actor = 'actor', _('演员')
# Playwright = 'playwright', _('播客')
# VoiceActor = 'voiceactor', _('配音')
# Host = 'host', _('主持人')
# Developer = 'developer', _('开发者')
# Publisher = 'publisher', _('出版方')
# Author = 'author', _('author')
# Translater = 'translater', _('translater')
# Producer = 'producer', _('producer')
# Director = 'director', _('director')
# Actor = 'actor', _('actor')
# Playwright = 'playwright', _('playwright')
# VoiceActor = 'voiceactor', _('voiceactor')
# Host = 'host', _('host')
# Developer = 'developer', _('developer')
# Publisher = 'publisher', _('publisher')
class PrimaryLookupIdDescriptor(object): # TODO make it mixin of Field
def __init__(self, id_type):
def __init__(self, id_type: IdType):
self.id_type = id_type
def __get__(self, instance, cls=None):
@ -184,7 +185,7 @@ class PrimaryLookupIdDescriptor(object): # TODO make it mixin of Field
class LookupIdDescriptor(object): # TODO make it mixin of Field
def __init__(self, id_type):
def __init__(self, id_type: IdType):
self.id_type = id_type
def __get__(self, instance, cls=None):
@ -198,14 +199,14 @@ class LookupIdDescriptor(object): # TODO make it mixin of Field
# class ItemId(models.Model):
# item = models.ForeignKey('Item', models.CASCADE)
# id_type = models.CharField(_("源网站"), blank=False, choices=IdType.choices, max_length=50)
# id_value = models.CharField(_("源网站ID"), blank=False, max_length=1000)
# id_type = models.CharField(_("Id Type"), blank=False, choices=IdType.choices, max_length=50)
# id_value = models.CharField(_("ID Value"), blank=False, max_length=1000)
# class ItemCredit(models.Model):
# item = models.ForeignKey('Item', models.CASCADE)
# credit_type = models.CharField(_("类型"), choices=CreditType.choices, blank=False, max_length=50)
# name = models.CharField(_("名字"), blank=False, max_length=1000)
# credit_type = models.CharField(_("Credit Type"), choices=CreditType.choices, blank=False, max_length=50)
# name = models.CharField(_("Name"), blank=False, max_length=1000)
# def check_source_id(sid):
@ -241,11 +242,11 @@ class ItemInSchema(Schema):
rating_count: int | None
class ItemSchema(ItemInSchema, BaseSchema):
class ItemSchema(BaseSchema, ItemInSchema):
pass
class Item(SoftDeleteMixin, PolymorphicModel):
class Item(PolymorphicModel, SoftDeleteMixin):
url_path = "item" # subclass must specify this
type = None # subclass must specify this
child_class = None # subclass may specify this to allow link to parent item
@ -315,13 +316,15 @@ class Item(SoftDeleteMixin, PolymorphicModel):
return IdType.choices
@classmethod
def lookup_id_cleanup(cls, lookup_id_type, lookup_id_value):
def lookup_id_cleanup(
cls, lookup_id_type: str | IdType, lookup_id_value: str
) -> tuple[str | IdType, str] | tuple[None, None]:
if not lookup_id_type or not lookup_id_value or not lookup_id_value.strip():
return None, None
return lookup_id_type, lookup_id_value.strip()
@classmethod
def get_best_lookup_id(cls, lookup_ids):
def get_best_lookup_id(cls, lookup_ids: dict[IdType, str]) -> tuple[IdType, str]:
"""get best available lookup id, ideally commonly used"""
for t in IdealIdTypes:
if lookup_ids.get(t):
@ -333,23 +336,23 @@ class Item(SoftDeleteMixin, PolymorphicModel):
return None
@property
def child_items(self):
def child_items(self) -> "QuerySet[Item]":
return Item.objects.none()
@property
def child_item_ids(self):
def child_item_ids(self) -> list[int]:
return list(self.child_items.values_list("id", flat=True))
def set_parent_item(self, value):
def set_parent_item(self, value: "Item | None"):
# raise ValueError("cannot set parent item")
pass
@property
def parent_uuid(self):
def parent_uuid(self) -> str | None:
return self.parent_item.uuid if self.parent_item else None
@property
def sibling_items(self):
def sibling_items(self) -> "QuerySet[Item]":
return Item.objects.none()
@property
@ -357,19 +360,19 @@ class Item(SoftDeleteMixin, PolymorphicModel):
return ""
@property
def sibling_item_ids(self):
def sibling_item_ids(self) -> list[int]:
return list(self.sibling_items.values_list("id", flat=True))
@classmethod
def get_ap_object_type(cls):
def get_ap_object_type(cls) -> str:
return cls.__name__
@property
def ap_object_type(self):
def ap_object_type(self) -> str:
return self.get_ap_object_type()
@property
def ap_object_ref(self):
def ap_object_ref(self) -> dict[str, Any]:
o = {
"type": self.get_ap_object_type(),
"href": self.absolute_url,
@ -379,12 +382,12 @@ class Item(SoftDeleteMixin, PolymorphicModel):
o["image"] = self.cover_image_url
return o
def log_action(self, changes):
def log_action(self, changes: dict[str, Any]):
LogEntry.objects.log_create( # type: ignore
self, action=LogEntry.Action.UPDATE, changes=changes
)
def merge_to(self, to_item):
def merge_to(self, to_item: "Item | None"):
if to_item is None:
if self.merged_to_item is not None:
self.merged_to_item = None
@ -394,7 +397,7 @@ class Item(SoftDeleteMixin, PolymorphicModel):
raise ValueError("cannot merge to self")
if to_item.merged_to_item is not None:
raise ValueError("cannot merge to item which is merged to another item")
if to_item.__class__ != self.__class__:
if not isinstance(to_item, self.__class__):
raise ValueError("cannot merge to item in a different model")
self.log_action({"!merged": [str(self.merged_to_item), str(to_item)]})
self.merged_to_item = to_item
@ -403,11 +406,11 @@ class Item(SoftDeleteMixin, PolymorphicModel):
res.item = to_item
res.save()
def recast_to(self, model):
def recast_to(self, model: "type[Item]") -> "Item":
logger.warning(f"recast item {self} to {model}")
if self.__class__ == model:
if isinstance(self, model):
return self
if model not in Item.__subclasses__():
if not issubclass(model, Item):
raise ValueError("invalid model to recast to")
ct = ContentType.objects.get_for_model(model)
old_ct = self.polymorphic_ctype
@ -442,15 +445,15 @@ class Item(SoftDeleteMixin, PolymorphicModel):
return f"/api{self.url}"
@property
def class_name(self):
def class_name(self) -> str:
return self.__class__.__name__.lower()
@property
def display_title(self):
def display_title(self) -> str:
return self.title
@classmethod
def get_by_url(cls, url_or_b62):
def get_by_url(cls, url_or_b62: str) -> "Item | None":
b62 = url_or_b62.strip().split("/")[-1]
if len(b62) not in [21, 22]:
r = re.search(r"[A-Za-z0-9]{21,22}", url_or_b62)
@ -633,7 +636,7 @@ class ExternalResource(models.Model):
return SiteName.Unknown
@property
def site_label(self):
def site_label(self) -> str:
if self.id_type == IdType.Fediverse:
from takahe.utils import Takahe
@ -704,7 +707,7 @@ def item_content_types():
_CATEGORY_LIST = None
def item_categories():
def item_categories() -> dict[ItemCategory, list[type[Item]]]:
global _CATEGORY_LIST
if _CATEGORY_LIST is None:
_CATEGORY_LIST = {}

View file

@ -114,12 +114,14 @@ class PodcastEpisode(Item):
self.program = value
@property
def display_title(self):
return f"{self.program.title} - {self.title}"
def display_title(self) -> str:
return f"{self.program.title} - {self.title}" if self.program else self.title
@property
def cover_image_url(self):
return self.cover_url or self.program.cover_image_url
def cover_image_url(self) -> str | None:
return self.cover_url or (
self.program.cover_image_url if self.program else None
)
def get_url_with_position(self, position=None):
return (

View file

@ -135,7 +135,7 @@ class BooksTW(AbstractSite):
}
pd = ResourceContent(metadata=data)
t, n = detect_isbn_asin(isbn)
t, n = detect_isbn_asin(str(isbn))
if t:
pd.lookup_ids[t] = n
pd.cover_image, pd.cover_image_extention = BasicImageDownloader.download_image(

View file

@ -169,7 +169,7 @@ def list_reviews(request, category: AvailableItemCategory | None = None):
"""
queryset = Review.objects.filter(owner=request.user.identity)
if category:
queryset = queryset.filter(q_item_in_category(category))
queryset = queryset.filter(q_item_in_category(category)) # type: ignore[arg-type]
return queryset.prefetch_related("item")

View file

@ -4,7 +4,6 @@ from .common import (
Piece,
PieceInteraction,
PiecePost,
UserOwnedObjectMixin,
VisibilityType,
max_visiblity_to_user,
q_item_in_category,
@ -14,6 +13,7 @@ from .common import (
)
from .like import Like
from .mark import Mark
from .mixins import UserOwnedObjectMixin
from .rating import Rating
from .renderers import render_md
from .review import Review
@ -25,3 +25,37 @@ from .utils import (
reset_journal_visibility_for_user,
update_journal_for_merged_item,
)
__all__ = [
"Collection",
"CollectionMember",
"FeaturedCollection",
"Comment",
"Piece",
"PieceInteraction",
"PiecePost",
"UserOwnedObjectMixin",
"VisibilityType",
"max_visiblity_to_user",
"q_item_in_category",
"q_owned_piece_visible_to_user",
"q_piece_in_home_feed_of_user",
"q_piece_visible_to_user",
"Like",
"Mark",
"Rating",
"render_md",
"Review",
"Shelf",
"ShelfLogEntry",
"ShelfManager",
"ShelfMember",
"ShelfType",
"Tag",
"TagManager",
"TagMember",
"journal_exists_for_item",
"remove_data_by_user",
"reset_journal_visibility_for_user",
"update_journal_for_merged_item",
]

View file

@ -73,7 +73,7 @@ def q_piece_in_home_feed_of_user(viewing_user: User):
return Q(owner_id__in=viewer.following, visibility__lt=2) | Q(owner_id=viewer.pk)
def q_item_in_category(item_category: ItemCategory | AvailableItemCategory):
def q_item_in_category(item_category: ItemCategory):
classes = item_categories()[item_category]
# q = Q(item__instance_of=classes[0])
# for cls in classes[1:]:

View file

@ -1,10 +1,10 @@
from typing import TYPE_CHECKING
from users.models import APIdentity, User
if TYPE_CHECKING:
from django.db.models import ForeignKey
from users.models import APIdentity, User
from .common import Piece
@ -21,7 +21,9 @@ class UserOwnedObjectMixin:
owner: ForeignKey[APIdentity, Piece]
visibility: int
def is_visible_to(self: "Piece", viewing_user: User) -> bool: # noqa # type: ignore
def is_visible_to(
self: "Piece", viewing_user: "User" # noqa # type: ignore
) -> bool:
owner = self.owner
if not owner or not owner.is_active:
return False
@ -41,7 +43,7 @@ class UserOwnedObjectMixin:
else:
return True
def is_editable_by(self: "Piece", viewing_user: User): # type: ignore
def is_editable_by(self: "Piece", viewing_user: "User"): # type: ignore
return viewing_user.is_authenticated and (
viewing_user.is_staff
or viewing_user.is_superuser

View file

@ -71,7 +71,7 @@
<span class="timestamp">{{ mark.review.created_time|date }}</span>
</span>
<div class="tldr">
评论:
{% trans "Review" %}
<span>
<a href="{% url 'journal:review_retrieve' mark.review.uuid %}">{{ mark.review.title }}</a>
</span>

View file

@ -23,44 +23,44 @@
{% endif %}
{% endfor %}
<option {% if '/reviews/' in request.path %}selected{% endif %}
value="reviews">评论</option>
value="reviews">{% trans "review" %}</option>
</select>
<select name="category" id="category" onchange="filter()">
{% visible_categories as cats %}
{% if 'book' in cats %}
<option {% if '/book/' in request.path %}selected{% endif %} value="book">书籍</option>
<option {% if '/book/' in request.path %}selected{% endif %} value="book">{% trans "Book" %}</option>
{% endif %}
{% if 'movie' in cats %}
<option {% if '/movie/' in request.path %}selected{% endif %} value="movie">电影</option>
<option {% if '/movie/' in request.path %}selected{% endif %} value="movie">{% trans "Movie" %}</option>
{% endif %}
{% if 'tv' in cats %}
<option {% if '/tv/' in request.path %}selected{% endif %} value="tv">剧集</option>
<option {% if '/tv/' in request.path %}selected{% endif %} value="tv">{% trans "TV" %}</option>
{% endif %}
{% if 'podcast' in cats %}
<option {% if '/podcast/' in request.path %}selected{% endif %}
value="podcast">播客</option>
value="podcast">{% trans "Podcast" %}</option>
{% endif %}
{% if 'music' in cats %}
<option {% if '/music/' in request.path %}selected{% endif %} value="music">音乐</option>
<option {% if '/music/' in request.path %}selected{% endif %} value="music">{% trans "Music" %}</option>
{% endif %}
{% if 'game' in cats %}
<option {% if '/game/' in request.path %}selected{% endif %} value="game">游戏</option>
<option {% if '/game/' in request.path %}selected{% endif %} value="game">{% trans "Game" %}</option>
{% endif %}
{% if 'performance' in cats %}
<option {% if '/performance/' in request.path %}selected{% endif %}
value="performance">演出</option>
value="performance">{% trans "Performance" %}</option>
{% endif %}
</select>
<select name="year" id="year" onchange="filter()">
<option value="">全部</option>
<option value="">{% trans "all" %}</option>
{% for y in years %}
<option value="{{ y }}" {% if y == year %}selected{% endif %}>{{ y }}</option>
{% endfor %}
</select>
<select name="sort" id="sort" onchange="filter()">
<option value="" disabled>排列顺序</option>
<option value="">时间</option>
<option value="rating" {% if 'rating' == sort %}selected{% endif %}>评分</option>
<option value="" disabled>{% trans "sorting" %}</option>
<option value="">{% trans "date" %}</option>
<option value="rating" {% if 'rating' == sort %}selected{% endif %}>{% trans "rating" %}</option>
</select>
</fieldset>
</form>

View file

@ -5,7 +5,7 @@
data: $("#calendar_data").text(),
start_monday: false,
always_show_tooltip: false,
month_names: ['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月'],
month_names: ['{% trans "JAN" %}', '{% trans "FEB" %}', '{% trans "MAR" %}', '{% trans "APR" %}', '{% trans "MAY" %}', '{% trans "JUN" %}', '{% trans "JUL" %}', '{% trans "AUG" %}', '{% trans "SEP" %}', '{% trans "OCT" %}', '{% trans "NOV" %}', '{% trans "DEC" %}'],
day_names: ['', '', ''],
colors: {
'default': getComputedStyle(document.documentElement).getPropertyValue('--pico-form-element-background-color'),

View file

@ -89,17 +89,17 @@
<section style="min-width:10vw;">
<div class="donut" style="background: conic-gradient(#7CBDFE 0deg {{ stats.complete_deg }}deg, #B4D2A5 {{ stats.complete_deg }}deg {{ stats.complete_deg|add:stats.progress_deg }}deg, #ccc {{ stats.complete_deg|add:stats.progress_deg }}deg 1deg );"><div class="hole"><div class="text">
{% if stats.progress %}
{{ stats.progress }} 进行中<br>
{{ stats.progress }} in progress<br>
{% endif %}
{% if stats.complete %}
{{ stats.complete }} 已完成
{{ stats.complete }} completed
{% elif not stats.progress %}
尚未开始
not started
{% endif %}
</div></div></div>
<div style="margin:8px; color:lightgray" class="muted">
开始于{{ featured_since|date }}
<a class="muted" href="#" title="停止" onclick="if (confirm('停止这个目标吗?')) $('#stop-featured').submit();"><i class="fa-solid fa-ban"></i></a>
Started {{ featured_since|date }}
<a class="muted" href="#" title="stop" onclick="if (confirm('Stop this target?')) $('#stop-featured').submit();"><i class="fa-solid fa-ban"></i></a>
<form action="{% url 'journal:collection_remove_featured' collection.uuid %}" method="post" id="stop-featured">
{% csrf_token %}
</form>

View file

@ -66,7 +66,7 @@
</div>
</div>
<div>
<input type="submit" class="button float-right" value="保存">
<input type="submit" class="button float-right" value="{% trans "Save" %}">
</div>
<div {% if item.class_name != "podcastepisode" %}style="display:none;"{% endif %}>
<div role="group">

View file

@ -11,8 +11,12 @@
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{ site_name }} - {{ identity.display_name }} -
{% if liked %}喜欢的{% endif %}
收藏单</title>
{% if liked %}
{% trans "Liked Collections" %}
{% else %}
{% trans "Collections" %}
{% endif %}
</title>
{% include "common_libs.html" %}
</head>
<body>
@ -21,8 +25,11 @@
<div class="grid__main">
<h5>
{{ identity.display_name }} -
{% if liked %}喜欢的{% endif %}
收藏单
{% if liked %}
{% trans "Liked Collections" %}
{% else %}
{% trans "Collections" %}
{% endif %}
</h5>
{% for collection in collections %}
<p>
@ -41,7 +48,7 @@
{% endif %}
</p>
{% empty %}
<div class="empty">暂无</div>
<div class="empty">{% trans "nothing so far." %}</div>
{% endfor %}
</div>
{% include "_sidebar.html" with show_profile=1 %}

View file

@ -10,7 +10,6 @@
{% if tag.visibility > 0 %}
<i class="fa-solid fa-user" title="{% trans "private tag" %}"></i>
{% endif %}
个人标签
{{ identity.display_name }} - {% trans 'Tags' %}
{% if identity.user == request.user %}
<form style="display:inline"

View file

@ -179,7 +179,7 @@ def collection_append_item(request: AuthedHttpRequest, collection_uuid):
if not collection.is_editable_by(request.user):
raise PermissionDenied(_("Insufficient permission"))
url = request.POST.get("url")
url = request.POST.get("url", "")
note = request.POST.get("note")
item = Item.get_by_url(url)
if item:

View file

@ -6,7 +6,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-05-25 23:36-0400\n"
"POT-Creation-Date: 2024-05-26 22:53-0400\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@ -33,7 +33,7 @@ msgstr "副标题"
msgid "original title"
msgstr "原名"
#: catalog/book/models.py:99 catalog/book/models.py:249
#: catalog/book/models.py:99 catalog/book/models.py:250
msgid "author"
msgstr "作者"
@ -85,322 +85,329 @@ msgstr "价格"
msgid "imprint"
msgstr "出品方"
#: catalog/book/models.py:256 catalog/game/models.py:58
#: catalog/book/models.py:257 catalog/game/models.py:58
#: catalog/movie/models.py:68 catalog/music/models.py:85
#: catalog/performance/models.py:111 catalog/performance/models.py:260
#: catalog/tv/models.py:131 catalog/tv/models.py:292
msgid "other title"
msgstr "其它标题"
#: catalog/common/models.py:29
#: catalog/common/models.py:30
msgid "Unknown"
msgstr "未知"
#: catalog/common/models.py:30
#: catalog/common/models.py:31
msgid "Douban"
msgstr "豆瓣"
#: catalog/common/models.py:31 catalog/common/models.py:64
#: catalog/common/models.py:32 catalog/common/models.py:65
msgid "Goodreads"
msgstr "Goodreads"
#: catalog/common/models.py:32 catalog/common/models.py:66
#: catalog/common/models.py:33 catalog/common/models.py:67
msgid "Google Books"
msgstr "谷歌图书"
#: catalog/common/models.py:33
#: catalog/common/models.py:34
msgid "BooksTW"
msgstr "博客來"
#: catalog/common/models.py:34 catalog/common/models.py:59
#: catalog/common/models.py:35 catalog/common/models.py:60
#: catalog/templates/movie.html:51 catalog/templates/tvseason.html:68
#: catalog/templates/tvshow.html:63
msgid "IMDb"
msgstr "IMDb"
#: catalog/common/models.py:35
#: catalog/common/models.py:36
msgid "TMDB"
msgstr "TMDB"
#: catalog/common/models.py:36 catalog/common/models.py:75
#: catalog/common/models.py:37 catalog/common/models.py:76
msgid "Bandcamp"
msgstr "Bandcamp"
#: catalog/common/models.py:37
#: catalog/common/models.py:38
msgid "Spotify"
msgstr "Spotify"
#: catalog/common/models.py:38
#: catalog/common/models.py:39
msgid "IGDB"
msgstr "IGDB"
#: catalog/common/models.py:39
#: catalog/common/models.py:40
msgid "Steam"
msgstr "Steam"
#: catalog/common/models.py:40 catalog/common/models.py:89
#: catalog/common/models.py:41 catalog/common/models.py:90
msgid "Bangumi"
msgstr "Bangumi"
#: catalog/common/models.py:41
#: catalog/common/models.py:42
msgid "BGG"
msgstr "BGG"
#: catalog/common/models.py:43
#: catalog/common/models.py:44
msgid "RSS"
msgstr "RSS"
#: catalog/common/models.py:44
#: catalog/common/models.py:45
msgid "Discogs"
msgstr "Discogs"
#: catalog/common/models.py:45 catalog/common/models.py:91
#: catalog/common/models.py:46 catalog/common/models.py:92
msgid "Apple Music"
msgstr "苹果音乐"
#: catalog/common/models.py:46 catalog/common/models.py:92
#: catalog/common/models.py:47 catalog/common/models.py:93
msgid "Fediverse"
msgstr "联邦宇宙"
#: catalog/common/models.py:50
#: catalog/common/models.py:51
msgid "WikiData"
msgstr "维基数据"
#: catalog/common/models.py:51
#: catalog/common/models.py:52
msgid "ISBN10"
msgstr "ISBN10"
#: catalog/common/models.py:52 catalog/templates/edition.html:19
#: catalog/common/models.py:53 catalog/templates/edition.html:19
msgid "ISBN"
msgstr "ISBN"
#: catalog/common/models.py:53
#: catalog/common/models.py:54
msgid "ASIN"
msgstr "ASIN"
#: catalog/common/models.py:54
#: catalog/common/models.py:55
msgid "ISSN"
msgstr "ISSN"
#: catalog/common/models.py:55
#: catalog/common/models.py:56
msgid "CUBN"
msgstr "统一书号"
#: catalog/common/models.py:56
#: catalog/common/models.py:57
msgid "ISRC"
msgstr "ISRC"
#: catalog/common/models.py:57
#: catalog/common/models.py:58
msgid "GTIN UPC EAN"
msgstr "条形码"
#: catalog/common/models.py:58
#: catalog/common/models.py:59
msgid "RSS Feed URL"
msgstr "RSS网址"
#: catalog/common/models.py:60
#: catalog/common/models.py:61
msgid "TMDB TV Serie"
msgstr "TMDB电视剧集"
#: catalog/common/models.py:61
#: catalog/common/models.py:62
msgid "TMDB TV Season"
msgstr "TMDB电视分季"
#: catalog/common/models.py:62
#: catalog/common/models.py:63
msgid "TMDB TV Episode"
msgstr "TMDB电视单集"
#: catalog/common/models.py:63
#: catalog/common/models.py:64
msgid "TMDB Movie"
msgstr "TMDB电影"
#: catalog/common/models.py:65
#: catalog/common/models.py:66
msgid "Goodreads Work"
msgstr "Goodreads著作"
#: catalog/common/models.py:67
#: catalog/common/models.py:68
msgid "Douban Book"
msgstr "豆瓣图书"
#: catalog/common/models.py:68
#: catalog/common/models.py:69
msgid "Douban Book Work"
msgstr "豆瓣图书著作"
#: catalog/common/models.py:69
#: catalog/common/models.py:70
msgid "Douban Movie"
msgstr "豆瓣电影"
#: catalog/common/models.py:70
#: catalog/common/models.py:71
msgid "Douban Music"
msgstr "豆瓣音乐"
#: catalog/common/models.py:71
#: catalog/common/models.py:72
msgid "Douban Game"
msgstr "豆瓣游戏"
#: catalog/common/models.py:72
#: catalog/common/models.py:73
msgid "Douban Drama"
msgstr "豆瓣舞台剧"
#: catalog/common/models.py:73
#: catalog/common/models.py:74
msgid "Douban Drama Version"
msgstr "豆瓣舞台剧版本"
#: catalog/common/models.py:74
#: catalog/common/models.py:75
msgid "BooksTW Book"
msgstr "博客来图书"
#: catalog/common/models.py:76
#: catalog/common/models.py:77
msgid "Spotify Album"
msgstr "Spotify专辑"
#: catalog/common/models.py:77
#: catalog/common/models.py:78
msgid "Spotify Podcast"
msgstr "Spotify播客"
#: catalog/common/models.py:86
#: catalog/common/models.py:87
msgid "IGDB Game"
msgstr "IGDB游戏"
#: catalog/common/models.py:87
#: catalog/common/models.py:88
msgid "BGG Boardgame"
msgstr "BGG桌游"
#: catalog/common/models.py:88
#: catalog/common/models.py:89
msgid "Steam Game"
msgstr "Steam游戏"
#: catalog/common/models.py:90
#: catalog/common/models.py:91
msgid "Apple Podcast"
msgstr "苹果播客"
#: catalog/common/models.py:108 catalog/common/models.py:125
#: catalog/common/models.py:138 catalog/jobs/discover.py:91
#: catalog/common/models.py:109 catalog/common/models.py:126
#: catalog/common/models.py:139 catalog/jobs/discover.py:91
#: common/templates/_header.html:25
#: journal/templates/_sidebar_user_mark_list.html:31
msgid "Book"
msgstr "书"
#: catalog/common/models.py:109
#: catalog/common/models.py:110
msgid "TV Serie"
msgstr "电视剧集"
#: catalog/common/models.py:110 catalog/templates/_sidebar_edit.html:140
#: catalog/common/models.py:111 catalog/templates/_sidebar_edit.html:140
msgid "TV Season"
msgstr "电视分季"
#: catalog/common/models.py:111
#: catalog/common/models.py:112
msgid "TV Episode"
msgstr "电视单集"
#: catalog/common/models.py:112 catalog/common/models.py:126
#: catalog/common/models.py:139 catalog/templates/_sidebar_edit.html:133
#: catalog/common/models.py:113 catalog/common/models.py:127
#: catalog/common/models.py:140 catalog/templates/_sidebar_edit.html:133
#: journal/templates/_sidebar_user_mark_list.html:34
msgid "Movie"
msgstr "电影"
#: catalog/common/models.py:113
#: catalog/common/models.py:114
msgid "Album"
msgstr "专辑"
#: catalog/common/models.py:114 catalog/common/models.py:129
#: catalog/common/models.py:142 common/templates/_header.html:41
#: catalog/common/models.py:115 catalog/common/models.py:130
#: catalog/common/models.py:143 common/templates/_header.html:41
#: journal/templates/_sidebar_user_mark_list.html:47
msgid "Game"
msgstr "游戏"
#: catalog/common/models.py:115
#: catalog/common/models.py:116
msgid "Podcast Program"
msgstr "播客节目"
#: catalog/common/models.py:116
#: catalog/common/models.py:117
msgid "Podcast Episode"
msgstr "播客单集"
#: catalog/common/models.py:117 catalog/common/models.py:131
#: catalog/common/models.py:144 common/templates/_header.html:45
#: catalog/common/models.py:118 catalog/common/models.py:132
#: catalog/common/models.py:145 common/templates/_header.html:45
#: journal/templates/_sidebar_user_mark_list.html:51
msgid "Performance"
msgstr "演出"
#: catalog/common/models.py:118
#: catalog/common/models.py:119
msgid "Production"
msgstr "上演"
#: catalog/common/models.py:119
#: catalog/common/models.py:120
msgid "Fanfic"
msgstr "网文"
#: catalog/common/models.py:120 catalog/common/models.py:133
#: catalog/common/models.py:121 catalog/common/models.py:134
msgid "Exhibition"
msgstr "展览"
#: catalog/common/models.py:121 catalog/common/models.py:134
#: catalog/common/models.py:122 catalog/common/models.py:135
#: journal/templates/collection.html:15 journal/templates/collection.html:22
#: journal/templates/collection_edit.html:9
#: journal/templates/collection_share.html:12
msgid "Collection"
msgstr "收藏单"
#: catalog/common/models.py:127 catalog/common/models.py:140
#: catalog/common/models.py:128 catalog/common/models.py:141
#: journal/templates/_sidebar_user_mark_list.html:37
msgid "TV"
msgstr "剧集"
#: catalog/common/models.py:128 catalog/common/models.py:141
#: catalog/common/models.py:129 catalog/common/models.py:142
#: common/templates/_header.html:37
#: journal/templates/_sidebar_user_mark_list.html:44
msgid "Music"
msgstr "音乐"
#: catalog/common/models.py:130 catalog/common/models.py:143
#: catalog/common/models.py:131 catalog/common/models.py:144
#: catalog/templates/_sidebar_edit.html:152 common/templates/_header.html:33
#: journal/templates/_sidebar_user_mark_list.html:41
msgid "Podcast"
msgstr "播客"
#: catalog/common/models.py:132
#: catalog/common/models.py:133
msgid "FanFic"
msgstr "网文"
#: catalog/common/models.py:255 journal/models/collection.py:48
#: catalog/common/models.py:256 journal/models/collection.py:48
msgid "title"
msgstr "标题"
#: catalog/common/models.py:256 journal/models/collection.py:49
#: catalog/common/models.py:257 journal/models/collection.py:49
msgid "description"
msgstr "描述"
#: catalog/common/models.py:258 catalog/forms.py:26
#: catalog/common/models.py:259 catalog/forms.py:26
msgid "Primary ID Type"
msgstr "主要标识类型"
#: catalog/common/models.py:261 catalog/forms.py:31
#: catalog/common/models.py:262 catalog/forms.py:31
msgid "Primary ID Value"
msgstr "主要标识数据"
#: catalog/common/models.py:267
#: catalog/common/models.py:268
msgid "metadata"
msgstr "元数据"
#: catalog/common/models.py:269
#: catalog/common/models.py:270
msgid "cover"
msgstr "封面"
#: catalog/common/models.py:563
#: catalog/common/models.py:566
msgid "source site"
msgstr "来源站点"
#: catalog/common/models.py:565
#: catalog/common/models.py:568
msgid "ID on source site"
msgstr "来源站点标识"
#: catalog/common/models.py:567
#: catalog/common/models.py:570
msgid "source url"
msgstr "来源站点网址"
#: catalog/common/models.py:579
#: catalog/common/models.py:582
msgid "IdType of the source site"
msgstr "来源站点的主要标识类型"
#: catalog/common/models.py:585
#: catalog/common/models.py:588
msgid "Primary Id on the source site"
msgstr "来源站点的主要标识数据"
#: catalog/common/models.py:588
#: catalog/common/models.py:591
msgid "url to the resource"
msgstr "指向外部资源的网址"
@ -481,6 +488,7 @@ msgid "release date"
msgstr "发布日期"
#: catalog/movie/models.py:115 catalog/tv/models.py:339
#: journal/templates/_sidebar_user_mark_list.html:62
msgid "date"
msgstr "日期"
@ -632,6 +640,7 @@ msgstr "所属"
#: catalog/templates/_item_comments_by_episode.html:13
#: catalog/templates/search_results.html:28
#: catalog/templates/search_results.html:30
#: journal/templates/_sidebar_user_mark_list.html:55
#: social/templates/notification.html:26
msgid "all"
msgstr "全部"
@ -669,6 +678,7 @@ msgstr "写该集短评"
#: journal/models/shelf.py:97 journal/models/shelf.py:132
#: journal/models/shelf.py:167 journal/models/shelf.py:202
#: journal/models/shelf.py:237 journal/models/shelf.py:267
#: journal/templates/_sidebar_user_mark_list.html:26
msgid "review"
msgstr "评论"
@ -1034,9 +1044,10 @@ msgstr "创建"
#: catalog/templates/catalog_edit.html:50
#: developer/templates/oauth2_provider/application_form.html:35
#: journal/templates/add_to_collection.html:35
#: journal/templates/collection_edit.html:38 journal/templates/mark.html:142
#: journal/templates/review_edit.html:40 journal/templates/tag_edit.html:60
#: users/templates/users/account.html:43 users/templates/users/account.html:104
#: journal/templates/collection_edit.html:38 journal/templates/comment.html:69
#: journal/templates/mark.html:142 journal/templates/review_edit.html:40
#: journal/templates/tag_edit.html:60 users/templates/users/account.html:43
#: users/templates/users/account.html:104
#: users/templates/users/preferences.html:170
#: users/templates/users/preferences.html:195
msgid "Save"
@ -1167,6 +1178,7 @@ msgstr "简介"
#: common/templates/_sidebar.html:197 journal/templates/collection_items.html:8
#: journal/templates/profile.html:109 journal/templates/profile.html:151
#: journal/templates/profile.html:187 journal/templates/replies.html:46
#: journal/templates/user_collection_list.html:43
#: journal/templates/user_item_list_base.html:25
#: journal/templates/user_tag_list.html:29 social/templates/events.html:46
#: users/templates/users/announcements.html:53
@ -1361,11 +1373,11 @@ msgstr "{show_title} 第{season_number}季"
msgid "{season_title} E{episode_number}"
msgstr "{season_title} 第{episode_number}集"
#: catalog/views.py:49 catalog/views.py:72
#: catalog/views.py:48 catalog/views.py:71
msgid "Item not found"
msgstr "条目不存在"
#: catalog/views.py:53 catalog/views.py:80
#: catalog/views.py:52 catalog/views.py:79
msgid "Item no longer exists"
msgstr "条目已不存在"
@ -1385,7 +1397,7 @@ msgstr "权限不足"
#: catalog/views_edit.py:200 journal/views/collection.py:229
#: journal/views/collection.py:296 journal/views/common.py:81
#: journal/views/mark.py:139 journal/views/post.py:41 journal/views/post.py:55
#: journal/views/mark.py:140 journal/views/post.py:41 journal/views/post.py:55
#: journal/views/review.py:93 journal/views/review.py:96 users/views.py:169
msgid "Invalid parameter"
msgstr "无效参数"
@ -2111,10 +2123,25 @@ msgstr "添加标记"
msgid "update mark"
msgstr "更新标记"
#: journal/templates/_list_item.html:75 journal/templates/review.html:14
#: journal/templates/review.html:22 journal/templates/review.html:52
#: journal/templates/review_edit.html:10
#: journal/templates/user_review_list.html:7
msgid "Review"
msgstr "评论"
#: journal/templates/_list_item.html:89
msgid "Update note"
msgstr "修改备注"
#: journal/templates/_sidebar_user_mark_list.html:61
msgid "sorting"
msgstr "排序"
#: journal/templates/_sidebar_user_mark_list.html:63
msgid "rating"
msgstr "评分"
#: journal/templates/action_boost_post.html:10
#: users/templates/users/preferences.html:111
msgid "boost"
@ -2150,6 +2177,54 @@ msgstr "创建新收藏单"
msgid "Note"
msgstr "备注"
#: journal/templates/calendar_data.html:8
msgid "JAN"
msgstr "一月"
#: journal/templates/calendar_data.html:8
msgid "FEB"
msgstr "二月"
#: journal/templates/calendar_data.html:8
msgid "MAR"
msgstr "三月"
#: journal/templates/calendar_data.html:8
msgid "APR"
msgstr "四月"
#: journal/templates/calendar_data.html:8
msgid "MAY"
msgstr "五月"
#: journal/templates/calendar_data.html:8
msgid "JUN"
msgstr "六月"
#: journal/templates/calendar_data.html:8
msgid "JUL"
msgstr "七月"
#: journal/templates/calendar_data.html:8
msgid "AUG"
msgstr "八月"
#: journal/templates/calendar_data.html:8
msgid "SEP"
msgstr "九月"
#: journal/templates/calendar_data.html:8
msgid "OCT"
msgstr "十月"
#: journal/templates/calendar_data.html:8
msgid "NOV"
msgstr "十一月"
#: journal/templates/calendar_data.html:8
msgid "DEC"
msgstr "十二月"
#: journal/templates/collection.html:40
#: journal/templates/collection_share.html:12
#: journal/templates/collection_share.html:66
@ -2250,12 +2325,6 @@ msgstr "喜欢的收藏单"
msgid "add a reply"
msgstr "添加回应"
#: journal/templates/review.html:14 journal/templates/review.html:22
#: journal/templates/review.html:52 journal/templates/review_edit.html:10
#: journal/templates/user_review_list.html:7
msgid "Review"
msgstr "评论"
#: journal/templates/review.html:62
msgid "The author has set it to be viewable only by logged-in users."
msgstr "作者已设置仅限已登录用户查看"
@ -2293,6 +2362,16 @@ msgstr ""
msgid "Delete this tag"
msgstr "删除这个标签"
#: journal/templates/user_collection_list.html:14
#: journal/templates/user_collection_list.html:24
msgid "Liked Collections"
msgstr "喜欢的收藏单"
#: journal/templates/user_collection_list.html:14
#: journal/templates/user_collection_list.html:24
msgid "Collections"
msgstr "收藏单"
#: journal/templates/user_tag_list.html:12
#: journal/templates/user_tagmember_list.html:4
#: journal/templates/user_tagmember_list.html:14
@ -2386,7 +2465,7 @@ msgstr "找不到条目,请使用本站条目网址。"
msgid "Login required"
msgstr "登录后访问"
#: journal/views/common.py:33 journal/views/mark.py:116
#: journal/views/common.py:33 journal/views/mark.py:117
msgid "Data saved but unable to repost to Fediverse instance."
msgstr "数据已保存但未能转发到联邦实例。"
@ -2398,11 +2477,11 @@ msgstr "正在重定向到你的联邦实例以重新认证。"
msgid "List not found."
msgstr "列表未找到"
#: journal/views/mark.py:107
#: journal/views/mark.py:108
msgid "Content too long for your Fediverse instance."
msgstr "内容过长,超出了你的联邦实例的限制。"
#: journal/views/mark.py:161 journal/views/review.py:30
#: journal/views/mark.py:162 journal/views/review.py:30
msgid "Content not found"
msgstr "内容未找到"

View file

@ -5,6 +5,11 @@ reportUnusedImport = false
reportUnknownVariableType = false
reportConstantRedefinition = false
reportUnusedCallResult = false
reportUnknownMemberType = false
reportUnknownArgumentType = false
reportAny = false
reportImplicitOverride = false
reportUninitializedInstanceVariable = false
[tool.djlint]
ignore="T002,T003,H005,H006,H019,H020,H021,H023,H030,H031,D018"

View file

@ -98,7 +98,7 @@ class Snowflake:
if t > cls.EPOCH:
now: int = int((t - cls.EPOCH) * 1000)
else:
now: int = int(t) if t > 0 else 0
now = int(t) if t > 0 else 0
# Generate random data
rand_seq: int = secrets.randbits(19)
# Compose them together

View file

@ -2,3 +2,5 @@ from .apidentity import APIdentity
from .preference import Preference
from .task import Task
from .user import User
__all__ = ["APIdentity", "Preference", "Task", "User"]

View file

@ -177,7 +177,6 @@
{% if request.user.mastodon_last_refresh %}
{% trans "Last updated" %} {{ request.user.mastodon_last_refresh }}
{% endif %}
上次更新时间
</small>
</form>
</details>
@ -185,11 +184,9 @@
<article>
<details>
<summary>{% trans 'Delete Account' %}</summary>
删除数据和账号信息
<form action="{% url 'users:clear_data' %}"
method="post"
onsubmit="return confirm('{% trans "Once deleted, account data cannot be recovered. Sure to proceed?" %}');">
账号数据一旦删除后将无法恢复。确认删除吗?
{% csrf_token %}
<div>
{% blocktrans %}Enter full <code>username@instance.social</code> or <code>email@domain.com</code> to confirm deletion.{% endblocktrans %}