1 (edited by Odyssey 2009-09-04 11:16:53)

Topic: Локализация INSTEAD и квестов на другие языки

Сразу хочу отметить, что смысл данной темы - не фичреквесты, а обсуждение. Хотелось бы узнать позицию разработчиков, а там если будет время - надеюсь будут и патчи.

1. Локализация INSTEAD
На данный момент, насколько я понял, поддерживается перевод на английский язык при сборке. Думаю, что по-хорошему нужен будет перевод в рантайме, в зависимости от текущего языка системы. Для этого нужно как-то хранить файлы со строковыми константами. Есть предложение делать это так же, как сейчас хранятся настройки в insteadrc, и при загрузке языка использовать тот же парсер. Файлы при этом могут выглядеть примерно так:
INSTEAD-0.8.8\languages\instead-ru_RU.txt

SAVE_SLOT_EMPTY = пусто
SELECT_LOAD_MENU = Загрузите игру\n\n

и т.д.
Либо, как вариант, можно использовать gettext, только оправдано ли это в данном случае?

2. Локализация квестов
Во-первых, сама идея локализации квестов может показаться сомнительной, поскольку для несложных квестов затраты на перевод соизмеримы с затратами на написанием квеста, и возникает вопрос "Не проще ли писать разные квесты для разных языков?".
Однако если логика квеста сложная, и он уже переведен на несколько языков -  синхронизация изменений в логике между локализованным версиями станет кошмаром, и подход с локализацией квестов себя окупит.

Теоретически, перевод квестов возможен уже сейчас с помощью костыля. Можно в main.lua игры прочитать значение LANG, извлечь из неё код языка и страны, найти по ним нужный языковой файл и сделать dofile. Костылём это является по следующим причинам:

  • Такой подход не решает проблему локализации названия игры, которое хранится в директиве в начале main.lua;

  • Для Windows переменная LANG по умолчанию не установлена, и ее пришлось бы устанавливать вручную или инсталлятором (игры или интерпретатора). Этого бы не хотелось, поскольку определить язык можно через WinAPI;

  • В Lua не очень удобно проверять наличие соответствующих языковых файлов, например я не нашел метода типа file:exists, позволяющего проверить существование файла без генерации ошибок, и реализация такого метода на основе имеющихся возможностей языка для меня неочевидна. Поэтому имхо было бы удобнее, если бы определением языка и поиском языкового файла занимался INSTEAD.

Также есть идея по поводу способа локализации:
1) При запуске квеста получать код языка и страны и сначала искать в его директории соответствующие языковые файлы.
2) Если один из этих файлов найден - выполнять его, иначе выполнять main.lua.

Т.е. для русского языка это будет выглядеть примерно так:
1) Ищем в директории игры файл main-ru_RU.lua. Если нашли - поступаем с ним так, как сейчас поступаем с main.lua - читаем из него название игры, и выполняем его (и только его).
2) Если не нашли - ищем main-ru.lua с той же целью.
3) Если не нашли ни того, ни другого - читаем main.lua.

Таким образом, мы получаем несколько преимуществ:

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

  • Сохраняем использование директив для указания названия игры, при этом избегаем использования нескольких директив в одном файле - типа $Name.ru="Обучение"$, $Name.en="Tutorial"$;

  • Выполняем принцип "одна локализация - один файл";

  • Получаем высокую гибкость. В main-xx_XX.lua можно выполнить инициализацию строковых переменных, затем сделать dofile("main.lua"), а можно написать другую версию квеста с модифицированной логикой;

  • Получаем простое решение, не требующее реализации дополнительной логики типа анализа переменных окружения. Только дополнительные файлы и вызов main.lua из них.

Кроме позиции разработчиков хотелось бы также узнать мнение всех участников сообщества по этому поводу.

Re: Локализация INSTEAD и квестов на другие языки

Odyssey wrote:

Кроме позиции разработчиков хотелось бы также узнать мнение всех участников сообщества по этому поводу.

В целом, мысль понятна. Но как делать локализацию уже самой игры на lua? То есть, просто константами вроде неудобно (непонятно в целом, что происходит в коде игры). Может попытаться сделать аналог gettext  на lua? В общем, мне кажется это основная сложность. Вообще -- в программе где существенное количество кода это строки, может и gettext не поможет. Исправил опечатку -- надо исправлять базу итд...

Также, если мы хотим сделать язык интерфейса по runtime, покажите мне WinApi, чтобы получить текущий язык. Остальное -- дело техники. (Что касается меню).

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

