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

Вниз

помогите определиться с форматом функции   Найти похожие ветки 

 
dev888   (2011-10-26 15:15) [0]

нужно написать функции (API), которые были бы максимально удобными для перемещения по товарам магазина (GoToFirstProduct, GoToNextProduct, GoToPreviousProduct, GoToLastProduct). Нужно предусмотреть возврат идентификатора товара, а также возможность перемещения к товару. Как это лучше сделать, чтобы не писать еще и функции EOF, BOF? Пока что думаю так:


function GoToFirstProduct(out ProductId: Integer): Boolean;
function GoToLastProduct(out ProductId: Integer): Boolean;


- Если товары в магазине отсутствую, функция будет возвращать False


function GoToNextProduct(out ProductId: Integer): Boolean;
function GoToPreviousProduct(out ProductId: Integer): Boolean;


- если товары в магазине отсутствую, либо текущий товар был последним (GoToNextProduct) / первым (GoToPreviousProduct) функция будет возвращать False.

но:

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


AdapterStore.GoToFirstProduct(ProductId);
// действие на товаром
while AdapterStore.GoToNextProduct(ProductId) do
begin
 // дейсвие над товаров
end;


 
Медвежонок Пятачок ©   (2011-10-26 15:21) [1]

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

type
TEnumCallbackFunc = function(AProduct : integer; var bMore : boolean; ACustomData : Pointer) : boolean;

function enum_products(ACallBackFunc : TEnumCallbackFunc; ACustomData : Pointer) : integer;
var bMore : boolean;
begin
Result := 0; bMore := True;
while product_exists do
 begin
  if ACallBackFunc(ProductID,bMore,ACustomData) then inc(Result);
  if not bMore then break;
 end;
end;


 
Ega23 ©   (2011-10-26 15:41) [2]

First;
Next;
Eof;


Этого достаточно.
И будет тогда что-то вроде

with AdapterStore do
begin
 First;
 while not Eof do
   try
     .... Работаем с ID, также выносим его в public
   finally
     Next;
   end;
end;


 
Омлет ©   (2011-10-26 15:42) [3]

> Нужно предусмотреть возврат идентификатора товара, а также возможность перемещения к товару.

А, может, сразу ссылку на объект возвращать?


 
Медвежонок Пятачок ©   (2011-10-26 15:44) [4]

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


 
Ega23 ©   (2011-10-26 15:46) [5]


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


Без индексирования или упорядочивания у тебя и так и так будет цикл перебора.


 
Медвежонок Пятачок ©   (2011-10-26 15:50) [6]

у меня цикл перебора есть.
но он один и упрятан в то, что у автора называется "апи".

а пользователю апи требуется всего лишь реализовать различные варианты
TEnumCallbackFunc

b затем вызвать enum_products c нужным колбаком.


 
Труп Васи Доброго ©   (2011-10-26 15:55) [7]


> Нужно предусмотреть возврат идентификатора товара, а также
> возможность перемещения к товару. Как это лучше сделать,
>  чтобы не писать еще и функции EOF, BOF?

Чувак, ты не поверишь, но я слышал, есть такая маза крутая - базы данных. Говорят вставляет не по детски!!!


 
dev888   (2011-10-26 16:07) [8]


> чувак, ты не поверишь, но я слышал, есть такая маза крутая
> - базы данных. Говорят вставляет не по детски!!!


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


> First;
> Next;
> Eof;
>
>
> Этого достаточно.


у меня одно из требований в тз это присутствие в имени функций слова "product". как тогда обозвать функцию-обертку над  "eof"


 
sniknik ©   (2011-10-26 16:07) [9]

редко кто хочет учиться... зато "велосипедотворцов" пруд пруди...


 
sniknik ©   (2011-10-26 16:08) [10]

> это присутствие в имени функций слова "product"
ну так...
productFirst;
productNext;
productEof;

