株式会社ライブキャストロゴ 株式会社ライブキャスト

2011年11月3日 追記
ご注意!
Google Translate API v1が2011年12月1日にサービスが終了いたします。添付のサンプルソースでは、Google Translate API v1を利用していますので、翻訳が正常に機能しない場合がございます。ご承知おきください。

Android Market公開を目指してAndroidアプリを開発する!(メイン画面作成編)の続きです。

今回は、google翻訳APIを呼び出す部分を実装します。

過去に、TranslatAIRでActionScriptを使って実装しているので、それをJavaに移植します。

まず、前回ご紹介したSpinnerコントロールを初期化する部分を少し修正します。なぜかというと、google翻訳APIに対して、「英語を日本語に訳して」というような指示をするには、langpairパラメータを指定しますが、「English」ではなく「en」というように指定します。この例で言いますと「langpair=en|ja」となります。

Spinnerコントロールのラベルと値を使い分けられるようにします。

strings.xmlに以下の部分を追記します。

    <string-array name="language_value">
        <item>en</item>
        <item>ja</item>
        <item>zh</item>
        <item>it</item>
        <item>es</item>
        <item>fr</item>
        <item>de</item>
        <item>ru</item>
        <item>ko</item>
    </string-array>

Spinnerコントロールのラベルと値を保持するためのモデルクラスを作成します。

package jp.flashcast.translator.android.model;

public class LanguageModel {
	private String label;
	private String value;

	public LanguageModel(String label, String value) {
		this.label = label;
		this.value = value;
	}

	public void setLabel(String label) {
		this.label = label;
	}

	public String getLabel() {
		return label;
	}

	public void setValue(String value) {
		this.value = value;
	}

	public String getValue() {
		return value;
	}

	@Override
	public String toString() {
		return label;
	}
}
toString()メソッドをオーバーライドすると、その結果を、Spinnerコントールのリストに表示することができます。

次に、Spinnerコントロールに値も格納するように、前回のサンプルを一部修正します。

●修正前

	String[] labels = getResources().getStringArray(R.array.language_label);
	ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, android.R.layout.simple_spinner_item, labels);
	adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);

●修正後

    	String[] labels = getResources().getStringArray(R.array.language_label);
    	String[] values = getResources().getStringArray(R.array.language_value);
    	ArrayAdapter<LanguageModel> adapter = new ArrayAdapter<LanguageModel>(this, android.R.layout.simple_spinner_item);
    	adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);

翻訳に必要な情報をまとめるためのモデルクラスを作成しました。

package jp.flashcast.translator.android.model;

public class TranslateModel {
	private String original;
	private String translated;
	private String srcLanguage;
	private String dstLanguage;
	private String dt;
	private boolean success;

	public TranslateModel() {
		this.original = "";
		this.translated = "";
		this.srcLanguage = "en";
		this.dstLanguage = "ja";
		this.success = false;
	}

	public void setOriginal(String original) {
		this.original = original;
	}

	public String getOriginal() {
		return original;
	}

	public void setTranslated(String translated) {
		this.translated = translated;
	}

	public String getTranslated() {
		return translated;
	}

	public void setSrcLanguage(String srcLanguage) {
		this.srcLanguage = srcLanguage;
	}

	public String getSrcLanguage() {
		return srcLanguage;
	}

	public void setDstLanguage(String dstLanguage) {
		this.dstLanguage = dstLanguage;
	}

	public String getDstLanguage() {
		return dstLanguage;
	}

	public void setDt(String dt) {
		this.dt = dt;
	}

	public String getDt() {
		return dt;
	}

	public void setSuccess(boolean success) {
		this.success = success;
	}

	public boolean isSuccess() {
		return success;
	}
}

翻訳APIを呼び出すところを実装します。
こちらもやり方いろいろあると思いますが、

長時間実行されうるネットワーク操作・データベース操作や、ビットマップのリサイズといった計算時間のかかるものについては、サブスレッド内で実行すべきです(あるいはデータベースアクセスについては非同期リクエストを利用する方法もあります)。

みゅお(muo_jp)によるAndroidのドキュメント翻訳より引用。

ということなので、Threadクラスを継承した別クラスで処理させることにしました。

package jp.flashcast.translator.android.thread;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Date;

import jp.flashcast.translator.android.model.TranslateModel;

import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.NameValuePair;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.json.JSONException;
import org.json.JSONObject;

import android.os.Handler;

public class TranslateThread extends Thread {
	private Handler handler;
	private Runnable runnable;
	private final HttpClient client;
	private final HttpPost post;
	private final static String G_TRANSLATE_API_URL = "http://ajax.googleapis.com/ajax/services/language/translate";
	private final static String G_TRANSLATE_API_VERSION = "1.0";
	private TranslateModel model;

