Языковая политика
Личные блоги => cetsalcoatle => Topic started by: cetsalcoatle on 31 December 2025, 01:01:40
-
function translitUA(text) {
const vowels = "аеєиіїоуюяАЕЄИІЇОУЮЯ";
const isVowel = ch => vowels.includes(ch);
const baseMap = {
"а":"a","б":"b","в":"v","г":"h","ґ":"g","д":"d",
"е":"e","ж":"ž","з":"z","и":"y","і":"i","й":"j",
"к":"k","л":"l","м":"m","н":"n","о":"o","п":"p",
"р":"r","с":"s","т":"t","у":"u","ф":"f","х":"x",
"ц":"c","ч":"č","ш":"š","щ":"ś",
"А":"A","Б":"B","В":"V","Г":"H","Ґ":"G","Д":"D",
"Е":"E","Ж":"Ž","З":"Z","И":"Y","І":"I","Й":"J",
"К":"K","Л":"L","М":"M","Н":"N","О":"O","П":"P",
"Р":"R","С":"S","Т":"T","У":"U","Ф":"F","Х":"X",
"Ц":"C","Ч":"Č","Ш":"Š","Щ":"Ś"
};
let result = "";
let prevChar = "";
for (let i = 0; i < text.length; i++) {
const ch = text[i];
const lower = ch.toLowerCase();
// Ї — всегда ji
if (lower === "ї") {
result += (ch === "Ї") ? "Ji" : "ji";
}
// Я
else if (lower === "я") {
const out = (!prevChar || isVowel(prevChar)) ? "ja" : "ǎ";
result += (ch === "Я") ? out.toUpperCase() : out;
}
// Є
else if (lower === "є") {
const out = (!prevChar || isVowel(prevChar)) ? "je" : "ě";
result += (ch === "Є") ? out.toUpperCase() : out;
}
// Ю
else if (lower === "ю") {
const out = (!prevChar || isVowel(prevChar)) ? "ju" : "ǔ";
result += (ch === "Ю") ? out.toUpperCase() : out;
}
// Йо / ьо
else if (
(lower === "о") &&
(prevChar === "й" || prevChar === "ь" || prevChar === "Й" || prevChar === "Ь")
) {
// уже обработали й / ь, заменяем o
result += (!prevChar || isVowel(prevChar)) ? "o" : "ǒ";
}
// Мягкий знак
else if (lower === "ь") {
result += "í";
}
// Обычные буквы
else if (baseMap[ch]) {
result += baseMap[ch];
}
// Всё остальное
else {
result += ch;
}
prevChar = ch;
}
return result;
}
Интересно, оно работает? :what? У меня просто под рукой редактора кода нет. :dunno:
-
А что непонятно? Вроде даже прокомментировано адекватно.
-
А что непонятно? Вроде даже прокомментировано адекватно.
Да вроде всё понятно, просто это код сгенерированный GPT (и на мой непрофессиональный взгляд довольно классно), вопрос в том, можно ли его проверить без редактора кода?
-
Это же JavaScript, открываете консоль браузера и проверяете.
-
Оригинал:
Сучасні USB на ноутбуках стали більш енергоощадливі, через що бувають казуси, як-от проблема зі зовнішнім CD-приводом у мене. Вирішення дійсно одне: додаткове живлення, якщо конструкція передбачує.
Результат:
Sučasni USB na noutbukax staly bilíš enerhoośadlyvi, čerez śo buvajutí kazusy, ǎk-ot problema zi zovnišnim CD-pryvodom u mene. Vyrišennǎ dijsno odne: dodatkove žyvlennǎ, ǎkśo konstrukcija peredbačuje.
-
Оригинал:
Результат:
Спасибо! :up:
Чёт он косячит, должно было быть jak-ot и jakśo.
И в потоке í и i сливаются, возможно стоит "ь" обставить без изменений, но в целом мне нравится.
-
(и на мой непрофессиональный взгляд довольно классно)
Я бы сказал - совсем не классно, по-джуниорски. :) Много бизнес-логики засунуто в самую низкоуровневую часть реализации (цепочка else if-ов внутри цикла с какими-то мутабельными переменными); много копипасты. Поэтому трудно понять, что делает алгоритм, и проблематично его поддерживать.
Технический момент: создавать строку с помощью += есть антипаттерн. Каждый промежуточный результат уходит в мусор, что означает квадратичный мусор. Когда строку создают из большого числа частей, их собирают в массив и применяют join.
-
Я бы сказал - совсем не классно, по-джуниорски. :) Много бизнес-логики засунуто в самую низкоуровневую часть реализации (цепочка else if-ов внутри цикла с какими-то мутабельными переменными); много копипасты. Поэтому трудно понять, что делает алгоритм, и проблематично его поддерживать.
Технический момент: создавать строку с помощью += есть антипаттерн. Каждый промежуточный результат уходит в мусор, что означает квадратичный мусор. Когда строку создают из большого числа частей, их собирают в массив и применяют join.
Можете, пожалуйста, дать пример фрагмента кода, чтобы оно было не по-джуниорски? :)
Учиться желание бешеное имею. :yes:
-
С йотированными вроде понятно, что должно быть, а какие правила для "йо" и "ьо"?
-
Насчёт чего у меня зачесались руки - что нужно разделить формулировку правил и транслитерацию текста. Потому что тут как бы два «специалиста» работают. «Филологу» интересно разрабатывать набор правил, а как они применяются к строке - неважно, технический вопрос. «Программисту», наоборот, конкретные правила не очень интересны. Loose coupling означает, что «программист» и «филолог» стараются минимально вмешиваться в работу друг друга. Они договорились, что набор правил будет объектом, определяющим правила преобразования конкретных литер. Ради гибкости такое правило может формулироваться в виде функции, преобразующей контекст. Использование функций в качестве входных данных обычно выводит гибкость на новый уровень.
Каждое нетривиальное правило реализовано в виде функции. Это всегда полезно, когда сущности предметной области соответствует сущность программы. Например, если есть проблема с транслитерацией буквы «о», мы сразу понимаем, что нужно дебажить, и не боимся, что фикс конкретного правила повлечёт неожиданные регресии. В качестве бонуса - можно одновременно экспериментировать с разными транслитами.
Такой подход, когда логика формулируется в виде некоего объекта, скармливаемого «движку», иногда называется data-driven programming.
Приведённая реализация не очень удобна для ди- и более графов. Для украинского это не очень важно, потому что нам нужно обработать только один диграф - ьо. Если её предполагается использовать для ситуаций, когда диграфов много, можно допилить «движок»: например, так, чтобы ключами объекта-преобразования допускались бы не только однолитерные строки.
/*
* В задаче есть две подзадачи.
* 1. Формулировка правил транслитерации.
* 2. Применение правил к транслитерации текста.
* Эти задачи достаточно независимы, и мы постараемся избегать tight coupling.
*
* Правило транслитерации может быть простым, когда транслитерируемой литере
* соответствует строка транслита, или специальным, когда литера
* транслитерируется в зависимости от контекста. Мы будем моделировать набор
* правил объектом, в котором однолитерным строкам ставятся в соответствие или
* строки, или функции, преобразующие контекст в строки. Под «контекстом» будем
* понимать тройку (currentIndex, text, currentChar). Такое представление
* правил не очень удобно для диграфов, но работает для данной задачи.
*/
/*
* Сначала напишем "движок", преобразующий набор правил в
* функцию-транслитератор текста.
*/
function createTranslit(rules) {
return text => {
const result = [];
const textLength = text.length;
for (let index = 0; index < textLength; ++index) {
const currentChar = text[index];
const translation = rules[currentChar];
if (typeof translation === 'string') {
// простое правило транслитерации
result.push(translation);
} else if (typeof translation === 'function') {
// специальное правило транслитерации: функция, преобразующая контекс
result.push(translation(index, text, currentChar));
} else {
// если правило не определено для литеры, литера не транслитерируется
result.push(currentChar);
}
}
return result.join('');
}
}
// Правила и транслитератор для украинской письменности
const LOWERCASE_CONSONANTS = 'бвгґджзйклмнпрстфхцчшщ';
const CONSONANTS = new Set(LOWERCASE_CONSONANTS + LOWERCASE_CONSONANTS.toUpperCase());
function isUaConsonant(ch) {
return CONSONANTS.has(ch);
}
// Специальное правило для йотированных букв.
function jotatedTranslation(jotated, softening) {
return (index, text) => {
const prevChar = text[index - 1];
// После согласной - смягчающий вариант, иначе - йотированный
return isUaConsonant(prevChar) ? softening : jotated;
}
}
// Транслитерация мягкого знака: «ьо» преобразуется в смягчающее «о», иначе
// возвращается softSignChar
function softSignTranslation(softSignChar, softeningOChar) {
return (index, text) => {
const nextChar = text[index + 1];
if (nextChar === 'о') return softeningOChar;
return softSignChar;
}
}
// Транслитерация «о»: преобразуется в oChar за исключением позиции после
// мягкого знака. В последнем случае буква является частью диграфа и не
// добавляется к транслитерируемому тексту
function oTranslation(oChar) {
return (index, text) => {
const prevChar = text[index - 1];
return prevChar === 'ь' ? '' : oChar;
}
}
const translitUa = createTranslit({
'я': jotatedTranslation('ja', 'ǎ'),
'Я': 'Ja',
'є': jotatedTranslation('je', 'ě'),
'Є': 'Je',
'ю': jotatedTranslation('ju', 'ǔ'),
'Ю': 'Ju',
'ь': softSignTranslation('í', 'ǒ'),
'Ь': 'Í',
'о': oTranslation('o'),
'О': 'O',
"'": '',
'’': '',
'а':'a',
'б':'b',
'в':'v',
'г':'h',
'ґ':'g',
'д':'d',
'е':'e',
'ж':'ž',
'з':'z',
'и':'y',
'і':'i',
'й':'j',
'к':'k',
'л':'l',
'м':'m',
'н':'n',
'п':'p',
'р':'r',
'с':'s',
'т':'t',
'у':'u',
'ф':'f',
'х':'x',
'ц':'c',
'ч':'č',
'ш':'š',
'щ':'ś',
'А':'A',
'Б':'B',
'В':'V',
'Г':'H',
'Ґ':'G',
'Д':'D',
'Е':'E',
'Ж':'Ž',
'З':'Z',
'И':'Y',
'І':'I',
'Й':'J',
'К':'K',
'Л':'L',
'М':'M',
'Н':'N',
'О':'O',
'П':'P',
'Р':'R',
'С':'S',
'Т':'T',
'У':'U',
'Ф':'F',
'Х':'X',
'Ц':'C',
'Ч':'Č',
'Ш':'Š',
'Щ':'Ś',
});
// translitUa('Сучасні USB на ноутбуках стали більш енергоощадливі, через що бувають казуси, як-от проблема зі зовнішнім CD-приводом у мене. Вирішення дійсно одне: додаткове живлення, якщо конструкція передбачує.')
// "Sučasni USB na noutbukax staly bilíš enerhoośadlyvi, čerez śo buvajutí kazusy, jak-ot problema zi zovnišnim CD-pryvodom u mene. Vyrišennǎ dijsno odne: dodatkove žyvlennǎ, jakśo konstrukcija peredbačuje.
// translitUa("льод м'яч")
// "lǒd mjač"
-
Там, кстати, реальный косяк в этом фрагменте, в комментарии написано «уже обработали й / ь» до проверки на «ь» в следующем else if.
-
Я, когда делал свои транслитераторы (на основе рекламируемого Lugat'ом макаронного), вместо системы {"ключ-оригинал": "значение-транслит", "к2": "з2"} использовал систему [["Элемент-оригинал", "Element-транслит"], "ЭE"], что позволяет использовать одинаковый синтаксис arr[0], arr[1] как для двухэлементных подмассивов, так и для двухсимвольных строк, в которых содержатся оригинальная и транслитная часть.
А те изменения, которые не подпадают под простую замену, пропускаются потом через дополнительный массив регэкспов, так как их может быть слишком много для написания отдельных функций под каждый случай. Это с украинским просто, а попробуйте санскрит или древнегреческий. С оригинального письма. :lol: