Знакомство с транслитерацией
Обычной работой лингвистов является транслитерация, то есть преобразование одной орфографии (набора правил написания определенного языка) в другую.
Например, лингвисты, как правило, используют упрощенную или практическую орфографию в своих заметках и базах данных, а не Международный фонетический алфавит (IPA), а затем преобразуют свои данные в IPA или общепринятую американскую нотацию. для публикации. Более того, лингвисты часто работают с сообществами, у которых есть собственная предпочтительная орфография, которая может совпадать или не совпадать с практической орфографией лингвиста. Создание материалов для сообщества требует транслитерации с практической орфографии на орфографию сообщества. И лингвисты обычно работают с данными из архивных источников и предшествующих исследователей, каждый из которых мог использовать различную орфографию для написания языка.
Моя собственная база данных Chitimacha (изолят, Луизиана; ISO 639–3: ctm, Glottolog: chit1248) использует не менее 8 орфографий, многие из которых исходят от предыдущих исследователей:
Современные орфографии
- Современная общественная орфография
- Международный фонетический алфавит
- Американистская нотация
Исторические орфографии
- Фонетическая запись Морриса Сводеха (1930–1934)
- Фонематическая запись Морриса Сводеша (1930–1952)
- Обозначения Джона Р. Суэнтона (1907–1920)
- Обозначения Альберта С. Гатше (1881–1882 гг.)
- Обозначения Мартина Дюральде (1802 г.)
В прошлом это означало, что я постоянно вручную транслитерировал то одну, то другую историческую орфографию в ту или другую современную орфографию. Часто одну и ту же фразу-пример необходимо транслитерировать несколько раз, в зависимости от конкретного издания (например, американистское обозначение для International Journal of American Linguistics, но IPA для более общих журналов). Очевидно, что это было чревато ошибками и отнимало много времени.
Базовая транслитерация
По сути, транслитерация — это простая процедура, в ходе которой выполняется ряд замен в строке. Например, чтобы транслитерировать написанное слово cat в IPA, нам нужно сделать две замены: <c> → <k>
и <a> → <æ>
.
Обратите внимание, что этот метод транслитерации является однонаправленным. Если бы мы хотели транслитерировать обратно с IPA на английский, нам нужно было бы сделать другой набор замен: <k> → <c>
и <æ> → <a>
. Иногда можно выполнить двунаправленную или многонаправленную транслитерацию, когда каждая графема (письменный символ) в орфографии соответствует одной и только одной графеме в другой орфографии. Однако двунаправленная транслитерация часто невозможна из-за сложности различных систем письма. Некоторые различия могут быть потеряны в процессе транслитерации, что делает невозможным обратный процесс без потери информации. Таким образом, алгоритм DLx transliterate()
является однонаправленным.
Имея это в виду, давайте настроим нашу функцию так, чтобы она принимала два аргумента: string
(строка для транслитерации) и substitutions
(список замен, которые нужно сделать в строке). Мы можем назвать набор замен схемой транслитерации, то есть набором правил замены для транслитерации из одной орфографии в другую. Аргумент substitutions
должен быть объектом, ключами которого являются заменяемые графемы, а значениями — замещаемые графемы. Например, схема транслитерации для нашего примера cat выше будет выглядеть так:
const substitutions = { a: 'æ', c: 'k', };
Вот как мы будем использовать наш метод transliterate()
, когда он будет готов:
const ipa = transliterate('cat', substitutions); console.log(ipa); // --> "kæt"
Внутри метода transliterate()
нам нужно будет выполнить каждую замену в аргументе substitutions
с помощью регулярных выражений. Вот как мы можем это сделать:
Выполнение этой функции в нашем примере cat дает нам правильный результат: /kæt/
— мы успешно транслитерировали строку! Теперь давайте рассмотрим некоторые крайние случаи, которые могут нарушить работу нашей функции, и способы их решения.
Пограничный случай 1: подстроки
Допустим, у нас есть следующая схема транслитерации:
{ s: 'x', ts: 'c', }
Если мы запустим нашу текущую версию функции transliterate()
для строки "tsaste"
(«касание» в читимача), мы получим "txaxte"
в качестве вывода — неверный результат! В соответствии с нашей схемой транслитерации мы хотим, чтобы на выходе было "caxte"
.
Что случилось? Проблема в том, что ввод "s"
является подстрокой ввода "ts"
. Когда наша функция делает первую замену "s" → "x"
, строка "ts"
заменяется на "tx"
. Когда наша функция пытается сделать вторую замену ("ts" → "c"
), она терпит неудачу, потому что в строке не может быть найдена последовательность "ts"
. Каждый экземпляр "ts"
уже был изменен на "tx"
.
Чтобы решить эту проблему, нам просто нужно упорядочить замены от самой длинной строки до самой короткой строки, прежде чем делать замены. Вы можете сами убедиться в этом вручную, запустив transliterate()
по приведенной ниже схеме транслитерации. Правила замены те же, что и раньше, но их порядок другой. На этот раз мы получаем желаемый результат.
{ ts: 'c', s: 'x', }
Чтобы программно отсортировать правила подстановки, мы просто запускаем метод .sort()
в нашем списке подстановок перед вызовом метода .forEach()
, например так (обратите внимание на добавленный метод .sort()
):
Пограничный случай 2: кормление
Для второго пограничного случая рассмотрим, что произойдет, если мы запустим нашу функцию transliterate()
, используя следующую схему транслитерации для строки "chaca"
.
{ ch: 'c', c: 'x', }
Используя нашу текущую версию метода transliterate()
, мы получаем неверный результат: "xaxa"
вместо ожидаемого "caxa"
.
Проблема здесь в том, что известно как проблема подачи в лингвистике (особенно в области фонологии), когда результат одного правила/подстановки также является входом для другого правила/подстановки. В приведенном выше примере наша функция сначала изменяет все экземпляры "ch"
на "c"
, а затем изменяет все экземпляры "c"
на "x"
, не оставляя ни одного экземпляра "ch"
или "c"
в конечном выводе.
Чтобы исправить это, мы сначала проверяем список подстановок на наличие проблем с кормлением, а затем разрываем цепочку кормления, временно заменяя вывод неправильного правила каким-либо другим случайным символом. Затем, после того, как замены сделаны с использованием случайного символа, мы заменяем правильный символ обратно. Для случайной временной замены символа мы будем использовать символ Unicode из блока геометрических фигур, так как маловероятно, что кто-то будет использовать символы из этого кодового блока в орфографии (к тому же это было бы крайне плохой практикой).
Вот окончательный код функции transliterate()
, которая сортирует замены и правильно обрабатывает проблемы с подачей. Обратите внимание, что теперь у нас есть объект temps
для хранения наших временных замен проблем с кормлением, метод getRandomCodePoint()
для выбора случайной точки Unicode в блоке геометрических фигур и несколько новых шагов в коде, где мы делаем временную замену, а затем отменяем ее. позже.
И это все! Теперь функция transliterate()
должна давать правильный результат для описанной выше проблемы с кормлением.
Найдите какие-либо другие пограничные случаи, которые этот алгоритм не обрабатывает? Пожалуйста, дайте мне знать в комментариях!
Вывод
Транслитерация оказывается несколько сложнее, чем кажется на первый взгляд. Однако иметь дело с крайними случаями, такими как подстроки и подача, на самом деле довольно просто. Результатом стала компактная и универсальная функция, способная транслитерировать любую орфографию в другую.