В этот раз я хочу рассказать о том как работать с еще одним online-сервисом, а именно с сервисом online-переводов Google Translate.

Для работы с этим сервисом у Google есть свой программный интерфейс, а именно AJAX Language API for Translation And Detection. Именно его мы и будем использовать.

Для того чтобы осуществить online-перевод текста необходимо сделать http-запрос к сервису переводов, доступному по адресу:

http://ajax.googleapis.com/ajax/services/language/translate

Параметры, которые необходимо передать сервису:

  • v=1.0 – версия сервиса
  • q=<sometext> – URL-encoded текст для перевода.
  • langpair=<source_language>%7C<result_language> – пара названий языков: исходного и результирующего


Например для перевода текста “Hello World” с английского на русский строка запроса будет выглядеть так:

http://ajax.googleapis.com/ajax/services/language/translate?v=1.0&q=Hello%20World&langpair=en%7Cru

Ну вот, небольшой теоретический экскурс закончен, можно приступать к кодингу:
wxGoogleTranslate.h

#ifndef _WX_GOOGLE_TRANSLATE_H
#define _WX_GOOGLE_TRANSLATE_H

#include <wx/wx.h>

class wxGoogleTranslate
{
	/// \brief Language Info
	struct wxGoogleTranslateLanguageInfo
	{
		/// \brief Full language name
		wxString languageName;
		/// \brief Language code
		wxString languageCode;
	};

	/// \brief Stores information about all supported languages
	static wxGoogleTranslateLanguageInfo m_LanguageInfoArray[];
	/**
	\brief Parses JSON response from Google Translate service
	\param response contains JSON response from Google Translate service
	\param translatedText contains translated text if parsing of JSON response was correct
	\param translateionDetails contains additional information from Google Translate service
		   usually it is error message
    \param translationStatus contains parsing status (error code)
	\return true if translation and parsing was successfull, otherwise false
	*/
	static bool ParseJSONResponse(const wxString & response,
		wxString & translatedText,
		wxString & translateionDetails,
		int & translationStatus);
public:
	/// \brief Returns the list of all suported languages
	/// \param result array for storing language names
	static void GetLanguages(wxArrayString & result);
	/**
	\brief Returns language code by language name
	\param languageName language name
	\return language code on success or empty string on error
	*/
	static wxString GetLanguageCode(const wxString & languageName);
	/**
	\brief Translates given text with Google Translate service
	\param source string to translate
	\param result translation result
	\param sourceLanguageCode source language code
	\param resultLanguageCode destination language code
	\param translationDetails error message
	\param errorCode error code
	\return true on success, otherwise false
	*/
	static bool Translate(const wxString & source, 
		wxString & result, 
		const wxString & sourceLanguageCode, 
		const wxString & resultLanguageCode,
		wxString & translationDetails,
		int & errorCode);
};

#endif

Итак, как я уже писал выше, для того чтобы сформировать корректный URL запроса нам необходимо чтобы текст для перевода был URL-encoded. Для этоко пишем функцию URLEncode()

wxString HexFromInt(const int &value)
{
	wxString szHexHolder;

	if( value < 16)
		szHexHolder.Printf(wxT("0%x"), value );
	else
		szHexHolder.Printf(wxT("%x"), value );

	return szHexHolder.MakeUpper();
}

