Форум помощи

Информация о пользователе

Привет, Гость! Войдите или зарегистрируйтесь.


Вы здесь » Форум помощи » Статьи программистам » C#. Пишем google-переводчик


C#. Пишем google-переводчик

Сообщений 1 страница 3 из 3

1

Сегодня мы с тобой напишем свой собственный переводчик. Сам функционал перевода мы, конечно же, реализовывать не будем, а обратимся к всемогущему Google, в частности, к Google API Translate. Также немного поковыряем связывание данных в WPF и немного коснемся библиотеки Json.NET, в результате чего у нас получиться свой собственный переводчик. Вперед!

Схема работы
Итак, прежде чем приступать к самому интересному (написанию кода, конечно же  ) надо сначала набросать небольшой план работ. Предлагаю разбить весь процесс на следующие этапы:
1. Разобраться с взаимодействием Google API Translate.
2. Спроектировать WPF приложение.
3. Написать код на C# для взаимодействия с Google.
4. Реализовать связь интерфейса WPF приложения с кодом на C#.
Google API Translate
Итак, сначала нам надо разобраться с тем, как мы будем взаимодействовать с переводчиком от google, так как именно его услугами мы будем пользоваться для непосредственного перевода текста. Небольшие поиски в самом же google подскажут, что для перевода строки текста нам надо послать запрос по такому адресу.

Код:
<a href="http://ajax.googleapis.com/ajax/services/language/translate?v=1.0&q=">http://ajax.googleapis.com/ajax/services/language/translate?v=1.0&q=</a>[текст для перевода]&langpair=[направление перевода]

Это так называемый REST-запрос, самый просто способ для работы с разного рода API различных веб-сервисов. Например, с теми же вконтакте, твитером можно работать с помощью REST.
В результате Google вернет нам следующий ответ:

Код:
{"responseData": {"translatedText":"[Переведенный текст]"}, "responseDetails": null, "responseStatus": [статус перевода]}

Это строка в формате JSON (произносится “джейсан”, насколько я знаю). Нам в нашей программе надо будет только отпарсить этот ответ и выдать результат перевода пользователю. Для того чтобы приложение могло разбирать ответы в виде JSON-строки, нам понадобится библиотека Json.NET. Слить ее можно отсюда: http://json.codeplex.com. Есть и другие библиотеки, но я остановился на этой. В принципе, в нашем случае можно было бы обойтись и регулярными выражениями, но для расширения кругозора я предлагаю попробовать Json.NET.
ПРОЕКТИРОВАНИЕ WPF-ПРИЛОЖЕНИЯ
Теперь разберемся, как будет выглядеть и работать наше WPF-приложение. Я предлагаю особо не заморачиваться и сделать простой интерфейс с двумя TextBlock’ами, TextBox’ами и одной кнопкой. Мой вариант на рисунке 1.
http://www.vr-online.ru/article/google-translate/001.png
Рис. 1
Но хочу тебе напомнить, чтобы мы имеем дело с WPF, а потому, ты можешь сделать по-настоящему красивый дизинг, все ограничивается твоей фантазией. Моей, как видишь, хватило не намного

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

Класс будет содержать три закрытых метода:
• Stream GetHttpStream(string url).Получаем поток данных с сайта с адресом url.
• String ConvertStreamTostring(Stream stmSource).Преобразуем полученный поток stmSource в строку.
• void GetTranslateDirection ().Определяем направление перевода, т.е. язык, с которого будем переводить. Делать это будет элементарным анализов первых нескольких символов.
Кстати, первые два метода из примеров программ на C# под названием “101 C# Samples”. Поковыряй на досуге, много интересного .
Затем нам понадобятся 4 закрытых объекта:
• объект типа HttpWebResponse, который будет содержать ответ от сервера google для его последующего анализа;
• логическая переменная, определяющая направление перевода bEnRus. Наш переводчик будет переводить только в 2-х направлениях: с английского на русский и обратно. Поэтому хватит одной переменной для определения направления перевода. Значение данной переменной, кстати, мы и будем устанавливать в описанном выше методе GetTranslateDirection().
• Оригинальный текст для перевода strOriginal;
• переведенный текст strResult;

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

