понедельник, 23 июня 2008 г.

Как я локализовался


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


«Help translating and proof-reading Aml Pages and get a free registration»,


причем вывешена далеко не в самом заметном месте. А как же? Святое дело! Удочку закинем, а когда уж «клевать» будет - разберемся что делать, кто виноват, и куда деваться. Но, тем не менее, через некоторое время ко мне обратился Patricio Silva - пользователь из Южной Америки, а точнее из Чили - и предложил сделать за бесплатную регистрацию испанский перевод пользовательского интерфейса. В «разговорах в курилке» этот приятель как-то быстро стал за глаза зваться просто «испанцем», ну и поэтому и здесь он фигурирует под этой партийной кличкой.



Ну и что? Да понятно что! Кому же не нужна локализация? Локализация интерфейса дело важное и нужное. Больше языков - хороших и разных. Тем более локализация на такой язык как испанский - все-таки на нем или на его производных половина бывших колоний лопочет - это такой локоток, который укусить хочется. Чего там укусить, слопать целиком, и на серебряном блюдечке. Конечно же, я согласился.



Microsoft более-менее официальным тоном во многих статьях предлагает следующую схему локализации: перевод rc-файла (RC, Resource Script - файл с описанием пользовательского интерфейса). Ну и гут: предлагают - бери, бьют - беги! Тем паче, что моя софтинка была написана на MFC, а эта библиотека предлагает к этому способу всякие другие занимательные рюшечки, глядя на которые в мечтах рисуется и небо в алмазах, и «малиновые штаны», и «банкомат на пляже».
Да и вообще все представляется в стиле Microsoft "Вы будете управлять [локализацией] на кончиках пальцев!!!"... Ну, ясное дело, раз предлагается такой рулез, на этот способ я и заложился. Это уж потом стало понятно, что «да-а-а-а-а-а-а уж» (© Киса Воробьянинов)! Точно! На кончиках! И именно пальцев! ...Только видимо пальцев ног, и некоторые мелочи в этом заявлении проскользнули между пальцами (судя по всем, между большими)...



Собственно подробности предлагаемых наворотов расписаны в статье "Динамическое переключение языка интерфейса в MFC-приложениях". Там же есть и эпохальная фраза о необходимости постоянного слежения за тем, что версия перевода и основного файла всегда должны быть в соответствии.


Фраза то там есть. Да только не описано в деталях, что будет когда версии станут хоть малость, но разными (а они станут, и очень быстро станут). А дьявол, как известно, он в деталях.


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



Танцы с бубном начались с того, как переведенный вариант «вливается» в саму программу. Пользовательский интерфейс может быть неотъемлемой частью exe-шника, или сопутствующей DLL или еще как-то. Я, понятное дело повелся на официальный способ, и возрадовался возможности иметь основной exe-файл с английским языком пользовательского интерфейса (UI), и дополнительную DLL с испанским переводом. Модульность, модульность и еще раз модульность. Очень заманчивой казалась возможность спокойно развивать сам код exe-шника с английской версией UI, и просто цеплять к нему испанский интерфейс в виде DLL. В общем, ночь-заполночь, выслал я испанцу английский rc-файл с инструкциями, что и как переводить.



Время идет, английская и русская версия как-то там потихоньку развиваются в пределах мелких багов, испанец периодически о чем-то спрашивает меня по мылу. В общем, чувствуется, что перевод движется, «процесс идет» и в скорости надо ожидать результат. В общем, солдат спит - служба идет. Довольно потираем ручки. В общем, в предвкушении я, в ожидании... Еще бы! Закажи я этот перевод за денюжку - влетело бы в сотни бакинских. А тут за какую-то регистрацию - фиг с ними с десятками зеленых - все в выигрыше: и я, и он. Уж не говорю о том, что переводит Native с нормальным родным!!! испанским языком, а не из Пердянского филиала педагогического факультета Мухосранского табаретостроительного института. Поэтому и качество перевода, вероятно, будет более-менее на уровне.



Спустя несколько недель испанец присылает мне rc-файл, уже переведенный на испанский язык. Ну, мол думаю, гут! Сейчас заделаем испанскую версию! И вот тут-то и начинается самое интересное.