wxString URLEncode(const wxString &value)
{
	wxString szToReturn = wxT("");
	unsigned int nPos = 0;

	while( value.length() > nPos ) 
	{
		wxChar cChar = value.GetChar(nPos);

		if( (cChar >= wxT('0') && cChar <= wxT('9')) || 
			(cChar >= wxT('a') && cChar <= wxT('z')) || 
			(cChar >= wxT('A') && cChar <= wxT('Z')) || 
			(cChar == wxT('-')) || (cChar == wxT('@')) || 
			(cChar == wxT('*')) || (cChar == wxT('_')) )
		{
			szToReturn.Append( cChar );
		}
		else
		{
			switch( cChar )
			{
			case wxT(' '):  szToReturn.Append(wxT('+')); break;
			case wxT('\n'): szToReturn.Append(wxT("%0D%0A")); break;
			default:
				{
					szToReturn.Append(wxT("%"));
					szToReturn += HexFromInt( cChar );
				}
			}
		}
		nPos++;
	}
	return szToReturn;
}
&#91;/sourcecode&#93;
На самом деле большая часть приведенного выше кода позаимствована из библиотеки wxHTTPEngine, но, к сожалению, исходная реализация не работала с русскими символами и пришлось ее немного доработать напильником.
Так, с URL-encoding'ом разобрались, но это еще не все. Перед тем как формировать URL-encoded строку нам необходимо текст преобразовать в UTF-8. Для этого можно использовать метод <strong>wxString::ToUTF8()</strong>.
Список поддерживаемых сервисом языков можно найти на странице описания Google Language API. У нас в программе для этого будет массив структур, каждая из которых будет содержать полное название языка и его код (который можно будет передать в параметр <strong>languagepair</strong>):

wxGoogleTranslate::wxGoogleTranslateLanguageInfo 
	wxGoogleTranslate::m_LanguageInfoArray[] =
{
	{wxT("AFRIKAANS"),				wxT("af")},
	{wxT("ALBANIAN"),				wxT("sq")},
	{wxT("AMHARIC"),				wxT("am")},
	{wxT("ARABIC"),					wxT("ar")},
	{wxT("ARMENIAN"),				wxT("hy")},
	{wxT("AZERBAIJANI"),			wxT("az")},
	{wxT("BASQUE"),					wxT("eu")},
	{wxT("BELARUSIAN"),				wxT("be")},
	{wxT("BENGALI"),				wxT("bn")},
	{wxT("BIHARI"),					wxT("bh")},
	{wxT("BULGARIAN"),				wxT("bg")},
	{wxT("BURMESE"),				wxT("my")},
	{wxT("CATALAN"),				wxT("ca")},
	{wxT("CHEROKEE"),				wxT("chr")},
	{wxT("CHINESE"),				wxT("zh")},
	{wxT("CHINESE_SIMPLIFIED"),		wxT("zh-CN")},
	{wxT("CHINESE_TRADITIONAL"),	wxT("zh-TW")},
	{wxT("CROATIAN"),				wxT("hr")},
	{wxT("CZECH"),					wxT("cs")},
	{wxT("DANISH"),					wxT("da")},
	{wxT("DHIVEHI"),				wxT("dv")},
	{wxT("DUTCH"),					wxT("nl")},
	{wxT("ENGLISH"),				wxT("en")},
	{wxT("ESPERANTO"),				wxT("eo")},
	{wxT("ESTONIAN"),				wxT("et")},
	{wxT("FILIPINO"),				wxT("tl")},
	{wxT("FINNISH"),				wxT("fi")},
	{wxT("FRENCH"),					wxT("fr")},
	{wxT("GALICIAN"),				wxT("gl")},
	{wxT("GEORGIAN"),				wxT("ka")},
	{wxT("GERMAN"),					wxT("de")},
	{wxT("GREEK"),					wxT("el")},
	{wxT("GUARANI"),				wxT("gn")},
	{wxT("GUJARATI"),				wxT("gu")},
	{wxT("HEBREW"),					wxT("iw")},
	{wxT("HINDI"),					wxT("hi")},
	{wxT("HUNGARIAN"),				wxT("hu")},
	{wxT("ICELANDIC"),				wxT("is")},
	{wxT("INDONESIAN"),				wxT("id")},
	{wxT("INUKTITUT"),				wxT("iu")},
	{wxT("ITALIAN"),				wxT("it")},
	{wxT("JAPANESE"),				wxT("ja")},
	{wxT("KANNADA"),				wxT("kn")},
	{wxT("KAZAKH"),					wxT("kk")},
	{wxT("KHMER"),					wxT("km")},
	{wxT("KOREAN"),					wxT("ko")},
	{wxT("KURDISH"),				wxT("ku")},
	{wxT("KYRGYZ"),					wxT("ky")},
	{wxT("LAOTHIAN"),				wxT("lo")},
	{wxT("LATVIAN"),				wxT("lv")},
	{wxT("LITHUANIAN"),				wxT("lt")},
	{wxT("MACEDONIAN"),				wxT("mk")},
	{wxT("MALAY"),					wxT("ms")},
	{wxT("MALAYALAM"),				wxT("ml")},
	{wxT("MALTESE"),				wxT("mt")},
	{wxT("MARATHI"),				wxT("mr")},
	{wxT("MONGOLIAN"),				wxT("mn")},
	{wxT("NEPALI"),					wxT("ne")},
	{wxT("NORWEGIAN"),				wxT("no")},
	{wxT("ORIYA"),					wxT("or")},
	{wxT("PASHTO"),					wxT("ps")},
	{wxT("PERSIAN"),				wxT("fa")},
	{wxT("POLISH"),					wxT("pl")},
	{wxT("PORTUGUESE"),				wxT("pt-PT")},
	{wxT("PUNJABI"),				wxT("pa")},
	{wxT("ROMANIAN"),				wxT("ro")},
	{wxT("RUSSIAN"),				wxT("ru")},
	{wxT("SANSKRIT"),				wxT("sa")},
	{wxT("SERBIAN"),				wxT("sr")},
	{wxT("SINDHI"),					wxT("sd")},
	{wxT("SINHALESE"),				wxT("si")},
	{wxT("SLOVAK"),					wxT("sk")},
	{wxT("SLOVENIAN"),				wxT("sl")},
	{wxT("SPANISH"),				wxT("es")},
	{wxT("SWAHILI"),				wxT("sw")},
	{wxT("SWEDISH"),				wxT("sv")},
	{wxT("TAJIK"),					wxT("tg")},
	{wxT("TAMIL"),					wxT("ta")},
	{wxT("TAGALOG"),				wxT("tl")},
	{wxT("TELUGU"),					wxT("te")},
	{wxT("THAI"),					wxT("th")},
	{wxT("TIBETAN"),				wxT("bo")},
	{wxT("TURKISH"),				wxT("tr")},
	{wxT("UKRAINIAN"),				wxT("uk")},
	{wxT("URDU"),					wxT("ur")},
	{wxT("UZBEK"),					wxT("uz")},
	{wxT("UIGHUR"),					wxT("ug")},
	{wxT("VIETNAMESE"),				wxT("vi")},
	{wxT("UNKNOWN"),				wxEmptyString}
};

