如何在 Android 项目中使用谷歌翻译 api v3 将 googleapis/java-translate 库转换为自定义 api rest 请求

sok*_*ive 5 api rest android kotlin google-translation-api

由于googleapis/java-translate库,在为我的应用程序构建版本时看到冲突之前,我使用了这些行并且它工作得很好:

    val translate: Translate = TranslateOptions.newBuilder().setApiKey(API_KEY).build().service
    val translations = translate.translate(
         textsToTranslate,
         Translate.TranslateOption.sourceLanguage(sourceLanguage),
         Translate.TranslateOption.targetLanguage(targetLanguage)
    )
Run Code Online (Sandbox Code Playgroud)

然后,在构建 release 时,我不得不在 app/build.gradle 中添加一些额外的代码以使其工作。但是,我看到我的应用程序从 10Mo 增长到 15Mo。只是翻译一些文本就很贵..

所以我决定使用最新的 Google Translate Api v3链接自己执行这些翻译

执行一个简单的 api 休息请求,如下所示:

    val jsonObjectResponse = JSONObject(translate(sourceLanguageCode = sourceLanguage, targetLanguageCode = targetLanguage, textsToTranslate).awaitString())
    val translations = jsonObjectResponse.getJSONArray("translations").toArray { getJSONObject(it) }.map { it.getString("translatedText") }
Run Code Online (Sandbox Code Playgroud)

其中“翻译”功能是:

    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()
            )
Run Code Online (Sandbox Code Playgroud)

但它返回:“HTTP Exception 401 Unautorhorized”。该链接没有提到 API_KEY 的使用,所以我想它与..

注意:Fuel只是一个用于 Kotlin/Android 的 HTTP 网络库,以避免样板代码。

继续:第一个使用 googleapis/java-translate 正在工作,但第二个(自定义)不起作用并返回:“HTTP Exception 401 Unautorhorized”。

我哪里错了?

额外说明:我知道我现在并没有限制 android 应用程序的包名称,但在这里,我只是想让它工作:)

编辑

  • jsonBody 函数将隐式标题“Content-Type”添加到“application/json”
  • 如果 apiKey 不能在 v3 中使用,我可以使用哪种其他方式?我已经有了 firebase Crashlytics,这意味着我已经有了一个 google 服务,可以处理与 google 服务的通信,但是到处都有太多的文档,这太糟糕了,这让我感到恶心。我可以使用哪个库来处理 android 应用程序中的凭证,而不会因为“Meta-*** 冲突”等原因破坏我的应用程序。我很困惑,迷失在这片信息的海洋中。

编辑 - 2020 年 12 月 3 日

再见赏金 [400]

最终将 Google Translate API v2 与 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)
                }
            }
        }
    }
Run Code Online (Sandbox Code Playgroud)
    /**
     * 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)
        }
    }
Run Code Online (Sandbox Code Playgroud)

在此 API KEY 上使用 Android 应用程序限制时,以下代码不起作用。根据this stackoverflow linkthis stackoverflow linkthis stackoverflow link来自同一问题。如果不使用处理 Auth2、凭据等的多个谷歌库之一,我们就没有正确的方法来发送指纹...

    /**
     * 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")
Run Code Online (Sandbox Code Playgroud)

下面是 v3 的代码,它不起作用,因为它需要似乎只能从 Google 库生成的令牌,而且我厌倦了随处可见的文档,这让我感到恶心。

    /**
     * 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)
        }
    }
Run Code Online (Sandbox Code Playgroud)

小智 1

我从 IBM Watson Translator 中得到了同样的错误并解决了它,所以我认为错误即将到来

  1. 内容类型请求:必须指定为“Content-Type”:“application/json”
  2. 授权:我不确定你的看法,但大多数时候授权密钥(api key)必须在标头请求中指定
  3. API KEY:大多数情况下取决于 api key,如果 api key 的单词之间有空格,则可能不起作用

我将教程作为 v3 中最简单的方法。要启动您需要来自标头的授权和内容类型请求,您需要这样做

//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)
}
Run Code Online (Sandbox Code Playgroud)

警告:如果 API_KEY 有像“apikey SFKHNKSJH-CSDFSCU”这样的空间,您需要使用这种工具将其转换为单个密钥

fun data(fromLang: String, toLang: String, word: String): String{
      return """{
  "sourceLanguageCode": "${fromLang}",
  "targetLanguageCodes": "${toLang}",
  "contents": ["${word}"]
}"""

}
Run Code Online (Sandbox Code Playgroud)

这些文档可能会对您有所帮助。