Reputation: 526
Before seeing conflicts when building a release for my app because of googleapis/java-translate library, I used these lines and it worked perfectly :
val translate: Translate = TranslateOptions.newBuilder().setApiKey(API_KEY).build().service
val translations = translate.translate(
textsToTranslate,
Translate.TranslateOption.sourceLanguage(sourceLanguage),
Translate.TranslateOption.targetLanguage(targetLanguage)
)
Then, when building release, I had to add some extra code in app/build.gradle to make it works. But, I saw that my app grows from 10Mo to 15Mo. Expensive just for translating some texts..
So I decide to perform these translations by myself using the latest Google Translate Api v3 link
Perform a simple api rest request like this :
val jsonObjectResponse = JSONObject(translate(sourceLanguageCode = sourceLanguage, targetLanguageCode = targetLanguage, textsToTranslate).awaitString())
val translations = jsonObjectResponse.getJSONArray("translations").toArray { getJSONObject(it) }.map { it.getString("translatedText") }
where "translate" function is :
private fun translate(sourceLanguageCode: String, targetLanguageCode: String, textsToTranslate: List<String>): Request =
Fuel.post(path = "https://translation.googleapis.com/v3/projects/$PROJECT_ID:translateText?key=$API_KEY")
.header(Headers.ACCEPT to "application/json")
.jsonBody(
JSONObject().apply {
put("contents", JSONArray().apply {
textsToTranslate.forEach { text -> put(text) }
})
put("sourceLanguageCode", sourceLanguageCode)
put("targetLanguageCode", targetLanguageCode)
}.toString()
)
but it returns : "HTTP Exception 401 Unautorhorized". The link doesn't mention usage of API_KEY so I suppose it's related to..
Note : Fuel is just a HTTP networking library for Kotlin/Android to avoid boiler plate code.
To resume: the first one using googleapis/java-translate was working but the second (custom) doesn't work and returns : "HTTP Exception 401 Unautorhorized".
Where am I wrong ?
Extra note: I know I'm not restricting right now to the package name of the android app but here, I just want to make it works :)
Edit
Edit - 03 december 2020
Goodbye bounty [400]
Ended up using Google Translate API v2 with REST :
// portion of suspend function code
coroutineScope {
async(Dispatchers.IO) {
try {
//handleTranslateV3Response(requests, JSONObject(translateV3(sourceLanguageCode = sourceLanguage, targetLanguageCode = targetLanguage, textsToTranslate = textsToTranslate).awaitString()))
//handleTranslateV2Response(requests, JSONObject(translateV2RestrictedToApplication(targetLanguageCode = targetLanguage, textsToTranslate = textsToTranslate, signature = signature).awaitString()))
handleTranslateV2Response(requests, JSONObject(translateV2(targetLanguageCode = targetLanguage, textsToTranslate = textsToTranslate).awaitString()))
} catch (e: Exception) {
Timber.tag("Translate").e(e)
val message: String = "An error occurred while translating ${sourceLanguage} to ${targetLanguage}"
requests.mapIndexed { index, entityTranslationRequest ->
TranslationResponse.Error(message, entityTranslationRequest)
}
}
}
}
/**
* Usage of Google Translate API v2 without restriction
* This function works properly
*/
private fun translateV2(targetLanguageCode: String, textsToTranslate: List<String>): Request =
Fuel.post(path = "https://translation.googleapis.com/language/translate/v2?key=$API_KEY")
.header(Headers.ACCEPT to "application/json")
.jsonBody(
JSONObject().apply {
put("q", JSONArray().apply {
textsToTranslate.forEach { text -> put(text) }
})
put("target", targetLanguageCode)
}.toString()
)
.header(Headers.CONTENT_TYPE to "application/json; charset=utf-8")
private fun handleTranslateV2Response(requests: List<EntityTranslationRequest>, jsonObject: JSONObject): List<TranslationResponse> {
val translations = jsonObject.getJSONObject("data").getJSONArray("translations").toArray { getJSONObject(it) }.map { it.getString("translatedText") }
return requests.mapIndexed { index, entityTranslationRequest ->
TranslationResponse.Success(translations[index], entityTranslationRequest)
}
}
When using Android application restriction on this API KEY, the code below don't work. According to this stackoverflow link and this stackoverflow link and this stackoverflow link from the same issue. There is no proper way to send fingerprints by ourselves without using one of multiple google libraries which handles Auth2, credentials, etc...
/**
* Usage of Google Translate API v2 with restriction to android app
* This function doesn't work and returns a "HTTP Exception 403 Forbidden"
* @param signature SHA-1
*/
private fun translateV2RestrictedToApplication(signature: String, targetLanguageCode: String, textsToTranslate: List<String>): Request =
Fuel.post(path = "https://translation.googleapis.com/language/translate/v2?key=$API_KEY")
.header(Headers.ACCEPT to "application/json")
.header("X-Android-Package" to BuildConfig.APPLICATION_ID)
.header("X-Android-Cert" to signature.toLowerCase())
.jsonBody(
JSONObject().apply {
put("q", JSONArray().apply {
textsToTranslate.forEach { text -> put(text) }
})
put("target", targetLanguageCode)
}.toString()
)
.header(Headers.CONTENT_TYPE to "application/json; charset=utf-8")
Below is the code for the v3 which don't work because it requires token which seems to be generate only from Google libraries and I'm tired of documentation splitted everywhere, makes me sick.
/**
* Usage of Google Translate API v3
* This function doesn't work and returns a "HTTP Exception 401 Unautorhorized"
*/
private fun translateV3(sourceLanguageCode: String, targetLanguageCode: String, textsToTranslate: List<String>): Request =
Fuel.post(path = "https://translation.googleapis.com/v3/projects/$PROJECT_ID:translateText")
.header(Headers.ACCEPT to "application/json")
.header(Headers.AUTHORIZATION to "Bearer {token provided by some Google libraries I guess}")
.jsonBody(
JSONObject().apply {
put("contents", JSONArray().apply {
textsToTranslate.forEach { text -> put(text) }
})
put("sourceLanguageCode", sourceLanguageCode)
put("targetLanguageCode", targetLanguageCode)
}.toString()
)
.header(Headers.CONTENT_TYPE to "application/json; charset=utf-8")
private fun handleTranslateV3Response(requests: List<EntityTranslationRequest>, jsonObject: JSONObject): List<TranslationResponse> {
val translations = jsonObject.getJSONArray("translations").toArray { getJSONObject(it) }.map { it.getString("translatedText") }
return requests.mapIndexed { index, entityTranslationRequest ->
TranslationResponse.Success(translations[index], entityTranslationRequest)
}
}
Upvotes: 5
Views: 549
Reputation:
I have got the same error from IBM Watson Translator and solved it yet so I thought the error are coming
I have got this tutorial as the easiest way to do it tho in v3. and to start the you need the authorization and content type request from the header, you need to do like
//okhttp3 library used
fun doRequest(){
val json = """${data("en","fr","Hello World")}"""
val request = RequestBody.create(MediaType.parse("application/json; charset=utf-8"), json);
val response = Request.Builder()
.url("https://translation.googleapis.com/v3/projects/project-number-or-id/locations/us-central1:translateText")
.post(request)
.addHeader("Authorization", "Bearer YOUR_API_GOES_HERE")
.addHeader("Content-Type", "application/json; charset=utf-8")
.build()
val answer = client.newCall(response).execute().body()!!.string()
print(answer)
}
WARNING: if the API_KEY have space like "apikey SFKHNKSJH-CSDFSCU" you need to convert it to single key using this kind of tool
fun data(fromLang: String, toLang: String, word: String): String{
return """{
"sourceLanguageCode": "${fromLang}",
"targetLanguageCodes": "${toLang}",
"contents": ["${word}"]
}"""
}
These docs might help you.
Upvotes: 1