Этого достаточно.


 
sniknik ©   (2011-10-26 16:10) [11]

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


 
Медвежонок Пятачок ©   (2011-10-26 16:11) [12]

type
TEnumCallbackFunc = function(ADataSet : TDataSet; var bMore : boolean; ACustomData : Pointer) : boolean;

function enum_dataset(ADataSet : TDataSet; ACallBack : TEnumCallBack; ACustomData : boolean; AFromTheVeryBegining : boolean = false) : integer;
var bMore : boolean;
begin
Result := 0; bMore := True;
if AFromTheVeryBegining then ADataSet.First;
while not ADataSet.Eof do
 begin
  if ACallBack(ADataSet,bMore,ACustomData) then Inc(result);
  if not bMore then Break;
  ADataSet.Next;
 end;
end;


 
Медвежонок Пятачок ©   (2011-10-26 16:15) [13]

а чем она должна/будет лучше чем прямые методы работы с таблицей?

А прикинь, если у тебя есть вот такой грид:

nCount := MyGrid.BatchProcess(MyDeleteCallBack,ACustomData);
nCount := MyGrid.BatchProcess(MyUpdateCallBack,ACustomData);
.....
nCount := MyGrid.BatchProcess(MyExportCallBack,ACustomData);

причем BatchProcess обрабатывает либо текущую, либо всё, либо только мультиселект если он есть.


 
sniknik ©   (2011-10-26 16:17) [14]

> причем BatchProcess обрабатывает либо текущую, либо всё, либо только мультиселект если он есть.
т.е. пишется замена SQL, так?


 
Ega23 ©   (2011-10-26 16:18) [15]


> у меня одно из требований в тз это присутствие в имени функций
> слова "product"


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


 
sniknik ©   (2011-10-26 16:19) [16]

> т.е. пишется замена SQL, так?
тогда + вопрос
а чем она должна/будет лучше чем прямые методы работы с таблицей?


 
Медвежонок Пятачок ©   (2011-10-26 16:21) [17]

т.е. пишется замена SQL, так?

нет не так.
пишется избавление от рутины по созданию циклов в групповых операциях.
внутри функций обратного вызова необязательно идет непосредственная обработка датасета.
в них может собираться инфа для (например) формирования sql скрипта который выполняется после BatchProcess если тот вернул > 0
Но не только это.
Это вообще некая групповая обработка записей. Любая.


 
sniknik ©   (2011-10-26 16:38) [18]

> необязательно идет непосредственная обработка датасета.
универсальность "карается"  лишением персонализированных "плюшек" объектов с которыми работа. делается "обрезка" по наименее работоспособному.

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

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


 
Медвежонок Пятачок ©   (2011-10-26 16:46) [19]

а кто сказал, что нет отключения контролов?
есть.
и эта моя универсальность ничем не карается абсолютно.
метод всего лишь тупо перебирает записи
либо с текущей, либо с начала.
либо все, либо отобранные.
на каждой итерации вызывает переданный колбак.
что он делает - методу неведомо.
вернулся тру - крутим счетчик успешных обработок.
если внутри колбака ошибка или исключение, или некое условие, то пользователь грида (программер) может выкинуть MessageDlg c вопросом "дальше будем продолжать?" и по ответу юзера выставляет флаг bMore
если он фалс делаем брейк из цикла.

Все.

Вот типичный пример:
Форма с гридом и три кнопки: add,edit,delete
С первыми двумя все ясно, а под третьей как раз место вызова BatchProcess

Ибо юзер может хотеть удалить не одну строку, а все. Или все отобранные мультиселектом.


 
Труп Васи Доброго ©   (2011-10-26 16:50) [20]


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

Я вот не пойму, нафига ты задаёшь вопросы под неизвестными никами, а потом сам же начинаешь критиковать ответы? Тебе от себя спросить стыдно? Послушай умного человека sniknik правильно говорит, не надо велосипед делать, есть SQL, есть СУБД, учи и пользуйся.


 
Медвежонок Пятачок ©   (2011-10-26 16:54) [21]

