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

Вниз

Переменные процедурного типа   Найти похожие ветки 

 
checkmate-maker   (2008-10-14 12:45) [0]

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

...
var
 f: procedure(a0: Integer; a1:String); stdcall;
begin
 @f := GetPtr();
end;

 Функция GetPtr возвращает указатель на процедуру(либо функцию). Если параметры процедуры f идентичны параметрам в возвращенной процедуры, то все нормально. Если совпадение не достигнуто, то ненормально. Как до вызова f определить корректность параметров?
 Спасибо.


 
clickmaker ©   (2008-10-14 12:54) [1]

а почему может возникнуть несовпадение?


 
Сергей М. ©   (2008-10-14 12:56) [2]


> до вызова f определить корректность параметров?


До вызова f нет никаких параметров, кроме формальных.


 
Сергей М. ©   (2008-10-14 13:01) [3]


> Функция GetPtr возвращает указатель на процедуру(либо функцию)


Делай так

type
 TMyProc = procedure(a0: Integer; a1:String); stdcall;
var
f: TMyProc;

function GetPtr(): TMyProc;

и ни о чем не задумывайся - чуть что не так, умный компилятор тут же даст по рукам)


 
Slym ©   (2008-10-14 13:08) [4]

Я чищу зубы... как мне удостовериться что я в рот засунул щетку а не строл дробовика? И что это за странный рычажок на моей щетке?


 
checkmate-maker   (2008-10-14 13:22) [5]


> Я чищу зубы... как мне удостовериться что я в рот засунул
> щетку а не строл дробовика? И что это за странный рычажок
> на моей щетке?


К сожалению щетки производишь не ты. Не стоит быть уверенным в том, что  параметры щетки постоянны во времени - может и выстрелить.


 
checkmate-maker   (2008-10-14 13:25) [6]


> Сергей М.

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


 
Slym ©   (2008-10-14 13:31) [7]

checkmate-maker   (14.10.08 13:25) [6]
я застрелил тебя из щетки


 
clickmaker ©   (2008-10-14 13:40) [8]

> функция GetPtr параметризована(мои извинения, что не обрисовал
> это сразу), возвращает указатели на разные процедуры(функции)

каждый раз с разным числом параметров?


 
Сергей М. ©   (2008-10-14 14:00) [9]


> checkmate-maker   (14.10.08 13:25) [6]


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


 
checkmate-maker   (2008-10-14 14:04) [10]


> каждый раз с разным числом параметров?

Совершенно верно. По сути дела в зависимости от параметра возвращает разные процедуры. Например если параметр = 0, то это процедура без параметров, если = 1, то с одним. Понимаю, что в зависимости от параметра следует использовать разное объявление переменной f, но хочется подстраховаться перед вызовом f, хочется знать, на процедуру с каким числом и каких параметров воззвращен указатель.
 Надеюсь, большой текст не затуманит мысль.
 Спасибо.


 
clickmaker ©   (2008-10-14 14:09) [11]

> Например если параметр = 0, то это процедура без параметров,
> если = 1, то с одним

заведи массив указателей на процедуры
параметр сделай индексом в нем


 
oxffff ©   (2008-10-14 14:22) [12]


> возвращает указатели на разные процедуры(функции) - Что
> делать в этом случае?


Контракт этих процедур совпадает?


 
Сергей М. ©   (2008-10-14 14:24) [13]


> хочется подстраховаться перед вызовом f


А чего собственно ты боишься, коли о страховке задумался ?


 
checkmate-maker   (2008-10-14 14:37) [14]


> А чего собственно ты боишься, коли о страховке задумался
> ?


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

> Возвратив pointer ты сознательно "обезличил" подпрограмму.

 Видимо, придется пользоваться try..except.


 
oxffff ©   (2008-10-14 14:41) [15]


> По сути дела в зависимости от параметра возвращает разные
> процедуры. Например если параметр = 0, то это процедура
> без параметров, если = 1, то с одним. Понимаю, что в зависимости
> от параметра следует использовать разное объявление переменной
> f, но хочется подстраховаться перед вызовом f, хочется знать,
>  на процедуру с каким числом и каких параметров воззвращен
> указатель.


Этот подход совершенно не выразителен и небезопасен.
Если возвращаемые функции известны в Compile time, то тебе нужно сделать

TGetProc<T>=function GetProc(idx:integer):T;
TMyProc2 = procedure(a0: Integer; a1:String); stdcall;
TMyProc3 = procedure(a0: Integer; a1:String;a2:String;); stdcall;

