AWS Builder 文章

在 AWS Manila Community Day 的 Tagalog 学习应用中,让额外示例唯一且可审查

当 Tagalog 学习应用扩展到 24 个文章页面后,最大的内容风险就是重复。一个页面看起来可能很完整,但许多卡片会重复相同的额外示例。Python 脚本展示了如何重写、去重并验证示例内容,让应用更容易审查,也更适合技术分享。

Tagalog 学习应用文章系列

English 繁體中文 简体中文

当 Tagalog 学习应用扩展到 24 个文章页面后,最大的内容风险就是重复。一个页面看起来可能很完整,但许多卡片会重复相同的额外示例。Python 脚本展示了如何重写、去重并验证示例内容,让应用更容易审查,也更适合技术分享。

免责声明

用途:这个静态应用是用于语言练习和开发者分享的教育原型,帮助学习者为 AWS Manila Community Day 准备礼貌的 Tagalog 短语。

非商业用途:本项目没有付费功能、广告、注册要求或商业目的,仅用于学习、实验和社区准备。

不作保证:生成的语言内容可能包含错误。Tagalog 翻译、语法解释、发音指南和文化说明在生产使用前应由母语者复核。

范围:此应用不是 AWS 官方产品,不是官方翻译工具,也不能替代真人语言教学。它是一个带有实用学习内容的技术 Demo。

社区尊重:应用应避免刻板印象,并谨慎讲解礼貌用语。poopokayoninyo 等词应被解释为表达尊重的工具,而不是装饰。


Demo

Natural Tagalog:
Nakalimutan ko ang payong ko.

英文:
I forgot my umbrella.

Polite Tagalog:
Nakalimutan ko po ang payong ko po.

Friendly Filipino-English:
Nakalimutan ko ang payong ko, okay po.

Playful Filipino-English:
Uy, Nakalimutan ko ang payong ko, all right.

语气:
daily

文化语境:
这句话可用于下雨场景。和保安、司机、摊贩、工作人员、长辈,以及第一次见面的人说话时,先使用礼貌版本。

语境用法:
适合 Manila 日常场景。简短的 Tagalog 句子加上礼貌标记,在公共场所会显得自然、温暖且实用。

语法拆解

  • Nakalimutan: 与忘记相关的动作。
  • ko: 根据句型不同,可表示“我的”“我”或“对我”。
  • ang: 放在主要名词或核心概念前的焦点标记。
  • payong: 雨伞。
  • po: 礼貌说话时使用的尊重标记。

发音指南

逐词发音可以读作:nah-kah-lee-moo-tahn koh ahng pah-yohng poh.

  • Nakalimutan: 可拆成 na: nah + ka: kah + li: lee + mu: moo + tan: tahn。
  • ko: 读作 koh。
  • ang: 读作 ahng。
  • payong: 可拆成 pa: pah + yong: yohng。
  • po: 读作 poh。

内容快照

Content QA scripts:
- rewrite_extras_from_main_examples.py
- force_rewrite_all_extra_examples.py
- dedupe_tagalog_examples.py

Main jobs:
- Read each sentence-card article block
- Extract Natural Tagalog
- Make Natural Tagalog unique when duplicated
- Create three extra examples for each card
- Add English, Natural Tagalog, and Polite Tagalog versions
- Regenerate grammar and pronunciation sections
- Assert every article still has 40 cards
- Print updated file counts

目录

第 1 部分:发现重复问题

这一部分解释为什么主文章页面生成后,额外示例还需要独立的 QA 流程。

第 2 部分:从主句重写额外示例

这一部分展示如何从当前卡片的自然 Tagalog 句子生成额外示例。

第 3 部分:在所有卡片中强制一致重写

这一部分解释当卡片边界或现有 HTML 结构不一致时,为什么第二轮处理会有用。

第 4 部分:用可追踪的课程语境去重

这一部分解释文章编号、句子编号和示例编号如何让重复内容变得可审查。

第 5 部分:验证卡片数量和更新文件

这一部分展示简单断言如何保护静态站点,避免生成损坏的文章页面。


第 1 部分:发现重复问题

目标

让每张卡片都显得有用,而不是像复制出来的内容。

开发技能

这里的技能是对生成静态页面进行内容 QA。生成站点可以通过布局检查,但如果每个额外示例都重复同一句话,学习质量仍然会失败。

提示词

Review every sentence card.
If extra examples are repeated or too generic, rewrite them.
Every card should keep three extra examples.
Each extra example should include:
- Tagalog
- English
- Natural Tagalog
- Polite Tagalog
- Grammatical Breakdown
- Pronunciation Guide

审查信号

重复可能隐藏在有效的 HTML 内部:

<div class="extra-example">
  <p><strong>Tagalog:</strong><br><span lang="tl">Ayos, salamat.</span></p>
  <p><strong>English:</strong><br>All right, thank you.</p>
</div>

这个块在结构上没有问题,但如果数百张卡片都使用同一句话,学习价值就会下降。

提示

  • 分别统计卡片和示例。
  • 搜索重复的 Tagalog 文本。
  • 按文章组审查示例。
  • 除非主句重复,否则保持主句稳定。
  • 让去重修改可追踪,而不是随机改写。

第 2 部分:从主句重写额外示例

目标

使用卡片当前的自然 Tagalog 句子作为相关示例的来源。

开发技能

rewrite_extras_from_main_examples.py 会提取主句,并围绕它构建三个示例块。

def extra_block(index: int, main: str) -> str:
    examples = [
        (
            f'Gagamitin ko rin ang linyang "{main}" mamaya.',
            f'I will also use the line "{main}" later.',
            f'Uulitin ko ang linyang "{main}" nang dahan-dahan.',
            f'Pakisuyo, uulitin ko po ang linyang "{main}" nang dahan-dahan.',
        ),
        (
            f'Sasabihin ko ang linyang "{main}" sa kausap ko.',
            f'I will say the line "{main}" to the person I am talking to.',
            f'Ipapaliwanag ko ang linyang "{main}" sa simpleng paraan.',
            f'Pakisuyo, ipapaliwanag ko po ang linyang "{main}" sa simpleng paraan.',
        ),
        (
            f'Magsanay tayo gamit ang linyang "{main}" ngayon.',
            f'Let us practice using the line "{main}" now.',
            f'Isusulat ko ang linyang "{main}" sa notes ko.',
            f'Pakisuyo, isusulat ko po ang linyang "{main}" sa notes ko.',
        ),
    ]

