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

Вниз

Помогите оптимизировать код (выполнения в консоли)!   Найти похожие ветки 

 
Pavel   (2008-11-29 14:49) [0]

Пытался сделать свою программу для её использования вместо bat-файла. Всё работает, но каждый раз приходится запускать цикл:
procedure TForm1.Button1Click(Sender: TObject);
var
P:TProcessProcParam;
begin
P.ApplicationName := nil;
P.CommandLine := "cmd.exe";
P.DataProc := AddToFormMemo;
GoProcess(P);
end;

И как (при необходимости) выполнить последовательность команд, чтобы они не выполнялись все сразу?!
Код такой:
type
TProcessProcParam =
record
  ApplicationName: PChar;
  CommandLine: PChar;
  DataProc: procedure (Str:PChar);
end;
PProcessProcParam = ^TProcessProcParam;

function ProcessProc(P:PProcessProcParam):DWord; stdcall;
var
StartupInfo: TStartupInfo;
ProcessInfo: TProcessInformation;
SecurityAttributes: TSecurityAttributes;
StdOutPipeRead, StdOutPipeWrite: THandle;
CreateResult: Boolean;
Chars: PChar;
BytesReadFromFile: Cardinal;
BytesReadFromPipe: Cardinal;
WaitResult: Cardinal;
begin
with SecurityAttributes do
begin
  nLength := SizeOf(SecurityAttributes);
  bInheritHandle := True;
  lpSecurityDescriptor := nil;
end;

CreateResult := CreatePipe(StdOutPipeRead, StdOutPipeWrite, @SecurityAttributes, 0);
if not CreateResult then
begin
  P^.DataProc(nil);
  if assigned(P^.ApplicationName) then FreeMem(P^.ApplicationName);
  if assigned(P^.CommandLine) then FreeMem(P^.CommandLine);
  Dispose(P);
  Result := 1;
  Exit;
end;

with StartupInfo do
begin
  FillChar(StartupInfo, SizeOf(StartupInfo), 0);
  cb := SizeOf(StartupInfo);
  dwFlags := STARTF_USESHOWWINDOW or STARTF_USESTDHANDLES;
  wShowWindow := SW_HIDE;
  hStdInput := 0;//StdOutPipeRead2;//GetStdHandle(STD_INPUT_HANDLE);
  hStdOutput := StdOutPipeWrite;
  hStdError := StdOutPipeWrite;
end;

ZeroMemory(@ProcessInfo, SizeOf(@ProcessInfo));

CreateResult := CreateProcess(P^.ApplicationName, P^.CommandLine, nil, nil,
  True, 0, nil, nil, StartupInfo, ProcessInfo);

if not CreateResult then
begin
  P^.DataProc(nil);
  CloseHandle(StdOutPipeRead);
  CloseHandle(StdOutPipeWrite);
  if assigned(P^.ApplicationName) then FreeMem(P^.ApplicationName);
  if assigned(P^.CommandLine) then FreeMem(P^.CommandLine);
  Dispose(P);
  Result := 2;
  Exit;
end;

repeat
  WaitResult := WaitForSingleObject(ProcessInfo.hProcess, 100);
  PeekNamedPipe(StdOutPipeRead, nil, 0, nil, @BytesReadFromPipe, nil);
  if BytesReadFromPipe > 0 then
  begin
    getMem(Chars, BytesReadFromPipe + 2);
    ZeroMemory(Chars, BytesReadFromPipe + 2);

    ReadFile(StdOutPipeRead, Chars^, BytesReadFromPipe, BytesReadFromFile, nil);
    OemToAnsi(Chars, Chars);
    P^.DataProc(Chars);

    FreeMem(Chars);
  end;
until (WaitResult <> WAIT_TIMEOUT) and (BytesReadFromPipe = 0);

P^.DataProc(nil);

CloseHandle(ProcessInfo.hThread);
CloseHandle(ProcessInfo.hProcess);

CloseHandle(StdOutPipeRead);
CloseHandle(StdOutPipeWrite);

if assigned(P^.ApplicationName) then FreeMem(P^.ApplicationName);
if assigned(P^.CommandLine) then FreeMem(P^.CommandLine);
Dispose(P);