Испанец попался усидчивый и настойчивый - он перевел всё. Нет, нет! Вы меня не поняли - он ва-а-а-бще всё перевел. Абсолютно. Поэтому если в rc-файле встречалась славная конструкция вроде:



"Button",11, 17, 22, 30 "My Bytton Caption",


то испанец и ее добросовестно и переводил

"Кнопка",11, 17, 22, 30 "Надпись моей кнопки",


(ну, соответственно, на испанский язык).



Кто видел RC-файл - тот понял. Но все ж поясню, в данном месте первое слово "Button" это название класса окна, которая «говорит» RC-компилятору, что данный контрол это кнопка (цифры это координаты кнопки диалоге). И таких «программистких» конструкций в rc-файле тьма тьмущая. Ага! И все переведены! Ух, как я зауважал Microsoft за то, что большая часть классов окон у них имеет малоудобоваримые названия для обычного человеческого глаза, вроде: control, groupbox, ltext. По ним то видно, что это какой-то код. А то бы испанец и их перевел - поверьте, он хоть и не IT-специалист, но нормальный человек - эти милые моему сердцу «коды» все же уцелели.



Но с другой стороны испанцу мне и сказать-то нечего! Я ж сам ему выдал инструкции: переводи всё, что в кавычках. Он и перевел. Ну, что поделать? В общем, я сам виноват. И послать нельзя: испанец не виноват, что он не читает код rc-файла глазами, компилируя в уме. Опять же столько времени потрачено - бросать не хочется. Да и выпустить перевод хочется:


а) испанский все же! Действительно распространенный язык.


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



Ну, ясное дело, править искореженный до полусмерти rc-файл пришлось мне самому. Т.к. ну не объясню я «дикому» испанцу, где Button надо переводить - ибо эта часть строки, а где это класс окна и нужно оставить как есть. Ох, и приятное же это занятие, хочу Вам доложить. Незабвенным "Replace All" этой проблемы не решить по той же причине - кое-где слово Button употребляется в составе какой-то строки, а где-то его все-таки переводить нужно. Поэтому покликать на «Найти далее» пришлось изрядно. Какие-нибудь регулярные выражения не предлагать. Ну, не силен я в них. Да и решать проблему надо так, чтобы ее не было. А не придумывать автоматизацию багфикса для такой засады.



Опять же есть и были другие нюансы. Как известно, любой парсер впадает в основательный ступор, если не закрыта какая-то парная конструкция формального языка вроде скобок (),{}, или «кавычек» (кто на Си писал - наверняка видел как компилятор клинит). Компилятор rc-файлов не исключение. Поэтому его крепко озадачивают те же самые незакрытые кавычки. А надо сказать, что в rc-файле их хватает: по пяток штук на строку - да легко!!! Мало того, что ими ограничиваются строки, но если в составе какой-то строки есть еще кавычки (уже по смыслу), то они отмечаются двойными кавычками.



Пример: «Пожалуйста, нажмите кнопку ««Закрыть»», чтобы больше не видеть этот rc-файл».


Вот в этом примере кавычки в начале и в конце строки это ограничители самой строки в RC-файле. А двойные кавычки внутри фразы на слове ««Закрыть»» - сие есть хитрая конструкция внутри rc-файла, которая говорит rc-компилятору, что эти кавычки реально по сути есть в самой строке.



Как вы думаете, испанец работал в рекурсивном режиме - и в уме контролировал число открытых и закрытых кавычек?

Правильно! Ага, щаззз!


Поэтому мой бедный rc-компилятор мучился не по-детски чтобы диагностировать: где же ошибка. В начале файла открыли - через пару-тройку десятков строк, уже в соседнем описании диалога «вумный» компилятор нашел закрытую :). Про всякие MENUITEM, переведенные на МенюЭлемент, уж помалкиваю - это семечки. Такое правится ручками без «отрыва от пива с друзьями» - просто такие ляпы и видны хорошо, да и "Replace All" с ними вполне справляется.



