Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "Прочее";
Текущий архив: 2006.11.12;
Скачать: [xml.tar.bz2];

Вниз

Какие именно задачи следует решать с помощью ХП?   Найти похожие ветки 

 
Курдль ©   (2006-10-26 17:14) [120]


> saxon   (26.10.06 17:01) [118]
> Да, весьма интересно глянуть на такую "реализацию" - в трехзвенке
> и без применения ХП.

И как тебе это показать? В гости пригласить?
Как раз в трехзвенке без ХП это легко реализуемо. Если принять, что привиленгия пользователя - это атрибут сущности "юзер" той же модели данных, что и для всего приложения, то работа с этим атрибутом ничем не отличается от, например, работы с атрибутом БИК сущности "Банк". Если требуется ввести ограничения даже на уровне отдельных записей, это решается обычным запросом к таблице или представлению.
Администрирование привилегий на уровне СУБД мне тоже приходилось реализовывать (правда тоже без ХП). Но для этого административному модулю системы приходилось давать действительно опасные привилегии доступа к системным таблицам и представлениям, причем включая модификацию.
Или Вы представляете себе, что система работает сама по себе, а пользователей в ней "ведет" специально обученный администратор БД?


 
kaif ©   (2006-10-26 17:15) [121]

Курдль ©   (26.10.06 16:44) [117]
Во что выльется, например, реализация бизнесс-процесса регистрации пользователя системы от уровня пользовательского интерфейса до получения им соответствующих привилегий?


Ничего страшного.
Я как раз пару дней назад решал подобную проблему.
Я создал таблицу:

TABLE ACTION_LIST:
ID,  NAME
=================
1  Доступ к тому-то
2  Право редактировать то-то
3  Доступ к интерфейсу такому-то
4  Доступность кнопки такой-то
....
Достаточно неформальный и на понятном людям языке списочек вещей, существенных для конкретной задачи, которую я решал.

У меня есть еще таблица

TABLE ACTION_PRIVILEGES:
ACTION_ID, PRIVILEGE, RELATION_NAME
================
1        S   TABLE1
1        I    TABLE1
1        U   TABLE1
1        S   TABLE2
2        I    TABLE2
2        E    PROCEDURE1
...

и так далее.  
Разумеется, привилении на какие-то объекты могут "пересекаться" в разных "привилегиях на функции системы".

Ничего страшного.
Далее я написал именно ХП, которая возвращает мне все нужные команды GRANT или REVOKE, если администратор предоставляет/отзывает функцию системы для данного пользователя:

/*Процедура возвращает команды, которые необходио выполнить для предоставления
 всех прав пользователю в связи с действием ACTION_ID*/