Result := 0;
end;

procedure GoProcess(P:TProcessProcParam);
var
PP:PProcessProcParam;
C:DWord;
begin
New(PP);
if assigned(P.ApplicationName) then
begin
  GetMem(PP^.ApplicationName, Length(P.ApplicationName));
  StrCopy(PP^.ApplicationName, P.ApplicationName);
end
else
begin
  PP^.ApplicationName := nil;
end;
if assigned(P.CommandLine) then
begin
  GetMem(PP^.CommandLine, Length(P.CommandLine));
  StrCopy(PP^.CommandLine, P.CommandLine);
end
else
begin
  PP^.CommandLine := nil;
end;
PP^.DataProc := P.DataProc;
CreateThread(nil, 0, @ProcessProc, PP, 0, C);
end;

procedure AddToFormMemo(Str:PChar);
begin
if assigned(Str)
 then Form1.Memo1.Lines.Add(Str)
 else Form1.Memo1.Lines.Add("выполнено");
end;


 
Leonid Troyanovsky ©   (2008-11-29 15:16) [1]


> Pavel   (29.11.08 14:49)  

> Пытался сделать свою программу для её использования вместо
> bat-файла.

Вот отсюда - по-медленней.
Огласи все ТЗ.

--
Regards, LVT.


 
Slym ©   (2008-11-29 15:51) [2]

Pavel   (29.11.08 14:49)
1. какое обилие Диспозов/Фримемов Нюсов и гетмемов... не запутался?
2. DataProc вызывается в другом потоке и Form1.Memo1.Lines.Add(Str) вызовет ерор..
3. Пайпа нужно два... без hStdInput никуда...
4. ReadFile - не процедура, а функция  возвращающая колво прочитанного
5. getMem(Chars, BytesReadFromPipe + 2); - что за магическая двойка?
6. И вообще код в топку...
7. Leonid Troyanovsky ©   (29.11.08 15:16) [1] +1


 
Slym ©   (2008-11-29 15:52) [3]

не Ц++ случаем портируешь?


 
Pavel   (2008-11-29 16:03) [4]

> Leonid Troyanovsky ©   (29.11.08 15:16) [1]
Требуется следующее:
Получить в TMemo результат выполнения в консоли (динамично по мере выполнения), само консольное окно скрыть (дабы не мешало). Пытался сделать, чтобы выполнялось в отдельном потоке, но с возможностью как последовательного выполнения команд, так и нескольких сразу.


 
Leonid Troyanovsky ©   (2008-11-29 16:25) [5]


> Pavel   (29.11.08 16:03) [4]

> Получить в TMemo результат выполнения в консоли (динамично
> по мере выполнения), само консольное окно скрыть (дабы не
> мешало).

