Salam,
Saya telah mengambil skrip Python di atas untuk membuatnya menjadi umum, di mana akan ada prompt untuk daftar kata dan leksikon (jika ada), serta mengoutputkan file SMF untuk diimpor ke FLEx
#!/usr/bin/python3
"""
wordlist_to_sfm.py
Mengekspor daftar kata Paratext ke format SFM (Standard Format Markers)
untuk diimpor ke FLEx (FieldWorks Language Explorer).
Penggunaan:
python wordlist_to_sfm.py
Skrip ini akan meminta:
- File XML daftar kata Paratext (misalnya, diekspor dari alat daftar kata Paratext)
- Opsional, file XML Leksikon Paratext untuk menyediakan glosa bahasa Inggris
- Path file output untuk file .sfm yang dihasilkan
Hanya kata yang ditandai sebagai "Correct" dalam daftar kata yang diekspor secara default.
Hal ini dapat diubah dengan mengedit pengaturan INCLUDE_* di bawah ini.
Format output adalah MDF (Multi-Dictionary Formatter), dialek SFM standar yang digunakan oleh FLEx untuk impor leksikon.
"""
import codecs
import os
import xml.etree.ElementTree as etree
# --- Pengaturan ----------------------------------------------------------------
# Mengontrol kategori ejaan mana yang disertakan dalam ekspor.
# Setel ke True untuk menyertakan kata dalam kategori tersebut, False untuk mengecualikannya.
INCLUDE_CORRECT = True # Kata yang telah disetujui penerjemah
INCLUDE_UNKNOWN = False # Kata yang belum ditinjau
INCLUDE_INCORRECT = False # Kata yang ditandai sebagai salah eja
# Setel ke True untuk menulis frekuensi korpus kata sebagai field komentar (\co_count)
INCLUDE_COUNT = True
# -----------------------------------------------------------------------------
def prompt_path(prompt_text, default):
"""Menampilkan prompt dengan nilai default opsional; mengembalikan input pengguna atau default."""
display = f" [{default}]" if default else ""
value = input(f"{prompt_text}{display}: ").strip()
return value if value else default
def get_user_inputs():
"""Meminta pengguna untuk tiga path file yang diperlukan untuk menjalankan ekspor."""
print("Paratext Word List to SFM Exporter")
print("-----------------------------------\n")
# Daftar kata diperlukan - terus meminta sampai file ditemukan
wordlist_path = prompt_path("Word list file (XML)", "Notsi-WL.xml")
while not os.path.exists(wordlist_path):
print(f" Cannot find '{wordlist_path}'. Please check the path and try again.")
wordlist_path = prompt_path("Word list file (XML)", "Notsi-WL.xml")
# Leksikon opsional - lewati secara diam-diam jika tidak ditemukan
lexicon_path = prompt_path("Lexicon file for glosses (press Enter to skip)", "")
if lexicon_path and not os.path.exists(lexicon_path):
print(f" Cannot find '{lexicon_path}'. Continuing without glosses.")
lexicon_path = ""
output_path = prompt_path("Output file name", "wordlist_for_flex.sfm")
return wordlist_path, lexicon_path, output_path
def load_lexicon_glosses(lexicon_path):
"""
Membaca glosa bahasa Inggris dari file XML Leksikon Paratext.
Mengembalikan kamus yang dikey berdasarkan bentuk lekseme, di mana setiap nilai berisi tipe lekseme (Word, Prefix, Suffix) dan daftar string glosa bahasa Inggris.
Mengembalikan kamus kosong jika tidak ada path leksikon yang disediakan.
"""
glosses = {}
if not lexicon_path:
print("No lexicon provided - entries will be exported without glosses.")
return glosses
print(f"Reading lexicon from: {lexicon_path}")
with open(lexicon_path, "rb") as f:
data = f.read()
# Hapus varian BOM apa pun (BOM UTF-8 standar, atau BOM yang dienkod dua kali)
for bom in (b"\xef\xbb\xbf", b"\xc3\xaf\xc2\xbb\xc2\xbf"):
if data.startswith(bom):
data = data[len(bom):]
break
xmldoc = etree.fromstring(data)
for item in xmldoc.findall("Entries/item"):
lexeme = next(item.iter("Lexeme"), None)
if lexeme is None:
continue
form = lexeme.get("Form", "")
lex_type = lexeme.get("Type", "Word") # Word, Prefix, atau Suffix
# Kumpulkan semua glosa bahasa Inggris untuk entri ini (mungkin ada lebih dari satu makna)
entry_glosses = [
gloss.text
for gloss in item.iter("Gloss")
if gloss.get("Language") == "en" and gloss.text
]
if form and entry_glosses:
glosses[form] = {"type": lex_type, "glosses": entry_glosses}
print(f" Found {len(glosses)} entries with English glosses.")
return glosses
def build_approved_list(wordlist_path):
"""
Membaca XML daftar kata Paratext dan mengembalikan kata yang lolos filter INCLUDE_*.
Mengembalikan daftar bentuk kata yang disetujui dan kamus metadata yang berisi jumlah korpus, penghubung, dan rincian morfologi untuk setiap kata.
"""
wordlist = etree.parse(wordlist_path)
all_items = wordlist.getroot().findall("item")
print(f"Reading word list from: {wordlist_path}")
print(f" {len(all_items)} words found in list.")
approved = []
metadata = {}
for item in all_items:
spelling = item.attrib.get("spelling", "Unknown")
word = item.attrib.get("word", "")
include = (
(spelling == "Correct" and INCLUDE_CORRECT) or
(spelling == "Unknown" and INCLUDE_UNKNOWN) or
(spelling == "Incorrect" and INCLUDE_INCORRECT)
)
if include and word:
approved.append(word)
metadata[word] = {
"count": item.attrib.get("count", "0"),
"hyphenation": item.attrib.get("hyphenation", ""),
"morphology": item.attrib.get("morphology", ""),
"morph_approved": item.attrib.get("morphologyApproved", "False"),
"specificcase": item.attrib.get("specificcase", ""),
}
print(f" {len(approved)} words marked as correct and ready to export.")
return approved, metadata
def write_sfm_entry(outfile, word, lex_type, glosses, meta, include_count):
"""
Menulis satu entri leksikon SFM ke file output.
Konvensi MDF yang diikuti:
\\lx - lekseme (kata utama); afiks mendapat tanda hubung di sisi yang terikat
\\sn - nomor makna (ditulis sekali per glosa)
\\ge - glosa bahasa Inggris untuk makna tersebut
\\mr - string morfologi (jika disetujui oleh penerjemah)
\\co_* - field komentar untuk metadata yang tidak memiliki penanda standar di FLEx
"""
# Tulis kata utama, menambahkan tanda hubung untuk menunjukkan titik tempelan afiks
outfile.write("\n\\lx ")
if lex_type == "Suffix":
outfile.write("-")
outfile.write(word)
if lex_type == "Prefix":
outfile.write("-")
outfile.write("\n")
# Catat tipe afiks sebagai komentar agar tetap ada setelah impor FLEx
if lex_type != "Word":
outfile.write(f"\\co_type {lex_type}\n")
# Frekuensi korpus - berguna untuk memprioritaskan entri selama pembuatan kamus
if include_count and meta:
outfile.write(f"\\co_count {meta['count']}\n")
# Morfologi: gunakan penanda standar \\mr jika analisis telah disetujui,
# jika tidak, simpan sebagai komentar untuk menghindari impor data yang belum diverifikasi
if meta and meta["morphology"]:
marker = "\\mr" if meta["morph_approved"] == "True" else "\\co_morph"
outfile.write(f"{marker} {meta['morphology']}\n")
# Tulis satu blok makna per glosa
for sense_num, gloss_text in enumerate(glosses, start=1):
outfile.write(f"\\sn {sense_num}\n")
outfile.write(f"\\ge {gloss_text}\n")
def main():
"""
Rutinitas ekspor utama.
Pendekatan dua langkah:
Langkah 1 - tulis entri yang memiliki glosa leksikon (data yang lebih kaya terlebih dahulu)
Langkah 2 - tulis kata disetujui lainnya yang tidak memiliki entri leksikon
Ini memastikan entri yang berglosa tidak diduplikasi.
"""
wordlist_path, lexicon_path, output_path = get_user_inputs()
print()
approved_words, metadata = build_approved_list(wordlist_path)
lexicon_glosses = load_lexicon_glosses(lexicon_path)
# Buka file output sebagai UTF-8 dan tulis header MDF
outfile = codecs.open(output_path, mode="w", encoding="utf-8")
outfile.write("\\_sh v3.0 400 MDF\n\\_DateStampHasFourDigitYear\n\n")
remaining = list(approved_words) # Kata yang masih harus ditulis setelah langkah 1
with_glosses = 0
# Langkah 1: entri yang muncul di kedua leksikon dan daftar kata disetujui
for form, lex_data in lexicon_glosses.items():
if form not in approved_words:
print(f" Skipping '{form}' (in lexicon but not marked as correct in word list)")
continue
if form in remaining:
remaining.remove(form)
write_sfm_entry(outfile, word=form, lex_type=lex_data["type"],
glosses=lex_data["glosses"], meta=metadata.get(form),
include_count=INCLUDE_COUNT)
with_glosses += 1
# Langkah 2: kata disetujui tanpa entri leksikon - diekspor tanpa glosa
for word in remaining:
write_sfm_entry(outfile, word=word, lex_type="Word", glosses=[],
meta=metadata.get(word), include_count=INCLUDE_COUNT)
total = with_glosses + len(remaining)
outfile.close()
print(f"\nExport complete.")
print(f" Total entries written : {total}")
print(f" With glosses : {with_glosses}")
print(f" Without glosses : {len(remaining)}")
print(f" Output file : {output_path}")
if __name__ == "__main__":
main()