CREATE PROCEDURE GET_PRIVILEGE_COMMANDS(USER_ID INTEGER, ACTION_ID INTEGER, SET_GRANTS SMALLINT)
RETURNS (GRANT_COMMAND VARCHAR(255) CHARACTER SET ASCII)
AS
DECLARE VARIABLE USER_NAME VARCHAR(31) CHARACTER SET ASCII;
DECLARE VARIABLE RELATION_NAME VARCHAR(31) CHARACTER SET ASCII;
DECLARE VARIABLE PRIVILEGE CHAR(1) CHARACTER SET ASCII;
DECLARE VARIABLE TMP VARCHAR(30) CHARACTER SET ASCII;
BEGIN
 SELECT USER_NAME FROM USER_LIST
 WHERE USER_ID = :USER_ID
 INTO :USER_NAME;

 /*Все привилегии на объекты базы, которые надо предоставить*/
 FOR SELECT DISTINCT AP.PRIVILEGE,  AP.RELATION_NAME
 FROM  ACTION_PRIVILEGES AP
       LEFT OUTER JOIN RDB$USER_PRIVILEGES UP
       ON RDB$USER = :USER_NAME AND
          RDB$RELATION_NAME = AP.RELATION_NAME AND
          AP.PRIVILEGE = UP.RDB$PRIVILEGE AND
          AP.RELATION_NAME = UP.RDB$RELATION_NAME
 WHERE :SET_GRANTS = 1 AND ACTION_ID = :ACTION_ID AND UP.RDB$PRIVILEGE IS NULL
 UNION
 /*Все привилегии на объекты базы, которые надо отозвать*/
 SELECT DISTINCT AP1.PRIVILEGE,  AP1.RELATION_NAME
 FROM  ACTION_PRIVILEGES AP1
       INNER JOIN RDB$USER_PRIVILEGES UP
       ON RDB$USER = :USER_NAME AND
          RDB$RELATION_NAME = AP1.RELATION_NAME AND
          AP1.PRIVILEGE = UP.RDB$PRIVILEGE AND
          AP1.RELATION_NAME = UP.RDB$RELATION_NAME
       LEFT OUTER JOIN ACTION_PRIVILEGES AP2
       ON AP1.RELATION_NAME = AP2.RELATION_NAME AND
          AP1.PRIVILEGE = AP2.PRIVILEGE AND
          AP2.ACTION_ID <> :ACTION_ID
 WHERE :SET_GRANTS = 0 AND AP1.ACTION_ID = :ACTION_ID AND AP2.PRIVILEGE IS NULL
 INTO :PRIVILEGE,  :RELATION_NAME
 DO
 BEGIN
   IF (PRIVILEGE = "S") THEN TMP =      "SELECT ON ";
   ELSE IF (PRIVILEGE = "I") THEN TMP = "INSERT ON ";
   ELSE IF (PRIVILEGE = "U") THEN TMP = "UPDATE ON ";
   ELSE IF (PRIVILEGE = "D") THEN TMP = "DELETE ON ";
   ELSE IF (PRIVILEGE = "R") THEN TMP = "REFERENCES ON ";
   ELSE IF (PRIVILEGE = "E") THEN TMP = "EXECUTE ON PROCEDURE ";

   IF (SET_GRANTS = 1) THEN
     GRANT_COMMAND = "GRANT "||TMP||RELATION_NAME||" TO "||USER_NAME||";";
   ELSE
     GRANT_COMMAND = "REVOKE "||TMP||RELATION_NAME||" FROM "||USER_NAME||";";

   SUSPEND;
 END
END


Дарю этот текст широкой публике.
:)
Работает изумительно.

Имеется еще таблица, в которой каждому юзеру предоставлены привилегии на функции:

TABLE USER_ACTIONS
USER_ID,  ACTION_ID
==================
1             1
1             2
2             1
3             1
3             2
3             3
....

И таблица, в которой перечислены все юзеры
TABLE USER_LIST
ID  USER_NAME NAME
==================
1   VASIA        Вася
2   PETIA        Петя
....

Итого мы имеем 4 таблицы: таблица юзеров, таблица "функций системы" , таблица привилегий функций на объекты базы и таблица привилегий юзеров на "функции системы".

Это - вся система безопасности того, что я сейчас пишу.

При старте приложение запрашивает из таблицы USER_ACTIONS все привилегии юзера на функции системы и обработчики событий OnShow всех дельфийских форм проверяют привилегии на нужные функции вызовом функции IsActionGranted(action_id: integer). Пользователь получает тот интерфейс, который должен получить по замыслу разработчика и согласно тем правам на функции, которые ему предоставил "Администратор", поставив птичку рядом с "функцией системы", когда раздавал привилегии этому юзеру.

Когда Администратор "ставит птичку" у функции № 13, тут же вставляется запись в таблицу USER_ACTIONS и тут же делается запрос к той ХП, что я привел.

SELECT * FROM GET_PRIVILEGE_COMMANDS(1, 13, 1)
Процедура возвращает набор фраз вида GRANT...., которые просто исполняются утилитой администратора.

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

