Author:
    Creation:2026-09-02Last update:2026-10-03

    ICU मैसेज फॉर्मेट: सिंटैक्स और वो हिस्से जहां लोग गलती करते हैं

    ICU MessageFormat एक स्ट्रिंग सिंटैक्स है जो अनुवाद को अपने स्वयं के ब्रांचिंग लॉजिक को शामिल करने की अनुमति देता है: बहुवचन, लिंग-आधारित रूप, संख्या और दिनांक स्वरूपण। इसका अस्तित्व इसलिए है क्योंकि व्याकरण अनुवादक का विषय है, न कि if (count === 1) लिखने वाले डेवलपर का। यह लेख सिंटैक्स, भाषा-निर्भर पहलुओं जो सरल कार्यान्वयनों को विफल करते हैं, और जावास्क्रिप्ट इकोसिस्टम इसे कैसे संभालता है, इस पर चर्चा करता है।

    विषय सूची

    व्यावहारिक रूप से समस्या

    यहाँ वह कोड है जो लगभग हर कोई सबसे पहले लिखता है:

    ts
    const label = count + " " + (count === 1 ? t("item") : t("items"));
    

    यह अंग्रेजी में तो ठीक काम करता है लेकिन अन्य लगभग सभी भाषाओं में विफल हो जाता है:

    • रूसी और पोलिश को दो नहीं, बल्कि तीन या चार रूपों की आवश्यकता होती है।
    • जापानी को केवल एक रूप की आवश्यकता होती है, और जोड़ा गया स्पेस गलत होता है।
    • अरबी को छह रूपों की आवश्यकता होती है, और संख्या को स्वयं स्थानीय अंक प्रणाली में प्रस्तुत किया जाना चाहिए।
    • फ्रेंच में कुछ विराम चिह्नों से पहले नॉन-ब्रेकिंग स्पेस की आवश्यकता होती है, जिसे आपके + " " ने नष्ट कर दिया।

    गहरी समस्या यह है कि वाक्य को टुकड़ों में काट दिया गया है। एक अनुवादक बिना किसी संदर्भ और वाक्य के क्रम को बदले बिना केवल item और items देखता है। ICU MessageFormat पूरे वाक्य को एक ही अनुवाद योग्य स्ट्रिंग में रखकर और अनुवादक को ब्रांचिंग ऑपरेटर देकर इसे हल करता है।

    सरल तर्क (Simple Arguments)

    सबसे छोटी इकाई एकल घुंघराले कोष्ठक (curly braces) में एक प्लेसहोल्डर है:

    text
    Hello, {name}!
    

    फॉर्मेटिंग के समय आप { name: "Alice" } पास करते हैं और आपको Hello, Alice! मिलता है। घुंघराले कोष्ठक ही एकमात्र विशेष वर्ण हैं; किसी शाब्दिक कोष्ठक को प्रिंट करने के लिए आप उसे एकल उद्धरण चिह्नों में लपेटते हैं: '{'.

    यह संपूर्ण "इंटरपोलेशन" सुविधा है। ICU में बाकी सब कुछ इसी के ऊपर बनाया गया है।

    बहुवचन (Plural)

    plural संख्या के आधार पर एक शाखा का चयन करता है:

    text
    {count, plural,
      one {You have one unread message}
      other {You have # unread messages}
    }
    

    जानने योग्य तीन प्रमुख बातें:

    • # को count के स्थानीय-स्वरूपित मान से बदल दिया जाता है, जिससे 1234 en-US में 1,234 और hi-IN में 1,234 बन जाता है।
    • other अनिवार्य है। इसके बिना कोई भी ICU कार्यान्वयन त्रुटि देगा या सत्यापन में विफल रहेगा। जब कोई श्रेणी मेल नहीं खाती है तो यह फॉलबैक के रूप में कार्य करता है।
    • =0, =1, … सटीक मानों से मेल खाते हैं और CLDR श्रेणियों से पहले जाँचे जाते हैं। इनका उपयोग विशेष मामलों ("कोई संदेश नहीं") के लिए करें, न कि one के विकल्प के रूप में।
    text
    {count, plural,
      =0 {No unread messages}
      one {One unread message}
      other {# unread messages}
    }
    

    offset

    offset:n श्रेणी चयन और # प्रतिस्थापन दोनों से पहले मान से n घटाता है। यह "Alice और 3 अन्य लोगों ने इसे पसंद किया" जैसे पैटर्न के लिए उपयोगी है:

    text
    {count, plural, offset:1
      =0 {No one liked this}
      =1 {{name} liked this}
      one {{name} and one other liked this}
      other {{name} and # others liked this}
    }
    

    count: 4 के साथ, # का मान 3 प्रदर्शित होगा। offset वास्तव में उपयोगी है लेकिन विभिन्न रनटाइम में इसका समर्थन अलग-अलग हो सकता है, इसलिए उपयोग से पहले अपने परिवेश की जांच कर लें।

    बहुवचन श्रेणियां भाषा-निर्भर हैं

    यह वह हिस्सा है जहाँ लोग सबसे अधिक गलती करते हैं। श्रेणी नाम zero, one, two, few, many, other सार्वभौमिक बकेट नहीं हैं जिन्हें आप हर भाषा के लिए भरते हैं। प्रत्येक स्थानीय भाषा CLDR बहुवचन नियमों द्वारा परिभाषित एक उपसमूह का उपयोग करती है, और ये नियम व्याकरणिक हैं, न कि सहज गणितीय।

    भाषाटैगप्रयुक्त श्रेणियांसंख्या
    जापानीjaother1
    चीनीzhother1
    अंग्रेजीenone, other2
    जर्मनdeone, other2
    फ्रेंचfrone, many, other3
    चेकcsone, few, many, other4
    पोलिशplone, few, many, other4
    रूसीruone, few, many, other4
    अरबीarzero, one, two, few, many, other6
    वेल्शcyzero, one, two, few, many, other6

    दो परिणाम जो लोगों को आश्चर्यचकित करते हैं:

    • one का अर्थ केवल "1" नहीं है। रूसी में, one 1, 21, 31, 101 को कवर करता है: 1 पर समाप्त होने वाली कोई भी संख्या, सिवाय उन संख्याओं के जो 11 पर समाप्त होती हैं। फ्रेंच में, 0 भी one में आता है।
    • अंग्रेजी स्रोत में श्रेणी जोड़ने से कुछ नहीं होता। अंग्रेजी संदेश को केवल one और other की आवश्यकता होती है; पोलिश अनुवाद को चार शाखाओं की आवश्यकता होती है, और वह संरचना पोलिश स्ट्रिंग में रहती है, न कि अंग्रेजी स्ट्रिंग में। कोई भी प्रारूप जो सभी भाषाओं को एक समान कुंजी संरचना साझा करने के लिए मजबूर करता है, वह यहाँ समस्या पैदा करेगा।

    आप बिना कुछ इंस्टॉल किए यह जांच सकते हैं कि रनटाइम वास्तव में क्या करता है:

    ts
    new Intl.PluralRules("pl").select(2); // "few"
    new Intl.PluralRules("pl").select(5); // "many"
    new Intl.PluralRules("ru").select(21); // "one"
    new Intl.PluralRules("ar").select(0); // "zero"
    

    Intl.PluralRules हर आधुनिक ब्राउज़र और Node में CLDR डेटा प्रदान करता है। CLDR प्लूरलाइजेशन का दावा करने वाली कोई भी लाइब्रेरी लगभग हमेशा इसके नीचे इसी को कॉल कर रही होती है।

    select और selectordinal

    select किसी भी यादृच्छिक स्ट्रिंग पर शाखा बनाता है: लिंग, भूमिका, स्थिति या योजना स्तर।

    text
    {gender, select,
      female {She updated her profile}
      male {He updated his profile}
      other {They updated their profile}
    }
    

    कुंजियों का शाब्दिक मिलान किया जाता है और यहाँ भी other अनिवार्य है। जब भी वाक्य की संरचना किसी इनम (enum) मान पर निर्भर करती है, तो select सही उपकरण है, क्योंकि भाषाएं इस बात पर असहमत होती हैं कि कौन से मान व्याकरण को प्रभावित करते हैं।

    selectordinal का आकार plural जैसा ही होता है, लेकिन यह क्रमसूचक (ordinal) बहुवचन नियमों का उपयोग करता है, जो मूल (cardinal) नियमों से अलग तालिका में होते हैं:

    text
    {rank, selectordinal,
      one {#st place}
      two {#nd place}
      few {#rd place}
      other {#th place}
    }
    

    अंग्रेजी चार क्रमसूचक श्रेणियों (1st, 2nd, 3rd, 4th) का उपयोग करती है, भले ही वह केवल दो मूल श्रेणियों का उपयोग करती हो। ठीक इसी असमानता के कारण दोनों ऑपरेटर अलग-अलग हैं।

    संख्या, दिनांक और समय तर्क

    ICU अपने द्वारा प्रक्षिप्त मान को सीधे प्रारूपित कर सकता है:

    text
    Total: {price, number, currency}
    Published {publishedAt, date, long} at {publishedAt, time, short}
    Conversion: {rate, number, percent}
    

    आधुनिक रूप skeleton है, जिसे ICU 60 के साथ पेश किया गया था और इसे :: उपसर्ग द्वारा चिह्नित किया जाता है। स्केलेटन पारंपरिक शैली नामों की तुलना में कहीं अधिक अभिव्यंजक हैं:

    text
    {price, number, ::currency/EUR}
    {value, number, ::percent scale/100}
    {amount, number, ::compact-short}
    {distance, number, ::unit/kilometer unit-width-narrow}
    

    इकोसिस्टम में स्केलेटन समर्थन अलग-अलग पुस्तकालयों में असमान है। FormatJS उन्हें पूरी तरह लागू करता है; कई अन्य रनटाइम केवल लेगेसी number, currency या date, long प्रारूप स्वीकार करते हैं। उत्पादन में उपयोग करने से पहले अपने रनटाइम में :: समर्थन की पुष्टि करें।

    नेस्टिंग और पठनीयता

    ICU संयोजन योग्य है। एक बहुवचन शाखा में एक select हो सकता है, जिसमें दूसरा बहुवचन हो सकता है:

    text
    {hostGender, select,
      female {{guestCount, plural, offset:1
        =0 {{host} does not give a party}
        =1 {{host} invites {guest} to her party}
        other {{host} invites {guest} and # other people to her party}
      }}
      other {{guestCount, plural, offset:1
        =0 {{host} does not give a party}
        other {{host} invites {guest} and # other people to their party}
      }}
    }
    

    यह विहित ICU उदाहरण है और गहरी नेस्टिंग के खिलाफ प्रमुख तर्क भी है। दो स्तरों से आगे अनुवादक कोष्ठक की त्रुटियाँ करने लगते हैं और TMS संपादक मदद करना बंद कर देते हैं। अधिक से अधिक दो स्तरों तक नेस्ट करें; यदि आपको तीसरे की आवश्यकता है, तो वाक्य को दो संदेशों में विभाजित करें।

    जेएस लाइब्रेरीज़ ICU को कैसे संभालती हैं

    लाइब्रेरीICU समर्थनआप वास्तव में क्या लिखते हैं
    react-intl (FormatJS)नेटिव, पूर्णस्केलेटन और रिच-टेक्स्ट टैग सहित ICU स्ट्रिंग्स
    next-intlनेटिवFormatJS के intl-messageformat के माध्यम से ICU स्ट्रिंग्स
    i18nextप्लगइन आवश्यकप्रत्यय कुंजियाँ key_one / key_other और {{name}}; ICU i18next-icu से
    vue-i18nआंशिक / अपना{name} इंटरपोलेशन और पाइप से अलग की गई बहुवचन शाखाएं
    Angular ($localize)उपसमूहटेम्प्लेट के अंदर ICU plural / select, XLIFF में निकाला गया

    तालिका के संदर्भ में कुछ महत्वपूर्ण बिंदु:

    • i18next का डिफ़ॉल्ट सिंटैक्स ICU नहीं है, और यह इसके लिए खराब नहीं है। प्रत्यय कुंजियाँ (item_one, item_few) Intl.PluralRules श्रेणियों पर मैप होती हैं और फ्लैट JSON में अनुवादकों के लिए संपादित करना आसान होती हैं। लेकिन select और नेस्टेड ब्रांचिंग इसका हिस्सा नहीं हैं, इसलिए आपको या तो i18next-icu जोड़ना होगा या कोड में लॉजिक लिखना होगा।
    • vue-i18n के पाइप बहुवचन डिफ़ॉल्ट रूप से CLDR श्रेणियों के बजाय प्रति-लोकेल नियम फ़ंक्शन का उपयोग करते हैं। यह काम करता है, लेकिन नियम डेटा के बजाय ऐप कॉन्फ़िगरेशन में रहता है।
    • FormatJS संदर्भ कार्यान्वयन है। जब लोग JS संदर्भ में "ICU MessageFormat" कहते हैं, तो उनका आमतौर पर मतलब वही होता है जो FormatJS स्वीकार करता है।
    • पूर्ण ICU समर्थन में बंडल आकार की लागत होती है। पार्सर और स्केलेटन हैंडलिंग लगभग 10 KB संपीड़ित जावास्क्रिप्ट जोड़ते हैं। देखें ICU जावास्क्रिप्ट के लिए क्यों नहीं बना है।

    Intlayer इसे कैसे संभालता है

    Intlayer स्ट्रिंग DSL का उपयोग नहीं करता है। ब्रांचिंग ऑपरेटर कंटेंट डिक्लेरेशन फ़ाइल में टाइप-सुरक्षित फ़ंक्शन हैं, इसलिए प्रत्येक भाषा केवल उन श्रेणियों को घोषित करती है जिनकी उसके व्याकरण को आवश्यकता होती है:

    **/*.content.ts
    import { plural, t, type Dictionary } from "intlayer";
    
    const openingsContent = {
      key: "total_openings",
      content: {
        totalOpenings: t({
          en: plural({
            one: "{{count}} opening",
            other: "{{count}} openings",
          }),
          hi: plural({
            one: "{{count}} रिक्ति",
            other: "{{count}} रिक्तियां",
          }),
          pl: plural({
            one: "{{count}} oferta",
            few: "{{count}} oferty",
            many: "{{count}} ofert",
            other: "{{count}} ofert",
          }),
        }),
      },
    } satisfies Dictionary;
    
    export default openingsContent;
    
    **/*.tsx
    const { totalOpenings } = useIntlayer("total_openings");
    
    totalOpenings(5); // पोलिश लोकेल → "5 ofert"
    

    ICU अवधारणाओं के साथ मैपिंग सीधी है:

    ICU निर्माणIntlayer समकक्ष
    {name}insert("Hello {{name}}"), या स्वचालित पहचान
    {count, plural, …}plural({ one, few, many, other })
    {value, select, …}select({ draft, published, fallback })
    select की लिंग शाखाgender({ male, female, fallback })
    select की बूलियन शाखाcond({ true, false })
    संख्यात्मक सीमाएं (गैर-CLDR)enu({ "0": …, ">5": …, fallback: … })
    {n, number, ::currency/EUR}useCurrency()(1234.5, { currency: "EUR" })

    plural श्रेणी चयन को सीधे Intl.PluralRules को सौंपता है, इसलिए ऊपर दी गई CLDR तालिका बिना किसी बदलाव के लागू होती है। फ़ॉर्मेटिंग अलग रहती है: संख्याएं, दिनांक, मुद्राएं और सूचियां संदेश में एम्बेड होने के बजाय फ़ॉर्मेटर हुक के माध्यम से नियंत्रित की जाती हैं।

    व्यावहारिक सीमाएं:

    • Intlayer को एक बिल्ड चरण की आवश्यकता होती है; कंपाइलर बिल्ड समय पर घोषणाओं को निकालता है। यदि आप रनटाइम पर साधारण JSON लोड करना चाहते हैं, तो वह एक अलग मॉडल है।
    • plural अभी अपनी शाखाओं के अंदर सीधे t() को नेस्ट नहीं कर सकता; आप plural को t() में लपेटते हैं, इसके विपरीत नहीं।
    • यह इकोसिस्टम i18next की तुलना में नया है, जिसमें कम तैयार TMS एकीकरण उपलब्ध हैं।

    यदि आप किसी ऐसे कोडबेस से आ रहे हैं जिसमें पहले से ही वास्तविक ICU स्ट्रिंग्स हैं, तो react-intl कम्पैट एडेप्टर उन्हें सीधे पार्स करता है: plural, select, selectordinal, #, और लेगेसी number / date / time तर्क। स्केलेटन और offset: उस रिज़ॉल्वर द्वारा कवर नहीं किए गए हैं, इसलिए माइग्रेट करते समय उन संदेशों की जांच करें। i18next एडेप्टर इसके बजाय प्रत्यय रूपों (key_one, key_male) को Intl.PluralRules के विरुद्ध हल करता है।

    सामान्य गलतियां

    • जावास्क्रिप्ट में बहुवचन लॉजिक को हार्डकोड करना। count === 1 ? a : b ऊपर दी गई तालिका की 10 में से 8 भाषाओं के लिए गलत आउटपुट देता है। एक बार जब टर्नरी ऑपरेटर कोड में आ जाता है, तो कोई भी अनुवादक इसे ठीक नहीं कर सकता।
    • अनुवादित टुकड़ों को आपस में जोड़ना। शब्दों का क्रम, व्याकरणिक सहमति और विराम चिह्नों का अंतर सभी भाषा-निर्भर हैं। पूरे वाक्य को एक साथ रखें।
    • other को छोड़ देना। यह विनिर्देश द्वारा आवश्यक है, कोई वैकल्पिक नियम नहीं। अधिकांश पार्सर संदेश को अस्वीकार कर देंगे या कुछ भी रेंडर नहीं करेंगे।
    • यह मान लेना कि आपकी श्रेणियां सार्वभौमिक हैं। अंग्रेजी स्रोत में one और other होने का मतलब यह नहीं है कि पोलिश फ़ाइल में केवल दो शाखाएं होंगी। प्रत्येक भाषा को अपनी स्वयं की शाखाएं घोषित करने दें। प्रति-भाषा सामग्री घोषणा देखें।
    • जहाँ one का मतलब था वहाँ =1 का उपयोग करना। =1 केवल शाब्दिक 1 से मेल खाता है। रूसी में, 21 को one की आवश्यकता होती है, और =1 इसके लिए कभी सक्रिय नहीं होगा।
    • # को बहुवचन शाखा के बाहर रखना। यह केवल plural या selectordinal के अंदर विशेष है। अन्य जगहों पर यह केवल एक साधारण हैश चिह्न है।
    • यह भूल जाना कि # पहले से ही स्वरूपित है। यदि आप कच्ची संख्या चाहते हैं, तो तर्क को नाम से इंटरपोलेट करें।

    आगे पढ़ें

    टिप्पणियाँ

    अभी तक कोई टिप्पणी नहीं। अपने विचार साझा करने वाले पहले व्यक्ति बनें।

    संबंधित पोस्ट

    नवीनतम पोस्ट