为什么重要

这些示例与卡片本身有关。如果卡片讲解的是:

Natural Tagalog:
Paki-check kung pumasok ang bayad.

额外示例就可以围绕如何使用、重复、解释或写下这句话展开。这比无关填充内容更有用。

生成示例结构

<div class="extra-example">
  <p><strong>Tagalog:</strong><br><span lang="tl">Gagamitin ko rin ang linyang "Paki-check kung pumasok ang bayad" mamaya.</span></p>
  <p><strong>English:</strong><br>I will also use the line "Paki-check kung pumasok ang bayad" later.</p>
  <p><strong>Natural Tagalog:</strong><br><span lang="tl">Uulitin ko ang linyang "Paki-check kung pumasok ang bayad" nang dahan-dahan.</span></p>
  <p><strong>Polite Tagalog:</strong><br><span lang="tl">Pakisuyo, uulitin ko po ang linyang "Paki-check kung pumasok ang bayad" nang dahan-dahan.</span></p>
</div>

提示

  • 基于当前卡片生成示例。
  • 每张卡片保留三个示例。
  • 同时包含自然版本和礼貌版本。
  • 重写示例后复用语法和发音辅助函数。
  • 写入 HTML 前对注入文本进行转义。

第 3 部分:在所有卡片中强制一致重写

目标

处理那些正则卡片匹配或示例边界需要更强处理的文章页面。

开发技能

force_rewrite_all_extra_examples.py 会扫描句子卡片起始标签,切分每张卡片,重写额外示例,刷新辅助章节,并检查卡片数量。

ARTICLE_START = re.compile(r'<article class="sentence-card" id="sentence-\d+">')

def rewrite_cards(path: Path, text: str) -> tuple[str, int]:
    starts = [match.start() for match in ARTICLE_START.finditer(text)]
    if not starts:
        return text, 0

    parts = []
    cursor = 0
    count = 0

    for index, start in enumerate(starts):
        end = starts[index + 1] if index + 1 < len(starts) else text.find("</section>", start)
        card = text[start:end]
        natural = field_text(card, "Natural Tagalog:")
        card = rewrite_extras(card, natural)
        card = refresh_main_sections(card)
        parts.append(text[cursor:start])
        parts.append(card)
        cursor = end
        count += 1

    parts.append(text[cursor:])
    return "".join(parts), count

为什么重要

生成的 HTML 可能包含长行、重复章节或嵌套块,这会让简单替换变得脆弱。按卡片切分后,脚本就有了更清晰的工作单元。

结果

脚本可以明确执行:

For every article page:
- identify 40 sentence cards
- rewrite each card
- refresh grammar and pronunciation
- fail if the page does not have 40 cards

提示

  • 按稳定的文章卡片标记进行切分。
  • 当预期数量不正确时明确失败。
  • 在同一轮处理中保留主卡片文本并刷新辅助章节。
  • 如果早期定向重写留下不一致块,就使用强制重写。
  • 强制处理后审查生成的 HTML。

第 4 部分:用可追踪的课程语境去重

目标

移除重复的自然 Tagalog 内容,并让示例足够唯一,便于审查。

开发技能

dedupe_tagalog_examples.py 加入了两个重要思路:

1. Track duplicate Natural Tagalog lines across all article pages.
2. Generate traceable extra examples using article number, sentence number, and extra example number.

代码模式