Теперь немного поговорим о связывании данных. Благодаря этому механизму, мы можем связывать элементы управления и объекты данных. В данном случае мы будем связывать элементы TextBox нашей формы с полями класса ClassTranslator. Для этого надо указать в параметре Text (это для примера, можно и другие параметры связывать) нужного TextBox’а свойство нашего класса. Теперь значение этого свойства будет определять значение параметра Text. И когда это свойство будет в классе меняться, свойство Text нашего элемента также будет меняться. Для того чтобы это делалось автоматически (изменение свойства вызывало изменение параметра), наш класс должен реализовать интерфейс INotifyPropertyChanged. Он уведомляет приемники данных (которыми и являются наши TextBox’ы) об изменении значения свойств. У него только 1 метод: PropertyChanged.

В результате у нас должен получиться такой каркас класса:

Код:
class ClassTranslator: INotifyPropertyChanged
    {
        String strOriginal;
        String strResult;
        Boolean bEnRus;
        HttpWebResponse wresScrape;
        /// <summary>
        /// Преобразуем поток данных в строку
        /// </summary>
        /// <param name="stmSource">Поток данных</param>
        /// <returns>Возвращаем строку</returns>
        private string ConvertStreamTostring(Stream stmSource)
        {        
        }
        /// <summary>
        /// Получаем поток данных от севрера,
        /// адрес которого указан в параметре
        /// </summary>
        /// <param name="url">Адрес,содержащий запрос
        /// к серверу Google
        /// </param>
        /// <returns>Возвращаем поток данных,
        /// полученных в ответ
        /// </returns>
        private Stream GetHttpStream(string url)
        {
        }
        #region Properties
        /// <summary>
        /// Свойство-обертка для поля
        /// strOriginal
        /// </summary>
        public string Original
        {
            ///устанавливаем значение
            ///и информируем о изменении свойства           
            set {strOriginal = value; OnPropertyChanged("Original");}
            get {return strOriginal;}            
        }
        /// <summary>
        /// Свойство-обертка для поля
        /// strOriginal
        /// </summary>
        public string Result
        {            
            get { return strResult;}            
        }
        #endregion
        /// <summary>
        /// Определяем направление перевода
        /// </summary>
        private void GetTranslateDirection()
        {
        }        
        /// <summary>
        /// ГЛАВНАЯ-Функция,выполняющая перевод
        /// </summary>
        public void Translate()
        {
        }
        /// <summary>
        /// указатель на событие PropertyChanged
        /// </summary>
        public event PropertyChangedEventHandler PropertyChanged;
        /// <summary>
        /// Функция,которая будет вызываться 
        /// при изменении нужных нам свойств
        /// </summary>
        /// <param name="strPropertyName">Имя сво-ва,котрое поменялось</param>
        protected void OnPropertyChanged(string strPropertyName)
        {        
        }
    }

Погружаемся в код
Начинаем потихоньку реализовывать наш класс. Сперва, разберемся с 3 закрытыми методами. Как я уже сказал, первые два взяты из “101 C# Samples”,так что я просто приведу их листинги с небольшими комментариями.

Код:
/// <summary>
        /// Получаем поток данных от севрера,
        /// адрес которого указан в параметре
        /// </summary>        
        private Stream GetHttpStream(string url)
        {
 
            ///Создаем объект для запроса к серверу
            HttpWebRequest wreqScrape = (HttpWebRequest)(WebRequest.Create(url));
            ///заполняем необходимые поля
            wreqScrape.UserAgent = "Mozilla/4.0 (compatible; MSIE 6.0b; Windows NT 5.1)";
            wreqScrape.Method = "GET";
            wreqScrape.Timeout = 10000;
 
            try
            {
                ///возвращаем поток данных,
                ///полученных от сервера
                wresScrape = (HttpWebResponse)(wreqScrape.GetResponse());
                return wresScrape.GetResponseStream();
 
            }
            catch
            {
 
                ///формируем объект исключение с текстом ошибки 
                ///и возвращаем вызывающей функции
                throw new Exception("There was an error retrieving the Web page " +
                    "you requested. Please check the Url and your connection to " +
                    "the Internet, and try again.");
 
            }
        }
