一個實作中滿常見的情況,分享一下我自己常用的解決方法。
錯誤訊息中帶有數字
假設請求失敗時會回傳以下錯誤代碼:
VALUE_MUST_BE_BETWEEN_0_AND_10
VALUE_MUST_BE_GREATER_THAN_0
VALUE_MUST_BE_LESS_THAN_10
這其中的數字都是會動態改變的,比如可能會是 VALUE_MUST_BE_BETWEEN_0_AND_100
或者 VALUE_MUST_BE_BETWEEN_10_AND_20
那麼如果要使用 i18n,就必須將這個代碼放進 json 檔中,key名如何對應就是一個問題,因為數字可能會改變。
如果把所有數字可能性都放進 json 檔中就會變成:
"VALUE_MUST_BE_BETWEEN_0_AND_10": "數字必須在0到10之間",
"VALUE_MUST_BE_BETWEEN_0_AND_100": "數字必須在0到100之間",
"VALUE_MUST_BE_BETWEEN_10_AND_20": "數字必須在10到20之間",
...
如果有幾百種上千種組合,就得為每個組合都新增一個 key,非常不現實。
使用正則獲取錯誤訊息中的數字
這種時候就可以使用正則先把錯誤訊息中的數字給取出。
const msg = error.response.data.code
const regex = /VALUE_MUST_BE_BETWEEN(\d+)_AND_(\d+)/
const matches = msg.match(regex)
console.log(matches) // [0, 10]
將錯誤代碼的正則集中在一起管理:
export const errorMap = {
VALUE_MUST_BE_BETWEEN: {
id: "VALUE_MUST_BE_BETWEEN",
regex: /VALUE_MUST_BE_BETWEEN(\d+)_AND_(\d+)/,
},
VALUE_MUST_BE_GREATER_THAN: {
id: "VALUE_MUST_BE_GREATER_THAN",
regex: /VALUE_MUST_BE_GREATER_THAN_(\d+)/,
},
VALUE_MUST_BE_LESS_THAN: {
id: "VALUE_MUST_BE_LESS_THAN",
regex: /VALUE_MUST_BE_LESS_THAN_(\d+)/,
}
}
export const errorMsgKeys = Object.keys(errorMap)
export const handleError = (error) => {
let msg = error.response.data.code
const matchedError = errorMsgKeys.find((key) => msg.includes(key))
if (matchedError) {
const { id, regex } = errorMap[matchedError]
const matches = msg.match(regex)
...
}
return msg
}
那麼獲取到數字之後該如何轉成 i18n 呢?有的錯誤代碼只有一個數,有的卻有兩個數。
i18n 動態變數
基本上所有的 i18n 第三方庫都支持動態變數,比如:
react-intl, react-i18next 可以這麼寫:
{
"VALUE_MUST_BE_BETWEEN": "數字必須在{min}到{max}之間",
}
{
"VALUE_MUST_BE_BETWEEN": "數字必須在%{min}到%{max}之間",
}
我平時用 react-i18next 比較多,所以這邊用 react-i18next 的寫法做示範。因此將全部錯誤代碼寫進 i18n json 檔中就會像這樣:
{
"VALUE_MUST_BE_BETWEEN": "數字必須在{min}到{max}之間",
"VALUE_MUST_BE_GREATER_THAN": "數字必須大於{value}",
"VALUE_MUST_BE_LESS_THAN": "數字必須小於{value}"
}
翻譯包含動態數字的錯誤代碼
萬事俱備,現在只剩下將數字放入 i18n 動態變數中了。
將 errorMap 改造一下,把 i18n 中的動態變數也一同寫入:
export const errorMap = {
VALUE_MUST_BE_BETWEEN: {
id: "VALUE_MUST_BE_BETWEEN",
regex: /VALUE_MUST_BE_BETWEEN(\d+)_AND_(\d+)/,
formatValues: (matches) => ({ min: matches[0], max: matches[1] }),
},
VALUE_MUST_BE_GREATER_THAN: {
id: "VALUE_MUST_BE_GREATER_THAN",
regex: /VALUE_MUST_BE_GREATER_THAN_(\d+)/,
formatValues: (matches) => ({ value: matches[0] }),
},
VALUE_MUST_BE_LESS_THAN: {
id: "VALUE_MUST_BE_LESS_THAN",
regex: /VALUE_MUST_BE_LESS_THAN_(\d+)/,
formatValues: (matches) => ({ value: matches[0] }),
}
}
使用 formatValues 將獲取的數字放入對應的 i18n 變數,如此一來便能完成翻譯包含動態數字的錯誤代碼了。
export const handleError = (error) => {
let msg = error.response.data.code
const matchedError = errorMsgKeys.find((key) => msg.includes(key))
if (matchedError) {
const { id, regex, formatValues } = errorMap[matchedError]
const matches = msg.match(regex)
const dynamicValues = matches ? formatValues(matches.slice(1)) : {}
msg = i18n.t(id, dynamicValues)
}
return msg
}