Author Topic: Транслитератор украинского  (Read 225 times)

Offline cetsalcoatle

  • Эльрат – дракон света
  • Глобальный модератор
  • Дважды герой
  • *
  • Posts: 9303
  • Карма: +1048/-373
  • Благородный муж прям и твёрд, но не упрям.
    • View Profile
Транслитератор украинского
« on: 31 December 2025, 01:01:40 »
  • 0
  • 0
Code: [Select]
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:
WHITE LIVES MATTER.

Всё, что нужно для счастья, — это тело, не страдающее от боли, и душа, свободная от тревог. (с) Эпикур

Останься прост, беседуя с царями,
Останься честен, говоря с толпой;
Будь прям и тверд с врагами и друзьями,
Пусть все, в свой час, считаются с тобой;

Simba mwenda pole ndiye mla nyama

Offline Upliner

  • Администратор
  • Дважды герой
  • *****
  • Posts: 6024
  • Карма: +645/-27
  • Gender: Male
  • Кис-кис-кис!
    • View Profile
    • лифчег
Re: Транслитератор украинского
« Reply #1 on: 31 December 2025, 01:21:12 »
  • 0
  • 0
А что непонятно? Вроде даже прокомментировано адекватно.
Вход в Московское княжество -- 1 рубль, выход -- бесценен.
Византийский цирк уехал, а клоуны остались среди русских.
Дві найголовніші помилки українського народу -- зек і зєк.

Offline cetsalcoatle

  • Эльрат – дракон света
  • Глобальный модератор
  • Дважды герой
  • *
  • Posts: 9303
  • Карма: +1048/-373
  • Благородный муж прям и твёрд, но не упрям.
    • View Profile
Re: Транслитератор украинского
« Reply #2 on: 31 December 2025, 01:41:57 »
  • 0
  • 0
А что непонятно? Вроде даже прокомментировано адекватно.
Да вроде всё понятно, просто это код сгенерированный GPT (и на мой непрофессиональный взгляд довольно классно), вопрос в том, можно ли его проверить без редактора кода?
WHITE LIVES MATTER.

Всё, что нужно для счастья, — это тело, не страдающее от боли, и душа, свободная от тревог. (с) Эпикур

Останься прост, беседуя с царями,
Останься честен, говоря с толпой;
Будь прям и тверд с врагами и друзьями,
Пусть все, в свой час, считаются с тобой;

Simba mwenda pole ndiye mla nyama

Offline Bhudh

  • Дважды герой
  • **
  • Posts: 8413
  • Карма: +2794/-606
    • View Profile
Re: Транслитератор украинского
« Reply #3 on: 01 January 2026, 01:23:20 »
  • 0
  • 0
Это же JavaScript, открываете консоль браузера и проверяете.
Jestem dokładny i dociekliwy. (Wg Pinii.)
Всё, что нужно для торжества зла — это бездействие добрых людей. Поэтому бездействовать не надо. Алексей Навальный
Ceterum censeo gebniam esse delendam.
…Когда ты чем-то не интересуешься, то после шокового события ты готов принять любую удобную интерпретацию, которую тебе предложат. Григорий Юдин

Offline Bhudh

  • Дважды герой
  • **
  • Posts: 8413
  • Карма: +2794/-606
    • View Profile
Re: Транслитератор украинского
« Reply #4 on: 01 January 2026, 01:26:07 »
  • 1
  • 0
Оригинал:
Сучасні USB на ноутбуках стали більш енергоощадливі, через що бувають казуси, як-от проблема зі зовнішнім CD-приводом у мене. Вирішення дійсно одне: додаткове живлення, якщо конструкція передбачує.

Результат:
Quote from:
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.
Jestem dokładny i dociekliwy. (Wg Pinii.)
Всё, что нужно для торжества зла — это бездействие добрых людей. Поэтому бездействовать не надо. Алексей Навальный
Ceterum censeo gebniam esse delendam.
…Когда ты чем-то не интересуешься, то после шокового события ты готов принять любую удобную интерпретацию, которую тебе предложат. Григорий Юдин

Offline cetsalcoatle

  • Эльрат – дракон света
  • Глобальный модератор
  • Дважды герой
  • *
  • Posts: 9303
  • Карма: +1048/-373
  • Благородный муж прям и твёрд, но не упрям.
    • View Profile
Re: Транслитератор украинского
« Reply #5 on: 01 January 2026, 02:27:52 »
  • 0
  • 0
Оригинал:

Результат:
Спасибо! :up:
Чёт он косячит, должно было быть jak-ot и jakśo.
И в потоке í и i сливаются, возможно стоит "ь" обставить без изменений, но в целом мне нравится.
« Last Edit: 01 January 2026, 02:29:38 by cetsalcoatle »
WHITE LIVES MATTER.

Всё, что нужно для счастья, — это тело, не страдающее от боли, и душа, свободная от тревог. (с) Эпикур

Останься прост, беседуя с царями,
Останься честен, говоря с толпой;
Будь прям и тверд с врагами и друзьями,
Пусть все, в свой час, считаются с тобой;

Simba mwenda pole ndiye mla nyama

