---
# System prepended metadata

title: 快速透過google sheet建立i18n文件

---

# 快速透過google sheet建立i18n文件

由於在公司偶爾會遇到特定的需求會對文案照三餐改動，改來改去真的煩到不行，所以不如就透過google sheet可以透過雲端修改，不如就將文件丟給pm請他們自行修改吧，這個方法基本上是對於靜態文字轉換成i18n對應文字方便產生檔案自動化所使用。

https://console.cloud.google.com/
## 建立專案
![](https://i.imgur.com/E23eY9M.png)

建立完成後選定專案進入

## 啟用Api服務

![](https://i.imgur.com/QSihp4K.png)
![](https://i.imgur.com/5msBWbA.png)
![](https://i.imgur.com/6nPk62p.png)

## 建立憑證服務帳戶

![](https://i.imgur.com/by8u0lj.png)
![](https://i.imgur.com/zGMEitO.png)
![](https://i.imgur.com/pN13xG2.png)
![](https://i.imgur.com/LNGCO9r.png)

## 建立金鑰
![](https://i.imgur.com/K2EQ2hy.png)
![](https://i.imgur.com/zwAtjcO.png)


## 安裝google sheet套件

```javascript!
npm i spreadsheet-to-json fs-extra flat -D
```

## 新增表單

### 表格內容
![](https://i.imgur.com/xZkpgrk.png)

### page名稱
![](https://i.imgur.com/nqu7id5.png)


## 檔案建制

將私鑰放到資料夾內
新增js檔案GoogleSheetToJson.js

```javascript!
(async () => {
  const { unflatten } = await import('flat')
  const fs = await import('fs-extra')
  const { extractSheets } = await import('spreadsheet-to-json')
  const path = await import('path')
  const env = await import('dotenv'ˊˊˊˊㄙ)
  env.config()

  const jsonFiles = await fs.default.readdir(
    path.dirname(
      new URL(import.meta.url).pathname
    )
  )
  const firstJsonFile = jsonFiles.find(file => file.endsWith('.json')) // 找到第一個以 .json 結尾的檔案

  const googleSheetJson = await import(`./${firstJsonFile}`, { assert: { type: 'json' } })
  const googleSheetApiKey = JSON.parse(JSON.stringify(googleSheetJson)).default

  extractSheets(
    {
      spreadsheetKey: process.env.NUXT_PUBLIC_GOOGLE_SHEET_KEY, // google sheet key
      credentials: googleSheetApiKey // google sheet private key
    },
    (err, data) => {
      if (err) { throw err }

      const result = {}
      const files = []
      const languages = []
      const allKeys = Object.values(data).flatMap(item => item)
      allKeys.forEach((item) => {
        Object.keys(item).forEach((key) => {
          // 檢察非i-18n格式不計入語言列表
          const langCodePattern = /^[a-z]{2}-[A-Z]{2}$/ // i18n 國際碼格式
          if (langCodePattern.test(key) && !languages.includes(key)) {
            languages.push(key) // 新增到 languages 陣列中
          }
        })
      })
      const read = Object.keys(data).flatMap(page =>
        data[page].map((item) => {
          // 檢查 item 中的鍵是否符合 i18n 國際碼格式
          const newItem = { key: `${page}.${item.key}` }
          languages.forEach((lang) => {
            newItem[lang] = item[lang]
          })
          return newItem
        })
      )

      for (const key in read[0]) {
        if (key !== 'key') {
          files.push(key)
          result[key] = {}
        }
      }

      read.forEach((el) => {
        files.forEach((file) => {
          result[file][el.key] = el[file] || ''
        })
      })
      files.forEach((fileName) => {
        const filePath = path.resolve('./languages/locales', `${fileName}.json`)
        const fileDir = path.dirname(filePath)
        fs.ensureDirSync(fileDir)
        fs.default.writeJSONSync(filePath, unflatten(result[fileName], { object: true }), { spaces: 2 })
      })
    }
  )
})()


```
### spreadsheetKey
![](https://i.imgur.com/fC5FhrZ.png)

### credentials

新增的私鑰檔案
![](https://i.imgur.com/Z85REDM.png)

## 執行指令

可於資料夾底下執行

```npm!
node node GoogleSheetToJson.js`
```

Common JS 版本配置
```json
  "devDependencies": {
    "nuxt": "3.12.4",
    "spreadsheet-to-json": "2.0.0",
    "flat": "6.0.1",
    "fs-extra": "11.2.0",
  },
```

如想放入打包時一起執行也可以
![](https://i.imgur.com/GO8hoez.png)


執行成功會建立langauge資料夾
![](https://i.imgur.com/sICFdoV.png)



## 自動於i18n引入多語系資料

如果希望未來在開新專案時都能自動引入或是因應需求他們自己增添不同語系時
```javascript!
// /plugins/i18n.ts
// 自動動態載入個語系文檔案
import { createI18n } from 'vue-i18n'

type Locales = {
  [index:string]: any
}
const loadLocaleMessages = async () => {
  const locales:Locales = {}
  const messages = import.meta.glob('../locales/*.json')
  for (const [name, path] of Object.entries(messages)) {
    const matched = name.match(/([a-z]{2}-[a-z]{2})\.json$/i)
    const moduleContent = await path()
    if (matched && matched.length > 1) {
      const locale = matched[1]
      locales[locale] = { ...moduleContent.default }
    }
  }
  return locales
}

export default defineNuxtPlugin(async ({ vueApp }) => {
  const message = await loadLocaleMessages()
  const i18n = createI18n({
    legacy: false,
    globalInjection: true,
    locale: 'zh-TW',
    messages: message
  })

  vueApp.use(i18n)
})

```

```javascript!
<template>
  <div>
    <form>
      <label for="locale-select">{{ $t('orange') }}: </label>
      <select id="locale-select" v-model="$i18n.locale">
        <option value="en-US">
          en-US
        </option>
        <option value="ja-JP">
          ja-JP
        </option>
        <option value="zh-TW">
          zh-TW
        </option>
      </select>
    </form>
  </div>
</template>

```


如何透過Google apps scripts 建立google sheet表格變動追蹤
https://hackmd.io/@kltony9898/rJYrP2Fwxe