Код:
/// <summary>
        /// Преобразуем поток данных в строку
        /// </summary>
        private string ConvertStreamTostring(Stream stmSource)
        {
            ///Объявляем обект,который будем использовать
            ///для чтения из потока
            StreamReader sr = null;
            ///если поток-источник не пуст
            if (stmSource != null)
            {
                ///предпринимаем попытку 
                ///считать данные
                try
                {
                    ///загружаем поток в StreamReader
                    sr = new StreamReader(stmSource);
                    ///считываем поток до конца 
                    ///и возвращаем результат
                    return sr.ReadToEnd();
                }
                catch
                {
 
                    ///ничего не сообщаем об ошибке
                    ///просто генерируем новое исключение
                    ///для вызывающего кода
                    throw new Exception();
                }
                finally
                {
                    ///при любом раскладе закрываем
                    ///закрываем объект-ответ от сайта                    
                    wresScrape.Close();
                    ///и поток-читатель
                    sr.Close();
                }
            }
            else
            {
                return null;
            }
        }

Теперь рассмотрим функцию определения языка оригинального текста GetTranslateLang().Нам главное определить язык, С КОТОРОГО будем переводить. Раз у нас переводчик поддерживает только 2 языка, то, определив язык оригинала, мы автоматически определим и язык, на который надо переводить. Я предлагаю просто просмотреть первые 5 символов (причем, именно буквенные, не пробелы, цифры и т.д.), а потом проанализировать, символы, какого алфавита встречались чаще. Вот как это выглядит в коде.

Код:
       /// <summary>
        /// Определяем направление перевода
        /// </summary>
        private void GetTranslateDirection()
        {
            ///переменная,содержащая кол-во 
            ///встретившихся русских символов
            Byte bRus=0;
            ///переменная,содержащая кол-во 
            ///встретившихся английских символов
            Byte bEn = 0;
            ///определяем кол-во необходимых символом
            ///алфавита для определения принадлежности
            ///к данному алфавиту
            Byte bSuccess = (strOriginal.Length<5)?(Byte)strOriginal.Length:(Byte)5;
            ///запускаем цикл по строке
            ///Пояснение:чтобы результат был
            ///регистронезависимым
            ///приводим всю строку
            ///к ВЕРХНЕМУ регистру
            foreach (char c in strOriginal.ToUpper())
            {
                ///проверяем принадлежность 
                ///символа тому или иному алфавиту
                if (c >= 'А' && c <= 'Я') bRus++;
                else if (c >= 'A' && c <= 'Z') bEn++;
                ///ПРОВЕРКА:найдено ли необходимо количество
                ///символом того или иного алфавита
                if (bRus == bSuccess) { bEnRus = false; break; }
                else if (bEn == bSuccess) { bEnRus = true; break; }
            }
        }

Разберем и главную функцию Translate.Суть ее работы проста: она формирует запрос к google-переводчику на основе полей strOriginal и bEnRus, затем получает и анализирует ответ, и записывает результат в переменную strResult.Все это, с комментариями и описано чуть ниже.

Код:
/// <summary>
        /// ГЛАВНАЯ-Функция,выполняющая перевод
        /// </summary>
        public void Translate()
        {
           ///Определяем направление перевода
           GetTranslateDirection();
           ///начинаем формировать строку
           ///запроса к серверу-переводчика
           StringBuilder sb=new StringBuilder(@"http://ajax.googleapis.com/ajax/services/language/translate?v=1.0&q=");
           ///добавляем оригинальный текст
           sb.Append(strOriginal);
           sb.Append("&langpair=");
           ///добавляем направление перевода
           sb.Append((bEnRus)?"en|ru":"ru|en");
           ///получаем строку-ответ от сервера 
           strResult = ConvertStreamTostring(GetHttpStream(sb.ToString()));
           ///преобразуем строку в JSON-объект
           JObject o = JObject.Parse(strResult);
           ///записываем результат
           strResult = o["responseData"].First().First().ToString();                     
           ///извещаем приемник данных
           ///о изменении свойства
           OnPropertyChanged("Result");
        }

Здесь хочу кое-что дополнительно объяснить. Как ты помнишь, ответ от google нам приходит в таком виде:

Код:
{"responseData": {"translatedText":"[Переведенный текст]"}, "responseDetails": null, "responseStatus": [статус перевода]}.

Для того чтобы нам добраться до строки Переведенный текст, нам надо от responseData перейти к его первому потомку. Мы попадем на translatedText, а уже его первым потомком будет как раз Переведенный текст. Поэтому мы и используем

Код:
strResult = o["responseData"].First().First().ToString();