Ну, получить - не проблема.
А для передачи строки уже пущенному cmd нужно как-то извращаться:
может быть путем WriteConsoleInput в собс-ручно распределенную
консоль (AllocConsole) или путем PostMessage WM_CHAR
каждого символа строки (& #13) окну консоли.

Проще уж cmd /c command, выполнять по-одной.

--
Regards, LVT.


 
Leonid Troyanovsky ©   (2008-11-29 16:33) [6]


> Pavel   (29.11.08 16:03) [4]

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

А еще проще - пустить telnet service и написать к нему гуи-клиента,
если уж готовые не угодили.

--
Regards, LVT.


 
Andy BitOff ©   (2008-11-29 16:48) [7]

А еще проще посмотреть форум поглубже. Недавно этот вопрос был решен положительно.


 
Leonid Troyanovsky ©   (2008-11-29 16:51) [8]


> Andy BitOff ©   (29.11.08 16:48) [7]

> был решен положительно.

Подкинь, плиз, кейвордов, что-то я это пропустил.

--
Regards, LVT.


 
Andy BitOff ©   (2008-11-29 16:54) [9]

> Leonid Troyanovsky ©   (29.11.08 16:51) [8]

http://delphimaster.net/view/2-1226165105/

Непосредственно ответ:
easy ©   (08.11.08 20:50) [2]
http://dfc.com.ru/?sid=2&id=7&itemid=2


 
Slym ©   (2008-11-29 16:56) [10]

анализируй
uses windows,SysUtils,classes;
type
 TOnStdOut=procedure(Sender:TObject;const Str:string) of object;
 TConsoleThread=class(TThread)
 private
   FCMD:string;
   FCloseHandles:boolean;
   FStdIn:TStrings;
   StdOutStr:string;
   FOnStdOut:TOnStdOut;
   FExitCode:DWord;
 protected
   procedure Execute;override;
   procedure DoStdOut;
 public
   constructor Create(const CMD:string;Commands:TStrings;CloseHandles:boolean=false);reintroduce;
   destructor Destroy; override;
   property ExitCode:DWord read FExitCode;

   property OnStdOut:TOnStdOut read FOnStdOut write FOnStdOut;
 end;

constructor TConsoleThread.Create(const CMD:string;Commands:TStrings;CloseHandles:boolean=false);
var i:integer;
begin
 FCMD:=CMD;
 FCloseHandles:=CloseHandles;
 FStdIn:=TStringList.Create;
 if assigned(Commands) then
 begin
   FStdIn.BeginUpdate;
   try
     for I:= 0 to Commands.Count - 1 do
       FStdIn.AddObject(Commands[I]+#13#10, Commands.Objects[I]);
   finally
     FStdIn.EndUpdate;
   end;
 end;

 inherited Create(false);
end;

destructor TConsoleThread.Destroy;
begin
 FStdIn.Free;
 inherited;
end;

procedure TConsoleThread.DoStdOut;
begin
 if assigned(FOnStdOut) then
   FOnStdOut(self,StdOutStr);
end;

procedure TConsoleThread.Execute;
var
 sa:TSecurityAttributes;
 si:TStartupInfo;
 pi:TProcessInformation;
 ChildStdInWr,ChildStdoutRd:THandle;
 ChildStdoutWr,ChildStdInRd:THandle;
 Tmp1,Tmp2:THandle;
 bytesRead:DWORD;
 p:PChar;
begin
 ChildStdInWr:=0;ChildStdoutRd:=0;
 ChildStdoutWr:=0;ChildStdInRd:=0;
 Tmp1:=0;Tmp2:=0;
 sa.nLength:=sizeof(TSecurityAttributes);
 sa.bInheritHandle:=true;
 sa.lpSecurityDescriptor:=nil;
 try
   if not CreatePipe(ChildStdoutRd,ChildStdoutWr,@sa,0) then
     RaiseLastOSError;
   if not CreatePipe(ChildStdinRd,ChildStdinWr,@sa,0) then
     RaiseLastOSError;
   if not DuplicateHandle(GetCurrentProcess(),ChildStdoutRd,GetCurrentProcess(),@Tmp1,0,Fa lse,DUPLICATE_SAME_ACCESS) then
     RaiseLastOSError;
   if not DuplicateHandle(GetCurrentProcess(),ChildStdinWr,GetCurrentProcess(),@Tmp2,0,Fal se,DUPLICATE_SAME_ACCESS) then
     RaiseLastOSError;
   if ChildStdoutRd<>0 then
     if CloseHandle(ChildStdoutRd) then
       ChildStdoutRd:=0;
   if ChildStdinWr<>0 then
     if CloseHandle(ChildStdinWr) then
       ChildStdinWr:=0;
   ChildStdoutRd:=Tmp1;Tmp1:=0;
   ChildStdinWr:=Tmp2;Tmp2:=0;

   GetStartupInfo(si);
   si.cb:=sizeof(TStartupInfo);
   si.dwFlags:=STARTF_USESTDHANDLES or STARTF_USESHOWWINDOW;
   si.hStdInput:=ChildStdInRd;
   si.hStdOutput:=ChildStdOutWr;
   si.hStdError:=ChildStdOutWr;
   si.wShowWindow:=SW_HIDE;
   if not CreateProcess(nil,PChar(FCMD),nil,nil,true,CREATE_NEW_CONSOLE,nil,nil,si,pi) then
     RaiseLastOSError;
   while not Terminated do
   begin
     if WaitForSingleObject(pi.hProcess,0)=WAIT_OBJECT_0 then
     begin
       GetExitCodeProcess(pi.hProcess,FExitCode);
       CloseHandle(pi.hProcess);
       CloseHandle(pi.hThread);
       Terminate;
     end;
     PeekNamedPipe(ChildStdoutRd,nil,0,nil,@bytesRead,nil);
     if bytesRead>0 then
     begin
       SetLength(StdOutStr,bytesRead);
       if not ReadFile(ChildStdoutRd,PChar(StdOutStr)^,bytesRead,bytesRead,nil) then
         RaiseLastOSError;
       SetLength(StdOutStr,bytesRead);
       OemToChar(PChar(StdOutStr),PChar(StdOutStr));
       Synchronize(DoStdOut);
     end;
     while FStdIn.Count>0 do
     begin
       p:=PChar(FStdIn[0]);
       CharToOem(p,p);
       if not WriteFile(ChildStdinWr,p^,Length(FStdIn[0]),bytesRead,nil) then
         RaiseLastOSError;
       FStdIn.Delete(0);
     end;
     if FCloseHandles and (ChildStdinWr<>0) then
       if CloseHandle(ChildStdinWr) then
         ChildStdinWr:=0;
   end;
 finally
   if ChildStdoutRd<>0 then CloseHandle(ChildStdoutRd);
   if ChildStdoutWr<>0 then CloseHandle(ChildStdoutWr);
   if ChildStdinRd<>0 then CloseHandle(ChildStdinRd);
   if ChildStdinWr<>0 then CloseHandle(ChildStdinWr);
   if Tmp1<>0 then CloseHandle(Tmp1);
   if Tmp2<>0 then CloseHandle(Tmp2);
 end;
end;

end.


 
Leonid Troyanovsky ©   (2008-11-29 17:09) [11]


> Andy BitOff ©   (29.11.08 16:54) [9]

> http://dfc.com.ru/?sid=2&id=7&itemid=2

А.. Это я, оказывается, видел.
Подобных функций я и сам в свое время понаписал.

Однако, тема передачи процессору (или, скажем, route)
строк для исполнения там не раскрыта.

Но, все равно, спасибо.

--
Regards, LVT.


 
Slym ©   (2008-11-29 17:17) [12]

Leonid Troyanovsky ©   (29.11.08 17:09) [11]
как не раскрыта? А [10]?
там даже РАР с командой @ можно выполнить (ввод сжимаемых файлов с консоли)


 
Leonid Troyanovsky ©   (2008-11-29 17:20) [13]


> Slym ©   (29.11.08 17:17) [12]

> как не раскрыта? А [10]?

Здесь вам не тут, а там вам покажут ;)

А [10] я еще не изучил.

--
Regards, LVT.


 
Pavel   (2008-11-29 17:36) [14]

> Slym ©   (29.11.08 16:56) [10]
> анализируй
> uses windows,SysUtils,classes;
> type
>  TOnStdOut=procedure(Sender:TObject;const Str:string) of
> object;
>  TConsoleThread=class(TThread)
>  private
........


чтобы соображалось получше, можно, пожалуйста, пример использования?


 
Slym ©   (2008-11-29 17:44) [15]

procedure TForm1.OnStdOut(Sender:TObject;const Str:string);
begin
 Memo1.lines.add(Str);
end;

Source:=TstringList.Create;
Source.add("file1.txt");
Source.add("file2.exe");

//TConsoleThread - нужно доработать чтоб Suspend был
ConsoleThread:=TConsoleThread.Create("rar.exe a dest.rar @",Source,true);
ConsoleThread.OnStdOut:=OnStdOut;
ConsoleThread.Resume;
ConsoleThread.WaitFor;
ConsoleThread.free;


 
Leonid Troyanovsky ©   (2008-11-29 17:51) [16]


> Leonid Troyanovsky ©   (29.11.08 17:20) [13]

> А [10] я еще не изучил.

Изучаю.

1. Исключения в потоке без except.
2. FStdIn.BeginUpdate &etc - лишнее.
3. Некрасивый вывод из консоли - нет обработки #13 - #13#10.

Ну, и несмотря на  FStdIn.AddObject(Commands)
нет передачи строки работающему экземпляру процессора.

Не знаю, может я чего-то непонятно говорю.

--
Regards, LVT.


 
Slym ©   (2008-11-29 17:53) [17]

аналогично copy con file.txt
Source:=TstringList.Create;
Source.add("file1.txt");
Source.add("file2.exe");
TConsoleThread.Create("cmd.exe. /c copy con file.txt",Source,true);

можно уйти от заранее сформированного Source... и слать напрямую...
procedure TConsoleThread.Write(const Str: string);
begin
 CriticalSection.Enter;
 try
   FStdIn.Add(Str);
 finally
   CriticalSection.Leave;
 end;
end;

procedure TConsoleThread.Execute;
.....
while FStdIn.Count>0 do
begin
 CriticalSection.Enter;
 try
   p:=PChar(FStdIn[0]);
   CharToOem(p,p);
   if not WriteFile(ChildStdinWr,p^,Length(FStdIn[0]),bytesRead,nil) then
     RaiseLastOSError;
   FStdIn.Delete(0);
 finally
   CriticalSection.Leave;
 end;
end;

но загвостка в EOF!
она реализована так:
if FCloseHandles and (ChildStdinWr<>0) then
       if CloseHandle(ChildStdinWr) then
         ChildStdinWr:=0;

и в коде выше EOF был известен заранее... а тут придется придумывать...
CloseWrite


 
Slym ©   (2008-11-29 17:55) [18]

Leonid Troyanovsky ©   (29.11.08 17:51) [16]
Некрасивый вывод из консоли - нет обработки #13 - #13#10.

на коленке переписывается старый код...
а красоту сами наводите :)


 
Slym ©   (2008-11-29 18:00) [19]

