पिछली बसंत एक क्लायंट ने मुझे एक brief दिया जिससे 2022 में मैं कराह गया होता। पाँच भाषाएँ। ग्यारह बाज़ार। एक डेवलपर। "हमने पहले से ही WPML सेट कर रखा है," उसने कहा, उस आत्मविश्वास के साथ जो बताता है कि वह कभी अपने Lighthouse scores को देखता ही नहीं रहा।
मैंने दूसरे दिन WordPress install को ख़त्म कर दिया। पूरी चीज़ को Astro में दोबारा बनाया। आठ हफ़्तों बाद, उन तीन locales में से Google के पहले पन्ने पर थे। चौथा महीने के अंदर आ गया। उस अनुभव ने मेरा multilingual काम के लिए WordPress के साथ रिश्ता बिलकुल खत्म कर दिया।
तो। यहाँ बिलकुल वही है जो मैं अब करता हूँ।
WordPress Multilingual Performance पर क्यों विफल हो रहा है
देखिए, मुझे WordPress पर तंज कसना पसंद नहीं है। Seahawk ने सैकड़ों WP साइट्स बनाई हैं और शायद सैकड़ों और भी बनाएँगे। लेकिन multilingual प्रोजेक्ट्स पर performance gap सच में बचाव के लिए मुश्किल है।
WPML और Polylang दोनों डेटाबेस लेयर पर अनुवादित पोस्ट जेनरेट करके और PHP के माध्यम से रूटिंग करके काम करते हैं। हर अनुरोध सर्वर तक पहुंचता है। भले ही आप पूरे पेज को कैश करें (WP Rocket, W3TC, या जो भी आप चाहें), आप अभी भी PHP इनिशियलाइजेशन ओवरहेड, कम ट्रैफिक वाले लोकेल पेजों पर कैश मिस, और hreflang प्रबंधन के साथ जूझ रहे हैं, जो दो से ज्यादा भाषाओं के होते ही एक दुःस्वप्न बन जाता है।
मैंने एक क्लाइंट की पांच-भाषा वाली WP साइट को Italian लोकेल पर 1.3 सेकंड की Time to First Byte पर मापा। वही कंटेंट, Astro में पूरी तरह स्टैटिक HTML के रूप में फिर से बनाया गया: 94ms। यह कोई मामूली सुधार नहीं है। यह उत्पाद की एक बिल्कुल अलग श्रेणी है।
Core Web Vitals एक पुष्ट रैंकिंग फैक्टर हैं, और CDN एज नोड से सर्व किए गए स्टैटिक फाइलें इस लड़ाई में हमेशा जीतते हैं।
Astro का Built-in i18n Routing (और क्यों इसने सब कुछ बदल दिया)
Astro ने v3 में सही i18n रूटिंग शिप किया था और v4 में इसे काफी हद तक परिष्कृत किया। जब तक हम 2026 में ऑपरेट कर रहे हैं, API स्थिर है और वास्तव में उपयोग में आनंददायक है।
यहाँ वह बुनियादी सेटअप है जो मैं उपयोग करता हूं। आपका astro.config.mjs एक i18n ब्लॉक पाता है:
`` i18n: { defaultLocale: 'en', locales: ['en', 'de', 'fr', 'it', 'es'], routing: { prefixDefaultLocale: false } } ``
यह एकमात्र कॉन्फ़िगरेशन Astro को English को / पर, German को /de/ पर, French को /fr/ पर, और इसी तरह सर्व करने के लिए कहती है। कोई प्लगइन नहीं। कोई अतिरिक्त पैकेज नहीं। यह अंदर से बना हुआ है।
prefixDefaultLocale: false जानबूझकर किया गया है। मेरे अधिकांश क्लाइंटों का प्राथमिक दर्शक अंग्रेजी बोलने वाला है, इसलिए मैं /en/ को canonical संरचना को खराब नहीं करना चाहता। Google hreflang के माध्यम से संबंध समझ सकता है।
वह फाइल संरचना जो वास्तव में स्केल करती है
यहीं पर ज्यादातर ट्यूटोरियल गलत रास्ते पर चले जाते हैं। वे आपको एक सपाट pages/[locale]/index.astro दिखाते हैं और काम पूरा मान लेते हैं। एक ब्रोशर के लिए ठीक है। लेकिन जैसे ही आपके पास 40 रूट हों, सब कुछ बिखर जाता है।
मैं इसकी जगह यह करता हूँ:
`` src/ pages/ index.astro (English) about.astro (English) [locale]/ index.astro about.astro i18n/ en.json de.json fr.json it.json es.json ``
i18n/ डायरेक्टरी आपकी ट्रांसलेशन स्ट्रिंग्स को फ्लैट JSON फाइलों के रूप में रखती है। en.json सच का स्रोत है। मैं बाकी हर लोकेल को इसके विरुद्ध लिखता हूँ। de.json में एक की गायब है? जर्मन पेज उस स्ट्रिंग के लिए English पर फॉलबैक कर जाता है, एरर फेंकने की बजाय, क्योंकि मैं एक छोटी सी t() यूटिलिटी फंक्शन जोड़ता हूँ जो फॉलबैक को सुंदरता से सँभालती है।
द ट्रांसलेशन लेयर: कीप इट बोरिंग
मैंने Paraglide JS, i18next और कुछ अन्य आजमाए हैं। Astro स्टैटिक बिल्ड्स के लिए मेरी ईमानदारी से सिफारिश है: इसे ओवर-इंजीनियर न करें।
एक सरल यूटिलिटी फाइल जो लोकेल JSON को इंपोर्ट करे और एक गेटर फंक्शन रिटर्न करे, 90% प्रोजेक्ट्स के लिए काफी है। Inlang से Paraglide JS विचार करने लायक है अगर आपको टाइप-सेफ ट्रांसलेशन्स की जरूरत है और आपकी टीम बड़ी है ताकि मिसिंग की पर कंपाइल-टाइम एरर्स की चिंता कर सके। सोलो वर्क या छोटी एजेंसियों के लिए, यह वह ओवरहेड है जिसकी आपको जरूरत नहीं है।
मुझे जो चिंता है: ट्रांसलेशन फाइलों को फ्लैट रखना, गहराई से नेस्टेड न होना। "hero.cta.button.label" कुंजी के रूप में संगठित लगता है जब तक आप इसे रात 11 बजे सर्च न करें। "heroCTALabel" बदसूरत है लेकिन खोजने लायक है।
Plurals और Dynamic Strings को हैंडल करना
जर्मन प्लुरलाइजेशन आपको विनम्र करेगी। Arabic भी करेगी अगर आप वहाँ जाएँ (मैंने एक बार एक क्लाइंट को UAE में विस्तार करने में मदद की और पूरी दोपहर सिर्फ नंबर फॉर्मेटिंग पर खर्च की)।
बहुवचन के लिए मैं एक छोटे इनलाइन हेल्पर का इस्तेमाल करता हूँ:
`` export function plural(count, one, other) { return count === 1 ? one : other; } ``
कच्चा? हाँ। लेकिन यह यूरोपीय भाषाओं के 95% मामलों को कवर करता है। अगर आप अरबी, रूसी या पोलिश में जा रहे हैं, तो आपको सही CLDR बहुवचन नियम चाहिए। Unicode CLDR प्रोजेक्ट इन्हें विस्तार से दस्तावेज़ित करता है। अपने plural हेल्पर को उन स्पेक्स के विरुद्ध बनाएँ अन्यथा आप इसे गलत पाएँगे।
hreflang: वह हिस्सा जिसे सभी गलत समझते हैं
ठीक है। यह वह जगह है जहाँ बहुभाषी SEO वास्तव में जीता या मरता है।
hreflang Google को बताता है कि किस locale को कौन सा संस्करण सर्व करना है। इसे गलत करो और तुम या तो अपनी ही rankings को नष्ट कर दोगे या जर्मन संस्करण फ्रेंच speakers को दे दोगे। मैंने यह दोनों स्थितियाँ उन साइटों पर देखी हैं जिन्हें मैंने inherit किया।
नियम:
- हर पृष्ठ को हर locale के लिए hreflang टैग include करने चाहिए, अपने आप को भी शामिल करते हुए।
- टैग को द्विदिशात्मक होना चाहिए। अगर
/de/about/fr/aboutको संदर्भित करता है, तो/fr/aboutको/de/aboutको वापस संदर्भित करना चाहिए। - अपने default locale की ओर इशारा करने वाला एक
x-defaultटैग शामिल करें। - निरपेक्ष URLs का उपयोग करें। हमेशा।
Astro में मैं ये BaseLayout.astro कंपोनेंट के अंदर जेनरेट करता हूँ जिसे हर पेज इंपोर्ट करता है। मैं वर्तमान पाथ पास करता हूँ, लोकेल ऐरे पर लूप करता हूँ, और <link rel="alternate"> टैग्स को ऑटोमेटिक रूप से आउटपुट करता हूँ। कोई मैनुअल रखरखाव नहीं।
x-default टैग को मिस करना एक ही सबसे आम गलती है जो मैं देखता हूँ। Google का अपना hreflang डॉक्यूमेंटेशन इस पर सचमुच स्पष्ट है। इसे एक बार ठीक से पढ़ लीजिए।
लोकेल-विशिष्ट साइटमैप्स
एक साइटमैप जिसमें सभी लोकेल मिले हुए हों, काम करता है। लेकिन लोकेल के अनुसार अलग-अलग साइटमैप्स (जो Google Search Console में अलग से सबमिट किए जाएँ) आपको स्वच्छ इंडेक्सिंग डेटा देते हैं और किसी विशिष्ट भाषा में क्रॉल एरर्स को पकड़ना बहुत आसान बनाते हैं।
मेरा Astro बिल्ड पाँच फाइलें जेनरेट करता है: sitemap-en.xml, sitemap-de.xml, और इसी तरह आगे। @astrojs/sitemap i18n विकल्प के साथ इसे बॉक्स से ही सपोर्ट करता है। इसे उपयोग करें।
मल्टीलिंगुअल स्टेटिक साइट्स के लिए कंटेंट स्ट्रेटेजी
यहाँ वह चीज है जिसे ज़्यादातर डेवलपर्स पूरी तरह छोड़ देते हैं: ट्रांसलेशन ही।
आपके पास एक तकनीकी रूप से सही Astro सेटअप हो सकता है, दोषरहित hreflang के साथ, और एक साइटमैप जो किसी Googler को खुशी से रुलाने वाली हो। अगर आपकी जर्मन कॉपी DeepL से गुज़री है और कभी किसी इंसान ने इसे छुआ नहीं है, तो साइट जर्मनी में रैंक नहीं करेगी।
मैं इस बारे में कोई अहंकार नहीं दिखा रहा हूँ। मैं DeepL को लगातार उपयोग करता हूँ। यह एक बेहतरीन पहला ड्राफ्ट है। लेकिन किसी भी पेज के लिए जिस पर आप सचमुच रैंक करना चाहते हैं, एक मूल वक्ता को इसे पढ़ना चाहिए। ज़रूरी नहीं कि फिर से लिखे। बस पढ़ लीजिए, अजीब जगहों को ठीक करें, और पुष्टि करें कि वाक्य ऐसा कुछ लगता है जो एक जर्मन व्यक्ति वास्तव में कहेगा।
Seahawk के पास पिछले साल एक फिनटेक प्रोजेक्ट था जहाँ क्लाइंट को सात भाषाओं और चार हफ्ते की टाइमलाइन चाहिए थी। हमने DeepL से शुरुआत की, फिर Gengo के जरिए सात फ्रीलांसरों को हायर किया रिव्यू पास के लिए लगभग £0.04 प्रति शब्द पर। कुल ट्रांसलेशन बजट £800 से कम था। वह साइट अब इन चार मार्केट्स में से चार में टॉप फाइव में रैंक करती है। इकोनॉमिक्स काम करती हैं।
लोकेल-स्पेसिफिक मेटाडेटा
Title tags और meta descriptions को भी लोकलाइज करना पड़ता है। सिर्फ ट्रांसलेट नहीं करना। "Best accounting software" सीधे जर्मन equivalent को search-map नहीं करता क्योंकि keyword volumes अलग हैं।
मैं locale-specific metadata को JSON ट्रांसलेशन फाइल्स में एक dedicated meta namespace के अंदर रखता हूँ। पेज टेम्प्लेट t('meta.title') से pull करता है और अगर की मिसिंग है तो default पर ही फॉल करता है। इसका मतलब एक SEO बिना कोड को छुए एक JSON फाइल प्रति locale को एडिट कर सकता है।
Deployment: एक 5-Language Static Site को कहाँ होस्ट करें
Vercel और Netlify दोनों ही static Astro builds को ठीक हैंडल करते हैं। लेकिन multilingual के लिए specifically, आप edge caching चाहते हैं per locale के साथ और locale-specific headers सेट करने की क्षमता।
मैंने Cloudflare Pages पर स्टैंडर्डाइज किया है। Free tier ज्यादातर client budgets को हैंडल करता है। Requests globally nearest edge node से serve होती हैं, जो बहुत मायने रखता है जब आपका Italian audience Virginia के data centre से फाइलें serve पा रहा हो। Cloudflare के नेटवर्क में Milan, Frankfurt, Amsterdam में nodes हैं। European visitors के लिए TTFB में फर्क measurable है।
एक चीज explicitly configure करनी है: Cache-Control headers। Static files को long cache TTLs होने चाहिए (hashed assets के लिए max-age=31536000, immutable)। HTML pages को shorter TTLs या एक stale-while-revalidate policy होनी चाहिए, खासतौर पर अगर आप content updates के बाद incremental builds कर रहे हों।
Debugging i18n Builds इससे पहले कि वे Live जाएँ
कुछ चीजें हैं जो मैं हर multilingual Astro build पर production में push करने से पहले चेक करता हूँ:
astro buildको locally चलाएँ औरdist/output को inspect करें। हर locale folder में हर page होना चाहिए। अगर कोई route मिसिंग है, तो build step ही वह जगह है जहाँ आप इसे catch कर सकते हैं।- Screaming Frog का उपयोग करके preview deployment को crawl करें। hreflang के आधार पर filter करें। orphaned pages (ऐसे pages जिनका कोई hreflang tag से reference नहीं है) तुरंत दिख जाते हैं।
- browser DevTools में कम से कम homepage और एक inner page पर
x-defaultcanonical को check करें। - किसी भी locale के लिए set किए गए browser में manually एक locale URL को visit करें। यह obvious लगता है। फिर भी issues को catch करता है।
सबसे आम build-time issue जो मुझे आता है वह है astro.config.mjs locales array में JSON file add करने के बाद नया locale add करना भूल जाना। build error नहीं देता। यह बस चुपचाप नए locale को ignore कर देता है। Annoying।
FAQ
क्या Astro का i18n server-side rendering (SSR) के साथ काम करता है या केवल static output के साथ?
दोनों। Astro की i18n routing SSR mode में भी काम करती है। getRelativeLocaleUrl() और getAbsoluteLocaleUrl() helper functions output mode के बावजूद identically काम करते हैं। कहने का मतलब, अगर आप पूरी तरह static जा रहे हैं, तो performance benefits काफी ज्यादा बड़े हैं क्योंकि आप pre-built HTML serve कर रहे हैं और कोई runtime cost नहीं है।
नए visitors के लिए मैं locale detection और redirects को कैसे handle करूँ?
Static sites के लिए मैं यह CDN/edge layer पर करता हूँ, Astro के अंदर नहीं। Cloudflare Workers Accept-Language header को read कर सकते हैं और appropriate locale path पर redirect कर सकते हैं। यह Astro app के अंदर मत करो क्योंकि यह एक round trip add करता है और static output को non-deterministic बनाता है।
साइट बढ़ने के साथ ही ट्रांसलेशन फ़ाइलों को मैनेज करने का सबसे अच्छा तरीका क्या है?
उन्हें वर्जन कंट्रोल में कोड के साथ रखें, किसी अलग CMS या थर्ड-पार्टी ट्रांसलेशन मैनेजमेंट सिस्टम में नहीं, जब तक प्रोजेक्ट को सच में इसकी जरूरत न हो। जिस पल आपके पास तीन से ज्यादा लोकेल हों और एक सक्रिय एडिटोरियल टीम हो, तब Tolgee या Localazy देखें। दोनों फ्लैट JSON फ़ाइलों के साथ इंटीग्रेट होते हैं और GitHub सिंक करते हैं। इससे पहले, एक शेयर्ड Notion डॉक्यूमेंट और मैनुअल JSON अपडेट्स ईमानदारी से ठीक है।
क्या मैं Astro Content Collections को i18n के साथ use कर सकता हूँ?
हाँ, और यह ब्लॉग-हेवी साइट्स के लिए मेरा पसंदीदा तरीका है। अपने कंटेंट कलेक्शन को लोकेल के साथ एक पाथ सेगमेंट के रूप में स्ट्रक्चर करें: src/content/blog/en/, src/content/blog/de/। फिर अपने getStaticPaths() फंक्शन में लोकेल के आधार पर फ़िल्टर करें। हर लोकेल को बिल्ड टाइम पर अपना स्टैटिक HTML मिलता है। साफ-सुथरा, तेज़, और एडिटोरियल टीम किसी भी कंपोनेंट को छुए बिना मार्कडाउन फ़ाइलों को मैनेज कर सकती है।
क्या कई लोकेल पेजेस होने से डुप्लिकेट कंटेंट के माध्यम से SEO को नुकसान पहुँचता है?
नहीं, बशर्ते आपका hreflang इंप्लीमेंटेशन सही हो। Google सही तरीके से एनोटेटेड लोकेल वेरिएंट्स को डुप्लिकेट के रूप में नहीं, बल्कि संबंधित लेकिन अलग पेजेस के रूप में ट्रीट करता है। जोखिम hreflang को गलत तरीके से इंप्लीमेंट करने से आता है, खास तौर पर रेसिप्रोकल एनोटेशन्स या गलत x-default हैंडलिंग से। इन दोनों चीजों को ठीक करें और डुप्लिकेट कंटेंट कोई चिंता नहीं है।
---
डायनामिक PHP-रेंडर्ड मल्टीलिंगुअल साइट्स से प्री-बिल्ट स्टैटिक वन्स तक का शिफ्ट असल में Astro के बारे में नहीं है। यह स्वीकार करने के बारे में है कि ज्यादातर ट्रांसलेशन कंटेंट घंटे-दर-घंटे नहीं बदलता। इसे एक बार बनाएँ, हर जगह कैश करें, CDN को भारी काम करने दें। Astro बस होता है कि उस वर्कफ़्लो को सच में सुखद बना देता है, फ्रेमवर्क के विरुद्ध एक लड़ाई के बजाय।
पाँच भाषाएँ, एक डेवलपर, आठ हफ्ते। टूल्स मौजूद हैं। उन्हें use करें।
