Skip to content
Better Analytics

See which language your readers actually use

Cookie-free content analytics built into the SDK you already ship: views by entry, language, model, and country — with a transport that never blocks a page.

Views by language · last 30 days

41,381

English18,420
Deutsch9,106
Türkçe6,733
日本語4,218
Español2,904

Top entries

shipping-i18n-without-a-tms5,201
icu-message-format-guide3,884
hreflang-for-subfolders2,470
live · no cookies, no fingerprint
A provider and a hook

useTrackView("content.view")

Only what you call

Safe during SSR and static builds — the tracker no-ops off the browser.

02 · Transport

sendBeacon

Fire-and-forget; survives navigation and tab close.

Write-only public key

bi_pub_…

Views by language · last 30 days

tr · content.view

Views by language · last 30 days

de · content.view

Country, not IP

DE · TR · JP

Top entries

entryId · contentModel

Better Analytics

live · no cookies, no fingerprint

01 · Two lines

A provider and a hook

Wrap the app once, then call the view hook on the pages you care about. Everything else — batching, retries, unload handling — is the SDK's problem.

  • Safe during SSR and static builds — the tracker no-ops off the browser.
  • Called outside the provider? It warns once and disables itself instead of crashing.
  • Invalid properties throw in development and are stripped in production.
app/blog/[slug]/page.tsx
import { useTrackView } from
  "@better-i18n/content/adapters/nextjs"

useTrackView("content.view", {
  entryId: post.id,
  contentModel: "blog",
  entrySlug: post.slug,
  language: post.locale,
})

content.view · tr · blog · 1 event

sent with sendBeacon · 0ms on the main thread

02 · Transport

Never block the page, never lose the event

Three transports are tried in order and every failure is swallowed. An analytics call can slow nothing down and can throw nothing at your app.

sendBeacon

Fire-and-forget; survives navigation and tab close.

on failure ↓

fetch(keepalive)

For bodies under 51.2 KB when beacon is unavailable.

on failure ↓

fetch

Last resort: a regular POST.

still failing → dropped silently

03 · What gets tracked

Explicit by default

The privacy posture is a design decision, not a setting you have to find: nothing is collected unless your code sends it.

Only what you call

No auto-pageview, no fingerprinting, no cookies. An event exists because your code asked for it.

Project context

Organization and project resolve from the API key, so payloads stay small.

Your own properties

entryId, contentModel, language, framework — any primitive you pass. Objects and arrays are rejected.

Country, not IP

The country code comes from Cloudflare's edge metadata; the IP is never stored.

04 · Key safety

A public key that cannot leak your data

The same threat model PostHog, Mixpanel, and Vercel Analytics use: the embedded key writes events and holds no read scope.

Write-only public key

A bi_pub_* key can send events and nothing else — it cannot read content or analytics.

Rate limited at the edge

Per-IP and per-project limits, plus WAF rules, so a leaked key can spam but not exfiltrate.

Scoped to one project

Keys are validated against a single projectId — cross-project reads are impossible.

First-party proxy

Rewrite the track endpoint through your own domain so ad blockers see only first-party requests.

FAQ

Questions teams ask before shipping it

We use Better I18N across our Next.js platform and mobile apps. One tool for every product — translations go live the moment we push, no redeploys, no friction.

Samet Selcuk

Founder, Hellospace

Get started

Find out which locales earn their translation budget

Install the SDK, track one view, and watch the language breakdown fill in — free while you evaluate.