Для получения списка языков будет использоваться метод wxGoogleTranslate::GetLanguages()

void wxGoogleTranslate::GetLanguages(wxArrayString & result)
{
	result.Clear();
	int count = sizeof(m_LanguageInfoArray) / 
		sizeof(wxGoogleTranslateLanguageInfo);
	for(int i = 0; i < count; i++)
	{
		result.Add(m_LanguageInfoArray&#91;i&#93;.languageName);
	}
}
&#91;/sourcecode&#93;
Для получения кода языка предназначен метод <strong>wxGoogleTranslate::GetLanguageCode()</strong>:

wxString wxGoogleTranslate::GetLanguageCode(const wxString & languageName)
{
	int count = sizeof(m_LanguageInfoArray) / 
		sizeof(wxGoogleTranslateLanguageInfo);
	for(int i = 0; i < count; i++)
	{
		if(m_LanguageInfoArray&#91;i&#93;.languageName.Lower().IsSameAs(
			languageName.Lower()))
		{
			return m_LanguageInfoArray&#91;i&#93;.languageCode;
		}
	}
	return wxEmptyString;
}
&#91;/sourcecode&#93;
Теперь можно реализовывать непосредственно метод для перевода текста:
&#91;sourcecode language="cpp"&#93;
bool wxGoogleTranslate::Translate(const wxString & source, 
		wxString & result, 
		const wxString & sourceLanguageCode, 
		const wxString & resultLanguageCode,
		wxString & translationDetails,
		int & errorCode)
{
	do
	{
		wxString urlEncodedSource = 
			URLEncode(wxString::FromAscii(source.ToUTF8()));
		wxString srcURL = wxString::Format(
			wxT("%s?v=1.0&q=%s&langpair=%s%%7C%s"),
			wxT("http://ajax.googleapis.com/ajax/services/language/translate"),
			urlEncodedSource.GetData(),
			sourceLanguageCode.GetData(),
			resultLanguageCode.GetData());
		wxURL url = srcURL;
		if(url.GetError() != wxURL_NOERR) break;
		wxInputStream * in = url.GetInputStream();
		if(!in) break;
		wxString response;
		wxStringOutputStream out(&response);
		in->Read(out);
		wxDELETE(in);
		if(response.IsEmpty()) break;
		if(!wxGoogleTranslate::ParseJSONResponse(response, result, 
			translationDetails, errorCode)) break;
		return true;
	}
	while(false);
	return false;
}

