- Add Docker Swarm deploy stack, CI workflow (.gitea), entrypoint script - Fix Dockerfile to build Nalu.Web (was pointing to old Nalu.Api path) - Add validate_name.md and other missing validators to prod - Add Stripe endpoints, HangfireDashboardAuth, InputGuard, NameLookupService - Add SuspiciousRateLimiter, En/ pages, Legal/ pages, Seguranca docs - Add Nalu.Jobs and Nalu.NameImporter projects (were untracked) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
6.3 KiB
Adding a new language
Reference: implemented PT (default) + EN (/en). Next planned: ES (/es).
Architecture overview
- Language is URL-prefix based:
/en/...,/es/... - No cookies, no sessions — URL is the source of truth
- Language detected in
_Layout.cshtmlviaContext.Request.Path - Pages without a translation simply don't get hreflang tags
Checklist to add a new language (e.g. /es)
1. Create page files
Mirror the Pages/En/ folder structure:
Pages/Es/
Index.cshtml + Index.cshtml.cs → @page "/es"
Pricing.cshtml + Pricing.cshtml.cs → @page "/es/pricing"
Playground.cshtml + Playground.cshtml.cs → @page "/es/playground"
Validators/
Index.cshtml + Index.cshtml.cs → @page "/es/validators"
Docs/
Index.cshtml + Index.cshtml.cs → @page "/es/docs"
Quickstart.cshtml + .cs → @page "/es/docs/quickstart"
ApiReference.cshtml + .cs → @page "/es/docs/api-reference"
Mcp.cshtml + .cs → @page "/es/docs/mcp"
N8n.cshtml + .cs → @page "/es/docs/n8n"
Credits.cshtml + .cs → @page "/es/docs/credits"
Errors.cshtml + .cs → @page "/es/docs/errors"
Validator ordering for ES: Universal first (same as EN), Brazilian second.
2. Update _Layout.cshtml
File: src/Nalu.Web/Pages/_Layout.cshtml
a) Language detection block (top @{ })
Add ES detection alongside EN:
var _isEn = Context.Request.Path.StartsWithSegments("/en");
var _isEs = Context.Request.Path.StartsWithSegments("/es"); // ADD
var _lp = _isEn ? "/en" : _isEs ? "/es" : ""; // UPDATE
b) Known pages sets — add ES set
var _knownEs = new HashSet<string> {
"/es", "/es/pricing", "/es/validators", "/es/playground",
"/es/docs", "/es/docs/quickstart", "/es/docs/api-reference",
"/es/docs/mcp", "/es/docs/n8n", "/es/docs/credits", "/es/docs/errors"
};
bool _hasAlternate = (_isEn ? _knownEn : _isEs ? _knownEs : _knownPt).Contains(_cur);
c) _altUrl switch — add ES cases to all three branches
The switch currently has EN→PT and PT→EN. Add:
- EN→ES branch (or link via PT as pivot)
- ES→PT branch
- PT→ES branch
Simplest: each language maps to PT as canonical pivot, then PT maps to all others. Or: extend the switch to map directly between all pairs. Given 3 languages, direct mapping is cleaner:
// When on ES page, altUrl = PT equivalent
"/es" => "/",
"/es/pricing" => "/precos",
// ... etc
// When on PT page, need to decide which alternate to link to.
// Convention: globe shows the "other" language not currently active.
// With 3 languages, globe becomes a dropdown with all options.
d) Globe dropdown — extend to 3 options
Replace the current 2-option globe with a 3-option dropdown:
<a href="[PT URL]">🇧🇷 Português @(_isEn || _isEs ? "" : "✓")</a>
<a href="[EN URL]">🇺🇸 English @(_isEn ? "✓" : "")</a>
<a href="[ES URL]">🇪🇸 Español @(_isEs ? "✓" : "")</a>
You'll need to compute both _altUrlEn and _altUrlEs (and _altUrlPt) separately instead of a single _altUrl.
e) Nav link labels — add ES strings
// Current: @(_isEn ? "Validators" : "Validadores")
// Updated:
@(_isEn ? "Validators" : _isEs ? "Validadores" : "Validadores")
// Most validator names are the same in ES/PT — adjust as needed.
Pricing label:
@(_isEn ? "Pricing" : _isEs ? "Precios" : "Preços")
f) Footer — add ES to use cases section
Currently the "Casos/Use cases" section links to PT-only /casos/... pages. For ES, either link to the same PT pages or create ES case study pages.
g) hreflang — add ES alternate
// _esAbsolute = ES version URL
<link rel="alternate" hreflang="es" href="@_esAbsolute" />
h) html lang attribute
<html lang="@(_isEn ? "en" : _isEs ? "es" : "pt-BR")">
3. Logo link
Currently:
<a href="@(_isEn ? "/en" : "/")">
Update:
<a href="@(_isEn ? "/en" : _isEs ? "/es" : "/")">
4. Pricing page — currency
ES pricing: same USD values as EN page (/en/pricing as reference). Spanish-speaking markets outside Brazil → use USD, show BRL as ≈ R$ X secondary (same pattern as EN).
Or: if targeting Spain specifically, consider EUR. Decision needed per market.
5. Validators ordering
| Language | Order |
|---|---|
| PT | Brazilian first, Universal second |
| EN | Universal first, Brazilian second |
| ES | Universal first, Brazilian second (same as EN) |
6. PT pages without EN equivalent
Pages like /casos/parcelas-48x, /docs/fluxos, /validadores/reply have no EN/ES version. Globe dropdown on these pages shows links to EN/ES home (/en, /es) as fallback. This is intentional.
URL slug conventions
| Concept | PT | EN | ES (planned) |
|---|---|---|---|
| Home | / |
/en |
/es |
| Pricing | /precos |
/en/pricing |
/es/precios |
| Validators | /validadores |
/en/validators |
/es/validadores |
| Playground | /playground |
/en/playground |
/es/playground |
| Docs | /docs |
/en/docs |
/es/docs |
| Credits | /docs/creditos |
/en/docs/credits |
/es/docs/credits |
| Errors | /docs/erros |
/en/docs/errors |
/es/docs/errors |
SEO tags (auto-generated in layout)
The layout generates these automatically for pages with known translations:
<link rel="canonical" href="https://naluai.dev/[current-path]" />
<link rel="alternate" hreflang="pt-BR" href="https://naluai.dev/[pt-path]" />
<link rel="alternate" hreflang="en" href="https://naluai.dev/en/[en-path]" />
<link rel="alternate" hreflang="x-default" href="https://naluai.dev/[pt-path]" />
x-default always points to PT (primary market). Add hreflang="es" when ES is implemented.
Files to touch (summary)
When adding ES:
Pages/En/*.cshtml— duplicate folder asPages/Es/, translate contentPages/_Layout.cshtml— 6 locations: detection, known sets, altUrl switch, globe dropdown, nav labels, hreflang tags- No changes needed to: PageModels (thin, language-agnostic), API endpoints, auth, Stripe