	public TranslateThread(Handler handler, Runnable runnable, TranslateModel model) {
		this.handler = handler;
		this.runnable = runnable;
		this.model = model;
		this.client = new DefaultHttpClient();
		this.post = new HttpPost(G_TRANSLATE_API_URL);
	}

	private String Translate(String query, String srcLanguage, String dstLanguage) {
		ArrayList<NameValuePair> params = new ArrayList<NameValuePair>(4);
		params.add(new BasicNameValuePair("v", G_TRANSLATE_API_VERSION));
		params.add(new BasicNameValuePair("q", query));
		params.add(new BasicNameValuePair("format", "text"));
		params.add(new BasicNameValuePair("langpair", srcLanguage + "|" + dstLanguage));

		StringBuilder builder = new StringBuilder();

		try {
			post.setEntity(new UrlEncodedFormEntity(params, "UTF-8"));
			final HttpResponse response = client.execute(post);

			if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
				InputStream stream = response.getEntity().getContent();
				BufferedReader reader = new BufferedReader(new InputStreamReader(stream));

				String line;

				while ((line = reader.readLine()) != null) {
					builder.append(line);
				}
				stream.close();
			}
		} catch (UnsupportedEncodingException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (ClientProtocolException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		return builder.toString();
	}

	@Override
	public void run() {
		if (model != null) {
			String translated = Translate(model.getOriginal(), model.getSrcLanguage(), model.getDstLanguage());

			try {
				JSONObject json = new JSONObject(translated);

				if (json.has("responseStatus") && json.getInt("responseStatus") == 200) {
					model.setTranslated(json.getJSONObject("responseData").getString("translatedText"));
					model.setDt(new Date().toLocaleString());
					model.setSuccess(true);
				}
				else {
					model.setSuccess(false);
				}
			} catch (JSONException e) {
				model.setSuccess(false);
			}

			handler.post(runnable);
		}
	}
}

呼び出し部分は、こんな感じです。

    private void Translate() {
    	if (dialog == null) {
    		dialog = new ProgressDialog(TranslatorSample2.this);
    		dialog.setIndeterminate(true);
    	}
		dialog.setMessage("Translating... Please wait.");
    	dialog.show();

    	(new TranslateThread(new Handler(), TranslatorSample2.this, model)).start();
    }

処理中のダイアログを出してから、翻訳処理に入ります。
こんな感じです。

device

この関数を、前回実装していなかったボタンクリック時の処理で呼び出します。

        btnTranslate.setOnClickListener(new OnClickListener() {

			public void onClick(View v) {
    			EditText text;
    			Spinner spSrc;
    			Spinner spDst;

    			ToggleButton btnVector = (ToggleButton)findViewById(R.id.vector);

    			if (btnVector.isChecked()) {
    				text = (EditText)findViewById(R.id.dsttext);
    				spSrc = (Spinner)findViewById(R.id.dstlanguage);
    				spDst = (Spinner)findViewById(R.id.srclanguage);
    			}
    			else {
    				text = (EditText)findViewById(R.id.srctext);
    				spSrc = (Spinner)findViewById(R.id.srclanguage);
    				spDst = (Spinner)findViewById(R.id.dstlanguage);
    			}

    			if (!text.getText().toString().equals("")) {
    				if (model == null) {
    					model = new TranslateModel();
    				}

    				model.setOriginal(text.getText().toString());
    				model.setSrcLanguage(((LanguageModel)spSrc.getSelectedItem()).getValue());
    				model.setDstLanguage(((LanguageModel)spDst.getSelectedItem()).getValue());

    				Translate();
    			}
		}

翻訳が終わったら、処理中ダイアログを消して、翻訳結果を画面に表示させますので、Runnableインターフェイスを実装します。

public class TranslatorSample2 extends Activity implements Runnable {

run()メソッドを実装して、翻訳の後処理をします。

	public void run() {
		dialog.dismiss();

		if (model.isSuccess()) {
			((EditText)findViewById(
				(!((ToggleButton)findViewById(R.id.vector)).isChecked()) ? R.id.dsttext : R.id.srctext))
				.setText(model.getTranslated());
		}
		else {
			Toast.makeText(this, "Failed to translate...", Toast.LENGTH_SHORT).show();
		}
	}

最後に、アプリケーションに対して、Internetに接続する権限を付与します。AndroidManifest.xmlに1行追記します。

	<uses-permission android:name="android.permission.INTERNET" />

これで、Internetに接続し、9ヶ国語の翻訳が出来るようになりました!

サンプルソース