Gå til hovedinnhold

In-context review

Review mode lets you check and fix translations on the running app: hold ALT, click a string and edit its translation in place. The fix goes into the .kbf.json file the string came from.

A translator working from a file cannot see that "Save" landed on a button 40 pixels wide, that the German for "Cancel" wraps to two lines, or that the tone is wrong for a dialog that deletes something. On the running app, you can.

Turn it on​

vite.config.ts
neokapi({
review: true, // or set KAPI_REVIEW=1
});

Start the dev server. There is nothing to import and no component to add.

Dev and staging only

Review mode adds review data to your markup and lets the browser write to your translation files. Never enable it for a production build you ship.

Review a string​

Hold ALT, and hovering outlines every translatable element. ALT+click one to open its panel:

  • Source: the original text, with its links, bold spans, and placeholders shown the way a translator sees them.
  • Target: the current translation, editable.
  • Note: the context the developer left, if any (data-i18n-note, or t(text, context)).
  • Findings: terminology and check findings against this string.

Type a fix, save, and the app repaints immediately with the new text.

The fix is written into the .kbf.json file the string came from. It shows up as a line in git diff, goes through pull-request review like any other change, and the next neokapi-i18n compile includes it. A reviewer's edit and a translator's edit land in the same file.

Head and SEO strings​

The page title, meta description, and Open Graph / Twitter card strings carry no rendered text node, so there is nothing on the page to hold ALT and click, yet they are often the strings a search result or a shared link shows first. Translate them through the head hooks and they become reviewable in a panel of their own.

import {
useTranslatedTitle,
useTranslatedDescription,
useTranslatedMeta,
} from "@neokapi/i18n-react/head";

function Head() {
useTranslatedTitle("Dashboard · Acme");
useTranslatedDescription("Manage your team, projects, and billing.");
useTranslatedMeta("Dashboard · Acme", { property: "og:title" });
return null;
}

Each hook translates its source through the runtime dictionary (the same lookup t() uses), sets the real head element, and registers the string for review. Only strings translated through these hooks appear in review, and other head markup is left alone.

A head/SEO button appears in the review toolbar whenever a page has such strings. It opens a panel that lists each string with its slot (title, description, og:title, …), its source and its current target, with the same editable field and save as the inline panel. An edit writes back through the same .kbf.json file and repaints the head in place. When a page registers no head strings, the button stays hidden.

Head translation resolves through the runtime dictionary, so it applies in OTA / runtime mode. Slots with identical wording, such as a <title> and its og:title, share one translation.

See terminology and check findings on the page​

Run kapi's checks against the same i18n/ directory and their findings appear on the rendered text:

kapi exec qa i18n/ --target-lang de
kapi exec term-check i18n/ --target-lang de --termstore terms/de.db

An approved term that wasn't used, a placeholder that went missing in translation, a string that runs 60% longer than its source and is about to break the button it lives in: each is marked on the words it concerns. Re-run a check while the app is open, and the marks update without a restart.

Review on statically-rendered pages​

The review identifier, data-kapi-id, is a content hash: it is the same whether the build inlines the translation (zero-runtime SSG/SSR) or emits a runtime t() call. Review therefore works in either mode, including on a fully static page with no app i18n runtime.

Turn review: true on for an inline (or SSG/SSR) build, and the plugin does two things in either mode. It stamps data-kapi-id into the baked HTML, and it emits translations/review.json, a read-only manifest of source, target and location for every string on the page. A hosted overlay needs nothing more:

import { initKapiReviewHosted } from "@neokapi/i18n-react/review/hosted";

initKapiReviewHosted(); // read-only; fetches translations/review.json

With only a static deploy (no dev server, no backend), a reviewer can deep-link to a string (?kapi-focus=<hash>), open the whole-page index (?kapi-review), and inspect any string's source and target in context.

To let a reviewer edit on that static page, pass edit: true and an endpoint that speaks the review-store protocol (a staging deployment of the dev middleware, or a proxied review backend):

initKapiReviewHosted({ edit: true, endpoint: "https://staging.example.com/review" });

Saving PUTs to {endpoint}/{hash} (the same write-back a body string uses) and repaints the element's text in place, independent of any app i18n runtime. Repaint is text-only: a string with inline markup (a link, a bold span) or an ICU plural saves to the store but is not repainted live; a reload picks up the real rendering. For a manifest that also carries every locale's target and the terminology and check findings, generate it over the whole KBF tree instead:

neokapi-i18n compile i18n --out public/translations --review

Reviewers without a checkout​

Review reads and writes through an endpoint. The local endpoint works on the .kbf.json files in your repository and needs no infrastructure. Point review at an endpoint on a staging deployment, and a reviewer with no repository and no toolchain can review the app in place:

initKapiReview({ endpoint: "https://staging.example.com/review" });
  • Translating with kapi: where the terminology and check findings come from.
  • Configuration: the review and reviewKbfDir options.
  • S-05: the architecture of the i18n runtime and the review overlay.