---
title: "Translate dynamic text and links"
description: "Use simple patterns for values, numbers, links, custom labels, and content that should stay unchanged."
canonical_url: "https://docs.linguana.dev/docs/build-with-code/dynamic-text-and-links"
markdown_url: "https://docs.linguana.dev/docs/build-with-code/dynamic-text-and-links.md"
x_farming_labs_generated_preamble: true
agent:
  task: "Annotate dynamic, rich, custom-attribute, and intentionally excluded React copy."
  outcome: "Eligible content appears once in the manifest and translated output never executes arbitrary HTML."
  appliesTo:
    framework:
      - "React"
    package:
      - "@linguanahq/react"
      - "@linguanahq/compiler"
  prerequisites:
    - "LocaleProvider is configured and the Vite compiler runs before React."
  files:
    - "The React component containing the message."
  commands:
    - "bun run build"
  verification:
    - "Inspect the message ID, placeholders, context, and source location in .linguana/manifest.json."
  rollback:
    - "Remove an explicit annotation only after replacing it with supported literal JSX or an intentional exclusion."
  failureModes:
    - symptom: "The build reports duplicate-id."
      resolution: "Give incompatible semantic messages different explicit IDs."
---

# Translate dynamic text and links
URL: /docs/build-with-code/dynamic-text-and-links
LLM index: /llms.txt
Description: Use simple patterns for values, numbers, links, custom labels, and content that should stay unchanged.
Related: /docs/getting-started/connect-your-app, /docs/build-with-code/text-and-code, /docs/reference/react

<!-- farming-labs:agent-contract:start -->
## Agent Contract

Task: Annotate dynamic, rich, custom-attribute, and intentionally excluded React copy.
Outcome: Eligible content appears once in the manifest and translated output never executes arbitrary HTML.

### Applies To

- Framework: `React`
- Package: `@linguanahq/react`, `@linguanahq/compiler`

### Prerequisites

- LocaleProvider is configured and the Vite compiler runs before React.

### Files

- `The React component containing the message.`

### Commands

- `bun run build`

### Verification

- Inspect the message ID, placeholders, context, and source location in .linguana/manifest.json.

### Rollback

- Remove an explicit annotation only after replacing it with supported literal JSX or an intentional exclusion.

### Failure Modes

- The build reports duplicate-id. — Recovery: Give incompatible semantic messages different explicit IDs.
<!-- farming-labs:agent-contract:end -->

# 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 | `<T>` |
| 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 (
    <p>
      {t(
        "{count, plural, one {# result} other {# results}}",
        { count },
        { id: "search.results", context: "Search result count" },
      )}
    </p>
  );
}
```

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 (
    <T
      id="legal.terms"
      defaultMessage="Read <terms>the terms</terms>."
      context="Checkout consent"
      components={{
        terms: <a href="/terms" />,
      }}
    />
  );
}
```

Translators can move the `<terms>` 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"
<Widget
  data-translate="emptyLabel"
  emptyLabel="No results"
/>
```

Use a space- or comma-separated list for multiple label props.

## The content should not be translated

```tsx title="src/product-code.tsx"
<section data-no-translate>
  <strong>Linguana</strong>
  <code>sku_checkout_v2</code>
</section>
```

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.

<FailureGuide symptom="The message is missing" cause="The source is dynamic, the t() source is not a literal, or the custom attribute was not named in data-translate." check="Use a literal ICU source, verify the import comes from @linguanahq/react, and inspect the nearest exclusion marker." />

## 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).

## Sitemap

See the full [sitemap](/sitemap.md) for all pages.
Docs-scoped sitemap: [/docs/sitemap.md](/docs/sitemap.md).
Well-known sitemap: [/.well-known/sitemap.md](/.well-known/sitemap.md).
