This commit is contained in:
hajimi
2026-08-02 02:16:36 +08:00
parent a6531a3ab1
commit ac20d5d914
10 changed files with 385 additions and 0 deletions
@@ -0,0 +1,232 @@
# Community Post Bilingual Pairing Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** Display translated community posts as vertically interleaved original/translation sentence pairs instead of separate long text blocks.
**Architecture:** Keep the change local to the community post detail page. Reuse the news detail page's newline-first, sentence-second splitting rule, derive a bilingual view model with a computed property, and render it through a dedicated template branch without changing API contracts.
**Tech Stack:** uni-app, Vue 3 `<script setup>`, TypeScript, SCSS
---
### Task 1: Add the bilingual sentence view model
**Files:**
- Modify: `uniapp/src/packages_community/pages/post_detail.vue`
- [ ] **Step 1: Upgrade the local text splitter**
Replace the newline-only `splitContentLines` helper with the article-detail behavior: normalize line endings, prefer explicit non-empty lines, and otherwise split the single line by Chinese or English sentence punctuation.
```ts
const splitContentLines = (value: unknown) => {
const normalized = String(value || '')
.replace(/\r\n/g, '\n')
.replace(/\r/g, '\n')
.trim()
if (!normalized) return []
const manualLines = normalized
.split('\n')
.map((line) => line.replace(/\s+/g, ' ').trim())
.filter(Boolean)
if (manualLines.length > 1) return manualLines
const sentenceLines = normalized
.replace(/\s+/g, ' ')
.match(/[^。!?.!?]+[。!?.!?]?/g)
?.map((line) => line.trim())
.filter(Boolean)
return sentenceLines?.length ? sentenceLines : [normalized]
}
```
- [ ] **Step 2: Derive paired original and translated blocks**
Add computed original lines and a computed array whose length is the maximum of both sides. Filter only fully empty rows so an unmatched trailing sentence remains visible.
```ts
const originalLines = computed(() => splitContentLines(post.value?.content))
const translatedLines = computed(() => splitContentLines(translatedText.value))
const bilingualBlocks = computed(() => {
const size = Math.max(originalLines.value.length, translatedLines.value.length)
return Array.from({ length: size }, (_, index) => ({
original: originalLines.value[index] || '',
translated: translatedLines.value[index] || ''
})).filter((block) => block.original || block.translated)
})
const hasBilingualContent = computed(() => originalLines.value.length > 0 && translatedLines.value.length > 0)
```
- [ ] **Step 3: Make expansion evaluate the complete bilingual body**
Use the combined bilingual text length and group count when bilingual content exists; keep the existing summary rule for ordinary posts.
```ts
const previewCanExpand = computed(() => {
if (hasBilingualContent.value) {
const contentLength = bilingualBlocks.value.reduce(
(total, block) => total + block.original.length + block.translated.length,
0
)
return contentLength > 180 || bilingualBlocks.value.length > 3
}
const summary = postPreview.value.summary
return summary.length > 90 || summary.split('\n').length > 3
})
```
### Task 2: Render one original sentence followed by one translation
**Files:**
- Modify: `uniapp/src/packages_community/pages/post_detail.vue`
- [ ] **Step 1: Add the bilingual template branch**
Place this branch before the ordinary title/summary content. The branch replaces both the artificial title and the former standalone translation card.
```vue
<view v-if="hasBilingualContent" class="detail-post-card__bilingual"
:class="{ 'detail-post-card__bilingual--collapsed': previewCanExpand && !previewExpanded }">
<view v-for="(block, index) in bilingualBlocks" :key="`bilingual-${index}`"
class="detail-post-card__bilingual-pair">
<text v-if="block.original" class="detail-post-card__bilingual-original">{{ block.original }}</text>
<text v-if="block.translated" class="detail-post-card__bilingual-translated">{{ block.translated }}</text>
</view>
</view>
<template v-else>
<text v-if="postPreview.title" class="detail-post-card__title">{{ postPreview.title }}</text>
<view v-if="postPreview.summary" class="detail-post-card__summary-wrap">
<text class="detail-post-card__summary"
:class="{ 'detail-post-card__summary--collapsed': previewCanExpand && !previewExpanded }">
{{ postPreview.summary }}
</text>
<text v-if="previewCanExpand" class="detail-post-card__expand"
@tap="previewExpanded = !previewExpanded">
{{ previewExpanded ? '收起' : '展开' }}
</text>
</view>
</template>
<text v-if="hasBilingualContent && previewCanExpand" class="detail-post-card__expand detail-post-card__expand--bilingual"
@tap="previewExpanded = !previewExpanded">
{{ previewExpanded ? '收起' : '展开' }}
</text>
```
- [ ] **Step 2: Remove the obsolete standalone translation block**
Delete the old `detail-post-card__translation`, `detail-post-card__translation-label`, and `detail-post-card__translation-text` template markup because translation is now rendered inside each bilingual pair.
### Task 3: Style the bilingual reading hierarchy
**Files:**
- Modify: `uniapp/src/packages_community/pages/post_detail.vue`
- [ ] **Step 1: Add bilingual layout styles**
Use a single-column, mobile-first hierarchy. Keep the original dominant and the translation visually subordinate without wrapping every pair in a heavy card.
```scss
&__bilingual {
margin-top: 20rpx;
display: flex;
flex-direction: column;
gap: 24rpx;
&--collapsed {
max-height: 430rpx;
overflow: hidden;
}
}
&__bilingual-pair {
display: flex;
flex-direction: column;
gap: 8rpx;
}
&__bilingual-original {
color: #20242b;
font-size: 29rpx;
font-weight: 500;
line-height: 1.72;
word-break: break-word;
}
&__bilingual-translated {
color: #647187;
font-size: 27rpx;
line-height: 1.72;
word-break: break-word;
}
&__expand--bilingual {
display: block;
margin-top: 12rpx;
text-align: right;
}
```
- [ ] **Step 2: Remove unused translation-card styles**
Delete the old `&__translation`, `&__translation-label`, and `&__translation-text` rules so the stylesheet matches the template.
### Task 4: Review and commit the source change
**Files:**
- Review: `uniapp/src/packages_community/pages/post_detail.vue`
- [ ] **Step 1: Inspect the scoped diff**
Run:
```powershell
git diff -- uniapp/src/packages_community/pages/post_detail.vue
```
Expected: only the bilingual template, computed pairing logic, expansion rule, and related styles change.
- [ ] **Step 2: Check whitespace and conflict markers**
Run:
```powershell
git diff --check -- uniapp/src/packages_community/pages/post_detail.vue
rg -n "<<<<<<<|=======|>>>>>>>" uniapp/src/packages_community/pages/post_detail.vue
if ($LASTEXITCODE -eq 0) { throw 'Conflict markers found' }
if ($LASTEXITCODE -gt 1) { throw 'Conflict marker scan failed' }
```
Expected: `git diff --check` exits successfully and `rg` returns no matches.
- [ ] **Step 3: Review logic against edge cases**
Confirm from the diff that:
- bilingual mode requires both original content and translated content;
- unmatched trailing sentences remain rendered;
- no API or paid-content logic changed;
- normal posts retain the old title/summary branch;
- old translation-card selectors have no remaining references.
- [ ] **Step 4: Commit only the intended files**
Run:
```powershell
git add -- docs/superpowers/plans/2026-08-02-community-post-bilingual-pairing.md uniapp/src/packages_community/pages/post_detail.vue
git commit -m "feat: pair community post translations"
```
Expected: one commit containing the plan and the uni-app page source. Do not include existing `.playwright-mcp` or modified shared docs.
## Verification boundary
The repository `AGENTS.md` prohibits automatically running builds or tests unless the user explicitly requests them. This plan therefore stops at scoped static diff checks and code review; suggested optional verification is `npm run build:h5` plus browser inspection of post `23361` after separate authorization.