TypeScript langsys-js-typescript

The framework-agnostic base SDK — plain TypeScript or JavaScript, no framework required. The React, Vue, and Svelte bindings are thin wrappers over this package; here it drives the DOM directly. Same rule everywhere: the phrase in your code is both the lookup key and the base-language default.

Using an AI coding agent? Give it this prompt
Add Langsys translation to this TypeScript app.

1. Read https://docs.langsys.dev/llms.txt, then follow
   https://docs.langsys.dev/learn/sdk/typescript/ for the install and setup.
2. The English text in the code is the translation key. Wrap user-facing
   strings as they are: no keys file, no extraction step, and no
   hand-written plural or gender branches (Langsys generates those).
3. Read the Langsys project id and API key from environment variables.
   The write key is for development and servers only; anything shipped
   to the browser gets the read-only key.
4. Keep brand names and user-generated content out of translation.

Install

npm install langsys-js-typescript

No project or keys yet? The Quickstart gets you both in two minutes.

Initialize

import { LangsysApp, createSignal } from 'langsys-js-typescript';

const locale = createSignal('en-US');

await LangsysApp.init({
  projectid: import.meta.env.VITE_LANGSYS_PROJECT_ID,
  key: import.meta.env.VITE_LANGSYS_API_KEY, // read-only in the browser
  UserLocaleStore: locale,
});

Interactive playground

Edit the phrase and category, set the params as fields or raw JSON, then switch locale and watch the real SDK translate live.

Examples
Params
Output

t() — inline string, wired to the DOM View source ↗
import { t, tSignal } from 'langsys-js-typescript';

const greeting = document.querySelector('#greeting');
const name = 'Sarah';

// tSignal re-emits a fresh t() whenever the locale or catalog
// changes — subscribe once and the element stays translated.
tSignal.subscribe(() => {
  greeting.textContent = t('Hello, {name}!', 'Greetings', { name });
});

Which one do I use?

You’re translating… Use Why
A plain string in your code — a label, a title, a message. Params included; write counts flat and Langsys generates the plural forms. t() Returns a string. The phrase in your code is both the lookup key and the base-language default.
One sentence containing inline markup or dynamic values, kept whole. Phrase Inline elements become neutral tokens, so translators reorder the full sentence — markup position included — and the real elements are reconstituted at render.
A region of markup with several phrases — a hero, a footer, a card. Translate One tag registers the region as a single content block: every text run is extracted as its own phrase and the markup structure is preserved.
Something that must never translate — brand names, code, IDs. translate="no" The tokenizer and renderer skip the subtree; it stays verbatim while the sentence around it translates.

Rule of thumb: string → t() · sentence with markup → Phrase · region of markup → Translate · untouchable → translate="no".

Each vanilla DOM handler running live on the left; the code that wires it up on the right.

Watch The demo cuts for these primitives are on the Svelte page for now — the surface is identical, only the syntax differs: <Translate> (1:04) · <Phrase> (1:06) · <DontTranslate> (0:34).

Translate — content block

Welcome to our store

Browse the catalog in your language.

Translate — content block View source ↗
import { Translate } from 'langsys-js-typescript';

// <section id="hero">
//   <h3>Welcome to our store</h3>
//   <p>Browse the catalog in your language.</p>
// </section>
new Translate(document.querySelector('#hero'), { category: 'Home' });

Phrase — params & markup

Hi {name}, you have {count} items in your cart.

Phrase — params & markup View source ↗
import { Phrase } from 'langsys-js-typescript';

// <p id="cart-note">Hi {name}, you have {count} items in your cart.</p>
new Phrase(document.querySelector('#cart-note'), {
  category: 'Cart',
  params: { name: 'Sarah', count: 3 },
});

translate="no" — never translated

Welcome! This sentence always stays in English. Thanks for visiting!

translate="no" — never translated View source ↗
// The standard translate="no" attribute — the tokenizer
// and renderer skip the subtree, so it stays verbatim while
// the sentence around it translates.
// <p id="signin">Welcome! <span translate="no">This sentence always stays in English.</span> Thanks for visiting!</p>
new Translate(document.querySelector('#signin'), { category: 'Tour' });

Gender agreement

Spanish, French, or Russian inflect {username} has been invited for the person it’s about. Langsys generates those branches during translation — the same way it generates plurals — and the argument is named after the variable, with _gender appended: male, female, or other. Pass it whenever you know it. How it works, and how to find the phrases that need it →

Passing the gender argument
import { t } from 'langsys-js-typescript';

// Pass the agreement argument alongside the value it inflects.
t('{username} has been invited', 'Team', {
  username: user.name,
  username_gender: user.gender, // 'male' | 'female' | 'other'
});

Don’t know the gender? Pass 'other' explicitly — it’s the neutral branch. If it is missing anyway, the SDK takes the other branch for you (langsys-js-typescript 0.6.4 and later), so the sentence still reads correctly. TypeScript currently rejects username_gender in an inline params object (error TS2353; a fix is in review). Until it ships, build the params object first and pass the variable.

Server-side rendering

Fetch the catalog on the server and hand it to the client, so the SDK starts without fetching it again. The SDK translates in the browser, so the server-rendered HTML is in the base language until it starts.

// The server fetches the catalog; the client init starts from it
// instead of fetching it again, and translates once it runs.
LangsysApp.init({
  projectid, key,
  UserLocaleStore: locale,
  initialTranslations,        // fetched on the server
  initialTranslationsLocale,  // fetched on the server
  ssrTokenStrategy: 'client',
});
Search the docs & Learning Center