Результат запроса приходит в формате JSON. В стандартной поставке wxWidgets библиотеки для парсинга JSON нет, зато есть сторонняя библиотека wxJSON, которую мы и будем использовать.
Формат ответа от сервиса Google Translate:

{"responseData": {"translatedText":"sometext"}, "responseDetails": "sometext", "responseStatus": 200}

Где

  • translatedText – переведенный текст
  • responseDetails – дополнительная информация от сервиса (сообщение об ошибке)
  • responseStatus – код ошибки

Метод для парсинга ответа сервиса:

bool wxGoogleTranslate::ParseJSONResponse(const wxString & response,
		wxString & translatedText,
		wxString & translateionDetails,
		int & translationStatus)
{
	do
	{
		wxJSONValue  root;
		wxJSONReader reader;
		if(reader.Parse(response, &root) > 0) break;
		translatedText = 
			root[wxT("responseData")][wxT("translatedText")].AsString();
		translateionDetails = 
			root[wxT("responseDetails")].AsString();
		translationStatus = 
			root[wxT("responseStatus")].AsInt();
		return true;
	}
	while(false);
	return false;
}

Ну вот, работу над классом переводчика закончили. Теперь посмотрим как его использовать:

void wxGoogleTranslateClientMainFrame::OnTRANSLATEClick( wxCommandEvent& event )
{
	do
	{
		wxString result;
		wxString details;
		int errorCode(0);
		int sourceLangselection = m_SourceLanguageChoice->GetSelection();
		int resultLangselection = m_ResultLanguageChoice->GetSelection();
		if((sourceLangselection < 0) || (resultLangselection < 0)) break;
		if(wxGoogleTranslate::Translate(m_SourceTextCtrl->GetValue(), 
			result, 
			wxGoogleTranslate::GetLanguageCode(
				m_SourceLanguageChoice->GetString(sourceLangselection)), 
			wxGoogleTranslate::GetLanguageCode(
			m_ResultLanguageChoice->GetString(resultLangselection)), 
				details, errorCode))
		{
			m_ResultTextCtrl->SetValue(result);
		}
		else
		{
			wxMessageBox(wxString::Format(_("Erorr occured: %s"), details.GetData()));
		}
	}
	while(false);
}

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

Исходный код библиотеки wxGoogleTranslate, а также исходный код и исполняемые файлы примера для Windows NT/2000/XP/Vista и для Windows Mobile 2003/5/6 можно загрузить здесь.

Previous ArticleNext Article
Технический директор IT-Dimension, компании-разработчика кросс-платформенного программного обеспечения

This post has 4 Comments

4
  1. ну все пишешь, пишешь, работаешь аки пчела 🙂
    Вот и я говорю, молодец.

Leave a Reply

Your email address will not be published. Required fields are marked *

В.

Вышел Smart Device Framework 2.3

Разработчики OpenNETCF выпустили новую версию библиотеки Smart Device Framework, которая содержит множество полезных классов, значительно упрощающих разработку приложений для Windows Mobile на платформе .NET. Есть бесплатная версия Smart Device Framework. По большому счету из недостатков бесплатной версии по сравнению с платными можно назвать отсутствие интеграции с Visual Studio и дизайнером форм.

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

К.

Как программно установить GPRS-соединение в Windows Mobile

Для того чтобы установить соединение с Internet в Windows Mobile предусмотрен специальный программный интерфейс – Connection Manager API.
Пользоваться им довольно просто. И вот пример такого использования для C# и C++ приведен ниже:

C# + .NET Compact Framework

using System;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Threading;
using System.Reflection;

