आपने Claude Code से sign-in form में loading state जोड़ने को कहा। Loading state सही काम करती है। लेकिन button अब “Sign in” के बजाय “Log in” कहता है, error message को ज़्यादा दोस्ताना बना दिया गया है और एक heading अचानक sentence case में आ गई है। इनमें से किसी बदलाव के लिए किसी ने नहीं कहा था, सभी tests पास हो जाते हैं और जब तक कोई पूरा diff शब्द-दर-शब्द न पढ़े, यह बदलाव production में चला जाता है।
यह post इसी खास failure के बारे में है: किसी AI coding agent द्वारा मौजूदा, approved copy को चुपचाप दोबारा लिख देना—और उसे रोकने वाला workflow। Agent द्वारा बनाए गए hardcoded और un-keyed strings एक अलग समस्या हैं, जिसे why AI coding tools keep adding hardcoded strings में कवर किया गया है। सामान्य रूप से product copy कहाँ रहनी चाहिए, यह architecture का सवाल है, जिसका उत्तर code-native product copy में दिया गया है। यह post सिर्फ़ incident पर केंद्रित है—वह Monday morning जब wording बदल गई और किसी ने तय नहीं किया था कि उसे बदलना चाहिए। globalize.now localization built-in वाला code-native copy management system है, और यही incident इसके अस्तित्व के कारणों में से एक है।
AI coding agents UI copy को दोबारा क्यों लिखते हैं?
AI coding agents UI copy को दोबारा लिखते हैं, क्योंकि उन्हें components के भीतर सीधे लिखा हुआ prose दिखाई देता है। जब वही copy source locale file में keys के पीछे रखी जाती है, तो उसे बदलना किसी दूसरे task के side effect के बजाय एक स्पष्ट, reviewable edit बन जाता है।
किसी component के भीतर “Sign in” की कोई विशेष हैसियत नहीं होती। वह JSX attribute में मौजूद एक string literal है, जिसे className या test id से अलग पहचानना मुश्किल है। Agent को ऐसे लाखों diffs पर train किया गया है जहाँ आसपास के code को बेहतर बनाना अच्छा व्यवहार माना जाता है; और model के लिए किसी label को “ज़्यादा स्पष्ट” बनाना variable का नाम बदलने जैसा ही है। न कोई type error आने वाला है, न कोई failing test, न ही उन शब्दों पर किसी owner का annotation। Wording दोबारा लिखने में agent को कोई लागत नहीं लगती।
फिर यही बदलाव review में भी उसी कारण से बच निकलता है। Reviewers logic पढ़ते हैं। Wording change ऐसे diff के बीच में छिपा होता है जो वास्तव में loading state के बारे में है; code compile होता है, render होता है और अगर snapshot test शिकायत करे, तो सामान्य प्रतिक्रिया snapshot update करना होती है—जिससे नई wording ऐसे दर्ज हो जाती है मानो वह approved हो।
क्या AGENTS.md जैसी rules files सचमुच इसे रोकती हैं?
Agent rule files इसकी frequency कम करती हैं; वे UI copy को unwritable नहीं बनातीं। Architecture ही edit को visible बनाती है।
फिर भी rules file ज़रूर रखें—यह सबसे सस्ती layer है और इतनी बार काम करती है कि इसके लिए तीस सेकंड देना उचित है। यह block AGENTS.md, CLAUDE.md और Cursor rules (.cursorrules या .cursor/rules/) में काम करता है:
## UI copy policy — do not modify user-facing text
- Never change user-visible strings (button labels, headings, empty states,
error messages, tooltips, placeholder text) unless the task explicitly
asks for a copy change.
- User-visible text lives in the source locale file (e.g. locales/en.json).
Reference it by key. Never inline a new user-facing string in a component.
- If a task needs new user-facing text, add a key to the source locale file,
reference it, and list the new key in your summary for copy review.
- Any diff to the source locale file is a copy change. Call it out
separately — it needs its own approval, apart from code review.
अब ईमानदार बात। Rule context होती है, और context आपस में प्रतिस्पर्धा करता है। लंबे task में यह instruction code के हज़ारों tokens के बीच सिर्फ़ एक पंक्ति होती है और कभी-कभी हार जाती है—compaction से, ऐसे sub-agent से जिसने इसे देखा ही नहीं, या आसपास की file में मौजूद किसी मजबूत pattern से। इससे भी बुरा यह है कि rule तोड़ने वाला agent इसकी घोषणा नहीं करता। आपको कोई notification नहीं मिलता कि wording बदल गई; आपको बस ऐसा diff मिलता है जिसमें वह बदल चुकी होती है। Convention कोई check नहीं है।
इसलिए rules file पहली layer है—और वह layer भी है जिस पर अकेले भरोसा नहीं करना चाहिए।
Copy edit को चुपचाप होने देने के बजाय visible कैसे बनाएं?
हर user-visible string को components से बाहर निकालकर source locale file में keys के पीछे रखें। इसके बाद wording बदलने वाले agent को ऐसी file edit करनी होगी जिसका पूरा उद्देश्य ही wording है।
यह वह step है जो rules file नहीं कर सकती, और इसका तरीका सीधा है। जब components “Sign in” को दोबारा लिखने के बजाय t('auth.signIn') reference करते हैं, तो एक साथ दो चीज़ें बदलती हैं। Component diffs में copy आना बंद हो जाती है, इसलिए loading state पर काम कर रहे agent के पास उसी जगह reword करने के लिए कुछ नहीं होता। और हर copy change—agent का हो या इंसान का—एक ही file में आता है, जहाँ बदली हुई line वही घटना होती है जिसकी आपको परवाह है, न कि उसके आसपास का noise। Locale-file diff को scan करने में कुछ सेकंड लगते हैं। खराब reword को revert करना एक line का काम है।
सटीक रूप से कहें तो इससे आपको क्या मिलता है: agent अभी भी locale file edit कर सकता है। उद्देश्य यह नहीं कि शब्दों को छुआ ही न जा सके—उद्देश्य यह है कि उन्हें छूना side effect न रहकर एक स्पष्ट, reviewable action बन जाए। यही diff से पता चलने और customer से पता चलने के बीच का अंतर है।
Source of truth को design tool में रखने के बजाय codebase में रखने का पूरा तर्क—भले ही design tool उससे sync होता हो—code-native product copy में pillar के रूप में दिया गया है। इस post के लिए इसका सिर्फ़ एक परिणाम महत्वपूर्ण है: keyed copy आपकी rules file को request से बदलकर ऐसी चीज़ बना देती है जिसे check किया जा सकता है।
Review gate कैसा दिखता है?
Source locale file में होने वाले हर diff को copy change मानें और उसे उन लोगों तक पहुँचाएँ जो wording के owner हैं।
व्यवहार में: locales directory को CODEOWNERS में रखें, ताकि उसमें होने वाले हर change पर copy-owning human को request किया जाए। PR template में एक line जोड़ें—“क्या यह PR user-facing wording बदलता है, और क्या यही task था?” साथ ही rules-file की यह requirement बनाए रखें कि agent अपने summary में बदली हुई keys की सूची दे। जब वह इसका पालन करता है, तो review तुरंत हो जाता है; और जब नहीं करता, तो CODEOWNERS hook फिर भी उसे पकड़ लेता है।
छोटी team में यह सुनने से कहीं आसान है। Locale diff छोटा होता है, plain language में पढ़ा जा सकता है और “क्या इस button पर अब Log in लिखा होना चाहिए?” ऐसा निर्णय कोई पाँच सेकंड में ले सकता है—बशर्ते सवाल वास्तव में उसके सामने रखा जाए। Gate का पूरा काम यही है।
अगर agent आपकी copy पहले ही दोबारा लिख चुका हो तो?
पहले approved wording वापस लाएँ, फिर strings को key करें ताकि अगली rewording diff के रूप में दिखाई दे।
नुकसान का पता लगाना git का काम है: प्रभावित components को उस आख़िरी भरोसेमंद commit से diff करें और केवल string literals पढ़ें। जो बदला है उसे वापस restore करें। अगर आप भरोसे से नहीं कह सकते कि approved wording कौन-सी थी—button पर “Log in” है, marketing page पर “Sign in” है और कोई artifact दर्ज नहीं करता कि सही कौन-सा है—तो आप उस गहरी स्थिति तक पहुँच चुके हैं जिसे यह incident उजागर करता है। Copy drift का अर्थ है user-visible text का अलग-अलग surfaces और समय के साथ अपनी approved source से भटकना, जबकि भटकने के लिए कोई single source of truth मौजूद न हो। यह term, इसके कारण और downstream प्रभाव अपने अलग लेख what is copy drift में हैं।
इसके बाद restore की गई copy को key करें—सबसे पहले उन strings को जिन्हें agent ने छुआ था। वे स्पष्ट रूप से सबसे अधिक जोखिम वाली जगह पर हैं।
Localization इसमें कहाँ आती है?
एक भाषा में चुपचाप की गई rewording सिर्फ़ परेशानी है, लेकिन दस भाषाओं में यह liability बन जाती है: पुरानी wording से निकला हर translation अब stale है और आपको retranslate करने के लिए कुछ भी सचेत नहीं करता।
यहीं agent incident का असर बढ़ जाता है। आप reworded English ship कर देते हैं, लेकिन German, Japanese और Spanish में अब भी पुरानी बात लिखी रहती है—अब surfaces सिर्फ़ screens के बीच नहीं, भाषाओं के बीच भी असहमत हैं। Catalogs का sync से बाहर हो जाना अपनी अलग failure mode है, जिसके fixes translation files out of sync में दिए गए हैं।
Keyed copy इस loop को भी बंद करती है, क्योंकि बदला हुआ source string एक visible event बन जाता है जिससे उसके translations जुड़े होते हैं, न कि ऐसा literal जो चुपचाप कहीं बदल गया हो। globalize.now के पीछे यही design है: copy codebase में रहती है, keys के पीछे single-sourced होती है और localization कोई bolt-on नहीं, बल्कि वही operation होती है—repository connect करें और हर language उसी एक source से derive होती है। AI coding tools के साथ ship करने वाली teams को यह समस्या सबसे पहले दिखती है, इसलिए हम developers और building with AI tools करने वालों के लिए लिखते हैं।
अगली बार जब कोई agent आपके components में काम कर रहा हो, सवाल यह है कि बदला हुआ label एक decision के रूप में दिखाई देगा या surprise के रूप में। globalize.now आपके connect किए गए repository में user-visible strings को single-source करता है और उसी एक copy से हर language derive करता है।
globalize.now आपके ऐप्लिकेशन की हार्डकोडेड कॉपी को अनुवाद-तैयार locale फ़ाइलों में बदल देता है और जैसे-जैसे आप नई रिलीज़ करते हैं, उन्हें अपडेट रखता है।
globalize.now को निःशुल्क आजमाएं