Skip to content

Commit 0ce4f8a

Browse files
ghuronclaude
andcommitted
Add README documenting translation and static site adoption process
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 54f5d98 commit 0ce4f8a

1 file changed

Lines changed: 137 additions & 0 deletions

File tree

README.md

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
# nl.firstlinesoftware.com — Dutch static mirror
2+
3+
A Dutch-language static version of [firstlinesoftware.com](https://firstlinesoftware.com),
4+
hosted on GitHub Pages at [nl.firstlinesoftware.com](https://nl.firstlinesoftware.com).
5+
6+
---
7+
8+
## How this site was built
9+
10+
### 1. Fetch the current English site with all assets
11+
12+
```bash
13+
wget --page-requisites \
14+
--span-hosts \
15+
--convert-links \
16+
--no-host-directories \
17+
--adjust-extension \
18+
-e robots=off \
19+
https://firstlinesoftware.com/
20+
```
21+
22+
`--convert-links` rewrites absolute URLs to relative ones in `index.html`.
23+
`--adjust-extension` appends `.css`/`.js` to files that have query strings
24+
(e.g. `style.alt.css?ver=123` → saved as `style.alt.css?ver=123.css`).
25+
26+
**Important:** wget overwrites `index.html` with a fresh English copy. Apply
27+
all Dutch translations *after* running wget, not before.
28+
29+
### 2. Dynamically loaded assets wget misses
30+
31+
Some assets are injected at runtime by JavaScript and are not reachable by
32+
wget's static crawler. Download them manually:
33+
34+
```bash
35+
# HubSpot form CSS (injected by forms/v2.js)
36+
curl -o wp-content/themes/cleanslate/althubspot.css \
37+
"https://firstlinesoftware.com/wp-content/themes/cleanslate/althubspot.css?ver=..."
38+
```
39+
40+
Check the browser Network tab for any remaining 404s after first deploy.
41+
42+
### 3. Files with `?ver=...` query strings
43+
44+
wget saves files with a literal `?` in the filename
45+
(e.g. `theme.js?ver=1773578688`). The HTML references them with a
46+
URL-encoded `%3F` (e.g. `src="theme.js%3Fver=1773578688"`).
47+
48+
Python's `http.server` decodes `%3F` back to `?` before the filesystem
49+
lookup, so local serving works correctly without renaming files.
50+
51+
For files where the browser sends a real query string (e.g. `althubspot.css?ver=...`),
52+
Python's `http.server` strips the query part and serves `althubspot.css` — so
53+
save those without the `?ver=...` suffix.
54+
55+
### 4. Dutch translation
56+
57+
Translations were applied via Python `str.replace()` after fetching. Watch out for:
58+
59+
- HTML entities: the source HTML uses `&#038;` (not `&amp;`) and `&#8217;` (not `'`)
60+
- Unicode characters that look like spaces/newlines: U+2028 (LINE SEPARATOR),
61+
U+00A0 (non-breaking space), U+2019 (right single quote)
62+
- Always inspect raw bytes when a replacement fails: `open(f, 'rb').read()`
63+
64+
Key metadata changes for Dutch:
65+
```html
66+
<html lang="nl-NL">
67+
<meta property="og:locale" content="nl_NL" />
68+
<meta property="og:url" content="https://nl.firstlinesoftware.com/" />
69+
<!-- JSON-LD -->
70+
"inLanguage": "nl-NL"
71+
```
72+
73+
### 5. Dutch text layout issues
74+
75+
Dutch words are longer than English on average. Check for overflow/wrapping
76+
problems using Playwright + headless Chromium:
77+
78+
```python
79+
# Accurate visual line count using Range.getClientRects()
80+
# (scrollHeight/lineHeight gives false positives due to CSS margins)
81+
def visual_lines(el):
82+
return el.evaluate('''e => {
83+
const range = document.createRange();
84+
range.selectNodeContents(e);
85+
const ys = new Set([...range.getClientRects()].map(r => Math.round(r.top)));
86+
return ys.size;
87+
}''')
88+
```
89+
90+
Fix overflow with CSS hyphenation (added to `<head>`):
91+
```html
92+
<style>
93+
body { hyphens: auto; -webkit-hyphens: auto; }
94+
a.button, button, nav a, .footer-nav a { hyphens: none; -webkit-hyphens: none; }
95+
</style>
96+
```
97+
98+
Shorten translations for narrow containers (mode panes ~312px, SDLC section
99+
~530px, leadership figcaptions, stats labels).
100+
101+
### 6. Cookiebot on a static site
102+
103+
The WordPress Cookiebot plugin uses `data-implementation="wp"`, which causes
104+
`uc.js` to fetch `/{cbid}/cc.js` from your own domain — a dynamic WordPress
105+
endpoint that doesn't exist on a static site.
106+
107+
**Use the standard (non-WordPress) Cookiebot embed instead:**
108+
```html
109+
<script id="Cookiebot"
110+
src="https://consent.cookiebot.com/uc.js"
111+
data-cbid="990e16bd-2861-4a00-a4ea-4dbe73a34971"
112+
data-blockingmode="auto">
113+
</script>
114+
```
115+
116+
- No `data-implementation="wp"` — everything loads from Cookiebot's CDN
117+
- No `data-georegions` — that's also WordPress-plugin-specific
118+
- The CBID `990e16bd...` is configured for `nl.firstlinesoftware.com` in the
119+
Cookiebot admin (different from the main site's CBID `066e3009...`)
120+
121+
### 7. Footer
122+
123+
```html
124+
Copyright © 2026 First Line Software B.V. (KVK 58362126)
125+
```
126+
127+
---
128+
129+
## Updating the site
130+
131+
When `firstlinesoftware.com` is updated:
132+
133+
1. Run the wget command above (saves fresh assets, overwrites `index.html`)
134+
2. Re-apply Dutch translations to the new `index.html`
135+
3. Check for new dynamically loaded assets in the browser Network tab
136+
4. Run the Playwright layout checker for new overflow issues
137+
5. Commit and push

0 commit comments

Comments
 (0)