def unique_main_text(text: str, article: int, sentence: int, occurrence: int) -> str:
    context = f"sa aralin {number_word(article)}, pangungusap {number_word(sentence)}"
    clean = text.strip()
    if occurrence <= 1:
        return clean
    if clean.endswith("?"):
        return re.sub(r"\?

當 Tagalog 學習 App 成長到 24 個文章頁後,最大的內容風險是重複。靜態頁面可能看起來完整,但許多卡片其實重複使用相同的 extra examples。Python scripts 展示了如何重寫、去重與驗證例句內容,讓 App 更容易審閱,也更適合技術分享。

免責聲明

目的:本專案是獨立的教育原型,用於語言學習、社群準備與技術分享。它的設計目標,是協助開發者在參加 AWS Manila Community Day 前練習簡單的 Tagalog。

非商業用途:本專案沒有商業模式、付費存取、廣告計畫、聯盟行銷方案或營利目標。它是為社群準備與開發者教育而建立的學習作品。

不保證正確性:App 可能使用生成式 AI 內容,因此翻譯、發音指南、文法說明與文化註解,在公開使用前都應由 Tagalog 母語者審閱。

範圍:目標不是打造完整的翻譯平台,而是示範如何用清楚的資料結構、可重複的產生邏輯與驗證檢查,將靜態學習網站在地化。

尊重社群:App 應避免刻板印象,並謹慎教導禮貌用語。像 poopokayoninyo 這類詞,應被解釋為表達尊重的工具,而不是裝飾。


Demo

Natural Tagalog:
Nakalimutan ko ang payong ko.

English:
I forgot my umbrella.

Polite Tagalog:
Nakalimutan ko po ang payong ko po.

Friendly Filipino-English:
Nakalimutan ko ang payong ko, okay po.

Playful Filipino-English:
Uy, Nakalimutan ko ang payong ko, all right.

Tone:
daily

Cultural Context:
這句可用於下雨情境。和警衛、司機、攤販、工作人員、長輩,以及第一次見面的人說話時,先使用禮貌形式。

Context Use:
適合日常 Manila 情境。簡短的 Tagalog 句子加上禮貌標記,在公共場所聽起來自然、溫暖且實用。

Grammatical Breakdown(文法拆解)

  • Nakalimutan:和 nakalimut 相關的地點、物件或動作形式。
  • ko:依句型可表示「我的」、「我」或「由我」。
  • ang:焦點標記,放在主要名詞或概念前。
  • payong:和 yong 相關的請求或方向用語。
  • po:禮貌標記,用於尊重語氣。

Pronunciation Guide(發音指南)

逐字發音可以讀成:nah-kah-lee-moo-tahn koh ahng pah-yohng poh.

  • Nakalimutan:拆成 na: nah + ka: kah + li: lee + mu: moo + tan: tahn。
  • ko:讀成 koh。
  • ang:讀成 ahng。
  • payong:拆成 pa: pah + yong: yohng。
  • po:讀成 poh。

內容快照

內容 QA 腳本:
- rewrite_extras_from_main_examples.py
- force_rewrite_all_extra_examples.py
- dedupe_tagalog_examples.py

主要工作:
- 讀取每個 sentence-card article block
- 抽出 Natural Tagalog
- 當 Natural Tagalog 重複時讓它變得可追蹤
- 為每張卡片建立三個 extra examples
- 加入 English、Natural Tagalog、Polite Tagalog 版本
- 重新產生文法與發音段落
- 確認每篇文章仍有 40 張卡片
- 印出更新檔案數量

目錄

Part 1: 找出重複問題

說明為什麼主文章頁產生後,extra examples 還需要自己的 QA pass。

Part 2: 從主句重寫 Extra Examples

說明如何根據當前卡片的 Natural Tagalog 產生 extra examples。

Part 3: 對所有卡片強制一致重寫

說明當卡片邊界或既有 HTML 結構不一致時,為什麼第二輪 pass 會有幫助。

Part 4: 用可追蹤的課程情境去重

說明 article number、sentence number 與 example number 如何讓重複內容更容易審閱。

Part 5: 驗證卡片數量與更新檔案

說明簡單 assert 如何保護靜態網站,避免產生破損文章頁。


Part 1: 找出重複問題

目標

讓每張卡片都感覺有用,而不是像複製貼上。

開發技能

這裡的技能是 generated static pages 的內容 QA。產生出的網站可能通過版型檢查,但如果每個 extra example 都重複同一句,學習品質仍然會失敗。

Prompt

審閱每一張 sentence card。
如果 extra examples 重複或太泛用,就重寫。
每張卡片維持三個 extra examples。
每個 extra example 都應包含:
- Tagalog
- English
- Natural Tagalog
- Polite Tagalog
- Grammatical Breakdown
- Pronunciation Guide

審閱訊號

重複內容可能藏在有效 HTML 裡:

<div class="extra-example">
  <p><strong>Tagalog:</strong><br><span lang="tl">Ayos, salamat.</span></p>
  <p><strong>English:</strong><br>All right, thank you.</p>
</div>

這個區塊結構正確,但如果數百張卡片都使用同一句,學習價值就會下降。

Tips

  • 分開計算 cards 與 examples。
  • 搜尋重複的 Tagalog text。
  • 依文章群組審閱例句。
  • 除非主句重複,否則保持主句穩定。
  • 去重變更要可追蹤,不要隨機。

Part 2: 從主句重寫 Extra Examples

目標

使用卡片目前的 Natural Tagalog 句子,作為相關例句的來源。

開發技能

rewrite_extras_from_main_examples.py 會抽出主句,並圍繞它建立三個 example blocks。

def extra_block(index: int, main: str) -> str:
    examples = [
        (
            f'Gagamitin ko rin ang linyang "{main}" mamaya.',
            f'I will also use the line "{main}" later.',
            f'Uulitin ko ang linyang "{main}" nang dahan-dahan.',
            f'Pakisuyo, uulitin ko po ang linyang "{main}" nang dahan-dahan.',
        ),
        (
            f'Sasabihin ko ang linyang "{main}" sa kausap ko.',
            f'I will say the line "{main}" to the person I am talking to.',
            f'Ipapaliwanag ko ang linyang "{main}" sa simpleng paraan.',
            f'Pakisuyo, ipapaliwanag ko po ang linyang "{main}" sa simpleng paraan.',
        ),
        (
            f'Magsanay tayo gamit ang linyang "{main}" ngayon.',
            f'Let us practice using the line "{main}" now.',
            f'Isusulat ko ang linyang "{main}" sa notes ko.',
            f'Pakisuyo, isusulat ko po ang linyang "{main}" sa notes ko.',
        ),
    ]

為什麼重要

這些例句會連回目前卡片。如果卡片教的是:

Natural Tagalog:
Paki-check kung pumasok ang bayad.

extra examples 就可以說明如何使用、重複、解釋或寫下這句話。這比不相關的填充句更有用。

產生後的 example shape

<div class="extra-example">
  <p><strong>Tagalog:</strong><br><span lang="tl">Gagamitin ko rin ang linyang "Paki-check kung pumasok ang bayad" mamaya.</span></p>
  <p><strong>English:</strong><br>I will also use the line "Paki-check kung pumasok ang bayad" later.</p>
  <p><strong>Natural Tagalog:</strong><br><span lang="tl">Uulitin ko ang linyang "Paki-check kung pumasok ang bayad" nang dahan-dahan.</span></p>
  <p><strong>Polite Tagalog:</strong><br><span lang="tl">Pakisuyo, uulitin ko po ang linyang "Paki-check kung pumasok ang bayad" nang dahan-dahan.</span></p>
</div>

Tips

  • 根據當前卡片建立 examples。
  • 每張卡片維持三個 examples。
  • 同時包含 natural 與 polite versions。
  • 重寫 examples 後重新使用文法與發音 helpers。
  • 寫入 HTML 前要 escape 注入文字。

Part 3: 對所有卡片強制一致重寫

目標

處理 regex card matching 或 example boundaries 需要更強 pass 的文章頁。

開發技能

force_rewrite_all_extra_examples.py 會掃描 sentence-card start tags、切出每張卡片、重寫 extra examples、刷新 helper sections,並檢查卡片數量。

ARTICLE_START = re.compile(r'<article class="sentence-card" id="sentence-\d+">')

def rewrite_cards(path: Path, text: str) -> tuple[str, int]:
    starts = [match.start() for match in ARTICLE_START.finditer(text)]
    if not starts:
        return text, 0

    parts = []
    cursor = 0
    count = 0

    for index, start in enumerate(starts):
        end = starts[index + 1] if index + 1 < len(starts) else text.find("</section>", start)
        card = text[start:end]
        natural = field_text(card, "Natural Tagalog:")
        card = rewrite_extras(card, natural)
        card = refresh_main_sections(card)
        parts.append(text[cursor:start])
        parts.append(card)
        cursor = end
        count += 1

    parts.append(text[cursor:])
    return "".join(parts), count

為什麼重要

產生的 HTML 可能有很長的單行、重複區塊或巢狀區塊,讓簡單替換變得脆弱。用卡片切片當工作單位,腳本會更清楚。

結果

腳本可以明確做到:

針對每個文章頁:
- 找出 40 張 sentence cards
- 重寫每張卡片
- 刷新文法與發音
- 如果頁面不是 40 張卡片就失敗

Tips

  • 用穩定的 article-card markers 切片。
  • 預期數量錯誤時要明確失敗。
  • 主卡片文字與 helper-section refresh 放在同一輪 pass。
  • 當較精準的 rewrite 留下不一致區塊時,使用 force rewrite。
  • force pass 後要審閱產生的 HTML。

Part 4: 用可追蹤的課程情境去重

目標

移除重複的 Natural Tagalog 內容,並讓 examples 足夠獨特、方便審閱。

開發技能

dedupe_tagalog_examples.py 加入兩個重要概念:

1. 追蹤所有文章頁中的重複 Natural Tagalog lines。
2. 用 article number、sentence number、extra example number 產生可追蹤的 extra examples。

程式模式

def unique_main_text(text: str, article: int, sentence: int, occurrence: int) -> str:
    context = f"sa aralin {number_word(article)}, pangungusap {number_word(sentence)}"
    clean = text.strip()
    if occurrence <= 1:
        return clean
    if clean.endswith("?"):
        return re.sub(r"\?$", f" {context}?", clean)
    return re.sub(r"[.!]?$", f" {context}.", clean)

為什麼重要

如果同一個 Natural Tagalog 句子出現超過一次,腳本可以讓重複版本可追蹤:

原本:
Salamat sa tulong.

可追蹤的 duplicate-safe 版本:
Salamat sa tulong sa aralin lima, pangungusap dalawa.

這不只是語言變更,也是 QA 訊號。審閱者可以定位重複內容,判斷這個 generated uniqueness 是否可接受,或是否應手動改寫 source content。

依主題產生 examples

dedupe script 也會根據文章群組選擇 example scenes:

def group_for(path: Path) -> str:
    if "friendship" in path.name:
        return "friendship"
    if "manila-daily" in path.name:
        return "manila"
    return "community"

接著每個群組會得到符合領域的 examples:

Community:
- session
- workshop
- community table

Friendship:
- mahinahong usapan
- tapat na mensahe
- ligtas na pag-uusap

Manila Daily:
- commute
- palengke
- araw-araw na errand

產生的 extra blocks 範例

Community example:
Sa aralin apat, pangungusap sampu, gagamitin ko ito sa workshop.

Friendship example:
Sa aralin labing-anim, pangungusap pito, aalagaan ko ang tapat na mensahe.

Manila Daily example:
Sa aralin dalawampu't dalawa, pangungusap tatlo, gagamitin ko ito sa palengke.

Tips

  • 讓 duplicate fixes 可追蹤。
  • 審閱 generated examples 時使用 article 與 sentence numbers。
  • 依內容群組選擇 example scenes。
  • polite forms 要和 natural forms 有差異。
  • 把 dedupe output 當審閱素材,不要當成最終語言權威。

Part 5: 驗證卡片數量與更新檔案

目標

大量重寫時,保護產生出的網站結構不被破壞。

開發技能

腳本使用 card count 的硬性檢查與輸出摘要。

for path in sorted(ROOT.glob("article-*.html"), key=lambda p: article_number(p)):
    text = path.read_text(encoding="utf-8")
    card_count = 0

    def replace_card(match: re.Match[str]) -> str:
        nonlocal card_count
        card_count += 1
        return rewrite_one_card(match.group(0))

    updated = CARD_RE.sub(replace_card, text)

    if card_count != 40:
        raise RuntimeError(f"{path.name}: expected 40 cards, found {card_count}")

結果

rewrite process 變得更安全:

預期:
24 個文章頁
每篇文章 40 張 sentence cards
每張卡片 3 個 extra examples

失敗模式:
raise error,而不是寫出部分破損頁面

審閱清單

  • 每篇文章是否仍有 40 張卡片?
  • 每張卡片是否仍有 Natural Tagalog 與 Polite Tagalog?
  • extra examples 是否分開且可讀?
  • 重複的 Natural Tagalog lines 是否可追蹤?
  • example rewrites 後是否重新產生文法與發音?
  • 產生的 examples 是否適合該文章群組?

Field Note 1: Extra Examples 是產品內容

背景:Extra examples 不是裝飾,它們教學習者如何重用短句。

目標:讓 examples 連回目前卡片。

Prompt:從卡片的 Natural Tagalog sentence 建立 extra examples。

結果:學習者會看到同一句如何被練習、重複、解釋或放進場景。

Review check:每個 example 是否支援主句,而不是分散注意力?

Field Note 2: Dedupe 應該可解釋

背景:Generated content 可能在許多卡片中重複。

目標:讓重複句子可見且可追蹤。

Prompt:追蹤所有文章頁的 Natural Tagalog strings,並在重複時加入 lesson context。

結果:審閱者能快速知道重複內容來自哪裡。

Review check:審閱者是否能把 generated example 對回 article number 與 sentence number?

Field Note 3: Polite Variants 需要真正不同

背景:如果 polite field 和 natural field 完全相同,教學價值有限。

目標:適當加入 poPakisuyo 或其他尊重句型。

Prompt:如果 Natural Tagalog 與 Polite Tagalog 相同,就建立一個有差異的 polite version。

結果:學習者更容易比較自然語氣與尊重語氣。

Review check:polite example 是否尊重但不生硬?

Field Note 4: Regex 有用,但需要護欄

背景:HTML regex 如果亂用會很脆弱。

目標:只在穩定的 generated markers 周圍使用 regex,並在之後驗證 counts。

Prompt:match 已知 sentence-card blocks,重寫它們,然後 assert 40 cards。

結果:對 generated static HTML 來說,腳本保持實用,也避免 silent corruption。

Review check:當預期 card count 不存在時,腳本是否會失敗?

技術分享角度

這個內容 QA flow 很適合開發者分享,因為它展示了一個可信 generated demo 背後的隱藏工作:

Generated article pages
      ->
detect sentence cards
      ->
extract Natural Tagalog
      ->
rewrite three extra examples
      ->
make duplicates traceable
      ->
regenerate grammar and pronunciation
      ->
assert 40 cards
      ->
write updated files
      ->
print JSON or count summary

最重要的 lesson 是:generated educational content 需要 QA pipeline,而不只是 prompt。

範例分享講稿

第一版產生了很多卡片,但 card count 不等於 learning quality。
所以我們加入 content QA pass。

腳本讀取每個 sentence-card block,抽出 Natural Tagalog line,然後重建 extra examples。
如果這句已經出現過,dedupe step 會加入 article 與 sentence context。

重寫後,腳本重新產生 grammar 與 pronunciation helper sections。
接著 assert 每篇文章仍然有 exactly 40 cards。

這帶來兩個好處:
1. 學習者得到更有用的 examples。
2. 開發者得到可量測的 review output。

結語

extra-example rewrite scripts 顯示內容 QA 也是工程問題。專案使用小型 Python 腳本、穩定 HTML markers、escaped output、依群組產生的 example scenes、duplicate tracking、grammar refresh、pronunciation refresh 與硬性 card-count checks。這個流程讓 Tagalog 學習 App 對學習者更有用,也讓技術分享 demo 更有說服力。

quot;, f" {context}?", clean) return re.sub(r"[.!]?

當 Tagalog 學習 App 成長到 24 個文章頁後,最大的內容風險是重複。靜態頁面可能看起來完整,但許多卡片其實重複使用相同的 extra examples。Python scripts 展示了如何重寫、去重與驗證例句內容,讓 App 更容易審閱,也更適合技術分享。

免責聲明

目的:本專案是獨立的教育原型,用於語言學習、社群準備與技術分享。它的設計目標,是協助開發者在參加 AWS Manila Community Day 前練習簡單的 Tagalog。

非商業用途:本專案沒有商業模式、付費存取、廣告計畫、聯盟行銷方案或營利目標。它是為社群準備與開發者教育而建立的學習作品。

不保證正確性:App 可能使用生成式 AI 內容,因此翻譯、發音指南、文法說明與文化註解,在公開使用前都應由 Tagalog 母語者審閱。

範圍:目標不是打造完整的翻譯平台,而是示範如何用清楚的資料結構、可重複的產生邏輯與驗證檢查,將靜態學習網站在地化。

尊重社群:App 應避免刻板印象,並謹慎教導禮貌用語。像 poopokayoninyo 這類詞,應被解釋為表達尊重的工具,而不是裝飾。


Demo

Natural Tagalog:
Nakalimutan ko ang payong ko.

English:
I forgot my umbrella.

Polite Tagalog:
Nakalimutan ko po ang payong ko po.

Friendly Filipino-English:
Nakalimutan ko ang payong ko, okay po.

Playful Filipino-English:
Uy, Nakalimutan ko ang payong ko, all right.

Tone:
daily

Cultural Context:
這句可用於下雨情境。和警衛、司機、攤販、工作人員、長輩,以及第一次見面的人說話時,先使用禮貌形式。

Context Use:
適合日常 Manila 情境。簡短的 Tagalog 句子加上禮貌標記,在公共場所聽起來自然、溫暖且實用。

Grammatical Breakdown(文法拆解)

  • Nakalimutan:和 nakalimut 相關的地點、物件或動作形式。
  • ko:依句型可表示「我的」、「我」或「由我」。
  • ang:焦點標記,放在主要名詞或概念前。
  • payong:和 yong 相關的請求或方向用語。
  • po:禮貌標記,用於尊重語氣。

Pronunciation Guide(發音指南)

逐字發音可以讀成:nah-kah-lee-moo-tahn koh ahng pah-yohng poh.

  • Nakalimutan:拆成 na: nah + ka: kah + li: lee + mu: moo + tan: tahn。
  • ko:讀成 koh。
  • ang:讀成 ahng。
  • payong:拆成 pa: pah + yong: yohng。
  • po:讀成 poh。

內容快照

內容 QA 腳本:
- rewrite_extras_from_main_examples.py
- force_rewrite_all_extra_examples.py
- dedupe_tagalog_examples.py

主要工作:
- 讀取每個 sentence-card article block
- 抽出 Natural Tagalog
- 當 Natural Tagalog 重複時讓它變得可追蹤
- 為每張卡片建立三個 extra examples
- 加入 English、Natural Tagalog、Polite Tagalog 版本
- 重新產生文法與發音段落
- 確認每篇文章仍有 40 張卡片
- 印出更新檔案數量

目錄

Part 1: 找出重複問題

說明為什麼主文章頁產生後,extra examples 還需要自己的 QA pass。

Part 2: 從主句重寫 Extra Examples

說明如何根據當前卡片的 Natural Tagalog 產生 extra examples。

Part 3: 對所有卡片強制一致重寫

說明當卡片邊界或既有 HTML 結構不一致時,為什麼第二輪 pass 會有幫助。

Part 4: 用可追蹤的課程情境去重

說明 article number、sentence number 與 example number 如何讓重複內容更容易審閱。

Part 5: 驗證卡片數量與更新檔案

說明簡單 assert 如何保護靜態網站,避免產生破損文章頁。


Part 1: 找出重複問題

目標

讓每張卡片都感覺有用,而不是像複製貼上。

開發技能

這裡的技能是 generated static pages 的內容 QA。產生出的網站可能通過版型檢查,但如果每個 extra example 都重複同一句,學習品質仍然會失敗。

Prompt

審閱每一張 sentence card。
如果 extra examples 重複或太泛用,就重寫。
每張卡片維持三個 extra examples。
每個 extra example 都應包含:
- Tagalog
- English
- Natural Tagalog
- Polite Tagalog
- Grammatical Breakdown
- Pronunciation Guide

審閱訊號

重複內容可能藏在有效 HTML 裡:

<div class="extra-example">
  <p><strong>Tagalog:</strong><br><span lang="tl">Ayos, salamat.</span></p>
  <p><strong>English:</strong><br>All right, thank you.</p>
</div>

這個區塊結構正確,但如果數百張卡片都使用同一句,學習價值就會下降。

Tips

  • 分開計算 cards 與 examples。
  • 搜尋重複的 Tagalog text。
  • 依文章群組審閱例句。
  • 除非主句重複,否則保持主句穩定。
  • 去重變更要可追蹤,不要隨機。

Part 2: 從主句重寫 Extra Examples

目標

使用卡片目前的 Natural Tagalog 句子,作為相關例句的來源。

開發技能

rewrite_extras_from_main_examples.py 會抽出主句,並圍繞它建立三個 example blocks。

def extra_block(index: int, main: str) -> str:
    examples = [
        (
            f'Gagamitin ko rin ang linyang "{main}" mamaya.',
            f'I will also use the line "{main}" later.',
            f'Uulitin ko ang linyang "{main}" nang dahan-dahan.',
            f'Pakisuyo, uulitin ko po ang linyang "{main}" nang dahan-dahan.',
        ),
        (
            f'Sasabihin ko ang linyang "{main}" sa kausap ko.',
            f'I will say the line "{main}" to the person I am talking to.',
            f'Ipapaliwanag ko ang linyang "{main}" sa simpleng paraan.',
            f'Pakisuyo, ipapaliwanag ko po ang linyang "{main}" sa simpleng paraan.',
        ),
        (
            f'Magsanay tayo gamit ang linyang "{main}" ngayon.',
            f'Let us practice using the line "{main}" now.',
            f'Isusulat ko ang linyang "{main}" sa notes ko.',
            f'Pakisuyo, isusulat ko po ang linyang "{main}" sa notes ko.',
        ),
    ]

為什麼重要

這些例句會連回目前卡片。如果卡片教的是:

Natural Tagalog:
Paki-check kung pumasok ang bayad.

extra examples 就可以說明如何使用、重複、解釋或寫下這句話。這比不相關的填充句更有用。

產生後的 example shape

<div class="extra-example">
  <p><strong>Tagalog:</strong><br><span lang="tl">Gagamitin ko rin ang linyang "Paki-check kung pumasok ang bayad" mamaya.</span></p>
  <p><strong>English:</strong><br>I will also use the line "Paki-check kung pumasok ang bayad" later.</p>
  <p><strong>Natural Tagalog:</strong><br><span lang="tl">Uulitin ko ang linyang "Paki-check kung pumasok ang bayad" nang dahan-dahan.</span></p>
  <p><strong>Polite Tagalog:</strong><br><span lang="tl">Pakisuyo, uulitin ko po ang linyang "Paki-check kung pumasok ang bayad" nang dahan-dahan.</span></p>
</div>

Tips

  • 根據當前卡片建立 examples。
  • 每張卡片維持三個 examples。
  • 同時包含 natural 與 polite versions。
  • 重寫 examples 後重新使用文法與發音 helpers。
  • 寫入 HTML 前要 escape 注入文字。

Part 3: 對所有卡片強制一致重寫

目標

處理 regex card matching 或 example boundaries 需要更強 pass 的文章頁。

開發技能

force_rewrite_all_extra_examples.py 會掃描 sentence-card start tags、切出每張卡片、重寫 extra examples、刷新 helper sections,並檢查卡片數量。

ARTICLE_START = re.compile(r'<article class="sentence-card" id="sentence-\d+">')

def rewrite_cards(path: Path, text: str) -> tuple[str, int]:
    starts = [match.start() for match in ARTICLE_START.finditer(text)]
    if not starts:
        return text, 0

    parts = []
    cursor = 0
    count = 0

    for index, start in enumerate(starts):
        end = starts[index + 1] if index + 1 < len(starts) else text.find("</section>", start)
        card = text[start:end]
        natural = field_text(card, "Natural Tagalog:")
        card = rewrite_extras(card, natural)
        card = refresh_main_sections(card)
        parts.append(text[cursor:start])
        parts.append(card)
        cursor = end
        count += 1

    parts.append(text[cursor:])
    return "".join(parts), count

為什麼重要

產生的 HTML 可能有很長的單行、重複區塊或巢狀區塊,讓簡單替換變得脆弱。用卡片切片當工作單位,腳本會更清楚。

結果

腳本可以明確做到:

針對每個文章頁:
- 找出 40 張 sentence cards
- 重寫每張卡片
- 刷新文法與發音
- 如果頁面不是 40 張卡片就失敗

Tips

  • 用穩定的 article-card markers 切片。
  • 預期數量錯誤時要明確失敗。
  • 主卡片文字與 helper-section refresh 放在同一輪 pass。
  • 當較精準的 rewrite 留下不一致區塊時,使用 force rewrite。
  • force pass 後要審閱產生的 HTML。

Part 4: 用可追蹤的課程情境去重

目標

移除重複的 Natural Tagalog 內容,並讓 examples 足夠獨特、方便審閱。

開發技能

dedupe_tagalog_examples.py 加入兩個重要概念:

1. 追蹤所有文章頁中的重複 Natural Tagalog lines。
2. 用 article number、sentence number、extra example number 產生可追蹤的 extra examples。

程式模式

def unique_main_text(text: str, article: int, sentence: int, occurrence: int) -> str:
    context = f"sa aralin {number_word(article)}, pangungusap {number_word(sentence)}"
    clean = text.strip()
    if occurrence <= 1:
        return clean
    if clean.endswith("?"):
        return re.sub(r"\?$", f" {context}?", clean)
    return re.sub(r"[.!]?$", f" {context}.", clean)

為什麼重要

如果同一個 Natural Tagalog 句子出現超過一次,腳本可以讓重複版本可追蹤:

原本:
Salamat sa tulong.

可追蹤的 duplicate-safe 版本:
Salamat sa tulong sa aralin lima, pangungusap dalawa.

這不只是語言變更,也是 QA 訊號。審閱者可以定位重複內容,判斷這個 generated uniqueness 是否可接受,或是否應手動改寫 source content。

依主題產生 examples

dedupe script 也會根據文章群組選擇 example scenes:

def group_for(path: Path) -> str:
    if "friendship" in path.name:
        return "friendship"
    if "manila-daily" in path.name:
        return "manila"
    return "community"

接著每個群組會得到符合領域的 examples:

Community:
- session
- workshop
- community table

Friendship:
- mahinahong usapan
- tapat na mensahe
- ligtas na pag-uusap

Manila Daily:
- commute
- palengke
- araw-araw na errand

產生的 extra blocks 範例

Community example:
Sa aralin apat, pangungusap sampu, gagamitin ko ito sa workshop.

Friendship example:
Sa aralin labing-anim, pangungusap pito, aalagaan ko ang tapat na mensahe.

Manila Daily example:
Sa aralin dalawampu't dalawa, pangungusap tatlo, gagamitin ko ito sa palengke.

Tips

  • 讓 duplicate fixes 可追蹤。
  • 審閱 generated examples 時使用 article 與 sentence numbers。
  • 依內容群組選擇 example scenes。
  • polite forms 要和 natural forms 有差異。
  • 把 dedupe output 當審閱素材,不要當成最終語言權威。

Part 5: 驗證卡片數量與更新檔案

目標

大量重寫時,保護產生出的網站結構不被破壞。

開發技能

腳本使用 card count 的硬性檢查與輸出摘要。

for path in sorted(ROOT.glob("article-*.html"), key=lambda p: article_number(p)):
    text = path.read_text(encoding="utf-8")
    card_count = 0

    def replace_card(match: re.Match[str]) -> str:
        nonlocal card_count
        card_count += 1
        return rewrite_one_card(match.group(0))

    updated = CARD_RE.sub(replace_card, text)

    if card_count != 40:
        raise RuntimeError(f"{path.name}: expected 40 cards, found {card_count}")

結果

rewrite process 變得更安全:

預期:
24 個文章頁
每篇文章 40 張 sentence cards
每張卡片 3 個 extra examples

失敗模式:
raise error,而不是寫出部分破損頁面

審閱清單

  • 每篇文章是否仍有 40 張卡片?
  • 每張卡片是否仍有 Natural Tagalog 與 Polite Tagalog?
  • extra examples 是否分開且可讀?
  • 重複的 Natural Tagalog lines 是否可追蹤?
  • example rewrites 後是否重新產生文法與發音?
  • 產生的 examples 是否適合該文章群組?

Field Note 1: Extra Examples 是產品內容

背景:Extra examples 不是裝飾,它們教學習者如何重用短句。

目標:讓 examples 連回目前卡片。

Prompt:從卡片的 Natural Tagalog sentence 建立 extra examples。

結果:學習者會看到同一句如何被練習、重複、解釋或放進場景。

Review check:每個 example 是否支援主句,而不是分散注意力?

Field Note 2: Dedupe 應該可解釋

背景:Generated content 可能在許多卡片中重複。

目標:讓重複句子可見且可追蹤。

Prompt:追蹤所有文章頁的 Natural Tagalog strings,並在重複時加入 lesson context。

結果:審閱者能快速知道重複內容來自哪裡。

Review check:審閱者是否能把 generated example 對回 article number 與 sentence number?

Field Note 3: Polite Variants 需要真正不同

背景:如果 polite field 和 natural field 完全相同,教學價值有限。

目標:適當加入 poPakisuyo 或其他尊重句型。

Prompt:如果 Natural Tagalog 與 Polite Tagalog 相同,就建立一個有差異的 polite version。

結果:學習者更容易比較自然語氣與尊重語氣。

Review check:polite example 是否尊重但不生硬?

Field Note 4: Regex 有用,但需要護欄

背景:HTML regex 如果亂用會很脆弱。

目標:只在穩定的 generated markers 周圍使用 regex,並在之後驗證 counts。

Prompt:match 已知 sentence-card blocks,重寫它們,然後 assert 40 cards。

結果:對 generated static HTML 來說,腳本保持實用,也避免 silent corruption。

Review check:當預期 card count 不存在時,腳本是否會失敗?

技術分享角度

這個內容 QA flow 很適合開發者分享,因為它展示了一個可信 generated demo 背後的隱藏工作:

Generated article pages
      ->
detect sentence cards
      ->
extract Natural Tagalog
      ->
rewrite three extra examples
      ->
make duplicates traceable
      ->
regenerate grammar and pronunciation
      ->
assert 40 cards
      ->
write updated files
      ->
print JSON or count summary

最重要的 lesson 是:generated educational content 需要 QA pipeline,而不只是 prompt。

範例分享講稿

第一版產生了很多卡片,但 card count 不等於 learning quality。
所以我們加入 content QA pass。

腳本讀取每個 sentence-card block,抽出 Natural Tagalog line,然後重建 extra examples。
如果這句已經出現過,dedupe step 會加入 article 與 sentence context。

重寫後,腳本重新產生 grammar 與 pronunciation helper sections。
接著 assert 每篇文章仍然有 exactly 40 cards。

這帶來兩個好處:
1. 學習者得到更有用的 examples。
2. 開發者得到可量測的 review output。

結語

extra-example rewrite scripts 顯示內容 QA 也是工程問題。專案使用小型 Python 腳本、穩定 HTML markers、escaped output、依群組產生的 example scenes、duplicate tracking、grammar refresh、pronunciation refresh 與硬性 card-count checks。這個流程讓 Tagalog 學習 App 對學習者更有用,也讓技術分享 demo 更有說服力。

quot;, f" {context}.", clean)