a:TGetProc<TMyProc2>;
a:TGetProc<TMyProc3>;
begin

//Сделать жесткое присваивание.
Честно говоря не пробовал но Barry использует "очень жесткое" при generics.Collections, а точнее
a:= @GetPtr
b:=@GetPtr

a(2)(1,"2")
b(3)(1,"2","3")

Надеюсь идею понял. :)


 
oxffff ©   (2008-10-14 14:46) [16]


> checkmate-maker   (14.10.08 14:37) [14]


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

UnyCall=class
procedure UnyCall;virtual;abstract;
end;

UnyCall2=class(UnyCall)
protected
a:integer;b:string;
procedure UnyCall;virtual;abstract;
constructor (a:integer;b:string);
procedure UnyCall;override;
end;

constructor (a:integer;b:string);
begin
self.a:=a;
self.b:=b;
end;

procedure UnyCall;override;
begin
use A,b here
end;

Вызов такой.

var a:UnyCall;

a:=UnyCall2.create(1,"2")

a.unycall();
...

Идея понятна?


 
oxffff ©   (2008-10-14 14:47) [17]


> a:TGetProc<TMyProc2>;
> a:TGetProc<TMyProc3>;


> a:TGetProc<TMyProc2>;
> b:TGetProc<TMyProc3>;


 
Сергей М. ©   (2008-10-14 14:50) [18]


> Видимо, придется пользоваться try..except


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

Может быть, есть резон обратить взор на ole-автоматизацию и диспинтерфейсы ?


 
oxffff ©   (2008-10-14 14:51) [19]


> Честно говоря не пробовал но Barry использует "очень жесткое"
> при generics.Collections.defaults, а точнее


cм.
Generics.Defaults;

функция создания объекта нештатными средствами.
function MakeInstance(vtable: Pointer; sizeField: Integer): Pointer;


 
oxffff ©   (2008-10-14 14:58) [20]

Могу еще предложить вариант

IGetParameterCallBack=interface
function GetParameter(idx:integer,var param):boolean;
end;

TUnyProc=function (const GetParameter:IGetParameterCallBack):integer;

function MyProc(const GetParameter:IGetParameterCallBack):integer;
var a:integer;
    b:string;
begin
if not GetParameter.GetParameter(1,a) then showmessage("No params");
if not GetParameter.GetParameter(2,b) then showmessage("No params");
....
end;


 
Сергей М. ©   (2008-10-14 14:58) [21]


> oxffff


Какие контракты, какие функторы ?
Ты сейчас с кем разговариваешь ?)
У человека D7, а ты ему про функторы да про сфинктеры)


 
checkmate-maker   (2008-10-14 15:00) [22]


> Параметры из объекта окружения, так называемый функтор.


> Идея понятна?

Спасибо, но непонятно как связать построенную Вами ООМ c моей задачей.
Объявлять методы в одном классе, а реализовывать в его наследниках я, к счастью, умею.
Спасибо за термин "функтор"  - почитаю.


 
oxffff ©   (2008-10-14 15:00) [23]


> Сергей М. ©   (14.10.08 14:58) [21]


oxffff ©   (14.10.08 14:46) [16]
oxffff ©   (14.10.08 14:58) [20]

Штатно реализуются в D7.

P.S. Жаль что вы только про сфинкторы знаете. :)


 
Сергей М. ©   (2008-10-14 15:09) [24]


> oxffff ©   (14.10.08 15:00) [23]

Я про [15])


 
oxffff ©   (2008-10-14 15:10) [25]


> Сергей М. ©   (14.10.08 15:09) [24]


А...
Дык я стараюсь не ограничиваться одним вариантом решения. :)


 
Сергей М. ©   (2008-10-14 15:16) [26]


> checkmate-maker   (14.10.08 15:00) [22]


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


 
oxffff ©   (2008-10-14 15:23) [27]


> Сергей М. ©   (14.10.08 15:16) [26]


Могу упростить решение до минимума.

TCallContext=class
end;

TCallContextIntegerAndString=class
a:integer;
b:string;
end;

procedure Myfunc(a:TCallContext);
begin
if a is TCallContextIntegerAndString then
 else
 begin
 Showmessage("Not valid context");
 exit;
 end;
end;


 
checkmate-maker   (2008-10-14 15:29) [28]