Re: Локализация INSTEAD и квестов на другие языки

Peter wrote:

В целом, мысль понятна. Но как делать локализацию уже самой игры на lua? То есть, просто константами вроде неудобно (непонятно в целом, что происходит в коде игры).

Есть такое.

Peter wrote:

Может попытаться сделать аналог gettext  на lua?

А вот это я бы не стал, действительно, после каждой опечатки придется перекомпоновывать все переводы.

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

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

Peter wrote:

Также, если мы хотим сделать язык интерфейса по runtime, покажите мне WinApi, чтобы получить текущий язык. Остальное -- дело техники. (Что касается меню).

GetLocaleInfo.

// Код языка (ru, en, ...) получается примерно так:
GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SISO639LANGNAME,
        buffer,sizeof(buffer));
// Код страны (RU, EN, US, ...) получается примерно так:
GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SISO3166CTRYNAME,
        buffer,sizeof(buffer));
Peter wrote:

... и найдутся энтузиасты перевести хотя бы один квест. smile

Думаю этим квестом будет Tutorial smile

Re: Локализация INSTEAD и квестов на другие языки

Odyssey wrote:

Думаю этим квестом будет Tutorial smile

Ок. Единственное -- я бы не завязывался на ru_RU а просто на код языка: ru, en итд,
+ должна быть возможность менять язык из меню. Это что касается локализации меню.

Когда появится время ( я неделю с дочкой сижу -- так что время на instead резко сократилось) буду реализовать меню.

С локализацией игр -- imho должно отстояться. Все-таки тоже есть путаница -- в зависимости от локали набор игр меняется -- есть в этом какой то изьян. Мне пока ближе идея просто делать версии игр -- никто не мешает написать мультиязычную игру и положить ее так, что в меню будет 2 title (en и ru). Имхо -- эти игры в чем-то литература. smile

Re: Локализация INSTEAD и квестов на другие языки

Peter wrote:

Все-таки тоже есть путаница -- в зависимости от локали набор игр меняется -- есть в этом какой то изьян.

Мда, действительно, о том, как это будет выглядеть из меню я не подумал..

Peter wrote:

Мне пока ближе идея протсо делать версии игр -- никто не мешает написать мультиязычную игру и положить ее так, что в меню будет 2 title (en и ru). Имхо -- эти игры в чем-то литература.

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

internaional_game
    main.ru.lua (локализация)
    main.en.lua (локализация)
    main.lua (логика)

можно сделать так:

internaional_game_ru
    main.lua (локализация)
    game_logic.lua (логика)
internaional_game_en
    main.lua (локализация)
    game_logic.lua (логика)

Т.е. при желании можно оставить в main строковые константы, а файл с логикой раскопировать по всем языковым версиям игры. Так мы создаем некоторую избыточность по файлам, зато получаем возможность перевода уже на текущей версии INSTEAD, плюс общее меню игр для всех локалей. А под Linux, при установке нескольких локализаций одной игры, можно даже использовать симлинки на общие файлы и ресурсы smile Супер, отзываю свою идею о main-xx_XX.lua.

Re: Локализация INSTEAD и квестов на другие языки

Odyssey wrote:

Т.е. при желании можно оставить в main строковые константы, а файл с логикой раскопировать по всем языковым версиям игры.

По-моему их можно даже не раскопирывать а делать dofile(../mygame-common/internals.lua); Правда в таком случае в каталоге с играми появляется каталог, который строго говоря не является игрой -- но интерпретатор его пропустит.

Т.е.

tutorial/common.lua

tutorial-en/main.lua (dofile(../tutorial/common.lua))
tutorial-ru/main.lua (dofile(../tutorial/common.lua))

А я бы предложил вообще другой вариант:

tutorial/main.lua - игра на русском (основной вариант -- превоначальный)
tutorial/common.lua

tutorial-en/main.lua -- игра на английском, делает dofile(../tutorial/common.lua)

Re: Локализация INSTEAD и квестов на другие языки

Odyssey wrote:

// Код языка (ru, en, ...) получается примерно так:
GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SISO639LANGNAME,
        buffer,sizeof(buffer));

Начал коммитить код в svn. Нормально ли следующее поведение?

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

Re: Локализация INSTEAD и квестов на другие языки

Если "первый старт" определяется по отсутствию кода языка в файле настроек - думаю нормально. Т.е. языка в настройках нет - определяем и вписываем, дальше используем язык из настроек. При желании меняем настройку через меню.