Я вот не пойму, нафига ты задаёшь вопросы под неизвестными никами

Гражданин, не надо гнать херню.


 
sniknik ©   (2011-10-26 17:01) [22]

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

т.е. в одном случае есть, в другом нет, но нужно универсально... значит возможность должна быть "урезана" ибо в одном варианте перестает работать а в другом просто добавляет тормоза.


 
sniknik ©   (2011-10-26 17:05) [23]

> Или все отобранные мультиселектом.
ну и вот еще кстати. "мультиселект" это в гриде имеется ввиду? с шифтом или контролом отмечено несколько записей. ???
не работает при отключенных. некуда ссылаться, даталинка нет.


 
Медвежонок Пятачок ©   (2011-10-26 17:09) [24]

>Послушай умного человека sniknik

Ты лучше меня послушай, так как sniknik хоть и умный человек, но совершенно не оценил всю красоту и изящество метода.

Речь же идет об элементарных принпах разработчика: разделение кода

Представь, что ты в команде. К вам "снаружи" пришла некая регулярная структура данных.
Одному в команде надо сделать по ней инсерты, другому надо сделать отчет, третьему надо сформировать html по пришедшим данным.

Мой метод:
Я раскуриваю структуру и пишу функцию енумерации структуры и публикую ее для команды.
А вся команда не вникая в сложности структуры пишет свои пользовательские функции обратного вызова и сосредотачивается на непосредственно своей задаче.
Всем сухо и комфортно.

Другой метод:

Вся команда изучает структуру, учится ее читать, затем все пишут свои циклы перебора вперемешку с обрабокой данных.
Разницу улавливаешь?

не работает при отключенных. некуда ссылаться, даталинка нет.

Ты поверь, что у меня работает. Уже лет пять.
Просто и мультиселект у меня не такой как у всех.
/* Расширенный и дополненный EhLib"ский. Который SQL-ориентированный */


 
Медвежонок Пятачок ©   (2011-10-26 17:11) [25]

например "обрабатываешь" связанные таблицы

Я таким механизмом обрабатываю все что имеет регулярную структуру.
датасеты, ixmldomnodelist"ы, файлы, списки и прочую дребедень.


 
sniknik ©   (2011-10-26 17:19) [26]

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


 
sniknik ©   (2011-10-26 17:21) [27]

> частная реализация
ничего плохого, сам для себя и не такое пишу... но тут то "для программиста", т.е. не для автора топика. :)


 
Медвежонок Пятачок ©   (2011-10-26 17:26) [28]

Вот стандартный мультиселект. Все отключено, но все работает.

DBGrid1.DataSource.DataSet.DisableControls;
for i := 0 to Pred(DBGrid1.SelectedRows.Count) do
 begin
  if DBGrid1.DataSource.DataSet.BookmarkValid(Pointer(DBGrid1.SelectedRows.Items[i]))  then
   begin
    DBGrid1.DataSource.DataSet.GotoBookmark(Pointer(DBGrid1.SelectedRows.Items[i]));
    ShowMessage(Table1.Fields[0].Value);
   end;
 end;

а вот дал ты свой метод "наружу"из своей среды обитания, и кто-то не пользующийся Eh-ом

Сначала я дал "наружу" расширенный EhDBGrid.
Команда им пользуется.
Затем я дал им новый метод этого грида.
Это же метод грида. Просто частный случай применения.
Только потому, что у конечного пользователя приложения есть механизм отбора записей, а у программиста нет удобного механизма обработки.
Никакая универсальность не пострадала.
Так как метод делает ровно то, что делает каждый, когда ему надо перебрать записи.


 
sniknik ©   (2011-10-26 17:49) [29]

> что делает каждый
ну давай посмотрим на реальном коде. сам скажи, что тут "оптимизируемо" твоим методом.