Спасибо Всем за участие в дискуссии.
Воспользоюсь тем, что параметры процедур в библиотеке храняться в БД(об этом я промолчал намеренно - без обид), создам фунцию, следующего вида
function MayConnectToFunc(aFuncIdent: WideString; aParamArray: array of String; aIsFunc: Boolean = False): Boolean;
здесь aFuncIdent - точка входа в процедуру(параметр, который я использую при вызове GetPtr, описанный выше);
aParamArray - массив с типами параметров в согласовании с порядком следования; Также можно использовать TStringList.
aIsFunc-флажок, что это функция и последнее значение в массиве - это тип возвращаемого значения.


 
Сергей М. ©   (2008-10-14 15:32) [29]


> oxffff ©   (14.10.08 15:23) [27]


Не пойдет.
При любом чихе править придется и вызываемую и вызывающую сторону.


 
oxffff ©   (2008-10-14 15:33) [30]


> checkmate-maker   (14.10.08 15:29) [28]


Главное, чтобы работало. :)


 
oxffff ©   (2008-10-14 15:36) [31]


> Сергей М. ©   (14.10.08 15:32) [29]


Дык, и в вашем случае тоже самое.
В любом случае при расширении функции придется менять ее реализацию и место вызова. Хотите сказать что в вашем случае не так?


 
Сергей М. ©   (2008-10-14 15:41) [32]


> checkmate-maker   (14.10.08 15:29) [28]


Т.е. ты таки движешься по пути изобретения велосипеда)

Тогда уж, чтобы меньше возни было и для пущей универсальности:

function CallProcByName(AProcName, AParams: OleVariant): OleVariant; safecall;


 
Сергей М. ©   (2008-10-14 15:44) [33]


> oxffff ©   (14.10.08 15:36) [31]


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


 
oxffff ©   (2008-10-14 15:46) [34]


> Сергей М. ©   (14.10.08 15:41) [32]


Этот вариант самый естественный, так же как и array of const.
Но с точки зрения эффективности, не самый удачный.
Поскольку большие дополнительные издержки.

>Т.е. ты таки движешься по пути изобретения велосипеда)

Я к счастью не Хейлсберг, но ответил бы так же как и он всегда отвечал на этот вопрос.


 
Сергей М. ©   (2008-10-14 15:55) [35]


> oxffff ©   (14.10.08 15:46) [34]


> с точки зрения эффективности, не самый удачный


А что делать ? Издержки интерпретации)

Хочешь универсальности и безопасности - плати за них сквозным снижением производительности..

По поводу же велосипеда - это ж не тебе было адресовано, а автору)


 
oxffff ©   (2008-10-14 15:56) [36]


> А реализацию вызывающего кода - нет.


Ведь лукавите.

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

То есть ваше
a:variant;
begin

a.SomeMethod(1,2,"222");
->
a.SomeMethod(1,2,"222",10...);


И в моем случае

TCallContextIntegerAndString.create(1,2,"222");
TCallContextOther.create(1,2,"222",10....);


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


 
Сергей М. ©   (2008-10-14 16:16) [37]


> oxffff ©   (14.10.08 15:56) [36]


Да нафига мне в вызывающем коде что-то проверять перед вызовом ?)
Пусть этим занимается вызываемый код при вызове !

Ты же, вводя в адр.строку браузера URL, не проверяешь ведь ее актуальность и корректность с т.з., например, наличия и кол-ва параметров ?

Не проверяешь.

И браузер тоже не проверяет - ему это тоже нафих не надо, пусть проверкой занимается сам сервер.

Было:

someproto://somedomain/someresource?param1=A?param2=B

запрос успешно выполнялся, сервер возвращал рез-т, браузер исправно его представлял юзеру.

После того как "отцы" ресурса с какого-то бодуна взяли да добавили обязательный 3-й параметр при запросах к ресурсу, стало:

someproto://somedomain/someresource?param1=A?param2=B?param3=C

А я ничего не зная об этом по-прежнему запрашиваю с 2-мя параметрами.

Кто ответственен за возможную коррекцию моей ошибки или корректный отлуп запроса ?
Разумеется, сервер (вызываемый код) ! Не браузер же (вызывающий код) !
Иначе на каждый чих "отцов" придется переписывать каждый браузер)


 
Сергей М. ©   (2008-10-14 16:33) [38]


> oxffff


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


 
Slym ©   (2008-10-14 16:42) [39]

Тогда проще
type
 TProc=procedure(Params:array of const);

и типы разные и переменное число параметров


 
Сергей М. ©   (2008-10-14 16:51) [40]


> Slym ©   (14.10.08 16:42) [39]


