Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/deploy-site.yml
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ jobs:
}

check "/" 200 "One translation model."
check "/frameworks" 200 "Five frameworks."
check "/frameworks" 200 "Six frameworks."
check "/proof" 200 "Claims you can re-run."
check "/get-started" 200 "First working translation"
check "/compare" 200 "Narrower than the alternatives."
Expand Down
9 changes: 5 additions & 4 deletions Containerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Run all twenty Palamedes example apps side by side in a single container, each
# Run all 24 Palamedes example apps side by side in a single container, each
# on its fixed port from scripts/example-matrix.mjs. Built and run with Podman:
#
# podman build -f Containerfile -t palamedes-examples .
Expand All @@ -8,6 +8,7 @@
# -p 4030:4030 -p 4031:4031 -p 4032:4032 -p 4033:4033 \
# -p 4040:4040 -p 4041:4041 -p 4042:4042 -p 4043:4043 \
# -p 4050:4050 -p 4051:4051 -p 4052:4052 -p 4053:4053 \
# -p 4060:4060 -p 4061:4061 -p 4062:4062 -p 4063:4063 \
# palamedes-examples
#
# (or let the matrix generate the flags: `podman run --init \
Expand Down Expand Up @@ -40,7 +41,7 @@ COPY . .

RUN pnpm install --frozen-lockfile
# `pnpm build` builds the workspace packages and compiles the native addon via
# cargo; `pnpm build:examples` builds all twenty example apps.
# cargo; `pnpm build:examples` builds all 24 example apps.
RUN pnpm build
RUN pnpm build:examples
# Drop build-only artifacts before they reach the runtime image: the compiled
Expand All @@ -66,13 +67,13 @@ COPY --from=build --chown=node:node /app /app
# Global pnpm at the version pinned in package.json (packageManager) so the
# unprivileged `node` user can run the example start scripts.
RUN npm install -g "pnpm@$(node -p 'require("./package.json").packageManager.split("@")[1].split("+")[0]')"
# Run the twenty public servers as a non-root user (least privilege).
# Run the 24 public servers as a non-root user (least privilege).
USER node

# Fixed ports — informational only; the authoritative list is
# scripts/example-matrix.mjs. Publish them without drift via:
# podman run $(node ./scripts/container/print-podman-ports.mjs) palamedes-examples
EXPOSE 4010 4011 4012 4013 4020 4021 4022 4023 4030 4031 4032 4033 4040 4041 4042 4043 4050 4051 4052 4053
EXPOSE 4010 4011 4012 4013 4020 4021 4022 4023 4030 4031 4032 4033 4040 4041 4042 4043 4050 4051 4052 4053 4060 4061 4062 4063

# tini is the init/reaper (no external `--init` needed). `-s` registers it as a
# subreaper so reaping still works if it ends up not as PID 1 (e.g. when the
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,14 +72,14 @@ easier to review, and easier to carry from one framework to the next.
## Current Status