为什么重要

如果同一句自然 Tagalog 出现多次,脚本可以让重复副本变得可追踪:

Original:
Salamat sa tulong.

Duplicate-safe:
Salamat sa tulong sa aralin lima, pangungusap dalawa.

这不只是语言改动,也是 QA 信号。审查者可以定位重复内容,并判断生成的唯一化结果是否可接受,或源内容是否应该手工重写。

感知主题的示例生成

去重脚本还会根据文章组选择示例场景:

def group_for(path: Path) -> str:
    if "friendship" in path.name:
        return "friendship"
    if "manila-daily" in path.name:
        return "manila"
    return "community"

然后每个组都会得到符合其领域的示例:

Community:
- session
- workshop
- community table

Friendship:
- mahinahong usapan
- tapat na mensahe
- ligtas na pag-uusap

Manila Daily:
- commute
- palengke
- araw-araw na errand

生成的额外块示例

Community example:
Sa aralin apat, pangungusap sampu, gagamitin ko ito sa workshop.

Friendship example:
Sa aralin labing-anim, pangungusap pito, aalagaan ko ang tapat na mensahe.

Manila Daily example:
Sa aralin dalawampu't dalawa, pangungusap tatlo, gagamitin ko ito sa palengke.

提示

  • 让重复修复可追踪。
  • 审查生成示例时使用文章编号和句子编号。
  • 按内容组选择示例场景。
  • 使用与自然形式不同的礼貌形式。
  • 把去重输出当作审查材料,而不是最终语言权威。

