diff --git a/backend/app/services/post_feed_service.py b/backend/app/services/post_feed_service.py index d21f19d..0921ccd 100644 --- a/backend/app/services/post_feed_service.py +++ b/backend/app/services/post_feed_service.py @@ -200,6 +200,8 @@ class PostFeedService: atts_map = await self._attachments_for([post.id]) item = self._to_dict(post, artist, source, thumbs_map, atts_map) item["description_full"] = html_to_plain(post.description) + # Full (uncapped) translated description for the detail view (#143). + item["description_translated_full"] = post.description_translated # Sanitized HTML body for faithful (semantic) rendering in the post view; # detail-only (the feed list stays lightweight plain text). None when the # post has no body. Inline `` sources are remapped to locally-served @@ -371,6 +373,12 @@ class PostFeedService: description_plain, truncated = None, False else: description_plain, truncated = truncate_at_word(plain_full, DESCRIPTION_LIMIT) + # Translation (#143): the stored translated description is already plain + # text; truncate it the same way for the card. + desc_trans = post.description_translated + desc_trans_short = ( + truncate_at_word(desc_trans, DESCRIPTION_LIMIT)[0] if desc_trans else None + ) thumbs_entry = thumbs_map.get(post.id, {"thumbs": [], "more": 0}) # `source` is null for filesystem-imported posts with no live # subscription (alembic 0030). Frontend renders that as a @@ -384,6 +392,11 @@ class PostFeedService: "downloaded_at": post.downloaded_at.isoformat(), "description_plain": description_plain, "description_truncated": truncated, + # Translation-forward fields (#143): shown by default when present; + # UI toggles back to the originals above. Source lang labels the toggle. + "post_title_translated": post.post_title_translated, + "description_translated": desc_trans_short, + "translated_source_lang": post.translated_source_lang, "artist": {"id": artist.id, "name": artist.name, "slug": artist.slug}, "source": ( {"id": source.id, "platform": source.platform} diff --git a/backend/app/services/provenance_service.py b/backend/app/services/provenance_service.py index 4a82b43..f2c7236 100644 --- a/backend/app/services/provenance_service.py +++ b/backend/app/services/provenance_service.py @@ -29,6 +29,12 @@ def _post_dict(p: Post) -> dict: "date": p.post_date.isoformat() if p.post_date else None, "description_html": sanitize_post_html(p.description), "attachment_count": p.attachment_count, + # Translation (#143): the English title/description shown by default when + # a translation exists; the UI toggles to the original. Source lang labels + # the original. + "title_translated": p.post_title_translated, + "description_translated": p.description_translated, + "translated_source_lang": p.translated_source_lang, } diff --git a/tests/test_post_feed_service.py b/tests/test_post_feed_service.py index 78a01da..87d0de5 100644 --- a/tests/test_post_feed_service.py +++ b/tests/test_post_feed_service.py @@ -283,6 +283,30 @@ async def test_scroll_item_shape_minimal(db): assert "description_full" not in item +@pytest.mark.asyncio +async def test_scroll_surfaces_translation_fields(db): + # #143: a translated post exposes the translated title/description + source + # lang, with the originals still present for the toggle. + artist = await _seed_artist(db, "alice-tr") + src = await _seed_source(db, artist.id, "pixiv", "https://p/alice-tr") + p = await _seed_post( + db, src.id, external_id="TR", post_date=datetime.now(UTC), + title="ねこ", description="

かわいい

", + ) + p.post_title_translated = "cat" + p.description_translated = "cute" + p.translated_source_lang = "ja" + await db.commit() + + page = await PostFeedService(db).scroll(cursor=None, limit=10) + item = page["items"][0] + assert item["post_title_translated"] == "cat" + assert item["description_translated"] == "cute" + assert item["translated_source_lang"] == "ja" + assert item["post_title"] == "ねこ" # original kept for the toggle + assert item["description_plain"] == "かわいい" + + @pytest.mark.asyncio async def test_scroll_description_truncates_long_text(db): artist = await _seed_artist(db, "alice-long")