- Recommended for new projects and teams that want cleaner i18n foundations
- Verified today across Next.js, TanStack Start, SolidStart, Waku, and React Router on Node.js `>=22.22`
- Verified today across Next.js, TanStack Start, SolidStart, Waku, React Router, and server-first Remix v3 on Node.js `>=22.22`
- Source-string-first catalogs are stable and powered by `ferrocat`, including structured audits and ICU authoring diagnostics
- Placeholder top-level packages exist, but there is no `palamedes` or `create-palamedes` first-run entry yet
- 1.0 stability tiers and public API expectations are documented in [Stability and versioning](https://github.com/sebastian-software/palamedes/blob/main/docs/stability.md)

## What Exists Today

- A browser-verified example matrix across five framework families
- A browser-verified example matrix across six framework families
- Versioned screenshots generated from the same Playwright-based verifier used in CI
- Reproducible benchmark commands for transform, extract, catalog update, compile steps, and end-to-end extract/update workflows
- Structured catalog audit and metadata validation APIs backed by `ferrocat`
Expand Down
69 changes: 46 additions & 23 deletions docs/demo-deployments.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,30 @@

The Palamedes example matrix is verified primarily through local runs and CI.
Automatic deployments are not part of the default merge or release path. The
matrix spans twenty examples (five frameworks × four locale strategies). Public
demo URLs are documented as the live reference surface, but reachability depends
on the hosting and DNS notes in this document. The five subdomain demos require
the per-example wildcard DNS records described under Subdomain Locale Hosting.
The five tld demos require the `examples.palamedes-i18n.*` domains described
under TLD Locale Hosting.
matrix spans 24 examples (six frameworks × four locale strategies). Public demo
URLs are documented as the live reference surface where hosting exists, but
reachability depends on the hosting and DNS notes in this document. The five
currently hosted subdomain demos require the per-example wildcard DNS records
described under Subdomain Locale Hosting. The five currently hosted tld demos
require the `examples.palamedes-i18n.*` domains described under TLD Locale
Hosting. Remix v3 is verified locally and in CI, but is not yet a public demo
deployment target.

## Current Policy

- the canonical verification path is `pnpm build:examples` plus `pnpm verify:examples`
- example deployments do not run automatically on `main`
- the Next.js and SolidStart examples (including their subdomain and tld variants) are excluded from `deploy-examples.yml`
- the Next.js, SolidStart, and Remix examples (including their subdomain and tld variants) are excluded from `deploy-examples.yml`
- the `deploy-examples.yml` workflow supports manual deployment of the Vite-based examples (TanStack, Waku, React Router — cookie, route, subdomain, and tld) if an additional hosted URL is needed

## Live Reference URLs

These URLs describe the intended public reference shape — five frameworks, each
in four locale strategies. Switch language in a reachable demo and watch copy,
plural seat counts, currency, and dates change together. The demos are grouped
by framework below, with every locale-specific URL linked directly.
These URLs describe the intended public reference shape — six frameworks, each in
four locale strategies. Switch language in a reachable demo and watch copy,
plural seat counts, currency, and dates change together. The demos are grouped by
framework below, with every locale-specific URL linked directly where public
hosting exists. Remix v3 rows link to source because that beta integration is
currently a local/CI proof surface.

How each strategy encodes the locale:

Expand Down Expand Up @@ -79,14 +83,27 @@ How each strategy encodes the locale:
| subdomain | [en](https://en.solidstart-subdomain.examples.palamedes.dev) · [de](https://de.solidstart-subdomain.examples.palamedes.dev) · [es](https://es.solidstart-subdomain.examples.palamedes.dev) |
| tld | [en](https://solidstart.examples.palamedes-i18n.com) · [de](https://solidstart.examples.palamedes-i18n.de) · [es](https://solidstart.examples.palamedes-i18n.es) · [fr](https://solidstart.examples.palamedes-i18n.fr) |

### Remix v3

Remix v3 examples are verified through the default Remix Node loader path in CI.
They are not public demo deployments yet while the Remix v3 beta hosting and UI
adapter story settles.

| Strategy | Reference source |
| --------- | ------------------------------------------------------- |
| cookie | [examples/remix-cookie](../examples/remix-cookie) |
| route | [examples/remix-route](../examples/remix-route) |
| subdomain | [examples/remix-subdomain](../examples/remix-subdomain) |
| tld | [examples/remix-tld](../examples/remix-tld) |

## Subdomain Locale Hosting (DNS And Reverse Proxy)

The subdomain demos encode the locale in the leftmost DNS label
(`de.nextjs-subdomain.examples.palamedes.dev` renders German). That label sits one
level below the existing `*.examples.palamedes.dev` wildcard, which only covers a
single label: it resolves `nextjs-subdomain.examples.palamedes.dev` but not
`de.nextjs-subdomain.examples.palamedes.dev`. Each subdomain example therefore
needs its own wildcard record:
`de.nextjs-subdomain.examples.palamedes.dev`. Each public subdomain example
therefore needs its own wildcard record:

- `*.nextjs-subdomain.examples.palamedes.dev`
- `*.tanstack-subdomain.examples.palamedes.dev`
Expand All @@ -113,7 +130,9 @@ implies, but it must hold for caching layers too.
These records and proxy routes are now in place, so the five subdomain rows above
are publicly reachable (each locale host returns 200 and renders its locale). The
canonical verification path remains `pnpm verify:examples`, which exercises the
subdomain strategy locally via `*.lvh.me` hosts.
subdomain strategy locally via `*.lvh.me` hosts. Remix subdomain support is
verified the same way, but it is not included in the public DNS/proxy deployment
plan yet.

## TLD Locale Hosting (DNS And Reverse Proxy)

Expand All @@ -140,10 +159,12 @@ the `Host` in its cache key (or the app must send `Vary: Host`); otherwise a
response for one TLD could be served for another. This is the same constraint the
per-host routing already implies, but it must hold for caching layers too.

Until these domains are provisioned, the five tld rows in the Live Reference table
are not yet reachable. The canonical verification path runs locally via
Until these domains are provisioned, the five public tld rows in the Live
Reference table are not yet reachable. The canonical verification path runs locally via
`pnpm verify:examples`, which exercises the tld strategy using Chromium's
`--host-resolver-rules` flag to simulate the TLD hosts without real DNS.
`--host-resolver-rules` flag to simulate the TLD hosts without real DNS. Remix TLD
support is covered by the same local/CI verification path, not by public TLD
deployment yet.

## Optional Manual Deployments

Expand Down Expand Up @@ -173,17 +194,19 @@ Supported deployment targets:
- `waku-tld`
- `react-router-tld`

## Why Next.js Is Not In `deploy-examples.yml`
## Why Next.js, SolidStart, And Remix Are Not In `deploy-examples.yml`

The Next.js examples are part of the verified matrix, but they are excluded from
the `deploy-examples.yml` workflow. That workflow targets the Vite-based examples
(TanStack, Waku, React Router — cookie, route, subdomain, and tld) specifically.
The Next.js, SolidStart, and Remix examples are part of the verified matrix, but
they are excluded from the `deploy-examples.yml` workflow. That workflow targets
the Vite-based examples (TanStack, Waku, React Router — cookie, route, subdomain,
and tld) specifically.

For this OSS setup, the guaranteed baseline is:

- the examples build
- the examples run locally
- SSR, locale routing, cookie handling, and localized server actions are covered in browser tests

The hosting mechanism for the Next.js examples is separate from
`deploy-examples.yml` and is not further documented here.
The hosting mechanism for the Next.js and SolidStart examples is separate from
`deploy-examples.yml` and is not further documented here. Remix v3 remains a
server-first beta proof surface until a public hosting target is chosen.
2 changes: 1 addition & 1 deletion docs/founder-led-content.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ Drafts included in this round:
- [What we delegated to Ferrocat and why](./site/posts/what-we-delegated-to-ferrocat-and-why.md)
- [Measuring Palamedes honestly](./site/posts/measuring-palamedes-honestly.md)
- [From Lingui to Palamedes without changing how authoring feels](./site/posts/from-lingui-to-palamedes.md)
- [How we browser-verify i18n across five frameworks](./site/posts/browser-verifying-i18n-across-five-frameworks.md)
- [How we browser-verify i18n across six frameworks](./site/posts/browser-verifying-i18n-across-five-frameworks.md)
- [The third time I built JavaScript i18n tooling](./site/posts/the-third-time-i-built-javascript-i18n-tooling.md)
- [Round 1 micro-content](./site/posts/micro-content-round-1.md)

Expand Down
40 changes: 23 additions & 17 deletions docs/operations/run-examples-container.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Running all examples together in one container

This document describes how to run all twenty Palamedes example apps together in
This document describes how to run all 24 Palamedes example apps together in
**one** Podman container – each on its own fixed port. It builds on the
`Containerfile` in the repo root and the supervisor
`scripts/container/start-all.mjs`.
Expand All @@ -12,7 +12,7 @@ This document describes how to run all twenty Palamedes example apps together in
## Prerequisites

- [Podman](https://podman.io/) installed
- Enough resources for ten concurrent SSR Node servers (several GB of RAM
- Enough resources for two dozen concurrent SSR Node servers (several GB of RAM
recommended)

A Rust or Node setup on the host is **not** required: the container's build stage
Expand Down Expand Up @@ -42,7 +42,7 @@ The build is multi-stage:

1. **Build stage** (glibc/Debian): `pnpm install`, `pnpm build` (builds the
packages including the native addon via `cargo` in release profile) and
`pnpm build:examples` (builds all twenty apps). The `cargo` compilation makes the
`pnpm build:examples` (builds all 24 apps). The `cargo` compilation makes the
first build noticeably longer.
2. **Runtime stage** (slimmer Debian slim): takes over the finished workspace and
starts the supervisor. Dev dependencies stay installed because TanStack's
Expand All @@ -57,7 +57,7 @@ from the ports the servers actually bind:
podman run $(node ./scripts/container/print-podman-ports.mjs) palamedes-examples
```

Spelled out, this is `-p 4010:4010 -p 4011:4011 … -p 4051:4051`.
Spelled out, this is `-p 4010:4010 -p 4011:4011 … -p 4063:4063`.

- **Init/reaping:** the image ships `tini` as its entrypoint, so reparented child
processes are reaped cleanly with no extra flag (also works later via
Expand All @@ -70,22 +70,28 @@ Spelled out, this is `-p 4010:4010 -p 4011:4011 … -p 4051:4051`.
The supervisor starts all apps from `scripts/example-matrix.mjs`, prefixes their
output with the example id (`[nextjs-cookie] …`), and intentionally exits the
container with an error code as soon as a server dies unexpectedly (fail-fast).
`podman stop` terminates all twenty servers via forwarded `SIGTERM`.
`podman stop` terminates all 24 servers via forwarded `SIGTERM`.

## Port overview

| Port | Example | Strategy |
| ---- | --------------------- | -------- |
| 4010 | `nextjs-cookie` | cookie |
| 4011 | `nextjs-route` | route |
| 4020 | `tanstack-cookie` | cookie |
| 4021 | `tanstack-route` | route |
| 4030 | `waku-cookie` | cookie |
| 4031 | `waku-route` | route |
| 4040 | `react-router-cookie` | cookie |
| 4041 | `react-router-route` | route |
| 4050 | `solidstart-cookie` | cookie |
| 4051 | `solidstart-route` | route |
| Port | Example | Strategy |
| ---- | ---------------------- | --------- |
| 4010 | `nextjs-cookie` | cookie |
| 4011 | `nextjs-route` | route |
| 4020 | `tanstack-cookie` | cookie |
| 4021 | `tanstack-route` | route |
| 4030 | `waku-cookie` | cookie |
| 4031 | `waku-route` | route |
| 4040 | `react-router-cookie` | cookie |
| 4041 | `react-router-route` | route |
| 4050 | `solidstart-cookie` | cookie |
| 4051 | `solidstart-route` | route |
| 4052 | `solidstart-subdomain` | subdomain |
| 4053 | `solidstart-tld` | tld |
| 4060 | `remix-cookie` | cookie |
| 4061 | `remix-route` | route |
| 4062 | `remix-subdomain` | subdomain |
| 4063 | `remix-tld` | tld |

`scripts/example-matrix.mjs` is the single source of truth for the ports. The
supervisor binds according to it automatically, and the `print-podman-ports.mjs`
Expand Down
8 changes: 4 additions & 4 deletions docs/site/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ every time the framework changes.

It gives you one runtime model, one message identity model, and one catalog
workflow across verified integrations for Next.js, TanStack Start, SolidStart,
Waku, React Router, and Vite. Backend servers use the same runtime model but
are documented as a guide rather than a verified matrix family.
Waku, React Router, Remix v3, and Vite. Backend servers use the same runtime
model but are documented as a guide rather than a verified matrix family.

We are not asking you to trust a slogan. The repo shows the work.

Expand All @@ -25,7 +25,7 @@ working across different app shapes.

The repo backs that claim with visible evidence:

- five framework families: Next.js, TanStack Start, SolidStart, Waku, and React Router
- six framework families: Next.js, TanStack Start, SolidStart, Waku, React Router, and Remix v3
- four locale strategies per framework: cookie, route segment, subdomain, and tld
- versioned browser screenshots generated from the same Playwright verifier used in CI
- reproducible benchmark commands for transform, extract, catalog update, artifact compile, and end-to-end extract/update workflows
Expand Down Expand Up @@ -96,7 +96,7 @@ Palamedes makes specific claims, and the repo shows the work behind them.

Current evidence assets:

- verified example matrix across five framework families
- verified example matrix across six framework families
- four locale strategies per framework
- browser screenshots generated from the same Playwright-based verifier used in CI
- benchmark fixtures and documented methodology
Expand Down
2 changes: 1 addition & 1 deletion docs/site/internal-storyline.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ without reinventing the narrative.
3. Why this is rare: most alternatives focus on one framework or carry more historical surface
4. The architectural core: native core, thin adapters, delegated catalog semantics
5. The runtime story: one runtime model, one identity model
6. The confidence story: verified example matrix across five framework families
6. The confidence story: verified example matrix across six framework families
7. The evidence story: screenshots, CI flows, benchmarks, ADRs
8. The comparison story: Lingui closest, `next-intl` different center, GT broader category
9. Why it matters commercially: cleaner migrations, clearer architecture, better foundations for higher-level workflows
Expand Down
2 changes: 1 addition & 1 deletion docs/site/posts/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ checkable in the repo or to a public source.
- [What we delegated to Ferrocat and why](./what-we-delegated-to-ferrocat-and-why.md)
- [Measuring Palamedes honestly](./measuring-palamedes-honestly.md)
- [From Lingui to Palamedes without changing how authoring feels](./from-lingui-to-palamedes.md)
- [How we browser-verify i18n across five frameworks](./browser-verifying-i18n-across-five-frameworks.md)
- [How we browser-verify i18n across six frameworks](./browser-verifying-i18n-across-five-frameworks.md)
- [The third time I built JavaScript i18n tooling](./the-third-time-i-built-javascript-i18n-tooling.md)
- [Round 1 micro-content](./micro-content-round-1.md)

Expand Down
15 changes: 8 additions & 7 deletions docs/site/posts/browser-verifying-i18n-across-five-frameworks.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
date: "2026-07-05"
---

# How We Browser-Verify i18n Across Five Frameworks
# How We Browser-Verify i18n Across Six Frameworks

Status: draft

Expand All @@ -14,21 +14,22 @@ interaction, and server actions all have to agree.

Palamedes treats that as a verification problem.

The repo contains an example matrix across five framework families:
The repo contains an example matrix across six framework families:

- Next.js
- TanStack Start
- SolidStart
- Waku
- React Router
- Remix v3

Each family has four locale strategies:

- cookie-based locale persistence
- route-segment locale persistence

That gives twenty example apps. Each one has browser-visible checks for the parts
that usually hide i18n bugs:
That gives 24 example apps. Each one has visible checks for the parts that
usually hide i18n bugs:

- server-rendered localized text before hydration
- client-side locale switching
Expand All @@ -55,9 +56,9 @@ For Palamedes, the thesis is:
The matrix is where that thesis has to survive contact with frameworks.

Next.js and React Router do not fail in the same places. TanStack Start,
SolidStart, and Waku each have their own server/client boundaries. Route-based
locale state and cookie-based locale state put pressure on different parts of
the adapter layer.
SolidStart, Waku, and Remix v3 each have their own server/client boundaries.
Route-based locale state and cookie-based locale state put pressure on different
parts of the adapter layer.

That is why this kind of verification is stronger than a single starter app.

Expand Down
Loading
Loading