Slym ©   (29.11.08 17:53) [17]
можно уйти от заранее сформированного Source... и слать напрямую...
procedure TConsoleThread.Write(const Str: string);
begin
CriticalSection.Enter;
try
  FStdIn.Add(Str);
finally
  CriticalSection.Leave;
end;
end;


или даже непосредственно в пайп
procedure TConsoleThread.Write(const Str: string);
begin

  CharToOem(pchar(str),pchar(str));
      if not WriteFile(ChildStdinWr,pchar(str)^,Length(Str),bytesRead,nil) then
        RaiseLastOSError;
end;


 
Slym ©   (2008-11-29 18:54) [20]

unit Unit2;

interface
uses windows,SysUtils,classes;
type
TOnStdOut=procedure(Sender:TObject;const Str:string) of object;
TConsoleThread=class(TThread)
private
  FCMD:string;
  StdOutStr:string;
  FOnStdOut:TOnStdOut;
  FExitCode:DWord;
  StdInPipeR,StdInPipeW:THandle;
  StdOutPipeR,StdOutPipeW:THandle;
protected
  procedure Execute;override;
  procedure DoStdOut;
public
  constructor Create(const CMD:string;CreateSuspended: Boolean);reintroduce;
  destructor Destroy; override;
  procedure WriteToStdIn(const Str:string);
  property ExitCode:DWord read FExitCode;
  property OnStdOut:TOnStdOut read FOnStdOut write FOnStdOut;