Может быть ещё добавить в меню выбора языка пункт "Выбрать автоматически", который снова определит системный язык и впишет его в файл настроек?

Кстати, немного оффтопика по поводу самих настроек. Я подумал о портабельности sdl-instead, в смысле возможности запуска с флэшки. Сейчас файлы настроек лежат в профиле пользователя, т.е. не портабельны. Возможно, в будущем стоит добавить поиск настроек в директории <sdl-instead>/settings? Я не знаю, как sdl-instead распределяется по директориям при установке под Linux, и вообще насколько идея запуска с флэшки работает под Linux..

Re: Локализация INSTEAD и квестов на другие языки

Odyssey wrote:

Если "первый старт" определяется по отсутствию кода языка в файле настроек - думаю нормально.
Может быть ещё добавить в меню выбора языка пункт "Выбрать автоматически", который снова определит системный язык и впишет его в файл настроек?

Да, если настройка пустая -- язык детектируется. Насчет автоматически -- я так и думал в начале, но опять же появляется двусмысленность. Стоит в настройках auto, отображается какой-то язык -- а какой -- не написано. То есть в настройках сейчас так: Язык: <<Русский>>.
Нажимая на стрелки -- выбираем язык. В эту систему авто как=то тяжело вписывается -- доходит очередь до auto -- интерфейс меняется -- но какой язык не написано. Пока оставляю как есть.

Насчет флешек, пути определены как функции в windows.c и unix.c -- я не знаю как определить приоритет? Ведь текущий каталог загаживать плохо, а в большинстве Win систем Document and Setting доступны на запись. wink
Но в принципе -- если придумаем адекватную логику таких функций -- я закоммичу.

Re: Локализация INSTEAD и квестов на другие языки

А не рановато ли локализовать по моему ещё сыроватый движок? Сначала в России популярность нужна.

Re: Локализация INSTEAD и квестов на другие языки

Punish wrote:

А не рановато ли локализовать по моему ещё сыроватый движок? Сначала в России популярность нужна.

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

По поводу сырости -- если обнаружил баги -- пиши о них.

Re: Локализация INSTEAD и квестов на другие языки

Peter wrote:

Начал коммитить код в svn. Нормально ли следующее поведение?

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

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

Re: Локализация INSTEAD и квестов на другие языки

Punish wrote:

А не рановато ли локализовать по моему ещё сыроватый движок? Сначала в России популярность нужна.

Движок, конечно, еще не дорос до версии 1.0, но концептуально вполне сформировался. Ошибки есть даже в самой надежной системе, но ошибки instead уже сейчас позволяют относительно комфортно обеспечивать игровой процесс.

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

Re: Локализация INSTEAD и квестов на другие языки

Ну, есть желающие доработать tutorial и перевести его на английский? wink

Re: Локализация INSTEAD и квестов на другие языки

Peter wrote:

Ну, есть желающие доработать tutorial и перевести его на английский? wink

Перевести на английский хочу я, только по срокам ничего обещать не могу. А по поводу доработки -- в каком направлении она должна быть?

Re: Локализация INSTEAD и квестов на другие языки

Odyssey wrote:

А по поводу доработки -- в каком направлении она должна быть?

Ну, может, покрасивее сделать хелп с клавишами и сам tutorial -- я его писал очень небрежно.

Re: Локализация INSTEAD и квестов на другие языки

Odyssey wrote:

Т.е. для русского языка это будет выглядеть примерно так:
1) Ищем в директории игры файл main-ru_RU.lua. Если нашли - поступаем с ним так, как сейчас поступаем с main.lua - читаем из него название игры, и выполняем его (и только его).
2) Если не нашли - ищем main-ru.lua с той же целью.
3) Если не нашли ни того, ни другого - читаем main.lua.

Считаю, что этого можно добиться, переопределив функции require и dofile
При загрузке игры в exe ставится язык. Для lua соответственно будет функция stead.lang(), дающая текущий язык.
Всегда при загрузке вызывается main.lua, а в нём stead.dofile("game.lua"), где stead.require смотрит stead.lang() и пробегает сначала все локализированные файлы game-ru_RU.lua, если нет то game-ru.lua, если нет, то game.lua


Ещё считаю такую запись:

-- $Name:Возвращение квантового кота$

костылём. Лучше, по-моему, сделать файл рядышком, типа config.lua, в котором все настройки выставляются. К примеру, название и доступные языки, тема и т.п.
При этом при загрузке проги вызываются config.lua в каждой папке и собирается нужная инфа по каждой игре

Re: Локализация INSTEAD и квестов на другие языки