Кстати, для использования библиотеки Json.NET надо выполнить следующие действия:
• добавить ссылку на нее, через Project->Add Reference(там нужный файл надо найти через закладку Browse ), после чего ты увидишь ее в списке ссылок;
• подключить нужное пространство имен using Newtonsoft.Json.Linq;
Связывание интерфейса с кодом
Ну вот, мы и подошли к заключительной стадии: связывание программного кода с формой. В принципе, основы связывания данных в WPF я уже обрисовал, тут мы просто посмотрим, как это практически реализуется.

Как ты помнишь, у меня есть 2 TextBox’а, в одном исходный текст, а в другом - переведенный.
Вот их разметка в XAML:

Код:
<TextBox Height="212" HorizontalAlignment="Left" Margin="12,32,0,0" Name="textBoxOriginal" VerticalAlignment="Top" Width="234" Grid.ColumnSpan="3" />
 
<TextBox Height="211" HorizontalAlignment="Right" Margin="0,32,12,0" Name="textBoxResult" VerticalAlignment="Top" Width="238" Grid.Column="2" />

Теперь нам надо связать их свойства Text со свойствами класса ClassTranslate. Делается это элементарно:
<TextBox Text="{Binding Path=Original,UpdateSourceTrigger=PropertyChanged}" … />

<TextBox Text="{Binding Path=Result,UpdateSourceTrigger=PropertyChanged}"…/>

Код:

Здесь мы указали в свойстве Path имена свойств, которые выступают источниками связывания и название триггера, который будет срабатывать при обновлении свойств.
В процедуре связывания есть еще один момент, который нам пригодиться - это режим связывания. По умолчанию стоит OneWay, т.е. односторонний. Это означает, что содержимое элемента будет зависеть от свойства класса (источника данных), а вот если мы что-то будем вводить в поле, то на свойство класса это влиять не будет. А нам надо, чтобы при вводе пользователем текста, который надо перевести, этот текст попадал в поле strOriginal нашего класса. Поэтому, при связывании текстового блока с полем класса strOriginal мы будем использовать двустороннее связывание-TwoWay.Итоговый вариант будет выглядеть вот так:

Код:
<TextBox Text="{Binding Path=Original, Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" …/>
<TextBox Text="{Binding Path=Result, Mode=OneWay,UpdateSourceTrigger=PropertyChanged}" … />

Дальше. Мы указали поля класса, которые будут выступать в качестве источников, но не указали сам объект данного класса, т.е. ЧЬИ поля то выводить. Для этого мы в форме объявим переменную нашего класса,
ClassTranslator translator = new ClassTranslator();
а затем укажем ее в качестве свойства DataContext главной сетки формы. Сделаем это в конструкторе формы

Код:
public MainWindow()
        {            
            InitializeComponent();
            MainGrid.DataContext=translator;
        }

И последний штрих. В коде нажатия на нашу кнопку будем вызывать метод Translate переменной

Код:
private void buttonTranslate_Click(object sender, RoutedEventArgs e)
        {
            translator.Translate();  
        }

Не удивляйся, когда после нажатия на кнопку форма будет немного подвисать. Отправление и принятие запроса от веб-сервера –не самое быстрое дело.
Заключение
В данной статье я постарался рассмотреть основы связывания данных в WPF и работу с Json-строками. Тебе судить, как это получилось, но я старался =) . В программе есть много моментов для доработки:
• более глубокий анализ переводимого языка. Можно смотреть не первые 5 символов, а, например, 5 слов.
• Надо помнить, что мы работаем с протоколом HTTP, а он имеет ограничение на длину запроса/ответа. Соответственно, если мы собираемся переводить большой текст, его надо разбить на несколько запросов.
• Можно вывести перевод в отдельный фоновый поток, чтобы пока пользователь набивает текст, делался синхронный перевод (как это сделано в самом Google).

Короче, делов много, но старт у тебя уже есть. Удачи!

Written by: Евгений Шапиро

Скачать исходник

Источник: vr-online.ru

0

2

Хорошая статья, только жаль что код такой громоздкий

0

3

хорошая статья по созданию переводчика на c#, но я изучаю c++ )

0


Вы здесь » Форум помощи » Статьи программистам » C#. Пишем google-переводчик


Рейтинг форумов | Создать форум бесплатно © 2007–2017 «QuadroSystems» LLC