#!/usr/bin/env node /** * Merge missing Nepali (ne) i18n keys from translations map. * Usage: node scripts/merge-ne-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>} */ const translations = { "adminUsers.json": { siteRequired: "साइट छान्नुहोस्", "table.sites": "बाँधिएका साइटहरू", "permissionDialog.site": "साइट", "accountDialog.site": "बाँधिएको साइट", "accountDialog.sitePlaceholder": "यो खाताले पहुँच पाउने साइट छान्नुहोस्", }, "agents.json": { "lineUi.kicker": "क्रेडिट शेयर · एजेन्ट ट्री", "lineUi.agentCount": "यो समूहमा {{count}} एजेन्ट", "lineUi.searchPlaceholder": "नाम वा लगइन खोज्नुहोस्", "lineUi.directChildren": "प्रत्यक्ष अधीनस्थ {{count}}", "lineUi.selectAgent": "शेयर र क्रेडिट हेर्न एजेन्ट छान्नुहोस्", "lineUi.selectAgentHint": "सेटलमेन्ट सीमा एजेन्ट ट्री अनुसार; शेयर, क्रेडिट र रिबेट प्रति नोड कन्फिग गरिन्छ।", "lineUi.allocatedCredit": "बाँडिएको", "lineUi.availableCredit": "उपलब्ध", "lineUi.profileFootnote": "रिबेट सीमा {{rebate}}% · पूर्वनिर्धारित {{defaultRebate}}%", "lineUi.tabOverview": "अवलोकन", "lineUi.tabProfile": "शेयर र क्रेडिट", "lineUi.tabProfileReadOnly": "शेयर र क्रेडिट (पढ्न मात्र)", "lineUi.currentSite": "साइट", "lineUi.viewAll": "सबै हेर्नुहोस्", "lineUi.shareRebateCap": "रिबेट सीमा {{rate}}%", "lineUi.overviewDownlineCard": "प्रत्यक्ष अधीनस्थ एजेन्ट हेर्नुहोस् र व्यवस्थापन गर्नुहोस्", "lineUi.overviewDownlineCount": "{{count}}", "lineUi.overviewPlayersHint": "प्रत्यक्ष खेलाडी र क्रेडिट स्थिति हेर्नुहोस्", "lineUi.overviewPlayersSummary": "खेलाडी व्यवस्थापन", "lineUi.downlineEmptyTitle": "अहिले प्रत्यक्ष अधीनस्थ छैन", "lineUi.editAccount": "खाता र स्थिति", "lineUi.saveProfile": "शेयर र क्रेडिट बचत", "lineUi.tabDownline": "अधीनस्थ", "lineUi.tabPlayers": "खेलाडी", "lineUi.noDelegatedTabs": "यो एजेन्टले अधीनस्थ एजेन्ट वा खेलाडी सिर्जना गर्न सक्दैन; शेयर र क्रेडिट मात्र लागू हुन्छ।", listSearch: "नाम / कोड / लगइन खोज्नुहोस्", siteSearch: "साइट नाम खोज्नुहोस्", parentAgent: "माथिल्लो", childrenCount: "प्रत्यक्ष अधीनस्थ", "subnav.provisionHint": "एक पटकको अनबोर्डिङ; दैनिक कामका लागि लाइन र एजेन्ट ट्री प्रयोग गर्नुहोस्", "profile.validation.creditBelowAllocated": "क्रेडिट सीमा बाँडिएको क्रेडिभन्दा कम हुन सक्दैन (न्यूनतम {{min}})", "profile.validation.creditExceedsParentWithMax": "क्रेडिट सीमा {{max}} भन्दा बढी हुन सक्दैन", "settlementBills.periodLabel": "अवधि", "settlementBills.periodPlaceholder": "अवधि छान्नुहोस्", "settlementBills.allPeriods": "सबै अवधि", "settlementBills.filteredByPeriodRange": "{{range}} का बिलहरू", "settlementBills.emptyNoPeriodsManage": "अहिले अवधि वा बिल छैन। अवधि व्यवस्थापनमा छिटो प्रिसेट प्रयोग गर्नुहोस्, त्यसपछि अवधि बन्द गर्नुहोस्।", "settlementBills.emptyNoPeriodsAgent": "अहिले बिल छैन। तपाईंको माथिल्लो वा प्लेटफर्मले अवधि बन्द गर्छ; मिति प्रविष्ट गर्नु पर्दैन।", "settlementBills.emptyNoClosed": "बन्द अवधि छैन। बिल बन्द पछि सिर्जना हुन्छ।", "settlementBills.typePlayer": "खेलाडी बिल", "settlementBills.typeAgent": "एजेन्ट बिल", "settlementBills.columns.period": "अवधि", "settlementPeriods.manageTitle": "अवधि व्यवस्थापन", "settlementPeriods.manageHint": "यहाँ अवधि खोल्नुहोस् र बन्द गर्नुहोस्; माथिका बिल स्वतः अद्यावधिक हुन्छ। छिटो प्रिसेट सामान्यतया पर्याप्त।", "settlementPeriods.presetThisWeek": "यो हप्ता", "settlementPeriods.presetLastWeek": "गत हप्ता", "settlementPeriods.presetThisMonth": "यो महिना", "settlementPeriods.openHint": "स्थानीय मिति ({{tz}})", "settlementPeriods.startDate": "सुरु मिति", "settlementPeriods.endDate": "अन्त्य मिति", "settlementPeriods.open": "अवधि खोल्नुहोस्", "settlementPeriods.openFailed": "अवधि खोल्न असफल", "settlementPeriods.datesRequired": "सुरु र अन्त्य मिति प्रविष्ट गर्नुहोस्", "settlementPeriods.invalidRange": "अन्त्य मिति सुरु मितिभन्दा अगाडि हुन सक्दैन", "settlementPeriods.statusOpen": "खुला", "settlementPeriods.statusClosed": "बन्द", "playersPanel.loginRequired": "लगइन प्रयोगकर्ता नाम र प्रारम्भिक पासवर्ड प्रविष्ट गर्नुहोस्", "playersPanel.loginUsername": "लगइन प्रयोगकर्ता नाम", "playersPanel.initialPassword": "प्रारम्भिक पासवर्ड", "playersPanel.externalIdOptional": "बाह्य ID (वैकल्पिक)", "playersPanel.externalIdHint": "स्वतः सिर्जनाका लागि खाली राख्नुहोस्", "playersPanel.creditLimit": "क्रेडिट सीमा", "playersPanel.rebateRate": "रिबेट दर (%)", "playersPanel.rebateRateHint": "प्रतिशत लेख्नुहोस्, जस्तै 5 = 5%", "playersPanel.availableToGrant": "एजेन्टले दिन सक्ने: {{amount}}", "playersPanel.riskTags": "जोखिम ट्याग", "playersPanel.riskTagsPlaceholder": "अल्पविरामले छुट्याउनुहोस्", "playersPanel.createSuccessNative": "खेलाडी {{name}} सिर्जना भयो — लटरी /login प्रयोग गर्नुहोस्", "users.email": "इमेल", }, "common.json": { "export.ticketCombinations.filename": "ticket-combinations", "export.ticketCombinations.sheetName": "संयोजनहरू", "integrationSites.emptyPlatformHint": "अहिले एकीकरण साइट छैन। एजेन्ट, खेलाडी वा सेटलमेन्ट व्यवस्थापन अघि साइट सिर्जना गर्नुहोस्।", "integrationSites.createSite": "एकीकरण साइट सिर्जना", }, "config.json": { "integrationSites.adminAccountCreated": "साइट एडमिन खाता {{username}} सिर्जना भयो", "integrationSites.delete": "साइट मेटाउनुहोस्", "integrationSites.deleteSuccess": "साइट {{code}} मेटाइयो", "integrationSites.deleteFailed": "साइट मेटाउन असफल", "integrationSites.deleteConfirmTitle": "यो साइट मेटाउने?", "integrationSites.deleteConfirmDescription": "यसले साइट {{code}} ({{name}}), यसको एजेन्ट लाइन, सेटलमेन्ट अवधि, खेलाडी र साइट एडमिन खाता स्थायी रूपमा हटाउँछ। पूर्ववत गर्न मिल्दैन।", "integrationSites.deleteConfirm": "मेटाउनुहोस्", "integrationSites.deleting": "मेटाउँदै…", "integrationSites.form.adminUsernameRequired": "साइट एडमिन प्रयोगकर्ता नाम आवश्यक", "integrationSites.form.adminNicknameRequired": "साइट एडमिन उपनाम आवश्यक", "integrationSites.form.adminPasswordRequired": "प्रारम्भिक साइट एडमिन पासवर्ड कम्तीमा ८ अक्षर", "integrationSites.fields.adminUsername": "एडमिन प्रयोगकर्ता नाम", "integrationSites.fields.adminNickname": "एडमिन उपनाम", "integrationSites.fields.adminPassword": "प्रारम्भिक पासवर्ड", "integrationSites.fields.adminEmail": "इमेल (वैकल्पिक)", "integrationSites.placeholders.adminUsername": "एडमिन प्रयोगकर्ता नाम लेख्नुहोस्", "integrationSites.placeholders.adminNickname": "खाता उपनाम लेख्नुहोस्", "integrationSites.placeholders.adminPassword": "कम्तीमा ८ अक्षर", "integrationSites.placeholders.adminEmail": "इमेल लेख्नुहोस्", "integrationSites.adminAccountSectionTitle": "साइट एडमिन खाता", "integrationSites.adminAccountSectionDescription": "साइट सिर्जना गर्दा त्यस साइटसँग बाँधिएको एउटा एडमिन खाता पनि सिर्जना हुन्छ।", "system.saveDrawSuccess": "ड्र प्यारामिटर बचत भयो", "system.saveCurrencyFormatSuccess": "मुद्रा प्रदर्शन ढाँचा बचत भयो", "system.saveSettlementSuccess": "सेटलमेन्ट स्वचालन बचत भयो", "system.sections.draw": "ड्र तालिका र समीक्षा", "system.sections.drawDescription": "ड्र समय, बन्द सञ्झ्याल, म्यानुअल समीक्षा र कूलडाउन नियन्त्रण। यो ब्लकमा परिवर्तित फिल्ड मात्र पेश हुन्छ।", "system.sections.currencyFormat": "मुद्रा प्रदर्शन ढाँचा", "system.sections.currencyFormatDescription": "साइटभरि रकमका दशमलव र विभाजक (मुद्रा मास्टर डाटाबाट अलग)।", "system.sections.settlement": "सेटलमेन्ट स्वचालन", "system.sections.settlementDescription": "टिक स्वतः सेटलमेन्ट, अनुमोदन र भुक्तानी चलाउने नियन्त्रण। यो ब्लकमा परिवर्तित फिल्ड मात्र पेश हुन्छ।", "system.confirmSaveDrawTitle": "ड्र प्यारामिटर बचत गर्ने?", "system.confirmSaveDrawDescription": "यसले यो ब्लकमा ड्र समीक्षा, तालिका समय र कूलडाउन मात्र अद्यावधिक गर्छ।", "system.confirmSaveCurrencyFormatTitle": "मुद्रा प्रदर्शन ढाँचा बचत गर्ने?", "system.confirmSaveCurrencyFormatDescription": "यसले दशमलव स्थान र विभाजक अद्यावधिक गर्छ।", "system.confirmSaveSettlementTitle": "सेटलमेन्ट स्वचालन बचत गर्ने?", "system.confirmSaveSettlementDescription": "यसले स्वतः सेटलमेन्ट, अनुमोदन र भुक्तानी स्विच अद्यावधिक गर्छ।", "play.filters.sectionTitle": "खेल फिल्टर", "play.filters.sectionDescription": "पहिले सूची संकुचित गर्नुहोस्, त्यसपछि ब्याच स्विच वा पङ्क्ति सम्पादन प्रयोग गर्नुहोस्।", "play.filters.keyword": "खेल खोज्नुहोस्", "play.filters.keywordPlaceholder": "खेल कोड, प्रदर्शन नाम वा श्रेणीले फिल्टर", "play.filters.category": "श्रेणी", "play.filters.status": "स्थिति", "play.filters.allCategories": "सबै श्रेणी", "play.filters.allStatuses": "सबै स्थिति", "play.filters.uncategorized": "अवर्गीकृत", "play.filters.reset": "फिल्टर हटाउनुहोस्", "play.filters.empty": "मिल्ने खेल प्रकार छैन", "play.filters.groupCount": "{{count}} खेल", }, "dashboard.json": { "analytics.summaryShareProfit": "आफ्नो शेयर नाफा", "analytics.shareProfitHint": "विभाजन पछि तपाईंको शेयर — प्लेटफर्म वा सम्पूर्ण टोलीको कुल नाफा/नोक्सान होइन", currentDrawFinanceHint: "तलका चार्ट ड्र {{drawNo}} का लागि", "agent.heroEyebrow": "आजको लाइन ककपिट", "agent.heroTitle": "{{name}} प्रत्यक्ष सञ्चालन", "agent.creditAllocated": "बाँडिएको {{amount}}", "agent.creditUsed": "प्रयोग {{amount}}", "agent.settlementCycle": "चक्र {{cycle}}", "agent.betOrdersToday": "आजका बाजी अर्डर", "agent.todayPayout": "आजको भुक्तानी", "agent.todayProfit": "आजको नाफा", "agent.sevenDayPayout": "भुक्तानी {{amount}}", "agent.sevenDayProfit": "नाफा {{amount}}", "agent.sevenDayShareProfit": "शेयर नाफा {{amount}}", "agent.topMomentum": "आजको बाजी केन्द्र", "agent.topMomentumHint": "नाफा {{profit}}", "agent.topMomentumPayout": "भुक्तानी {{amount}}", "agent.managementFocus": "व्यवस्थापन केन्द्र", "agent.focusBet": "आजको बाजी मात्रा निगरानी", "agent.focusPlayers": "आजका सक्रिय खेलाडी", "agent.focusBills": "पछ्याउन बाँकी बिल", "agent.quickStatsTitle": "लाइन अनुमति झलक", "agent.canCreateChildAgent": "सन्तान एजेन्ट सिर्जना गर्न सक्छ", "agent.canCreatePlayer": "खेलाडी सिर्जना गर्न सक्छ", "agent.lineDepth": "लाइन गहिराइ", "agent.viewBills": "बिल हेर्नुहोस्", "agent.viewLine": "एजेन्ट लाइन", "agent.quickLinks.tickets": "टिकट", "agent.quickLinks.players": "खेलाडी", "agent.quickLinks.reports": "रिपोर्ट", "agent.quickLinks.agents": "अधीनस्थ एजेन्ट", "agent.quickLinks.bills": "एजेन्ट बिल", "warnings.apiResourceMissing": "ड्यासबोर्ड विश्लेषण API दर्ता छैन। चलाउनुहोस्: php artisan lottery:admin-auth-sync (वा पछिल्लो माइग्रेसन), त्यसपछि रिफ्रेस।", }, "draws.json": { backToList: "ड्र सूचीमा फर्कनुहोस्", overviewTitle: "ड्र अवलोकन", overviewBetTotal: "कुल बाजी", overviewPayoutTotal: "कुल भुक्तानी", overviewProfitLoss: "नाफा/नोक्सान", reviewQueueHint: "नतिजा सिर्जना पछि समीक्षा र प्रकाशनमा जारी राख्नुहोस्।", }, "players.json": { ticketTableHint: "यो तालिकाले खेलाडीका हालैका टिकट देखाउँछ। पूर्ण सन्दर्भ र समस्या समाधानका लागि पङ्क्ति कार्यबाट मुख्य टिकट सूचीमा जानुहोस्।", tabCreditLedger: "क्रेडिट लेजर", filterSite: "साइट", filterAllSites: "सबै साइट", scopeAllSites: "स्कोप: सबै साइटका सबै खेलाडी (सुपर एडमिन)", scopeFilteredSite: "स्कोप: साइट {{site}}", scopeAgentLine: "स्कोप: {{site}} · एजेन्ट लाइन “{{name}}” र अधीनस्थ खेलाडी", scopeSingleSite: "स्कोप: साइट {{site}}", scopeMultiSite: "स्कोप: {{count}} बाँधिएका साइट; संकुचित गर्न फिल्टर प्रयोग गर्नुहोस्", fundingMode: "फन्डिङ मोड", authSource: "प्रमाणीकरण स्रोत", creditSection: "क्रेडिट लाइन", usedCredit: "प्रयोग भएको क्रेडिट", fundingCredit: "क्रेडिट लाइन", fundingWallet: "मुख्य साइट वालेट", authMainSite: "मुख्य साइट SSO", authNative: "लटरी नेटिभ", creditLimit: "क्रेडिट सीमा", availableCredit: "उपलब्ध क्रेडिट", confirmFreezeTitle: "खेलाडी फ्रिज गर्ने?", confirmFreezeDescription: "खेलाडी {{name}} ले बाजी लगाउन सक्ने छैन।", confirmUnfreezeTitle: "खेलाडी अनफ्रिज गर्ने?", confirmUnfreezeDescription: "खेलाडी {{name}} सामान्य स्थितिमा फर्किनेछ।", }, "reconcile.json": { createHint: "छानिएको अवधिमा ट्रान्सफर अर्डर स्क्यान गर्छ, लटरी वालेट लेजर तुलना गर्छ, र वालेट API कन्फिग भएमा मुख्य साइट idempotent रेकर्ड जाँच गर्छ।", createSuccessEmpty: "स्क्यान सम्पन्न: कुनै समस्या फेला परेन", transferNo: "ट्रान्सफर नं.", walletTxnNo: "लटरी वालेट लेनदेन", mainSiteRef: "मुख्य साइट सन्दर्भ", mainSiteCheck: "मुख्य साइट जाँच", actions: "कार्य", itemMainSiteRecordMissing: "मुख्य साइटमा छैन", itemMainSiteFailed: "मुख्य साइटमा असफल", mainSiteMatched: "मुख्य साइट ठीक", mainSiteNotFound: "मुख्य साइटमा छैन", mainSiteFailed: "मुख्य साइट असफल", mainSiteUnavailable: "मुख्य साइट उपलब्ध छैन", mainSiteSkipped: "जाँच गरिएको छैन", }, "reports.json": { "tasks.currentReportHint": "यहाँ हाल छानिएको रिपोर्टका निर्यात कार्य मात्र देखिन्छ।", "preview.stats.notQueried": "क्वेरी गरिएको छैन", "preview.stats.notSet": "सेट गरिएको छैन", }, "risk.json": { "confirm.closeTitle": "नम्बर बन्द गर्ने?", "confirm.closeDescription": "नम्बर {{number}} यो ड्रका लागि ब्लक हुनेछ।", "confirm.recoverTitle": "नम्बर पुन: खोल्ने?", "confirm.recoverDescription": "नम्बर {{number}} फेरि बाजीका लागि खुल्नेछ।", }, "tickets.json": { filterSite: "साइट", filterAllSites: "सबै साइट", viewTicketInList: "यो टिकट हेर्नुहोस्", statusSelectedCount: "{{count}} छानिएको", }, "wallet.json": { scopeHint: "यो क्षेत्र मुख्य साइट वालेट मोड (वालेट लेनदेन र ट्रान्सफर) का लागि हो। क्रेडिट लाइन अवधि सेटलमेन्टका लागि हेर्नुहोस्", scopeHintSettlementLink: "सेटलमेन्ट केन्द्र", scopeHintSettlement: "सेटलमेन्ट केन्द्र", ledgerChannel: "लेजर", ledgerCredit: "क्रेडिट लेजर", ledgerWallet: "वालेट लेनदेन", }, }; 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 nePath = path.join(localesDir, "ne", file); const ne = JSON.parse(fs.readFileSync(nePath, "utf8")); for (const [dotKey, neValue] of Object.entries(keys)) { setNested(ne, dotKey, neValue); merged++; } fs.writeFileSync(nePath, `${JSON.stringify(sortKeysDeep(ne), null, 2)}\n`, "utf8"); console.log(`Updated ${file} (+${Object.keys(keys).length} keys)`); } console.log(`Total merged: ${merged}`);