AWS Builder 文章

為 AWS Manila Community Day 打造 Tagalog 卡片打造文法與發音補強流程

Tagalog 學習 App 已經有文章頁面與句子卡片。下一個開發挑戰是內容補強:替每個額外例句加入更好的文法拆解與發音指南,而且不要手動編輯數百個重複的 HTML 區塊。Python 展示了一個實用的批次處理模式。

Tagalog 學習 App 文章系列

English 繁體中文 简体中文

Tagalog 學習 App 已經有文章頁面與句子卡片。下一個開發挑戰是內容補強:替每個額外例句加入更好的文法拆解與發音指南,而且不要手動編輯數百個重複的 HTML 區塊。Python 展示了一個實用的批次處理模式。

免責聲明

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

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

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

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

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


Demo

Paki-check po kung pumasok ang bayad.

文法拆解

  • Paki-check:Filipino-English 請求語,意思接近「請幫忙確認」。
  • po:禮貌標記,用於尊重語氣。
  • kung:意思是「如果」或「是否」。
  • pumasok:在付款情境中表示「進來了」或「入帳了」。
  • bayad:意思是「付款」。

發音指南

逐字發音可以讀成:

pah-kee-chehk poh koong poo-mah-sohk ahng bah-yahd.

  • Paki-check:pah-kee-chehk。
  • po:poh。
  • kung:koong。
  • pumasok:poo-mah-sohk。
  • bayad:bah-yahd。

內容快照

核心補強任務:

  • 找出每個 div.extra-example 區塊
  • 從 span 讀取 Tagalog 句子
  • 產生文法拆解項目
  • 產生逐字發音與音節拆解
  • 替換 HTML 中舊的輔助段落
  • 寫出更新後的文章檔案
  • 印出 sanity check

目錄

Part 1: 用批次腳本處理文章群組

說明為什麼補強流程拆成多個腳本,而不是寫成一個巨大檔案。

Part 2: 把 glossary 當成小型本地知識庫

說明腳本如何用字典條目提供初學者友善的文法意思與本地 loanword 解釋。

Part 3: 用已知詞與 fallback 規則產生發音

說明 pronunciation map、token 處理、母音 fallback 與初學者可讀的拆音輸出。

Part 4: 用 BeautifulSoup 修補 HTML

說明如何找到現有段落,並只替換特定標題後面的內容。

Part 5: 驗證補強後的輸出

說明為什麼腳本要印出額外例句、發音句、local tip 與文法拆解的數量。


Part 1: 用批次腳本處理文章群組

目標

不用手動編輯每張卡片,也能改善 24 個文章頁的文法與發音內容。

開發技能

關鍵技能是受控的批次處理。每個腳本負責一小組文章檔案,並搭配該主題專用的 glossary。

files = [
    "article-22-manila-daily-home-laundry-bills-and-errands.html",
    "article-23-manila-daily-work-study-and-social-plans.html",
    "article-24-manila-daily-health-safety-weather-and-money.html",
]

這個模式比一個巨大腳本更容易審閱,因為每個批次都能帶有自己的主題詞彙。Community Day 頁面需要 registrationworkshopbadgevolunteer。Manila Daily 頁面需要 laundrydeliverybatterycashclinicmedicine

Prompt

針對每個文章群組,更新所有 extra example。
保留既有 Tagalog 句子。
用初學者友善的詞義替換文法拆解。
用逐字發音替換發音指南。
寫出更新後的 HTML 檔案並印出摘要。

結果

專案得到一個可重複的補強流程:

文章群組
  -> 主題 glossary
  -> 可見 Tagalog 句子
  -> 文法清單
  -> 發音指南
  -> 更新後 HTML
  -> sanity check

Tips

  • 當詞彙領域不同時,依文章主題拆分腳本。
  • 明確列出檔案,讓審閱者知道腳本範圍。
  • 跨批次維持相同函式名稱。
  • 優先使用確定性輸出,而不是 runtime AI 生成。
  • 每個批次完成後都印出摘要。

Part 2: 把 glossary 當成小型本地知識庫

目標

讓每個腳本成為小型、可檢查的語言輔助工具。

