Пока все спорят о современной попсе, мы в Хабаровске продолжаем гнуть свою линию. Мы — группа «Пожилая Биба». Делаем авторский хоррор-хардкор, вдохновленный эстетикой «угрюмого железа», и прямо сейчас собираем на коленке собственную настольную игру.
Мы долго запрягали, но этот «бычара» наконец вырвался на свободу. Встречайте наше новое видео!
Мы делаем всё своими силами, без продюсеров и многомиллионных бюджетов. В этом клипе - чистая энергетика нашей команды, атмосфера дальневосточного андеграунда и много тяжелого звука. Если вы думали, что наши прошлые треки были пределом, то этот боевик точно заставит вас взбодриться.
Для тех, кому интересно, что за дичь тут происходит: мы строим целую вселенную вокруг «Пожилой Бибы». Прямо сейчас у нас идет кампания на «Планете» по запуску настолки и мерча. Так что если хотите поддержать независимый отечественный хоррор-проект - мы будем дико рады.
Пишите в комментариях, как вам звук и картинка. Нам реально важно мнение пикабушников!
Да, это нативная сборка под Linux, причем с фейковым Vulkan API и без железа - используется программный рендер на встроенной видеокарте.
История с исходниками достойна экранизации:
Code here is decompiled with IDA Pro and manually cleaned up, uninlined and rewritten to use templates. It is not a matching decompilation, and there is no workflow to merge the functions here with functions from the binary. The .exe contains class names as part of RTTI (see objtree.txt) but there has been no source leak. There has however been a debug info leak for Tomb Raider (2013). It's a different game, but uses a similar engine.
Можно грузить уровни, модели и персонажей, к сожалению сама игра пока не работает. Но редакция внимательно следит за развитием событий.
Показываю на конкретном примере как выглядит идеальный «вкат в ИТ» — с почетом и уважением от старших коллег и карьерой, сразу улетающей ракетой в космос.
Без нейронок и римминга руководству.
Наш герой.
После ряда прошлых публикаций, многим читателям предсказуемо поплохело от глубины разрыва между больными фантазиями о будущей сказочной жизни в ИТ и жестокой реальностью.
Для примера самый частый отзыв на статью «Как на самом деле стать программистом» — «так не бывает», это какая-то фантастика и дурной прикол, а автор в лучшем случае сильно преувеличивает, в худшем — стебет и троллит почтенную публику.
Чтобы окончательно закрыть вопрос перспективности «вката в ИТ по видеокурсам», показываю почтенной публике эталонного джуна, из палаты мер и весов. Именно таких берут на работу без собеседования, за такими охотятся серьезные рекрутеры и таких переманивают конкурирующие компании.
Известный лозунг «Дорогу молодым» — тоже именно про них.
Знакомтесь:
Коннор Крукоски (Connor Krukosky), The "Mainframe Kid"
Коннор с раннего детства увлекался старыми компьютерами и успел собрать немалую коллекцию в подвале родительского дома. В 2016 году, когда ему было лишь 18 лет, он смог купить на интернет-аукционе списанный.. мейнфрейм IBM z890 за жалкие 200 баксов.
Настоящий "Big Iron", оригинальной ценой от $300 000
Так выглядел его первый пост на Reddit сразу после покупки:
So I saw someone post about this system for sale on the auction site 'GovDeals' on a mailing list I am a part of. They had no interest in the machine they just were wondering if anyone else had interest.
I was wondering what ridiculous price they were starting it at, 100 dollars, not the ridiculous I was thinking of...
Being that it was only about 2 hours from me I bid and ended up winning it at $237.
Затем эту адскую древнюю железку весом в полторы тонны храбрый подросток смог утащить в свой подвал, для чего папе пришлось расширять фундамент и немного поработать экскаватором.
Процесс перемещения свежего лута в любимый подвал. Когда родители действительно тебя поддерживают.
Затем было еще немало приключений с поиском недостающих частей, лицензионных ключей для специального ПО и кабелей — даже просто запустить эту «шайтан‑арбу» храбрый подросток очень далеко не сразу. Тут находится целая галерея с фотографиями эпического процесса перевозки и монтажа мейнфрейма подручными средствами.
Комната жениха
Коннор стал знаменитостью, сюжет о нем показывали по местному (американскому) телевидению и неоднократно брали интервью разные издания. Журналисты же придумали ему ник «Mainframe Kid», для примера одно из его более свежих интервью.
А затем его взяла на работу корпорация IBM, несмотря на учебу в колледже.
Причем и колледж и сам Коннор — из каких-то совсем далеких американских пердей в лесной глуши.
На момент написания статьи, Коннору 28 лет и он все также трудится в IBM, успев получить 10 лет непрерывного стажа в известной мировой корпорации.
Фактически его карьера теперь обеспечена и застрахована навсегда — с такими вводными его с радостью возьмут в любую компанию на планете, даже если там нет мейнфреймов, продукции IBM или компьютеров вообще.
Недостижимый идеал
Теперь разберем историю Коннора с точки зрения работодателя, чтобы вы понимали насколько такой человек идеален в качестве сотрудника. Коннор интересовался с детских лет не фентанилом или фурри-оргиями а винтажными компьютерами и уже к 15ти годам смог собрать внушительную коллекцию.
Обратите внимание на полки на заднем плане.
Чтобы вы понимали, процесс даже пользовательского взаимодействия (не говоря о разработке) с винтажными компьютерами для ребенка 21 века очень непрост:
надо постоянно рыть интернет в поисках документации, доставать редкие запчасти и носители данных.
А еще постоянно общаться с не самыми приятными людьми, из тех кто либо сам застал «те времена» либо является фанатом винтажных железок.
Со всеми этими «избранными» от мира компьютерных задротов надо еще уметь договориться, поскольку помогать просто так эти товарищи обычно не очень любят — Коннор натурально пробивался чтобы вся эта история с мейнфреймом получилась.
Ну и сам процесс работы с компьютером даже из начала 90х для современного ребенка, привыкшего к ноутбукам и планшетам покажется невероятно сложным.
А Коннор ковырялся с железками из 80х причем еще в 15-летнем возрасте.
Это вам не Майнкрафт с Роблоксами.
Даже по репозиторию заметно, что Коннор неплохо разбирается в матчасти компьютерного железа — эмуляторы, FPGA и комлекты «железячника», видно что человек учился с малых лет сложной работе инженера. Учился сам, по собственному желанию и следуя собственным интересам.
Теперь что касается всей истории с мейнфреймом.
Фактически будучи 18-летним студентом колледжа, Коннор повторил рабочие обязанности полевого инженера IBM:
демонтаж, перевозка, монтаж и подключение сверхсложного компьютерного оборудования — мейнфрейма.
Причем проявил недюженную смекалку, решив все возникшие проблемы, связанные с перевозкой полторы тонны железа на обычной легковой машине с прицепом и последующим монтажем в неподготовленном помещении.
Кабель питания мейнфрейма в тонкой детской ручке.
Уверяю если автору в его взрослые годы выдать такой мейнфрейм — далеко не факт что он сможет повторить подвиг и такую железку хотя-бы запустить.
А Коннор смог, причем в 18 лет.
Презентация
Серьезная подготовка, мотивация и интеллект в столь юные годы это замечательно, но еще не все. У Коннора оказалось в наличии еще одно важное качество, которого в свое время сильно не хватало автору — хорошее воспитание.
Посмотрите его эпическую презентацию и обратите внимание как он выглядит и что говорит:
В столь юные годы Коннор выглядит и говорит как настоящий высокооплачиваемый сотрудник IBM:
у него нет пирсинга и татуировок на лице, нет разноцветных волос или золотых насадок на зубах;
одет в рубашку классического покроя а не в разноцветный прикид с золотыми цепями или черный балахон с пауком на спине;
не хамит, не ржет в голос, не использует жаргон, не матерится и не размахивает руками;
во время презентации он с большим уважением рассказывает о своих родителях и благодарит их за помощь с проектом;
несмотря на глубокие знания в столь юном возрасте, он не заносчив и не считает собравшуюся аудиторию идиотами.
Коннор четко осознает куда он пришел и зачем, ему не надо объяснять зачем нужен деловой костюм и как себя вести в приличном обществе. Сама презентация вызвала серьезный интерес у публики — на видео выше видно, что народ в зале смотрел не отрываясь, а в конце — аплодировал.
Собравшиеся в зале высоклассные инженеры с многими годами стажа аплодировали стоя 18-летнему подростку.
Уверяю, далеко не каждый убеленный сединами менеджер способен так увлечь аудиторию как это сделал Коннор, причем сложную аудиторию из технарей и инженеров.
Что в итоге увидел работодатель:
увлеченный и мотивированный начинающий специалист, с уже сформированным багажем профильных знаний и компетенций;
с опытом решения сложных технических и производственных проблем — не социальный калека, который будет валить все на "беды с башкой" или «тревожность»;
достаточно умный и организованный, чтобы суметь во всем этом разобраться, причем с минимальным бюджетом;
без раздутого самомнения, позиции «мне все должны» и сказочных ожиданий от будущей работы;
смог повторить дорогостоящий рабочий процесс в домашних условиях и без специального начального обучения.
Собственно корпорации IBM оставалось только нанять его официально, чтобы «легализовать» деятельность и немедленно получать профит:
Коннор как сотрудник окупился сразу с первых дней своей работы в IBM, поскольку фактически еще до официального трудоустройства был полностью готов к будущей работе.
Компания IBM просто выдала ему бейдж с именем и отправила на площадку к первому клиенту.
Эпилог
Пример Коннора — эталон того каким должен быть подающий надежды «джун» и как должен выглядеть правильный «вкат в ИТ».
Именно к такому стоит стремиться «вкатунам», если не хотите общаться с недалекими рекрутерами, недалекими коллегами и вообще миновать все посредственные компании, чтобы заниматься настоящим делом в приличной компании.
Молодость спишет многое, это время экспериментов и первых побед — не надо тратить ее на алкогольные трипы и разрисовывание лица.
Если вы хотите попасть с порога в Газпром — сделайте (например) симулятор нефтедобывающей вышки, со сложными расчетами и оптимизацией процессов. Если хотите работать в Росатоме — создайте (навскидку) модель реактора из Чернобыля с расчетом причин аварии и симуляцией.
Даже если это окажется в итоге полной херней — вас запомнят и вполне возможно возьмут на хорошую работу.
Собственно изначальная идея дипломной работы — демонстрация ваших компетенций, уникальных талантов и уровня знания и автору известны случаи, когда особо эпическая дипломная работа открывала путь в светлое будущее.
PS
Это не последняя статья про уникальную и талантливую молодежь, действительно заслуживающую внимания широкой аудитории и почета с уважением от коллег по отрасли — автор и дальше будет целенаправленно показывать как выглядят приличные люди в нашей индустрии и чего стоит их опыт и талант.
А вы дорогой читатель, сможете сравить и более адекватно оценивать собственные перспективы.
Статья была опубликована на Хабре, менее цензурный оригинал как обычно в нашем блоге. Добавлю что мы пытались связаться и с самим Коннором, но ответа не поступило.
Продолжаю тему отбитого ретрогейминга для "особенных" пользователей, показываю только что собранную из исходников первую Diablo, запущенную на моей домашней FreeBSD:
Буднично рассказываю как локализовать обычное корпоративное приложение на нечеловеческие языки: Клингонский и Р’льех.
На этом скриншоте куда больше реального приложения чем кажется на первый взгляд.
Эээ.. думаю стоит начать с демонстрации результата — той самой нереальной локализации, ради которой все это и затевалось, чтобы всем сразу "все стало понятно".
Так выглядит версия на клингонском:
Обратите внимание на даты — это настоящий Stardate.
А вот так выглядит версия на Р'льех:
«Cthulhu fhtagn!» на JSF, CDI и JPA. Сложно сказать какая часть предложения напугает сильнее.
Ну и наконец банальный английский:
Вот так выглядит в работе переключение локализации:
Да, это самое обычное веб-приложение на Java, работающее в обычном браузере.
Но только с локализацией на клингонский и Р'льех.
Матчасть
Чтобы вы смогли оценить сложность задачи «локализации на язык которого нет», стоит для начала рассказать как происходит обычная локализация — на обычные человеческие языки.
Возьмем для примера классику в виде русско‑английской локализации, вот что необходимо реализовать в этом случае:
Определение текущей локали
Переключение локали
Хранение локализованных строк
Отображение локализованных данных
Данный функционал подразумевается как минимальный, когда речь заходит о локализации ПО, причем большая часть всей этой логики уже реализована в любом современном инструментарии и все что нужно сделать для поддерживаемых языков — «включить и использовать».
Вот так например выглядит хранение локализованных строк:
Это абсолютно стандартный способ, поддерживаемый как самим JDK так и всем прикладным ПО на Java
Также легко и просто оперировать обычным человеческим языком со стороны прикладного кода, например вот так выглядит получение локали из кодового названия:
Locale locale = Locale.forLanguageTag("en_US");
Где en — это указание на английский а US — на страну США.
Не менее легко происходит и переключение между языками (в данном случае в Jakarta Faces):
Но вся эта благодать быстро заканчивается, стоит только выйти за границу реальности поддерживаемых локалей и попытаться использовать «то чего нет».
Язык которого нет
Символы несуществующих фантастических языков предсказуемо отсутствуют в официальной таблице символов Unicode, их нет в списке поддерживаемых средствами разработки и нет в браузере.
Что означает невозможность какой-либо работы «из коробки» с таким языком — без специальных шагов.
Но прежде хотелось бы немного рассказать о самих фантастических языках, выбранных для локализации — чтобы у вас появилось некоторое представление куда может завести фанатизм и любовь к хардкору.
В мире где настоящие человеческие языки отмирают по сотне в день по мере ухода из жизни последних носителей, кто-то специально учит вымышленный!
Поскольку большинство фанатов клингонского — самые разнообразные гики, хорошо дружащие с техникой и матчастью, было и есть множество попыток протащить вымышленный язык куда только можно.
Например в ядро Linux:
In September 1997, Michael Everson made a proposal for encoding KLI pIqaD in Unicode, based on the Linux kernel source code. The Unicode Technical Committee rejected the Klingon proposal in May 2001
September 1997: first Unicode proposal for pIqaD.1 May 2001: Rick McGowan submits Proposal to Reject Klingon May 2001: Proposal to reject Klingon adopted by UTC (minutes) November 2016: New Proposal for Encoding Klingon, showing lots of examples of usage July 2020: Another New Proposal for Encoding Klingon. This one uses the correct “Klingon” names for the letters. August 2021: Request to Remove Klingon from Non-Approval List, made in accordance with Ken Whistler’s suggestion from 2016, linked above.
Как видите фанаты "Star Trek" крайне упертые товарищи, которые уже второй десяток лет продолжают упорно осаждать двери офиса по адресу:
611 Gateway Blvd. Suite 120
в Сан‑Франциско CA 94 080, где и располагается «The Unicode Consortium». Кстати вы также можете позвонить в консорциум Unicode на их офисный номер:
+1-408-401-8915
и поинтересоваться почему клингонский до сих пор не включен в официальный набор символов — дело же важное.
Удивительно (или нет), но в Microsoft тоже любят клингонский, настолько что добавили его поддержку в свой онлайн-переводчик:
Именно его я использовал для клингонского перевода.
При таком интересе технически продвинутой общественности, очень быстро появились готовые TTF-шрифты, использующие PUA область:
Since then several fonts using that encoding have appeared, and software for typing in pIqaD has become available
Это важный момент, поскольку такой шрифт позволяет комбинировать символы клингонского со всеми остальными, например одним шрифтом можно отобразить и английский и клингонский.
Вот так выглядит клингонский алфавит:
Обратите внимание на соответствие одного глифа клингонского сразу нескольким на английском — это влияет на реализацию транслятора (см. ниже).
Р'льех
С языком древнихР’льех все обстоит куда проще — этот также полностью выдуманный язык, приверженцы которого живут под водой и к счастью мало интересуются продвижением своего фантастического языка в широкие массы.
С названием есть небольшая неточность:
Cthuvian, which is also called R'lyehian, is a fictional language created by H. P. Lovecraft in "The Call of Cthulhu" and expanded upon by various authors.
Дословный перевод — «ктулхский» или «р'льехский», что (да простят меня подводные боги) показалось не очень благозвучным.
Поэтому я использовал термин Р'льех, который на самом деле означает иное:
Н'ЯРЛАФОТЕП — отличное название для нового проекта, не находите?
Доступные TTF-шрифты для Р'льех не используют PUA-область Unicode, поэтому применение такого шрифта превратит все символы в месиво:
Обратите внимание на поле ввода - текст в нем визуально на Р'льех, хотя введены символы английского.
Но если переключиться на клингонский, будет виден ввод символов на нормальных языках:
В этом и заключается главная сила PUA-области и ее главная фишка.
Будете создавать локализацию на древнеегипетский или руническое письмо викингов — обязательно используйте шрифт с PUA-областью.
Так выглядит проект из среды разработки.
Тестовый проект
Для статьи был специально выбран самый «тру‑энтерпрайз» стек, чтобы показать насколько далеко продвинулись технологии локализации. Это не какие-то околонаучные экспериментальные языки или малоизвестные специализированные фреймворки и не дикий «low level» с песьеголовыми программистами на С, это самый настоящий технологический мейнстрим — тот вид разработки и набор технологий, с которыми вы (если занимаетесь разработкой) сталкиваетесь каждый день:
представьте любимый клиент-банк с локализацией на клингонском.
Разумеется будет много специфики именно для Java и выбранных технологий, но описанные идеи и подходы очень даже применимы и для большинства других языков и решений.
Вот что в меню:
JakartaEE 10, который в девичестве назывался JavaEE а в далеком детстве J2EE.
В качестве сервера приложений был взят IBM OpenLiberty — современный открытый потомок большой IBM Websphere Application Server, который IBM ныне продвигает в светлое корпоративное будущее как платформу для разработки микросервисов.
Технически тестовый проект представляет собой веб‑приложение (WAR), которое разворачивается на сервере приложений и по полной использует его ресурсы — все как в золотые годы JavaEE.
Но чтобы не загонять читателей в классические мытарства с установкой и развертыванием — был добавлен автозапуск приложения с автоматическим развертыванием (как в Spring Boot).
Внутри классика корпоративной разработки:
JPA, CDI, JSF и новое Servlet API 6 — уже полностью на аннотациях.
Все прямо как на настоящей работе в банке, где деньги платят.
И сейчас мы будем локализовывать все это на выдуманный язык из фантастического сериала 1970х.
Но прежде опишу стандатное — сборку и запуск.
Сборка
Для сборки используется обычный Apache Maven и последняя версия JDK (22+), забираем проект из репозитория:
Готовое приложение будет находиться в каталоге target:
В каталоге liberty находится распакованный сервер приложений Open Liberty, с установленным внутрь нашим приложением — за все эти радости отвечает специальный плагин (см. ниже).
Запуск
Как уже упоминалось выше, наш замечательный проект предназначен для запуска и работы на сервере приложений IBM Open Liberty.
Разумеется вы можете сходить по ссылке выше, прокрутить страницу вниз до раздела Releases, скачать версию 24.0.0.6+ с профилем Jakarta EE 10, развернуть и затем установить туда наше приложение.
Для настоящего развертывания в корпоративной среде обычно и делают. По крайней мере делали до эры докера.
Но поскольку у нас тут технологическое демо, я посчитал что все эти шаги по развертыванию будут слишком сложными и добавил в сборку специальный плагин для автоматического развертывания и запуска.
Одной командой:
mvn liberty:dev
Произойдет скачивание IBM Open Liberty, распаковка, настройка, установка внутрь нашего приложения и немедленный запуск.
Вот так это выглядит из среды разработки Intellj Idea:
Начну с самого главного вопроса — с отображения символов несуществующего фантастического языка. Взгляните:
Нет это не галлюцинации или фотошоп, это установленный правильный TTF-шрифт клингонского в системе.
На скриншоте выше стандартный gedit, в настройках которого был задан клингонский шрифт для отображения основной части. Как видите использование PUA‑области Unicode в шрифте позволяет неплохо дружить символы обычного и фантастического языков.
Если приглядитесь — увидите сглаживание, работающее даже для глифов клингонского.
К сожалению для Р'льех не нашлось шрифта, использующего PUA‑область Unicode, поэтому при отображении происходит замена всех символов глифами Р'льех:
Тут все служат подводным богам, без исключений.
К сожалению нехватило времени для разработки с нуля шрифтов двух несуществующих языков, поэтому были взяты готовые.
Для клингонского:
Klingon pIqaD Mandel takes the Klinzhai or Mandel font glyphs (really a different alphabet from the KLI’s Standard pIqaD) and refits them for use as pIqaD.
I created this font based on the description by H.P. Lovecraft. Click here to download the Rlyehian font package, which includes two version of the font and a guide to understanding its use.
Но для полноты картины, все же расскажу как происходит разработка новых шрифтов, если вдруг вам понадобится локализовать проект скажем на дотракийский.
FontForge
Уже достаточно давно и успешно существует отличный открытый редактор шрифтов:
FontForge is a FOSSfont editor which supports many common font formats. Developed primarily by George Williams until 2012, FontForge is free software and is distributed under a mix of the GNU General Public License Version 3 and the 3-clause BSD license.[2] It is available for operating systems including Linux, Windows,[3] and macOS,[4] and is localized into 12 languages
Редактор мощный и доступный практически для любых ОС — его возможностей точно хватит с запасом, по крайней мере для стадии прототипирования и любительской работы со шрифтами.
Вот так выглядит клингонский шрифт, открытый в этом редакторе:
Обратите внимание на фразу «Private Use Area» — она означает что глифы клингонского расположены именно в PUA‑области.
Вот так выглядит процесс редактирования отдельного символа:
Имейте ввиду что это долгий и утомительный процесс, особенно если речь про разработку шрифта с нуля.
А вот так для сравнения выглядит шрифт для Р'льех:
Как видите тут не используется PUA и заменяются символы ASCII, с самого начала таблицы.
Для полного погружения, вот так выглядит редактирование одного из этих стильных глифов:
И ведь кто-то сидел и рисовал это. Воистину воля подводных богов безгранична.
Разумеется, можно было потратить какое‑то время и перенести глифы Р'льех в PAU‑область, что позволило бы использование шрифта по аналогии с клингонским — параллельно с другими языками.
Но к сожалению я не верю в Ктулху обладаю достаточным запасом времени и сил, так что оставил как есть.
На самом деле есть еще одна важная причина — показать вам два подхода к локализации, а не один:
второй вариант реализации шрифта с полной заменой всех символов на безумные иероглифы чем-то фантастическим (без использования PAU-области) встречается куда чаще.
Его точно стоит учитывать, поскольку скорее всего именно с таким шрифтом вы и столкнетесь, пытаясь работать с фантастическими языками.
Отображение в браузере
Отдельно опишу как происходит отображение этих фантастических языков в браузере — поскольку мы используем веб, а не отдельное десктоп-приложение.
Все современные браузеры поддерживают регистрацию и использование пользовательских шрифтов на странице — это мягко говоря не новость.
Регистрация TTF‑шрифта происходит путем использования CSS‑стиля и специальной директивы font‑face:
Сложно выглядящая директива #resource[''] на самом деле уже часть парсера страниц JSF — EL-выражение, преобразующее относительный путь к указанному ресурсу в полный.
А вот так выглядит задание отдельных стилей для использования наших фантастических шрифтов:
.klingon { font-family: 'Klingon'; }
Эти стили применяются выборочно, для включения фантастического шрифта при включенной перекодировке у сообщения:
Если сообщение было написано на клингонском pIqaD — оно будет пропущено через транслятор (см. ниже) и при отображении будет использован клингонский TTF‑шрифт.
Таким образом сохраняется обратная совместимость с другими языками и остается возможность ввода на обычном английском.
Но это решение только для отдельных блоков сообщений, ведь есть еще глобальное переключение выбранной локали:
Для решения этой задачи, используется вот такая логика:
Звездочка (*) означает что указанный шрифт должен быть применен ко всем элементам на странице, что и дает вот такой эффект глобальной локализации всего:
Также тут задается фоновая картинка в немного странном формате:
klingon.jpg.xhtml
На самом деле файл называется klingon.jpg и находится в каталоге webapp/resources, а постфикс .xhtml — особенность работы ресурсов в JSF, он нужен для правильной работы, хотя и выглядит полной дичью.
Переходим к следующей важной теме.
Транслятор
При локализации на несуществующий и неподдерживаемый язык существует еще одна проблема:
необходимо как-то работать с локализованным на такой язык текстом из стандарного окружения.
Конечно можно попробовать ставить шрифты, поддерживающие ваш фантастический язык в каждый используемый редактор, каждый терминал и среду разработки — да, это будет работать (см. ниже).
Но с точки зрения промышленной разработки это плохой путь — любая ошибка приведет к тому что вы не сможете увидеть локализованный текст вообще, либо он будет отображаться неправильно.
Если очень повезет, то пойдя этим путем можно получить что-то такое:
Круто, но слишком сложно и не подходит для массовой разработки — когда задействовано много разработчиков.
Есть способ лучше. Дело в том что ни один, даже трижды фантастический язык не существует в вакууме — для него в обязательном порядке создается:
Транслитера́ция (лат. trans- «через; пере-» + littera — «буква») — точная передача знаков одной письменности знаками другой письменности[1][2], при которой каждый знак (или последовательность знаков) одной системы письма передаётся соответствующим знаком (или последовательностью знаков) другой системы письма.
Даже если речь про например дотракийский — выдуманный сценаристами язык кхала Дрого из «Игры Престолов», к нему все равно в качестве приложения идет транслитерация на английском — актерам надо как-то учить произношение.
Более того, такая транслитерация существует и для самих человеческих языков, причем видимо для всех (исключений пока не встречал).
Например есть широко известный вариант написания кириллицы с помощью символов латиницы:
Нет людей в рунете старше 30ти, которые бы его никогда не видели.
Собственно транслит встречается до сих пор — стоит только сломаться мультиязычному вводу на вашем компьютере или телефоне и все — вам тоже придется его использовать.
Именно транслитерацию в латинские символы мы и будем использовать.
Да это «Гамлет» на клингонском — а что вы знаете о фанатизме?
pIqaD
Вариант написания клингонского латинскими символами называется pIqaD, конечно же он куда более широко распространен и популярен чем те сложные клингонские иероглифы, которые я с таким трудом отображал выше.
Думаю не стоит упоминать, что при такой популярности есть и устоявшиеся правила транслитерации и (что куда более важно) — готовые наработки. Очень быстро были найдены и готовые трансляторы, самый популярный (из открытых) выглядит вот так:
На основе его исходного кода (на Javascript) была написана моя реализация на Java, с помощью которой вот такие строковые ресурсы:
Превращаются во время работы приложения в те самые фантастические иероглифы:
Как видите тут происходит достаточно простая замена символов согласно таблице подстановки, с латинских на Unicode из PAU‑области — все внешне сложное, на самом деле устроено очень просто.
Один из немногих оригиналов документов на Р'льех.
К сожалению (или к счастью — в зависимости от контекста), фантастический язык Р’льех из миров Лавкрафта куда менее популярен, поэтому получилось найти всего один рабочий транслятор:
Using the digital serpent's package, you can translate english to the language of the "old ones" Spread aimgr'luh
Занимается им некий китайский DevOps-инженер (надеюсь не в рамках должностных обязанностей), сам транслятор и написан на Python:
Важным моментом является другой принцип работы — вместо транслитерации символов происходит подстановка слов или даже целых фраз:
Вся логика была портирована в мой проект, мою реализацию транслятора для Р'льех можно посмотреть вот тут. Разумеется с таким подходом в виде зашитого и очень небольшого словаря, нет возможности реализовать перевод технических терминов:
у меня честно нет идей как могут выглядеть слова «Авторизация», «Назад» или «Сохранить» на языке древних.
Поэтому транслятор Р’льех используется только для ввода текста — чтобы найти истинных последователей показать как это работает.
Но перейдем к следующей интересной теме.
Нереальная локаль
Следующей проблемой при работе с фантастическими языками является их регистрация в системе — в том языке, платформе или фреймворке, который вы используете.
Это нужно в первую очередь для того, чтобы как‑то сигнализировать внутри приложения о том что используется такой фантастический язык и проводить соответствующую подстройку — например вызывать тот самый транслятор, описанный выше.
Тут может быть огромное количество вариантов, проблем и подводных камней, поскольку такой разработкой мы выходим за рамки обыденного поддерживаемого. И при возникающих проблемах вам скорее всего никто не поможет — кроме нас разумеется.
Но для Java весь процесс более-менее отработан, описан и предсказуем:
в Java у локалей есть поддержка т. н. «variant» — специальной вариации языка, которая может быть сколь угодно нестандартной.
Сама локаль остается системной (в данном случае — английской), но при этом к ней добавляется специальный постфикс, означающий что используется «вариация»:
Поскольку такие variants являются частью официального API, они поддерживаются всем прикладным ПО и библиотеками (за редкими исключениями).
В том числе они используются в механизме работы ResourceBundle:
Если включить «variant» в название файла с ресурсами — он будет найден и загружен при выборе локали с таким «variant». К сожалению стандартной реализации ResourceBundle оказалось недостаточно — хотелось получить перекодированный клингонский сразу из ресурсов, поэтому я сделал свою:
package com.Ox08.experiments.kligon; import jakarta.annotation.Nonnull; import jakarta.faces.context.FacesContext; import java.util.Enumeration; import java.util.Locale; import java.util.ResourceBundle; import java.util.logging.Level; import java.util.logging.Logger; /** * Extended resource bundle, used to inject Klingon glyphs if Klingon locale * used * * @Author <a href="mailto:alex3.145@gmail.com">Alex Chernyshev</a> */ publicclass KlingonedResourceBundle extends ResourceBundle { public KlingonedResourceBundle() { setParent(ResourceBundle.getBundle("i18n.messages", FacesContext.getCurrentInstance().getViewRoot().getLocale())); } @override publicfinalvoid setParent(ResourceBundle parent) { super.setParent(parent); } @override protectedObject handleGetObject(@Nonnull String key) { // here will be extracted and substituted value finalObject v = parent.getObject(key); if (!(v instanceofString vstring)) { return v; } LOG.log(Level.INFO, "handleGetObject : {0}", vstring); // current locale final Locale l = FacesContext.getCurrentInstance().getViewRoot().getLocale(); // check if its Klingon and transliterate to glyphs if ("KLINGON".equals(l.getVariant())) return KlingonTranslator.transliterate(vstring); // .. and for Rlyeh if ("RLYEH".equals(l.getVariant())) return RlyehTranslator.translate(vstring);
Основное действие происходит в методе handleGetObject() ,сейчас разберу логику этого метода по шагам, благо она будет повторяться и в других местах.
Первым шагом происходит вызов такого же метода, но из родительского класса — для получения еще не перекодированного текстового шаблона:
final Object v = parent.getObject(key);
Затем происходит отбраковка по возвращаемому типу — мы работаем только со строками и все остальные варианты пропускаем:
if (!(v instanceofString vstring)) { return v; }
Дальше происходит получение текущей локали пользователя:
final Locale l = FacesContext.getCurrentInstance() .getViewRoot().getLocale();
Что несколько неправильно с точки зрения архитектуры большой системы, но достаточно для демо проекта.
Затем в зависимости от значения «variant» вызывается перекодировщик для клингонского:
if ("KLINGON".equals(l.getVariant())) return KlingonTranslator.transliterate(vstring);
или для Р'льех:
if ("RLYEH".equals(l.getVariant())) return RlyehTranslator.translate(vstring);
Регистрация кастомной реализации ResourceBundle задается в файле с настройками Jakarta Faces (webapp/WEB-INF/faces-config.xml):
.. <resource-bundle> <!-- Note that 'base name' points to specific class, not to .properties file --> <base-name>com.Ox08.experiments.kligon.KlingonedResourceBundle</base-name> <var>msgs</var> </resource-bundle> ..
Там же указывается список поддерживаемых локалей, с учетом «variants»:
Но это еще не все интесное и необычное, что хотелось бы раскрыть в рамках статьи.
Валидация данных
Как каша без масла протеина или водка без закуски — не бывает корпоративных приложений без валидации данных.
В Jakarta EE (как и в ее предшественнике JavaEE) для автоматической валидации входных данных используются механизмы из спецификации JSR 303 «Bean Validation».
В самом простом случае это выглядит как аннотирование полей класса:
.. @size(min = 3, max = 255) privateString title; // a title @NotBlank(message = "{validation.message.not-blank}") @Lob @column(length = Integer.MAX_VALUE) privateString message; // message, stored as CLOB in database, //so size is almost unlimited @size(min = 3, max = 30) @email privateString author; // author's email ..
Когда такой класс попадает в качестве входящего аргумента метода класса, управляемого CDI‑окружением, срабатывает автоматическая валидация и в интерфейсе появляются сообщения об ошибках:
Если ошибка имеет привязку к конкретному полю, за ее отображение отвечает отдельный блок:
<h:message for="f_message" errorClass="msg" />
если нет — она отображается через «глобальную свалку»:
Вместо текста сообщения, тут указан некий код, который автоматически заменяется на текст из специального ResourceBundle:
Согласно спецификации JSR303 название для бандла должно быть именно ValidationMessages.
Вся эта логика является частью спецификации JSR303 и вообщем-то отлично работает без вашего участия — до тех пор пока не появляется необходимость сотворить какую-нибудь дичь.
К сожалению поддержка несуществующих языков в текстах сообщений об ошибках является именно такой дичью:
Текст красненьким — та самая валидация JSR303. На клингонском.
Поэтому придется немного подумать.
После долгих поисков и изучения документации, все же был найден способ вклиниться в процесс получения текстов сообщений с ошибками валидации:
Message interpolators are used by the validation engine to create user readable error messages from constraint message descriptors.
В итоге была написана собственная реализация такого «интерполятора»:
package com.Ox08.experiments.kligon; import jakarta.validation.MessageInterpolator; import jakarta.validation.Validation; import java.util.Locale; import java.util.logging.Level; import java.util.logging.Logger; /** * Custom JSR 303 Message Interpolator, used to inject Klingon glyphs * into JSR303 validation * * @author <a href="mailto:alex3.145@gmail.com">Alex Chernyshev</a> */ publicclass JSR303KlingonMessageInterpolator implements MessageInterpolator { // we need to have existing MessageInterpolator, // to being used as parent privatefinal MessageInterpolator delegate; public JSR303KlingonMessageInterpolator() { // take default implementation from JSR303 configuration this.delegate = Validation.byDefaultProvider() .configure().getDefaultMessageInterpolator(); } @Override publicString interpolate(String string, Context cntxt) { LOG.log(Level.INFO, "interpolating {0}", string); // without specified locale - just pass interpolation to delegate return delegate.interpolate(string, cntxt); } @Override publicString interpolate(String string, Context cntxt, Locale locale) { LOG.log(Level.INFO, "interpolating {0} with locale: {1}", newObject[]{string, locale.toLanguageTag()}); // here will be extracted and substituted value finalString result = delegate.interpolate(string, cntxt, locale); // check for Klingon locale and transliterate to glyphs if ("KLINGON".equals(locale.getVariant())) return KlingonTranslator.transliterate(result);
if ("RLYEH".equals(locale.getVariant())) return RlyehTranslator.translate(result);
Основная магия логика заключается вот в этих строках:
.. finalString result = delegate.interpolate(string, cntxt, locale); // check for Klingon locale and transliterate to glyphs if ("KLINGON".equals(locale.getVariant())) return KlingonTranslator.transliterate(result);
if ("RLYEH".equals(locale.getVariant())) return RlyehTranslator.translate(result);
return result;
Как видите, локаль поступает на вход метода в готовом виде — ее не надо определять из контекста JSF, а вот в этом месте происходит получение оригинальной строки из файла с текстовыми строками:
final String result = delegate.interpolate(string, cntxt, locale);
Дальше в зависимости от наличия «variant» у локали, текст либо пропускается через транслятор либо отдается «как есть».
Регистрация кастомного интерполятора также имеет свою специфику — она происходит в отдельном XML-файле:
Который находится в файле src/main/resources/META-INF/validation.xml
Последней интересной темой, достойной освещения в рамках статьи про локализацию будут фантастические даты.
Заметьте — не просто фантастический формат отображения а целый календарь!
Фантастические даты
Никогда не задумывались какой смысл закладывается в дату?
Что такое на самом деле 2024й год?
Фактически это означает что прошло 2024 года с рождения Иисуса Христа (по новому летоисчислению), что возможно не очевидно некоторым представителям молодого поколения, но вполне достаточно для жизни и работы цивилизации.
А что если вам надо использовать альтернативную систему расчета времени?
Миллион лет от последнего динозавра?
40 000 лет бесконечной войны?
Озадачившись данным вопросом, я решил что неплохо было бы реализовать для фантастического языка еще и фантастическое летоисчисление. И использовать его для обычного корпоративного приложения, да.
A stardate is a fictional system of time measurement developed for the television and film series Star Trek. In the series, use of this date system is commonly heard at the beginning of a voice-over log entry, such as "Captain's log, stardate 41153.7.
Именно ее поддержку я и решил реализовать:
За основу был взят фанатский проект с реализацией StarDate на куче разных языков, оригинальный код был сильно уменьшен и почищен.
Но одной только реализации кастомного календаря оказалось мало — нужен еще один класс-конвертер, реализующий непосредственно конвертацию дат с этим календарем:
package com.Ox08.experiments.kligon; import jakarta.faces.component.UIComponent; import jakarta.faces.context.FacesContext; import jakarta.faces.convert.Converter; import jakarta.faces.convert.FacesConverter; import java.time.ZoneId; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; importjava.util.Date; import java.util.Locale; /** * A custom converter for StarDate * @author alex0x08 */ @FacesConverter(value = "stardateConverter") publicclass StarDateConverter implements Converter<Date> { @Override public Date getAsObject(FacesContext fc, UIComponent uic, String string) { final Locale l = fc.getViewRoot().getLocale(); if ("KLINGON".equals(l.getVariant())) return StarDate.parseStarDate(string).getDate();
return Date.from(ZonedDateTime.parse(string, DateTimeFormatter.ISO_DATE_TIME .withZone(ZoneId.systemDefault())).toInstant()); } @Override publicString getAsString(FacesContext fc, UIComponent uic, Date t) { final Locale l = fc.getViewRoot().getLocale(); if ("KLINGON".equals(l.getVariant())) return StarDate.newInstance(t).toString(); return DateTimeFormatter.ISO_DATE_TIME .withZone(ZoneId.systemDefault()) .format(t.toInstant()); } }
Активируется этот конвертер автоматически благодаря наличию аннотации:
@FacesConverter(value = "stardateConverter")
И автоматически же применяется для всех полей с типом Date, проходящих через бины, управляемые CDI.
Внутри уже традиционная логика получения текущей локали:
final Locale l = fc.getViewRoot().getLocale();
Затем при наличии клингонского «variant» происходит либо преобразование из объекта в строку с учетом кастомного календаря:
if ("KLINGON".equals(l.getVariant())) return StarDate.parseStarDate(string).getDate();
либо из строки в объект (также с учетом StarDate):
if ("KLINGON".equals(l.getVariant())) return StarDate.newInstance(t).toString();
На этом красивая история о нереальном подходит к концу, подведем итоги.
Итоги и выводы
В современных реалиях и с использованием современных инструментов нет серьезных препятствий для локализации на любые неведомые языки — искусственные или настоящие.
Отсутствие официальной поддержки «из коробки» в инструментах разработки и даже отсутствие символов в таблице символов Unicode — не является проблемой для настоящего джедая.
Первым шагом необходимо разработать или найти готовый TTF‑шрифт для вашего языка и проверить его отображение в системе и браузере — если планируется веб‑разработка.
Следующим шагом необходимо реализовать либо взять готовые правила транслитерации вашего фантастического языка символами существующего — кириллицей, латиницей и так далее. И написать соответствующий транслятор символов.
Вся дальнейшая работа сведется к включению транслятора в ключевых местах проекта.