namespace GPRSConnection
{
    public class GPRSConnection
    {
        const int S_OK = 0;
        const uint CONNMGR_PARAM_GUIDDESTNET = 0x1;
        const uint CONNMGR_FLAG_PROXY_HTTP = 0x1;
        const uint CONNMGR_PRIORITY_USERINTERACTIVE = 0x08000;
        const uint INFINITE = 0xffffffff;
        const uint CONNMGR_STATUS_CONNECTED = 0x10;
        static Hashtable ht = new Hashtable();

        static GPRSConnection()
        {
            ManualResetEvent mre = new ManualResetEvent(false);
            mre.Handle = ConnMgrApiReadyEvent();
            mre.WaitOne();
            CloseHandle(mre.Handle);
        }

        ~GPRSConnection()
        {
            ReleaseAll();
        }

        public static bool Setup(Uri url)
        {
            return Setup(url.ToString());
        }

        public static bool Setup(string urlStr)
        {
            ConnectionInfo ci = new ConnectionInfo();
            IntPtr phConnection = IntPtr.Zero;
            uint status = 0;

            if (ht[urlStr] != null)
                return true;

            if (ConnMgrMapURL(urlStr, ref ci.guidDestNet, IntPtr.Zero) != S_OK)
                return false;

            ci.cbSize = (uint)Marshal.SizeOf(ci);
            ci.dwParams = CONNMGR_PARAM_GUIDDESTNET;
            ci.dwFlags = CONNMGR_FLAG_PROXY_HTTP;
            ci.dwPriority = CONNMGR_PRIORITY_USERINTERACTIVE;
            ci.bExclusive = 0;
            ci.bDisabled = 0;
            ci.hWnd = IntPtr.Zero;
            ci.uMsg = 0;
            ci.lParam = 0;

            if (ConnMgrEstablishConnectionSync(ref ci, ref phConnection, INFINITE, ref status) != S_OK &&
                status != CONNMGR_STATUS_CONNECTED)
                return false;

            ht[urlStr] = phConnection;
            return true;
        }

        public static bool Release(Uri url)
        {
            return Release(url.ToString());
        }

        public static bool Release(string urlStr)
        {
            return Release(urlStr, true);
        }

        private static bool Release(string urlStr, bool removeNode)
        {
            bool res = true;
            IntPtr ph = IntPtr.Zero;
            if (ht[urlStr] == null)
                return true;
            ph = (IntPtr)ht[urlStr];
            if (ConnMgrReleaseConnection(ph, 1) != S_OK)
                res = false;
            CloseHandle(ph);
            if (removeNode)
                ht.Remove(urlStr);
            return res;
        }

        public static void ReleaseAll()
        {
            foreach (DictionaryEntry de in ht)
            {
                Release((string)de.Key, false);
            }
            ht.Clear();
        }

        [StructLayout(LayoutKind.Sequential)]
        public struct ConnectionInfo
        {
            public uint cbSize;
            public uint dwParams;
            public uint dwFlags;
            public uint dwPriority;
            public int bExclusive;
            public int bDisabled;
            public Guid guidDestNet;
            public IntPtr hWnd;
            public uint uMsg;
            public uint lParam;
            public uint ulMaxCost;
            public uint ulMinRcvBw;
            public uint ulMaxConnLatency;
        }

        [DllImport("cellcore.dll")]
        private static extern int ConnMgrMapURL(string pwszURL, ref Guid pguid, IntPtr pdwIndex);

        [DllImport("cellcore.dll")]
        private static extern int ConnMgrEstablishConnectionSync(ref ConnectionInfo ci, ref IntPtr phConnection, uint dwTimeout, ref uint pdwStatus);

        [DllImport("cellcore.dll")]
        private static extern IntPtr ConnMgrApiReadyEvent();

        [DllImport("cellcore.dll")]
        private static extern int ConnMgrReleaseConnection(IntPtr hConnection, int bCache);

        [DllImport("coredll.dll")]
        private static extern int CloseHandle(IntPtr hObject);
    }
}

Пример использования

