구글 스프레드시트파일을 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 } }