end;
implementation

constructor TConsoleThread.Create(const CMD:string;CreateSuspended: Boolean);
var
 sa:TSecurityAttributes;
begin
 FCMD:=CMD;
 sa.nLength:=sizeof(TSecurityAttributes);
 sa.bInheritHandle:=true;
 sa.lpSecurityDescriptor:=nil;
 Win32Check(CreatePipe(StdInPipeR,StdInPipeW,@sa,0));
 Win32Check(CreatePipe(StdOutPipeR,StdOutPipeW,@sa,0));
inherited Create(CreateSuspended);
end;

destructor TConsoleThread.Destroy;
begin
  if StdInPipeR<>0 then CloseHandle(StdInPipeR);
  if StdInPipeW<>0 then CloseHandle(StdInPipeW);
  if StdOutPipeR<>0 then CloseHandle(StdOutPipeR);
  if StdOutPipeW<>0 then CloseHandle(StdOutPipeW);
 inherited;
end;

procedure TConsoleThread.DoStdOut;
begin
if assigned(FOnStdOut) then
  FOnStdOut(self,StdOutStr);
end;

procedure TConsoleThread.Execute;
var
si:TStartupInfo;
pi:TProcessInformation;
bytesRead:DWORD;
begin
try
  GetStartupInfo(si);
  si.cb:=sizeof(TStartupInfo);
  si.dwFlags:=STARTF_USESTDHANDLES or STARTF_USESHOWWINDOW;
  si.hStdInput:=StdInPipeR;
  si.hStdOutput:=StdOutPipeW;
  si.hStdError:=StdOutPipeW;
  si.wShowWindow:=SW_HIDE;
  Win32Check(CreateProcess(nil,PChar(FCMD),nil,nil,true,CREATE_NEW_CONSOLE,nil,nil ,si,pi));
  while not Terminated do
  begin
    if WaitForSingleObject(pi.hProcess,0)=WAIT_OBJECT_0 then
    begin
      GetExitCodeProcess(pi.hProcess,FExitCode);
      Terminate;
    end;
    PeekNamedPipe(StdOutPipeR,nil,0,nil,@bytesRead,nil);
    if bytesRead>0 then
    begin
      SetLength(StdOutStr,bytesRead);
      if not ReadFile(StdOutPipeR,PChar(StdOutStr)^,bytesRead,bytesRead,nil) then
        RaiseLastOSError;
      SetLength(StdOutStr,bytesRead);
      OemToChar(PChar(StdOutStr),PChar(StdOutStr));
      Synchronize(DoStdOut);
    end;
  end;