public void DoTcpConnection()
        {
            string t = System.IO.Path.AltDirectorySeparatorChar.ToString() + System.IO.Path.DirectorySeparatorChar.ToString();
            string url = "www.msn.com";
            bool res = GPRSConnection.Setup(Uri.UriSchemeHttp + 
                Uri.SchemeDelimiter + url + Path.AltDirectorySeparatorChar);
            if (res)
            {
                TcpClient tc = new TcpClient(url, 80);
                NetworkStream ns = tc.GetStream();
                byte[] buf = new byte[100];
                ns.Write(buf, 0, 100);
                tc.Client.Shutdown(SocketShutdown.Both);
                ns.Close();
                tc.Close();
                MessageBox.Show("Wrote 100 bytes");
            }
            else
            {
                MessageBox.Show("Connection establishment failed");
            }
        }

C++

Singleton.h
#ifndef _SINGLETON_H
#define _SINGLETON_H

template<typename T> class Singleton
{
public:
	static T & Instance()
	{
		static T instance;
		return instance;
	}
};

#endif
GPRSConnection.h
#pragma once

#include <windows.h>
#include <connmgr.h>
#include <map>
#include "Singleton.h"

class GPRSConnection : public Singleton<GPRSConnection>
{
	friend class Singleton<GPRSConnection>;
#ifndef _UNICODE
	typedef std::string StringType;
#else
	typedef std::wstring StringType;
#endif
	bool apiReady;
	std::map<StringType, HANDLE> Connections;
protected:
	GPRSConnection() : apiReady(false)
	{
		do
		{
			HANDLE apiHandle = ConnMgrApiReadyEvent();
			if(!apiHandle) break;
			if(WaitForSingleObject(apiHandle, 10000) != WAIT_OBJECT_0) break;
			CloseHandle(apiHandle);
			apiReady = true;
		}
		while(false);
	}
public:
	~GPRSConnection()
	{
		ReleaseAll();
	}

	bool Setup(LPCTSTR url)
	{
		StringType urlInternal = url;
		if(Connections[urlInternal] != NULL) return true;
		CONNMGR_CONNECTIONINFO ci;
		if(ConnMgrMapURL(url, &ci.guidDestNet, NULL) != S_OK) return false;
		ci.cbSize = sizeof(CONNMGR_CONNECTIONINFO);
		ci.dwParams = CONNMGR_PARAM_GUIDDESTNET;
		ci.dwFlags = CONNMGR_FLAG_PROXY_HTTP;
		ci.dwPriority = CONNMGR_PRIORITY_USERINTERACTIVE;
		ci.bExclusive = 0;
		ci.bDisabled = 0;
		ci.hWnd = NULL;
		ci.uMsg = 0;

		DWORD status = CONNMGR_STATUS_UNKNOWN;
		HANDLE phConnection;
		if (ConnMgrEstablishConnectionSync(&ci, &phConnection, -1, &status) != S_OK &&
			status != CONNMGR_STATUS_CONNECTED) return false;

		Connections[urlInternal] = phConnection;
		return true;
	}

	bool Release(LPCTSTR url, bool removeNode)
	{
		StringType urlInternal = url;
		bool res = true;
		if(Connections[urlInternal] == NULL) return true;
		if (ConnMgrReleaseConnection(Connections[urlInternal], 1) != S_OK) res = false;
		CloseHandle(Connections[urlInternal]);
		if(removeNode)
		{
			Connections.erase(urlInternal);
		}
		else 
		{
			Connections[urlInternal] = NULL;
		}
		return res;
	}

	void ReleaseAll()
	{
		for(std::map<StringType, HANDLE>::iterator i = Connections.begin(); i != Connections.end(); ++i)
		{
			Release(i->first.c_str(), false);
		}
		Connections.clear();
	}
};

Пример использования

void MainFrame::OnCONNECTClick( wxCommandEvent& event )
{
	TransferDataFromWindow();
	bool res = GPRSConnection::Instance().Setup(m_URL.GetData());
	if(res)
	{
		wxLogMessage(_("Connection established"));
		// Дальше здесь можно делать всякие штуки
	}
	else
	{
		wxLogMessage(_("Connection error"));
	}
}

Скачать исходный код к статье (C++/C#)