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
- jsonBody function add implicitly header "Content-Type" to "application/json"
- If apiKey can't be use in v3, which other way can I use ? I already have firebase Crashlytics, it means I already have a google service to potentially handle a communication to a google service but there's way too much documentation going all over the place, it's just terrible, it makes me sick. Which library can I use to handle credential in android application without breaking my app for "Meta-*** conflict", etc. I'm confused and lost in this ocean of information.
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)
}
}