SELECT * FROM GET_PRIVILEGE_COMMANDS(1, 13, 0)

(последний парамет теперь 0)

И тут же выполняются команды REVOKE..., которая эта процедура вернула.
------------------------

Я сам не ожидал. но эта работает и очень легко расширяется. Как только я (разработчик) добавляю в систему новый интерфейс или таблицы, я тут же прописываю новые функции в список ACTION_LIST, и нужные привилегии в ACTION_PRIVILEGES.

Дальше мне остается запустить приложение администратора и раздать птички юзерам.

Я не утверждаю, что нельзя сделать такие же вещи без той ХП, что я написал. Можно. Но невозможно будет этот код вот так вот легко выставить на форуме и порекомендовать всем, кто работает с IB. И к тому же в ХП удобнее было это реализовать, чем на стороне клиента. Не знаю, почему удобнее. Просто удобнее - и все тут.

Если кто-то сможет показать, как в данном случае применение ХП у меня означает неумение или нежелание писать SQL-запросы, я буду весьма признателен :)
:)


 
ANB ©   (2006-10-26 17:18) [122]


> И в любом случае сервер приложений не отменяет необходимость
> в ХП, когда они действительно нужны.

Зато ХП вполне могут отменить необходимость в сервере приложений.

Давайте посчитаем.

Имеем довольно сложную задачу.

Нужен клиент (сделаем его более менее толстым, чтобы не терять гибкости)
и нужна централизованная логика.

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

В вариантах б) и в) вместо работы над хранимками нам придется либо писать и их (причем на скудном языке - что снизит производительность) либо целиком запихать в сервер приложений. Причем апп.сервер писать все равно придется.
Написание и отладка апп.сервера займет довольно много времени. При этом мы вряд ли сможет менять его на лету, даже если попилим на DLL (что внесет дополнительный геморрой). Любые изменения в структуре БД потребуют очень тщательного тестирования. Предположим, что на апп.сервер кинули трех программеров и писали они его полгода (и это очень простой апп.сервер, я заню систему, где его 10 человек пишут и дописывают его уже 7 лет, каждый раз выгребая ошибки типа неправильно написали имя поля или забыли исправить запрос).

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

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


 
ANB ©   (2006-10-26 17:20) [123]


> Но для этого административному модулю системы приходилось
> давать действительно опасные привилегии доступа к системным
> таблицам и представлениям, причем включая модификацию.

Эта - а зачем ?
ЗЫ. Все уже сделано. До нас. (С) Операция Ы.


 
kaif ©   (2006-10-26 17:24) [124]

2 ANB ©   (26.10.06 17:18) [122]
Добавлю и свои 5 копеек.
Если я исполдьзую ХП в IB, то мне сервен не даст в случае изменения структуры таблиц совершить целый ряд ошибок, если эти таблицы обвешаны ХП, так как IB поддерживает целостность метаданных. Например я не смогу так вот запросто удалить использующееся в какой-то ХП поле. А если это поле используется в каком-то там серверном приложении, я на стадии удаления поля просто могу об этом ничего не знать.


 
Курдль ©   (2006-10-26 17:25) [125]


> ANB ©   (26.10.06 17:18) [122]

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


> ANB ©   (26.10.06 17:20) [123]
> Эта - а зачем ?

Что "зачем"? "Зачем сдавать заказчику систему с полным функционалом, если можно обучить фельдшера провинциальной поликлиники управлять вводом пользователем и распределением их привилегий средствами оракла?"


 
ANB ©   (2006-10-26 17:30) [126]


> Курдль ©   (26.10.06 17:25) [125]

Зачем давать привелегии на модификацию системных таблиц ?

Есть куча отработанных готовых решений. Которые можно просто вставить к себе в проект. Будет не сложнее, чем на трехслойке.


> где я рассматривал преимущества ООП

Нету у него никаких преимуществ. А для особо желающих оракл дает возможность работать с явой и объектами на pl/sql. Опять таки апп.сервер ни на фиг не сдался.