А надо сказать, интерфейс у меня был немаленький, как впрочем, и сам продукт. Я уже упоминал выше, что испанец трудился несколько недель. Причем явно «пиво не пил», а действительно занимался делом, т.к. письма с вопросами от него приходили через день точно, а иногда и почаще. Тут еще примечателен такой момент, что испанец то Чилийский. Это вообще-то соседнее полушарие, другая сторона Земли. По прямой - через внутреннее ядро Земли - ей богу короче, чем по экватору (правда, все же, на экваторе задницу не так напечет как внутри). Это значит, что когда у нас день - у них ночь. И наоборот. У нас разница так примерно с часов восемь. Самое продуктивное по времени было отправлять ему ответы в наш вечер - тогда у него было более менее утро, и он как раз успевал что-то по делу ответить часиков до 2-3 ночи по Москве. Так что все прелести неслабо распределенной в пространстве команды тоже себя проявили по полной. Он мне вопрос: «переводить ли "Button" вот тут вот». Я часов через 7 проснусь - ему отвечу, он через часов 7 прочтет. Отлично! Еще день прошел, мы еще сдвинулись на одно предложение. И это когда вопрос простой. А когда что-то существенное, по делу, когда мне нужно самому погуглить, поискать решение, подумать - то какой уж тут ответ в три минуты!?! Вот такие вот темпы работы.


Но ё-моё... Вы представляете объем этой моей правки rc-файла? Я потратил полностью два вечера (часов по 4-5) на приведение rc-файла в порядок, и что-то где-то урывал по мелочам - то с утра минут 20 поаппрувишь, то там 10 минут, то тут 15. Очень, скажу вам, это тонизирующее занятие - после него перестаешь ругать все проблемы баз данных, компиляторов языков, недокументированности API и бежишь к родной среде разработке как к любимой женщине - разве ж это проблемы??? А испанец тоже потрудился дай боже, так что, ну просто грех было не дать ему регистрацию. Даже если перевод (не по сути качества перевода - оно было более чем достойное) - никуда не годился. Итак, регистрацию я ему дал.


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


Спустя некоторое время я как-то разгреб rc-файл, откомпилился, и выслал ему испанскую версию...


Думаете, This is the End? Конец?


Щаззз!


Лента Мёбиуса, знаете что такое? :)



Ан нет! Начались проблемы с диалектами испанского - т.к. их в Windows тоже далеко не один вариант: кастильский, гвинейский, аргентинский и.т.д. Можете сами посмотреть - правой кнопкой по языковой панели Windows, в меню «Параметры», «Добавить язык» - смотрите, любуйтесь. И чтобы все его евойные тильды, умляуты и прочие диакритические штришки отображались нормально, нужно в rc-файле еще выставить правильный вариант испанского, да и всякие мелочи прописать тоже корректно, и тоже ручками.


Тут, я уже наученный опытом на свою попу, особо его не инструктировал - просто сгенерил с десяток тупых тупых MFC-ишных примеров, да выставил им испанский язык. Потом накомпилил соответствующее число exe-шников под каждый вариант испанского и радостно, с гиками и плясками зарядил их ему в почту. Причем по одному exe-шнику на одно письмо. Ну, во-первых письмо с одним маленьким атачментом, значительно вероятнее дойдет до адресата. Это как-то интереснее, чем гадать: чего там в Чили с файерволами, ограничениями объема вложений, а может у них ночь и он просто спит? К чему мучаться сомнениями!?!


Во вторых, приятно было, чтобы он прочувствовал всю творческую наполненность таких проверок - «держи исчо бинарник, и этот тоже запусти (да не попутай с другой версией), да посмотри все ли хорошо аль плохо». Вай, плохо? Нибеда! Лови следующий



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



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


Ну чего?


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



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



И... В общем, если кратко это называется «Здравствуй, Ёптить!». Новых команд меню у него в испанской версии нет. В английской версии то они есть, а в испанской нету.


Все очень просто: когда он переключается на английский язык интерфейса, содержащийся в самом exe-шнике программы - он видит полностью английский интерфейс, и видит новые команды меню с английскими же названиями. Когда же он указывает моей софтинке, использовать DLL с испанским интерфейсом, перезапускает софтинку, подгружается испанский интерфейс. ОК! Все на испанском, как и должно быть, но только новых команд меню нету - ни на английском, ни на испанском. Т.е. интерфейс в испанском варианте никакой новизны не содержит.



