- 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>
187 lines
6.3 KiB
Markdown
187 lines
6.3 KiB
Markdown
# 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:
|
|
|
|
```csharp
|
|
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
|
|
|
|
```csharp
|
|
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:
|
|
|
|
```csharp
|
|
// 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:
|
|
|
|
```html
|
|
<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
|
|
|
|
```csharp
|
|
// Current: @(_isEn ? "Validators" : "Validadores")
|
|
// Updated:
|
|
@(_isEn ? "Validators" : _isEs ? "Validadores" : "Validadores")
|
|
// Most validator names are the same in ES/PT — adjust as needed.
|
|
```
|
|
|
|
Pricing label:
|
|
```csharp
|
|
@(_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
|
|
|
|
```csharp
|
|
// _esAbsolute = ES version URL
|
|
<link rel="alternate" hreflang="es" href="@_esAbsolute" />
|
|
```
|
|
|
|
#### h) `html lang` attribute
|
|
|
|
```csharp
|
|
<html lang="@(_isEn ? "en" : _isEs ? "es" : "pt-BR")">
|
|
```
|
|
|
|
### 3. Logo link
|
|
|
|
Currently:
|
|
```csharp
|
|
<a href="@(_isEn ? "/en" : "/")">
|
|
```
|
|
|
|
Update:
|
|
```csharp
|
|
<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:
|
|
|
|
```html
|
|
<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
|