We Built a Multilingual Citizen-Service Portal for a Tier-2 Municipal Corporation: 7 Languages, 1.4M Users
A Tier-2 municipal corporation citizen portal: Next.js + i18n, Aadhaar Login, DigiLocker, 7 languages, 1.4M registered users in year 1. Architecture decisions, accessibility tradeoffs, and lessons that port to SMB.
Vivek Kumar
December 26, 202514 min read
0%
In 2024 a Tier-2 municipal corporation (population ~2.1 million, the city is identified by the client only as "Project Praja" for confidentiality) asked us to replace a 9-year-old PHP citizen-services portal that handled 11,000 daily logins on a good day, crashed twice a week, and supported only English and the regional language. We rebuilt it on Next.js 14 + i18n + Aadhaar Login + DigiLocker, with WCAG 2.2 AA accessibility, support for 7 Indian languages, and a target capacity of 200k concurrent users. By Dec 2025, the new portal had 1.4 million registered users, processed 38 lakh service requests, and held a 99.93% uptime SLA. This post is the architecture, the decisions we got wrong the first time, and the lessons that port to SMB-scale builds.
1.4M
Registered users by Dec 2025
7
Indian languages supported
99.93%
Year-1 uptime (8h 22m total downtime)
38 lakh
Service requests processed (Dec 24 - Dec 25)
## TL;DR β what we built in 60 words
Next.js 14 (App Router) + Tailwind + i18n with 7 language packs (English, Hindi, regional, plus 4 minority-language packs), Aadhaar Login via the official MeitY-empanelled provider, DigiLocker integration for document fetch, PostgreSQL on a 3-node managed Patroni cluster, Redis Sentinel for sessions, Cloudflare in front, citizen-services microservices (24 services across 6 departments) on Kubernetes. Capacity: 200k concurrent. Mobile-first; 84% of traffic is mobile.
## Why this matters now β December 2025
India's Digital India push has accelerated. Tier-2 municipal corporations are now under explicit mandate (under the [Smart Cities Mission](https://smartcities.gov.in/) and various state-level digital-governance frameworks) to migrate citizen services online with Aadhaar Login and DigiLocker integration. Most existing portals are 6-12 years old, monolithic PHP/Java applications running on aging on-prem servers. Replacement budgets opened up in FY 2024-25 across at least 18 Tier-2 cities we are aware of. The pattern of work is the same; the lessons here apply broadly.
The case study has obvious read-across to SMB e-governance work, but more interestingly, the patterns (multilingual UI, government-issued identity verification, mobile-first accessibility, public-scale traffic) port directly to fintech, edtech, and B2C SaaS being built for non-English Indian markets.
What we cannot share: client name, exact city, exact dates of the migration, the security architecture details, and the specific Aadhaar Login provider name. The architecture, language matrix, and lessons below are released with explicit client approval.
## The 4 architectural decisions that shaped everything
β‘
1. Next.js 14 App Router with SSR + edge caching
Pre-render every public page (service catalog, FAQ, language pack) at the CDN edge. Authenticated pages stream SSR from a regional pod. p50 TTFB <180ms across 7 languages.
π
2. i18n via next-intl, not raw key lookups
[next-intl](https://next-intl-docs.vercel.app/) for ICU message format support β handles plurals, gender, number formatting per locale. Critical for languages where "5 documents" agrees with the noun differently than English.
π
3. Aadhaar Login via MeitY-empanelled API
Aadhaar Login (eKYC + OTP via UIDAI) for primary authentication. DigiLocker for document fetching. Both integrated via the empanelled-provider API; we never touch Aadhaar numbers directly.
βΏ
4. WCAG 2.2 AA from day one
Government-issued mandate. Designed mobile-first; tested with screen readers (NVDA, TalkBack); typography sized for low-literacy users; voice-input supported for form fields.
## The full architecture
## The 7 languages β choosing what to support
Not all "Indian languages" are equally needed. Our research with the city's RTI office (citizen complaint records) showed:
Language
Speaker share (city)
Existing portal usage
Decision
English
~22%
78% of legacy portal traffic (urban skew)
v1 launch
Hindi
~38%
11% (fallback when English failed)
v1 launch
Regional language (state's official)
~32%
9% (poorly translated)
v1 launch (re-translated)
Urdu
~5%
0% (not supported)
v1 launch
Marathi (cross-border migrants)
~2%
0%
v2 launch
Tamil (cross-border migrants)
~0.6%
0%
v2 launch
Bengali (cross-border migrants)
~0.4%
0%
v2 launch
The speaker-share data did not justify all 7 from cost-perspective alone. The political mandate from the municipal commissioner did: "no citizen should be excluded by language." We launched English + Hindi + regional + Urdu in v1 (12 weeks); added the other 3 minority languages in v2 (8 weeks later). Year-1 usage of the v2 languages was a combined 1.8% of traffic β small but meaningful. The complaint volume from non-supported-language speakers dropped by an estimated 63% (per the same RTI complaint records).
## The translation pipeline (what nobody warns you about)
Government portal translation is a real engineering problem. Three lessons:
Lesson 1: machine translation is not enough for legal/government text. Aadhaar consent text, RTI declarations, and grievance-form acknowledgements have specific legal meaning. We used Google Translate for first-draft English β target language, then routed to a panel of 3 linguistic reviewers per language (paid βΉ4-8 per word). The cost: roughly βΉ2.4 lakh per language for the v1 content set (~30,000 words), βΉ1.2 lakh per language for v2 minority languages (smaller content set).
Lesson 2: number, date, and currency formatting per locale. "βΉ1,00,000" in Indian-English is "1,00,000.00 ΰ€°ΰ₯ΰ€ͺΰ€―ΰ₯" in Hindi (with Devanagari digits as preference, Latin as fallback). next-intl's ICU support handles this β but you have to wire it correctly. Skip this and the page looks like a translation, not a real localisation.
Lesson 3: continuous translation (not just at launch). New schemes, new municipal orders, new error messages get added monthly. We built a translation workflow: developer writes English string β automatic GitHub PR to "missing translations" issue per language β linguistic reviewer confirms in a Notion workflow β deployed via hot-reload of the i18n service (no app restart). Time from English string to all 7 languages live: median 6.4 hours.
## Aadhaar Login: what you can and cannot do
We integrated Aadhaar Login via a [MeitY-empanelled Aadhaar authentication provider](https://uidai.gov.in/en/ecosystem/authentication-devices-documents.html). The non-negotiable rules:
You never receive or store the citizen's 12-digit Aadhaar number. The provider returns a UIDAI-generated stable user ID.
OTP flow uses the citizen's UIDAI-registered mobile. You cannot bypass the OTP.
You must show the [Aadhaar consent screen](https://uidai.gov.in/) verbatim β text approved by UIDAI, in the user's chosen language.
Audit logs of every authentication attempt (success + failure) retained for 6 months minimum.
WCAG 2.2 AA on the login screen β including OTP entry which is genuinely hard for screen-reader users.
No data sharing with third parties; no analytics SDKs that capture form input.
DigiLocker integration is similar but lower-stakes β citizens authorise the portal to fetch specific documents (driving license, voter ID, education certificates) from their DigiLocker. The provider returns the document directly to the user; the portal sees only metadata (issued by, date, type). Read [DigiLocker's developer docs](https://digilocker.gov.in/) for the exact OAuth flow.
## Year-1 numbers (Dec 2024 - Dec 2025)
1.4M
Registered users (60% mobile-first signup)
38 lakh
Service requests processed
99.93%
Year-1 uptime (8h 22m downtime)
2.8 sec
Median request-to-response on a service form (mobile, 4G)
The 8 hours 22 minutes of downtime was 4 incidents: (1) a Patroni failover that took longer than expected (38 min, May 2025), (2) a Cloudflare regional outage (1h 14m, August 2025), (3) a planned migration window for the Aadhaar provider that overran (3h 22m, October 2025), (4) one bot-DDoS attack we mitigated within 3 hours of detection (3h 8m, November 2025). All 4 documented openly in the city's IT-quarterly reports per Right To Information requirements.
## The 5 things we got wrong the first time
Wrong #1: assumed font support was free. The default Next.js font loader does not include Devanagari, Tamil, Bengali, or Urdu glyphs. Pages rendered with Roman-font fallbacks looked terrible in the regional and minority languages. Fix: explicitly load [Noto Sans](https://fonts.google.com/noto) variants per language; defer non-active language fonts. Adds 18-32 KB per language; worth it.
Wrong #2: SSR cache key did not include locale. First-week deployment had English content cached and served to Hindi requests. Fix: locale always in the cache key. Trivial code change; we missed it because dev/staging traffic was English-only.
Wrong #3: under-tested screen reader experience. WCAG 2.2 AA is a code standard; screen-reader UX is a usability problem. Our first NVDA test on a service form took 8 minutes to complete what sighted users finished in 90 seconds. Fix: hire 2 visually-impaired UX testers as paid consultants; iterate on ARIA labels, focus order, and announcement patterns.
Wrong #4: optimistic on Aadhaar OTP delivery. The first month had a ~6% OTP-failure rate (OTP not received on registered mobile). Cause: the Aadhaar provider's SMS gateway was hit by a different government client's burst, dropping our OTPs. Fix: dual-provider OTP fallback with 30-second retry to a backup gateway. OTP failure dropped to <0.4%.
Wrong #5: did not budget for sustained DDoS volume. A bot-net targeted the OTP endpoint in November 2025 trying to brute-force authentication. Cloudflare absorbed the volume; our backend nearly toppled because we had not rate-limited per-Aadhaar-ID. Fix: per-stable-ID rate limit (max 5 OTP requests per 15 min per Aadhaar user), per-IP global limit (100 OTP requests per minute), CAPTCHA after 3 failures.
## What ports to SMB-scale builds
You are not building a citizen portal. But three patterns from this work apply directly to fintech, edtech, and B2C SaaS for non-English markets:
π±
Mobile-first, low-bandwidth-first
84% of users on mobile, 60% on 4G in suburbs. Pages must be functional under 200 KB initial bundle and render usable content in <2s on a 3G connection.
π
Real i18n vs translated strings
ICU message format with plurals, gender, number formatting per locale. This is the difference between "translated app" and "localised app". Your Tier-2/3 buyers can tell.
π
DigiLocker for document workflow
Any onboarding flow that asks for "upload your driving license" can use DigiLocker instead. Lower friction, government-verified, no fraud-doc risk. Adds 4-6 days of dev work.
βΏ
WCAG 2.2 AA as competitive moat
Most B2C apps in India are inaccessible. Building accessibly opens up 80M+ disabled users + the older-user segment that is rarely served. Marketing magic.
For a smaller SMB build that uses the same DigiLocker + Aadhaar pattern, see our work for an Indian fintech client (case study available on request β currently under NDA).
## When NOT to build like this
Three cases where this architecture is overkill or wrong for your project.
You are building a B2B SaaS for <1k users. WCAG 2.2 AA is still good practice, but the i18n + Aadhaar + DigiLocker layer is noise at this scale. Build the core product; add localisation when user research demands it.
Your users are uniformly English-speaking enterprise. A Bangalore B2B SaaS for fintech operations teams genuinely does not need Hindi. Spending the localisation budget on better English UX is the right call.
You do not need government identity verification. Aadhaar Login adds 6-10 weeks of integration work, ongoing UIDAI compliance, and an annual audit. If your product can use email + phone OTP authentication, do that.
## Real example β the schedule and cost shape
For "Project Praja" specifically:
- Discovery & UX (8 weeks): βΉ14.4 lakh β RTI data analysis, citizen interviews, accessibility audit of legacy portal, language-need research, IA design.
- v1 build (16 weeks): βΉ98 lakh β 6 engineers + 1 designer + 1 PM + 0.5 FTE compliance officer. Delivered 4 languages, Aadhaar Login, DigiLocker, 12 of 24 services.
- v2 build (8 weeks, post-launch): βΉ42 lakh β added 3 minority languages, 12 more services, accessibility iteration, performance hardening.
- Year-1 ops (Dec 2024 - Dec 2025): βΉ68 lakh β managed cloud, observability, on-call, ongoing translation, security patching.
- Total year-1: ~βΉ2.22 crore β for a portal serving 1.4M users at 99.93% uptime. Per-user year-1 cost: ~βΉ15.85.
The ROI was measured in citizen-time-saved (per the city's own KPI: average time to obtain a basic municipal service dropped from 4.2 days to 2.1 hours), not direct revenue. For SMB e-governance work, the budgets shrink (βΉ40-90 lakh year-1 is typical for a Tier-2 city subset deployment), but the architectural pattern stays similar.
## A common question
"Is the architecture proprietary or can we share the patterns?"
The architecture is intentionally non-proprietary. We pushed the city's IT department to publish the technical reference architecture as part of the [eGovernments Foundation](https://egov.org.in/) open-source initiative β the goal is that other Tier-2 cities can fork rather than rebuild. As of December 2025, three other cities have approved similar projects using the published reference. The implementations are independent (different vendors); the architecture is shared.
For SMBs reading this: the same i18n + Aadhaar + DigiLocker patterns are reusable directly. The municipal-specific microservices (water billing, birth certificate workflow) are not, but they are not what you would copy.
## FAQ
### How does Aadhaar Login differ from Aadhaar OTP?
Aadhaar Login is a UIDAI-blessed authentication flow that returns a stable user ID after biometric or OTP verification. It does not expose the 12-digit Aadhaar number to your application. Aadhaar OTP is a more limited eKYC service for verifying that a citizen owns a specific Aadhaar β used for one-time KYC, not ongoing authentication. For citizen portals, Aadhaar Login is the right primitive.
### Can private companies use Aadhaar Login?
Yes, with restrictions. Private entities must be empanelled with UIDAI as Authentication User Agencies (AUAs) or Sub-AUAs through a MeitY-empanelled service provider. The empanelment process takes 4-12 weeks. Cost: βΉ0 application fee; per-transaction charges apply (~βΉ0.50-2.00 per authentication depending on volume).
### What about citizens without smartphones?
The portal supports SMS-based service request submissions for citizens with feature phones β they SMS a structured code (e.g., "WATER 12345" for a water complaint at registered property 12345) and a citizen-service kiosk volunteer follows up. Approximately 4% of v1 traffic was via this pathway.
### How did you handle translation accuracy disputes?
We set up a "report translation" link on every page that opened a Google Form. Reports went to the linguistic review panel weekly. Median resolution: 9 days. ~340 translation issues reported across year-1; 87% confirmed and fixed.
### Is the codebase open source?
The reference architecture is open source via eGovernments Foundation. Specific city implementations (including this one) are not. The civic-tech argument for full open-sourcing is strong but the political call rests with each city.
### What about voice input for low-literacy users?
We integrated [Bhashini](https://bhashini.gov.in/) (the Government of India's national language translation initiative) for voice-to-text on form fields in 4 languages. Year-1 usage: ~2.4% of form submissions used voice input β small but meaningful for the demographic that needed it most.
### How do you handle the GDPR-equivalent data protection requirements (DPDP Act 2023)?
The Digital Personal Data Protection Act 2023 (notified rules expected 2025-26) treats government data fiduciaries with stricter standards than private. We built consent flows, data-minimisation logging, and a citizen-data-deletion request mechanism aligned with DPDP early-draft rules. When the final rules notify, we expect minor adjustments only.
## Where this work fits
We do this kind of large-scale Next.js + i18n + Aadhaar + DigiLocker integration through our web development and mobile development services. The municipal-portal work is the most visible; we do similar deep-stack builds for fintech, edtech (see our in-house PenLeap product as an example of language-pack and DigiLocker patterns applied to edtech), and B2C SaaS clients.
This post was written by Vivek Kumar, who led the engagement. For more on the engineering practice that scales these projects, our founder Vivek Singh writes from a more first-person founder lens. For our case study work on a smaller-scale Indian fintech build, see Radiant Finance.
For more on the Indian regulatory and compliance angle (UPI, GST, advance tax) that surrounds this kind of build, see our recent posts on Dec 15 advance-tax sprint and Razorpay-Tally reconciliation.
The thread on [r/india](https://www.reddit.com/r/india/) discussing the new wave of municipal portals captures the citizen sentiment well β broadly positive, with a sharp eye for accessibility failures.
Need a Multilingual Government Portal or Large-Scale Citizen Service?
We build large-scale multilingual web platforms with Aadhaar Login + DigiLocker + WCAG 2.2 AA accessibility. Typical engagement: 16-24 weeks for v1, βΉ40 lakh - βΉ2.5 crore depending on scope and language count. Suitable for municipal corporations, state e-governance projects, and private B2C platforms targeting Tier-2/3 Indian markets. First call is technical with the engineer who would lead your project.