ИМХО : Частенько попытки все сделать через ООП приводят к более громоздкому и сложному решению. Все хорошо в меру и к месту.


 
Игорь Шевченко ©   (2006-10-26 17:31) [127]

Курдль ©   (26.10.06 17:25) [125]


> "Зачем сдавать заказчику систему с полным функционалом,
> если можно обучить фельдшера провинциальной поликлиники
> управлять вводом пользователем и распределением их привилегий
> средствами оракла?"


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


 
kaif ©   (2006-10-26 17:35) [128]

Расскажу один случай. Может быть это тоже поможет разобраться.
Как-то меня пригласили на одну фирму, как человека, "знающего Интербейс". У них была тяжелая проблема - их складская программа каким-то образом (возможно из-за ошибки в самой программе или в данных) создала неверные складские остатки и они не могли отгружать некоторые виды товара из-за жесткого ограничения "минус на складе".
База данных была сложная и огромная.
Приложение я вообще не мог изменить (EXE-файл).
Но мне повезло в одном отношении.
Я посмотрел список хранимых процедур (а там их было три тысячи !!!) и нашел там процедуру, буквально по-английски названную "заново сформировать складские остатки", которая была чисто технической и вызвалась из каких-то других процедур. Взглянув на текст этой процедуры, я пришел к выводу, что накопители данных она не затрагивает (приходы и расходы) а лишь делает какие-то "перетряхивания" дальше.
Я запустил ее и сервер думал пару минут, после чего "все исправилось".

Как бы я смог решить эту проблему, будь это все организовано в приложении? Вот так вот - за 5 минут?


 
Курдль ©   (2006-10-26 17:35) [129]


> ANB ©   (26.10.06 17:30) [126]
> > где я рассматривал преимущества ООП
> Нету у него никаких преимуществ.


Я имею право на защиту своей позиции в основном благодаря богатому опыту, приобретенному при исполнении успешных проектов.

Поэтому на твое заявление совершенно спокойно тебе отвечаю: "ООП дает многократное преимущество в скорости IT разработок!".


 
saxon   (2006-10-26 17:38) [130]


> Курдль ©   (26.10.06 17:14) [120]
> И как тебе это показать?

Я, Вас, не просил мне что то показывать. Читайте внимательнее.


 
Курдль ©   (2006-10-26 17:45) [131]


> Игорь Шевченко ©   (26.10.06 17:31) [127]
> Как бы предусматривается, что у оракла есть администратор.


Кем предусматривается? Я привел пример из жизни.
Согласно контракту БД типа "оракл" не требовала администрирования. И после внедрения так все и осталось. Наша фирма осуществляла поддержку системы несколько лет, за которые пару раз приходилось восстанавливать
данные из бэкапленного дампа после падения аппаратных средств. Потом
заказчик отказался от наших услуг (все и так прекрасно работало).


 
Игорь Шевченко ©   (2006-10-26 17:47) [132]

Курдль ©   (26.10.06 17:45) [131]

Примеры из жизни разные бывают. Бывает пример с раздачей прав пользователем при установке системы. Тоже из жизни.
В твоем примере кто дампы делал ?


 
Курдль ©   (2006-10-26 17:49) [133]


> saxon   (26.10.06 17:38) [130]
> Я, Вас, не просил мне что то показывать. Читайте внимательнее.


Насколько внимательнее читать? Вот читаю по буквам:


> saxon   (26.10.06 17:01) [118]
> Да, весьма интересно глянуть на такую "реализацию" - в трехзвенке
> и без применения ХП.


Этот Ваш пост был чисто риторическим, а последующий должен был повлечь конфронтацию?


 
ANB ©   (2006-10-26 17:50) [134]


> Поэтому на твое заявление совершенно спокойно тебе отвечаю:
>  "ООП дает многократное преимущество в скорости IT разработок!
> ".

