یک فرهنگ تلفظ آمریکایی میگوید گربه شبیه K AE1 T بهنظر میرسد. الفبای آوانگاریِ بینالمللی همان صدا را kæt مینویسد. یک نظامِ فقط-ASCIIِ متفاوت آن را k"{t مینویسد. هر سه دقیقاً همان دو واج را توصیف میکنند — یک صدای “k” پس از آن یک “a”ی کوتاه پس از آن یک “t”. هیچچیز دربارهٔ خودِ صدا تغییر نکرده. تنها الفبای بهکاررفته برای نوشتنش تغییر کرده.
این اتفاق پیوسته برای هرکسی میافتد که دادهی تلفظ را از بیش از یک منبع ترکیب میکند. یک مجموعهدادهٔ گفتاری ساختهشده از یک فرهنگ آمریکایی از یک نشانهگذاری استفاده میکند. یک واژگان اروپایی از یکی دیگر. یک موتور متن به گفتار سومی را انتظار دارد. پیش از آنکه هر یک از آن دادهها بتوانند ادغام، جستوجو یا مقایسه شوند، باید از یک الفبای آوایی به دیگری ترجمه شوند — همان کاری که یک مترجم میان زبانهای انسانی انجام میدهد، جز اینکه اینجا “زبانها” شیوههای نوشتنِ صدا هستند بهجای شیوههای نوشتنِ واژه.
scriptconv یک کتابخانهٔ کوچک پایتون است که این ترجمه، بهعلاوهٔ کاری مرتبط یک لایه بالاتر را انجام میدهد: تشخیص اینکه یک تکه متن اصلاً در کدام نظام نوشتاری است پیش از آنکه هر کار دیگری بتواند رویش انجام شود. هیچ نظری دربارهٔ زبانشناسی ندارد — حدس نمیزند یک واژه چگونه تلفظ میشود. تنها نمادهایی را که از پیش صداهای شناختهشدهای را بازنمایی میکنند از یک نشانهگذاری به دیگری منتقل میکند، و رسمالخطها را از خودِ نویسهها شناسایی میکند.
چند اصطلاح، بهروشنی تعریفشده
- رسمالخط (Script): یک نظام نوشتاری — همان مجموعهٔ واقعیِ نویسهها، مانند لاتین، سیریلیک یا هانگول. با زبان یکی نیست: انگلیسی، فرانسوی و ویتنامی همگی از رسمالخط لاتین استفاده میکنند، و صربی میتواند به سیریلیک یا لاتین نوشته شود.
- املا (Orthography): قواعد املاییِ متعارف برای نوشتن یک زبان خاص در یک رسمالخط — حروف بزرگ، نشانههای تکیه، فاصلهگذاری.
- واج (Phoneme): یک واحد متمایز صدا در یک زبان، مانند صدای “k” در “cat”.
- IPA (الفبای آوانگاریِ بینالمللی): یک الفبای استاندارد برای نوشتن دقیق صداها، مستقل از املای معمولِ هر زبانی.
- حرفنویسی (Transliteration): تبدیل متن از یک رسمالخط به دیگری با نگاشت نویسهها، با هدف حفظ دقیقِ خودِ املا بهجای تلفظ.
- لاتیننویسی (Romanization): حرفنویسی بهطور خاص به رسمالخط لاتین.
چرا الفباهای آواییِ فقط-ASCII اصلاً وجود دارند
IPA به نویسههایی مانند ʃ، ʒ، ə و ˈ نیاز دارد که روی یک صفحهکلید استاندارد نیستند. این دههها مسئلهٔ واقعیِ محاسبات بود، پیش از آنکه یونیکد جهانی شود و پیش از آنکه بیشتر فونتها، ترمینالها و قالبهای فایل بهطور قابلاعتماد از متن غیر-ASCII پشتیبانی کنند. پژوهشگران جایگزینهای فقط-ASCII ساختند: ARPABET، توسعهیافته برای کار تشخیص گفتار انگلیسی آمریکایی، و X-SAMPA، رمزگذاری ASCII از کل IPA که برای ایمیل و ترمینالهای قدیمی توسعه یافت. اینها کنجکاویهای تاریخی نیستند. ARPABET هنوز نشانهگذاریِ بهکاررفته توسط فرهنگهای تلفظِ انگلیسیِ آمریکاییِ بهشدت مستقر و ابزارهای گفتاری است، و X-SAMPA هنوز در ابزارهای زبانشناختی که به متن ساده نیاز دارند نمود مییابد. هرچیزی که آن داده را میخواند باید بتواند آن الفبا را بخواند.
scriptconv تبدیل واقعی را اجرا میکند. این خروجیِ اجراشده است، نه یک توصیف:
from scriptconv import convert, arpa_to_ipa, ipa_to_arpa
convert("K AE1 T", "arpa", "ipa")
# 'kæt'
convert("HH AH0 L OW1", "arpa", "ipa")
# 'həloʊ'
convert("kˈæt", "ipa", "x-sampa")
# 'k"{t'
arpa_to_ipa("HH AH0 L OW1", stress=True)
# 'həlˈoʊ'
ipa_to_arpa("həlˈoʊ", stress=True)
# 'HH AH0 L OW1'
نشانگرهای تکیه از سفر رفتوبرگشت زنده بیرون میآیند. ARPABET تکیه را با یک رقم چسبیده به واکه نشان میدهد (OW1)؛ IPA آن را با یک ˈ قرارگرفته پیش از هجای تکیهدار نشان میدهد. arpa_to_ipa(..., stress=True) آن اطلاعات را منتقل میکند، و تبدیل به عقب همان ارقام اصلی را دقیقاً بازسازی میکند.
IPA عمداً در میانهٔ همهٔ اینها مینشیند. scriptconv با هر نشانهگذاری مانند یک گره در یک گراف و با هر تبدیلگر مانند یک یال رفتار میکند، و تبدیلها را از میان IPA بهعنوان یک هاب مسیریابی میکند بهجای نوشتنِ دستیِ یک تبدیلگر برای هر جفت نشانهگذاری مستقیماً:
from scriptconv import DEFAULT_GRAPH
[f"{e.src}->{e.dst}" for e in DEFAULT_GRAPH.route("arpa", "x-sampa")]
# ['arpa->ipa', 'ipa->x-sampa']
نه نشانهگذاری در مجموع از میان آن هاب رمزگذاری میشوند: ARPABET، X-SAMPA، Kirshenbaum، Lexique، Cotovía، RFE و mantoq، بهعلاوهٔ Buckwalter، که پایینتر پوشش داده میشود.
تشخیص رسمالخط پیش از هر کار دیگری
پیش از آنکه نرمافزار بتواند تصمیم بگیرد چگونه یک تکه متن را پردازش کند — در کدام جهت آن را رندر کند، کدام غلطیاب را اجرا کند، کدام فونت را انتخاب کند — باید بداند متن در کدام رسمالخط است. این پرسشی متفاوت از این است که در کدام زبان است. رسمالخط مجموعهٔ نویسهها را مشخص میکند؛ زبان واژگان و دستور را مشخص میکند. صربی، دوباره، میتواند سیریلیک یا لاتین باشد. ازبکی هم میتواند. scriptconv رسمالخط را مستقیماً از نویسهها تشخیص میدهد، و جداگانه یک کدِ زبان را به رسمالخطی که معمولاً در آن نوشته میشود نگاشت میکند:
from scriptconv import detect_script, script_runs, lang_to_script, base_direction
detect_script("Здравствуйте")
# 'Cyrl'
detect_script("안녕하세요")
# 'Hang'
script_runs("привет hello")
# [('Cyrl', 'привет '), ('Latn', 'hello')]
base_direction("مرحبا hello")
# 'mixed'
lang_to_script("uzb_cyr")
# 'Cyrl'
detect_script یک کد ISO 15924 برمیگرداند — رجیستریِ استاندارد برچسبِ چهارحرفی برای رسمالخطها (Cyrl برای سیریلیک، Hang برای هانگول، Latn برای لاتین، Arab برای عربی). script_runs متنِ مخلوط را به بخشهای پیوستهٔ بر پایهٔ رسمالخط تقسیم میکند، که همان چیزی است که یک رندرکننده نیاز دارد تا جملهبهجمله تصمیم بگیرد کدام فونت و جهت متن را اعمال کند. base_direction گزارش میدهد آیا یک رشتهٔ مخلوط از چپبهراست، راستبهچپ، یا هر دو خوانده میشود.
موارد سخت: Buckwalter، هانگول و کانا
سه تبدیل نظام نوشتاری آنقدر در خط لولههای واقعی رخ میدهند که scriptconv هر یک را مستقیماً مدیریت میکند.
Buckwalter، برای عربی، یک طرح حرفنویسیِ ASCII است که هر حرف و اعراب عربی را یکبهیک به یک نویسهٔ ASCII خاص نگاشت میکند، پس املای اصلی — از جمله نشانههای واکهای که بیشتر متن بومی حذفشان میکند — میتواند دقیقاً بازسازی شود. این وجود دارد چون رسمالخط عربی در خط لولهها و ابزارهای ساختهشده حول ASCII دستوپاگیر است: مرتبسازی، diff گرفتن، عبارات باقاعده، و قالبهای متنیِ قدیمیتر همه راحتتر میشوند وقتی متن ASCII حروف لاتین باشد، به شرطی که نگاشت دقیق و بازگشتپذیر باشد.
from scriptconv import buckwalter_to_arabic, arabic_to_buckwalter
buckwalter_to_arabic("mrHbA")
# 'مرحبا'
arabic_to_buckwalter("مرحبا")
# 'mrHbA'
arabic_to_buckwalter("رحمٰن")
# 'rHm`n'
نمونهٔ آخر شامل الف خنجری است، یک نشانهٔ کوچک بالانویس بهکاررفته در مشتی واژه (رحمٰن، رحمان) — Buckwalter یک نویسهٔ ASCII خاص (`) برایش رزرو کرده، متمایز از یک الف معمولی، پس حرفنویسی آن دو را در هم نمیریزد.
هانگول مانند بلوکهای هجایی بهنظر میرسد، اما هر بلوک یک خوشهٔ ترکیبیِ حروف منفرد (جامو) چیدهشده در یک شبکه است — همانطور که “H”، “A”، “N” از نظر دیداری در یک گلیف برای “han” ترکیب میشوند بهجای اینکه از چپ به راست نوشته شوند. نرمافزاری که به حروف منفرد نیاز دارد — برای جستوجو، برای تحلیل واجشناختی، برای تغذیهٔ یک سامانهٔ دیگر — باید آنها را دوباره از هم جدا کند:
from scriptconv.translit import decompose_hangul
decompose_hangul("한국")
# 'ㅎㅏㄴㄱㅜㄱ'
decompose_hangul("국민")
# 'ㄱㅜㄱㅁㅣㄴ'
آن مورد آخر برای چیزی که انجام نمیدهد اهمیت دارد: 국민 (گونگمین، “شهروند”) با همگونیِ خیشومی تلفظ میشود، [ɡuŋmin]، اما decompose_hangul حروف را همانطور که نوشته شدهاند برمیگرداند — ㄱㅜㄱㅁㅣㄴ، بدون همگونی — چون تجزیه محاسبهای روی نقطهکد یونیکد است، نه یک قاعدهٔ واجشناختی. به شما میگوید چه نوشته شده، نه چه صدایی میدهد.
تبدیل کانا میان دو هجانگاریِ ژاپنی، هیراگانا و کاتاکانا، حرکت میکند، که همان صداها را با نویسههای متفاوت در یک افستِ نقطهکدِ ثابت بازنمایی میکنند:
from scriptconv import hira_to_kana, kana_to_hira
hira_to_kana("こんにちは")
# 'コンニチハ'
kana_to_hira("カタカナ")
# 'かたかな'
چرا این در کتابخانهٔ خودش زندگی میکند
یک فونمایزر — ابزاری که حدس میزند یک واژهٔ نوشتهشده چگونه تلفظ میشود — به قضاوت زبانشناختی نیاز دارد: قواعد تکیه، استثنائات، تلفظ وابسته به بستر. scriptconv عمداً هیچکدام از اینها را ندارد. هر تابع بالا یا یک جستوجوی جدول است یا یک محاسبهٔ نقطهکد: همان ورودی، همان خروجی، بدون حدسزدن، بدون مدل زبانی، بدون هیچچیزی که بتواند دربارهٔ اینکه یک زبان خاص واقعاً چه صدایی میدهد اشتباه باشد. همین است که بهاشتراکگذاری آن در سراسر هر فونمایزری که به آن نیاز دارد را امن میکند، بهجای اینکه هر فونمایزر جدولِ ARPABET خودش را با اشکالات خودش دوباره پیادهسازی کند. نوشتهٔ پشتهٔ واجشناسی پوشش میدهد که موتورهای واقعیِ حدسزنندهی تلفظ — آنهایی که واقعاً نظرات زبانشناختی حمل میکنند — چگونه روی این لایه ساخته شدهاند بهجای تکرار آن.
جایی که نگاشت دقیق نیست
تبدیل میان نشانهگذاریها همیشه بدوناتلاف نیست، و scriptconv این را بهصورت دادهی قابلپرسوجو ثبت میکند بهجای اینکه آن را بهعنوان یک غافلگیری رها کند. هر نشانهگذاری دو ویژگیِ مستقلاً ردیابیشده دارد: اینکه آیا تبدیل آن به IPA و برگشت، نمادهای اصلی را دقیقاً بازتولید میکند، و اینکه آیا IPA تبدیلشده به آن و برگشت، هر نماد IPA را بازتولید میکند.
ARPABET در هر دو جهت شکست میخورد: یک فهرست واجیِ محدود و انگلیسی-ویژه دارد، پس رفتن IPA ← ARPABET ← IPA میتواند تمایزهایی را که IPA میتواند بسازد اما جدول ARPABET نمادی برایشان ندارد از دست بدهد. X-SAMPA و Lexique کل فهرست IPA را وفادارانه میپوشانند اما تضمینی ندارند که از سمت خودشان تمیز رفتوبرگشت کنند. Kirshenbaum و Buckwalter از سمت خودشان به IPA تمیز رفتوبرگشت میکنند اما برعکسش نه. Mantoq، الفبای آوایی فونتایزر عربیِ حلبی، تنها یکطرفه تبدیل میکند، به IPA — هیچ تبدیلگر بازگشتی وجود ندارد. هیچکدام از اینها جایی در یک docstring مدفون نیست؛ دادهای است که کتابخانه نمایان میکند تا یک فراخوان بتواند پیش از فرض کردنِ ایمنِ یک رفتوبرگشت آن را بررسی کند.
اگر دادهی تلفظ را از چندین منبع کنار هم میگذارید، یا نیاز دارید رسمالخطها را تشخیص دهید و متن را پیش از رسیدن به یک فونمایزر نرمالسازی کنید، با ما تماس بگیرید یا ببینید در این حوزه چه چیزهای دیگری در صفحهٔ خدمات میسازیم.