Users were no longer sent to their preferred locale. `LocaleRedirector` was emptied in `115fa5aca` after its previous versions caused repeated redirects, and nothing replaced it.
**Cause**
- The earlier logic compared the best locale (a string) against the current locale entry (an object), so the check was always true on non-default locale pages
- That made the redirect fire repeatedly, which led to the logic being removed entirely
**Changes**
- Derive the current locale from the URL, treating the default locale as unprefixed to match how pages are generated
- Redirect only when the best locale differs from the current locale, so it stops after one hop
- Redirect users with a saved `preferred-locale` from any page
- Redirect users without a saved preference from default-locale URLs only, using their browser language, so shared locale links are respected
- Preserve the rest of the path when swapping the locale prefix
- Skip `/api` paths
PrimaryArmorSceneComponent rebuilt its ShaderMaterial every render with null uniforms that were only set once on mount. Create it once with useState instead.
Adds a **Peekaboo DPM** entry nested under DPM for single-shot guns, estimating damage per minute when only firing after resetting camouflage (or waiting out the reload, whichever is slower), assuming at least 10 seconds between shots.
- Shots per minute are capped at `60/10` for reloads faster than 10s, and fall back to `60/reload` otherwise
- The result is multiplied by the currently selected shell's damage and rounded
- An (i) icon next to the label opens a popover explaining the calculation
- The row is hidden for auto-loader/auto-reloader guns
- No percentile ranking bar is shown, since no dataset exists yet for this new stat
The /session/ page crashed when a tracked player's tank stats included
a tank with no matching tank definition or no average stats (WN8
baseline). The delta memo dereferenced `tank.id` on a possibly missing
definition and forced `average?.mu!` through as non-null, so a missing
average threw inside `compositeStats`/`calculateWN8` uncaught, breaking
the page on every load while the bad state persisted in localStorage.
Skip those entries instead of crashing: entries with no tank
definition or no average `mu` are dropped from the delta via
`flatMap`, so the rest of the session table still renders.