fix(i18n): i18n key should can nested

Signed-off-by: Innei <i@innei.in>
This commit is contained in:
Innei 2024-09-24 12:51:19 +08:00
parent 34b9a20acf
commit 98e899df09
No known key found for this signature in database
GPG Key ID: 0F62D33977F021F7
6 changed files with 88 additions and 19 deletions

View File

@ -82,7 +82,7 @@ export const SettingLists = () => {
{t.settings("lists.view")}
</TableHead>
<TableHead className="w-20" size="sm">
{t.settings("lists.fee")}
{t.settings("lists.fee.label")}
</TableHead>
<TableHead className="w-20" size="sm">
@ -147,7 +147,7 @@ export const SettingLists = () => {
variant="ghost"
onClick={() => {
present({
title: t.settings("lists.edit"),
title: t.settings("lists.edit.label"),
content: ({ dismiss }) => (
<ListCreationModalContent dismiss={dismiss} id={row.id} />
),
@ -314,7 +314,7 @@ const ListCreationModalContent = ({ dismiss, id }: { dismiss: () => void; id?: s
render={({ field }) => (
<FormItem>
<div>
<FormLabel>{t("lists.fee")}</FormLabel>
<FormLabel>{t("lists.fee.label")}</FormLabel>
<FormDescription>{t("lists.fee.description")}</FormDescription>
</div>
<FormControl>
@ -434,7 +434,7 @@ export const ListFeedsModalContent = ({ id }: { id: string }) => {
}
}}
>
{t("lists.feeds.add")}
{t("lists.feeds.add.label")}
</Button>
</div>
<Divider className="mt-8" />

View File

@ -1,6 +1,7 @@
// @ts-check
import { defineConfig } from "eslint-config-hyoban"
import checkI18nJson from "./plugins/eslint-check-i18n-json.js"
import recursiveSort from "./plugins/eslint-recursive-sort.js"
export default defineConfig(
@ -49,9 +50,11 @@ export default defineConfig(
files: ["locales/**/*.json"],
plugins: {
"recursive-sort": recursiveSort,
"check-i18n-json": checkI18nJson,
},
rules: {
"recursive-sort/recursive-sort": "error",
"check-i18n-json/valid-i18n-keys": "error",
},
},
)

View File

@ -156,19 +156,19 @@
"lists.created.error": "Failed to create list.",
"lists.created.success": "List created successfully!",
"lists.description": "Description",
"lists.edit": "Edit",
"lists.edit.error": "Failed to edit list.",
"lists.edit.label": "Edit",
"lists.edit.success": "List edited successfully!",
"lists.fee": "Fee",
"lists.fee.description": "The fee others need to pay you to subscribe to this list.",
"lists.feeds": "Feeds",
"lists.fee.label": "Fee",
"lists.feeds.actions": "Actions",
"lists.feeds.add": "Add",
"lists.feeds.add.error": "Failed to add feed to list.",
"lists.feeds.add.label": "Add",
"lists.feeds.add.success": "Feed added to list.",
"lists.feeds.delete.error": "Failed to remove feed from list.",
"lists.feeds.delete.success": "Feed removed from list.",
"lists.feeds.id": "Feed ID",
"lists.feeds.label": "Feeds",
"lists.feeds.manage": "Manage Feeds",
"lists.feeds.owner": "Owner",
"lists.feeds.search": "Search for feed",
@ -234,4 +234,4 @@
"wallet.withdraw.modalTitle": "Withdraw Power",
"wallet.withdraw.submitButton": "Submit",
"wallet.withdraw.success": "Withdrawal successful!"
}
}

View File

@ -156,19 +156,19 @@
"lists.created.error": "목록 생성에 실패했습니다.",
"lists.created.success": "목록이 성공적으로 생성되었습니다!",
"lists.description": "설명",
"lists.edit": "편집",
"lists.edit.error": "목록 편집에 실패했습니다.",
"lists.edit.label": "편집",
"lists.edit.success": "목록이 성공적으로 편집되었습니다!",
"lists.fee": "수수료",
"lists.fee.description": "다른 사용자가 이 목록을 구독하려면 지불해야 하는 비용입니다.",
"lists.feeds": "피드",
"lists.fee.label": "수수료",
"lists.feeds.actions": "액션",
"lists.feeds.add": "추가",
"lists.feeds.add.error": "목록에 피드를 추가하지 못했습니다.",
"lists.feeds.add.label": "추가",
"lists.feeds.add.success": "피드가 목록에 추가되었습니다.",
"lists.feeds.delete.error": "목록에서 피드를 제거하지 못했습니다.",
"lists.feeds.delete.success": "목록에서 피드가 제거되었습니다.",
"lists.feeds.id": "피드 ID",
"lists.feeds.label": "피드",
"lists.feeds.manage": "피드 관리",
"lists.feeds.owner": "소유자",
"lists.feeds.search": "피드 검색",
@ -234,4 +234,4 @@
"wallet.withdraw.modalTitle": "파워 인출",
"wallet.withdraw.submitButton": "제출",
"wallet.withdraw.success": "인출이 성공적으로 완료되었습니다!"
}
}

View File

@ -156,19 +156,19 @@
"lists.created.error": "创建列表失败",
"lists.created.success": "创建列表成功",
"lists.description": "描述",
"lists.edit": "编辑",
"lists.edit.error": "编辑列表失败",
"lists.edit.label": "编辑",
"lists.edit.success": "编辑列表成功",
"lists.fee": "费用",
"lists.fee.description": "其他人订阅这个列表时要支付的费用",
"lists.feeds": "订阅源",
"lists.fee.label": "费用",
"lists.feeds.actions": "自动化",
"lists.feeds.add": "添加",
"lists.feeds.add.error": "添加订阅源到列表时失败",
"lists.feeds.add.label": "添加",
"lists.feeds.add.success": "订阅源已添加到列表",
"lists.feeds.delete.error": "添加订阅源到列表时失败",
"lists.feeds.delete.success": "订阅源已从列表删除",
"lists.feeds.id": "订阅源 ID",
"lists.feeds.label": "订阅源",
"lists.feeds.manage": "管理订阅源",
"lists.feeds.owner": "所有者",
"lists.feeds.search": "搜索订阅源",
@ -234,4 +234,4 @@
"wallet.withdraw.modalTitle": "提现 Power",
"wallet.withdraw.submitButton": "提交",
"wallet.withdraw.success": "提现成功!"
}
}

View File

@ -0,0 +1,66 @@
import path from "node:path"
/** @type {import("eslint").ESLint.Plugin} */
export default {
rules: {
"valid-i18n-keys": {
meta: {
type: "problem",
docs: {
description: "Ensure i18n JSON keys are flat and valid as object paths",
category: "Possible Errors",
recommended: true,
},
fixable: null,
},
create(context) {
return {
Program(node) {
const filename = context.getFilename()
if (path.basename(filename) !== "en.json") return
const sourceCode = context.getSourceCode()
let json
try {
json = JSON.parse(sourceCode.text)
} catch {
context.report({
node,
message: "Invalid JSON format",
})
return
}
const keys = Object.keys(json)
const keyPrefixes = new Set()
for (const key of keys) {
if (key.includes(".")) {
const parts = key.split(".")
for (let i = 1; i < parts.length; i++) {
const prefix = parts.slice(0, i).join(".")
if (keys.includes(prefix)) {
context.report({
node,
message: `Invalid key structure: '${key}' conflicts with '${prefix}'`,
})
}
keyPrefixes.add(prefix)
}
}
}
for (const key of keys) {
if (keyPrefixes.has(key)) {
context.report({
node,
message: `Invalid key structure: '${key}' is a prefix of another key`,
})
}
}
},
}
},
},
},
}