NALU/docs/adding-language.md
Ricardo Carneiro e01787ee60 Add deploy infrastructure, missing validators, and new features
- 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>
2026-05-15 12:31:12 -03:00

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.cshtml via Context.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.

// 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")

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")">

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:

  1. Pages/En/*.cshtml — duplicate folder as Pages/Es/, translate content
  2. Pages/_Layout.cshtml — 6 locations: detection, known sets, altUrl switch, globe dropdown, nav labels, hreflang tags
  3. No changes needed to: PageModels (thin, language-agnostic), API endpoints, auth, Stripe