Показаны сообщения с ярлыком перевод. Показать все сообщения
Показаны сообщения с ярлыком перевод. Показать все сообщения

вторник, 21 октября 2008 г.

И снова баги...

Вчера убил больше двух часов на поиск ошибки. Причем шибка возникла на ровном месте там, где ее в принципе и не могло бы быть...

А дело было так.

Правлю я там там что-то в переводчике связанное с интерфейсом взаимодействия между GUI и плагинами и в процессе тестирования вижу странное.

Берем предложение и переводим, получаем два варианта перевода. Все как ожидалось:


А теперь берем и делаем второй вариант перевода предложения основным (т.е. первым в списке переводов):


Упс! А текст-то не тот!

Надо заметить, что алгоритм сортировки вариантов перевода предложений в соответствии с предпочтениями пользователя крайне примитивен - все выбранные предложения хранятся в специальном списке. При переводе текста, по окончании процесса перевода полученные в итоге варианты сортируются. Вообщем, ну никак сортировка не может повлиять на перевод - она осуществляется в конце!

Два часа я искал проблему. Я выделил и сравнил логи перевода до и после сортировки - они совпали. Я пересмотрел алгоритм перевода (вызова модулей). Я вспомнил как работает алгоритм склонения слов и пролазил его весь под отладчиком. Я даже переписал алгоритм сортировки! Я сделал еще целую кучу действий - и ничего. До некоторого момента всё нормально, а потом - упс! - и всё плохо.

И совершенно случайно я все-таки нашел в чем была проблема... Вы не поверите.

Итак, где-то в классе TextTranslator (отвечающем за перевод текста) есть такой код:

            for (k = 0; k < ersent.rus.Count;k++ )
                RunProccessRVariantQueue(ersent.rus, par, sent, k);

... который в итоге вызывает такую функцию:

        static void RunProccessRVariantQueue(List<trstr> snt, int par, int sent, int variant)
        {
            for (int i = 0; i < ModulesFactory.Instance.rvar_proccess_queue.Count; i++)
            {
                ((IRStrVariantProcessor) ModulesFactory.Instance.rvar_proccess_queue[i].obj).ProcessRStringVariant(snt, variant, par, sent);
            }
        }

Модель сортировки же был сделан так же как и, например, модуль склонения слов, т.е. вызывался для каждого варианта перевода предложения. Сделано это было дабы не плодить лишних сущностей в виде специального интерфейса для модулей, которые должны работать с несколькими вариантами перевода предложения, а не только с одним (кстати, скорее всего зря - такие модули возможно были бы полезны, правда, пока что я в этом не уверен - нет соответствующих алгоритмов).

Ну и вот, чтобы не допустить ошибок при сортировке и лишних потерь в производительности, функция ProcessRStringVariant модуля сортировки начинается со строчки:


            if(variant != 0) return;

... что по идее должно было заставлять выполнять сортировку один раз. И оно даже работало. Но с одним интересным побочным эффектом.

Не вдаваясь в детали, скажу, что цепочка вызовов модулей обрабатывающих эти самые варианты перевода предложения состоит из двух элементов: модуля склоения и модуля сортировки. Что же происходило и почему в итоге возникала ошибка? Рассмотрим пошагово работу алгоритма:

Итерация №1
Вызов модуля склонения для варианта №0
Вызов сортировкщика. В сортировщике стоит указние, что надо вариант с нормером 1 поставить в начало списка (т.е. сделать его номером 0).

Итерация №2
Вызов модуля склонения для варианта №1
Сортировщик ничего не делает, т.к. вариант !=0

Так вот, на итерации №1 мы имеем что? Правильно! Тот же самый вариант, что и на итерации №0, т.к. сортировщик свое дело сделал :) В результате, второй вариант перевода не склонялся.

Решением проблемы оказалась замена приведенного выше условия на следующее:

            if(variant!=Count-1) return;

Вот так вот... Два часа на поиск одной единственной строчки...

пятница, 10 октября 2008 г.

Свершилось!