procedure TPaymForm.FillTemplate;
var
 i: integer;
 fID, fCode, fName, fValue: TField;
 Template: TTemplate;
begin
 sPhone:= "";
 
 for i:= 0 to comBoxTemplate.Items.Count - 1 do
   if comBoxTemplate.Items.Objects[i] <> nil then begin
     comBoxTemplate.Items.Objects[i].Free;
     comBoxTemplate.Items.Objects[i]:= nil;
   end;
 comBoxTemplate.Items.Clear;

 if DMod.DSTemplate.Active then
   with DMod.DSTemplate do begin
     fID   := FieldByName("Usr");
     fCode := FieldByName("Code");
     fName := FieldByName("Name");
     fValue:= FieldByName("Value");

     Sort    := "Name";
     Filter  := "Code=" + IntToStr(Code);
     Filtered:= true;

     First;
     while not Eof do begin
       if fCode.AsInteger = Code then begin
         Template:= TTemplate.Create;
         Template.ID   := fID.AsInteger;
         Template.Code := fCode.AsInteger;
         Template.Name := fName.AsString;
         Template.Value:= fValue.AsString;

         comBoxTemplate.Items.AddObject(fName.AsString, Template);
       end;
       Next;
     end;
   end;

 if comBoxTemplate.Items.Count > 0 then begin
   comBoxTemplate.Enabled:= true;
   comBoxTemplate.Color  := clWindow;
 end else begin
   comBoxTemplate.Enabled    := false;
   comBoxTemplate.ParentColor:= true;
 end;
end;


 
Медвежонок Пятачок ©   (2011-10-26 18:04) [30]

если по принципу "можно ли вообще", то можно.
заменить два цикла.

enum_list(comBoxTemplate.Items,DeleteCallback,nil);
enum_dataset(DMod.DSTemplate,Template,nil);

может и не нужно, но зато можно


 
sniknik ©   (2011-10-26 20:17) [31]

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

не, было бы для себя, да бога ради. сам пишешь, сам понимаешь как/для чего написал/как можно использовать... а вот как "для программиста", т.е. "на вынос", не стоит того "удобство" (да удобство это только в глазах писавшего).

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


 
Медвежонок Пятачок ©   (2011-10-26 20:53) [32]

да нет никаких жестких рамок.

есть банальное желание повторного использования кода и оно реализовано.

я стопицот раз писал вот так, до того как решил покончить с этим:

iList := xdoc.selectNodes("//..........");
for i := 0 to Pred(iLst.length) do
begin
 <что-то_там_в_носу>;
end;

теперь я больше так не делаю. ну жалко мне времени.

nCount := enum_xml_nodes(xdoc,"//.....", MyCallback, ACustomData);

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


 
sniknik ©   (2011-10-26 21:34) [33]

> И все! И никаких ограничений, никаких рутинных циклов.
да ну? я вот например xml использую (когда сам, а не навязан. когда внутри программы, а не извне пришло) только при сложной структуре, с "плоским" видом проще и понятнее рекордсет. это к чему, у меня "рутинного цикла" для xml-я не бывает, т.к. вложенности/"деревья" пишу обработку рекурсией в общем.

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


 
Медвежонок Пятачок ©   (2011-10-26 21:51) [34]

у меня он справляется и с рекурсией если надо и без.

например с рекурсией:

emun_nodes(xdoc,xpath_str_1,firstcallback);

firstcallback(anode : ixmldomnode,......) : boolean;
begin
...
enum_nodes(anode,"./somechild_nodes/....",secondcallback,....)
...
end;

и так далее ...


 
Медвежонок Пятачок ©   (2011-10-26 21:53) [35]

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


 
sniknik ©   (2011-10-26 22:38) [36]

> и он на меня работает.
со стороны мне кажется, что это ТЫ на него работаешь...  копеечная экономия -
вместо 3х строчек цикла
while ... do begin
 Next;
