Add progressive web app companion for cross-platform access

Vite + TypeScript PWA that mirrors the Android app's core features:
- Pre-processed shelter data (build-time UTM33N→WGS84 conversion)
- Leaflet map with shelter markers, user location, and offline tiles
- Canvas compass arrow (ported from DirectionArrowView.kt)
- IndexedDB shelter cache with 7-day staleness check
- Service worker with CacheFirst tiles and precached app shell
- i18n for en, nb, nn (ported from Android strings.xml)
- iOS/Android compass handling with low-pass filter
- Respects user map interaction (no auto-snap on pan/zoom)
- Build revision cache-breaker for reliable SW updates

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Ole-Morten Duesund 2026-03-08 17:41:38 +01:00
commit e8428de775
12051 changed files with 1799735 additions and 0 deletions

49
pwa/src/i18n/i18n.ts Normal file
View file

@ -0,0 +1,49 @@
/**
* Minimal i18n system: detects locale from navigator.languages,
* provides t(key, ...args) for string lookup with %d/%s substitution.
*/
import { en } from './en';
import { nb } from './nb';
import { nn } from './nn';
const locales: Record<string, Record<string, string>> = { en, nb, nn };
let currentLocale = 'en';
/** Detect and set locale from browser preferences. */
export function initLocale(): void {
const langs = navigator.languages ?? [navigator.language];
for (const lang of langs) {
const code = lang.toLowerCase().split('-')[0];
if (code in locales) {
currentLocale = code;
return;
}
// nb and nn both start with "n" — also match "no" as Bokmål
if (code === 'no') {
currentLocale = 'nb';
return;
}
}
}
/** Get current locale code. */
export function getLocale(): string {
return currentLocale;
}
/**
* Translate a string key, substituting %d and %s with provided arguments.
* Falls back to English, then to the raw key.
*/
export function t(key: string, ...args: (string | number)[]): string {
let str = locales[currentLocale]?.[key] ?? locales.en[key] ?? key;
// Replace %d and %s placeholders sequentially
for (const arg of args) {
str = str.replace(/%[ds]/, String(arg));
}
return str;
}