🥰
i18nの辞書ファイルをspreadsheetで管理する(spreadsheet⇄json相互変換)
モチベーション
とあるプロジェクトで中国語対応することになり、社外の翻訳者とやり取りするようになりました。
それに伴い、辞書ファイル(json)をspreadsheetで管理することになったのですが、それならばspreadsheetとjsonファイルを相互変換できるようにしようということになりました。
前提
- spreadsheetに書き込む元となるjsonファイルは、Google Driveにアップロードしてください。
- GASにコピペして実行することを想定しています。
- jsonファイルは下記のフォーマットのものを使用する想定です。
sample.json
{
"aaa.bbb": "sample line",
"aaa.ccc": "sample line 2",
...
}
実際のコード
i18-helper.gs
const FILES = [
{
lang: "en",
inputId: "EN_INPUT_ID",
outputFileName: "output_en.json",
}, {
lang: 'ja',
inputId: "JA_INPUT_ID",
outputFileName: "output_ja.json",
},
]
const SPREADSHEET_URL = "SPREADSHEET_URL"
const OUTPUT_FOLDER_ID = "OUTPUT_FOLDER_ID"
/*
* json to spreadsheet
*/
const jsonToSpreadsheet = () => {
// get spreadsheet
const ss = SpreadsheetApp.openByUrl(SPREADSHEET_URL)
const sheet = ss.getSheets()[0]
// get all keys
const allJsonObjs = []
let allKeys = []
for (const file of FILES) {
const jsonObj = getJson(file.inputId)
const keys = Object.keys(jsonObj)
allKeys.push(...keys)
allJsonObjs.push(jsonObj)
}
allKeys = allKeys
.filter((value, index, self) => self.indexOf(value) === index)
.sort((a, b) => a > b ? 1 : -1)
// set header
const keyValues = []
const header = ["keys"]
for (const file of FILES) {
header.push(file.lang)
}
keyValues.push(header)
// set key-value
for (const key of allKeys) {
const keyValue = [key]
for (const targetObj of allJsonObjs) {
keyValue.push(targetObj[key] || "")
}
keyValues.push(keyValue)
}
// write data to spreadsheet
setDataToSheet(sheet, keyValues)
}
const setDataToSheet = (sheet, keyValues) => {
const lastRow = sheet.getLastRow()
const startRow = lastRow + 1
const startCol = 1
const numRows = keyValues.length
const numCols = keyValues[0].length
const range = sheet.getRange(startRow, startCol, numRows, numCols)
range.setValues(keyValues)
}
const getJson = (fileId) => {
const file = DriveApp.getFileById(fileId)
const blob = file.getBlob()
const data = blob.getDataAsString()
const jsonObj = JSON.parse(data)
return jsonObj
}
/*
* spreadsheet to json
*/
const spreadsheetToJson = () => {
// get spreadsheet
const ss = SpreadsheetApp.openByUrl(SPREADSHEET_URL)
const sheet = ss.getSheets()[0]
// get data from spreadsheet
const lastRow = sheet.getLastRow()
const lastColumn = sheet.getLastColumn()
const rows = sheet.getRange(1, 1, lastRow, lastColumn).getValues()
// set data as object
const outputObjs = []
for (const file of FILES) {
const data = {}
const targetCol = rows[0].findIndex((curr) => curr === file.lang)
if (targetCol < 0) {
continue
}
for (const row of rows.slice(1)) {
data[row[0]] = row[targetCol]
}
outputObjs.push({ output: file.outputFileName, data })
}
// write data to json
const folder = DriveApp.getFolderById(OUTPUT_FOLDER_ID)
for (const obj of outputObjs) {
const file = getFileFromFolder(folder, obj.output)
if (file !== null) {
file.setContent(JSON.stringify(obj.data, null, " "))
} else {
folder.createFile(obj.output, JSON.stringify(obj.data, null, " "), MimeType.PLAIN_TEXT)
}
}
}
const getFileFromFolder = (folder, fileName) => {
const files = folder.getFilesByName(fileName)
while (files.hasNext()) {
const file = files.next()
if (fileName === file.getName()) {
return file
}
}
return null
}
設定について
- FILES: 各辞書ファイルの情報。
- lang: 言語名。
- inputId: spreadsheetに書き込む元となるjsonファイルのID。Google Driveにアップロードした際の
https://drive.google.com/file/d/${id}/view
の${id}
の部分にあたります。 - outputFileName: spreadsheetの内容を書き出すjsonファイル名。同名ファイルがある場合は上書きされます。
- SPREADSHEET_URL: spreadsheetのURL。
https://docs.google.com/spreadsheets/d/${id}/edit
を入力。 - OUTPUT_FOLDER_ID:
outputFileName
を書き出すフォルダのID。https://drive.google.com/drive/folders/${id}
の${id}
の部分にあたります。
あとがき
エンジニアがjsonファイルを更新しても、翻訳者がspreadsheetを更新しても、シームレスにファイルのやり取りができるようになりました。
エンジニアの立場としては、これまで複数のjsonファイルを見比べていたのが、単一のspreadsheetで見比べられるようになったのがありがたい点ですね。
Discussion