Offline Квас

  • Ветеран
  • *****
  • Posts: 1535
  • Карма: +476/-18
    • View Profile
Re: Транслитератор украинского
« Reply #6 on: 01 January 2026, 12:58:57 »
  • 0
  • 0
(и на мой непрофессиональный взгляд довольно классно)
Я бы сказал - совсем не классно, по-джуниорски. :) Много бизнес-логики засунуто в самую низкоуровневую часть реализации (цепочка else if-ов внутри цикла с какими-то мутабельными переменными); много копипасты. Поэтому трудно понять, что делает алгоритм, и проблематично его поддерживать.

Технический момент: создавать строку с помощью += есть антипаттерн. Каждый промежуточный результат уходит в мусор, что означает квадратичный мусор. Когда строку создают из большого числа частей, их собирают в массив и применяют join.

Offline cetsalcoatle

  • Эльрат – дракон света
  • Глобальный модератор
  • Дважды герой
  • *
  • Posts: 9303
  • Карма: +1048/-373
  • Благородный муж прям и твёрд, но не упрям.
    • View Profile
Re: Транслитератор украинского
« Reply #7 on: 02 January 2026, 18:45:05 »
  • 0
  • 0
Я бы сказал - совсем не классно, по-джуниорски. :) Много бизнес-логики засунуто в самую низкоуровневую часть реализации (цепочка else if-ов внутри цикла с какими-то мутабельными переменными); много копипасты. Поэтому трудно понять, что делает алгоритм, и проблематично его поддерживать.

Технический момент: создавать строку с помощью += есть антипаттерн. Каждый промежуточный результат уходит в мусор, что означает квадратичный мусор. Когда строку создают из большого числа частей, их собирают в массив и применяют join.
Можете, пожалуйста, дать пример фрагмента кода, чтобы оно было не по-джуниорски? :)

Учиться желание бешеное имею. :yes:
WHITE LIVES MATTER.

Всё, что нужно для счастья, — это тело, не страдающее от боли, и душа, свободная от тревог. (с) Эпикур

Останься прост, беседуя с царями,
Останься честен, говоря с толпой;
Будь прям и тверд с врагами и друзьями,
Пусть все, в свой час, считаются с тобой;

Simba mwenda pole ndiye mla nyama

Offline Квас

  • Ветеран
  • *****
  • Posts: 1535
  • Карма: +476/-18
    • View Profile
Re: Транслитератор украинского
« Reply #8 on: 02 January 2026, 20:47:18 »
  • 0
  • 0
С йотированными вроде понятно, что должно быть, а какие правила для "йо" и "ьо"?

Offline Квас

  • Ветеран
  • *****
  • Posts: 1535
  • Карма: +476/-18
    • View Profile
Re: Транслитератор украинского
« Reply #9 on: 02 January 2026, 23:55:40 »
  • 0
  • 0
Насчёт чего у меня зачесались руки - что нужно разделить формулировку правил и транслитерацию текста. Потому что тут как бы два «специалиста» работают. «Филологу» интересно разрабатывать набор правил, а как они применяются к строке - неважно, технический вопрос. «Программисту», наоборот, конкретные правила не очень интересны. Loose coupling означает, что «программист» и «филолог» стараются минимально вмешиваться в работу друг друга. Они договорились, что набор правил будет объектом, определяющим правила преобразования конкретных литер. Ради гибкости такое правило может формулироваться в виде функции, преобразующей контекст. Использование функций в качестве входных данных обычно выводит гибкость на новый уровень.

Каждое нетривиальное правило реализовано в виде функции. Это всегда полезно, когда сущности предметной области соответствует сущность программы. Например, если есть проблема с транслитерацией буквы «о», мы сразу понимаем, что нужно дебажить, и не боимся, что фикс конкретного правила повлечёт неожиданные регресии. В качестве бонуса - можно одновременно экспериментировать с разными транслитами.

Такой подход, когда логика формулируется в виде некоего объекта, скармливаемого «движку», иногда называется data-driven programming.

Приведённая реализация не очень удобна для ди- и более графов. Для украинского это не очень важно, потому что нам нужно обработать только один диграф - ьо. Если её предполагается использовать для ситуаций, когда диграфов много, можно допилить «движок»: например, так, чтобы ключами объекта-преобразования допускались бы не только однолитерные строки.
Code: [Select]
/*
 * В задаче есть две подзадачи.
 * 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č"
« Last Edit: Today at 00:00:13 by Квас »

Offline Bhudh

  • Дважды герой
  • **
  • Posts: 8413
  • Карма: +2794/-606
    • View Profile
Re: Транслитератор украинского
« Reply #10 on: Today at 00:01:07 »
  • 0
  • 0
Там, кстати, реальный косяк в этом фрагменте, в комментарии написано «уже обработали й / ь» до проверки на «ь» в следующем else if.
Jestem dokładny i dociekliwy. (Wg Pinii.)
Всё, что нужно для торжества зла — это бездействие добрых людей. Поэтому бездействовать не надо. Алексей Навальный
Ceterum censeo gebniam esse delendam.
…Когда ты чем-то не интересуешься, то после шокового события ты готов принять любую удобную интерпретацию, которую тебе предложат. Григорий Юдин