第 5 部分:验证卡片数量和更新文件

目标

在批量重写过程中保护生成站点,避免结构损坏。

开发技能

脚本对卡片数量和输出摘要使用硬性检查。

for path in sorted(ROOT.glob("article-*.html"), key=lambda p: article_number(p)):
    text = path.read_text(encoding="utf-8")
    card_count = 0

    def replace_card(match: re.Match[str]) -> str:
        nonlocal card_count
        card_count += 1
        return rewrite_one_card(match.group(0))

    updated = CARD_RE.sub(replace_card, text)

    if card_count != 40:
        raise RuntimeError(f"{path.name}: expected 40 cards, found {card_count}")

结果

重写过程会更安全:

Expected:
24 article pages
40 sentence cards per article
3 extra examples per card

Failure mode:
raise an error instead of writing a partial page

审查清单

  • 每篇文章是否仍有 40 张卡片?
  • 每张卡片是否仍有自然 Tagalog 和礼貌 Tagalog?
  • 额外示例是否独立且可读?
  • 重复的自然 Tagalog 行是否可追踪?
  • 示例重写后,语法和发音章节是否已重新生成?
  • 生成示例是否适合对应文章组?

现场笔记 1:额外示例是产品内容

背景:额外示例不是装饰。它们教学习者如何复用一句短语。

