Some checks failed
lotteryadmin CI / build (push) Has been cancelled
- 各角色仪表盘:KPI 可点击跳转、快捷入口、去重冗余区块,代理/站点降级 analytics - 代理线路:创建玩家权限、侧栏搜索、深链 URL、档案保存与删除预检等修复 - 统一密码最短 6 位;代理模块表格/侧栏背景色一致(admin-table-inset) - 报表、钱包、对账、开奖、结算、配置等模块结构与 i18n 同步更新
237 lines
12 KiB
JavaScript
237 lines
12 KiB
JavaScript
#!/usr/bin/env node
|
||
/**
|
||
* Merge missing English (en) i18n keys.
|
||
* Usage: node scripts/merge-en-translations.mjs
|
||
*/
|
||
import fs from "fs";
|
||
import path from "path";
|
||
import { fileURLToPath } from "url";
|
||
|
||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||
const root = path.join(__dirname, "..");
|
||
const localesDir = path.join(root, "src/i18n/locales");
|
||
|
||
/** @type {Record<string, Record<string, string>>} */
|
||
const translations = {
|
||
"adminUsers.json": {
|
||
roleListHint:
|
||
"You can add custom roles and configure permissions. Built-in roles (super admin, site admin, site finance, site support, agent) cannot be deleted.",
|
||
"rolePermissionDialog.packageHint":
|
||
"Checking a module row on the left grants View only. Select Manage separately for data entry, close, draw, and other admin actions.",
|
||
confirmSaveRolesTitle: "Save admin roles?",
|
||
confirmSaveRolesDescription:
|
||
"This updates role bindings for admin {{name}}; their console permissions will change accordingly.",
|
||
confirmSaveAccountTitle: "Save admin account?",
|
||
confirmSaveAccountCreateDescription: "This creates a new admin account and assigns the selected roles.",
|
||
confirmSaveAccountEditDescription:
|
||
"This updates account details for admin {{name}}, including status and password changes.",
|
||
confirmSaveRolePermissionsTitle: "Save role permissions?",
|
||
confirmSaveRolePermissionsDescription:
|
||
"This updates feature permissions for role “{{name}}”. All admins bound to this role are affected immediately.",
|
||
confirmSaveRoleTitle: "Save role details?",
|
||
confirmSaveRoleCreateDescription: "This creates a new role {{name}}.",
|
||
confirmSaveRoleEditDescription: "This updates the name, description, and status of role {{name}}.",
|
||
},
|
||
"agents.json": {
|
||
"lineUi.profileReadOnlyHint":
|
||
"Share, credit, and rebate are configured by your upline. Contact your parent agent or the platform to request changes.",
|
||
"lineUi.selfAgentOverviewHint":
|
||
"Credit below is allocated by your upline. Share and rebate are maintained upstream; this account cannot view or edit them.",
|
||
"lineUi.overviewDownlineHint": "{{count}} direct downline agent(s). Manage them in the Downline tab.",
|
||
"lineUi.playersUnavailableHint":
|
||
"This agent cannot create players. Enable “Allow create player” in the agent profile first.",
|
||
"lineUi.playersNoPermissionHint": "This account does not have player management permission on this node.",
|
||
"lineUi.downlineColumns.email": "Email",
|
||
"lineUi.downlineColumns.downlineCount": "Downline count",
|
||
"lineUi.editCurrent": "Edit this agent",
|
||
"lineUi.profileTabHint":
|
||
"Maintain share, credit, rebate, and risk tags here. Use Account & status for login name and password.",
|
||
"lineUi.nextSteps": "Suggested next steps",
|
||
"lineUi.stepProfile": "Configure share, credit, and rebate",
|
||
"lineUi.stepDownline": "Manage direct downline ({{count}} now)",
|
||
"lineUi.stepPlayers": "Create or maintain direct players",
|
||
"lineUi.downlineEmpty": "No direct downline yet. Child agents will appear here after creation.",
|
||
"lineUi.downlineEmptyShort": "No direct downline yet.",
|
||
"lineUi.sidebarShareRate": "Share {{rate}}%",
|
||
"lineUi.sidebarAvailableCredit": "Available to grant {{amount}}",
|
||
"lineUi.expand": "Expand",
|
||
"lineUi.collapse": "Collapse",
|
||
"profile.relativeShareRate": "Share rate (% of parent)",
|
||
"profile.relativeShareRateValue": "{{rate}}% of parent",
|
||
"profile.capabilityHint":
|
||
"Takes effect on this agent’s primary account after save. If the platform Agent role included player/node management, login permissions are now tightened by these switches.",
|
||
"profile.parentCaps": "Parent share {{share}}%, available to grant {{credit}}",
|
||
"profile.availableCredit": "Available to grant {{amount}}",
|
||
"profile.riskTags": "Risk tags",
|
||
"profile.riskTagsPlaceholder": "Comma-separated, e.g. overdue, high_turnover",
|
||
"profile.saveSuccess": "Share and credit settings saved",
|
||
"settlementBills.platform": "Platform",
|
||
"settlementBills.filteredByPeriod": "Showing bills for period #{{id}} only",
|
||
"settlementBills.clearPeriodFilter": "Show all periods",
|
||
"settlementBills.columns.party": "Party",
|
||
"settlementBills.columns.counterparty": "Counterparty",
|
||
"settlementBills.columns.grossWinLoss": "Win/Loss",
|
||
"settlementBills.detail": "Details",
|
||
"settlementBills.confirm": "Confirm bill",
|
||
"settlementBills.confirmed": "Confirmed",
|
||
"settlementBills.paymentAmount": "Payment amount",
|
||
"settlementBills.recordPayment": "Record payment",
|
||
"settlementBills.paid": "Payment recorded",
|
||
"settlementBills.subtreeSummary": "Subtree summary",
|
||
"settlementBills.grossWinLoss": "Win/Loss (gross_win_loss)",
|
||
"settlementBills.rebateAmount": "Rebate",
|
||
"settlementBills.shareProfit": "Share profit",
|
||
"settlementBills.platformRounding": "Platform rounding",
|
||
"settlementReports.title": "Period reports (credit share line)",
|
||
"settlementReports.description":
|
||
"Report set: player win/loss, agent share, rebate, credit, unpaid/overdue bills, and platform P/L.",
|
||
"settlementReports.type": "Report type",
|
||
"settlementReports.footnote":
|
||
"These reports use credit-line period semantics, not legacy wallet commission/rebate reports.",
|
||
"settlementReports.noPeriodHint":
|
||
"When no specific period is selected, the last 7 days are used. Platform P/L requires a period.",
|
||
"settlementReports.types.summary": "Summary",
|
||
"settlementReports.types.player_win_loss": "Player win/loss",
|
||
"settlementReports.types.agent_share": "Agent share",
|
||
"settlementReports.types.rebate": "Rebate",
|
||
"settlementReports.types.credit": "Credit",
|
||
"settlementReports.types.unpaid_bills": "Unpaid bills",
|
||
"settlementReports.types.overdue": "Overdue",
|
||
"settlementReports.types.platform_pnl": "Platform P/L",
|
||
"settlementReports.types.draw_period": "By draw",
|
||
"settlementReports.summary.billCount": "Bill count",
|
||
"settlementReports.summary.totalNet": "Total net",
|
||
"settlementReports.summary.totalUnpaid": "Total unpaid",
|
||
"settlementReports.summary.overdueCount": "Overdue bills",
|
||
"settlementReports.summary.platformRounding": "Total platform rounding",
|
||
"settlementReports.rebate.accrued": "Accrued",
|
||
"settlementReports.rebate.inBill": "In bill",
|
||
"settlementReports.rebate.settled": "Settled",
|
||
"settlementReports.rebate.allocated": "Allocated",
|
||
"settlementReports.credit.agents": "Agent credit",
|
||
"settlementReports.credit.players": "Player credit",
|
||
"settlementReports.platformPnl.periodRequired": "Select a specific period to view platform P/L.",
|
||
"settlementReports.platformPnl.billNet": "Platform bill net",
|
||
"settlementReports.platformPnl.rounding": "Rounding adjustment",
|
||
"settlementReports.platformPnl.shareProfit": "Share profit (metadata)",
|
||
"settlementReports.columns.player": "Player",
|
||
"settlementReports.columns.gameType": "Play",
|
||
"settlementReports.columns.grossWinLoss": "Win/Loss",
|
||
"settlementReports.columns.rebate": "Rebate",
|
||
"settlementReports.columns.agentId": "Agent ID",
|
||
"settlementReports.columns.count": "Count",
|
||
"settlementReports.columns.billId": "Bill",
|
||
"settlementReports.columns.billType": "Type",
|
||
"settlementReports.columns.unpaid": "Unpaid",
|
||
"settlementReports.columns.status": "Status",
|
||
"settlementReports.columns.overdueDays": "Overdue days",
|
||
"settlementReports.columns.drawNo": "Draw no.",
|
||
"settlementReports.columns.code": "Code",
|
||
"settlementReports.columns.name": "Name",
|
||
"settlementReports.columns.creditLimit": "Credit limit",
|
||
"settlementReports.columns.allocated": "Allocated",
|
||
"settlementReports.columns.available": "Available",
|
||
"settlementReports.columns.used": "Used",
|
||
"settlementReports.columns.frozen": "Frozen",
|
||
"settlementReports.columns.rebateType": "Rebate type",
|
||
"settlementReports.columns.amount": "Amount",
|
||
"settlementPeriods.title": "Agent periods",
|
||
"settlementPeriods.presetHint": "Quick period presets (recommended)",
|
||
"settlementPeriods.openWithPreset": "Apply quick preset",
|
||
"settlementPeriods.showAdvanced": "Custom start/end dates",
|
||
"settlementPeriods.hideAdvanced": "Hide custom dates",
|
||
"settlementPeriods.range": "Period",
|
||
"settlementPeriods.empty": "No periods yet. Choose a quick preset and open a period.",
|
||
"settlementPeriods.start": "Start",
|
||
"settlementPeriods.end": "End",
|
||
"settlementPeriods.status": "Status",
|
||
"settlementPeriods.close": "Close period and generate bills",
|
||
"settlementPeriods.viewBills": "Bills",
|
||
"settlementPeriods.opened": "Period opened",
|
||
"settlementPeriods.closed": "Period closed; bills generated",
|
||
"settlementPeriods.closeFailed": "Failed to close period",
|
||
"lineProvision.siteCode": "Integration site",
|
||
"lineProvision.siteCodePlaceholder": "Select site",
|
||
"lineProvision.siteRequired": "Select an integration site",
|
||
"lineProvision.noUnboundSite": "No sites without a level-1 agent",
|
||
"lineProvision.openIntegrationSites": "Go to integration sites",
|
||
"lineProvision.passwordHint": "At least 6 characters",
|
||
"playersPanel.siteCode": "Line site",
|
||
"playersPanel.passwordHint": "At least 6 characters",
|
||
"playersPanel.passwordMinLength": "Initial password must be at least 6 characters",
|
||
"playersPanel.creditLimitInvalid": "Credit limit must be an integer ≥ 0",
|
||
"playersPanel.creditLimitExceeded": "Credit limit cannot exceed this agent’s available grant",
|
||
"playersPanel.rebateRateInvalid": "Rebate rate must be between 0 and 100%",
|
||
"playersPanel.fundingMode": "Funding mode",
|
||
"playersPanel.authSource": "Auth source",
|
||
"playersPanel.rebateInherited": "Inherit agent default rebate",
|
||
"playersPanel.creditListHint":
|
||
"Credit line: below shows player credit limits and available credit, not main-site wallet balance.",
|
||
"playersPanel.playerRef": "Player ref",
|
||
"playersPanel.usernameNickname": "Username / nickname",
|
||
"playersPanel.creditLimitAvailable": "Credit limit / available",
|
||
"roles.selectedCount": "{{selected}} / {{total}} selected",
|
||
"roles.groupSelectedCount": "{{selected}} / {{total}}",
|
||
"roles.selectGroup": "Select all in group",
|
||
"roles.noAssignablePermissions": "No permissions available to assign",
|
||
},
|
||
"config.json": {
|
||
"integrationSites.columns.currency": "Currency",
|
||
"integrationSites.columns.lineRoot": "Level-1 agent",
|
||
"integrationSites.columns.h5Url": "Lottery H5",
|
||
"integrationSites.columns.ssoSecret": "SSO secret",
|
||
"integrationSites.columns.walletApiKey": "Wallet API key",
|
||
"integrationSites.lineRootBound": "Bound",
|
||
"integrationSites.lineRootUnbound": "Unbound",
|
||
"integrationSites.secretNotConfigured": "Secret not configured",
|
||
"integrationSites.secretCopyRequiresManage":
|
||
"Integration site manage permission is required to copy secrets",
|
||
},
|
||
"jackpot.json": {
|
||
confirmSavePoolTitle: "Save jackpot pool settings?",
|
||
confirmSavePoolDescription:
|
||
"This updates fill rate, thresholds, payout ratio, and related parameters (not pool balance). Use Balance adjustment for balance changes.",
|
||
},
|
||
"players.json": {
|
||
rebateRate: "Rebate",
|
||
riskTags: "Risk tags",
|
||
},
|
||
};
|
||
|
||
function setNested(obj, dotKey, value) {
|
||
const parts = dotKey.split(".");
|
||
let cur = obj;
|
||
for (let i = 0; i < parts.length - 1; i++) {
|
||
if (!(parts[i] in cur) || typeof cur[parts[i]] !== "object" || Array.isArray(cur[parts[i]])) {
|
||
cur[parts[i]] = {};
|
||
}
|
||
cur = cur[parts[i]];
|
||
}
|
||
cur[parts[parts.length - 1]] = value;
|
||
}
|
||
|
||
function sortKeysDeep(value) {
|
||
if (Array.isArray(value)) return value;
|
||
if (value && typeof value === "object") {
|
||
return Object.fromEntries(
|
||
Object.keys(value)
|
||
.sort()
|
||
.map((k) => [k, sortKeysDeep(value[k])]),
|
||
);
|
||
}
|
||
return value;
|
||
}
|
||
|
||
let merged = 0;
|
||
for (const [file, keys] of Object.entries(translations)) {
|
||
const enPath = path.join(localesDir, "en", file);
|
||
const en = JSON.parse(fs.readFileSync(enPath, "utf8"));
|
||
for (const [dotKey, enValue] of Object.entries(keys)) {
|
||
setNested(en, dotKey, enValue);
|
||
merged++;
|
||
}
|
||
fs.writeFileSync(enPath, `${JSON.stringify(sortKeysDeep(en), null, 2)}\n`, "utf8");
|
||
console.log(`Updated ${file} (+${Object.keys(keys).length} keys)`);
|
||
}
|
||
console.log(`Total merged: ${merged}`);
|