Мой переводчик опубликован на Codeplex'e. И даже с исходными кодами :) Правда не всё - отдельные части прогрыммы я пока что попридержу... Вообщем, кому интересно - качайте и мучайте :) В исходниках много-много комментариев, думаю часть вопросов снимет... Ну и конечно - пишите, помогу чем получится...

среда, 1 октября 2008 г.

Мой переводчик. Обновление

Выложил текущую версию переводчика (от 01.10.2008). Куча мелких изменений :)

Все ближе к релизу...

воскресенье, 28 сентября 2008 г.

Про мой переводчик. Структура.

Продолжение. Начало смотрим тут. 

Итак, как я уже писал ранее - переводчик является модульным, причем модули должны быть независимы друг от друга. Не то чтобы это было обязательным требованием, однако, чем более связаны между собой модули - тем сложнее их менять. Соответственно, данное требование накладывает определенные ограничения на сам процесс перевода. На данный момент он является линейным. Примерная блок-схема такова:

... правда семантического анализа у меня еще нет.

Такой подход не является обязательным - никто не мешает переделать сам алгоритм перевода. Однако, на данный момент необходимости в этом я не вижу.

В процессе разработки был придуман следующий подход к процессу перевода предложений:

  • на первоначальном этапе ко всем словам предложения добавляется вся известная о них информация (части речи, параметры, варианты перевода и т.п.);
  • основная задача всех последующих алгоритмов - удалить у слов лишнюю информацию. Чем больше удалим - тем более правильный будет результат.

Как видно - получившийся алгоритм оказался жизнеспособным.

Вот так вот мутировал изначальный линейный алгоритм в процессе развития:

... синие блоки примерно соответствуют блокам на первом рисунке.

Ну и напоследок - степень завершенности ключевых модулей анализа:

Продолжение следует...

вторник, 16 сентября 2008 г.

Про мой переводчик. История.

Вместо предисловия



Давным-давно, аж в далеком 2002 году я совершенно случайно наткнулся на книжки замечательной писательницы Лорел К. Гамильтон. На тот момент на русский язык были переведены только первые 6 книг из серии про Аниту Блейк.

В то время я уже отказался от чтения современной американской фантастики и фэнтези ввиду их примитивности (в сравнении с отчественными). Поэтому вдвойне удивительно то, что эти книжки так меня увлекли, что я полез в сеть в поисках продолжения :) Которое и нашел на фанатском сайте.

Но радость была недолгой - пятая книга, переведенная силами фанатов обрывалась (по закону подлости) на самом интересном месте.

К счастью - в наличии был английский текст книжки и даже еще две книги-продолжения :)

Идея читать в оригинале была отброшена сразу как несостоятельная - мое тогдашнее знание языка позволяло уверенно читать без словаря документацию на сайте MSDN и всё. Точнее, художественный текст я бы осилил, но скорость чтения была бы катастрофически медленной, что меня, естественно, не устраивало.

И я совершил страшное. Я запустил ПРОМТ и перевел книжку...

Не буду говорить что из этого получилось, но результат можно было читать. Главное - не заострять внимание на том, как написано, а пытаться понять что написано :) При некотором опыте читать - можно.

При чтении я как-то обратил внимание, что, в принципе, основные ошибки перевода ПРОМТа - это ошибки склонения слов (естественно, в тех случаях, когда предложение было разобрано верно).

Вот тогда-то и появилась идея (нет-нет, не переводчика!) программы-корректора, которая бы подправляла текст, сгенерированный ПРОМТом.

Примечание: в то далекое время я еще ничего не знал про Microsoft .Net Framework. Да и был он в стадии "почти релиза" и особо нигде не упоминался. Так что писал на том, что знал - C++ Builder 6.0.



Этап №1



Была написана маленькая программа, со своим словарем слов, которая заменяла отдельные слова на альтернативные. Изначально хотелось увидев "неправильное" слово или словосочетание заменить их на "правильное".