end;
писать 1 строчку вызова процедуры. но с "довеском" в виде написания обработчика в виде другой процедуры, тратя гораздо больше строк.
теряешь в понятности (код не по месту, а куда то вынесен), но тешишь гордость используя везде (ну как же. такое оригинальное, "красивое", решение. бессмысленное но ведь сам придумал! (хотя на самом деле это не оригинальная идея, а такое же в каких то виндовых сетевых функциях видел, использовал... не помню где. главное в винде это есть, и было давным давно (а это было в Симпсонах! ха-ха ;)...)).


 
Медвежонок Пятачок ©   (2011-10-26 22:42) [37]

в понятности как раз теряешь ты.
к тебя в одной процедуре и перебор-итерация чего-то там и одновременно обработка/формирование чего то здесь.

у меня итерация в одном месте, в другом месте только обработка.


 
sniknik ©   (2011-10-26 23:01) [38]

> в понятности как раз теряешь ты.
вот моя часть кода, из приведенного, что может быть "оптимизирована"
    First;
    while not Eof do begin
      if fCode.AsInteger = Code then begin
        Template:= TTemplate.Create;
        Template.ID   := fID.AsInteger;
        Template.Code := fCode.AsInteger;
        Template.Name := fName.AsString;
        Template.Value:= fValue.AsString;

        comBoxTemplate.Items.AddObject(fName.AsString, Template);
      end;
      Next;
    end;


обработка там же где подготовительные/связанные действия

вот что будет у тебя
подготовка, связанное тоже самое а вместо этого "рабочего цикла" -

enum_dataset(DMod.DSTemplate,Template,nil);

сам цикл в отдельном модуле, а обработчик в 3м месте, гда там будет описана процедура Template... ну, если тебе так понятно. ну что тут сказать... а кому нибудь другому ты свой код читать давал? чтобы совсем со стороны.


 
Медвежонок Пятачок ©   (2011-10-26 23:08) [39]

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

представил?
правильно, это будет копипаст.

а уменя колбэк и я его могу вызвать откуда мне вздумается и сколько раз мне вздумается.


 
sniknik ©   (2011-10-27 08:04) [40]

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

> там где одна строка датасета
никаких изменений, цикл по одной строке работает также как по списку...

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

> правильно, это будет копипаст.
ага, копипаст организации цикла - while not Eof do begin Next; end; всегда копипаст. все остальное "по месту" другое.
кстати не задумывался что ВЕСЬ дельфи сплошной копипаст, все слова повторяются... вот например begin end; их же пишем постоянно... ужасть. не думал заменить на свое? избежать "копипаста"? хотя... по большому счету ведь и твоя функция в использовании в другом месте будет с тем же именем... непорядок. копипаст. :)


 
sniknik ©   (2011-10-27 08:10) [41]

p.s. вообще, есть такая фраза, не помню кто сказал - не плодите сущностей...
вот. по моему это вам.


 
Медвежонок Пятачок ©   (2011-10-27 10:26) [42]

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


 
sniknik ©   (2011-10-27 10:59) [43]

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


 
sniknik ©   (2011-10-27 11:07) [44]

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


 
Медвежонок Пятачок ©   (2011-10-27 13:01) [45]

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



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

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

Наверх




Память: 0.61 MB
Время: 0.004 c
2-1319714974
Очень Злой
2011-10-27 15:29
2012.02.05
Можно ли описать запись неизвестного заранее размера


1-1284126038
Rouse_
2010-09-10 17:40
2012.02.05
Проблема с отрисовкой сабитема в ListView


15-1318620676
Rouse_
2011-10-14 23:31
2012.02.05
Хм, задачка...


2-1319547859
TKN
2011-10-25 17:04
2012.02.05
Многострочные заголовки DBGrid


15-1318530903
AlexDn
2011-10-13 22:35
2012.02.05
Wifi





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