目标:让示例与当前卡片相关。

提示词:从卡片的自然 Tagalog 句子构建额外示例。

结果:学习者可以看到同一句话如何被练习、重复、解释或用于某个场景。

审查检查:每个示例是否支持主短语,而不是分散注意力?

现场笔记 2:去重应该可解释

背景:生成内容可能在许多卡片中重复。

目标:让重复行可见且可追踪。

提示词:追踪所有文章页面中的自然 Tagalog 字符串,并在出现重复时加入课程语境。

结果:审查者可以快速识别重复内容来自哪里。

审查检查:审查者能否把生成示例映射回文章编号和句子编号?

现场笔记 3:礼貌变体需要真实差异

背景:如果礼貌字段只是重复自然字段,学习价值并不高。

目标:在合适时加入 poPakisuyo 或其他尊重句型。

提示词:如果自然 Tagalog 和礼貌 Tagalog 完全相同,就创建一个有区别的礼貌版本。

结果:学习者可以更清楚地比较自然说法和尊重说法。

审查检查:礼貌示例是否听起来尊重且不尴尬?

现场笔记 4:Regex 有用,但需要护栏

背景:如果使用不谨慎,HTML regex 会很脆弱。

目标:只围绕稳定的生成标记使用 regex,并在之后验证数量。