Я думаю, не стоит говорить о том, что в конечном итоге то, что получилось оказалось на 100% привязанным к тестовому тексту и с чем-либо другим корректно работать не могло.

Стало понятно, что необходим собственный алгоритм построения склонения/спряжения слов. Алгоритм был написан и успешно заработал. Причем он оказался настолько успешным, что без особых изменений переползал из версии в версию и существует и используется и поныне :)

Однако, алгоритм склонения не сильно помог - стало понятно, что простое склонение пар слов относительно друг-друга нужного эффекта не даёт - необходимо, как минимум, знать связи между словами.



Этап №2



Вот тогда-то и появилась амбициозная идея написать "свой" переводчик, в котором можно было бы контролировать процесс перевода.

Именно тогда родился тот подход к синтаксическому анализу, который я использую и сейчас. Как раз тогда я впервые прочитал про регулярные выражения (regexp) и подумалось: "да это же самый простой способ определить вид предложения!".

Сам алгоритм анализа основанный на регулярных выражениях был написан всего за 4 часа. А вот уже загрузчик правил писался долго и муторно. Достаточно сказать, что пока я не начал использовать формат xml для хранения этих правил анализа алгоритм чтения файла с правилами продолжал глючить и падать от малейшего чиха. И это при том, что он постоянно правился в течение нескольких месяцев! Именно после этого я так невзлюбил Си++...

Кстати, та самая реализация алгоритма анализа до сих пор вполне успешно используется :) Ну понятно, что она нексколько мутировала, но все-таки...



Этап №3



Где-то в районе 2004-го года мне попадается интересная статья про Mono. И как-то руки зачесались попробовать :) А там как раз стало понятно, что Mono и .Net Framework - братья. А там и SharpDevelop попался... Вообщем, был написан тестовый редактор правил - благо правила уже давно хранились в формате xml, поэтому, фактически, писалась оболочка.

И как-то оно всё так хорошо пошло, что буквально за неделю весь проект переполз на C#. Я сам не ожидал, честно :) Ну не без глюков, конечно... Но в целом - изменения в коде были минимальные и в основном касались работы со строками.

Примечание: Самая старая версия переводчика, которую я смог раскопать в архивах датируется 19.09.2004. Однако, это уже практически окончательный вариант третьего поколения алгоритма перевода (а может и большей версии - сейчас сложно сказать).



Этапы №4 и далее



Становится понятно, что то, что получилось, в принципе, даже работает. И где-то даже правильно. И вообще...

Система довольно длительное время развивается пока не упирается в принципиальный тупик: разбора только одного варианта предложения недостаточно. Вероятность ошибки неверного разбора предложения прямо зависела от того, вариант какой части речи стоял у слова в словаре первым.

Поэтому был добавлен маленький кусочек кода из-за которого пришлось переписывать половину программы :) А занимался он генерацией вариантов предложений. Основная идея - в предложении каждое слово имеет варианты перевода только одной части речи.

Примерно тогда же становится понятно, что при таком подходе легко можно получить агромаднейшее количество вариантов предложения. Главное в него подсунуть правильные слова с несколькими переводами разных частей речи (и побольше, побольше!).

Не знаю уж, что мне подсказало пойти таким путем, но я использовал все тот же алгоритм синтаксического анализа - для предварительной обработки предложения до этапа разделения его на варианты. В этом случае использовался специальный набор правил, выполнение которых дает 100% правильный результат и позволяет сократить количество вариантов перевода отдельныъ слов.

Примечание: Например, если после артикля the стоит слово - это слово 100% не глагол. И т.д и т.п.

В итоге, на самом большом моем тестовом тексте я до сих пор получаю не более 17 вариантов предложения. Что, конечно много, но, во-первых, правил не так уж и много, а во-вторых, при существующей производительности железа - это крайне мало.



Этап №14



Худо-бедно система развивалась, пока я не уперся в потолок - каждое последующее изменение требовало все больше и больше усилий. Я думаю, многим это знакомо.