А как результат возвращать, если это функция ?


 
oxffff ©   (2008-10-14 17:18) [41]


> А я ничего не зная об этом по-прежнему запрашиваю с 2-мя
> параметрами.


Ну дык, а что мешет наследовать TCallContextOther  от TCallContextIntegerAndString?
Где я ошибаюсь?


 
oxffff ©   (2008-10-14 17:20) [42]


> Пусть этим занимается вызываемый код при вызове !


Он у меня как раз этим и занимается.


 
Сергей М. ©   (2008-10-14 17:22) [43]


> Где я ошибаюсь?


Этот "наследник" д.б. известен как вызывающей, так и вызываемой стороне.


 
Сергей М. ©   (2008-10-14 17:32) [44]


> oxffff


Ключевое здесь это:

> параметры процедур в библиотеке храняться в БД


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


 
oxffff ©   (2008-10-14 19:55) [45]


> Сергей М. ©   (14.10.08 17:22) [43]
>
> > Где я ошибаюсь?
>
>
> Этот "наследник" д.б. известен как вызывающей, так и вызываемой
> стороне.


Почему должен быть?

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

Вариант первый использование наследника базового функционала.

То есть TCallContextBase(базовый набор) и TCallContextExtended(расширенный набор), где TCallContextExtended=class(TCallContextBase) в
вызываемом коде стоит первая проверка

TCallContextBase=class
a:integer;
b:string;
end;

TCallContextExtended=class(TCallContextBase)
с:variant;
constructor create(a..,b..,c..)
end;

Вызываемая функция
function Workfunc(context:TCallContextBase):...
begin
if context is TCallContextExtended then
           else
          if context is TCallContextBase then
          begin
          //если появились какие либо обязательные параметры для старых  
          вызовов инициализируем их по умолчанию
          end
          else exit;
end;

Вызывающая функция
Workfunc(TCallContextBase.create(1,"2"))

Ничего менять не нужно для вызывающей стороны.


 
oxffff ©   (2008-10-14 19:59) [46]


> Сергей М. ©   (14.10.08 16:16) [37]
>
> > oxffff ©   (14.10.08 15:56) [36]
>
>
> Да нафига мне в вызывающем коде что-то проверять перед вызовом
> ?)
> Пусть этим занимается вызываемый код при вызове !
>
> Ты же, вводя в адр.строку браузера URL, не проверяешь ведь
> ее актуальность и корректность с т.з., например, наличия
> и кол-ва параметров ?
>
> Не проверяешь.
>
> И браузер тоже не проверяет - ему это тоже нафих не надо,
>  пусть проверкой занимается сам сервер.
>
> Было:
>
> someproto://somedomain/someresource?param1=A?param2=B
>
> запрос успешно выполнялся, сервер возвращал рез-т, браузер
> исправно его представлял юзеру.
>
> После того как "отцы" ресурса с какого-то бодуна взяли да
> добавили обязательный 3-й параметр при запросах к ресурсу,
>  стало:
>
> someproto://somedomain/someresource?param1=A?param2=B?param3=C
>
> А я ничего не зная об этом по-прежнему запрашиваю с 2-мя
> параметрами.
>
> Кто ответственен за возможную коррекцию моей ошибки или
> корректный отлуп запроса ?
> Разумеется, сервер (вызываемый код) ! Не браузер же (вызывающий
> код) !
> Иначе на каждый чих "отцов" придется переписывать каждый
> браузер)


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


 
Сергей М. ©   (2008-10-14 20:01) [47]


> у меня вызывающий код ничего не проверяет


Дык о чем тогда базар ?)

Читай еще раз и внимательно:


> Как до вызова f определить корректность параметров?


 
oxffff ©   (2008-10-14 20:15) [48]


> Дык о чем тогда базар ?)



> После того как "отцы" ресурса с какого-то бодуна взяли да
> добавили обязательный 3-й параметр при запросах к ресурсу,
>  стало:
> someproto://somedomain/someresource?param1=A?param2=B?param3=C
> А я ничего не зная об этом по-прежнему запрашиваю с 2-мя
>
> параметрами.
>  
> Кто ответственен за возможную коррекцию моей ошибки или
>
> корректный отлуп запроса ?
> Разумеется, сервер (вызываемый код) ! Не браузер же (вызывающий
>
> код) !
> Иначе на каждый чих "отцов" придется переписывать каждый
>
> браузер)


Да почему базар?  

А у меня встречный вопрос. Вы [37] спрашивали про третий обязательный параметр. В моем способе я показал как выкрутиться для старого кода.

