#
最近由于工作需求,需要为游戏Android平台接入Google翻译的SDK,由于关于翻译的文章非常少,访问官方文档又需要翻墙,更可气的是找到的博客写了一大堆结果是错的,官网给的库跟链接也不对,导致耽搁了挺长一段时间。今天我就分享下我的踩坑之旅,希望这篇博客能帮助到也需要接Google翻译的人。
废话不多说,首先使用Google翻译是需要翻墙的,请自备VPN。关于怎么注册申请翻译API请看这篇博客需要注意的一点是Android平台需要申请参数APIKey,在申请时是需要选择Android apps 并需要填入应用包名 和应用签名的SHA1字符串的,假如你申请时没有填入这两项,那你申请的可能不是Android的翻译API。
参数申请完毕后还需要下载一个jar包 guava.jar ,点这进入下载页。本来我是看了这篇博客进行接入的 http://blog.csdn.net/nicolelili1/article/details/76973097 ,由于公司项目是eclipse工程,我把依赖里的jar包全部下载,放到工程里大概10M左右,并且方法数超出65535限制,使用dex分包方案后测试发现翻译失败,Log打印错误日志如下:
{
"code": 403,
"errors": [{
"domain": "global",
"message": "Requests from this Android client application <empty> are blocked.",
"reason": "forbidden"
}],
"message": "Requests from this Android client application <empty> are blocked.",
"status": "PERMISSION_DENIED"
}
在 stackoverflow 上看到一个一模一样的问题,看了半天发现说的不是翻译。。。然后接着换了关键词去搜,发现这里有一个回答有点靠谱,一个老外发的帖子说自己用OKHttp接入谷歌翻译出错,虽然错误内容不一样,但有个回复引起了我的注意:
大概意思是说在调用 Google翻译URL进行翻译时需要在请求头加上"X-Android-Package" 包名和"X-Android-Cert"应用签名SHA1然后给了获取SHA1的示例代码:
/**
* Gets the SHA1 signature, hex encoded for inclusion with Google Cloud Platform API requests
*
* @param packageName Identifies the APK whose signature should be extracted.
* @return a lowercase, hex-encoded
*/
public static String getSignature(@NonNull PackageManager pm, @NonNull String packageName) {
try {
PackageInfo packageInfo = pm.getPackageInfo(packageName, PackageManager.GET_SIGNATURES);
if (packageInfo == null
|| packageInfo.signatures == null
|| packageInfo.signatures.length == 0
|| packageInfo.signatures[0] == null) {
return null;
}
return signatureDigest(packageInfo.signatures[0]);
} catch (PackageManager.NameNotFoundException e) {
return null;
}
}
private static String signatureDigest(Signature sig) {
byte[] signature = sig.toByteArray();
try {
MessageDigest md = MessageDigest.getInstance("SHA1");
byte[] digest = md.digest(signature);
return BaseEncoding.base16().lowerCase().encode(digest);
} catch (NoSuchAlgorithmException e) {
return null;
}
}
以及如何使用HttpulrConnection进行正确的请求:
java.net.URL url = new URL(REQUEST_URL);
HttpURLConnection connection = (HttpURLConnection)url.openConnection();
try {
connection.setDoInput(true);
connection.setDoOutput(true);
connection.setRequestProperty("Content-Type", "application/json; charset=UTF-8");
connection.setRequestProperty("Accept", "application/json");
// add package name to request header
String packageName = mActivity.getPackageName();
connection.setRequestProperty("X-Android-Package", packageName);
// add SHA certificate to request header
String sig = getSignature(mActivity.getPackageManager(), packageName);
connection.setRequestProperty("X-Android-Cert", sig);
connection.setRequestMethod("POST");
// ADD YOUR REQUEST BODY HERE
// ....................
} catch (Exception e) {
e.printStackTrace();
} finally {
connection.disconnect();
}
在此基础上我写了Demo进行测试,不过网络框架我是用的OkHttp,在有VPN的情况下是可以翻译成功的,没有VPN可能会报链接超时的错误。由于需要分dex,我把代码打成了jar包: 源码下载 jar包下载
其实就是使用链接访问google翻译API,Google翻译链接地址是这个: https://www.googleapis.com/language/translate/v2 其中需要传入几个参数:
- key:apiKey
- target:目标语言(en zh等语言代号)
- q:要翻译的内容(该参数是可以传递多个的,如:…url?q=内容&q=测试)
以及要在请求头加上 X-Android-Package 和 X-Android-Cert 信息,只要参数传递无误,并且有翻墙软件开启,得到的翻译结果格式如下:
{
"data": {
"translations": [
{
"translatedText": "test",
"detectedSourceLanguage": "zh-CN"
}
]
}
}
translatedText 就是翻译结果,detectedSourceLanguage是
考虑到有的朋友不能访问github下载代码,在此处贴入具体代码:
public class OkHttpFactory {
private static OkHttpClient okHttpClient;
public static OkHttpClient getOkHttpClient(){
if(okHttpClient == null){
synchronized (OkHttpFactory.class){
if(okHttpClient == null){
okHttpClient = new OkHttpClient.Builder()
.connectTimeout(20, TimeUnit.SECONDS)
.build();
}
}
}
return okHttpClient;
}
}
public interface TranslationCallback {
void onTranslationSuccess(String resultJson);
void onTranslationFailed(String errMsg);
}
public class Translation {
private String apiKey = "";
private static String REQUEST_URL = "https://www.googleapis.com/language/translate/v2";
private static String PACKAGE_KEY = "X-Android-Package";
private static String SHA1_KEY = "X-Android-Cert";
public Translation(String apiKey){
this.apiKey = apiKey;
}
public void translate(Context context,String content,String targetLanguage, TranslationCallback callback){
JSONArray array = new JSONArray();
array.put(content);
translate(context,array,targetLanguage,callback);
}
public void translate(Context context, JSONArray contents, String targetLanguage, final TranslationCallback callback){
OkHttpClient okHttpClient = OkHttpFactory.getOkHttpClient();
String sig = getSignature(context.getPackageManager(),context.getPackageName());
FormBody.Builder builder = new FormBody.Builder()
.add("key", apiKey)
.add("target", targetLanguage);
for (int i = 0; i < contents.length(); i++) {
String text;
try {
text = contents.getString(i);
builder.add("q",text);
} catch (JSONException e) {
e.printStackTrace();
}
}
FormBody formBody = builder.build();
Request request = new Request.Builder()
.url(REQUEST_URL)
.header(PACKAGE_KEY,context.getPackageName())
.header(SHA1_KEY,sig)
.post(formBody)
.build();
Call call = okHttpClient.newCall(request);
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
callback.onTranslationFailed(e == null?"null":e.getMessage());
}
@Override
public void onResponse(Call call, Response response) throws IOException {
callback.onTranslationSuccess(response.body().string());
}
});
}
/**
* Gets the SHA1 signature, hex encoded for inclusion with Google Cloud Platform API requests
*
* @param packageName Identifies the APK whose signature should be extracted.
* @return a lowercase, hex-encoded
*/
private static String getSignature(PackageManager pm, String packageName) {
try {
PackageInfo packageInfo = pm.getPackageInfo(packageName, PackageManager.GET_SIGNATURES);
if (packageInfo == null
|| packageInfo.signatures == null
|| packageInfo.signatures.length == 0
|| packageInfo.signatures[0] == null) {
return null;
}
return signatureDigest(packageInfo.signatures[0]);
} catch (PackageManager.NameNotFoundException e) {
return null;
}
}
private static String signatureDigest(Signature sig) {
byte[] signature = sig.toByteArray();
try {
MessageDigest md = MessageDigest.getInstance("SHA1");
byte[] digest = md.digest(signature);
return BaseEncoding.base16().lowerCase().encode(digest);
} catch (NoSuchAlgorithmException e) {
return null;
}
}
}