Я уже писал, что для поклонников ООП оракл предусмотрел объектные типы.


 
Курдль ©   (2006-10-26 17:51) [135]


> Игорь Шевченко ©   (26.10.06 17:47) [132]
> В твоем примере кто дампы делал ?


Дампы делали джобы (не к столу будет сказано) :)
Но! Во время внедрения системы с этим справлялся далекостоящий от IT персонал (посылали нам дампы мылом, чтобы мы могли разобраться с проблемами в данных).


 
ANB ©   (2006-10-26 18:00) [136]


> Курдль ©   (26.10.06 17:51) [135]

ЭЭЭЭ. Я это не понял - вы за ХП или против ?


 
Курдль ©   (2006-10-26 18:02) [137]


> ANB ©   (26.10.06 17:50) [134]
> Я уже писал, что для поклонников ООП оракл предусмотрел
> объектные типы.


Объектные типы != ООП. Когда оракл предоставит возможность вести проекты от бизнес-логики до пользовательского интерфейса (Оракл Формз не предлагать!) ...


 
kaif ©   (2006-10-26 18:03) [138]

Дамп ORACLE мылом - круто.
Все вопросы снимаются.
Если Ваши заказчики могут посылать мылом гигабайты, то у них достаточно аппаратных мощностей, чтобы вообще все на Visual Basic перевсти и вместо сервера БД использовать текстовый файл.
:)

Кстати, а нафиг сервер-то?
У меня есть знакомый, который писал Web-сайты на PHP+MySQL, но в результате "развития" пришел к тому что пишет все исключительно на C + текстовый файл и уверяет, что любой сервер БД - от лукавого. От неумения программистов писать код. Это напоминает мне Вашу позицию: "ХП от неумения писать SQL-запросы". В идеале вообще не должно быть никакого сервера БД. Кто сказал, что он вообще нужен? Ведь насколько проще сделать так, чтобы каждый объект в ООП сам себя куда-нибудь записывал бы, например, в отдельный файл. Вото кто сказал, что "Товар на складе" вообще нужно совать в СУБД? Давайте сделаем метод SaveToFile и покончим с этим. А если кто-то хочет посмотреть остатки на складе, пускай вызывает метод GoodsCollection.LoadFromExisitingFiles() И длальше отыскивает там товар методом GoodsCollection.GetGood: TGood, после чего смотрит свойство Good.StockRemainder.

Зачем СУБД?

Объясните.
Я не вижу смысла в СУБД. Она только замедляет выборку всех товаров в кэш, так как грузит страницы файла база данных, где кроме товаров еще и другие вещи записаны.


 
k2 ©   (2006-10-26 18:03) [139]

Курдль ©   (26.10.06 18:02) [137]
почему не предлагать? :)


 
ANB ©   (2006-10-26 18:06) [140]


> (Оракл Формз не предлагать!)

Ну тогда оракл аппликейшион :)

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

А первая - полутонкий клиент на делфи + ядро на оракле. Стояла по всем наложкам татарстана. Проработала лет 10. До сих пор в наложках ее с ностальгией вспоминают. Впрочем наследники этой системы до сих пор используются в реализации серьезных проектов.


 
Игорь Шевченко ©   (2006-10-26 18:06) [141]

Курдль ©   (26.10.06 17:51) [135]

Точно так же далекостоящий от IT персонал мог выполнить хранимую процедуру, создающую пользователя и дающую ему все необходимые привилегии :))


 
vuk ©   (2006-10-26 18:17) [142]

Насчет безопасности. У нас пользователи БД вообще не имеют никаких прав на работу с таблицами. Все только через процедуры. Ну и до кучи реализована своя подсистема прав/привелегий. Все внутри процедур.


 
ИА   (2006-10-27 06:52) [143]

>В продолжении темы о Web-проектировании выделился вопрос о целесообразности применения ХП. Причем именно в альтернативе с исполнением бизнес-логики на сервере приложений.
Моя позиция - в пользу сервера приложений.

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