Герыч wrote:

Ещё считаю такую запись:

-- $Name:Возвращение квантового кота$

костылём.
...
При этом при загрузке проги вызываются config.lua в каждой папке и собирается нужная инфа по каждой игре

Тут как бы специально и был сделан костыль, чтобы не выполнять заранее никакой LUA код (тут считывается только заголовок- не LUA интерпретатором). Так как LUA код всегда относится к контексту LUA, котрый если не играет игра как бы вроде бы и не нужно создавать. Но мысль понял так, что выполнили lua и записали в свои структуры уже c-шные то что нам нужно? Может быть и стоит подумать в этом направлении, но сейчас основная цель -- игры smile

Re: Локализация INSTEAD и квестов на другие языки

Peter wrote:

записали в свои структуры уже c-шные то что нам нужно?

Ну вот смотри, про локализацию:
как я понял текущее предложение для 1 игры на 5 разных языках создать 5 папок, в каждой из которых лежит загрузчик самого кода игры вообще из каталога, где лежат папки с играми. Скажу честно - тупость) нарушает логичность организации директорий. Я же предлагаю при загрузки exe проглядеть все config.lua и получить список поддерживаемых языков. К примеру так:

stead.init {         --Говорим интерпретатору, что в этой папке 3 языковых версии игры, с дефолтной - английской
  lang=
  {
     default = {"Return of the quantum cat","en_EN"},
                    {"Возвращение квантового кота","ru_RU"},
                    {"El retorno del gato cuántico","es_ES")}
  }
}

затем в main.lua к примеру:

dofile("lang_constants.lua")
me().nam = PLAYER_NAME;

в lang_constants-ru_RU.ua

PLAYER_NAME="Олег"

в lang_constants-en_EN.ua

PLAYER_NAME="Bill"

в lang_constants-esES.ua

PLAYER_NAME="Pedro"

Вроде всё понятно)

20 (edited by Odyssey 2009-09-12 09:24:53)

Re: Локализация INSTEAD и квестов на другие языки

Герыч wrote:

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

Отчасти согласен, 1 игра = 1 директория было бы логичнее. Однако текущий подход имеет несколько преимуществ:
П1) он уже работает, обратно совместим, и освобождает наше время на написание игр. Т.е. затраты на реализацию этого варианта -- 0 человекочасов.
П2) более удобное управление языковыми версиями - если мне не нужен "Кот" на испанском - я спокойно его удаляю. В больших квестах с учетом UTF-8 локализации могут занимать мегабайты.
П3) такой способ обеспечивает постоянное меню игр вне зависимости от языка. Т.е. если я установил "Return of the quantum cat" и "Возвращение квантового кота", то могу запустить обе версии вне зависимости от моей системной локали.
П4) добавленный в новой версии механизм выбора игр через параметры командной строки ИМХО со временем заменит меню. Шариться в меню из полусотни и больше игр будет труднее, чем запускать игры через ярлык или скрипт. А при использовании этих параметров (-gamespath и -game) игры можно будет раскладывать по директориям как угодно, и можно будет для конкретной игры сделать например такую структуру директорий:
cat\ru_RU\
cat\en_US\
cat\common\
и запускать языковые версии через соответствующий ярлык или скрипт. При этом необходимость в config.lua отпадает.
П5) принципа 1 игра = 1 директория можно достигнуть, не вынося логику игры в папку с играми, а копируя файл с логикой в каждую языковую версию. Это, конечно, замена одного костыля другим, зато мы сохраняем логичность структуры директорий, и языковые версии становятся независимыми друг от друга. Представим, что в мультиязычном Коте поменялась логика, тогда все локализации кроме основной (в которой менялась логика) как минимум будут неполными, а в худшем случае такое несоответствие между старыми локализациями и новой логикой может привести к багам в соответствующих языковых версиях. А если продублировать логику -- локализованные версии всегда будут адекватными, а их мэйнтейнеры сами будут следить за обновлением основной версии, копировать себе новую логику и обновлять перевод.

Достоинство твоего предложения -- как раз 1 игра = 1 директория. Теперь о недостатках:

Н1)

stead.init {         --Говорим интерпретатору, что в этой папке 3 языковых версии игры, с дефолтной - английской
  lang=
  {
     default = {"Return of the quantum cat","en_EN"},
                    {"Возвращение квантового кота","ru_RU"},
                    {"El retorno del gato cuántico","es_ES")}
  }
}

