Skip to content

Commit aa556c5

Browse files
Copilotbrunoborges
andcommitted
docs: add i18n architectural plans to specs/i18n
Co-authored-by: brunoborges <129743+brunoborges@users.noreply.github.com>
1 parent 494e2a2 commit aa556c5

File tree

2 files changed

+650
-0
lines changed

2 files changed

+650
-0
lines changed
Lines changed: 253 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,253 @@
1+
# Plan A — Locale-Keyed Content Files
2+
3+
## Overview
4+
5+
Introduce locale-specific JSON content directories that mirror the existing
6+
`content/` tree. The generator merges each locale's translations on top of the
7+
English baseline at build time, then emits a fully localised copy of every
8+
page under a locale-prefixed path (e.g. `site/pt-BR/language/slug.html`).
9+
10+
A language selector in the navigation bar lets users switch locales. The
11+
English site continues to live at `/` (no prefix) so existing links and
12+
search-engine rankings are preserved.
13+
14+
---
15+
16+
## Directory Layout
17+
18+
```
19+
content/ # English source of truth (unchanged)
20+
language/
21+
type-inference-with-var.json
22+
collections/
23+
...
24+
25+
content/i18n/ # Locale overlays
26+
pt-BR/
27+
language/
28+
type-inference-with-var.json # Only the fields that differ
29+
ui.json # Navigation / section labels
30+
ja/
31+
language/
32+
type-inference-with-var.json
33+
ui.json
34+
...
35+
36+
templates/ # Templates unchanged
37+
html-generators/
38+
categories.properties # English display names
39+
categories.pt-BR.properties # Localised category display names
40+
generate.java / generate.py # Extended to accept a --locale flag
41+
42+
site/ # Generated output
43+
index.html # English home (no prefix)
44+
language/
45+
type-inference-with-var.html
46+
data/
47+
snippets.json # English search index
48+
pt-BR/ # Locale subtree
49+
index.html
50+
language/
51+
type-inference-with-var.html
52+
data/
53+
snippets.json
54+
ja/
55+
...
56+
```
57+
58+
---
59+
60+
## JSON Overlay Schema
61+
62+
Each locale overlay file contains only the fields that change; everything else
63+
is inherited from the English baseline file.
64+
65+
```json
66+
// content/i18n/pt-BR/language/type-inference-with-var.json
67+
{
68+
"title": "Inferência de tipo com var",
69+
"summary": "Use var para deixar o compilador inferir o tipo local.",
70+
"explanation": "...",
71+
"oldApproach": "Tipos explícitos",
72+
"modernApproach": "Palavra-chave var",
73+
"whyModernWins": [
74+
{ "icon": "", "title": "Menos ruído", "desc": "..." },
75+
{ "icon": "👁", "title": "Mais legível", "desc": "..." },
76+
{ "icon": "🔒", "title": "Seguro", "desc": "..." }
77+
]
78+
}
79+
```
80+
81+
Fields **not** present in the overlay (`oldCode`, `modernCode`, `jdkVersion`,
82+
`docs`, `related`, `prev`, `next`, `support`) are always taken from the
83+
English baseline — code snippets, links, and navigation stay language-neutral.
84+
85+
### `ui.json` — UI Strings
86+
87+
```json
88+
// content/i18n/pt-BR/ui.json
89+
{
90+
"nav": {
91+
"allPatterns": "← Todos os padrões",
92+
"toggleTheme": "Alternar tema",
93+
"viewOnGitHub": "Ver no GitHub"
94+
},
95+
"sections": {
96+
"codeComparison": "Comparação de código",
97+
"whyModernWins": "Por que a forma moderna ganha",
98+
"oldApproach": "Abordagem antiga",
99+
"modernApproach": "Abordagem moderna",
100+
"sinceJdk": "Desde o JDK",
101+
"difficulty": "Dificuldade",
102+
"jdkSupport": "Suporte ao JDK",
103+
"howItWorks": "Como funciona",
104+
"relatedDocs": "Documentação relacionada",
105+
"relatedPatterns": "Padrões relacionados"
106+
},
107+
"filters": {
108+
"show": "Mostrar:",
109+
"all": "Todos",
110+
"difficulty": {
111+
"beginner": "Iniciante",
112+
"intermediate": "Intermediário",
113+
"advanced": "Avançado"
114+
}
115+
},
116+
"search": {
117+
"placeholder": "Buscar padrões...",
118+
"noResults": "Nenhum resultado encontrado."
119+
}
120+
}
121+
```
122+
123+
---
124+
125+
## Generator Changes
126+
127+
1. **Accept `--locale` flag** (e.g. `generate.java --locale pt-BR`).
128+
When omitted the generator builds only the English site (current
129+
behaviour).
130+
131+
2. **Merge algorithm**:
132+
```
133+
merged = deepMerge(english_baseline, locale_overlay)
134+
```
135+
Merge happens in memory; neither source file is modified.
136+
137+
3. **Output path**: all files for locale `<loc>` are written to
138+
`site/<loc>/…` with relative asset paths adjusted
139+
(`../../styles.css`, `../../app.js`, etc.).
140+
141+
4. **Category display names**: loaded from
142+
`html-generators/categories.<locale>.properties` when it exists,
143+
falling back to `categories.properties`.
144+
145+
5. **`data/snippets.json`**: a separate
146+
`site/<loc>/data/snippets.json` is generated from the merged content
147+
so the locale-specific search index is accurate.
148+
149+
6. **Build all locales at once**:
150+
```bash
151+
jbang html-generators/generate.java # English
152+
jbang html-generators/generate.java --locale pt-BR
153+
jbang html-generators/generate.java --locale ja
154+
```
155+
Or, when a build-all mode is added:
156+
```bash
157+
jbang html-generators/generate.java --all-locales
158+
```
159+
160+
---
161+
162+
## Template Changes
163+
164+
Templates (`templates/slug-template.html`, `templates/index.html`) gain
165+
additional `{{…}}` tokens for localised UI strings:
166+
167+
| Token | Source field |
168+
|---|---|
169+
| `{{labelCodeComparison}}` | `sections.codeComparison` |
170+
| `{{labelWhyModernWins}}` | `sections.whyModernWins` |
171+
| `{{labelHowItWorks}}` | `sections.howItWorks` |
172+
| `{{labelRelatedDocs}}` | `sections.relatedDocs` |
173+
| `{{labelRelatedPatterns}}` | `sections.relatedPatterns` |
174+
| `{{navAllPatterns}}` | `nav.allPatterns` |
175+
| `{{searchPlaceholder}}` | `search.placeholder` |
176+
177+
English values are hard-coded defaults (matching the current literal strings)
178+
so the English build requires no `ui.json`.
179+
180+
---
181+
182+
## HTML `<head>` Changes
183+
184+
Each detail page gains `<link rel="alternate">` tags and the `<html lang>`
185+
attribute is set to the locale:
186+
187+
```html
188+
<html lang="pt-BR">
189+
<head>
190+
...
191+
<link rel="alternate" hreflang="en" href="https://javaevolved.github.io/language/type-inference-with-var.html">
192+
<link rel="alternate" hreflang="pt-BR" href="https://javaevolved.github.io/pt-BR/language/type-inference-with-var.html">
193+
<link rel="alternate" hreflang="x-default" href="https://javaevolved.github.io/language/type-inference-with-var.html">
194+
</head>
195+
```
196+
197+
---
198+
199+
## Navigation — Language Selector
200+
201+
A locale pill is added to the nav bar (right-hand side, beside the theme
202+
toggle). It is rendered as a `<select>` or a dropdown `<button>`:
203+
204+
```html
205+
<select id="localePicker" aria-label="Select language">
206+
<option value="en" selected>English</option>
207+
<option value="pt-BR">Português (Brasil)</option>
208+
<option value="ja">日本語</option>
209+
</select>
210+
```
211+
212+
JavaScript (`app.js`) listens for changes and navigates to the equivalent
213+
page in the chosen locale by rewriting the path prefix.
214+
215+
---
216+
217+
## GitHub Actions Changes
218+
219+
The deploy workflow gains a step that loops over every locale directory in
220+
`content/i18n/` and invokes the generator for each:
221+
222+
```yaml
223+
- name: Generate locales
224+
run: |
225+
jbang html-generators/generate.java
226+
for locale in content/i18n/*/; do
227+
loc=$(basename "$locale")
228+
jbang html-generators/generate.java --locale "$loc"
229+
done
230+
```
231+
232+
---
233+
234+
## Migration Path
235+
236+
| Phase | Work |
237+
|---|---|
238+
| 1 | Add `ui.json` English defaults; extract hard-coded label strings from templates into tokens |
239+
| 2 | Extend generator to accept `--locale`, implement merge algorithm |
240+
| 3 | Add language selector to nav; write `app.js` path-rewrite logic |
241+
| 4 | Create first locale overlay (e.g. `pt-BR`) as a proof-of-concept |
242+
| 5 | Update GitHub Actions; add `hreflang` and `<html lang>` to templates |
243+
244+
---
245+
246+
## Trade-offs
247+
248+
| Pros | Cons |
249+
|---|---|
250+
| Overlay-only translations mean small locale files | Translators must know the JSON schema |
251+
| English site path unchanged — SEO not affected | Each new locale doubles generated-file count |
252+
| Build-time generation — no runtime i18n library needed | Generator becomes more complex |
253+
| Code snippets stay language-neutral automatically | Navigation (`prev`/`next`) still points to English slugs (cross-locale navigation not addressed) |

0 commit comments

Comments
 (0)