Вариантов у него собственно два: или работать с испанским интерфейсом, не имея доступа к новым командам меню, т.к. их просто нет в испанском варианте. Или, если так уж они ему нужны, работать с полностью английским интерфейсом. Третьего не дано.


Я говорю, о нормальном среднем пользователе - про всякие там «хаки» с WM_COMMAND напрямую и речи быть не может - да и все равно куда это годится!?!. Представить же себе пользователя, до посинения переключающегося между английским и испанским интерфейсом да еще и с перезапуском программы, лично я себе в здравом уме не могу. Поэтому все складывается просто шикарно: если перевод версии и есть, то только для конкретной версии и не старше. Ценность такой локализации весьма ограничена - или конечный пользователь имеет старенькую версию с нужным языком, или уж новую, но только не с родным интерфейсом.



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



Попробую пояснить, почему я так думаю. Дело в том, что RC-файл содержит далеко не только какие-то там названия меню, кнопок, строки и прочие «буковки» - всё то, что мы именно прочитываем в пользовательском интерфейсе. RC-файл также описывает и структуру интерфейса: где какое меню, где какая кнопка находится, какого она размера, какая графика где расположена и.т.д. Попросту говоря, в нем описывается еще и структура пользовательского интерфейса. Оно и понятно, большая корпорация а-ля Microsoft не выпускает версии продукта два раза в месяц, поэтому приемлемо возможно полностью локализовать интерфейс для каждой отдельной новой версии. Опять же локализуется в таких приложениях не только текст (в основном), но и многие другие ресурсы - к примеру, те же изображения могут меняться в локализованной версии на адаптированный для целевой культуры вариант, форматы дат, времени и.т.п.



В простой же софтинке на 99 процентов переводится в основном только текст интерфейса: названия, надписи, и.т.п. Да даже если софтинка не проста - то все равно вряд ли Вы сможете содержать такой же штат переводчиков, да еще и знакомых со структурой RC-файлов.


Разве что однажды пришлось учесть случай вроде кнопки панели инструментов «полужирный шрифт». В английском варианте такая кнопка должна иметь рисунок с жирной буквой «B», а в русской с буквой «Ж» - посмотрите на панель инструментов того же Word...


А больше особо, в общем случае, как правило и не нужно ничего переводить.


Ребяты-ы-ы-ы! Всё-ё-ё-ё-ё!



Я вряд ли буду городить еще один перевод интерфейса на французский или немецкий язык только из-за этого рисунка кнопки. Нет, нет! Я не спорю - оно конечно неплохо, когда и для французов с немцами есть свой вариант кнопки «Жирный/Bold». Но выпуск локализованной версии дело ответственное и важное. Я попросту не могу потратить еще несколько месяцев на подготовку локализованной версии только из-за одной буквы. В любом случае есть такая же команда меню с нормально переведенным названием, и таким же нормальным переводом всплывающей подсказки к панели инструментов. Но такая кнопка всего лишь «штрих», «мелочь». Не спорю, мелочи важны! Но все равно, если этот штрих удался - прекрасно! Но если нет, это не позволяет задерживать выпуск локализованной версии на несколько месяцев. Это просто не то что невыгодно, а из-за отсутствия еще несколько месяцев еще одной локализованной версии - это просто в убыток. К тому же, когда этот идеальный перевод будет готов, основные версии тоже не будут «курить в сторонке», и обрастут новыми возможностями. И что делать? Опять перевод должен догонять? Или просто выпустить локализованную, но старую версию? Что лучше? Я не знаю - все зависит от специфики проекта.



Второй момент, что в основном нужен перевод именно «строчной» части интерфейса, и ни в коем случае не изменяя (нет, ну скажем, почти не изменяя) саму структуру, строение пользовательского интерфейса. Поэтому крайне не хочется отдавать переводчикам rc-файл полностью. Они в нем напортачат куда больше, чем переведут! Уж не говоря о том, что строки вперемешку с «дремучим кодом» и переводить им куда сложнее. Перефразируя известную поговорку: «Хороший «rc-программист» редкость! Хороший переводчик еще большая редкость!» А тут все вместе и в одном флаконе, да?