Рецеп банкета "Трехзвенка" от ИА

Бульон

  Взять 1 базу данных и тщательно очистить ее от триггеров. Добавить структуры данных в виде таблиц, первичные и вторичные ключи, cascade правила, required поля и прочие железные принципы которые можно задать в виде индексов и механизмов самих таблиц. Равномерно взбить. Отсортировать хранимые процедуры - те что меняют данные помыть от лишней бизнес логики и связать в пучки, по одному на порцию транзакции. Из тех что данные запрашивают отобрать посвежее, остальное выкинуть. Взять права пользователя на таблицы, обвязать ниточкой и положить в скрипт, привязав другой конец к администратору.
Залить все это данными и поставить на сильную загрузку. Когда жесткие диски на сервере диски размягчеют, загрузку поменять на минимальную и долить результаты Index Analyzer. Загрузку убрать, букет прав вытащить.

Второе

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

Гарнир

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

Подача

 Клиента аккуратно разложить по сети. Выложить горкой бизнеслогику, в центре сделать углубление в которое аккуратно налить данные (для этого возьмите OleDB и лейте данные по ней тонкой струйкой).
Следите что бы данные не просочились к гарниру.
Поставить мисочки с плагинами для сервера.

Приятного аппетита


 
Danilka ©   (2006-10-27 09:51) [144]

[105] Курдль ©   (26.10.06 15:48)
Действительно, работает, спасибо. А я думал, это чисто Микросовтская выдумка. :))
Правда, твой запрос все равно неправильный. Вместо "distinct count(*)" надо "VISIT_INTERVAL, count(*)". :))


 
Mystic ©   (2006-10-27 21:56) [145]

Не знаю, что-то мне кажется, что проблема частично в используемой СУБД. Многие из перечисленых здесь вопросов решаются специальными средствами Oracle (аналитические функции, row level security, ...). Гибкая реализация хранимых процедур в IB [FB] также посволяет это релизовать своими руками (пусть с некоторым падением производительности). А вот простого решения для MS SQL (2005 не пробовал) я не нахожу (может недостаточно опыта), поэтому просто необходим сервер, который бы мог помочь решить эти проблемы :)

> Согласен, что этот запрос будет работать дольше, чем ХП,
>  но если на прогнозируемом объеме данных этот запрос покажет
> приемлемые результаты...


И получится история про маляра Шлейхема.



Страницы: 1 2 3 4 вся ветка

Форум: "Прочее";
Текущий архив: 2006.11.12;
Скачать: [xml.tar.bz2];

Наверх




Память: 0.82 MB
Время: 0.055 c
15-1161346296
ArtemESC
2006-10-20 16:11
2006.11.12
Задачка по физике


1-1159639343
Calibr
2006-09-30 22:02
2006.11.12
вопрос по Variant?


15-1161900291
ArtemESC
2006-10-27 02:04
2006.11.12
Си - чего ему не нравится?


2-1162032308
TIF
2006-10-28 14:45
2006.11.12
Как создать файл в Program Files?


6-1151062890
antoxa2005
2006-06-23 15:41
2006.11.12
Подскажите, как решается задача "связки" БД сайта (MySQL) c ,базо





Afrikaans Albanian Arabic Armenian Azerbaijani Basque Belarusian Bulgarian Catalan Chinese (Simplified) Chinese (Traditional) Croatian Czech Danish Dutch English Estonian Filipino Finnish French
Galician Georgian German Greek Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Korean Latvian Lithuanian Macedonian Malay Maltese Norwegian
Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish Bengali Bosnian
Cebuano Esperanto Gujarati Hausa Hmong Igbo Javanese Kannada Khmer Lao Latin Maori Marathi Mongolian Nepali Punjabi Somali Tamil Telugu Yoruba
Zulu
Английский Французский Немецкий Итальянский Португальский Русский Испанский