開發技能

腳本使用 dictionary 作為本地知識庫。像 posaan 這類詞會得到穩定的初學者說明;未知詞則會回退到通用的本地使用說明。

defs = {
    "po": "Respect marker used for polite speech.",
    "saan": "Means where.",
    "workshop": "English loanword used locally; means workshop.",
    "badge": "English loanword used locally; means badge.",
}

def get_def(word):
    key = token_key(word)
    if key in defs:
        return defs[key]
    return f'English loanword or useful word used locally; means "{word}" in this context.'

為什麼重要

這不是完整文法解析器,但對靜態學習原型很實用。學習者會看到一致的詞義;開發者也能在需要改善意思時,只更新一個 dictionary entry。

範例

句子:
Saan po ang registration area?

產生的文法拆解:
- Saan: Means where.
- po: Respect marker used for polite speech.
- ang: Focus marker placed before the main noun or idea.
- registration: English loanword used locally; means registration.
- area: English loanword or useful word used locally; means "area" in this context.

Tips

  • 詞義保持短。
  • 誠實標示 loanword,不要假裝每個字都是純 Tagalog。
  • 使用初學者能理解的 wording。
  • 當 fallback 文字太常出現時,補上主題專用 glossary。
  • 把 dictionary entries 當成可審閱內容。

Part 3: 用已知詞與 fallback 規則產生發音

目標

即使 pronunciation map 沒有涵蓋所有字,也要讓每個 extra example 有可讀的發音指南。

開發技能

腳本結合兩種策略:

已知詞:
  使用人工整理過的發音與可選音節拆解。

未知詞:
  使用簡單母音 fallback,讓學習者仍有可讀的指南。

程式模式

pron = {
    "salamat": ("sah-lah-maht", [("sa", "sah"), ("la", "lah"), ("mat", "maht")]),
    "kayo": ("kah-yoh", [("ka", "kah"), ("yo", "yoh")]),
    "bayad": ("bah-yahd", [("ba", "bah"), ("yad", "yahd")]),
}

vmap = {"a": "ah", "e": "eh", "i": "ee", "o": "oh", "u": "oo"}

def fallback_pron(word):
    output = []
    for character in word.lower():
        if character in vmap:
            output.append(vmap[character])
        elif character.isalpha():
            output.append(character)
    return "".join(output) or word

範例輸出

Tagalog:
Uminom po kayo ng tubig dahil mainit.

發音:
oo-mee-nohm poh kah-yoh ngah too-beeg dah-heel mah-ee-neet.

拆解:
- Uminom: oo-mee-nohm.
- po: poh.
- kayo: kah-yoh.
- tubig: too-beeg.
- dahil: dah-heel.
- mainit: mah-ee-neet.

Tips

  • 先整理常用字。
  • fallback 要簡單且透明。
  • 小心保留縮寫與技術詞。
  • 發音指南以練習有用為目標,不必假裝過度精準。
  • 重要短句請母語者審閱。

Part 4: 用 BeautifulSoup 修補 HTML

目標

不重寫整個文章頁,也能更新產生後的 HTML。

開發技能

補強腳本用 BeautifulSoup 解析頁面,找到每個 div.extra-example,讀取 Tagalog span,並替換特定標題後面的內容。

for fname in files:
    soup = BeautifulSoup(Path(fname).read_text(encoding="utf-8"), "html.parser")
    divs = soup.find_all("div", class_="extra-example")

    for div in divs:
        span = div.find("span", lang="tl")
        if not span:
            continue

        sentence = " ".join(span.get_text(" ", strip=True).split())
        replace_after_heading(div, "Grammatical Breakdown:", [make_breakdown_ul(soup, sentence)])
        replace_after_heading(div, "Pronunciation Guide:", make_pronunciation(soup, sentence))

replace_after_heading 很重要,因為它避免替換整張卡片,只移除某個標題到下一個已知標題之間的舊內容。