Третий момент: для динамично разрабатываемого приложения - когда новые версии появляются куда чаще, чем локализованные - есть непреодолимое желание погонять «переведенный интерфейс» в отладчике. Посмотреть что да как. А не наблюдать очередной crach после подключения к releas-ной версии очередной языковой DLL-ки (с последующей затем традиционной медитацией в дизассемблере).



Вот, собственно говоря, как я поимел первый опыт работы с весьма и очень весьма мне нужной локализацией моего же продукта. Подводные камни и недостатки схемы локализации rc-файла я постарался описать. Так чего бы хотелось бы?


  1. Возможность отделить непосредственно локализуемую часть: строки, названия меню, кнопок и.т.п. от структуры, строения самого пользовательского интерфейса. Причем выделить ее так, чтобы в процессе перевода на иностранный язык переводчику она не «маячила» перед глазами.

  2. Возможность «вживить» перевод непосредственно в исполняемый файл в момент компиляции. Одно дело связывание DLL, все ошибки вылезут в run-time. Совсем другое дело design-time. Лично я предпочитаю возможность увидеть настойчивые матюги компилятора что «такой-то строки вообсче нету блин», чем решать все эти проблемы в run-time.


    А кстати, как вообще решать подобные проблемы в рантайм?


    Если эта строка с советом дня... Ну да ладно - печально, но не критично. А если отсутствует какое-то критическое сообщение? И результат такого сообщения - выбор пользователя - влияет на сохранность его же пользовательских данных? Вроде «Действительно удалить весь текст без возможности восстановления? Да\Нет». И что будет, если пользователь узрит шедевр «Строка не найдена. Да\Нет»?. Не-е-е, робяты - нафиг, нафиг такой софт. Код мы сто раз перепишем, поправим и юзера подтехсаппортим. Но вот исправить его данные, безвозвратно запоротые - тут, как правило, «увы», только разводить руками. Только вот пользователю от этих «увы» ничуть не легче.

  3. Возможность использовать перевод версии Икс, для версии Икс+1 таким образом, чтобы всё, что уже переведено, используется в переведенном варианте. Для чего перевода нет - пусть показывается хоть как-то, а хоть бы и на основном языке.



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

Про себя могу сказать, что у меня вполне успешно живут такие полу-украинские версии. Вновь созданные, еще непереведенные части интерфейса отображаются на русском языке. То, что уже переведено в переводе старой версии, отображается по украински. Пользователям виднее! Никто ни на чем не настаивает. Гроссе патриот? Нивапрос! Юзай старую версию - она полностью украинская! Хочешь с новыми возможностями? Извольте, просю! Вот полностью русская версия, а вот на 85-90 процентов украинская с «примесью» русского. Опять Гроссе Патриот? Опять же нивапрос! Можешь хоть с нуля переводить, можешь доделать перевод старой версии до уровня новой. В любом случае, у пользователя полная свобода выбора. Не стоит принимать за них решения - им всегда виднее, что им нужно (и не только потому, что «клиент всегда прав»).





Вот такие вот проблемы возникли после первой попытки сколько-нибудь серьезной локализации. И вот такие вот требования, пожелания, возможности локализации мне в результате и понадобились. Стало очевидно, что перевод rc-файла - никак «не катит». Нужно искать другое решение. И кстати оно было найдено. Какое? Об этом в следующей нетленке. Я и так расписался. Но с другой стороны, не вспомнить боевую «молодость» со всеми ее «геморроями», «шишками» и новыми в процессе появившимся великорусскими и ох каким живыми словами? Ну это уж «ваще»! Сентиментальность, однако. С другой стороны: об этом всём как-то неоднократно возникали дискуссии на некоторых форумах, вот и решил «не вырубить топором» - описать все с деталями и душераздирающими подробностями ручной правки всякой «мути», с которой «эти чертовы калькуляторы, стоящие на ваших, и на наших рабочих столах» вп-а-алне могли бы справляться самостоятельно. Продолжение, о том как все же была решена проблема, напишу в следующий раз.