# Linguana docs
> Set up Linguana with code or an AI coding agent, then translate, review, and publish your app's text from the dashboard.
## Migrate from @linguanahq/compiler
URL: https://docs.linguana.dev/docs/advanced/migrate-compiler
Move an existing React or TanStack Start integration to the multi-framework Vite package.
# Migrate from `@linguanahq/compiler`
Replace the build dependency:
```sh
bun remove @linguanahq/compiler
bun add -d @linguanahq/vite
```
Then select the React adapter explicitly:
```ts
import { linguana } from "@linguanahq/vite";
import { react as linguanaReact } from "@linguanahq/vite/react";
linguana({
adapter: linguanaReact(),
projectId: "your-project",
token: process.env.LINGUANA_PROJECT_TOKEN,
});
```
`environment` now defaults to `development`, `sourceLocale` defaults to `en`, and a supplied token enables manifest upload unless `upload: false` is set. For build-driven translation, replace `targetLocales` with `enabled: true`; the dashboard’s enabled Languages list is authoritative.
The compatibility package remains available for one release and emits one deprecation warning per build.
---
## Automatic extraction
URL: https://docs.linguana.dev/docs/advanced/testing/automatic-text-discovery
Learn which JSX text and attributes become stable manifest messages during a build.
# Automatic text discovery
> Advanced reference for Linguana contributors and beta evaluators.
The Vite plugin parses JSX and TSX, transforms eligible content into runtime lookups, and writes source metadata to `.linguana/manifest.json`. It never writes back to input files.
## Eligible copy
```tsx
export function Checkout() {
return ;
}
```
Literal JSX children are extracted. Literal `title`, `placeholder`, `aria-label`, `alt`, `label`, and button `value` attributes are allowed. Every record includes repository-relative location, source, normalized text, placeholders, context, content hash, confidence, and stable ID.
Whitespace-only formatting changes preserve identity. Changing semantic context or an explicit ID can produce a new identity.
## Build and inspect
```bash
bun run build
bunx biome format .linguana/manifest.json
```
Messages are sorted by ID, locations point to their JSX source, and source files remain byte-for-byte unchanged.
## Verify source integrity
Use the repository’s guarded check around a build:
```bash
bun run source:before
bun run build
bun run source:after
```
## Common failures
If expected copy is absent, confirm it is literal, visible, inside included files, and not in an excluded subtree. Dynamic or rich content should use [dynamic text and links](/docs/build-with-code/dynamic-text-and-links); do not weaken exclusions globally.
## Next
Read [Keep product text and code separate](/docs/build-with-code/text-and-code), then use the [compiler reference](/docs/reference/compiler).
---
## Test text discovery
URL: https://docs.linguana.dev/docs/advanced/testing/extraction
Verify manifests, strict diagnostics, exclusions, and source integrity without paid translation.
# Test text discovery
> For Linguana contributors and beta evaluators.
Extraction does not require a token or provider.
## Configure a free build
Omit `token` and `translation`, and keep `upload: false`. Then run source guards around the build:
```bash
bun run source:before
bun run build
bun run source:after
```
## Inspect the artifact
Open `.linguana/manifest.json` and verify schema version, project, environment, source locale, source revision, messages, diagnostics, and locations. Run the build twice; the hash and stable IDs should not change without semantic copy changes.
## Test a policy corpus
Add fixtures for literal text, allowed attributes, `data-no-translate`, `data-translate`, hidden values, URL-like strings, ICU placeholders, rich content, and conflicting explicit IDs. Assert eligible messages appear and excluded content does not.
## CI strictness
Start with an object strict policy so duplicate IDs fail while expected conservative skips stay visible. Promote additional categories only after the current diagnostic set is clean.
The build writes a deterministic manifest, reports actionable diagnostics, performs no network request, and changes no tracked source.
## Failure and next
If a build attempts upload, check both `upload` and `translation`; either remote mode requires API URL and token. See [Build diagnostics](/docs/troubleshooting/build-diagnostics).
---
## Live-provider testing
URL: https://docs.linguana.dev/docs/advanced/testing/live-provider
Run a guarded paid OpenRouter translation and verify review, publishing, and memory reuse.
# Test OpenRouter translation
> For Linguana contributors and beta evaluators. This workflow makes paid translation-service calls.
> This workflow makes paid provider calls. Confirm the account has credit and the organization cap permits the estimate before starting it.
## Configure
Set these server-only values in `apps/server/.env`:
```dotenv
OPENROUTER_API_KEY=your-key
OPENROUTER_MODEL=qwen/qwen3.8-flash
TRANSLATION_PROVIDER=openrouter
```
## Start the isolated stack
```bash
bun run example:openrouter:up
```
Open `http://localhost:3102/jobs?project=project_phase1_demo`, sign in with the local test account documented in the repository’s testing fixtures, choose French, Amharic, or Afaan Oromo, and select **Translate latest build**.
Follow the job to `SUCCEEDED` or `REVIEW_REQUIRED`. Resolve any review items, open **Operations**, choose **Publish ready catalog**, then open `http://localhost:3110` and select the language.
## Verify reuse
Repeat the unchanged job. Exact safe translation-memory matches should avoid another provider call and variable charge.
## Stop
```bash
bun run docker:down
```
The repository script detects either the Docker Compose plugin or standalone command.
---
## Local demo
URL: https://docs.linguana.dev/docs/advanced/testing/local-demo
Operate and inspect the disposable Docker-backed Linguana demonstration stack.
# Test the local demo
> For Linguana contributors and beta evaluators. This is not the application installation guide.
## Start
Copy `.env.example` to `.env`, provide `OPENROUTER_API_KEY`, then run:
```bash
bun run demo:up
```
The command builds the API, dashboard, PostgreSQL, MinIO, Inngest, and TanStack example; deploys migrations; seeds idempotently; and waits for the example on `http://localhost:3010`.
## Service map
| Service | URL |
| --- | --- |
| API | `http://localhost:3000` |
| Landing | `http://localhost:3001` |
| Dashboard | `http://localhost:3002` |
| Admin | `http://localhost:3003` |
| Example | `http://localhost:3010` |
| Inngest | `http://localhost:8288` |
| MinIO console | `http://localhost:9001` |
## Exercise the loop
Open the example, switch to French, edit literal example copy, and run `bun run demo:up` again. The changed build should translate only new content while reusing prior exact matches.
## Diagnose and clean up
```bash
bun run demo:logs
bun run docker:down
```
The example renders French without a locale URL, source files remain unchanged, and production is not automatically published.
If startup fails, inspect the earliest unhealthy dependency rather than repeatedly rebuilding. Continue with [Live-provider testing](/docs/advanced/testing/live-provider) for the retained interactive stack.
---
## Advanced testing
URL: https://docs.linguana.dev/docs/advanced/testing
Repository and provider workflows for Linguana contributors and beta evaluators.
# Advanced testing
These guides cover the Linguana repository test stack. They are not required to add Linguana to your application.
- [Test text discovery](/docs/advanced/testing/extraction)
- [Run the local demo](/docs/advanced/testing/local-demo)
- [Test the live translation service](/docs/advanced/testing/live-provider)
For application setup, use [Connect hosted translations](/docs/getting-started/connect-your-app).
---
## Automate translations in CI
URL: https://docs.linguana.dev/docs/automate/ci-cd
Keep pull requests fast, update development languages, and keep production releases deliberate.
# Automate translations in CI
Keep pull requests fast, let development builds update translations, and keep production releases deliberate.
```text
Pull request
Check the app and find new text
Development build
Send new text for translation
Production release
Review and publish the selected language
```
## GitHub Actions example
Pull requests do not need a project token. The development job runs only on `main` and receives its token from a protected CI environment.
```yaml title=".github/workflows/linguana.yml"
name: Linguana
on:
pull_request:
push:
branches: [main]
jobs:
validate:
runs-on: ubuntu-latest
timeout-minutes: 15
env:
LINGUANA_UPLOAD: "false"
LINGUANA_TRANSLATE: "false"
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: oven-sh/setup-bun@v2
with:
bun-version: 1.3.14
- run: bun install --frozen-lockfile
- run: bun run check-types
- run: bun run docs:verify
- name: Snapshot source
run: bun run source:before
- name: Extraction-only build
run: bun run build
- name: Verify source integrity
run: bun run source:after
- uses: actions/upload-artifact@v4
with:
name: linguana-manifest
path: .linguana/manifest.json
if-no-files-found: error
retention-days: 14
translate-development:
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
needs: validate
runs-on: ubuntu-latest
timeout-minutes: 15
environment: linguana-development
env:
LINGUANA_API_URL: ${{ vars.LINGUANA_API_URL }}
LINGUANA_PROJECT_TOKEN: ${{ secrets.LINGUANA_PROJECT_TOKEN }}
LINGUANA_UPLOAD: "true"
LINGUANA_TRANSLATE: "true"
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: oven-sh/setup-bun@v2
with:
bun-version: 1.3.14
- run: bun install --frozen-lockfile
- name: Upload and translate development catalog
run: bun run build
```
Configure the plugin with an explicit charge and wait ceiling:
```ts title="vite.config.ts"
translation: process.env.LINGUANA_PROJECT_TOKEN &&
process.env.LINGUANA_TRANSLATE !== "false"
? {
enabled: true,
maxChargeMicros: 100_000,
waitTimeoutMs: 300_000,
publish: "development",
}
: undefined,
```
## Other CI providers
Use the same stage boundaries:
```bash title="pull request validation"
export LINGUANA_UPLOAD=false
export LINGUANA_TRANSLATE=false
bun install --frozen-lockfile
bun run check-types
bun run docs:verify
bun run source:before
bun run build
bun run source:after
```
```bash title="authorized development build"
test -n "$LINGUANA_PROJECT_TOKEN"
export LINGUANA_UPLOAD=true
export LINGUANA_TRANSLATE=true
bun run build
```
## Keep credentials private
| Value | Store | Browser-safe? |
| --- | --- | --- |
| `LINGUANA_PROJECT_TOKEN` | Protected CI environment secret | No |
| Provider API key | Translation service secret store | No |
| `VITE_LINGUANA_API_URL` | Build/public variable | Yes |
| Publishable catalog key | Public runtime configuration | Yes |
Do not print environment values, authorization headers, source bodies, or translated bodies. Retain the manifest artifact, manifest hash, job IDs, request IDs, and redacted logs.
## If something fails
Keep the error code, translation-run ID, and request ID from the build logs. Check an existing run before retrying so you do not start the same translation twice.
The build integration updates development languages only. Production remains an explicit dashboard action after review.
Pull requests perform no authenticated translation work. Merged development builds may update development languages, while production requires review and an explicit release.
## Next
Test the setup with [Test your integration](/docs/automate/test-integration), then complete the [production checklist](/docs/automate/production-checklist).
---
## Automate
URL: https://docs.linguana.dev/docs/automate
Add Linguana to CI, test your integration, and prepare for production.
# Automate
Keep pull requests fast, let development builds update translations, and keep production releases deliberate.
```text
Pull request
Check the app and find new text
Development build
Send new text for translation
Production release
Review and publish the selected language
```
## Guides
- [Automate translations in CI](/docs/automate/ci-cd)
- [Test your integration](/docs/automate/test-integration)
- [Production checklist](/docs/automate/production-checklist)
Contributor test workflows are under [Advanced testing](/docs/advanced/testing).
---
## Production checklist
URL: https://docs.linguana.dev/docs/automate/production-checklist
Make sure your app, credentials, languages, and release process are ready for production.
# Production checklist
Use this checklist before making a translated language available to customers.
## Build and credentials
- [ ] Production `projectId`, `environment`, and API origin match the dashboard.
- [ ] Project token is available only during server/build execution.
- [ ] Runtime uses a publishable key, never the project token.
- [ ] Translation has explicit target locales, charge ceiling, timeout, and development-only build publish.
- [ ] CI retains IDs and redacted diagnostics, not credentials or source bodies.
## Runtime resilience
- [ ] An embedded original-language file ships with the application.
- [ ] Remote catalogs pass schema, integrity, and scope verification.
- [ ] Missing, invalid, and failed-load paths render source fallback.
- [ ] Server and client begin with the same hydration state.
- [ ] Locale cookie, signed-in preference, and routing precedence are tested.
## Operations
- [ ] Production release policy is explicit and authorized.
- [ ] Review queue is clear for the target version.
- [ ] Monthly hard cap and estimate acknowledgement are understood.
- [ ] A previous catalog has been restored in a rollback rehearsal.
- [ ] Published language loading is monitored separately from translation work.
## Privacy and observability
- [ ] Logs redact source text, translations, authorization headers, provider keys, and payment data.
- [ ] Diagnostic callbacks record only codes, language, message IDs, and request IDs.
- [ ] Provider data use and retention are approved for production content.
Disabling the language endpoint still leaves the production application readable in its original language.
## Next
Keep [Catalog runtime troubleshooting](/docs/troubleshooting/catalog-runtime) and [Publish a language](/docs/use-linguana/publish-language) in the launch runbook.
---
## Test your integration
URL: https://docs.linguana.dev/docs/automate/test-integration
Check language switching, original-language fallback, remote loading, and server rendering.
# Test your integration
## Required scenarios
1. Render the source locale with only the embedded source catalog.
2. Switch to a loaded target catalog and verify values and rich component slots.
3. Request a missing message and assert source fallback plus one `missing-translation` diagnostic.
4. Provide invalid ICU content and assert source fallback plus `invalid-translation`.
5. Reject remote loading and assert the current/embedded source remains visible plus `catalog-load-failed`.
6. Change locale twice quickly and verify the earlier request is aborted.
7. Verify the `linguana_locale` cookie and optional preference callback.
## SSR precedence
Create requests where override, signed-in preference, cookie, and `Accept-Language` disagree. Assert the first supported match wins in that order, including language-only matching.
## Example diagnostic assertion
```tsx
const diagnostics: DiagnosticEvent[] = [];
render(
diagnostics.push(event)}>
,
);
expect(diagnostics[0]?.code).toBe("missing-translation");
```
Every failure leaves readable content on screen. Diagnostics are sampled by code, locale, and message so repeated renders do not flood telemetry.
## Next
Use [Catalog runtime troubleshooting](/docs/troubleshooting/catalog-runtime) when a browser test fails, then add the checks to [CI](/docs/automate/ci-cd).
---
## Translate dynamic text and links
URL: https://docs.linguana.dev/docs/build-with-code/dynamic-text-and-links
Use simple patterns for values, numbers, links, custom labels, and content that should stay unchanged.
# Translate dynamic text and links
Most text can be found automatically. Use these patterns when the text depends on data or contains a link, emphasis, or another component.
## Which pattern should I use?
| What are you translating? | Use |
| --- | --- |
| Text directly in JSX | Nothing extra |
| Text with a value or number | `t()` |
| Text with a link or emphasis | `` |
| A custom label prop | `data-translate` |
| Code or an identifier | `data-no-translate` |
## The text contains a value
Call `t(source, values?, options?)` from `useTranslation()`. The source must be a string literal for the compiler to extract it.
```tsx title="src/search-results.tsx"
import { useTranslation } from "@linguanahq/react";
export function SearchResults({ count }: { count: number }) {
const { t } = useTranslation();
return (
{t(
"{count, plural, one {# result} other {# results}}",
{ count },
{ id: "search.results", context: "Search result count" },
)}
);
}
```
Linguana keeps the variable in place when the message is translated.
## The text changes based on a number
```tsx
t(
"{count, plural, one {# item} other {# items}}",
{ count },
)
```
Plural rules follow the selected language.
## The text contains a link
Your application still owns the destination and behavior. The translation controls only where the linked words appear.
```tsx title="src/checkout-consent.tsx"
import { T } from "@linguanahq/react";
export function CheckoutConsent() {
return (
,
}}
/>
);
}
```
Translators can move the `` slot, but the application still owns the destination and component behavior.
## A custom component has a user-facing label
The compiler automatically handles `title`, `placeholder`, `aria-label`, `alt`, `label`, and the literal `value` of a button. Opt one additional literal attribute in by name when you do not control the component API:
```tsx title="src/empty-state.tsx"
```
Use a space- or comma-separated list for multiple label props.
## The content should not be translated
```tsx title="src/product-code.tsx"
Linguanasku_checkout_v2
```
Use this for code samples, identifiers, or content handled by another system.
## What Linguana already leaves alone
Linguana leaves IDs, class names, URLs, event handlers, analytics values, and test identifiers alone.
## Next
Review [Keep product text and code separate](/docs/build-with-code/text-and-code), then [show translated content](/docs/build-with-code/show-translated-content).
---
## Build with code
URL: https://docs.linguana.dev/docs/build-with-code
Configure runtime delivery, dynamic messages, exclusions, and server rendering.
# Build with code
Keep static user-facing text in your components. The build plugin extracts it, and the framework runtime renders translations with the original source as fallback.
## Choose the integration task
- [Show translated content](/docs/build-with-code/show-translated-content): provider setup, hosted catalogs, and language switching.
- [Dynamic text and links](/docs/build-with-code/dynamic-text-and-links): values, ICU plurals, rich slots, and custom label props.
- [Keep product text and code separate](/docs/build-with-code/text-and-code): exclusions and the compiler's extraction boundaries.
- [Server rendering](/docs/build-with-code/server-rendering): resolve the language per request and share hydration state.
These guides use React examples. For Vue, Svelte, or Solid, start with [framework setup](/docs/getting-started/frameworks) and the [matching runtime reference](/docs/reference). TanStack Start uses the [request-aware React setup](/docs/getting-started/tanstack-start).
## Keep the scopes aligned
The build plugin, runtime provider, and published catalog must agree on project, environment, and locale. Build and translation APIs use a secret project token. Browser catalog delivery uses a publishable key.
Static text appears in the manifest without new translation keys. Dynamic messages preserve their variables and rich slots. A missing catalog leaves the source text readable.
[Show translated content](/docs/build-with-code/show-translated-content), or [install the SDK](/docs/getting-started/install) if you have not connected the build yet.
---
## Server rendering
URL: https://docs.linguana.dev/docs/build-with-code/server-rendering
Resolve request locale and hydrate React with the same verified catalog state.
# Configure server rendering
Import server helpers from `@linguanahq/react/ssr`. Resolve once per request, load only a verified catalog, and pass the resulting hydration state unchanged to the browser.
## Locale precedence
| Priority | Source | Typical owner |
| --- | --- | --- |
| 1 | Explicit `override` | Application route policy |
| 2 | `userPreference` | Signed-in account |
| 3 | `linguana_locale` cookie | Prior language selection |
| 4 | Weighted `Accept-Language` | Browser |
| 5 | `defaultLocale` | Project/application |
Exact locale matches win before language-only matches, so `fr-CA` may match configured `fr`.
```ts title="server/locale.ts"
import { resolveRequestLocale } from "@linguanahq/react/ssr";
const locale = resolveRequestLocale({
request,
override: routeLocale,
userPreference: account?.locale,
supportedLocales: ["en", "fr", "am"],
defaultLocale: "en",
});
```
## Load and serialize
Attempt target catalog loading on the server. If it fails, return source locale and the embedded catalog instead of claiming the unavailable target.
```ts
const requested = resolveRequestLocale({
request,
supportedLocales: ["en", "fr"],
defaultLocale: "en",
});
const target = requested === "en"
? undefined
: await loadCatalog({
endpoint: process.env.LINGUANA_API_URL!,
projectId,
environment,
locale: requested,
publishableKey: process.env.LINGUANA_PUBLISHABLE_KEY!,
}).catch(() => undefined);
const hydrationState = createRequestHydrationState(
target ? requested : "en",
target ? [embeddedEnglish, target] : [embeddedEnglish],
);
```
Pass `hydrationState` to `LocaleProvider`. It contains schema version `1`, the resolved locale, and validated catalogs.
## Cookie and route ownership
The default cookie is first-party, path-wide, and `SameSite=Lax`. Linguana does not redirect or add locale path segments. If the application owns localized routes, pass that locale as `override` and keep URL generation in the router.
Create requests where override, account preference, cookie, and `Accept-Language` disagree. Assert the first supported value wins, then hydrate without a text mismatch.
## Next
Use the [SSR API reference](/docs/reference/ssr) and add the precedence cases to [integration testing](/docs/automate/test-integration).
---
## Show translated content
URL: https://docs.linguana.dev/docs/build-with-code/show-translated-content
Make translated text available throughout your application with LocaleProvider, t(), and T.
# Show translated content in your app
Add `LocaleProvider` once, then use `t()` or `` wherever your application displays text.
## Add the provider
`LocaleProvider` makes translations available to every component below it:
```tsx title="src/locale-provider.tsx"
import { LocaleProvider } from "@linguanahq/react";
import { embeddedEnglish } from "./catalogs/en";
export function AppLocaleProvider({ children }: { children: React.ReactNode }) {
return (
{children}
);
}
```
## Translate text with a value
```tsx title="src/greeting.tsx"
function Greeting({ name }: { name: string }) {
const { t } = useTranslation();
return
{t("Welcome, {name}", { name })}
;
}
```
Use `t()` for simple text and values. Use `` when the message contains a link or another component. See [Translate dynamic text and links](/docs/build-with-code/dynamic-text-and-links).
## Add a language selector
`LanguageSelector` is ready to use. `useLocaleOptions()` gives you the same behavior when you want to build your own UI.
```tsx
function LocaleMenu() {
const { locale, options, ready, setLocale } = useLocaleOptions();
return (
);
}
```
When a user chooses a language, Linguana loads the published language file and remembers the preference.
## Load remote languages
Add a browser-safe endpoint and publishable key when your app should load translated language files remotely:
```tsx
```
The publishable key may reach the browser. The project token may not.
## If a language is unavailable
Your original language remains visible. Advanced diagnostics and preference synchronization are covered in the [React API reference](/docs/reference/react) and [Catalog runtime troubleshooting](/docs/troubleshooting/catalog-runtime).
## Next
Add [server rendering](/docs/build-with-code/server-rendering) if your app renders on the server, then [choose your languages](/docs/use-linguana/choose-languages).
---
## Keep product text and code separate
URL: https://docs.linguana.dev/docs/build-with-code/text-and-code
Translate words your users read while leaving code, URLs, identifiers, and hidden content alone.
# Keep product text and code separate
Linguana translates words your users read. It leaves code, identifiers, URLs, analytics values, and hidden content alone.
## Leave code unchanged
```tsx
checkout_v2
```
Use `data-no-translate` for a complete subtree that should stay unchanged.
## What is left alone automatically
- `script`, `style`, `code`, and `pre` subtrees.
- `hidden`, `aria-hidden="true"`, and hidden inputs.
- Any subtree marked `data-no-translate`.
- IDs, class names, names, roles, URLs, sources, actions, methods, test IDs, analytics values, and event attributes.
- URL-like and machine-readable text patterns.
```tsx
npm install example
```
## Translate a custom label
Unknown literal attributes can be opted in by name:
```tsx
```
Prefer a normal user-facing prop or `t()` when you control the component.
## Text with links or components
Use `` for text that contains a link, emphasis, or another component. See [Translate dynamic text and links](/docs/build-with-code/dynamic-text-and-links).
## Verify
Run your normal build and check that product text appears while code and identifiers stay out of `.linguana/manifest.json`.
## Failure and next
If expected text is missing, use [Build diagnostics](/docs/troubleshooting/build-diagnostics). For focused extraction tests, see [Advanced testing](/docs/advanced/testing/extraction).
---
## Set up with an AI agent
URL: https://docs.linguana.dev/docs/getting-started/ai-agent-setup
Give your coding agent a precise setup prompt, then manage translations in the dashboard.
# Set up with an AI agent
Let the coding agent you use in your repository install Linguana and connect your build. You can then choose languages, edit translations, and publish from the dashboard.
## 1. Gather the project details
1. Sign in to the dashboard and [create a project](/docs/use-linguana/create-project), or ask an owner or admin to create it.
2. Copy the project ID, API URL, and browser publishable key from **Integrations**. Its templates name the browser values `VITE_LINGUANA_API` and `VITE_LINGUANA_KEY`; your agent may use the longer names in the docs, provided the stored values and runtime reads agree.
3. Ask an owner or admin to create a development token in **Tokens**. Store it in an ignored local environment file or CI secret as `LINGUANA_PROJECT_TOKEN`.
4. Choose a first target language in **Languages**, such as French (`fr`).
You can start with local extraction before you have an account. Leave hosted credentials unset until the local build works. The dashboard creates projects with English (`en`) as the source language; tell your agent if your app uses another source language so it can check the project configuration first.
## 2. Paste this prompt into your coding agent
Replace the bracketed values. Give the agent the secret's location, rather than including its value in the prompt.
```text title="Setup prompt"
Set up Linguana in this repository using the existing app framework.
Read the Linguana docs at [DOCS_ORIGIN]/llms.txt and the relevant pages.
Project ID: [PROJECT_ID]
API URL: [API_URL]
Browser publishable key: [PUBLISHABLE_KEY]
Source language: en
First target language: fr
Build environment: development
The project token is stored in [IGNORED_ENV_FILE_OR_CI_SECRET] as
LINGUANA_PROJECT_TOKEN. Do not print it or include it in browser code.
Inspect package.json, vite.config, the application root, and SSR setup.
Install the matching public @linguanahq runtime, @linguanahq/catalog,
and @linguanahq/vite. Use the documented adapter before the framework
plugin. Preserve existing framework, routing, and deployment plugins.
The 0.1.0 SDK ships TypeScript source; use --configLoader runner for Vite.
For SSR, follow the framework guide and share the server hydration state.
First use upload: false. Keep static user-facing text as written.
Use the documented explicit-message API for variables and rich text.
Build and inspect .linguana/manifest.json; confirm source fallback works.
Then configure upload using the project ID and development token.
Keep unprefixed secrets in build config; only browser-safe values may
use VITE_. Do not start paid translation or publish a release yet.
Report changed files, the manifest result, and remaining dashboard steps.
Explain which environment the runtime reads and how to switch it after
publishing. Do not claim a language is live because it is accepted or ready.
```
Use the origin of the docs you are reading for `[DOCS_ORIGIN]`. In a local checkout, that is normally `http://localhost:3004`.
## 3. Give the agent documentation access
The docs expose [llms.txt](/llms.txt) for an index and [llms-full.txt](/llms-full.txt) for the full text. If your agent supports remote HTTP MCP, configure the docs origin plus `/mcp` as its documentation server. `/api/docs/mcp` is also supported.
This MCP server supplies documentation context. It does not grant dashboard access, create tokens, approve spending, or publish releases. Use your agent's own connection settings; the setup prompt also works when it reads the documentation directly.
## 4. Confirm the handoff, then use the dashboard
Ask the agent to show where your app's visible text appears in the manifest and confirm the upload appears in **Jobs**. Then:
1. In **Languages**, enable the first target language.
2. In **Jobs**, choose **Translate latest build** and review the maximum charge before starting.
3. Use [the strings editor](/docs/use-linguana/edit-strings) or **Review** to inspect and correct the wording.
4. Before production publication, have the agent upload a production build using a separate production token. Prepare and review its target catalog, then follow [Publish a language](/docs/use-linguana/publish-language). Set the runtime to production too. A development catalog must not be assumed compatible with production delivery.
For a development preview, ask the agent to follow [CI/CD](/docs/automate/ci-cd) after you approve the run's spending limit; the optional plugin workflow publishes only to development.
Your role controls which actions you can take. Viewers can inspect; editors, admins, and owners can edit, review, and publish.
The build appears in the dashboard. Once a catalog is published to the environment your runtime reads, selecting the target language shows its translated text. The original source remains the fallback.
Take the [dashboard tour](/docs/use-linguana/dashboard-tour), then [edit strings](/docs/use-linguana/edit-strings).
---
## Connect hosted translations
URL: https://docs.linguana.dev/docs/getting-started/connect-your-app
Upload your app's text, publish a translated language, and load it in the browser.
# Connect hosted translations
Start here after [installation](/docs/getting-started/install). You should already have a working build and a `.linguana/manifest.json` containing your app's text.
## 1. Create a project
Open **Projects** in the Linguana dashboard and choose **New project**. Create an organization first if you do not have one. The dashboard currently creates projects with English (`en`) as the source language.
Copy the project ID and replace the local `my-app` identifier in both your build plugin and runtime. This guide uses `production` in both places so the dashboard’s production publish action serves a catalog built for the same environment. Uploading a production build does not publish it. See [Create a project](/docs/use-linguana/create-project) for permissions and project settings.
In **Tokens**, create a production project token and copy it when shown. Obtain your API URL and browser publishable key from **Integrations**. The project token authenticates builds; the publishable key loads catalogs in the browser. The Integrations templates use `VITE_LINGUANA_API` and `VITE_LINGUANA_KEY`; this guide names those values `VITE_LINGUANA_API_URL` and `VITE_LINGUANA_PUBLISHABLE_KEY`. Match the names you store to the names your runtime reads.
## 2. Upload your text
```dotenv title=".env.local — replace the placeholders"
LINGUANA_API_URL=https://your-linguana-api.example.com
LINGUANA_PROJECT_TOKEN=your-project-token
LINGUANA_UPLOAD=false
VITE_LINGUANA_API_URL=https://your-linguana-api.example.com
VITE_LINGUANA_PUBLISHABLE_KEY=your-publishable-key
```
Vite does not automatically populate `process.env` from `.env.local` while evaluating its config. Load those values explicitly when configuring authenticated builds:
```ts title="vite.config.ts"
import { defineConfig, loadEnv } from "vite";
import react from "@vitejs/plugin-react";
import { linguana } from "@linguanahq/vite";
import { react as linguanaReact } from "@linguanahq/vite/react";
export default defineConfig(({ mode }) => {
const env = { ...loadEnv(mode, process.cwd(), ""), ...process.env };
return {
plugins: [
linguana({
adapter: linguanaReact(),
projectId: "your-project-id",
environment: "production",
sourceLocale: "en",
apiUrl: env.LINGUANA_API_URL,
token: env.LINGUANA_PROJECT_TOKEN,
upload: env.LINGUANA_UPLOAD === "true",
}),
react(),
],
};
});
```
Reading unprefixed values here keeps them in the build process. Do not forward the entire environment through Vite's `define` or `envPrefix` options.
Set `LINGUANA_UPLOAD=true` in `.env.local`, then run your build:
```bash title="terminal"
npm run build
```
Confirm the new build appears in the dashboard. For Vue, Svelte, or Solid, keep the adapter and framework plugin from your installation and add the same environment options.
## 3. Translate and publish
1. In **Languages**, enable a target language such as French.
2. In **Jobs**, choose **Translate latest build**, review the estimate, and start the run for the uploaded production build.
3. Inspect the wording in **Strings** and resolve pending decisions in **Review**.
4. In **Releases**, inspect the ready catalog, choose **Publish ready catalog**, and confirm production.
Uploading text does not translate or publish it. For a separate development workflow with optional build-triggered publishing, use a development token and matching build/runtime environment; see [CI/CD](/docs/automate/ci-cd). Production publishing remains an explicit release action.
## 4. Load the published language
For React, update your existing provider and add a language selector:
```tsx title="src/main.tsx"
import { createRoot } from "react-dom/client";
import { LanguageSelector, LocaleProvider } from "@linguanahq/react";
import "@linguanahq/react/styles.css";
import { App } from "./app";
createRoot(document.getElementById("root")!).render(
,
);
```
Use your existing `App` import and root element. For other frameworks, pass `catalogEndpoint` and `publishableKey` to the [matching runtime](/docs/getting-started/frameworks). Server-rendered apps should use the [TanStack Start guide](/docs/getting-started/tanstack-start) to keep the initial language consistent.
Restart the app after changing environment values and select French. Text included in your published catalog should now appear in French; missing translations fall back to the original text.
If the language does not load, check that the project and environment match the published catalog, then follow [Catalog runtime troubleshooting](/docs/troubleshooting/catalog-runtime).
## Next
Use [Dynamic text and links](/docs/build-with-code/dynamic-text-and-links) for variables and rich text, or [Show translated content](/docs/build-with-code/show-translated-content) for embedded catalogs and custom language switching.
---
## Vue, Svelte, and Solid
URL: https://docs.linguana.dev/docs/getting-started/frameworks
Set up the public Linguana SDK in React, Vue, Svelte, or Solid.
# Vue, Svelte, and Solid
For the public `0.1.0` SDK, configure Vite scripts with `--configLoader runner` as shown in [Install Linguana](/docs/getting-started/install). This lets Node load the packages’ TypeScript source.
Install the packages for your framework using [Install Linguana](/docs/getting-started/install), then configure its adapter and runtime below. These examples use local extraction with `upload: false`; no account or project token is required. Use the same project ID, source locale, and environment in the plugin and runtime.
## React and TanStack Start
Use `@linguanahq/react` with `@linguanahq/vite/react`. Follow the complete [React installation example](/docs/getting-started/install) or the [TanStack Start setup](/docs/getting-started/tanstack-start) for request-specific locale state and hydration.
## Vue
```ts title="vite.config.ts"
import { defineConfig } from "vite";
import { linguana } from "@linguanahq/vite";
import { vue as linguanaVue } from "@linguanahq/vite/vue";
import vue from "@vitejs/plugin-vue";
export default defineConfig({
plugins: [
linguana({
adapter: linguanaVue(),
projectId: "my-app",
environment: "development",
sourceLocale: "en",
upload: false,
}),
vue(),
],
});
```
Install the Vue plugin once on your application:
```ts title="src/main.ts"
import { createApp } from "vue";
import { createLinguana } from "@linguanahq/vue";
import "@linguanahq/vue/styles.css";
import App from "./App.vue";
createApp(App)
.use(createLinguana({
projectId: "my-app",
environment: "development",
initialLocale: "en",
fallbackLocale: "en",
}))
.mount("#app");
```
Keep `@vue/compiler-dom` and `@vue/compiler-sfc` installed as development dependencies, aligned with your Vue version.
## Svelte
```ts title="vite.config.ts"
import { defineConfig } from "vite";
import { linguana } from "@linguanahq/vite";
import { svelte as linguanaSvelte } from "@linguanahq/vite/svelte";
import { svelte } from "@sveltejs/vite-plugin-svelte";
export default defineConfig({
plugins: [
linguana({
adapter: linguanaSvelte(),
projectId: "my-app",
environment: "development",
sourceLocale: "en",
upload: false,
}),
svelte(),
],
});
```
Set context in the root component during initialization, before its child components render:
```svelte title="src/App.svelte"
```
```svelte title="src/Content.svelte"
Welcome to your application
```
Svelte components are exported through subpaths such as `@linguanahq/svelte/LanguageSelector.svelte` and `@linguanahq/svelte/T.svelte`. Keep translated content in descendants of the component that initializes the context.
## Solid
```ts title="vite.config.ts"
import { defineConfig } from "vite";
import { linguana } from "@linguanahq/vite";
import { solid as linguanaSolid } from "@linguanahq/vite/solid";
import solid from "vite-plugin-solid";
export default defineConfig({
plugins: [
linguana({
adapter: linguanaSolid(),
projectId: "my-app",
environment: "development",
sourceLocale: "en",
upload: false,
}),
solid(),
],
});
```
Wrap the application in `LinguanaProvider`:
```tsx title="src/main.tsx"
import { render } from "solid-js/web";
import { LinguanaProvider } from "@linguanahq/solid";
import "@linguanahq/solid/styles.css";
import App from "./App";
render(
() => (
),
document.getElementById("root")!,
);
```
## Verify your integration
Run `npm run build` or `bun run build`, check that `.linguana/manifest.json` includes visible text, and run your application to confirm source-language fallback. The adapter belongs before the framework plugin so Linguana sees the original JSX or template.
The optional `@linguanahq//styles.css` import styles the native language selector. Remove it if you provide your own styles using the `data-linguana-*` hooks.
## Load translated languages
Add an embedded source catalog through the runtime's `catalogs` option when you have one. For hosted delivery, set `catalogEndpoint` and `publishableKey` on the provider or the options passed to `createLinguana`. Use your real project ID and matching environment. The project token belongs only in the build config; never pass it to a runtime component.
Enable languages and publish catalogs in your project before expecting them to appear in the selector. Continue with [Connect hosted translations](/docs/getting-started/connect-your-app) and [Show translated content](/docs/build-with-code/show-translated-content).
---
## Glossary
URL: https://docs.linguana.dev/docs/getting-started/glossary
Understand builds, strings, catalogs, releases, environments, and credentials.
# Glossary
These are the terms you will see while connecting your app and managing translations.
| Term | What it means |
| --- | --- |
| Organization | The team that owns projects, memberships, billing, and spending limits. |
| Project | One application's source language, builds, target languages, catalogs, and credentials. |
| Environment | A named scope for build credentials and release pointers. Start with development; production has explicit publishing. Inspect the environments actually present in your project. |
| Build | Text found during an application build, with source locations and context. The SDK and HTTP API call its artifact a **manifest**. |
| String | A piece of source text plus its placeholders and context. The SDK and stored data may call it a **message**. |
| Language / locale | A language code such as `fr` or a regional code such as `fr-CA`. The registry labels locales Supported, Preview, or Certified. Enabling a locale does not publish it. |
| Translation version | A recorded wording for a string in a target language. A human edit creates a new version. |
| Accepted | A translation has passed acceptance or a human decision. It can contribute to a catalog; this says nothing about which release is live. |
| Ready catalog | A complete, validated catalog available for publication. Pending or rejected work can prevent readiness. |
| Catalog | A versioned language file mapping messages to translated text for a project and environment. |
| Release / published | An environment's active pointer selects a catalog version for runtime delivery. Publishing and restoring change that pointer. |
| Source fallback | The original text remains readable when a translation is absent or cannot be loaded. You can also explicitly approve source wording for an individual string. |
| Project token | A secret credential scoped to a project and environment, used by builds and CI. It must stay out of browser code. |
| Publishable key | A browser-safe credential for reading runtime configuration and published catalogs. It cannot edit translations. |
| Product glossary | Versioned rules for product terms used during translation and validation. This terminology page is separate from those project rules. |
| Translation memory | Compatible prior translations reused to avoid translating the same content again. |
| Spend reservation | The maximum charge set aside before an active translation run; completed work commits usage within that authorization. |
## Follow a string through the product
A build discovers a source string. A translation run prepares target wording. Review or an edit accepts a version. A complete catalog becomes ready. Publishing makes that catalog available to the runtime. The app retains source fallback throughout.
You can distinguish a discovered string, an accepted translation, a ready catalog, and a published release. Only the release pointer determines the hosted catalog an environment serves.
Use the [dashboard tour](/docs/use-linguana/dashboard-tour) to find each stage, or [start with code](/docs/getting-started/quickstart).
---
## Install Linguana
URL: https://docs.linguana.dev/docs/getting-started/install
Install the public npm SDK for React, Vue, Svelte, or Solid and run your first local build.
# Install Linguana
Install Linguana, connect it to your build, and extract your first messages. Local setup works without an account or token.
## Requirements
Use an existing Vite application and its framework plugin. The published `0.1.0` packages support:
| Application | Framework requirement | Runtime | Build adapter import |
| --- | --- | --- | --- |
| React / TanStack Start | React and React DOM 19+ | `@linguanahq/react` | `@linguanahq/vite/react` |
| Vue | Vue 3.5+ | `@linguanahq/vue` | `@linguanahq/vite/vue` |
| Svelte | Svelte 5+ | `@linguanahq/svelte` | `@linguanahq/vite/svelte` |
| Solid | Solid 1.9+ | `@linguanahq/solid` | `@linguanahq/vite/solid` |
The shared plugin supports Vite `>=7 <9`. Use a Node.js version supported by your Vite release. Keep the Linguana runtime and Vite plugin on matching release versions and commit your package manager's lockfile.
## 1. Install your framework's packages
Choose one framework. These commands assume its framework and official Vite plugin are already installed.
```bash title="Bun"
bun add @linguanahq/react @linguanahq/catalog
bun add --dev @linguanahq/vite
```
```bash title="Bun"
bun add @linguanahq/vue @linguanahq/catalog
bun add --dev @linguanahq/vite @vue/compiler-dom @vue/compiler-sfc
```
Keep `@vue/compiler-dom` and `@vue/compiler-sfc` versions aligned with Vue.
```bash title="Bun"
bun add @linguanahq/svelte @linguanahq/catalog
bun add --dev @linguanahq/vite
```
The Svelte adapter uses your installed Svelte compiler.
```bash title="Bun"
bun add @linguanahq/solid @linguanahq/catalog
bun add --dev @linguanahq/vite
```
`@linguanahq/vite/react` and the other adapter paths are imports from `@linguanahq/vite`, not separate packages to install. `@linguanahq/catalog` is needed directly when you import `createCatalog`. The core, compiler, and build-core packages are installed transitively for normal framework integrations.
## 2. Configure the config loader
The `0.1.0` packages publish TypeScript source. When running Vite with Node.js, use its module runner to load the config and SDK imports:
```json title="package.json — merge into your existing scripts"
{
"scripts": {
"dev": "vite --configLoader runner",
"build": "vite build --configLoader runner",
"preview": "vite preview --configLoader runner"
}
}
```
Preserve any existing type-check step in your build script and append `--configLoader runner` to its Vite command. This also works when invoking the scripts through `bun run`; installing with Bun alone does not force Vite to run under Bun.
If you see `ERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING`, check this setting before changing application code.
## 3. Add the build plugin
The example below uses React. For Vue, Svelte, or Solid, use the [framework-specific plugin and runtime](/docs/getting-started/frameworks), then return to step 5. For server-rendered React, follow [TanStack Start](/docs/getting-started/tanstack-start).
Add the Linguana plugin before the React plugin:
```ts title="vite.config.ts"
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import { linguana } from "@linguanahq/vite";
import { react as linguanaReact } from "@linguanahq/vite/react";
export default defineConfig({
plugins: [
linguana({
adapter: linguanaReact(),
projectId: "my-app",
environment: "development",
sourceLocale: "en",
upload: false,
}),
react(),
],
});
```
`my-app` is a local identifier for this extraction-only example. Replace it consistently in the plugin and runtime with your real project ID before connecting to the API. See [framework setup](/docs/getting-started/frameworks) for Vue, Svelte, and Solid, or [TanStack Start](/docs/getting-started/tanstack-start) for SSR.
## 4. Add the runtime
The transformed application needs its framework runtime. For React, wrap the app once:
```tsx title="src/main.tsx"
import { createRoot } from "react-dom/client";
import { LocaleProvider } from "@linguanahq/react";
import { App } from "./app";
createRoot(document.getElementById("root")!).render(
,
);
```
Use your application's existing `App` import and root element. The compiler keeps source text as fallback, so you can check extraction before any translations exist. For explicit catalogs and locale switching, continue with [Show translated content](/docs/build-with-code/show-translated-content).
## 5. Build your app
```bash title="terminal"
bun run build
```
Confirm `.linguana/manifest.json` contains messages from your application, run your app, and check that its source-language text still renders. No upload occurs with `upload: false`.
## Next
Your app now extracts text and renders its original language. Continue with [Connect hosted translations](/docs/getting-started/connect-your-app) to upload text and publish your first language.
For text with variables or links, see [Dynamic text and links](/docs/build-with-code/dynamic-text-and-links). If the build fails, see [Build diagnostics](/docs/troubleshooting/build-diagnostics).
---
## Get started
URL: https://docs.linguana.dev/docs/getting-started
Choose a code or AI agent path to your first published language.
# Get started
Use an existing Vite app with React, Vue, Svelte, or Solid. TanStack Start uses the React integration with request-aware server rendering.
## Before you begin
For local extraction, you need access to the app's repository and a working build. No account or token is required. For hosted translation, you need a Linguana organization and project, an enabled target language, and permission to translate and publish.
If you work through an AI coding agent, give it repository access and the setup prompt. Someone still needs to connect the build; after that, daily translation work happens in the dashboard.
## Follow your path
1. [Quickstart with code](/docs/getting-started/quickstart) or [set up with an AI agent](/docs/getting-started/ai-agent-setup).
2. [Connect hosted translations](/docs/getting-started/connect-your-app) after local extraction works.
3. [Edit and review strings](/docs/use-linguana/edit-strings), then [publish a language](/docs/use-linguana/publish-language).
Use [Install Linguana](/docs/getting-started/install) for package and config details, [framework setup](/docs/getting-started/frameworks) for Vue, Svelte, and Solid, or [TanStack Start](/docs/getting-started/tanstack-start) for SSR. Look up unfamiliar terms in the [glossary](/docs/getting-started/glossary).
Your first build contains the app's visible text. Once a target catalog is published to the environment your runtime reads, selecting that language shows the translated text. Missing text remains readable in the source language.
Start with [the quickstart](/docs/getting-started/quickstart) or [the agent prompt](/docs/getting-started/ai-agent-setup).
---
## Quickstart with code
URL: https://docs.linguana.dev/docs/getting-started/quickstart
Extract your first strings, then connect the app to a published language.
# Quickstart with code
Add Linguana to an existing React app on Vite and confirm that its text is extracted. For other frameworks, use the [matching adapter and runtime](/docs/getting-started/frameworks); for server rendering, use [TanStack Start](/docs/getting-started/tanstack-start).
## 1. Install the packages
```bash title="terminal"
bun add @linguanahq/react @linguanahq/catalog
bun add --dev @linguanahq/vite
```
The public `0.1.0` packages ship TypeScript source. Add `--configLoader runner` to your Vite commands, preserving existing type checks:
```json title="package.json — merge with existing scripts"
{
"scripts": {
"dev": "vite --configLoader runner",
"build": "vite build --configLoader runner",
"preview": "vite preview --configLoader runner"
}
}
```
## 2. Add the plugin before React
Merge this into your existing Vite configuration. Keep the other plugins your app uses.
```ts title="vite.config.ts"
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import { linguana } from "@linguanahq/vite";
import { react as linguanaReact } from "@linguanahq/vite/react";
export default defineConfig({
plugins: [
linguana({
adapter: linguanaReact(),
projectId: "my-app",
environment: "development",
sourceLocale: "en",
upload: false,
}),
react(),
],
});
```
`my-app` is a local identifier. Replace it with your dashboard project ID before uploading.
## 3. Wrap the application once
Use your existing `App` import and root element:
```tsx title="src/main.tsx"
import { createRoot } from "react-dom/client";
import { LocaleProvider } from "@linguanahq/react";
import { App } from "./app";
createRoot(document.getElementById("root")!).render(
,
);
```
Text in components beneath the provider stays readable without a remote catalog. You do not need translation keys for static JSX.
## 4. Build and inspect the strings
```bash title="terminal"
bun run build
```
Open `.linguana/manifest.json` and find a visible string from your app. Run your app and confirm it still renders the source language. This build has not uploaded, translated, or published anything.
## 5. Connect your first language
Follow [Connect hosted translations](/docs/getting-started/connect-your-app) to create a project, replace the local identifier, upload a build, enable a language, translate it under a spending limit, and publish a catalog. Your runtime's project and environment must match the published release.
Store `LINGUANA_PROJECT_TOKEN` only in the build environment. The browser uses a publishable key; never give a provider the project token or prefix that token with `VITE_`.
The local manifest contains your app's strings and the source page still works. After the hosted steps, selecting a published language displays its translations, with source text as fallback.
[Connect hosted translations](/docs/getting-started/connect-your-app), then [edit strings](/docs/use-linguana/edit-strings).
---
## TanStack Start
URL: https://docs.linguana.dev/docs/getting-started/tanstack-start
Connect the Vite compiler to request-aware locale resolution and matching hydration state.
# Add Linguana to TanStack Start
For the public `0.1.0` SDK, configure Vite scripts with `--configLoader runner` as shown in [Install Linguana](/docs/getting-started/install). This lets Node load the packages’ TypeScript source.
This guide shows how to keep the selected language consistent between the server and the browser.
Install the React packages and configure the config loader using [Installation](/docs/getting-started/install). Keep your existing TanStack Start and deployment plugins; the Nitro example below applies to apps that already use Nitro.
## 1. Configure Vite
```ts title="vite.config.ts"
import { linguana } from "@linguanahq/vite";
import { react as linguanaReact } from "@linguanahq/vite/react";
import { tanstackStart } from "@tanstack/react-start/plugin/vite";
import viteReact from "@vitejs/plugin-react";
import { nitro } from "nitro/vite";
import { defineConfig } from "vite";
const token = process.env.LINGUANA_PROJECT_TOKEN;
export default defineConfig({
ssr: { noExternal: [/^@linguanahq\//] },
plugins: [
linguana({
adapter: linguanaReact(),
projectId: "your-project-id",
environment: "development",
sourceLocale: "en",
apiUrl: process.env.LINGUANA_API_URL,
token,
upload: process.env.LINGUANA_UPLOAD === "true",
translation: token && process.env.LINGUANA_TRANSLATE !== "false"
? {
enabled: true,
maxChargeMicros: 100_000,
waitTimeoutMs: 300_000,
publish: "development",
}
: undefined,
}),
tanstackStart(),
nitro(),
viteReact(),
],
});
```
The `ssr.noExternal` setting lets Vite transform the published SDK TypeScript and TSX for server rendering. Keep the project token in the build environment. For values stored in `.env.local`, use `loadEnv` as shown in [Connect hosted translations](/docs/getting-started/connect-your-app).
## 2. Resolve the language for each request
```ts title="src/locale-bootstrap.ts"
import type { Catalog } from "@linguanahq/catalog";
import { createHydrationState, loadCatalog } from "@linguanahq/react";
import { resolveRequestLocale } from "@linguanahq/react/ssr";
import { createServerFn } from "@tanstack/react-start";
import { getRequest } from "@tanstack/react-start/server";
import { embeddedEnglish } from "./catalogs/en";
const projectId = "your-project-id";
const environment = "development";
const supportedLocales = ["en", "fr"] as const;
export const getLocaleBootstrap = createServerFn({ method: "GET" }).handler(
async () => {
const locale = resolveRequestLocale({
request: getRequest(),
supportedLocales,
defaultLocale: "en",
});
let remote: Catalog | undefined;
if (locale !== "en") {
try {
remote = await loadCatalog({
endpoint: process.env.LINGUANA_API_URL!,
projectId,
environment,
locale,
publishableKey: process.env.LINGUANA_PUBLISHABLE_KEY!,
});
} catch {
// Return deterministic source state when the target is unavailable.
}
}
return createHydrationState(
remote ? locale : "en",
remote ? [embeddedEnglish, remote] : [embeddedEnglish],
);
},
);
```
The server uses `LINGUANA_API_URL`. The browser may use `VITE_LINGUANA_API_URL` for later locale changes when container and public origins differ.
## 3. Pass the same state to the browser
Call `getLocaleBootstrap()` in the route that owns your application shell, then pass its return value unchanged:
```tsx title="src/routes/app.tsx"
import { createFileRoute, Outlet } from "@tanstack/react-router";
import { LocaleProvider } from "@linguanahq/react";
import { getLocaleBootstrap } from "../locale-bootstrap";
export const Route = createFileRoute("/app")({
loader: () => getLocaleBootstrap(),
component: ApplicationRoute,
});
const availableLocales = [
{ code: "en", displayName: "English", nativeName: "English" },
{ code: "fr", displayName: "French", nativeName: "Français" },
];
function ApplicationRoute() {
const hydrationState = Route.useLoaderData();
return (
);
}
```
Adapt `/app` and the relative import to your existing route layout. Use the same environment in the build, embedded catalog, server loader, and provider; switch them together when targeting production.
Do not independently re-resolve the locale in the browser. `hydrationState` is authoritative for the initial render.
## 4. Test with Accept-Language
```bash title="terminal"
curl -H 'Accept-Language: fr' http://localhost:3000
```
When a French development catalog exists, both server HTML and hydration use French. When it does not, both use the embedded English state.
The initial locale and catalog set match on server and client. Locale switching persists a cookie and does not change the URL unless your application implements that routing policy.
## Next
Read the [server rendering guide](/docs/build-with-code/server-rendering), then add client-side language switching with [Show translated content](/docs/build-with-code/show-translated-content).
---
## Introduction
URL: https://docs.linguana.dev/docs
Set up Linguana with code or an AI agent, then review and publish translations.
# Linguana
Get your app translated, review the wording, and choose when it reaches users. Start with code or let an AI coding agent handle the integration.
## What Linguana does
The build plugin finds user-facing text in React, Vue, Svelte, and Solid applications on Vite. Your source files stay as written:
```tsx title="src/Welcome.tsx"
export function Welcome() {
return
Welcome to your application
;
}
```
Static text like this is extracted automatically. For variables, plurals, and rich links, use [explicit messages](/docs/build-with-code/dynamic-text-and-links). The original source text remains the runtime fallback, and Linguana does not change your routes or link destinations.
Translation edits and approvals prepare new catalog versions. Publishing selects the version an environment serves; accepting a string alone does not make it live. Optional build-triggered publishing is limited to development.
## Find your next task
- [Install the SDK](/docs/getting-started/install) for local extraction without an account.
- [Connect hosted translations](/docs/getting-started/connect-your-app) to upload a build and load a published language.
- [Use the dashboard](/docs/use-linguana/dashboard-tour) to manage languages, strings, releases, and costs.
- [Automate builds in CI](/docs/automate/ci-cd) with a secret project token and a spend cap.
- [Reference](/docs/reference) for exact SDK options and HTTP contracts.
- [What's new](/docs/whats-new) for release notes.
Choose [the code quickstart](/docs/getting-started/quickstart) or [AI agent setup](/docs/getting-started/ai-agent-setup). Both lead to the same build, translate, review, and publish workflow.
---
## Catalog API
URL: https://docs.linguana.dev/docs/reference/catalog
Reference the files and IDs that connect application text to published languages.
# Catalog API
`@linguanahq/catalog` is the shared contract between compiler, API, translation workflow, and React runtime. Its current `SCHEMA_VERSION` is `1.0`.
## Schemas and types
The package exports Zod schemas and inferred types for message locations, placeholders, message context, manifest messages, diagnostics, manifests, and catalogs.
## Identity and hashing
- `normalizeSource(source)` creates the semantic identity input.
- `parseMessagePlaceholders(source)` parses ICU placeholders.
- `stableMessageId(input)` combines source locale, source, context, and optional explicit ID.
- `contentHash(value)` hashes canonical JSON.
- `manifestHash(manifest)` hashes manifest content.
Formatting-only source changes should preserve normalized identity. Context or explicit ID changes may intentionally create a different message.
## Catalog integrity
`createCatalog(input)` adds integrity evidence to an immutable catalog. `verifyCatalog(catalog)` recomputes and checks it. The runtime additionally verifies requested project, environment, and locale before rendering.
## Compatibility
Keep catalog, compiler, server, and runtime versions aligned. A schema parse or integrity failure is a hard rejection, not a warning that may be ignored.
## See also
[Find text in your app](/docs/getting-started/connect-your-app) · [Show translated content](/docs/build-with-code/show-translated-content)
---
## Language files
URL: https://docs.linguana.dev/docs/reference/catalog-delivery
Load published language files in your application and keep the original language available when needed.
# Language files
## Load a catalog
```ts
const catalog = await loadCatalog({
endpoint: "https://api.example.com",
projectId: "your-project-id",
environment: "production",
locale: "fr",
publishableKey: "pk_…",
});
```
`loadCatalog` requests the selected language file with `x-linguana-key`, checks that it belongs to your project, and returns it to the application.
## Credentials
A **project token** authorizes build and translation work and stays in CI. A **publishable key** allows the app to read published language files and may be configured in browser code. They are not interchangeable.
## Fallback order
When a user changes language, `LocaleProvider` uses a loaded language first and then tries the API. If loading fails, it uses the embedded original language and keeps the page readable.
## Verify
Publish a language, request it from the app, and confirm the correct project, environment, and language are returned. Then deny the request and confirm the original language remains readable.
## Next
Add [integration tests](/docs/automate/test-integration) and complete the [production checklist](/docs/automate/production-checklist).
---
## Vite integration API
URL: https://docs.linguana.dev/docs/reference/compiler
Reference the multi-framework Vite plugin, adapters, strict policy, defaults, and failure behavior.
# Vite integration API
Import `linguana` from `@linguanahq/vite`, then select exactly one framework adapter. The plugin extracts matching framework source and writes a version 2 manifest without changing source files.
## `linguana(options)`
```ts
import { linguana, type LinguanaViteOptions } from "@linguanahq/vite";
import { react } from "@linguanahq/vite/react";
```
| Option | Type | Required | Default | Example | Exposure | Failure behavior |
| --- | --- | --- | --- | --- | --- | --- |
| `adapter` | `react() | vue() | svelte() | solid()` | Yes | - | `react()` | Build config | Missing or misplaced adapters fail with setup guidance |
| `projectId` | `string` | Yes | - | `"checkout"` | Public identity | Missing value is a TypeScript/config error |
| `environment` | `string` | No | `"development"` | `"development"` | Public identity | Translation rejects non-development values |
| `sourceLocale` | `string` | No | `"en"` | `"en"` | Public | Used for stable IDs and source fallback |
| `apiUrl` | `string` | Remote work | - | `process.env.LINGUANA_API_URL` | Server/build config | Upload or translation fails when absent |
| `token` | `string` | Remote work | - | `process.env.LINGUANA_PROJECT_TOKEN` | Secret | Upload or translation fails when absent/invalid |
| `include` | `string | string[]` | No | JSX and TSX | `"src/**/*.tsx"` | Build config | Nonmatching files are ignored |
| `exclude` | `string | string[]` | No | Dependencies/tests/specs | `"**/*.stories.tsx"` | Build config | Matching files are ignored |
| `output` | `string` | No | `.linguana/manifest.json` | `"artifacts/messages.json"` | Build config | Parent directory is created automatically |
| `upload` | `boolean` | No | `Boolean(token)` | `true` | Build config | Requires `token` |
| `strict` | `StrictPolicy` | No | `false` | `{ duplicate: true }` | Build policy | Selected diagnostics fail the build |
| `translation` | `object` | No | - | See below | Paid build policy | Waits, fails safely, publishes development only |
## Translation options
```ts
translation: {
enabled: true,
maxChargeMicros: 100_000,
waitTimeoutMs: 300_000,
publish: "development",
}
```
Target languages come from the project’s enabled Languages list in the dashboard. `maxChargeMicros` and an optional `waitTimeoutMs` must be positive safe integers. `publish` accepts only `development`.
The compiler derives one idempotent build intent from manifest hash, sorted target locales, and publish target. It polls jobs until success, review/failure/cancellation, dispatch failure, or timeout. Every successful target must provide a catalog version before the development release is created.
## `StrictPolicy`
```ts
type StrictPolicy =
| boolean
| { ambiguous?: boolean; unsafe?: boolean; duplicate?: boolean };
```
`true` fails on every non-info diagnostic. Object policy defaults incompatible duplicate IDs to fatal while allowing you to promote ambiguous or unsafe categories independently.
## Framework adapters
React and Solid transform JSX before their official compiler plugins. Vue transforms the template block through the SFC compiler AST. Svelte transforms source before the official preprocess/compile plugins. Place `linguana()` before the official framework plugin.
## Defaults and extraction boundaries
- Excluded elements: `script`, `style`, `code`, and `pre`.
- Allowed attributes: `title`, `placeholder`, `aria-label`, `alt`, `label`, and button `value`.
- Custom attributes require `data-translate`.
- `data-no-translate`, hidden content, URLs, and machine attributes are excluded.
- Default output is `.linguana/manifest.json`.
## Common failures
| Message or code | Meaning | Recovery |
| --- | --- | --- |
| `Manifest upload requires apiUrl and token` | Remote work is enabled without both credentials | Supply both in the build or disable remote work |
| `duplicate-id` | One explicit ID maps to incompatible source | Give each semantic message a distinct ID |
| `TRANSLATION_BUILD_TIMEOUT` | Durable work exceeded the build wait | Inspect the listed jobs before retrying the same intent |
| `INNGEST_DISPATCH_FAILED` | The workflow worker was unreachable | Start/reconnect the worker and inspect the job ID |
| `REVIEW_REQUIRED` | A candidate needs human evidence | Resolve it in Review rather than publishing around it |
## See also
[Choose a framework](/docs/getting-started/frameworks) · [Migrate from the compiler package](/docs/advanced/migrate-compiler) · [Automate translations in CI](/docs/automate/ci-cd)
---
## Environment variables
URL: https://docs.linguana.dev/docs/reference/environment-variables
Classify build, runtime, provider, and documentation settings by exposure and purpose.
# Environment variables
## Build and CI
| Variable | Example | Required | Safe to expose? | Purpose | Failure behavior |
| --- | --- | --- | --- | --- | --- |
| `LINGUANA_PROJECT_TOKEN` | `lna_...` | Remote build work | No | Manifest and paid-work authorization | Upload/translation returns authorization failure |
| `LINGUANA_API_URL` | `https://api.example.com` | Remote compiler and SSR | Server configuration only | API origin for builds and server catalog reads | Registration or catalog load fails |
| `LINGUANA_UPLOAD` | `true` | No | Build config, not secret | Enables manifest upload | `false` keeps extraction local |
| `LINGUANA_TRANSLATE` | `false` | No | Build config, not secret | Disables translation while retaining upload | Omitted/true may translate when token config enables it |
| `GIT_COMMIT_SHA` | Git revision | No | Yes | Records source revision in the manifest | Manifest omits source revision |
## Browser runtime
| Variable | Example | Required | Safe to expose? | Purpose |
| --- | --- | --- | --- | --- |
| `VITE_LINGUANA_API_URL` | `https://api.example.com` | Remote browser catalogs | Yes | Browser-reachable catalog API |
| `VITE_LINGUANA_PUBLISHABLE_KEY` | `pk_...` | Remote browser catalogs | Yes | Read-only catalog authorization |
Only intentionally public values should use the `VITE_` prefix. Never create `VITE_LINGUANA_PROJECT_TOKEN`.
## Translation provider
| Variable | Example | Required | Safe to expose? | Purpose |
| --- | --- | --- | --- | --- |
| `OPENROUTER_API_KEY` | Provider secret | OpenRouter mode | No | Paid translation provider authorization |
| `OPENROUTER_MODEL` | `qwen/qwen3.8-flash` | Provider-dependent | Server config only | Selects the provider model |
| `TRANSLATION_PROVIDER` | `openrouter` | Server translation | Server config only | Selects deterministic or live provider adapter |
## Documentation deployment
| Variable | Example | Required | Safe to expose? | Purpose |
| --- | --- | --- | --- | --- |
| `DOCS_BASE_URL` | `https://docs.example.com` | Hosted metadata | Yes | Canonical docs and `llms.txt` origin |
| `DOCS_PORT` | `3004` | Deployment-dependent | Yes | Docs service port |
| `VITE_DOCS_URL` | `https://docs.example.com` | Landing integration | Yes | Browser-visible docs destination |
## Server and browser origins
The verified example uses `LINGUANA_API_URL` on the server and `VITE_LINGUANA_API_URL` in the browser because container networking and public browser origins may differ. Both must resolve the same API and project catalog scope.
Inspect production client bundles and generated environment output for token prefixes and provider values. Rotate any secret that reached a browser artifact or unredacted log.
## See also
[Tokens and environments](/docs/use-linguana/tokens-and-environments) · [CI/CD](/docs/automate/ci-cd)
---
## HTTP API
URL: https://docs.linguana.dev/docs/reference/http-api
Reference the build and language endpoints used by Linguana integrations.
# HTTP API
Prefer the typed build integration and matching framework runtime. Direct HTTP integration is useful for diagnostics and nonstandard build systems but must preserve the same schemas and safety rules.
## Build endpoints
| Method and path | Purpose |
| --- | --- |
| `POST /api/v1/manifests` | Register manifest metadata and receive optional presigned upload |
| `POST /api/v1/manifests/:id/complete` | Confirm artifact upload and validation |
| `POST /api/v1/manifests/:id/translations` | Request bounded target-locale jobs |
| `GET /api/v1/jobs/:id` | Read job, dispatch, error, and catalog state |
| `POST /api/v1/manifests/:id/development-release` | Publish successful build catalogs to development |
Send `Authorization: Bearer ` and a deterministic `Idempotency-Key`. Manifest registration uses the manifest content hash. Never send project authorization to a presigned object-storage URL; use only the returned upload headers.
## Runtime endpoint
`GET /api/v1/catalogs/:project/:environment/:locale` uses `x-linguana-key: `. Verify the returned catalog schema, integrity, and scope before rendering.
## Dashboard strings endpoints
The [Strings API](/docs/reference/strings-api) lists source text, reads version history, saves human edits with optional `expectedVersion`, and reviews selections. These routes use dashboard sessions and organization roles, rather than project tokens or publishable keys. Acceptance and catalog readiness are separate from publication.
Runtime configuration is available at `GET /api/v1/runtime/projects/:project/environment/:environment`, also using `x-linguana-key`. Use the environment name that matches your published catalog.
## Errors
SDK-consumed JSON failures may contain `code`, `message`, and `requestId`. Preserve code and request ID in redacted telemetry. HTTP status alone is insufficient to distinguish cap, provider, dispatch, validation, or scope failures.
## Idempotency
Retry the same logical request with the same key. Inventing a new key after a timeout can create parallel work and duplicate reservations.
## See also
[Upload troubleshooting](/docs/troubleshooting/upload-and-auth) · [Jobs troubleshooting](/docs/troubleshooting/jobs-and-provider)
---
## Reference
URL: https://docs.linguana.dev/docs/reference
Look up Linguana build, runtime, language file, server rendering, API, and environment details.
# Reference
Use the typed Vite plugin and matching framework runtime for normal integrations. Use these pages when you need exact option names, return types, endpoint contracts, or environment exposure rules.
- [Compiler API](/docs/reference/compiler)
- [React API](/docs/reference/react)
- [Vue API](/docs/reference/vue)
- [Svelte API](/docs/reference/svelte)
- [Solid API](/docs/reference/solid)
- [SSR API](/docs/reference/ssr)
- [Catalog API](/docs/reference/catalog)
- [Catalog delivery](/docs/reference/catalog-delivery)
- [HTTP API](/docs/reference/http-api)
- [Strings API](/docs/reference/strings-api)
- [Environment variables](/docs/reference/environment-variables)
- [Supported languages](/docs/reference/supported-languages)
---
## React API
URL: https://docs.linguana.dev/docs/reference/react
Reference the React provider, hooks, messages, language controls, and diagnostics.
# React API
## `LocaleProvider`
Required: `projectId`, `environment`, `initialLocale`, `fallbackLocale`, and `children`. Optional inputs include `publishableKey`, `catalogs`, `catalogEndpoint`, `cookieName`, `onDiagnostic`, `availableLocales`, `syncLocalePreference`, and `hydrationState`.
## `useTranslation()`
Returns:
- `locale`, `sourceLocale`, `ready`, and sampled `diagnostics`.
- async `setLocale(locale)`.
- `t(source, values?, options?)`.
- lower-level `translate(id, source, values?, components?)`.
- `availableLocales`.
It throws when called outside `LocaleProvider`.
## Explicit components
`` accepts `id?`, required `defaultMessage`, `values?`, `components?`, and `context?`. `MessageComponents` maps tag names to React elements or chunk functions.
## Locale controls
`useLocaleOptions(query?)` returns filtered, display-name-sorted options with locale, readiness, and setter. `LanguageSelector` supplies a searchable input and accessible select.
## Catalog and preference helpers
- `loadCatalog(input)` fetches, parses, verifies integrity, and checks scope.
- `persistLocalePreference(locale, cookieName?, target?)` writes the first-party locale cookie.
- `createHydrationState(locale, catalogs)` validates and serializes initial state.
- `resolveLocale(input)` implements override, user preference, cookie, browser language, and default precedence.
## Diagnostics
`DiagnosticEvent.code` is `missing-translation`, `invalid-translation`, or `catalog-load-failed`, with locale, message, and optional message ID.
## See also
[Show translated content](/docs/build-with-code/show-translated-content) · [Integration testing](/docs/automate/test-integration)
---
## Solid API
URL: https://docs.linguana.dev/docs/reference/solid
Solid provider, primitives, messages, and language controls.
# Solid API
`LinguanaProvider` owns the framework-neutral client. `useTranslation()` exposes Solid accessors for locale, readiness, and enabled languages alongside `setLocale`, `t`, and `translate`. `T` and `LanguageSelector` are native Solid components.
---
## SSR API
URL: https://docs.linguana.dev/docs/reference/ssr
Reference server language selection and keeping the browser in sync.
# SSR API
Import server helpers from `@linguanahq/react/ssr`.
## `resolveRequestLocale(input)`
```ts
type ResolveRequestLocaleInput = {
request: Request;
override?: string | null;
userPreference?: string | null;
supportedLocales: readonly string[];
defaultLocale: string;
cookieName?: string;
};
```
Reads the configured cookie (default `linguana_locale`) and `Accept-Language`, then delegates to the shared precedence algorithm. It returns one of the supported matches or the default.
## `createRequestHydrationState(locale, catalogs)`
Returns schema-versioned `LocaleHydrationState` after validating every catalog. Pass it unchanged to `LocaleProvider.hydrationState` on the initial render.
## Precedence
1. Explicit application override.
2. Signed-in preference supplied by the host.
3. First-party cookie.
4. Weighted `Accept-Language` match.
5. Project default.
Exact locale matches win before language-only matches.
## See also
[Server rendering setup](/docs/build-with-code/server-rendering) · [TanStack Start](/docs/getting-started/tanstack-start)
---
## Strings API
URL: https://docs.linguana.dev/docs/reference/strings-api
List source strings, inspect version history, save human translations, and review selections.
# Strings API
These endpoints power the dashboard's strings editor. They use the signed-in user's organization membership. A project build token or browser publishable key does not authorize string editing.
## Authentication and scope
Send the dashboard session cookie to the API, subject to its configured origin policy. `OWNER`, `ADMIN`, `EDITOR`, and `VIEWER` can read. `OWNER`, `ADMIN`, and `EDITOR` can write when the project and organization are active. Unauthorized access uses `404 PROJECT_NOT_FOUND` to avoid disclosing project existence.
All paths below start with `/api/v1`. `:project` is the project ID. `:message` is the string's database `id` returned by the list, not its stable `key`. `:environment` in the list accepts an environment ID or name. Writes affect project translation versions; they do not select a published environment release.
| Method and path | Purpose |
| --- | --- |
| `GET /projects/:project/environments/:environment/strings` | List strings in the latest ready build with selected target translations and language statistics. |
| `GET /projects/:project/strings/:message` | Read source context, locations, translation history, glossary matches, and memory suggestions. |
| `PUT /projects/:project/strings/:message/translations/:locale` | Save a validated human translation as a new accepted version. |
| `POST /projects/:project/strings/bulk-review` | Apply one decision to a selection of translation IDs, with per-item results. |
## List strings
```http title="Example request"
GET /api/v1/projects/PROJECT_ID/environments/development/strings?locale=fr,de&status=review&limit=50
```
| Query | Contract |
| --- | --- |
| `locale` | Optional comma-separated project target locale codes. Defaults to enabled target locales. Unknown codes, including the source locale, return `400 LOCALE_NOT_IN_PROJECT`. |
| `status` | `all` (default), `missing`, `review`, `rejected`, or `accepted`. `accepted` requires acceptance in every selected locale; other status filters match at least one selected locale. |
| `q` | Case-insensitive search in the stable key or source text. Trimmed; maximum 200 characters. It does not search target translations. |
| `file` | Source-location file filter; maximum 1,000 characters. |
| `cursor` | Opaque pagination value from `nextCursor`. Keep the same scope and filters while paging. |
| `limit` | Integer 1–200; defaults to 50. |
The response contains:
| Field | Meaning |
| --- | --- |
| `strings` | Rows sorted by stable key. Each has `id`, `key`, `sourceText`, `sourceLocale`, `placeholders`, `context`, `locations`, and `translations`. |
| `strings[].translations` | Map from selected locale code to its latest translation view, or `null` when missing. |
| `locales` | Per-locale totals: `code`, `total`, `translated` (accepted), `needsReview`, `rejected`, and `missing`. These are whole-build statistics, not counts for just the current page or search. |
| `total` | Number of strings matching the selected filters before pagination. |
| `manifest` | Latest ready build's `id` and `createdAt`, or `null`. |
| `nextCursor` | Cursor for the next page, or `null`. |
With no ready build, the API returns an empty `strings` array, zero statistics, `total: 0`, `manifest: null`, and `nextCursor: null`.
A translation view includes `id`, `text`, `status`, `version`, `origin`, `authorId`, `updatedAt`, and `outdated`. `outdated` compares the version's source content hash to the current string. Status values include `ACCEPTED`, `REVIEW_REQUIRED`, and `REJECTED`. None of these fields identifies what is currently live.
## Inspect a string
```http title="Example request"
GET /api/v1/projects/PROJECT_ID/strings/MESSAGE_ID?environment=development
```
`environment` is optional and accepts a project environment ID or name. When a ready build exists in that environment, locations come from it; without that build, the current implementation can fall back to the string's latest recorded build location. Without the query, the latest recorded location is used. Translation history is project-wide rather than restricted to the chosen environment.
The response has:
- `string`: source text, stable key, placeholders, context, hashes, and timestamps.
- `manifest` and `locations`: recorded build context for the source string.
- `locales`: project target locale codes and their statuses.
- `translations`: versions grouped by locale, newest first. Each includes the translation view, `approvalKind`, `jobId`, `validation`, author, and review records with reviewer, decision, note, and replacement version ID.
- `glossary`: active terms that match the source text, with target wording, locale, rule, case sensitivity, and notes.
- `suggestions`: up to five translation-memory suggestions per target locale. A `match` of `exact` or `source` describes compatibility or matching normalized source; suggestions are not automatically accepted.
## Save a human translation
```http title="Example request — replace IDs and use your session"
PUT /api/v1/projects/PROJECT_ID/strings/MESSAGE_ID/translations/fr
Content-Type: application/json
Idempotency-Key: UNIQUE_KEY_FOR_THIS_EDIT
{
"text": "Bienvenue, {name}",
"expectedVersion": 3,
"note": "Use the same greeting as the sign-in screen."
}
```
The sample assumes the source contains `{name}`. Use the actual source placeholders and latest version from the read response.
| Body field | Contract |
| --- | --- |
| `text` | Required string, 1–20,000 characters. Must preserve source placeholders, ICU syntax, named rich slots, and applicable glossary rules. |
| `expectedVersion` | Optional integer, 0 or greater. Send the latest version you read; use `0` when no version exists for this string and locale. |
| `note` | Optional trimmed note, maximum 2,000 characters. Recorded with the edit and, when applicable, its review. |
The locale must be a target language added to the project. It cannot be the source locale. Saving creates a new `ACCEPTED` version with `origin: HUMAN` and `approvalKind: human_edit`; it records the author and audit event and updates translation memory. A pending or rejected predecessor receives an edit review; a pending predecessor leaves the review queue.
### Concurrent edits
When `expectedVersion` differs from the latest stored version, the API returns `409` with `code: STALE_TRANSLATION`. No replacement version is saved for that stale attempt. Keep the draft, fetch the string again, compare the latest wording, then submit the resolved edit with the new version and a new idempotency key. Omitting `expectedVersion` omits this comparison; editor clients should send it.
### Idempotency
`Idempotency-Key` is required. Retry the same logical edit with the same key and identical body after a transport failure. A key reused for different content returns `409 IDEMPOTENCY_CONFLICT`. A successful replay returns the recorded `translation`, `replay: true`, and `catalog: null`; it does not create another version or rerun catalog preparation.
A normal success returns `translation` plus `catalog` containing `ready`, per-build `results`, and optional `jobCatalogReady`. A saved edit can return `catalog.errorCode: CATALOG_REBUILD_FAILED` with `ready: false`. The translation is still saved; inspect catalog state before trying to publish. Readiness depends on the other required strings too.
Structural problems return `422 VALIDATION_FAILED` with `details.issues`. An extreme length ratio is a nonblocking warning stored with a human edit. Successful validation and acceptance do not publish a release or modify the contents of a published catalog.
## Review a selection
```http title="Example request — translation IDs, not message IDs"
POST /api/v1/projects/PROJECT_ID/strings/bulk-review
Content-Type: application/json
Idempotency-Key: UNIQUE_KEY_FOR_THIS_SELECTION
{
"translationIds": ["TRANSLATION_ID_A", "TRANSLATION_ID_B"],
"decision": "APPROVED",
"note": "Checked against the checkout screen."
}
```
| Body field | Contract |
| --- | --- |
| `translationIds` | Required array of 1–200 nonempty translation IDs. Duplicate IDs are deduplicated. |
| `decision` | `APPROVED`, `REJECTED`, or `SOURCE_FALLBACK`. |
| `note` | Optional string, maximum 2,000 characters. |
Approval and source fallback create accepted replacement versions. Source fallback uses the original source text. Rejection marks the selected wording rejected. Approval validates the wording before accepting it. Each decision is recorded, and eligible catalogs are rebuilt after the batch. None of these decisions publishes a release.
The response contains `results`, `summary: { succeeded, failed }`, and `catalogs`. Each result includes `id`, `ok`, and `status`; success can include `reviewId`, `replacementTranslationId`, and `replay`, while failure includes `code` and `message`. Catalog results contain `jobs` and `locales`, and may include `errorCode: CATALOG_REBUILD_FAILED`.
The batch is not all-or-nothing. An HTTP success can contain failed items, including missing IDs, superseded versions, already accepted versions, validation failures, or concurrent write conflicts. Inspect every result and refresh failed rows. Use a new key for a changed selection or decision, and the same key for an unchanged retry.
For a single review, the dashboard also uses `POST /api/v1/translations/:translation/review`. Its body can carry `APPROVED`, `REJECTED`, `EDITED`, or `SOURCE_FALLBACK`; it also requires a session and an idempotency key. Use the `PUT` string endpoint for editor saves with `expectedVersion`.
## Errors to handle
| HTTP / code | Recovery |
| --- | --- |
| `400 INVALID_STRINGS_QUERY` | Correct locale, status, cursor, or limit filters. |
| `400 IDEMPOTENCY_KEY_REQUIRED` | Supply a key for the logical write. |
| `400 INVALID_TRANSLATION` / `INVALID_BULK_REVIEW` | Correct the request body and size limits. |
| `400 LOCALE_NOT_IN_PROJECT` | Choose a project target language. |
| `404 PROJECT_NOT_FOUND` / `ENVIRONMENT_NOT_FOUND` / `STRING_NOT_FOUND` | Check the selected scope and membership; do not assume the resource exists. |
| `409 STALE_TRANSLATION` | Reload current version, reconcile your draft, and submit with the current expected version. |
| `409 IDEMPOTENCY_CONFLICT` | Use the original body for a retry or a new key for a new action. |
| Per-item `409 TRANSLATION_SUPERSEDED` / `TRANSLATION_ALREADY_ACCEPTED` | Refresh the selection and review only current pending work. |
| `422 VALIDATION_FAILED` / per-item `STRUCTURAL_VALIDATION_FAILED` | Restore required placeholders, rich slots, ICU syntax, and glossary terms. |
API errors include `code`, `message`, `requestId`, and optional `details`. Keep the code and request ID for diagnostics.
An edit produces an accepted version and catalog preparation results. A stale edit is rejected. Bulk review reports individual outcomes. Published catalog delivery continues to follow the environment's release pointer, with original source text as fallback.
[Publish a language](/docs/use-linguana/publish-language), or return to the [HTTP API](/docs/reference/http-api) for build and runtime endpoints.
---
## Supported languages
URL: https://docs.linguana.dev/docs/reference/supported-languages
Every language and regional variant Linguana can translate, with locale codes and support levels.
# Supported languages
Linguana supports **121 locales across 120 languages**, including two script variants of Chinese: Simplified (`zh-Hans`) and Traditional (`zh-Hant`). Enable any of them for a project in **Languages**; see [Choose your languages](/docs/use-linguana/choose-languages).
Use the locale code exactly as listed when you configure a source language or read a catalog. The region is the representative country used for the flag in the dashboard; it does not restrict where the language is used.
| Language | Native name | Code | Region | Support level |
| --- | --- | --- | --- | --- |
| Afaan Oromo | Afaan Oromoo | `om` | ET | Preview |
| Afrikaans | Afrikaans | `af` | ZA | Supported |
| Amharic | አማርኛ | `am` | ET | Preview |
| Armenian | Հայերեն | `hy` | AM | Supported |
| Assamese | অসমীয়া | `as` | IN | Supported |
| Asturian | Asturianu | `ast` | ES | Supported |
| Awadhi | अवधी | `awa` | IN | Supported |
| Balinese | Basa Bali | `ban` | ID | Supported |
| Banjar | Bahasa Banjar | `bjn` | ID | Supported |
| Bashkir | Башҡортса | `ba` | RU | Supported |
| Basque | Euskara | `eu` | ES | Supported |
| Belarusian | Беларуская | `be` | BY | Supported |
| Bengali | বাংলা | `bn` | BD | Supported |
| Bhojpuri | भोजपुरी | `bho` | IN | Supported |
| Bosnian | Bosanski | `bs` | BA | Supported |
| Bulgarian | Български | `bg` | BG | Supported |
| Burmese | မြန်မာဘာသာ | `my` | MM | Supported |
| Cantonese | 粵語 | `yue` | HK | Supported |
| Catalan | Català | `ca` | ES | Supported |
| Cebuano | Cebuano | `ceb` | PH | Supported |
| Chhattisgarhi | छत्तीसगढ़ी | `hne` | IN | Supported |
| Croatian | Hrvatski | `hr` | HR | Supported |
| Czech | Čeština | `cs` | CZ | Supported |
| Danish | Dansk | `da` | DK | Supported |
| Dari | دری | `prs` | AF | Supported |
| Dutch | Nederlands | `nl` | NL | Supported |
| Eastern Yiddish | ייִדיש | `yi` | IL | Supported |
| Egyptian Arabic | مصري | `arz` | EG | Supported |
| English | English | `en` | GB | Certified |
| Estonian | Eesti | `et` | EE | Supported |
| Faroese | Føroyskt | `fo` | FO | Supported |
| Finnish | Suomi | `fi` | FI | Supported |
| French | Français | `fr` | FR | Supported |
| Friulian | Furlan | `fur` | IT | Supported |
| Galician | Galego | `gl` | ES | Supported |
| Georgian | ქართული | `ka` | GE | Supported |
| German | Deutsch | `de` | DE | Supported |
| Greek | Ελληνικά | `el` | GR | Supported |
| Gujarati | ગુજરાતી | `gu` | IN | Supported |
| Haitian | Kreyòl ayisyen | `ht` | HT | Supported |
| Hebrew | עברית | `he` | IL | Supported |
| Hindi | हिन्दी | `hi` | IN | Supported |
| Hungarian | Magyar | `hu` | HU | Supported |
| Icelandic | Íslenska | `is` | IS | Supported |
| Iloko | Ilokano | `ilo` | PH | Supported |
| Indonesian | Bahasa Indonesia | `id` | ID | Supported |
| Irish | Gaeilge | `ga` | IE | Supported |
| Italian | Italiano | `it` | IT | Supported |
| Japanese | 日本語 | `ja` | JP | Supported |
| Javanese | Basa Jawa | `jv` | ID | Supported |
| Kabuverdianu | Kabuverdianu | `kea` | CV | Supported |
| Kannada | ಕನ್ನಡ | `kn` | IN | Supported |
| Kazakh | Қазақша | `kk` | KZ | Supported |
| Khmer | ខ្មែរ | `km` | KH | Supported |
| Korean | 한국어 | `ko` | KR | Supported |
| Lao | ລາວ | `lo` | LA | Supported |
| Latvian | Latviešu | `lv` | LV | Supported |
| Levantine Arabic | عربي شامي | `apc` | LB | Supported |
| Ligurian | Ligure | `lij` | IT | Supported |
| Limburgish | Limburgs | `li` | NL | Supported |
| Lithuanian | Lietuvių | `lt` | LT | Supported |
| Lombard | Lombard | `lmo` | IT | Supported |
| Luxembourgish | Lëtzebuergesch | `lb` | LU | Supported |
| Macedonian | Македонски | `mk` | MK | Supported |
| Magahi | मगही | `mag` | IN | Supported |
| Maithili | मैथिली | `mai` | IN | Supported |
| Malay | Bahasa Melayu | `ms` | MY | Supported |
| Malayalam | മലയാളം | `ml` | IN | Supported |
| Maltese | Malti | `mt` | MT | Supported |
| Marathi | मराठी | `mr` | IN | Supported |
| Mesopotamian Arabic | عراقي | `acm` | IQ | Supported |
| Minangkabau | Baso Minangkabau | `min` | ID | Supported |
| Moroccan Arabic | الدارجة المغربية | `ary` | MA | Supported |
| Najdi Arabic | عربي نجدي | `ars` | SA | Supported |
| Nepali | नेपाली | `ne` | NP | Supported |
| North Azerbaijani | Azərbaycanca | `az` | AZ | Supported |
| Northern Uzbek | Oʻzbekcha | `uz` | UZ | Supported |
| Norwegian Bokmål | Norsk bokmål | `nb` | NO | Supported |
| Norwegian Nynorsk | Norsk nynorsk | `nn` | NO | Supported |
| Occitan | Occitan | `oc` | FR | Supported |
| Oriya | ଓଡ଼ିଆ | `or` | IN | Supported |
| Pangasinan | Pangasinan | `pag` | PH | Supported |
| Papiamento | Papiamentu | `pap` | CW | Supported |
| Persian | فارسی | `fa` | IR | Supported |
| Polish | Polski | `pl` | PL | Supported |
| Portuguese | Português | `pt` | PT | Supported |
| Punjabi | ਪੰਜਾਬੀ | `pa` | IN | Supported |
| Romanian | Română | `ro` | RO | Supported |
| Russian | Русский | `ru` | RU | Supported |
| Sardinian | Sardu | `sc` | IT | Supported |
| Serbian | Српски | `sr` | RS | Supported |
| Sicilian | Sicilianu | `scn` | IT | Supported |
| Silesian | Ślōnskŏ gŏdka | `szl` | PL | Supported |
| Simplified Chinese | 简体中文 | `zh-Hans` | CN | Supported |
| Sindhi | سنڌي | `sd` | PK | Supported |
| Sinhala | සිංහල | `si` | LK | Supported |
| Slovak | Slovenčina | `sk` | SK | Supported |
| Slovenian | Slovenščina | `sl` | SI | Supported |
| Spanish | Español | `es` | ES | Supported |
| Standard Arabic | العربية الفصحى | `ar` | SA | Supported |
| Sundanese | Basa Sunda | `su` | ID | Supported |
| Swahili | Kiswahili | `sw` | TZ | Supported |
| Swedish | Svenska | `sv` | SE | Supported |
| Tagalog | Tagalog | `tl` | PH | Supported |
| Taizzi-Adeni Arabic | تعزية-عدنية | `acq` | YE | Supported |
| Tajik | Тоҷикӣ | `tg` | TJ | Supported |
| Tamil | தமிழ் | `ta` | IN | Supported |
| Tatar | Татарча | `tt` | RU | Supported |
| Telugu | తెలుగు | `te` | IN | Supported |
| Thai | ไทย | `th` | TH | Supported |
| Tok Pisin | Tok Pisin | `tpi` | PG | Supported |
| Tosk Albanian | Shqip | `als` | AL | Supported |
| Traditional Chinese | 繁體中文 | `zh-Hant` | TW | Supported |
| Tunisian Arabic | تونسي | `aeb` | TN | Supported |
| Turkish | Türkçe | `tr` | TR | Supported |
| Ukrainian | Українська | `uk` | UA | Supported |
| Urdu | اردو | `ur` | PK | Supported |
| Venetian | Vèneto | `vec` | IT | Supported |
| Vietnamese | Tiếng Việt | `vi` | VN | Supported |
| Waray | Waray | `war` | PH | Supported |
| Welsh | Cymraeg | `cy` | GB | Supported |
This list is generated from the locale registry the dashboard uses, so it matches the languages you can enable.
---
## Svelte API
URL: https://docs.linguana.dev/docs/reference/svelte
Svelte context, stores, messages, and language controls.
# Svelte API
Create the shared client with `createLinguana(options)` and place it in root component context with `setLinguana(client)`. Descendants use `getLinguana()`. Import `T.svelte` for explicit messages and `LanguageSelector.svelte` for the native language control.
---
## Vue API
URL: https://docs.linguana.dev/docs/reference/vue
Vue plugin, composables, messages, and language controls.
# Vue API
`createLinguana(options)` returns a Vue plugin and its underlying client. Install it with `app.use(...)`. `useTranslation()` exposes reactive locale, readiness, languages, `setLocale`, `t`, and `translate`. `T` and `LanguageSelector` are native Vue components.
---
## Build diagnostics
URL: https://docs.linguana.dev/docs/troubleshooting/build-diagnostics
Resolve missing extraction, unsafe content, duplicate IDs, strict failures, and runtime import errors.
# Troubleshoot build diagnostics
## No messages were extracted
Confirm the file matches `include`/`exclude`, `linguana()` runs before React, and the content is literal visible JSX. Code blocks, hidden content, URLs, and opted-out subtrees are intentionally absent. Use `t()` or `` for dynamic content.
## Unsafe or ambiguous copy
The compiler skips machine-looking values and diagnoses mixed rich structures. Do not globally loosen the policy. Use `data-translate` for one literal custom attribute or named component slots for rich text.
## Duplicate ID
Search every explicit use of the ID. Compatible repeated source is allowed; incompatible source must receive distinct IDs. Do not suppress this error because a catalog cannot map one ID to two meanings.
## Strict build failed
Read the diagnostic code, severity, location, and remediation. If the candidate is an intentional conservative skip, use an object `strict` policy while fixing the corpus. Keep incompatible duplicates fatal.
## Compiler runtime import cannot resolve
Transformed files import `@linguanahq/react/compiler-runtime`. Ensure the compiler and React runtime come from a compatible workspace/release and the bundler has not externalized only one package.
## Verify
Run an extraction-only build, inspect `.linguana/manifest.json`, and run source-integrity checks. If the manifest is valid and the application builds without network access, compiler diagnosis is complete.
The build either succeeds with a deterministic manifest or points to a diagnostic code, source location, and bounded recovery action.
## Next
For remote build failures, continue with [Upload and authentication](/docs/troubleshooting/upload-and-auth).
---
## Language loading
URL: https://docs.linguana.dev/docs/troubleshooting/catalog-runtime
Fix language authorization, availability, server rendering, and original-language fallback.
# Troubleshoot language loading
## 404 or access error
Confirm the project, environment, language, published version, API URL, and publishable key. A project token is not a browser key.
## The language file is rejected
Do not render the response. Check that the project, environment, and language in the response match the request, then request it again.
## The language does not change
`setLocale` changes when the language is the fallback, already loaded, or successfully fetched. Confirm it appears in `availableLocales`, then inspect `ready` and the language-loading diagnostic.
## Hydration mismatch
Serialize the exact server-resolved locale and catalogs through `LocaleHydrationState`. Do not independently re-resolve the first client locale. When server remote loading fails, return source state rather than the requested unavailable locale.
## The original language appears
This is the expected behavior when a language is missing or cannot be loaded. Inspect the diagnostics:
- `missing-translation`
- `invalid-translation`
- `catalog-load-failed`
The page remains readable while diagnostics identify the language and optional message ID without leaking content.
## Next
Add the scenario to [Test your integration](/docs/automate/test-integration) and rehearse [publishing a language](/docs/use-linguana/publish-language) when a published language is the cause.
---
## Jobs and provider
URL: https://docs.linguana.dev/docs/troubleshooting/jobs-and-provider
Fix translation runs that are rejected, delayed, over budget, or waiting for review.
# Troubleshoot jobs and provider
## The translation service rejected the run
Invalid credentials, exhausted credit, rate limits, or an unavailable model can stop translation. Keep the error code and run/request IDs. Never paste provider credentials or application content into logs.
## The run is over the limit
The requested work does not fit within the organization limit. Reduce the content, wait for active work to finish, or ask an owner to update the limit.
## The run timed out
The build stopped waiting, but the translation run may still exist. Check the run before trying again so you do not create duplicate work.
## The run does not start
Check that the API and translation worker are running, then use the run ID to inspect the failure.
## Run states
- `REVIEW_REQUIRED`: resolve items in **Review**.
- `FAILED`: read the run details before retrying.
- `CANCELED`: start new work only when cancellation was intentional.
Every failed run provides an error code and IDs you can use to find it. Read the run details before retrying.
## Next
After a successful language version exists, use [Publish a language](/docs/use-linguana/publish-language) or troubleshoot [runtime loading](/docs/troubleshooting/catalog-runtime).
---
## Troubleshooting
URL: https://docs.linguana.dev/docs/troubleshooting
Start from the symptom and find the smallest safe recovery step.
# Troubleshooting
Keep the diagnostic code, job ID, and request ID. Do not paste source text, translations, project tokens, provider keys, or authorization headers into logs or support requests.
| Symptom | Guide |
| --- | --- |
| Manifest is empty or strict mode fails | [Build diagnostics](/docs/troubleshooting/build-diagnostics) |
| Upload returns 401, 403, or registration errors | [Upload and authentication](/docs/troubleshooting/upload-and-auth) |
| Job is stuck, capped, rejected, or timed out | [Jobs and provider](/docs/troubleshooting/jobs-and-provider) |
| Locale or catalog fails in the browser | [Catalog runtime](/docs/troubleshooting/catalog-runtime) |
---
## Upload and authentication
URL: https://docs.linguana.dev/docs/troubleshooting/upload-and-auth
Fix missing credentials, wrong project settings, and build upload problems.
# Troubleshoot upload and authentication
## Missing configuration
“Manifest upload requires apiUrl and token” means `upload` or `translation` is enabled without both values. Either disable remote behavior for extraction-only builds or provide a server/build-only token.
## 401 or 403
Check token revocation, project ID, environment, API origin, and whether the token belongs to the selected environment. Never test by putting the token into browser code. Create a replacement, update CI, verify **Last used**, then revoke the old value.
## The build upload failed
The build sends the text it found to the Linguana API. Keep the HTTP status and request ID when an upload fails. Retrying the same build is safe.
## The file upload failed
If the API returns a separate upload URL, confirm the build runner can reach it and sends the returned headers unchanged. Do not attach the project token to that storage URL.
## The upload did not finish
Check the API and storage service, then retry the same build. Translation should not begin until the upload is complete.
A successful authenticated build updates token usage and either stops after upload or starts the translation settings you enabled.
## Next
If a completed manifest does not translate, continue with [Jobs and provider](/docs/troubleshooting/jobs-and-provider).
---
## Choose your languages
URL: https://docs.linguana.dev/docs/use-linguana/choose-languages
Select the languages your application should support and keep important product terms consistent.
# Choose your languages
Start with the languages your customers need most. You can add more later without changing your application integration.
## 1. Open Languages
1. Open **Languages** for your project.
2. Search by language name, native name, locale code, or country.
3. Enable the language for your project.
Your source language stays enabled automatically.
## 2. Request development translations
The plugin requests the enabled target locales from project settings. It has no target-locale list option. To translate and publish those languages during an authorized development build, configure:
```ts
translation: {
enabled: true,
maxChargeMicros: 100_000,
waitTimeoutMs: 300_000,
publish: "development",
}
```
The language appears in project settings and can be offered by your application.
The `translation` option requires a development project token and `environment: "development"`. The per-build cap is expressed in microdollars; `100_000` is $0.10. Production publication is explicit in Releases.
## Product terminology
In **Languages**, add required target wording for important product terms. **Save glossary version** creates and activates a new version of those rules for subsequent work. Keep placeholders intact when editing translations; glossary validation still applies.
## Verify
Open a translation run and confirm the language is included before reviewing its result.
## Failure and next
If a language cannot be enabled, check the project fallback settings and [troubleshoot the dashboard](/docs/troubleshooting/jobs-and-provider).
---
## Create a project
URL: https://docs.linguana.dev/docs/use-linguana/create-project
Create the organization and project that own manifests, languages, catalogs, and releases.
# Create a project
**Audience:** organization owners and admins. **Outcome:** a project with development, staging, and production environments.
## Prerequisites
- A running or hosted Linguana dashboard.
- A signed-in account.
- Permission to create an organization and project.
## Steps
1. Open **Projects** in the dashboard.
2. If the workspace has no organization, choose **Create organization**, enter a name, and save it.
3. Choose **New project**.
4. Enter the application name. The current dashboard creates it with English (`en`) as the source locale.
5. Expand the project to inspect its environments and release policies.
Projects isolate manifests, languages, glossaries, catalogs, and tokens. Environments isolate credentials and release behavior. Do not reuse a production token for development builds.
The project appears in the workspace selector and exposes development, staging, and production environments. Its ID is the `projectId` used by the compiler and runtime.
## Verify
Open **Languages** and confirm English is the source language. Open **Tokens** and confirm no secret exists until an owner or admin creates one.
## Next
Create [environment-scoped tokens](/docs/use-linguana/tokens-and-environments), then [connect hosted translations](/docs/getting-started/connect-your-app).
---
## Dashboard tour
URL: https://docs.linguana.dev/docs/use-linguana/dashboard-tour
Find your project, builds, languages, strings, releases, and spending controls.
# Dashboard tour
Use the dashboard to turn an uploaded build into reviewed, published translations. Select the organization and project first; the work you see belongs to that scope.
## Find the task you need
| Area | What to do there |
| --- | --- |
| Overview | See the current workflow and next action for the selected project. |
| Projects | Create a project and inspect its environments and release policy. |
| Integrations | Get framework setup instructions, project details, and runtime configuration. |
| Jobs | Inspect uploaded builds, estimate a translation run, and follow progress or failures. |
| Languages | Enable target locales, set fallback relationships, and manage versioned product terms. |
| Strings | Inspect the latest ready build's source text and target versions; search, filter, and edit translations. |
| Review | Resolve translations that need a human decision, individually or in a selection. |
| Releases | Publish a ready catalog and restore an earlier published version. |
| Usage | Compare committed usage, reservations, and limits; export usage. |
| Team | Inspect members and manage access when your role permits it. |
| Tokens | Create or revoke environment-scoped build credentials. |
| Settings | Manage organization settings and billing according to your role. |
[AI agent setup](/docs/getting-started/ai-agent-setup) delegates repository changes to your coding agent. Dashboard tasks become available once that integration uploads a build.
## Follow your first build
1. Open **Jobs** and confirm that an uploaded build contains the expected strings.
2. Enable a target locale in **Languages**.
3. Choose **Translate latest build** in **Jobs**, review its estimate, and start the run.
4. Check the wording in **Strings** and resolve decisions in **Review**.
5. Open **Releases** when a complete catalog is ready. Confirm the target environment before publishing.
## Read the states correctly
Missing means no usable translation exists for that string. Needs review means a person must decide. Accepted means a translation is ready to contribute to a catalog. Published means an environment serves a selected catalog version. An accepted count or an empty review queue does not prove that the app is serving the new wording.
## Understand access
Viewers can read project data. Editors can edit, translate, review, and publish. Admins and owners can also manage members and tokens. Billing management belongs to the owner. If a write action is unavailable, ask your organization's owner to check your role in **Team**.
You can find the uploaded build, inspect the target wording, and identify the catalog currently published to the environment your app reads. Source text remains available as fallback.
[Edit strings](/docs/use-linguana/edit-strings), or [connect your app](/docs/getting-started/connect-your-app) if Jobs has no uploaded build.
---
## Edit strings
URL: https://docs.linguana.dev/docs/use-linguana/edit-strings
Correct a translation, preserve placeholders, and prepare a new catalog without changing the live release.
# Edit strings
Use **Strings** to inspect source text and correct a target translation. Saving creates a new accepted translation version. Your app receives that wording after a catalog containing it is published.
## 1. Find the string
1. Select your project and environment, then open **Strings**.
2. Choose one target language or **All enabled languages**.
3. Search by source text or string key, or narrow the view by status or source file.
4. Open the string to inspect its source text, placeholders, context, source locations, and translation history.
The list follows the latest ready build in the selected environment. If it is empty, first [upload your application text](/docs/getting-started/connect-your-app). Change source wording in the repository and upload a new build; the editor changes target translations.
## 2. Correct the target text
Read the original text and any matching glossary terms. Enter the target wording, optionally add a note, and choose **Save version**. **Use suggestion** copies a memory suggestion into your draft; **Discard edit** restores the saved draft base. You can use translation-memory suggestions as a starting point, but check that they match the current context.
Preserve every placeholder and named rich-text slot. For example, the sample source `Welcome, {name}` still needs `{name}` in the target. ICU plural syntax must remain valid. The application owns link destinations; translations can rearrange the named slots.
If the server rejects the edit, follow its validation message. Missing placeholders, invalid ICU, changed rich slots, and glossary violations must be corrected before saving. A length warning can remain attached to an accepted human edit.
## 3. Resolve review decisions
Use **Review** to approve, reject, or keep source wording for pending translations. You can select multiple items for a shared decision. Bulk results can include failures for individual items; inspect them before continuing.
- **Approve** accepts a new version for catalog preparation.
- **Reject** keeps that wording out of an accepted catalog and requires further work.
- **Use source** explicitly accepts the source wording for that target string.
In Strings, **Approve selected** and **Reject selected** act on selected reviewable versions. **Use source** is available in Review. The Strings filter labels accepted versions **Ready**; this is a string state, not confirmation that a complete catalog or published release exists. An accepted string can still be shown in its history after the pending item leaves the queue.
## 4. Check readiness, then publish
Saving and approval rebuild eligible catalogs. A catalog becomes ready only when the required strings are usable. Open **Releases** and inspect the ready catalog before choosing **Publish ready catalog**. A successful edit can remain saved even if catalog rebuilding fails; that does not mean it is ready or published.
An edit never changes the text inside an already published catalog. Restoring an earlier release selects its recorded catalog and preserves newer versions.
The edited target shows a new version and accepted state. The current published release stays selected until you publish another catalog. Missing or unavailable translations still fall back to the original source.
[Publish a language](/docs/use-linguana/publish-language). For custom integrations, use the [Strings API](/docs/reference/strings-api).
---
## Use the dashboard
URL: https://docs.linguana.dev/docs/use-linguana
Manage languages, edit and review strings, publish releases, and track costs.
# Use the dashboard
Once your app uploads a build, daily translation work happens here. You can follow the same workflow whether you connected the SDK yourself or used an AI coding agent.
## Find your task
- [Dashboard tour](/docs/use-linguana/dashboard-tour): find builds, strings, releases, and spending controls.
- [Create a project](/docs/use-linguana/create-project) and [manage tokens](/docs/use-linguana/tokens-and-environments).
- [Choose your languages](/docs/use-linguana/choose-languages) and add required product terms.
- [Run translations](/docs/use-linguana/translation-runs) with an approved maximum charge.
- [Edit strings](/docs/use-linguana/edit-strings) and [review translations](/docs/use-linguana/review-translations).
- [Publish a language](/docs/use-linguana/publish-language) or restore an earlier release.
- [Understand usage and costs](/docs/use-linguana/usage-and-costs).
Accepted strings contribute to a catalog. A complete catalog can become ready. Publishing selects what an environment serves. Those are separate states; editing never rewrites a published catalog.
Your application retains its original source text as fallback while translations are prepared. New wording reaches hosted delivery through an explicit release, or the optional development-only build publishing workflow.
Take the [dashboard tour](/docs/use-linguana/dashboard-tour), or [connect hosted translations](/docs/getting-started/connect-your-app) if your project has no uploaded build.
---
## Publish a language
URL: https://docs.linguana.dev/docs/use-linguana/publish-language
Make a reviewed language available to customers and restore an earlier version when needed.
# Publish a language
Once a language has been reviewed, publish it from the dashboard. You can restore an earlier version without losing the newer one.
## Development and production
Development builds can publish development catalogs when the Vite plugin’s `translation` option explicitly enables it with a token, cap, and `publish: "development"`. A plain upload does not translate or publish. The dashboard’s **Publish ready catalog** action currently targets production.
## Publish a language
1. Open **Releases** for your project.
2. Confirm **Ready to publish** shows the expected language and content.
3. Choose **Publish ready catalog**.
4. Confirm the production target and choose **Publish catalog**. Use a catalog built for production and a runtime configured for production; do not assume a development catalog can be served in another environment.
New catalog requests use the published release pointer. Browser or CDN caches and an already loaded runtime can retain the prior wording until it reloads. The previous immutable version remains available.
## Restore an earlier version
In **Release history**, find the version you want, choose **Restore**, and confirm. This changes what your application loads without deleting newer versions.
New language requests load the selected version. Existing versions remain available for inspection or restoration.
## If a language is not ready
Finish the translation run, translate missing strings, or resolve its review list first. Accepted strings and an empty review queue alone do not prove a complete catalog is ready. If your application still shows old text, check its project, environment, language, and API settings.
## Failure and next
For loading problems, read [Show translated content](/docs/build-with-code/show-translated-content) and [Catalog runtime troubleshooting](/docs/troubleshooting/catalog-runtime).
---
## Review translations
URL: https://docs.linguana.dev/docs/use-linguana/review-translations
Check translated text before you make a language available to customers.
# Review translations
Some translated text needs a person to check it before it is published. Review keeps your product language consistent and gives your team a chance to correct unclear wording.
## Review a translation
1. Open **Review** for your project.
2. Compare the original and translated text.
3. Choose **Approve**, **Reject**, or **Use source**.
Approving accepts the translation. Rejecting keeps it out of the published language. Use source keeps the original wording for that message.
For wording changes, use [Edit strings](/docs/use-linguana/edit-strings). Saving creates an accepted human version and prepares eligible catalogs. Viewers can read; editors, admins, and owners can make decisions.
## What to check
- Names, numbers, and placeholders still make sense.
- Links and emphasis appear in the right place.
- Product terms follow your glossary.
- The translation matches the surrounding screen.
The item leaves the review list after a decision. Approval or source fallback records an accepted replacement version. A clear review queue is one readiness check; required missing or rejected strings can still prevent a catalog from becoming ready. Publishing remains a separate action.
## Failures
If a decision is not saved, check that you have editor access and try again. Do not edit stored language files directly.
## Next
Publish the result with [Publish a language](/docs/use-linguana/publish-language).
---
## Tokens and environments
URL: https://docs.linguana.dev/docs/use-linguana/tokens-and-environments
Create, store, rotate, and revoke the build credentials for each project environment.
# Tokens and environments
Create a project token for builds and CI. Keep it out of browser code.
## Create a token
1. Open **Tokens** and select the project and environment.
2. Choose **New token** and use a recognizable name such as `GitHub Actions`.
3. Copy the complete value immediately. Linguana stores only its hash and never shows the secret again.
4. Save it as `LINGUANA_PROJECT_TOKEN` in the matching CI environment.
```bash title="local shell"
export LINGUANA_PROJECT_TOKEN='lna_…'
```
Do not prefix it with `VITE_`, serialize it into client state, log it, or commit it to an environment file.
## Environment policy
Use development tokens for manifest upload and optional build-triggered development publishing. Keep production publishing in the dashboard’s explicit release workflow. A token is scoped to its project and environment; a correct-looking token from another environment should be rejected.
## Rotate safely
1. Create the replacement token.
2. Update CI and complete a successful authenticated build.
3. Confirm **Last used** changed for the replacement.
4. Revoke the old token.
Token lists show only name, prefix, creation time, last use, and revocation state. The complete secret is visible once.
## Next
[Connect hosted translations](/docs/getting-started/connect-your-app) using the token. For runtime delivery, use a browser-safe publishable key as described in [Show translated content](/docs/build-with-code/show-translated-content).
---
## Run translations
URL: https://docs.linguana.dev/docs/use-linguana/translation-runs
Send application text for translation, follow progress, and understand what happens next.
# Run translations
## Start a translation run
Choose an enabled language in the dashboard and start a translation run. Linguana shows the expected work and cost before it starts.
Review the estimate, then choose **Translate latest build**. The run uses the text from your latest application build.
## Build-triggered translations
The compiler can start the same workflow during a development build when you add the `translation` option. Set a maximum charge and a wait time in the configuration.
## What you will see
- A run starts in progress and shows its current status.
- A completed run gives you a language version to review or publish.
- Some runs need a human decision before publishing.
- Failed or canceled runs keep their status so you can understand what happened.
Text that has already been translated can be reused on later runs, which keeps repeat work faster and less expensive.
The run page shows progress, language, estimated and actual cost, and the resulting language version.
## Next
Continue with [Review translations](/docs/use-linguana/review-translations), then [Publish a language](/docs/use-linguana/publish-language).
---
## Usage and costs
URL: https://docs.linguana.dev/docs/use-linguana/usage-and-costs
See what translation work costs and how Linguana helps you stay in control.
# Usage and costs
Linguana shows the expected cost before a translation run starts. Previously translated text can be reused, so you do not repeat the same work unnecessarily.
## Understand the dashboard
- **Committed usage** is completed translation work.
- **Reserved** is the maximum cost set aside for an active run.
- **Reused text** is content that did not need to be translated again.
- **Projected invoice** combines your plan and translation usage.
- **Remaining authorization** is what is still available for new work.
## Stay within your limit
Each run must fit beneath your organization’s monthly limit. Linguana checks the estimate before work begins, and the final charge cannot exceed the amount you accepted.
## Verify and export
Open **Usage and limits** to compare completed and active work, then download the usage CSV when you need a report.
A run that exceeds your limit does not start. Already-published languages continue to work.
## Failure and next
If a run is rejected, reduce the amount of content, wait for active work to finish, or ask an owner to update the limit. Continue with [Automate translations in CI](/docs/automate/ci-cd).
---
## What's new
URL: https://docs.linguana.dev/docs/whats-new
User-facing changes, improvements, and fixes for Linguana.
# What's new
See what has changed for teams using Linguana. These notes focus on setup, application workflows, and the product experience.
## Recent updates
- [Public launch](/docs/whats-new/public-launch)
- [Public SDKs on npm](/docs/whats-new/public-sdk)
- [Private beta: first Linguana workflow](/docs/whats-new/private-beta)
## Reading release notes
Each entry explains what changed and links to the guide you need next.
---
## Private beta: first Linguana workflow
URL: https://docs.linguana.dev/docs/whats-new/private-beta
Connect an application, translate selected languages, and publish reviewed versions.
# Private beta: first Linguana workflow
This historical beta note describes the initial integrations. For current availability, see [Public launch](/docs/whats-new/public-launch) and [framework setup](/docs/getting-started/frameworks).
The first Linguana workflow helps teams connect an application, prepare languages, review the result, and publish when ready.
## Added
- Connect an existing Vite application to Linguana.
- Find product text during the application build.
- Translate dynamic values and rich links with `t()` and ``.
- Review language before publishing it.
- Keep the original language available if a translation is missing.
- Vite + React and TanStack Start setup paths.
## Changed
- Documentation is organized around getting started, using Linguana, automation, and reference.
- CI guidance separates pull-request checks from development translation and production release.
## Fixed
- The setup flow focuses on connecting your own application instead of the repository demo.
- Setup examples distinguish build tokens from browser-safe publishable keys.
## Known limitations
- The SDK packages are public on npm under the `@linguanahq/*` scope. The earlier `@linguana/*` package names were never published and should not be used.
- Vite + React and TanStack Start are the current integrations.
- Production publication remains an explicit dashboard action.
## Migration
If you used a source workspace or prerelease instructions with `@linguana/*` imports, replace the public SDK scope with `@linguanahq/*` and install the packages from npm. If you link to the docs from outside the site, use the new getting-started and use-linguana paths.
---
## Public launch
URL: https://docs.linguana.dev/docs/whats-new/public-launch
Set up with code or an AI agent, edit translations, and control published releases.
# Public launch
Linguana is publicly launched in 2026. Use the public SDK to connect your app, then manage translation work in the dashboard.
## Choose your setup path
The [code quickstart](/docs/getting-started/quickstart) and [AI agent setup](/docs/getting-started/ai-agent-setup) lead to the same workflow. React, Vue, Svelte, and Solid use the shared Vite plugin; TanStack Start uses the React integration with request-aware hydration. Local extraction needs no account or token.
## Edit and release with control
- [Inspect and edit strings](/docs/use-linguana/edit-strings) with source context, placeholders, and version history.
- [Review translations](/docs/use-linguana/review-translations) before preparing complete catalogs.
- [Publish or restore a release](/docs/use-linguana/publish-language) explicitly. Accepted and ready states are separate from the published version.
- [Track usage and costs](/docs/use-linguana/usage-and-costs) under approved maximum charges and organization limits.
Source text remains the fallback, and the SDK does not change your URLs or routes. Project tokens stay in the build environment; browser delivery uses publishable keys.
## Pricing
The SDK is free and open. Hosted service pricing is $19 per organization per month plus $8 per million newly translated target characters, billed through Polar. Translation memory can reuse compatible prior work. See [Usage and costs](/docs/use-linguana/usage-and-costs) for estimates and spending controls.
## Existing integrations
Keep the public `@linguanahq/*` imports and matching SDK versions. The code guides now live under [Build with code](/docs/build-with-code); existing bookmarks to those guides redirect to their new paths.
You can complete setup through either path and manage strings in the dashboard. New wording reaches hosted runtime delivery through a published release.
[Get started](/docs/getting-started), or take the [dashboard tour](/docs/use-linguana/dashboard-tour).
---
## Public SDKs on npm
URL: https://docs.linguana.dev/docs/whats-new/public-sdk
Install Linguana from npm and connect React, Vue, Svelte, Solid, or TanStack Start.
# Public SDKs on npm
The Linguana SDK packages are publicly installable under the `@linguanahq` scope. You can extract application text locally without an invitation, registry credentials, or a Linguana project token.
## Available packages
- `@linguanahq/vite`: shared build integration with React, Vue, Svelte, and Solid adapters.
- `@linguanahq/react`, `@linguanahq/vue`, `@linguanahq/svelte`, and `@linguanahq/solid`: framework runtimes.
- `@linguanahq/catalog`: embedded catalog helpers.
- `@linguanahq/core`, `@linguanahq/compiler`, and `@linguanahq/build-core`: public lower-level packages used by the integrations.
TanStack Start uses the React runtime and adapter, with request-aware SSR setup.
## Get started
Follow [Install Linguana](/docs/getting-started/install) for package commands and config details, then choose your [framework setup](/docs/getting-started/frameworks). Configure Vite scripts with `--configLoader runner` for the TypeScript source packages and keep `upload: false` for the first build.
Uploading manifests and loading hosted catalogs still require project access and the appropriate credentials. See [Get Linguana project access](/docs/getting-started/connect-your-app).
---