Seize the day

POST : Android Dev Study

안드로이드 다국어 번역툴 #6

구글 스프레드시트파일을 JSON 파일로 내려받아서, Restring에 초기화했다. 아파치 POI를 이용하는 방법보다 간단하고, apk 사이즈도 늘어나지 않는다. 


스프레드시트 파일에 모든 사용자 읽기허용으로 공개하면 별도의 권한체크없이 다운로드가 가능하다. {FileId}에 해당 파일의 Id를 넣어주면 된다.

https://docs.google.com/spreadsheets/d/{FileId}/gviz/tq?tqx=out:json


이 API는 웹브라우저 자바스크립트에서 사용하기 위한 파일로 보인다. Json파싱이 성공하려면 앞뒤로 불필요한 자바스크립트 코드를 삭제해야 한다.

import android.content.Context
import android.os.Handler
import android.os.Looper
import android.support.annotation.WorkerThread
import android.util.Log
import android.widget.Toast
import com.ice.restring.Restring
import okhttp3.*
import org.json.JSONObject
import java.io.IOException
import java.lang.RuntimeException

class LanguageDownloader(private val context: Context) : Restring.StringsLoader {

    companion object {
        private const val JSON_PREFIX = "google.visualization.Query.setResponse("
        private const val JSON_EXPORT_URL =
            "https://docs.google.com/spreadsheets/d/1W9HOGd0qWNtLpvgKzsrT0Gf6RlKxrknHUVULRooNYJg/gviz/tq?tqx=out:json"
    }

    private val httpClient = OkHttpClient()
    private val languageToColumnMap = mutableMapOf<String, Int>()
    private val columnToLanguageMap = mutableMapOf<Int, MutableMap<String, String>>()
    private val languages = mutableListOf<String>()
    private val handler = Handler(Looper.getMainLooper())

    private fun showToast(message: String?) {
        handler.post {
            Toast.makeText(context.applicationContext, message, Toast.LENGTH_LONG).show()
        }
    }

    private fun downloadSheet() {
        val request = Request.Builder()
            .url(JSON_EXPORT_URL)
            .build()

        httpClient.newCall(request).enqueue(object : Callback {
            override fun onFailure(call: Call, e: IOException) {
                showToast(e.message)
            }

            override fun onResponse(call: Call, response: Response) {
                try {
                    val bodyString = response.body()?.string()
                        ?: throw RuntimeException("Download failed: google spreadsheet json file")
                    parseBodyString(bodyString)
                } catch (ex: Exception) {
                    showToast(ex.message)
                }
            }

        })
    }

    private fun downloadSheetSync() {
        val request = Request.Builder()
            .url(JSON_EXPORT_URL)
            .build()

        try {
            val bodyString = httpClient.newCall(request).execute().body()?.string()
                ?: throw RuntimeException("Download failed: google spreadsheet json file")
            parseBodyString(bodyString)
        } catch (ex: Exception) {
            showToast(ex.message)
        }
    }

    private fun parseBodyString(bodyString1: String) {
        var bodyString = bodyString1
        val prefixIndex = bodyString.indexOf(JSON_PREFIX)
        if (prefixIndex > 0) {
            val startIndex = prefixIndex + JSON_PREFIX.length
            val endIndex = bodyString.lastIndexOf(')')
            if (endIndex > 0) {
                bodyString = bodyString.substring(startIndex, endIndex)
            }
        }

        parseJson(JSONObject(bodyString))
    }

    private fun parseJson(jsonObject: JSONObject) {
        val rowsArray = jsonObject.optJSONObject("table")?.optJSONArray("rows")
            ?: throw RuntimeException("Parse failed: table , rows element not found")

        for (row in 0 until rowsArray.length()) {
            val c = rowsArray.getJSONObject(row).optJSONArray("c") ?: break
            var stringId: String? = null
            for (column in 0 until c.length()) {
                val text = c.optJSONObject(column)?.optString("v") ?: continue

                if (column == 0) {
                    if (text == "[common]" || text == "[app]" || text == "--end--") {
                        continue
                    }
                    stringId = text
                }

                if (stringId == null || column < 4) {
                    continue
                }

                // check header row
                if (stringId == "String Id") {
                    languageToColumnMap[text] = column
                    columnToLanguageMap[column] = HashMap()
                    languages.add(text)
                } else {
                    columnToLanguageMap[column]?.put(stringId, touchString(text))
                }
            }
        }

        printStrings("ko")
    }

    private fun touchString(text: String): String {
        return text.replace("\${str}", "%s")
            .replace("\${str1}", "%1\$s")
            .replace("\${str2}", "%2\$s")
            .replace("\${str3}", "%3\$s")
            .replace("\${str4}", "%4\$s")
            .replace("\${num}", "%d")
            .replace("\${num1}", "%1\$d")
            .replace("\${num2}", "%2\$d")
            .replace("\${num3}", "%3\$d")
            .replace("\${num4}", "%4\$d")
    }

    private fun printStrings(language: String) {
        val column = languageToColumnMap[language] ?: return
        val map = columnToLanguageMap[column] ?: return
        for ((key, value) in map.entries) {
            Log.e("__T", "$language $key=$value")
        }
        Log.e("__T", "$language count=${map.size}")
        showToast("Download language file done")
    }

    @WorkerThread
    override fun getLanguages(): MutableList<String> {
        if (languages.isEmpty()) {
            downloadSheetSync()
        }
        return languages
    }

    @WorkerThread
    override fun getStrings(language: String): MutableMap<String, String>? {
        languageToColumnMap.get(language)?.let { langIndex ->
            return columnToLanguageMap.get(langIndex)
        }
        return null
    }
}

top

posted at

2018. 12. 26. 22:09


CONTENTS

Seize the day
BLOG main image
김대정의 앱 개발 노트와 사는 이야기
RSS 2.0Tattertools
공지
아카이브
최근 글 최근 댓글
카테고리 태그 구름사이트 링크