aboutsummaryrefslogtreecommitdiffstats
path: root/build.js
diff options
context:
space:
mode:
authorAdam Soltys <[email protected]>2026-06-24 08:59:31 -0700
committerAdam Soltys <[email protected]>2026-06-24 08:59:31 -0700
commit17acf5ec80a0c0b0acc44a6a8373e154c68d4748 (patch)
treea3fb56b85cc9cd21962ed64b084294096948d75b /build.js
parentf59cca61111f38963099190bdbbce9a76bc3fafe (diff)
downloadcoinosv3-17acf5ec80a0c0b0acc44a6a8373e154c68d4748.tar.gz
coinosv3-17acf5ec80a0c0b0acc44a6a8373e154c68d4748.zip
Split translations into per-language files, lazy-loaded
Only English is bundled in index.html now; the other 19 languages are emitted as dist/locales/<code>.json and fetched on demand when a visitor actually uses one (via auto-detect, the picker, or ?lang=xx). This drops index.html from ~511KB to ~318KB; a non-English visitor fetches just their one ~8-10KB locale, which the service worker then caches for offline use. t() falls back to English until a locale resolves. - i18n.js keeps only en + a loadLocale() fetcher; locale data moved to src/locales/*.json. - build.js emits dist/locales/; dev.js serves /locales/<code>.json. - Deploy hook publishes the locales/ subdir. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Diffstat (limited to 'build.js')
-rw-r--r--build.js25
1 files changed, 22 insertions, 3 deletions
diff --git a/build.js b/build.js
index e99b0a4..52b29af 100644
--- a/build.js
+++ b/build.js
@@ -9,7 +9,7 @@
// installed. index.html stays self-contained; these are optional extras that
// simply 404 (harmlessly) when the file is opened on its own from file://.
-import { mkdir } from 'node:fs/promises';
+import { mkdir, readdir } from 'node:fs/promises';
const FAVICON =
'data:image/svg+xml,' +
@@ -77,7 +77,21 @@ self.addEventListener('fetch', (e) => {
);
return;
}
- e.respondWith(caches.match(req).then((hit) => hit || fetch(req)));
+ // Cache-first, but cache same-origin assets (e.g. the chosen locale, icons)
+ // on first fetch so they're available offline without precaching them all.
+ e.respondWith(
+ caches.match(req).then(
+ (hit) =>
+ hit ||
+ fetch(req).then((res) => {
+ if (res.ok) {
+ const copy = res.clone();
+ caches.open(CACHE).then((c) => c.put(req, copy));
+ }
+ return res;
+ })
+ )
+ );
});
`;
@@ -148,6 +162,11 @@ if (import.meta.main) {
// browser without BarcodeDetector opens the scanner.
await Bun.write('dist/jsqr.js', await buildJsQr());
+ // Per-language strings — fetched on demand so visitors only download theirs.
+ await mkdir('dist/locales', { recursive: true });
+ const locales = await readdir('src/locales');
+ for (const f of locales) await Bun.write('dist/locales/' + f, Bun.file('src/locales/' + f));
+
// PWA sidecars.
await Bun.write('dist/manifest.webmanifest', JSON.stringify(MANIFEST, null, 2));
const version = Bun.hash(html).toString(36);
@@ -156,5 +175,5 @@ if (import.meta.main) {
const kb = (Buffer.byteLength(html) / 1024).toFixed(0);
console.log(`✓ dist/index.html written (${kb} KB) — open it offline, no server needed`);
- console.log(`✓ PWA: manifest.webmanifest, sw.js (cold-${version}), ${STATIC.length} icons, jsqr.js`);
+ console.log(`✓ PWA: manifest.webmanifest, sw.js (cold-${version}), ${STATIC.length} icons, jsqr.js, ${locales.length} locales`);
}