finally
  CloseHandle(pi.hProcess);
  CloseHandle(pi.hThread);
end;
end;

procedure TConsoleThread.WriteToStdIn(const Str: string);
var bytes:DWORD;
begin
 CharToOem(PChar(Str),PChar(Str));
 Win32Check(WriteFile(StdInPipeW,PChar(str)^,Length(Str),bytes,nil));
 if bytes<>Length(Str) then raise Exception.Create("StdIn write error");
end;

end.

пользование:
procedure TForm1.FormCreate(Sender: TObject);
begin
 ConsoleThread:=TConsoleThread.Create("C:\WINDOWS\system32\cmd.exe",true);
 ConsoleThread.OnStdOut:=OnStdOut;
 ConsoleThread.OnTerminate:=OnTerminate;
 ConsoleThread.FreeOnTerminate:=true;
 ConsoleThread.Resume;
end;

procedure TForm1.OnTerminate(Sender: TObject);
begin
 Application.Terminate;
end;

procedure TForm1.OnStdOut(Sender:TObject;const Str:string);
begin
Memo1.lines.add(Str);
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
 ConsoleThread.WriteToStdIn(Edit1.Text+#13#10);
end;


 
Leonid Troyanovsky ©   (2008-11-29 20:44) [21]


> Slym ©   (29.11.08 17:55) [18]

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

Вот. И меня чрез оную коленку хочешь.. переписать.
Давай уж по-существу.

--
Regards, LVT.


 
Slym ©   (2008-11-30 08:11) [22]

Leonid Troyanovsky ©   (29.11.08 20:44) [21]
[20] проверено... получится мини гуи аналог консоли, но нет обработки eof которая нужна например в copy con txt.txt



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

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

Наверх





Память: 0.55 MB
Время: 0.007 c
6-1196964272
BlazeMaster
2007-12-06 21:04
2009.01.11
Вопрос о компоненте WebBrowser


15-1226294001
Design
2008-11-10 08:13
2009.01.11
Как определить работает ХРManifest или нет?


15-1226802329
axd
2008-11-16 05:25
2009.01.11
Помогите найти песню mp3


15-1226589788
@!!ex
2008-11-13 18:23
2009.01.11
Вышел Red Alert 3.


1-1205915866
Андрей Пл
2008-03-19 11:37
2009.01.11
Вопрос по дополнительным потокам!!!





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