Это было страшное время :( Когда понятно, что надо сделать, но непонятно как это сделать так, чтобы ничего не сломалось...

Поэтому в начале 2006-го года было принятно решение все переписать :)

Программа была переписана полностью с нуля. Т.е. вообще всё с нуля. Естественно, часть старого кода переползла в новую версию, но все пережитки вызванные предыдущим бурным развитием безжалостно уничтожались...

Какое-то время удавалось поддерживать новую версию на плаву... Пока я не уперся в тот же самый потолок.



Этап №15



Собственно, деваться было некуда и пришлось полностью менять подход к созданию крупных программ. Благо уже "созрел".

К лету 2006-го стало понятно, что надо делать модульную систему. К такому решению подталкивало два фактора:

  • во-первых, по своей структуре она уже была модульной. Осталось только реализовать динамическую загрузку и переделать основную часть программы (ядро) под более абстрактный код.
  • во-вторых, пришло понимание, что написать переводчик полностью силами одного человека - задача из области фантастики. Использование же модулей позволяет распределить отдельные части задачи между разными разработчиками.


Практически год потребовался на полную переделку программы. Конечно, основной причиной столь длительного срока было угасание интереса - в основном из-за понимания насколько много придется переписывать заново. И желания это делать как-то не наблюдалось :)

В конце-концов, процесс подошел к своей конечной стадии. По пути программа лишилась удобного интерфейса выбора варианта перевода предложения и алгоритма поиска фраз (который был хоть и кривой и недоделанный, но все равно - жалко). Нет-нет! Они не совсем умерли. Просто пока что руки не доходят реализовать их по-новому - надо сначала доделать то, что важнее.

Зато появилось много чего нового и интересного :)

В конечном итоге - оно того стоило.



Продолжение следует...

воскресенье, 20 июля 2008 г.

Обновление переводчика от 20.07.2008

Взять можнно тут. Переделал бОльшую часть диалогов в редакторах, поправил несколько ошибок, получше распределил формы/контролы/классы между сборками. Вообщем-то ничего особо существенного для целей перевода - но выглядит теперь симпатичнее :)

воскресенье, 13 июля 2008 г.

Обновление переводчика от 13.07.2008

Итак, последнюю на данный момент версию можно взять тут.

  • Убрал использование сторонних алгоритмов анализа.
  • Убрал редактор скриптов на C# и модуль анализа - теоретически использовать можно, практически же то, что было, намного удобнее.
  • Дописан модуль добавления в словарь переводов слов напрямую из редактора-переводчика.
  • Подчищен код во всех файлах проекта. Исправлены мелкие недочеты.
  • Добавлены интерфейсы IOpenFormatConverter, ISaveFormatConverter через которые реализуется процесс чтения/сохранения данных в разных форматах.
Пожалуй скоро уже можно будет и код с документацией выкладывать... Вот доделаю еще немного...

вторник, 8 июля 2008 г.

Переводчик

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

Дорабатывать сам алгоритм перевода пока не планирую. Во-первых, основная часть - загрузка и выполнение модулей-обработчиков текста и некоторые вспомогательные модули - более-менее отлажена. Да, работает не так чтобы совсем замечательно. Но какой-то результат, отличный от неправильного все-таки получается при переводе. Учитывая же основную идею - построение полностью модульной системы, в которой алгоритм могут свободно расшираять и модифицировать любые разработчики - это, в принципе, может подождать.

Вообще, хотелось бы наконец довести до ума "внешнюю" часть переводчика. Потому что смотреть на тот код, что используется в пользовательском интерфейсе (особенно в самом редакторе-переводчике) просто страшно. Кажется, что проще написать с нуля, чем понять как ЭТО вообще может работать :) Болезнь роста...

Больше всего меня удручает то, что я так и не придумал более-менее нормального варианта взаимодействия модулей с пользовательским интерфейсом. Нужно, чтобы модули могли добавлять свои пункты главного и контекстного меню, кнопки на панели и свои панели. Но вот как правильно это надо делать я так и не нашел. Если кто поможет советом - буду сильно благодарен.

Немного картинок: