← 목록

Added English / Hindi Language Toggle

devto 2026-08-07 원문 보기 ↗


🕉️ मोक्ष — Devlog v0.0.9

द्विभाषी मोक्ष — English / Hindi Language Toggle

Issue: #36 · Milestone: v0.1
Labels: UI · accessibility · enhancement

क्यों

मोक्ष अब तक केवल हिंदी में था। README में स्पष्ट लिखा था —

"For now its only for hindi users who deeply understand hindu philosophy."

पर यह एक सीमा थी, विशेषता नहीं। खेल की सारी यंत्रिकी — प्रारब्ध, वैराग्य, समर्पण — गहरे शास्त्रीय अर्थ रखती है। जो खिलाड़ी हिंदी नहीं पढ़ता, वह केवल भाषा के कारण उस दर्शन से वंचित रह जाता था।

यह अनुवाद का काम नहीं था। यह द्वार खोलने का काम था।

शास्त्रीय नियम — अनुवाद की सीमा

सबसे पहला निर्णय तकनीकी नहीं, शास्त्रीय था: क्या-क्या अनूदित नहीं होगा?

नियम E-1 — श्लोक कभी अनूदित नहीं होते

मीमांसा-सिद्धांत कहता है कि मन्त्र में शब्द-शक्ति है, अर्थ में नहीं। इसलिए English mode में भी हर श्लोक मूल देवनागरी में ही रहता है। नीचे केवल अन्वयार्थ जुड़ता है।

नाम जपत मंगल दिसि दसहूँ॥
By chanting the Nāma, auspiciousness arises in all ten directions.
— Rāmacaritamānasa

पहले प्रयास में मैंने अनुवाद लिखा था — "Auspiciousness in all ten directions"। यह ग़लत था: कर्ता ही लुप्त हो गया। मूल चौपाई कारण-कथन है (नाम-जप से मंगल होता है), स्थिति-कथन नहीं। एक पद छूटने से पूरी चौपाई का हार्द गिर गया।

अन्वयार्थ का नियम: कोई पद न छोड़ें, कोई भाव न जोड़ें।

नियम E-3 — पारिभाषिक शब्द transliteration-first

पाप को bad karma कहना अर्थ-संकोच है। पाप नैतिक बुराई नहीं — मलिनता / ऋण है।

हिंदी ❌ ✅
पाप Bad Karma Pāpa
पुण्य Good Karma Puṇya
प्रारब्ध Past-life karma Prārabdha
कृपा Grace Kṛpā (grace)
ॐ Om ॐ (कभी नहीं बदलता)

Grace अकेला नहीं छोड़ा — वह ईसाई धर्मशास्त्र का भारित शब्द है।

वास्तुकला

src/i18n.js — भाषा-प्रबंधक

DOM पर कोई निर्भरता नहीं, इसलिए engine.js और render.js इसे सुरक्षित import कर सकते हैं।

initLang()    → localStorage से पसंद पढ़ें
getLang()     → वर्तमान भाषा
setLang()     → बदलें + persist + subscribers को सूचित करें
t(key, params)→ lookup + {placeholder} interpolation
resolveAlert()→ render-time पर alert का अनुवाद

Fallback chain: current lang → hi → key स्वयं. कोई key छूट जाए तो गेम crash नहीं होता — हिंदी दिखती है।

Late binding — सबसे महत्वपूर्ण निर्णय

Alert queue में resolved string नहीं, key store होती है। अनुवाद render समय पर होता है।

// alertQueue में:
{ titleKey: 'alert.chetana.title', params: null, icon: '👁️' }

// render.js में, हर frame:
const { icon, title, subtitle } = resolveAlert(a);

इससे बीच खेल में भाषा बदलने पर queue में पड़े पुराने cards भी नई भाषा में render होते हैं। कोई मिश्रित-भाषा UI नहीं।

साथ में एक अच्छा दुष्प्रभाव: emoji अब strings से निकलकर code में आ गए। पहले _updateAlert() regex से leading emoji निकालता था — अब वह पूरी परत ही अनावश्यक हो गई। भाषा-निरपेक्ष चीज़ भाषा-फ़ाइल में क्यों रहे?

src/shastra.js — संरचित ग्रंथ

शास्त्र-ग्रंथ i18n.js में नहीं गया। कारण:

UI strings शास्त्र
स्वरूप छोटे, बिखरे, {n} सहित लंबा सतत दस्तावेज़
markup कोई नहीं <strong>, <em>, श्लोक
उपयोग हर frame overlay खुलने पर एक बार

हर अनुच्छेद अब एक object है — markup पाठ में embed नहीं:

{ type: 'term', label: '📜 प्रारब्ध', text: '...' }
{ type: 'shloka', id: 'prarabdha' }        // श्लोक-कोश से
{ type: 'note', text: '...', color: '#ff3232' }

श्लोक-कोश भाषा-निरपेक्ष है — एक ही बार लिखा, दोनों भाषाओं से id द्वारा referenced। एक जगह सुधारने पर दोनों भाषाओं में सुधरता है।

रास्ते में मिले बग

