Build1 distinct publisher3 min readUpdated
A fan site ships nine languages as static HTML with English on bare root URLs. The stated reason is per-request Worker billing, and the arithmetic behind that reason is wrong.
The Engineer · Build desk

Compiled by The EngineerSomething wrong?How this is made
The arithmetic does not hold, even though the conclusion mostly does. Cloudflare bills per request and next-intl middleware runs on every request [5], so middleware invocations for a page load track requests, not the size of the locale table. One visitor pulling `/zh/gameplay` is one request whether the project ships two languages or ninety [9]. The author's "9x the middleware invocations for every page load" [6] is not what those two facts produce. The saving on offer is one invocation per request going to zero, which is a real saving and a smaller story than the multiplier suggests.
It is also an unpriced one. The post carries no invocation counts, no Workers pricing tier, and no before-and-after bill [7], so the motivation stays a stated preference rather than a measured result.
What the pattern definitely costs is structure. The directory listing shows `gameplay/` and `characters/` under both `(root)` and `[locale]` [12], so the route tree exists twice and every new page has to be created in both places. Shared view components absorb most of that, since the same component renders all nine languages and only the message file changes [13]. The locale list is the part that leaks: it is hardcoded in `generateStaticParams` and again in `sitemap.ts` [14], two arrays that have to be edited together when a tenth language arrives.
The sitemap is worth counting, because the post does not. Five paths at 0.8 priority for English, plus eight locales times five paths at 0.7 [15], is 45 URLs [16] from one build with zero runtime locale detection [8].
The `safeLoad` helper is the most honest thing in the post. It wraps `t.raw()` in a try/catch and a shape check so a section renders only when the translation actually exists [10]. That is not defensive habit, it is a consequence of the content pipeline: the strings were scraped out of the game's official nine-language gameplay guide with a Python script rather than translated [11], and scraped coverage is ragged by nature. The null check is load-bearing structure standing in for the missing content.
The other borrowed asset is heavier. Screenshots are referenced straight off FromSoftware's own CDN, under a path containing `networktest`, with no local copies [17]. The author calls that CDN fast and reliable [17], which it presumably is right up to the point where a finished network test stops needing its image directory.
Strip the billing story out and the technique still stands on its own for any site whose locale set is fixed at build time and whose default language deserves the bare root. English on `/gameplay`, everything else prefixed [4], nine static variants out of one OpenNext build [18]. The price is a duplicated tree and the loss of any server-side guess about who the visitor is.
Follow any of these and your For You feed starts watching them — no settings page required.
Ranked by verification strength, evidence, and original report placement.
The author built a multilingual fan site for The Duskbloods, an upcoming FromSoftware game, covering nine languages (English, Japanese, Korean, Chinese, Spanish, French, German, Italian, Portuguese), statically generated, with no middleware, deployed on Cloudflare Workers.
The site uses Next.js 15 App Router with next-intl v4 for internationalization.
Instead of middleware for locale detection, the author uses route groups: (root) holds English content at the root path, and [locale] holds other languages at /zh, /ja, /ko and so on.
English gets clean URLs such as /gameplay while other languages get prefixed URLs such as /zh/gameplay; the author states this is good for SEO because English is the default and other languages have clear URL signals.
The post states the cost motivation but contains no Worker invocation counts, no pricing tier, and no before-and-after billing figures.
generateStaticParams in src/app/[locale]/layout.tsx returns ja, zh, ko, es, fr, de, it and pt, pre-generating all locale variants at build time with, in the author's words, zero runtime locale detection.
Evidence-backed comparisons of source perspectives and observed adoption signals. Read the methodology
Which Builder, Operator, and Investor concerns the observed source mix emphasized—not a truth score.
Evidence, demonstrated adoption, hype gap, incentives, and confidence are assessed independently, each on its own current evidence. How these are measured.
Code shown, cost claim unmeasured
Implementation claims are unusually well evidenced for a single-source post: real snippets for route groups, generateStaticParams, safeLoad, the scraper, sitemap and JSON-LD. But everything is self-reported by the author with no independent verification, and the load-bearing economic claim carries no invocation counts, pricing tier or billing comparison, while its stated multiplier conflicts with the post's own per-request premise.
One self-reported hobby site
Adoption evidence is a single personal fan site the author says is live at duskbloods.net across nine locales. There are no other deployments, no traffic or request volumes, and no third party reporting use of the pattern.
Cost savings overstated
The framing that route groups are 'simpler, cheaper, better for SEO' is carried by an arithmetic error: middleware runs once per request, so locale count does not multiply invocations and the saving is one invocation per request rather than nine. Cost, SEO and cold-start benefits are all asserted without measurement, so the conclusions run ahead of the evidence, though the implementation patterns themselves are described accurately.
Self-promotional build log
The article is a first-person developer post promoting the author's own live site and preferred pattern, closing with the site URL and a call for comments. That is a mild, visible incentive to present the architecture favourably and to skip unflattering measurement; there is no disclosed vendor sponsorship or commercial relationship with Cloudflare, next-intl or OpenNext in the supplied material.
Confident on code, weak on economics
Confidence is high for what the code does, since the article shows it directly, and high for the arithmetic correction, which follows from the post's own stated premise. Confidence is low for the cost, SEO and cold-start benefits and for any generalization beyond this one site, because there is a single self-reported source and no measurements.
build
A build step instead of a backend: 1,025 records, 8 locales, no runtime API1 distinct publisher
build
At 70,000 images, next/image is not your optimizer, it is your outage1 distinct publisher
build
A $5-a-month monitoring SaaS on Workers, Turso and R2 is a cost datapoint, not a blueprint1 distinct publisher
product
Nintendo's next Switch 2 exclusive is an eight-player FromSoftware brawl, not a Mario game1 distinct publisher
Distinct publishers with included, body-backed reporting in this cluster.
dev.to
1 article · August 21, 2026