diff --git a/frontend/src/components/modal/ProvenancePanel.vue b/frontend/src/components/modal/ProvenancePanel.vue index 2bd8309..3b8f9e5 100644 --- a/frontend/src/components/modal/ProvenancePanel.vue +++ b/frontend/src/components/modal/ProvenancePanel.vue @@ -42,13 +42,23 @@ · {{ e.post.attachment_count }} files +
+ +{{ e.post.description_translated }}
@@ -117,8 +127,12 @@ const effectiveImage = computed(() => props.image ?? modal.current) // 2026-05-28. Reset when the viewed image changes. const expanded = reactive({}) function toggleDesc(id) { expanded[id] = !expanded[id] } +// Per-entry "show the original (untranslated) text" toggle (#143). +const showOrig = reactive({}) +function toggleOrig(id) { showOrig[id] = !showOrig[id] } watch(() => effectiveId.value, () => { for (const k of Object.keys(expanded)) delete expanded[k] + for (const k of Object.keys(showOrig)) delete showOrig[k] }) watch( @@ -157,10 +171,23 @@ const show = computed(() => { const attachments = computed(() => state.value?.attachments || []) function postDate(e) { return formatPostDate(e.post.date) } +// Translation-forward (#143): show the English by default when present. +function hasTranslation(e) { + return !!(e.post.title_translated || e.post.description_translated) +} +function showTranslated(e) { + return hasTranslation(e) && !showOrig[e.provenance_id] +} +function langLabel(e) { + return e.post.translated_source_lang ? ` (${e.post.translated_source_lang})` : '' +} function postTitle(e) { // Titles can arrive as stored HTML (e.g. "…"); render // as plain text (the CSS makes it bold). - return toPlainText(e.post.title) || `Post ${e.post.external_post_id}` + const t = showTranslated(e) && e.post.title_translated + ? e.post.title_translated + : e.post.title + return toPlainText(t) || `Post ${e.post.external_post_id}` } function openPost(postId, artistId) { diff --git a/frontend/src/components/posts/PostCard.vue b/frontend/src/components/posts/PostCard.vue index 4fdce08..1b7837b 100644 --- a/frontend/src/components/posts/PostCard.vue +++ b/frontend/src/components/posts/PostCard.vue @@ -58,16 +58,23 @@(no description)
@@ -231,6 +238,32 @@ const canExpand = computed( () => props.post.description_truncated === true || cssOverflow.value, ) +// --- translation-forward (#143): the English is shown by default when a +// translation exists; a per-card toggle reveals the original. Translations are +// plain text, so showTranslated also decides HTML-vs-plain body rendering. +const showOriginal = ref(false) +const hasTranslation = computed( + () => !!(props.post.post_title_translated || props.post.description_translated), +) +const showTranslated = computed(() => hasTranslation.value && !showOriginal.value) +const sourceLangLabel = computed(() => + props.post.translated_source_lang ? ` (${props.post.translated_source_lang})` : '', +) +const displayTitle = computed(() => + showTranslated.value && props.post.post_title_translated + ? toPlainText(props.post.post_title_translated) + : plainTitle.value, +) +const descTextDisplay = computed(() => { + if (!showTranslated.value) return descText.value + return descExpanded.value + ? (detail.value?.description_translated_full || props.post.description_translated) + : props.post.description_translated +}) +const showHtmlBody = computed( + () => !showTranslated.value && descExpanded.value && !!descHtml.value, +) + function measureOverflow () { const el = descEl.value cssOverflow.value = !!el && el.scrollHeight > el.clientHeight + 1 @@ -467,6 +500,19 @@ function formatBytes (n) { font-size: 0.85rem; font-weight: 600; } .fc-post-card__more:hover { text-decoration: underline; } +/* Translation toggle (#143) — quiet secondary affordance under the title. */ +.fc-post-card__lang { + display: block; margin: 2px 0 6px; padding: 0; + background: none; border: 0; cursor: pointer; + color: rgb(var(--v-theme-on-surface-variant)); + font-size: 0.75rem; font-weight: 600; +} +.fc-post-card__lang:hover { + text-decoration: underline; color: rgb(var(--v-theme-accent)); +} +.fc-post-card__lang:focus-visible { + outline: 2px solid rgb(var(--v-theme-accent)); outline-offset: 1px; +} .fc-post-card__atts { display: flex; flex-wrap: wrap; gap: 8px; diff --git a/frontend/src/components/settings/MaintenancePanel.vue b/frontend/src/components/settings/MaintenancePanel.vue index dfc5693..b379015 100644 --- a/frontend/src/components/settings/MaintenancePanel.vue +++ b/frontend/src/components/settings/MaintenancePanel.vue @@ -13,6 +13,7 @@+ Point this at your Interpreter service. It stays off until a URL is set, and + only translates posts whose text isn't already in the target language — the + English is shown by default on post cards, with a toggle back to the original. +
+ +