Services

Localization

Request-scoped locale resolution and Effect HttpApi middleware for Paraglide applications.

Install
Add this registry item to your app with shadcn.
bash
bunx --bun shadcn@latest add https://krakstack.net/r/localization.json

Overview

localization provides request-scoped locale context for Effect HttpApi applications using Paraglide. It resolves the locale from the locale URL query parameter, the locale request header, the locale cookie, or Accept-Language, in that order, then provides that context to downstream handlers through LocaleMiddleware. All explicit locale values are validated with Effect Schema and cookies are parsed with Effect's HTTP cookie utilities. The localize helper selects a record translation using the current locale and fallback settings.

Included Files

  • lib/localization.ts - LocaleContext, LocaleMiddleware, LocaleMiddlewareLive, LocalizedInputType, and localize

Requirements

The receiving project must use Paraglide and expose its generated runtime at @/paraglide/runtime. Supported locales are derived from the receiving project's Paraglide configuration.

API Setup

Register the live middleware layer in the application HTTP API layer:

typescript
import { Layer } from "effect"; import { LocaleMiddlewareLive } from "@/lib/localization"; const apiLayer = Layer.mergeAll(/* application layers */).pipe( Layer.provide(LocaleMiddlewareLive), );

Apply LocaleMiddleware to an API group and read LocaleContext in endpoint handlers:

typescript
import { Effect } from "effect"; import { HttpApiGroup } from "effect/unstable/httpapi"; import { LocaleContext, LocaleMiddleware } from "@/lib/localization"; const RecordsGroup = HttpApiGroup.make("records").middleware(LocaleMiddleware); const program = Effect.gen(function* () { const { locale, fallbackLocale } = yield* LocaleContext; // Use locale and fallbackLocale to resolve localized data. });

Translation Resolution

localize expects a record with a translations array whose items have a locale field. It returns the matching translation merged with the base record, falls back to fallbackLocale or the first translation, and respects fallbackLocale: "none" when a translation must exactly match.

typescript
import { localize, type LocalizedInputType } from "@/lib/localization"; const context: LocalizedInputType = { locale: "fr", fallbackLocale: "en" }; const record = localize(context, { id: "welcome", translations: [ { locale: "en", name: "Welcome" }, { locale: "fr", name: "Bienvenue" }, ], });
Dependencies
Packages and shadcn components required by this registry item.