def replace_after_heading(div, heading_text, new_nodes):
    heading = None
    for candidate in div.find_all("p", recursive=False):
        strong = candidate.find("strong")
        if strong and heading_text in strong.get_text():
            heading = candidate
            break

    if not heading:
        return False

    sibling = heading.find_next_sibling()
    while sibling:
        next_sibling = sibling.find_next_sibling()
        if sibling.name == "p":
            strong = sibling.find("strong")
            if strong and "Pronunciation Guide:" in strong.get_text():
                break
        sibling.extract()
        sibling = next_sibling

    last = heading
    for node in new_nodes:
        last.insert_after(node)
        last = node
    return True

Tips

  • 結構重要時,解析 HTML,不要盲目字串替換。
  • 搜尋範圍要窄。
  • 保留學習者要讀的句子。
  • 只替換產生出的輔助段落。
  • 測試風險較高的批次時,先寫出另一份檔案。

Part 5: 驗證補強後的輸出

目標

證明批次更新確實處理了預期內容。

開發技能

腳本在寫檔後印出摘要列與 sanity check。

print("Update summary:")
for source, out, total, updated, missing in summary:
    print(f"{source} -> {out}: extra_examples={total}, updated={updated}, missing={missing}")

print("Sanity check:")
for out in outputs:
    soup = BeautifulSoup(Path(out).read_text(encoding="utf-8"), "html.parser")
    divs = soup.find_all("div", class_="extra-example")
    phrase = sum(1 for div in divs if "It is pronounced word by word as:" in div.get_text())
    breakdown = sum(1 for div in divs if "Grammatical Breakdown:" in div.get_text())
    print(f"{out}: extra_examples={len(divs)}, pron_phrase={phrase}, has_breakdown={breakdown}")

結果

開發者可以把補強流程解釋成可量測的流程,而不是手動清理。

輸入:
article HTML files

轉換:
grammar and pronunciation regeneration

輸出:
updated HTML files

證據:
extra examples、pronunciation phrases、grammar breakdowns 的數量

Tips

  • 精準計算你改動的區塊。
  • 遇到缺失段落要印出來,不要靜默跳過。
  • 驗證邏輯要靠近腳本。
  • 把產生的 counts 當成技術分享證據。
  • 腳本通過後,仍抽查幾張更新後的卡片。

Field Note 1: 補強是產品層

背景:只有 phrase pair 很有用,但文法與發音會把它變成學習卡片。

目標:不改變句子卡版型,也能加入可重複的學習支援。

Prompt:從可見的 Tagalog 句子產生文法與發音。

結果:每個 extra example 對初學者更有幫助。

Review check:產生的輔助內容是否真的解釋卡片上的句子?

Field Note 2: 批次腳本是可審閱的

背景:一個腳本處理 24 篇文章會太大,也很難調整詞彙。

目標:讓每個批次貼近自己的詞彙領域。

Prompt:每個腳本只處理三個文章檔案。

結果:Community Day、Friendship、Manila Daily 內容都能有更貼切的本地 glossary。

Review check:審閱者能否從 file list 與註解理解詞彙範圍?

Field Note 3: Fallback 需要謙遜

背景:發音 fallback 有用,但不是母語者保證。

目標:給學習者起點,同時保留審閱需求。

Prompt:已知詞用整理過的發音,需要時才用簡單 fallback。

結果:就算每個字還沒有完美發音條目,網站仍然可用。

Review check:重要活動短句是否有整理過,而不是只依賴 fallback?

技術分享角度

對開發者分享來說,這個補強流程是很好的內容工程案例:

HTML article files
      ->
BeautifulSoup parser
      ->
extra-example blocks
      ->
Tagalog sentence extraction
      ->
glossary definitions
      ->
pronunciation map and fallback
      ->
section replacement
      ->
updated HTML files
      ->
sanity checks

重點很簡單:AI 輔助的學習內容仍然需要確定性的工具。小型腳本可以把產生出的頁面變成可審閱的教育材料。

結語

文法與發音腳本展示了手動編輯與過度工程之間的實用中間點。專案不需要資料庫或語言引擎,也能改善每張卡片。它需要的是清楚的文章批次、主題 glossary、發音 helper、謹慎的 HTML patching 與驗證輸出。這讓 App 對學習者更有用,也更容易向開發者解釋。