Skip to content

Migrating the application runtime to Palamedes v2

V2 uses compiled messages on server and client, including development. The package roots and existing compiled subpaths share one implementation. This is a coordinated major transition; publication remains held while the host integration slices and final release verification are completed under 1204. This guide covers the runtime changes in 1206; host-specific delivery migration will be completed with their integration slices before release readiness.

Compile catalogs before loading

A v1 application could pass ICU strings directly:

const i18n = createI18n();
i18n.load("de", { greeting: "Hallo {name}" });

A standalone/custom integration now loads the generated module:

import { createI18n } from "@palamedes/core";
import { messages } from "./generated/de.js";

const i18n = createI18n({ locale: "de" });
i18n.load("de", messages);

Generate that module through the existing catalog compiler, CLI or framework plugin. defineCompiledCatalog() marks compiled constants/functions; it does not compile an ICU string map. The standard framework path delegates loading to its adapter, while locale selection remains application policy.

Author rich text and choices with macros

Move direct ICU/choice components to the authoring entrypoint so extraction and compilation can discover them. For example:

// v1: interpreted choice props during rendering
import { Plural } from "@palamedes/react";
// v2: the framework plugin transforms this into a compiled message lookup
import { Plural, Trans } from "@palamedes/react/macro";

export function Inbox({ count }: { count: number }) {
  return (
    <>
      <Plural value={count} one="# message" other="# messages" />
      <Trans>
        Hello <strong>friend</strong>
      </Trans>
    </>
  );
}

Use the corresponding @palamedes/solid/macro or @palamedes/remix/macro entrypoint for those renderers. Runtime Trans remains the compiler target; hand-authored raw ICU in its message prop is not a runtime fallback. Dynamic values remain supported, but message text and choices must be extractable rather than constructed during rendering.

Replace removed APIs

V1 API or optionV2 replacement
@palamedes/core/compiled and framework compiled importsCompatible aliases; root imports are also parser-free.
parseMessagePattern(), formatMessagePattern(), parsePattern()Compile authoring catalogs before application execution; use macros for messages. No optional runtime parser mode exists.
getMessage(), getMessageNodes(), parsed-node types, resolveChoice()Use _() for text or renderMessage() with the compiled renderer ABI. Use catalog inspection/audit tooling for authoring diagnostics.
buildChoiceMessage() and direct runtime Plural, Select, SelectOrdinalUse the corresponding macro entrypoint and framework transform.
reportError() and ReportedMessageErrorLet renderMessage() propagate errors; observe through onError.
MessageMetadata.reportMissing, renderUncompiledPatternRemoved; metadata only contains diagnostic source identity.
keepSourceFallbacksRetained legacy spelling for including diagnostic source metadata. Neither value changes runtime failure behavior. false omits that metadata.
failOnCompileErrorRemove it. Invalid/unsupported ICU always fails compilation; failOnMissing remains the separate translation-completeness policy.
CompiledMessageRuntime.pattern()Regenerate v1 catalog artifacts. The v2 ABI contains executable operations only.

The existing CatalogMessages string-map type can describe build-time data; load() accepts only CompiledCatalogMessages. CompiledPalamedesI18n aliases PalamedesI18n; custom runtimes must provide the required renderMessage() capability. No parsed-node fallback is used for older custom instances.

Handle failures through the host

Valid missing translations are resolved at compilation through the fallback locale chain and ultimately the source message. Variables, rich text and choices in that compiled fallback behave like translations. failOnMissing can require a translation in the requested locale anyway.

Missing runtime entries are different: _() and Trans throw MissingCompiledMessageError. Formatter execution errors also throw. Put ordinary host error handling around the affected route or subtree and render an error view that does not depend on the failed catalog. Do not display raw exceptions or diagnostic IDs. Server callers should fail the operation rather than return partially translated success.

onMissing receives { id, locale, metadata }; onError receives those fields plus error. They observe failures; returning or throwing from these hooks cannot convert them into successful message rendering. The removed pattern/fallback error fields no longer imply replacement output.

Set the same timeZone on server and client. Missing required message values, invalid numeric/date formatter inputs and malformed compiled branch tables propagate failures. Compiled literal strings, including empty strings, remain valid and are never interpreted as ICU.

Validate runtime packages with pnpm check:runtime after pnpm build, and run the relevant framework tests after migrating the application's authoring and catalog setup. Initial loading, hydration and later navigation failure recovery remain part of each host integration's release proof.