А в предложенном вами способе разве не придется менять код вызывающего
для дополнительного обязательного параметра. А то вы тут намекаете на негибкость предложенного мною метода, а про свой, что скажете, повторяю обязательного. В вашем же случае тоже придется менять код вызывающего.
Может он все таки не такой уж и обязательный, чтобы старый Idispatch код работал?
;)


 
oxffff ©   (2008-10-14 20:19) [49]


> Читай еще раз и внимательно:
>
>
> > Как до вызова f определить корректность параметров?


В run time только TypeInfo поможет до вызова стороне клиента.


 
oxffff ©   (2008-10-14 23:44) [50]

Я тут слегка усовершенствовал свою версию
отцепив привязку от провайдера контекста.

TContex=record
a,b:integer;
c:string;
end;

IContextProvider=interface
["{C98B6149-BCCA-432D-B9B8-9066ACDAA253}"]
function AssignContext(var Contex:TContex):boolean;
end;

TContextProvider=class(TInterfacedObject,IContextProvider)
protected
function AssignContext(var Contex:TContex):boolean;virtual;abstract;
end;

TContextProvider1=class(TContextProvider)
protected
DataA,DataB:integer;
function AssignContext(var Contex:TContex):boolean;override;
public
constructor create(DataA,DataB:integer);
end;

TContextProvider2=class(TContextProvider)
protected
DataA:integer;
DataC:string;
function AssignContext(var Contex:TContex):boolean;override;
public
constructor create(DataA:integer;DataC:string);
end;

function WorkFunc(Unknown:IUnknown):boolean;
var Contex:TContex;
   ContextProvider:IContextProvider;
begin
try
ContextProvider:=Unknown as IContextProvider;
//Устанавливаем значение по умолчанию
Contex.a:=0;
Contex.b:=0;
Contex.c:="Значение по умолчанию";
//Получаем данные контекста вызова
if ContextProvider.AssignContext(Contex) then
     begin
     showmessage("a="+inttostr(Contex.a)+" b="+inttostr(Contex.b)+" c="+Contex.c);
     result:=true;
     exit;
     end;
except
end;
result:=False;
end;

{ TContextProvider1 }

function TContextProvider1.AssignContext(var Contex: TContex): boolean;
begin
Contex.a:=DataA;
Contex.b:=DataB;
end;

constructor TContextProvider1.create(DataA, DataB: integer);
begin
self.DataA:=DataA;
self.DataB:=DataB;
end;

{ TContextProvider2 }

function TContextProvider2.AssignContext(var Contex: TContex): boolean;
begin
Contex.a:=DataA;
Contex.c:=DataC;
end;

constructor TContextProvider2.create(DataA:integer;DataC:string);
begin
self.DataA:=DataA;
self.DataC:=DataC;
end;

{ TContextProvider }

procedure TForm1.Button1Click(Sender: TObject);
begin
WorkFunc(TContextProvider1.create(1,2));
WorkFunc(TContextProvider2.create(1,"Параметр задан"));
end;


 
Сергей М. ©   (2008-10-15 13:10) [51]


> oxffff


Автор уже забыл свою изначальную затею как страшный сон , а ты все мысли плодишь на эту тему)

Обрати внимание - вопрос вырос из указателя.

А в [28] автор ортогонально развернул оглобли, так что от первоначальная идеи не осталось и следа.

Пилите, Шура, пилите !)


 
oxffff ©   (2008-10-15 14:14) [52]


> Сергей М. ©   (15.10.08 13:10) [51]


Мысли рождаются сами собой.
Например [50] пришла в голову в ванной, когда я мылся после тяжелой физической работы(ремонт дома делаю). Неоднократно замечено, что физический труд очень полезно. Столько мыслей сразу.

:)



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

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

Наверх





Память: 0.61 MB
Время: 0.013 c
2-1223642970
Gurd
2008-10-10 16:49
2008.11.23
Присвоить событию компонента функцию


15-1222095887
Городской Шаман
2008-09-22 19:04
2008.11.23
Опрос зарплата и работа.


4-1200835818
brotherirk
2008-01-20 16:30
2008.11.23
Свернутое чужое окно


2-1223575339
Первокласник Вася
2008-10-09 22:02
2008.11.23
Свойство SQL Query1


2-1224145722
SpiderWho
2008-10-16 12:28
2008.11.23
Отладка DLL





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
Английский Французский Немецкий Итальянский Португальский Русский Испанский