提示词:匹配已知的句子卡片块,重写它们,然后断言有 40 张卡片。

结果:脚本对生成静态 HTML 保持实用,同时避免静默破坏。

审查检查:当预期卡片数量缺失时,脚本是否会失败?

技术分享角度

这条内容 QA 流程适合开发者分享,因为它展示了一个可信生成式 Demo 背后的隐藏工作:

Generated article pages
      ->
detect sentence cards
      ->
extract Natural Tagalog
      ->
rewrite three extra examples
      ->
make duplicates traceable
      ->
regenerate grammar and pronunciation
      ->
assert 40 cards
      ->
write updated files
      ->
print JSON or count summary

最重要的经验是:生成式教育内容需要 QA 流水线,而不只是一个提示词。

示例演讲稿

The first version generated many cards, but card count is not the same as learning quality.
So we added a content QA pass.

The script reads every sentence-card block, extracts the Natural Tagalog line, and rebuilds the extra examples.
If the line has already appeared, the dedupe step adds article and sentence context.

After rewriting, the script regenerates grammar and pronunciation helper sections.
Then it asserts that each article still has exactly 40 cards.

This gives us two wins:
1. The learner gets more useful examples.
2. The developer gets measurable output for review.

结语反思

额外示例重写脚本说明,内容 QA 是一个工程问题。项目使用小型 Python 脚本、稳定的 HTML 标记、转义输出、感知分组的示例场景、重复追踪、语法刷新、发音刷新,以及严格的卡片数量检查。这套工作流让 Tagalog 学习应用对学习者更有用,也让它作为技术分享 Demo 更有说服力。