Пишем апплет-переводчик для Gnome [python]

    Предисловие



    Сидел я как-то тихим вечером, читал англоязычные сайты. Когда понадобилось перевести пару фраз, я как обычно открыл новую вкладку, набрал translate.google.ru, скопировал, вставил текст, нажал перевести, прочитал. И ведь такую длинную последовательность действий приходится проделывать каждый раз, когда есть необходимость перевести текст, пару фраз или целый абзац. Да, можно использовать какой-либо клиент для перевода, но окно программы придется открывать или доставать из-под других окон. Да, последовательность короче, но все равно длинная. Было бы здорово сократить последовательность действий до скопировать, нажать кнопку. Так появилась идея для апплета. Его логика такая:
    • На панели появляется кнопка «GTranslate»
    • Если вам надо перевести текст, то копируете его и нажимаете на кнопку
    • Автоопределение языка


    И я начал читать различные статьи по написанию апплетов, в том числе и на хабре. Во всех использовалась gtk.Label(), но это и правильно, те апплеты использовались для вывода информации. Наш же апплет никакую информацию на панель выводить не будет, он всего лишь посредник между нами и диалоговым окном с переводом. Поэтому будем использовать gtk.Button(). Итак, приступим!

    Диалоговое окно



    Изначально для вывода текста использовался элемент easygui.codebox, но easygui в стандартной поставке нету и заставлять её ставить не очень-то хотелось. Решено было написать свой простенький элемент управления. Он должен был иметь три контрола: текст на начальном языке, текст на переведенном языке (это должен был быть textbox для возможности копирования перевода) и, конечно же, кнопка с надписью «OK». Благодаря вот этим двум туторам появилось диалоговое окно.

    Код, в общем-то, понятный, немного прокомментированный.
    import pygtk
    pygtk.require("2.0")
    import gtk

    """Спасибо этим туторам! Без них я бы этого не сделал =)"""<br/>
    #http://www.pygtk.org/docs/pygtk/class-gdkwindow.html<br/>
    #http://www.moeraki.com/pygtktutorial/pygtk2tutorial/

    """Этот класс описывает диалог для вывода переведенного текста"""<br/>
    class ShowTextDialog():
      def __init__(self):
        self.default_window_name="GTranslate [Neon Mercury]"
        
        self.window=gtk.Window(gtk.WINDOW_TOPLEVEL)
        self.window.set_title(self.default_window_name)
        self.window.connect("delete_event", self.delete_event)
        self.window.set_border_width(10)
        self.box=gtk.VBox()
        self.window.add(self.box)
        
        """Эта Label будет содержать непереведенный текст"""<br/>
        self.info=gtk.Label()
        self.box.pack_start(self.info, True, False, 0)
        
        """А из этого поля можно скопировать уже переведенный текст, если потребуется"""<br/>
        self.textview=gtk.TextView()
        self.box.pack_start(self.textview, True, True, 0)    
          
        self.button=gtk.Button("OK")
        self.button.connect("clicked", self.button_clicked)
        self.box.pack_start(self.button, False, False, 0)
        
        self.info.show()
        self.textview.show()
        self.button.show()
        self.box.show()
        
        self.window.move(100, 100) 
        self.window.resize(640, 480)
        
      """info - текст для label, textbox_info - текст для textview"""<br/>
      """Показывает диалог"""<br/>
      def show(self, info, textbox_info, main_window_title="GTranslate [Neon Mercury]"):
        info=self.process_text(info)
        textbox_info=self.process_text(textbox_info)
        self.info.set_text(info)
        self.textview.get_buffer().set_text(textbox_info)
        self.window.set_title(main_window_title)
        self.window.show()
      
      def hide(self):
        self.window.hide()
        
      def button_clicked(self, widget, data=None):
        self.hide()
        
      """По нажатии на кнопку, просто скрываем диалог"""<br/>
      def delete_event(self, widget, event, data=None):
        self.hide()
        
      """Обрабатывает текст. Google отдает текст одной строкой, даже если он содержит 65536 символов :)"""<br/>
      """Если в ручную не разбить этот текст на строки, то наше окно будет очень длинным!"""<br/>
      """Вот каждый max_len символ мы вставляем перевод строки. Если вдруг перевод строки встретиться раньше"""<br/>
      """max_len символа, то обнуляем счетчик."""<br/>
      def process_text(self, text, max_len=160):
        result=""<br/>
        i=0
        for char in text:
          i+=1
          if i>max_len and char==" ":
            result+="\n"<br/>
            i=0
          else:
            if char=="\n":<br/>
              i=0
            result+=char<br/>
        return result


    * This source code was highlighted with Source Code Highlighter.


    Библиотека для перевода



    Для перевода текста также была написана отдельная библиотека. Она также очень проста и имеет три функции. Первая заменяет наиболее частоиспользуемые HTML-мнемоники на их эквивалент. Их безумно много, поэтому написал простенький скрипт, который распарсил википедию и, фактически, написал за меня всю функцию. Её я не буду на хабре постить, она очень длинная, в исходниках все будет. Вторая функция, определяющая исходный язык текста, тоже простая. Мы просто берем каждый символ текста. Если он из английского алфавита, то увеличиваем один счетчик, если из русского, то другой. То есть мы определяем доминирующий язык в тексте и считаем его исходным. Третья функция и есть переводчик. Она определяет исходный язык, посылает запрос гуглю и получает переведенный текст.
    """Функция определяет основной язык текста. По-умолчанию английский."""<br/>
    """Считаем сколько символов англ. алфавита в тексте, сколько рус. алфавита."""<br/>
    """Кого больше - тот и выиграл!"""<br/>
    def determine_languages(text):
      en_chars_count=0
      ru_chars_count=0
      for char in text:
        if (char>="a" and char<="z") or (char>="A" and char<="Z"):
          en_chars_count+=1
        elif (char>="а" and char<="я") or (char>="А" and char<="Я"):
          ru_chars_count+=1
      source_lang="en"<br/>
      dest_lang="ru"<br/>
      if ru_chars_count>en_chars_count:
        source_lang, dest_lang="ru", "en"<br/>
      return source_lang, dest_lang

    """Основная функция для перевода текста."""<br/>
    def translate(text):
      source_lang, dest_lang=determine_languages(text)  
      params={"ie":"UTF-8", "text":text,
          "sl":source_lang, "tl":dest_lang}
      params=urllib.urlencode(params)
      headers={"Content-Length":"%d" % len(params)}
      
      connection=httplib.HTTPConnection("translate.google.ru")
      connection.request("POST", "/translate_t", params, headers)
      response=connection.getresponse()
      answer=response.read()
      index=answer.find("id=result_box")
      if index!=-1:    
        index+=24
      """Использовать регулярку не хотелось бы для одного поиска"""<br/>
        translated_text=answer[index:answer.find("</div", index)]
        translated_text=replace_html_mnemonics(translated_text)
        translated_text=translated_text.decode("koi8-r")
        return translated_text
      else:
        return ""


    * This source code was highlighted with Source Code Highlighter.


    Апплет



    Ну и вот собственно код самого апплета:
    #!/usr/bin/python
    # -*- coding: utf-8 -*-
    import sys
    import gtk
    import gnomeapplet

    import NeonDialogs
    from GTranslateLib import translate

    """Спасибо этим туторам! Без них я бы этого не сделал =)"""<br/>
    #http://www.citforum.ru/programming/python/gnome_applets/

    """Класс нашего апплета"""<br/>
    class GTranslate(gnomeapplet.Applet):
      def __init__(self, applet, iid):
        self.applet=applet
        self.applet.set_name("GTranslate")
        self.box=gtk.HBox()
        self.applet.add(self.box)
        self.button=gtk.Button("GTranslate")
        self.box.add(self.button)
        self.applet.show_all()
        
        self.button.connect("clicked", self.on_button)
        self.dialog=NeonDialogs.ShowTextDialog()
        
      """Если юзер хочет перевести текст и кликнул по нашей кнопке, то """<br/>
      """берем текст из буфера обмена, переводим и выводим через самописный диалог."""<br/>
      def on_button(self, widget, data=None):
        text=self.get_text_from_clipboard()
        translated_text=translate(text)
        self.dialog.show(text, translated_text)
        
      """Этот пункт со справкой на будущее...пока не используется."""<br/>
      def on_ppm_about(self, event, data=None):
        gnome.ui.About("GTranslate", "1.0", "GPL General Public License",
                "Скопируйте текст в буфер обмена и получите переведенный на другой язык текст!",
                ["Neon Mercury <wishmaster2009@gmail.com>",]).show()
        
      """Просто получает текст из буфера обмена. Вроде объект clipboard можно не пересоздавать каждый раз, """<br/>       
      """но почему-то работает только так. Надо почитать маны."""
      def get_text_from_clipboard(self):
        clipboard=gtk.Clipboard(display=gtk.gdk.display_get_default(), selection="CLIPBOARD")
        text=clipboard.wait_for_text()
        clipboard.store()    
        return text
               
    def applet_factory(applet, iid):
      GTranslate(applet, iid)
      return True  

    """Просто заглушка для дебага. В релизе вызвать нельзя (без правки скрипта)."""<br/>
    def run_in_window():
      main_window=gtk.Window(gtk.WINDOW_TOPLEVEL)  
      main_window.set_title("GTranslate [debug mode]")
      main_window.connect("destroy", gtk.main_quit)
      app=gnomeapplet.Applet()
      applet_factory(app, None)
      app.reparent(main_window)
      main_window.show_all()
      gtk.main()
     
    """Запускает наш апплет"""
    def run_in_panel():
      gnomeapplet.bonobo_factory("OAFIID:GTranslate_Factory",
                    GTranslate.__gtype__,
                    "GTranslate", "1.0", applet_factory)
      
    """Если хотим ловить жучков, то меняем run_in_panel() на run_in_window()"""<br/>
    run_in_panel()


    * This source code was highlighted with Source Code Highlighter.


    .server файл



    Если вы писали/копипастили апплет сами, то ниже приведен .server файл для этого апплета.
    <oaf_info>

    <oaf_server iid="OAFIID:GTranslate_Factory"
              type="exe"          location="/home/neon/applets/GTranslate/GTranslate.py"><br/>

      <oaf_attribute name="repo_ids" type="stringv"><br/>
        <item value="IDL:Bonobo/GenericFactory:1.0" /><br/>
        <item value="IDL:Bonobo/Unknown:1.0" />

      </oaf_attribute><br/>
      <oaf_attribute name="name" type="string" value="GTranslate factory" /><br/>
      <oaf_attribute name="name-ru" type="string" value="Фабрика GTranslate" /><br/>

      <oaf_attribute name="description" type="string" value="Factory of GTranslate" /><br/>
      <oaf_attribute name="description-ru" type="string" value="Фабрика GTranslate" /><br/>

    </oaf_server><br/>
    <oaf_server iid="OAFIID:GTranslate"
              type="factory"          location="OAFIID:GTranslate_Factory"><br/>
      <oaf_attribute name="repo_ids" type="stringv"><br/>

        <item value="IDL:GNOME/Vertigo/PanelAppletShell:1.0" /><br/>
        <item value="IDL:Bonobo/Control:1.0" /><br/>
        <item value="IDL:Bonobo/Unknown:1.0" /><br/>

      </oaf_attribute><br/>
      <oaf_attribute name="name" type="string" value="GTranslate" /><br/>
      <oaf_attribute name="name-ru" type="string" value="GTranslate" /><br/>

      <oaf_attribute name="description" type="string" value="GTranslate service of text translation." /><br/>
      <oaf_attribute name="description-ru" type="string" value="Сервис перевода текста GTranslate." /><br/>

      <oaf_attribute name="panel:category" type="string" value="Accessories" /><br/>
      <oaf_attribute name="panel:icon" type="string" value="/home/neon/applets/GTranslate/GTranslate.png" /><br/>

    </oaf_server><br/>
    </oaf_info><br/>

    * This source code was highlighted with Source Code Highlighter.


    Одно замечание: выделенные строки замените на свои. Первая — путь к главному файлу апплета (GTranslate.py). Вторая — это картинка апплета (Необязательно, но красиво). Не забудьте сделать файл GTranslate.py исполняемым.

    Заключение



    Вот(tar.gz) (rar) архив с исходниками апплета и скриптом установки. Для установки апплета в систему необходимо запустить файл install.py с правами root'a (для копирования файлов в /usr/lib/bonobo/servers/).

    Теперь для перевода достаточно выделить текст и нажать кнопку GTranslate на панели. Удачи!

    UPD: Перенес в блог PyGTK

    Метки:
    Поделиться публикацией
    Похожие публикации
    Реклама помогает поддерживать и развивать наши сервисы

    Подробнее
    Реклама
    Комментарии 66
      • +2
        С сайта:
        Version: 3.1.83
        OS: Windows (98, 2000, XP, Vista, 7)

        Имхо не в тему ;-)
      • 0
        Просто офигеная программка, помогает каждый день, автору большое спасибо
      • 0
        зачем нажимать?
        хоткей было бы здорово :)
        ну и иконка на панели :)
        • +3
          Я просто писал её под себя и с программированием под gnome знаком мало… в следующей версии обязательно ждите :)
          Кстати, в принципе, исходники у вас имеются ;)
          • +2
            А решением стала кнопка, потому что с моей темой она стильно выглядит на панельке :)
            • 0
              что за тема, кстати?
              • +1
                • +1
                  Почти в каждом топике, хоть как-то относящимся к линуксу, и в котором есть скриншоты, неизменно возникает вопрос «А что у Вас за тема?». :)
                  • 0
                    любопытно, что люди выбирают. я сам дефолтной пользуюсь и менять не собираюсь)
            • –5
              Такое ощущение, что слово «GTranslate» ты написал в пэйнте и буквы разукрасил инструментом «заливка».
              • +8
                У меня на Ubuntu paint'a нету.
                • 0
                  Вот-вот! У вас в Ubunt'е даже Paint'а нету, а всё потому что гном! Вот в кедах — есть!

                  Ну это шутка, а вообще от этого треда подумалось, почему это во вроде бы ориентированной на простоту связке убунту-гном по умолчанию в качестве графического редактора предлагается монструозный фотошопоподобный GIMP, возможностей которого явно слишком много для рядового пользователя. Для Open Office vs. Gnumeric + Abiword выбор первого для состава дистрибутива мне понятен, он лучше поддерживает документы Microsoft Office, а вот про GIMP непонятно. Неужели ничего Paint подобного гномовского нет? Верится с трудом *гуглит «gnome simple graphics editor»*
                  • 0
                    PS: Я помню, что гном вырос из GIMP'а, но тем не менее.
                    • 0
                      Насколько я помню, GIMP будет исключен из стандартной поставки начиная с 10.4 версии.
                      • 0
                        и вместо него включат что-то попроще
                        • 0
                          Да, я читал на хабре. Хотел ещё в том посте написать мол «а я знал!», но нафига? промолчал.
                  • 0
                    а почему бы вам не добавить в код возможность запускаться как отдельное приложение с иконкой в трее — тогда бы был намек на кросплатформенность ;)
                    • 0
                      Точно) Вот тогда когда сделаю вместо кнопки иконку, тогда и перепишу run_in_window() пот трей.
                    • НЛО прилетело и опубликовало эту надпись здесь
                      • +2
                        1. Как уже говорил, я писал для себя. just for fun!
                        2. Аддоны для лисы я не искал (я вообще удивлен, что названия совпали)
                        3. Это апплет к системе и переводит любой текст в буфере обмена, а не только с сайта
                        4. Подумайте о тех, кто использует браузер, отличный от лисы.
                        • НЛО прилетело и опубликовало эту надпись здесь
                          • +5
                            Какой ответственности? Вы вообще о чем говорите? Я написал удобный для себя инструмент, который мне помогает… свои функции он выполняет. Также были даны краткие пояснения с кодом как и что я делал. Есть скрипт установки. Выше были даны советы как лучше сделать и я с ними согласен. Если вас данная программа не устраивает, это не значит, что она не устраивает все хабрасообщество.

                            >> Зачем разбивать гуглотекст искусственно, если можно просто выставить свойство word wrap
                            С gtk, как я уже говорил, не настолько знаком. По этому пункту согласен, учту.

                            По поводу второго пункта: вот вам надо написать сообщение вашему англоязычному товарищу, но вы плохо знаете язык. Вы берете, набираете текст на русском, нажимаете GTranslate и что вы видите? Километровый baloon hint, из которого перевод нельзя скопировать и вы вынуждены набивать текст заново! Я не думаю, что это «намного более удобно». И потом, что значит «активировать ваше окно»? Окно с переводом выводится поверх всех сразу, вам надо только прочитать перевод и кликнуть «ОК», чтобы его скрыть…
                            • НЛО прилетело и опубликовало эту надпись здесь
                            • +1
                              Какая нафиг разница, что именно послужило примером для написания апплета? Человек по сути написал статью по разработке апплетов для гнома, за что ему респект и уважуха.
                        • 0
                          По-моему, это логично перенести в блог PyGTK. Кармы подбросил, ещё кто-нибудь добавит единичку, и будет можно :-)
                          • 0
                            Перенесу, как только смогу :)
                            • 0
                              Бросил клич братьям-питоноводам из своего списка друзей :-)
                              • 0
                                Они мне итак уже помогли инвайтом, не буду пока еще мучать :)
                                • +2
                                  Я своим из списка написал, чтоб тебя плюсанули :-)
                                  Питоновод питоноводу — друг, товарищ и брат :-)
                        • 0
                          я тоже однажды писал похожую статью — habrahabr.ru/blogs/python/64317/, только там целью было научиться писать апплет на примере конкретного апплета.
                          • +1
                            Я частично по вашему топику и делал. У меня ссылка на тот пост во втором абзаце. :)
                          • 0
                            кстате, а что нынче модно коментарии к коду писать в тройных кавычках?, к тому же мой питон интерпретирует это как ошибку, у вас разве не так?
                            • +1
                              Не, тройные кавычки я пишу для того, чтобы визуально отделять описание функции от обычных комментариев (правильнее было бы писать их после объявления функции, но никак не могу выработать эту привычку). А про ошибку первый раз слышу, у меня всё интерпретирует и не возникает… интерпретирую через python2.6.
                              • 0
                                как гласит www.python.org/dev/peps/pep-0257/ тройные кавычки используются для документирования классов, функций, модулей и методов. ИМХО такое применение как в статье недопустимо
                                • 0
                                  Извиняюсь. Надо правда вырабатывать привычку документировать функцию после объявления. Просто я с питоном дружу чуть меньше двух месяцев и поэтому некоторых тонкостей еще не знаю. Надо почитать на досуге. Спасибо.
                                  • 0
                                    Вот именно, после! Это важно.

                                    Строго говоря, ""«foobar»"" == «foobar»
                              • НЛО прилетело и опубликовало эту надпись здесь
                                • +3
                                  Здесь речь не про мое знание английского языка, не про винды, не про лингву и уж тем более не про геморрой! Вы знаете английский на подобающем уровне — я раз за вас, не более. Я не считаю, что этого языка не знаю, однако возникают моменты, когда какое-либо слово не помню… просто из-за того, что редко его употребляю/вижу.
                                  Если бы вы запостили топик про Windows и Lingvo, а я пришел и сказал: «Власть пингвинам! Ставь GTranslate.», вы бы разве не посчитали бы это оффтопиком? И уж тем более, если бы это было такой проблемой, то я вряд ли бы постил это на хабр…
                              • 0
                                Спасибо, очень интересно. Сам с недавнего времени изучаю питон. правда хочу pyqt\pyside поюзать. Но всего понемножку.
                                • 0
                                  Вообще говоря, в убунте (в смысле в дефолном виндов манагере, равно как и в кде) есть плагин словаря на панель, достаточно там добавить dict.dvo.ru в качестве источника — и можно пользоваться. Но за саморазвитие — безусловно плюс.
                                  • 0
                                    В убунте еще StarDict, умеет переводить, используя google. Нужно приложить вот такой патч:
                                    #! /bin/sh /usr/share/dpatch/dpatch-run
                                    ## google.dpatch by  <funca@localhost>
                                    ##
                                    ## All lines beginning with `## DP:' are a description of the patch.
                                    ## DP: No description.
                                    
                                    @DPATCH@
                                    diff -urNad stardict-3.0.1~/src/stardict.cpp stardict-3.0.1/src/stardict.cpp
                                    --- stardict-3.0.1~/src/stardict.cpp	2007-11-05 08:39:51.000000000 +0500
                                    +++ stardict-3.0.1/src/stardict.cpp	2009-08-22 23:36:01.000000000 +0600
                                    @@ -1943,7 +1943,7 @@
                                     			}
                                     		}
                                     	} else if (engine_index == 0) {
                                    -		#define GoogleTranslateStartMark "<div id=result_box dir=ltr>"
                                    +		#define GoogleTranslateStartMark "<div id=result_box dir=\"ltr\">"
                                     
                                     		char *p = g_strstr_len(buffer, buffer_len, GoogleTranslateStartMark);
                                     		if (p) {
                                    
                                    
                                  • 0
                                    При попытке добавить апплет на панель возникает ошибка:
                                    The panel encountered a problem while loading «OAFIID:GTranslate».
                                    в чем может быть проблема?
                                    • 0
                                      У вас точно в .server файле правильный путь к апплету?
                                      • 0
                                        да, путь который задан в install.py
                                        /usr/bin/GTranslate/GTranslate.py
                                        • 0
                                          попробуй запустить его из консоли, типа:
                                          $ python /usr/bin/GTranslate/GTranslate.py
                                          • 0
                                            помогло, спасибо!
                                            была не указана кодировка в файле NeonDialogs.py и ошибки синтаксиса GTranslateLib.py и GTranslate.py
                                            странно, такое только у меня? файлы брал из архива в статье

                                            • 0
                                              да, у меня тоже оно сначала ругалось на кодировку, потом на строки «коменты в кавычках», после ковыряний получилось таки добавить на панель, а вот удалять корректно у меня так и не получилось.
                                      • 0
                                        chmod +x не забыли?
                                        • 0
                                          добавил для файла GTranslate.py, та же проблема
                                      • 0
                                        неплохо бы добавить в скрипт поддержку консольных параметров, т.е. вместо того чтобы менять последнюю строчку в скрипте можно добавить специальный параметр --run-in-window для дебага.
                                        • 0
                                          Потенциал для этого имеется ;)
                                          Ну а если серьезно, я сейчас её переделываю с учетом всех советов, что мне дали...--run-in-window будет непременно.
                                        • –5
                                          глупцы! я в винде просто выделаю слово и нажимаю ctrl+ins+ins и вижу перевод в окне lingvo
                                          • +1
                                            глупо сравнивать комбайн-переводчик lingvo, стоимость полной версии которого стоит десятки баксов, и маленький апплет на панель гнома. Автор ведь не заставляет Вас пересаживаться на linux и ставить его апплет.
                                            • 0
                                              Эх, вот смотрю я на вас и думаю: «А стоит ли спорить?»… мда…
                                            • 0
                                              Может кому пригодится. Делал аналог на AIR code.google.com/p/airtranslator/
                                              • 0
                                                Можно багрепорт? :)
                                                  """Использовать регулярку не хотелось бы для одного поиска"""
                                                    translated_text=answer[index:answer.find("</div", index)]
                                                    translated_text=replace_html_mnemonics(translated_text)
                                                    translated_text=translated_text.decode("koi8-r")
                                                

                                                А почему koi8-r? В answer есть указание на кодировку страницы в виде.
                                                <meta content="text/html; charset=UTF-8" http-equiv="content-type"/>
                                                

                                                Мне вот такое выдал… Дергайте от charset= и до " и передавайте значение в translated_text.decode(). Хотя идеологически правильнее было бы декодировать сам answer, прежде чем разгребать.
                                                • 0
                                                  Да, я знаю… сначала получал кодировку прямиком из ответа и применял. Но при выводе получал иероглифы, хотя получал правильно — UTF-8. Я долго мучался, почему это не работает, перерыл много форумов, но ответа не нашел… когда уже почти отчаялся, написал koi8-r и все отобразилось правильно. Я не понимаю этого бага, честно, но исправить получилось. Если где-то наткнетесь, почему так происходит, скажите обязательно мне. ;)
                                                • –1
                                                  Эту задачу давно решили куда более простым методом: translate.google.com/translate_buttons
                                                  • 0
                                                    И ведь опять же только только для браузера, а не для всей системы…
                                                  • +1
                                                    Видел на днях аналог welinux.ru/post/1216/
                                                    • 0
                                                      к слову у вас бага в GTranslateLib.py, наверное нужно было везде писать
                                                      text = text.replace
                                                      а не только в первых двух строчках, это я про функцию replace_html_mnemonics
                                                      • 0
                                                        Спасибо, надо будет попробовать.

                                                        Я обычно для переводов использую jabber-ботов Google. Удобно — открыл окошко и переписываешься с ним.

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

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