अनुवाद का काम कोड की सफ़ाई भी करा गया। जो निकला:

दृश्यमान

बग प्रभाव
laguage-toggle (id में n गिरा) languageToggle सदा null — पूरा टॉगल निष्क्रिय था, ?. ने चुपचाप निगल लिया
last.Time = ... R (पुनर्जन्म) दबाते ही पूरा keydown handler टूटता
_tutorialClickRead tutorial card क्लिक से कभी बंद नहीं होता
info.alert दोहरा call हर शंख/ज्योति संग्रह पर दो alerts, एक खाली

मौन — जो अकेले कभी न पकड़े जाते

1. aria-describedby="language-choicce-status" — दो c। screen-reader को भाषा-परिवर्तन की सूचना कभी नहीं मिलती थी। issue #36 पर accessibility label लगा था — और यही उसका मूल था।

2. padding:10px, 28px — CSS में अल्पविराम अमान्य है। पूरी declaration चुपचाप गिर जाती थी; गुरु-दीक्षा बटन बिना padding के सिकुड़ा रहता।

3. letter-spacing:0.5pxl — pxl कोई इकाई नहीं। वही मौन विफलता।

4. overlayTitle.style.fontFamily केवल प्रलय-शाखा में सेट होता था। DOM element साझा है — एक बार प्रलय देखने के बाद वह font मोक्ष और पुनर्जन्म पर भी चिपका रह जाता।

5. 'Orbitron', sans-serif — Orbitron में देवनागरी glyphs हैं ही नहीं। हिंदी alerts browser fallback में render होते थे, असंगत baseline के साथ।

6. CARD_H = 290 स्थिर था। अंग्रेज़ी पाठ देवनागरी से ~25% लंबा होता है — tutorial card से बाहर निकलता और hint pill dismiss-button पर चढ़ जाती। अब ऊँचाई मापी हुई सामग्री से बनती है:

const CARD_H = HEADER_H
             + shlokaLines.length  * LH_SHLOKA
             + meaningLines.length * LH_MEANING
             + taskLines.length    * LH_TASK
             + ...

साथ में एक wrapText() helper — क्योंकि fillText(..., maxWidth) wrap नहीं करता, glyphs को दबाकर सँकरा कर देता है। हिंदी की छोटी पंक्तियों में यह कभी दिखा ही नहीं था।

शास्त्रीय

सबसे संतोषजनक सुधार यह रहा। index.html में लिखा था:

🪢 लगाम (मन)
🪈 सारथी (मन)      ← मन दो जगह

कठोपनिषद् १.३.३ स्पष्ट है:

बुद्धिं तु सारथिं विद्धि, मनः प्रग्रहमेव च॥
बुद्धि को सारथी जानो, और मन को लगाम।

सारथी बुद्धि है, मन नहीं। दिलचस्प बात — render.js में sprite का नाम पहले से cachedBuddhiSprite था। कोड सही जानता था; दस्तावेज़ पीछे रह गया था।

अब वही श्लोक Page 3 के शीर्ष पर जोड़ दिया गया है — पूरे खेल का रूपक जिस पर खड़ा है, वह अब स्वयं ग्रंथ में उपस्थित है।

जो सीखा

grep -c पंक्तियाँ गिनता है, keys नहीं। कई बार गिनती मिलती दिखी पर keys असंतुलित थीं। असली जाँच यह निकली:

const k = [...src.matchAll(/'([a-z]+\.[A-Za-z0-9.]+)':/g)].map(m => m[1]);
const c = {}; k.forEach(x => c[x] = (c[x]||0) + 1);
Object.entries(c).filter(([,v]) => v !== 2);   // असंतुलित keys

इसी ने अंत में 'starat.description' पकड़ी — एक वर्तनी-दोष जिसके कारण English mode में विवरण हिंदी में दिख रहा था, बिना किसी error के।

node --check नाम-समाधान नहीं करता। एक बार wrapText object के अंदर था और calls सादे थे — syntax निर्दोष, पर चलते ही ReferenceError। Syntax पास होना पर्याप्त प्रमाण नहीं।

git diff | grep "^-" सबसे अच्छा सुरक्षा-जाल निकला। अंत के दो regression इसी से पकड़े गए — जिनमें एक tutorialBtn की declaration थी, जो पैच लगाते समय गलती से मिट गई थी।

और सबसे बड़ा: git status ने दिखाया कि i18n.js और shastra.js untracked थीं। commit हो जाता तो repo में न जातीं — बाक़ी सात फ़ाइलें उन्हें import करती हैं, इसलिए किसी और के clone पर गेम बिलकुल न चलता। स्थानीय मशीन पर सब ठीक दिखता रहता।

आँकड़े

134 keys × 2 भाषाएँ — पूर्ण संतुलित
 40+ alert call sites — literals से keys में
  2 नई फ़ाइलें — i18n.js, shastra.js
 25+ अतिरिक्त बग — जिनमें कई मौन

आगे

मोक्ष प्राप्ति का एकमात्र मार्ग — त्याग।
The only path to liberation — is to let it go.

🎮 itch.io · 💛 PayPal ·

Developed by Weird Codes