Мы смешиваем в одном файле несколько локализаций, поэтому чтобы удалить или доустановить локализацию мы вынуждены будем не только копировать папки, но и модифицировать файлы. Имхо это недопустимо. Если завтра, например, Комуро Ишикава решит создать для "Кота" японскую локализацию, то установка этой локализации будет нетривиальной задачей, и ему придется либо писать инсталлятор, изменяющий config.lua, либо искать Петра и просить его включить новую локализацию в дистрибутив, увеличивая размер дистрибутива.

Н2)

dofile("lang_constants.lua")
me().nam = PLAYER_NAME;

в lang_constants-ru_RU.ua

PLAYER_NAME="Олег"

Глобальное переопределение dofile плохо по двум причинам. Во-первых, параметр перестает однозначно указывать на файл для исполнения, что вводит в заблуждение. И что делать, если в какой-то истуации нам нужен именно обычный dofile, т.е. выполнить именно то, что мы написали в параметре? Во-вторых, а если при русской системной локали файла lang_constants-ru_RU.lua нет в папке, что тогда? Пользователь не сможет посмотреть вообще никакую версию игры -- ни русскую ни другую.

Поэтому, чтобы сделать все по-человечески, кроме принципа "1 игра = 1 директория" нужно бы еще придерживаться принципа "1 локализация = 1 директория внутри директории игры". И реализовать в main.lua подгрузку нужной локализации из соответствующей директории, а в config.lua -- подгрузку нужного локализованного названия. Для этого не хватает нескольких вещей, что-то типа:
1) lua.os.langAndCtryCode, которая бы возвращала строки типа ru_RU, en_US, en_GB и т.д.
2) lua.fs.dirExists и lua.fs.fileExists которые бы возвращали Boolean при существовании соотв. объектов ФС
3) механизма загрузки названий игр из lua-файлов.
В сумме набирается достаточно много изменений в Instead, на их внесение и исправление возникших багов уйдет время. А игр в течение этого времени так и не будет.

Я вижу два возможных варианта решения проблемы:
1. потерпеть текущий метод локализации, и писать игры,
2. или писать патчи для добавления функций (1)-(3) в instead.
Я предпочту вариант 1, тем более что благодаря П4 и П5 он меня полностью устраивает, и "терпеть" не придется smile

Re: Локализация INSTEAD и квестов на другие языки

ё-маё, скока критики) делайте как хотите, я просто варианты подкидываю. А

2) lua.fs.dirExists и lua.fs.fileExists

можно реализовывать через модуль luaFileSystem

Глобальное переопределение dofile

можно делать не глобальным, а опять таки stead.doLangFile("game.lua")

ну в остальном согласен.

Re: Локализация INSTEAD и квестов на другие языки

Герыч wrote:

ё-маё, скока критики)

Извиняюсь, если переборщил smile Вроде бы старался быть конструктивным. Просто немного задело

Скажу честно - тупость

поэтому так много контрагргументов.

Герыч wrote:

делайте как хотите, я просто варианты подкидываю.

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

Герыч wrote:

lua.fs.dirExists и lua.fs.fileExists можно реализовывать через модуль luaFileSystem

Отлично.

Re: Локализация INSTEAD и квестов на другие языки

да я ж не обижаюсь))

24 (edited by Odyssey 2009-09-13 13:14:03)

Re: Локализация INSTEAD и квестов на другие языки

Выкладываю английскую версию "Туториала". Сделал несколько изменений, возможно их стоит перенести в наш родной туториал.
* Подробного описания клавиш и режимов клавиатуры не сделал, пока только короткое упоминание об выборе объектов с клавиатуры.
* Заменил нож с яблоком на листок и карандаш, чтобы при съедании не делать комментариев как в "Истории", типа "Ни на секунду не забывая, где вы подобрали яблоко с ножом.." smile
* Немного изменил способ перехода между уроками.

Конструктивная критика приветствуется.

UPD: Вложение удалено, обновленная версия в следующих сообщениях.

Re: Локализация INSTEAD и квестов на другие языки

Odyssey wrote:

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

Мне очень понравилось. Добавить справку по клавишам и перенести в русский туториал -- по-моему правильный вариант. Интересно, что стиль программирования совсем иной. smile

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

Еще -- наверное стоит запретить делать правильные действия но в неправильных локациях? А то заранее все сделал потыкал -- а дальше уже заранее горит Continue в след комнатах.

UPD: да -- в итоге удалось сломать логику и я повис на одном из уроков (просто делал все заранее)

UPD2: Я наверное подожду, чтобы включить ее в 0.8.9 версию?