Skip to content

fix: remove deprecated cli dictionary metadata types#1697

Open
bgub wants to merge 154 commits into
mainfrom
bg/remove-deprecated-cli-dictionary-metadata
Open

fix: remove deprecated cli dictionary metadata types#1697
bgub wants to merge 154 commits into
mainfrom
bg/remove-deprecated-cli-dictionary-metadata

Conversation

@bgub

@bgub bgub commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

Summary

  • remove deprecated dictionary metadata aliases from CLI types: context, maxChars, and variablesOptions
  • keep supported , , ``, and custom metadata fields

Tests

  • pnpm --filter gt typecheck

Greptile Summary

This PR removes three @deprecated field declarations (context, maxChars, variablesOptions) from the DictionaryMetadata type in the CLI package. The existing pipeline code in createDictionaryUpdates.ts was already reading only the $-prefixed fields ($context, $maxChars), so the runtime behaviour is unchanged.

  • The index signature [key: string]: unknown is still present, meaning objects that carry the old field names at runtime will continue to compile without errors — they just won't receive dedicated TypeScript autocomplete or direct typed access any more.
  • No other file in the CLI source imports or reads these three fields from DictionaryMetadata directly, so there is no internal usage to clean up.

Confidence Score: 5/5

Safe to merge — purely a TypeScript type cleanup with no runtime logic changes.

The only touched file is a type declaration. The CLI processing code was already exclusively reading $context and $maxChars, so removing the deprecated un-prefixed aliases from the type has no effect on what the CLI actually does with dictionary entries. The remaining [key: string]: unknown index signature preserves runtime compatibility for any consumer still passing the old field names.

No files require special attention.

Important Files Changed

Filename Overview
packages/cli/src/types/data.ts Removes three deprecated DictionaryMetadata fields (context, maxChars, variablesOptions); the CLI processing code in createDictionaryUpdates.ts was already exclusively reading the $-prefixed equivalents, so no runtime behaviour changes.

Class Diagram

%%{init: {'theme': 'neutral'}}%%
classDiagram
    class DictionaryMetadata_Before {
        +$context?: string
        +$maxChars?: number
        +$_hash?: string
        +context?: string
        +maxChars?: number
        +variablesOptions?: Record
        +[key: string]: unknown
    }
    class DictionaryMetadata_After {
        +$context?: string
        +$maxChars?: number
        +$_hash?: string
        +[key: string]: unknown
    }
    DictionaryMetadata_Before --> DictionaryMetadata_After : PR removes deprecated aliases
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
classDiagram
    class DictionaryMetadata_Before {
        +$context?: string
        +$maxChars?: number
        +$_hash?: string
        +context?: string
        +maxChars?: number
        +variablesOptions?: Record
        +[key: string]: unknown
    }
    class DictionaryMetadata_After {
        +$context?: string
        +$maxChars?: number
        +$_hash?: string
        +[key: string]: unknown
    }
    DictionaryMetadata_Before --> DictionaryMetadata_After : PR removes deprecated aliases
Loading

Reviews (1): Last reviewed commit: "fix: remove deprecated cli dictionary me..." | Re-trigger Greptile

github-actions Bot and others added 24 commits June 26, 2026 12:48
This PR was opened by the [Changesets
release](https://github.com/changesets/action) GitHub action. When
you're ready to do a release, you can merge this and the packages will
be published to npm automatically. If you're not ready to do a release
yet, that's fine, whenever you add more changesets to odysseus, this PR
will be updated.

⚠️⚠️⚠️⚠️⚠️⚠️

`odysseus` is currently in **pre mode** so this branch has prereleases
rather than normal releases. If you want to exit prereleases, run
`changeset pre exit` on `odysseus`.

⚠️⚠️⚠️⚠️⚠️⚠️

# Releases
## gt-next@7.0.0-odysseus.2

### Patch Changes

- Updated dependencies
[[`e29cd01`](e29cd01)]:
  - gt-react@11.0.0-odysseus.2
## @generaltranslation/gt-next-lint@15.0.0-odysseus.2

### Patch Changes

- Updated dependencies []:
  - gt-next@7.0.0-odysseus.2
## gt-react@11.0.0-odysseus.2

### Patch Changes

- [#1657](#1657)
[`e29cd01`](e29cd01)
Thanks [@ErnestM1234](https://github.com/ErnestM1234)! - Persist
TanStack Start locale resolution through the browser locale cookie.
Allow React i18n config checks to work across bundled react-core
entrypoints.
- Updated dependencies
[[`e29cd01`](e29cd01)]:
  - @generaltranslation/react-core@2.0.0-odysseus.1
## @generaltranslation/react-core@2.0.0-odysseus.1

### Patch Changes

- [#1657](#1657)
[`e29cd01`](e29cd01)
Thanks [@ErnestM1234](https://github.com/ErnestM1234)! - Persist
TanStack Start locale resolution through the browser locale cookie.
Allow React i18n config checks to work across bundled react-core
entrypoints.
## gt-react-native@11.0.0-odysseus.2

### Patch Changes

- Updated dependencies
[[`e29cd01`](e29cd01)]:
  - @generaltranslation/react-core@2.0.0-odysseus.1
## gt-tanstack-start@1.0.0-odysseus.2

### Patch Changes

- [#1657](#1657)
[`e29cd01`](e29cd01)
Thanks [@ErnestM1234](https://github.com/ErnestM1234)! - Persist
TanStack Start locale resolution through the browser locale cookie.
Allow React i18n config checks to work across bundled react-core
entrypoints.
- Updated dependencies
[[`e29cd01`](e29cd01)]:
  - @generaltranslation/react-core@2.0.0-odysseus.1
  - gt-react@11.0.0-odysseus.2

<!-- codesmith:footer -->
---
<a
href="https://app.blacksmith.sh/generaltranslation/codesmith/gt/pr/1658"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://pr-comments-assets.blacksmith.sh/codesmith/view-with-codesmith-dark-v2.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://pr-comments-assets.blacksmith.sh/codesmith/view-with-codesmith-light-v2.svg"><img
alt="View with Codesmith"
src="https://pr-comments-assets.blacksmith.sh/codesmith/view-with-codesmith-dark-v2.svg"></picture></a>
<a
href="https://backend.blacksmith.sh/track/enable-autofix?expires=1785094458&installation_id=127936663&pr_number=1658&repository=generaltranslation%2Fgt&return_to=https%3A%2F%2Fgithub.com%2Fgeneraltranslation%2Fgt%2Fpull%2F1658&signature=51bc6c4bf53c80e9a45ebce419126eaf34e915aa35b177629ee36c3e448d6b4d"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://pr-comments-assets.blacksmith.sh/codesmith/autofix-with-codesmith-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://pr-comments-assets.blacksmith.sh/codesmith/autofix-with-codesmith-light.svg"><img
alt="Autofix with Codesmith"
src="https://pr-comments-assets.blacksmith.sh/codesmith/autofix-with-codesmith-dark.svg"></picture></a>
<sup>Need help on this PR? Tag <code>/codesmith</code> with what you
need. Autofix is disabled.</sup>

<!-- codesmith:autofix:disabled -->
<!-- /codesmith:footer -->

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
## Summary
- increase Cargo network retries for the gt-next SWC plugin
- disable Cargo HTTP multiplexing to reduce flaky sparse registry
downloads in CI

## Testing
- pnpm exec turbo build --filter=generaltranslation --filter=gt-i18n
--filter='@generaltranslation/react-core' --filter=gt-react
--filter=gt-tanstack-start --filter=gt-next

## Changeset
- None

<!-- codesmith:footer -->
---
<a
href="https://app.blacksmith.sh/generaltranslation/codesmith/gt/pr/1662"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://pr-comments-assets.blacksmith.sh/codesmith/view-with-codesmith-dark-v2.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://pr-comments-assets.blacksmith.sh/codesmith/view-with-codesmith-light-v2.svg"><img
alt="View with Codesmith"
src="https://pr-comments-assets.blacksmith.sh/codesmith/view-with-codesmith-dark-v2.svg"></picture></a>
<a
href="https://backend.blacksmith.sh/track/enable-autofix?expires=1785106690&installation_id=127936663&pr_number=1662&repository=generaltranslation%2Fgt&return_to=https%3A%2F%2Fgithub.com%2Fgeneraltranslation%2Fgt%2Fpull%2F1662&signature=2d7b9c0ca3fcc5f12af3c3ab8a20ee602424596fa9a575b94884316b857e2c91"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://pr-comments-assets.blacksmith.sh/codesmith/autofix-with-codesmith-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://pr-comments-assets.blacksmith.sh/codesmith/autofix-with-codesmith-light.svg"><img
alt="Autofix with Codesmith"
src="https://pr-comments-assets.blacksmith.sh/codesmith/autofix-with-codesmith-dark.svg"></picture></a>
<sup>Need help on this PR? Tag <code>/codesmith</code> with what you
need. Autofix is disabled.</sup>

<!-- codesmith:autofix:disabled -->
<!-- /codesmith:footer -->

<!-- greptile_comment -->

<h3>Greptile Summary</h3>

Adds two Cargo configuration knobs to the SWC plugin's local
`.cargo/config.toml` to reduce flaky CI build failures caused by
transient network issues and HTTP/2 multiplexing problems with sparse
registries.

- `[net] retry = 10` raises Cargo's network retry limit from the default
of 2 to 10, giving more headroom when fetching crates under unreliable
CI network conditions.
- `[http] multiplexing = false` disables HTTP/2 multiplexing, a
well-known workaround for intermittent sparse-registry download errors
that some CI environments trigger.

<details open><summary><h3>Confidence Score: 5/5</h3></summary>

Safe to merge — this is a narrow CI hardening change with no logic or
runtime impact beyond build-time network behavior.

The change only touches `.cargo/config.toml` inside the SWC plugin
subdirectory. Both settings (net.retry and http.multiplexing) are
standard, well-documented Cargo knobs that improve resilience without
altering build outputs, binary behavior, or any application logic.

No files require special attention.
</details>

<details><summary><h3>Important Files Changed</h3></summary>

| Filename | Overview |
|----------|----------|
| packages/next/swc-plugin/.cargo/config.toml | Adds [net] retry=10 and
[http] multiplexing=false to harden Cargo network fetches in CI;
existing alias and target sections are unchanged. |

</details>

<details><summary><h3>Flowchart</h3></summary>

<a href="#gh-light-mode-only">

```mermaid
%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[cargo build] --> B{Fetch crate from sparse registry}
    B -->|HTTP/1.1 only\nmultiplexing=false| C[Download crate]
    C --> D{Success?}
    D -->|Yes| E[Build continues]
    D -->|No - network error| F{retry <= 10?}
    F -->|Yes - increment retry| B
    F -->|No - max retries exceeded| G[Build fails]
```

</a>
<a href="#gh-dark-mode-only">

```mermaid
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
    A[cargo build] --> B{Fetch crate from sparse registry}
    B -->|HTTP/1.1 only\nmultiplexing=false| C[Download crate]
    C --> D{Success?}
    D -->|Yes| E[Build continues]
    D -->|No - network error| F{retry <= 10?}
    F -->|Yes - increment retry| B
    F -->|No - max retries exceeded| G[Build fails]
```

</a>
</details>

<sub>Reviews (1): Last reviewed commit: ["fix: harden swc plugin cargo
fetches"](71c436a)
| [Re-trigger
Greptile](https://app.greptile.com/api/retrigger?id=40219079)</sub>

<!-- /greptile_comment -->
## Summary
- add gt-next to transpilePackages in withGTConfig so package runtime
config reads are compiled with the app env values
- preserve existing user transpilePackages entries

## Testing
- pnpm --filter gt-next test:js
- pnpm --filter gt-next typecheck
- pnpm --filter gt-next build
- packed gt-next tarball and verified next-pages-router SSR/browser
renders all LocaleSelector options with the package install
## Summary
- Add `gt-next/link` as a client wrapper around `next/link`.
- Prefix internal hrefs with the active `useLocale()` value, or an
explicit `locale` prop.
- Match Next.js `locale` prop shape: `string | false | undefined`;
`false` opts out of GT locale prefixing.
- Leave external hrefs unchanged.

## Testing
- `pnpm --filter gt-next typecheck`
- `pnpm --filter gt-next build:no-swc-plugin`
- `pnpm --filter gt-next test:js`
- `pnpm format`
- `pnpm lint`
- Linked `base/next-app-router-locale-routing` from `gt-test-apps` to
this worktree and production-verified `locale={false}` renders `/plain`
while default locale rendering produces `/fr`.
- Previously browser-verified dev and production internal/external href
behavior plus client navigation.

<!-- greptile_comment -->

<h3>Greptile Summary</h3>

This PR adds a `gt-next/link` subpackage export — a `'use client'`
wrapper around `next/link` that auto-prefixes internal paths with the
active GT locale and passes `locale={false}` to the underlying
`NextLink` to suppress Next.js's own i18n routing.

- Adds `packages/next/src/link/Link.tsx`: a `forwardRef` component with
two code paths — one that reads locale from `useLocale()` at render time
(`LinkWithLocale`), and one that accepts an explicit `locale` prop
(including `false` to opt out). The `localizePath` helper parses paths
with the URL constructor to safely handle query strings and fragments.
- Registers the new `gt-next/link` entry point in `package.json`
exports, `typesVersions`, and the internal path alias map, and adds a
patch-level changeset entry.

<details open><summary><h3>Confidence Score: 5/5</h3></summary>

Safe to merge — the new Link component is additive, isolated behind its
own entry point, and leaves all existing paths untouched.

The change introduces a new, self-contained gt-next/link export. The
path-prefixing logic correctly handles string hrefs, URL objects,
external URLs, protocol-relative URLs, and the locale={false} opt-out.
The 'use client' boundary is correctly placed, the package.json
entry-point additions follow established patterns, and no existing code
is modified.

No files require special attention.
</details>

<details><summary><h3>Important Files Changed</h3></summary>

| Filename | Overview |
|----------|----------|
| packages/next/src/link/Link.tsx | Core locale-aware Link component;
logic for path prefixing, URL object handling, and locale={false}
opt-out is sound. No new bugs found beyond what was flagged in prior
threads. |
| packages/next/src/link.ts | Barrel re-export file; 'use client'
directive is correctly placed first and consistent with Link.tsx. |
| packages/next/package.json | Adds ./link entry to exports,
typesVersions, and path aliases; follows the same pattern as other
existing entry points. |
| .changeset/locale-aware-next-link.md | Standard patch-level changeset
for gt-next; correctly categorized as patch. |

</details>

<details><summary><h3>Flowchart</h3></summary>

<a href="#gh-light-mode-only">

```mermaid
%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A["<Link href locale ...>"] --> B{locale === undefined?}
    B -- yes --> C["LinkWithLocale\n(reads useLocale() hook)"]
    B -- no --> D["renderLink(href, locale)"]
    C --> D

    D --> E["localizeHref(href, locale)"]
    E --> F{locale falsy?}
    F -- yes --> G["return href unchanged"]
    F -- no --> H{href is string?}
    H -- yes --> I["localizePath(href, locale)"]
    H -- no --> J{href.pathname is string?}
    J -- no --> G
    J -- yes --> K["localizePath(href.pathname, locale)"]
    K --> L["{ ...href, pathname: localizedPathname }"]

    I --> M{starts with '/'? not '//'?}
    M -- no --> G
    M -- yes --> N["new URL(href, 'http://localhost')\nprepend /{locale} to pathname\nreturn pathname+search+hash"]

    G --> O["<NextLink href=... locale={false} />"]
    L --> O
    N --> O
```

</a>
<a href="#gh-dark-mode-only">

```mermaid
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
    A["<Link href locale ...>"] --> B{locale === undefined?}
    B -- yes --> C["LinkWithLocale\n(reads useLocale() hook)"]
    B -- no --> D["renderLink(href, locale)"]
    C --> D

    D --> E["localizeHref(href, locale)"]
    E --> F{locale falsy?}
    F -- yes --> G["return href unchanged"]
    F -- no --> H{href is string?}
    H -- yes --> I["localizePath(href, locale)"]
    H -- no --> J{href.pathname is string?}
    J -- no --> G
    J -- yes --> K["localizePath(href.pathname, locale)"]
    K --> L["{ ...href, pathname: localizedPathname }"]

    I --> M{starts with '/'? not '//'?}
    M -- no --> G
    M -- yes --> N["new URL(href, 'http://localhost')\nprepend /{locale} to pathname\nreturn pathname+search+hash"]

    G --> O["<NextLink href=... locale={false} />"]
    L --> O
    N --> O
```

</a>
</details>

<sub>Reviews (2): Last reviewed commit: ["feat: add locale-aware next
link"](c496746)
| [Re-trigger
Greptile](https://app.greptile.com/api/retrigger?id=40185193)</sub>

<!-- /greptile_comment -->
Merges the latest `main` (through #1654, `7b87c5efd`) into `odysseus` to
bring the 8.x release-line fixes into the next-major WIP branch.

## Summary

44 commits from `main` were merged. Most conflicts were mechanical
version/changelog conflicts; a handful of source conflicts required
manual resolution against the odysseus refactors.

## Conflict resolution

**Mechanical (version bumps):**
- `package.json` (all packages): kept odysseus prerelease versions (e.g.
`9.0.0-odysseus.0`), discarding main's 8.x bumps.
- `CHANGELOG.md` (all packages): union — odysseus prerelease entry on
top, followed by main's released entries.
- `pnpm-lock.yaml`: auto-merged; verified consistent with `pnpm install
--frozen-lockfile`.

**Source:**
- `packages/next/src/request/utils/getRequestFunction.ts` — deleted in
odysseus (refactor #1593); main only reworded a doc comment. Kept
deleted.
-
`packages/react-native/src/provider/hooks/locale/useDetermineLocale.ts`
— deleted in odysseus (provider/hooks refactor); main switched a
hardcoded cookie name to a constant. Kept deleted.
- `packages/react-native/src/utils/nativeStore.ts` — kept odysseus
naming (`defaultLocaleStoreKey` from `react-core/pure`).
- `packages/react-native/src/index.tsx` — kept odysseus's restructured
exports; main's additions (`getLocaleFromNativeStore`, `RelativeTime`)
were already present.
- `packages/supported-locales/src/__tests__/supportedLocales.test.ts` —
took main's version to keep the new `ar-OM` (Omani Arabic) test; `ar-OM`
is already in odysseus's locale list.

## Verification

- `pnpm install --frozen-lockfile` — lockfile consistent.
- Built `gt-react-native`, `gt-next`,
`@generaltranslation/supported-locales`,
`@generaltranslation/react-core` — all pass.
- Tests pass: supported-locales 3/3 (incl. ar-OM), react-native 10/10,
gt-next 295/295.
- No leftover conflict markers; deleted files have no remaining
references.

<!-- codesmith:footer -->
---
<a
href="https://app.blacksmith.sh/generaltranslation/codesmith/gt/pr/1665"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://pr-comments-assets.blacksmith.sh/codesmith/view-with-codesmith-dark-v2.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://pr-comments-assets.blacksmith.sh/codesmith/view-with-codesmith-light-v2.svg"><img
alt="View with Codesmith"
src="https://pr-comments-assets.blacksmith.sh/codesmith/view-with-codesmith-dark-v2.svg"></picture></a>
<a
href="https://backend.blacksmith.sh/track/enable-autofix?expires=1785111447&installation_id=127936663&pr_number=1665&repository=generaltranslation%2Fgt&return_to=https%3A%2F%2Fgithub.com%2Fgeneraltranslation%2Fgt%2Fpull%2F1665&signature=ee6cace58649fbf40d963c6b6627124ad87e2ba0e747667e605fa283fa3cba39"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://pr-comments-assets.blacksmith.sh/codesmith/autofix-with-codesmith-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://pr-comments-assets.blacksmith.sh/codesmith/autofix-with-codesmith-light.svg"><img
alt="Autofix with Codesmith"
src="https://pr-comments-assets.blacksmith.sh/codesmith/autofix-with-codesmith-dark.svg"></picture></a>
<sup>Need help on this PR? Tag <code>/codesmith</code> with what you
need. Autofix is disabled.</sup>

<!-- codesmith:autofix:disabled -->
<!-- /codesmith:footer -->

<!-- greptile_comment -->

<h3>Greptile Summary</h3>

This is a `main → odysseus` integration merge bringing 44 commits of 8.x
fixes into the next-major WIP branch. Conflict resolutions were
mechanical (versions, changelogs) or straightforward
(deleted-in-odysseus files kept deleted). The substantive code changes
include: authentication header format migration to `Bearer` tokens, 429
rate-limit retry support in `core`, an upload-command refactor that
reuses `aggregateFiles`, a pre-enqueue deduplication filter, and a
corrected `experimentalClearLocaleDirs` trigger point.

- **Auth header rename**: `x-gt-project-id` → `gt-project-id` and all
API key headers collapsed into `Authorization: Bearer <key>` in both
`packages/core` and `packages/cli`, removing the internal key prefix
branch.
- **Rate-limit retry** (`apiRequest.ts`): 429 responses are now retried
with delay derived from `Retry-After` / `RateLimit-Reset` headers,
falling back to a 60-second constant.
- **Upload refactor** (`upload.ts`): file parsing and publish-map
construction delegated to `aggregateFiles`, eliminating ~130 lines of
duplicated per-format logic; the new `filterFilesForEnqueue` utility
pre-checks completed translations to skip redundant enqueue calls.

<details open><summary><h3>Confidence Score: 4/5</h3></summary>

Safe to merge — the conflict resolutions are mechanical and the
substantive code changes are well-tested and internally consistent.

All 44 merged commits resolve cleanly. The auth header migration is
applied identically in both `core` and `cli`. The pre-enqueue filter,
upload refactor, and download-clear-dir reordering each have dedicated
tests and pass the full suites. No deleted-file references remain. The
one area worth a second look is the `logEnqueueResult` count when only a
subset of files are locally skipped versus server-confirmed — the
displayed count narrows to the server-checked subset, which is
technically correct but may surprise users who see 'All 2 files already
translated' after being told 3 were skipped locally.

No files require special attention.
`packages/cli/src/workflows/enqueue.ts` and `download.ts` are the most
behaviorally changed files and are worth a quick read-through.
</details>

<details><summary><h3>Important Files Changed</h3></summary>

| Filename | Overview |
|----------|----------|
| packages/core/src/translate/utils/apiRequest.ts | Adds 429 retry with
Retry-After/RateLimit-Reset header parsing; logic is correct and
well-structured with appropriate fallback constant. |
| packages/core/src/translate/utils/generateRequestHeaders.ts | Auth
header migration to Bearer token and gt-project-id; coordinated
consistently with CLI headers.ts change. |
| packages/cli/src/utils/headers.ts | CLI-side auth header migration
matching core; internal key prefix logic removed alongside the x-gt-*
header names. |
| packages/cli/src/cli/commands/upload.ts | Refactored to delegate file
aggregation to aggregateFiles(); removes ~130 lines of duplicated
per-format parsing logic; correctly guards empty-file case with early
return. |
| packages/cli/src/workflows/utils/filterFilesForEnqueue.ts | New
utility that pre-checks completed translations before enqueue; correctly
fails open on API error and handles force flag; well-tested. |
| packages/cli/src/workflows/utils/queryCompletedTranslations.ts | New
shared utility for querying and keying completed translations;
eliminates inline key-string construction in PollJobsStep. |
| packages/cli/src/workflows/download.ts | clearLocaleDirs now fires
after polling (using fileTracker.completed) rather than before (using
raw fileQueryData); staging path still uses inline key format instead of
getFileTranslationKey. |
| packages/cli/src/workflows/steps/EnqueueStep.ts | Correctly
short-circuits when files is empty (no spinner, no API call);
spinnerStarted flag prevents stop() without a matching start(). |
| packages/cli/src/utils/persistPostprocessHashes.ts | Properly resolves
branchId from downloadedMeta before reading the lockfile, fixing a
missing-context issue; returns early if no branchId is found. |
| packages/cli/src/workflows/enqueue.ts | Adds filterFilesForEnqueue
before EnqueueStep.run; logEnqueueResult fileCount logic correctly uses
total file count when all are skipped but uses filtered count otherwise.
|
| packages/supported-locales/src/supportedLocales.ts | Adds ar-OM (Omani
Arabic) to the supported locale list; tested by the newly merged test. |
| packages/cli/src/cli/base.ts | Simplifies handleUploadCommand by
removing dataFormat derivation and file decomposition; delegates
entirely to upload(settings). |
| packages/cli/src/formats/files/aggregateFiles.ts | Removes the inline
empty-file error log (now in callers); remaining changes are from the
main merge and look correct. |

</details>

<details><summary><h3>Sequence Diagram</h3></summary>

<a href="#gh-light-mode-only">

```mermaid
%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
    participant CLI
    participant aggregateFiles
    participant filterFilesForEnqueue
    participant GT as GT API
    participant EnqueueStep
    participant DownloadWorkflow

    CLI->>aggregateFiles: aggregateFiles(settings)
    aggregateFiles-->>CLI: "{files, publishMap}"
    CLI->>filterFilesForEnqueue: "filterFilesForEnqueue({gt, files, locales, force})"
    filterFilesForEnqueue->>GT: "queryFileData({translatedFiles})"
    GT-->>filterFilesForEnqueue: "{translatedFiles: [...completed]}"
    filterFilesForEnqueue-->>CLI: "{filesToEnqueue, skippedFiles}"
    CLI->>EnqueueStep: run(filesToEnqueue)
    alt filesToEnqueue is empty
        EnqueueStep-->>CLI: "{jobData:{}, message:No files need to be enqueued}"
    else files to enqueue
        EnqueueStep->>GT: enqueueFiles(filesToEnqueue)
        GT-->>EnqueueStep: EnqueueFilesResult
        EnqueueStep-->>CLI: EnqueueFilesResult
    end
    CLI->>DownloadWorkflow: "runDownloadWorkflow({fileVersionData, jobData, branchData})"
    DownloadWorkflow->>GT: Poll/queryFileData (PollJobsStep)
    GT-->>DownloadWorkflow: completedTranslations
    DownloadWorkflow->>DownloadWorkflow: clearCompletedLocaleDirs (if experimentalClearLocaleDirs)
    DownloadWorkflow->>GT: Download completed files
    GT-->>DownloadWorkflow: file content
    DownloadWorkflow-->>CLI: success/failure
```

</a>
<a href="#gh-dark-mode-only">

```mermaid
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
    participant CLI
    participant aggregateFiles
    participant filterFilesForEnqueue
    participant GT as GT API
    participant EnqueueStep
    participant DownloadWorkflow

    CLI->>aggregateFiles: aggregateFiles(settings)
    aggregateFiles-->>CLI: "{files, publishMap}"
    CLI->>filterFilesForEnqueue: "filterFilesForEnqueue({gt, files, locales, force})"
    filterFilesForEnqueue->>GT: "queryFileData({translatedFiles})"
    GT-->>filterFilesForEnqueue: "{translatedFiles: [...completed]}"
    filterFilesForEnqueue-->>CLI: "{filesToEnqueue, skippedFiles}"
    CLI->>EnqueueStep: run(filesToEnqueue)
    alt filesToEnqueue is empty
        EnqueueStep-->>CLI: "{jobData:{}, message:No files need to be enqueued}"
    else files to enqueue
        EnqueueStep->>GT: enqueueFiles(filesToEnqueue)
        GT-->>EnqueueStep: EnqueueFilesResult
        EnqueueStep-->>CLI: EnqueueFilesResult
    end
    CLI->>DownloadWorkflow: "runDownloadWorkflow({fileVersionData, jobData, branchData})"
    DownloadWorkflow->>GT: Poll/queryFileData (PollJobsStep)
    GT-->>DownloadWorkflow: completedTranslations
    DownloadWorkflow->>DownloadWorkflow: clearCompletedLocaleDirs (if experimentalClearLocaleDirs)
    DownloadWorkflow->>GT: Download completed files
    GT-->>DownloadWorkflow: file content
    DownloadWorkflow-->>CLI: success/failure
```

</a>
</details>

<sub>Reviews (1): Last reviewed commit: ["chore: fix changelog
blank-line seams
fr..."](55735b8)
| [Re-trigger
Greptile](https://app.greptile.com/api/retrigger?id=40229317)</sub>

<!-- /greptile_comment -->

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Ernest McCarter <ernest@generaltranslation.com>
Co-authored-by: fernando-aviles <fernando@generaltranslation.com>
Co-authored-by: Brian Lou <69982825+brian-lou@users.noreply.github.com>
## Summary
- Replace the static size-limit JSON with a CJS config that covers
current package entrypoints.
- Include node-platform sizing for server/tooling entrypoints.
- Build all packages required by the size checks in CI.

## Testing
- `pnpm exec oxfmt --check .size-limit.cjs
.github/workflows/size-limit.yml pnpm-workspace.yaml`
- `pnpm size`

<!-- codesmith:footer -->
---
<a
href="https://app.blacksmith.sh/generaltranslation/codesmith/gt/pr/1663"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://pr-comments-assets.blacksmith.sh/codesmith/view-with-codesmith-dark-v2.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://pr-comments-assets.blacksmith.sh/codesmith/view-with-codesmith-light-v2.svg"><img
alt="View with Codesmith"
src="https://pr-comments-assets.blacksmith.sh/codesmith/view-with-codesmith-dark-v2.svg"></picture></a>
<a
href="https://backend.blacksmith.sh/track/enable-autofix?expires=1785109053&installation_id=127936663&pr_number=1663&repository=generaltranslation%2Fgt&return_to=https%3A%2F%2Fgithub.com%2Fgeneraltranslation%2Fgt%2Fpull%2F1663&signature=bce34eba9527123b50613052fce8be6454f1a6e494b97bb84b890100097617d3"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://pr-comments-assets.blacksmith.sh/codesmith/autofix-with-codesmith-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://pr-comments-assets.blacksmith.sh/codesmith/autofix-with-codesmith-light.svg"><img
alt="Autofix with Codesmith"
src="https://pr-comments-assets.blacksmith.sh/codesmith/autofix-with-codesmith-dark.svg"></picture></a>
<sup>Need help on this PR? Tag <code>/codesmith</code> with what you
need. Autofix is disabled.</sup>

<!-- codesmith:autofix:disabled -->
<!-- /codesmith:footer -->

<!-- greptile_comment -->

<h3>Greptile Summary</h3>

Replaces the five-entry static `.size-limit.json` with a CJS config that
covers all current package entrypoints, and updates the CI workflow to
build every package the new config references.

- The new `.size-limit.cjs` uses composable helper functions (`entry`,
`nodeEntry`, and per-package wrappers) to register browser and
server/tooling entrypoints with the correct esbuild platform, brotli
flag, and peer-dependency ignore lists across `generaltranslation`,
`@generaltranslation/format`, `gt-i18n`,
`@generaltranslation/react-core`, `gt-react`, `gt-next`, `gt-node`,
`gt-tanstack-start`, and `gt-react-native`.
- The workflow's `Build packages` step now adds
`@generaltranslation/format`, `gt-node`, and `gt-react-native` to the
turbo filter so the freshly added entrypoints can be measured.

<details open><summary><h3>Confidence Score: 4/5</h3></summary>

Safe to merge; the change is limited to CI infrastructure and
bundle-size measurement configuration with no effect on runtime code.

The RSC entrypoint for `gt-react` is measured with the browser esbuild
platform while the parallel `gt-next (rsc)` entrypoint uses the node
platform. If `index.rsc.mjs` imports Node.js built-ins, the size
measurement will be inaccurate, but this only affects the accuracy of
the size check rather than any production behaviour.

`.size-limit.cjs` around the `gt-react (rsc)` entry — verify whether a
browser or node esbuild target is intended for `index.rsc.mjs`.
</details>

<details><summary><h3>Important Files Changed</h3></summary>

| Filename | Overview |
|----------|----------|
| .size-limit.cjs | New CJS size-limit config covering all package
entrypoints with correct brotli, platform, and peer-ignore settings;
`gt-react (rsc)` uses browser platform instead of node, inconsistent
with how the same RSC entrypoint is handled in `gt-next`. |
| .github/workflows/size-limit.yml | Adds `@generaltranslation/format`,
`gt-node`, and `gt-react-native` to the turbo build filter so all
packages referenced by the expanded size-limit config are built before
the check runs. |
| .size-limit.json | Deleted in favour of the new CJS config; the old
file only tracked 5 entrypoints and referenced
`packages/react-core/dist/index.esm.min.mjs` which has no corresponding
package export. |

</details>

<details><summary><h3>Flowchart</h3></summary>

<a href="#gh-light-mode-only">

```mermaid
%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[CI: size-limit workflow] --> B[pnpm install]
    B --> C[turbo build - all filtered packages]
    C --> D{.size-limit.cjs}
    D --> E[Browser entries\nindex.client / link / rsc*]
    D --> F[Node entries\nindex.server / config / middleware\nindex.rsc / server / plugin]
    E --> G[pnpm size - brotli check]
    F --> G
    G --> H{All within limits?}
    H -- Yes --> I[Pass]
    H -- No --> J[Fail with size diff]
```

</a>
<a href="#gh-dark-mode-only">

```mermaid
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
    A[CI: size-limit workflow] --> B[pnpm install]
    B --> C[turbo build - all filtered packages]
    C --> D{.size-limit.cjs}
    D --> E[Browser entries\nindex.client / link / rsc*]
    D --> F[Node entries\nindex.server / config / middleware\nindex.rsc / server / plugin]
    E --> G[pnpm size - brotli check]
    F --> G
    G --> H{All within limits?}
    H -- Yes --> I[Pass]
    H -- No --> J[Fail with size diff]
```

</a>
</details>

<a
href="https://app.greptile.com/ide/claude-code?prompt=Fix%20the%20following%201%20code%20review%20issue.%20Work%20through%20them%20one%20at%20a%20time%2C%20proposing%20concise%20fixes.%0A%0A---%0A%0A%23%23%23%20Issue%201%20of%201%0A.size-limit.cjs%3A94-96%0A**%60gt-react%20%28rsc%29%60%20measured%20with%20browser%20esbuild%20target**%0A%0A%60gt-react%20%28rsc%29%60%20is%20measured%20using%20the%20plain%20%60react%60%20helper%20%28no%20%60modifyEsbuildConfig%60%29%2C%20which%20defaults%20to%20the%20browser%20platform.%20RSC%20bundles%20run%20on%20the%20server%20and%20may%20import%20Node.js%20built-ins%3B%20if%20%60index.rsc.mjs%60%20does%20so%2C%20esbuild%20will%20emit%20an%20inaccurate%20size%20with%20missing%20polyfills%20or%20spurious%20%22can't%20resolve%22%20warnings.%20Compare%20with%20%60gt-next%20%28rsc%29%60%20which%20correctly%20uses%20%60nextNode%60%20for%20the%20same%20RSC%20entrypoint%20pattern.%20Switching%20to%20%60reactNode%60%20here%20would%20align%20both%20packages.%0A%0A&repo=generaltranslation%2Fgt&pr=1663&platform=github"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://greptile-static-assets.s3.amazonaws.com/badges/FixAllInClaudeDark.svg?v=3"><source
media="(prefers-color-scheme: light)"
srcset="https://greptile-static-assets.s3.amazonaws.com/badges/FixAllInClaude.svg?v=3"><img
alt="Fix All in Claude Code"
src="https://greptile-static-assets.s3.amazonaws.com/badges/FixAllInClaude.svg?v=3"
height="20"></picture></a> <a
href="https://app.greptile.com/api/ide/codex?prompt=IMPORTANT%3A%20Work%20in%20the%20repository%20%22generaltranslation%2Fgt%22%20on%20the%20existing%20branch%20%22e%2Fodysseus%2Fsize-limit-entrypoints%22.%20Checkout%20that%20branch%20%E2%80%94%20do%20NOT%20create%20a%20new%20branch%20or%20open%20a%20new%20PR.%20Push%20your%20changes%20to%20%22e%2Fodysseus%2Fsize-limit-entrypoints%22.%0A%0AFix%20the%20following%201%20code%20review%20issue.%20Work%20through%20them%20one%20at%20a%20time%2C%20proposing%20concise%20fixes.%0A%0A---%0A%0A%23%23%23%20Issue%201%20of%201%0A.size-limit.cjs%3A94-96%0A**%60gt-react%20%28rsc%29%60%20measured%20with%20browser%20esbuild%20target**%0A%0A%60gt-react%20%28rsc%29%60%20is%20measured%20using%20the%20plain%20%60react%60%20helper%20%28no%20%60modifyEsbuildConfig%60%29%2C%20which%20defaults%20to%20the%20browser%20platform.%20RSC%20bundles%20run%20on%20the%20server%20and%20may%20import%20Node.js%20built-ins%3B%20if%20%60index.rsc.mjs%60%20does%20so%2C%20esbuild%20will%20emit%20an%20inaccurate%20size%20with%20missing%20polyfills%20or%20spurious%20%22can't%20resolve%22%20warnings.%20Compare%20with%20%60gt-next%20%28rsc%29%60%20which%20correctly%20uses%20%60nextNode%60%20for%20the%20same%20RSC%20entrypoint%20pattern.%20Switching%20to%20%60reactNode%60%20here%20would%20align%20both%20packages.%0A%0A&repo=generaltranslation%2Fgt&pr=1663&platform=github"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://greptile-static-assets.s3.amazonaws.com/badges/FixAllInCodexDark.svg?v=3"><source
media="(prefers-color-scheme: light)"
srcset="https://greptile-static-assets.s3.amazonaws.com/badges/FixAllInCodex.svg?v=3"><img
alt="Fix All in Codex"
src="https://greptile-static-assets.s3.amazonaws.com/badges/FixAllInCodex.svg?v=3"
height="20"></picture></a>

<details><summary>Prompt To Fix All With AI</summary>

`````markdown
Fix the following 1 code review issue. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 1
.size-limit.cjs:94-96
**`gt-react (rsc)` measured with browser esbuild target**

`gt-react (rsc)` is measured using the plain `react` helper (no `modifyEsbuildConfig`), which defaults to the browser platform. RSC bundles run on the server and may import Node.js built-ins; if `index.rsc.mjs` does so, esbuild will emit an inaccurate size with missing polyfills or spurious "can't resolve" warnings. Compare with `gt-next (rsc)` which correctly uses `nextNode` for the same RSC entrypoint pattern. Switching to `reactNode` here would align both packages.

`````

</details>

<sub>Reviews (1): Last reviewed commit: ["chore: expand size limit
entrypoints"](136c171)
| [Re-trigger
Greptile](https://app.greptile.com/api/retrigger?id=40224466)</sub>

> Greptile also left **1 inline comment** on this PR.

<!-- /greptile_comment -->
## Summary
- Replace the two pending `gt-next` Odysseus changesets with one
consolidated changeset.
- Include explicit PR, commit, and author metadata for the changelog
entry.
- Leave package source and release workflow files unchanged.

## Testing
- `pnpm install`
- Reproduced `pnpm run version-packages` in a temporary Odysseus
worktree with this changeset replacement applied before committing it.

## Notes
- This is a targeted workaround for the current Odysseus release failure
in GitHub changelog metadata generation.

<!-- codesmith:footer -->
---
<a
href="https://app.blacksmith.sh/generaltranslation/codesmith/gt/pr/1667"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://pr-comments-assets.blacksmith.sh/codesmith/view-with-codesmith-dark-v2.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://pr-comments-assets.blacksmith.sh/codesmith/view-with-codesmith-light-v2.svg"><img
alt="View with Codesmith"
src="https://pr-comments-assets.blacksmith.sh/codesmith/view-with-codesmith-dark-v2.svg"></picture></a>
<a
href="https://backend.blacksmith.sh/track/enable-autofix?expires=1785112355&installation_id=127936663&pr_number=1667&repository=generaltranslation%2Fgt&return_to=https%3A%2F%2Fgithub.com%2Fgeneraltranslation%2Fgt%2Fpull%2F1667&signature=71a65aab22b8c8def36e53d96eda06f7152ea899bd40f85abed8acb6e9f43183"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://pr-comments-assets.blacksmith.sh/codesmith/autofix-with-codesmith-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://pr-comments-assets.blacksmith.sh/codesmith/autofix-with-codesmith-light.svg"><img
alt="Autofix with Codesmith"
src="https://pr-comments-assets.blacksmith.sh/codesmith/autofix-with-codesmith-dark.svg"></picture></a>
<sup>Need help on this PR? Tag <code>/codesmith</code> with what you
need. Autofix is disabled.</sup>

<!-- codesmith:autofix:disabled -->
<!-- /codesmith:footer -->
## Summary
- Configure the Odysseus release job to use Changesets' built-in git
changelog instead of `@changesets/changelog-github`.
- Keep the normal `main` release path unchanged.
- Avoid GitHub GraphQL calls during Odysseus `changeset version`, which
is the failing path in the release action.

## Testing
- `pnpm exec oxfmt --check .github/workflows/release.yml`
- In a temporary worktree at current `origin/odysseus`, rewrote
`.changeset/config.json` with the same `jq` expression and ran `pnpm run
version-packages`; it completed successfully.

## Notes
- Odysseus prerelease changelog entries will use local git commit
references instead of GitHub-enriched PR/user links.

<!-- codesmith:footer -->
---
<a
href="https://app.blacksmith.sh/generaltranslation/codesmith/gt/pr/1670"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://pr-comments-assets.blacksmith.sh/codesmith/view-with-codesmith-dark-v2.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://pr-comments-assets.blacksmith.sh/codesmith/view-with-codesmith-light-v2.svg"><img
alt="View with Codesmith"
src="https://pr-comments-assets.blacksmith.sh/codesmith/view-with-codesmith-dark-v2.svg"></picture></a>
<a
href="https://backend.blacksmith.sh/track/enable-autofix?expires=1785114309&installation_id=127936663&pr_number=1670&repository=generaltranslation%2Fgt&return_to=https%3A%2F%2Fgithub.com%2Fgeneraltranslation%2Fgt%2Fpull%2F1670&signature=a48f10e5a5e62e66a055f1ea51d296d0093e8ab4c64d542c46ea1e7707006787"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://pr-comments-assets.blacksmith.sh/codesmith/autofix-with-codesmith-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://pr-comments-assets.blacksmith.sh/codesmith/autofix-with-codesmith-light.svg"><img
alt="Autofix with Codesmith"
src="https://pr-comments-assets.blacksmith.sh/codesmith/autofix-with-codesmith-dark.svg"></picture></a>
<sup>Need help on this PR? Tag <code>/codesmith</code> with what you
need. Autofix is disabled.</sup>

<!-- codesmith:autofix:disabled -->
<!-- /codesmith:footer -->

<!-- greptile_comment -->

<h3>Greptile Summary</h3>

This CI-only PR patches the Odysseus prerelease job in `release.yml` to
swap out `@changesets/changelog-github` (which makes GitHub GraphQL API
calls during `changeset version`) for the built-in
`@changesets/cli/changelog` module. The main `release` job targeting
`main` is untouched.

- The `jq` expression now chains `.baseBranch = "odysseus" | .changelog
= "@changesets/cli/changelog"`, correctly replacing both fields in the
temporary `config.json` mutation. The built-in changelog is the default
generated by `changeset init` and is confirmed valid when the package is
locally installed (as it is via `pnpm install` earlier in the job).
- Odysseus changelog entries will use local git commit references
instead of enriched GitHub PR/user links, which is an acceptable
trade-off for unblocking the prerelease pipeline.

<details open><summary><h3>Confidence Score: 5/5</h3></summary>

Safe to merge; the change is limited to a one-line CI config mutation in
the Odysseus prerelease job and does not touch any production code or
the main release path.

The jq expression is correct chained syntax, `@changesets/cli/changelog`
is the confirmed default built-in module (installed locally via `pnpm
install` earlier in the job), the temporary config mutation pattern is
unchanged from before, and the main release job is completely
unaffected.

No files require special attention.
</details>

<details><summary><h3>Important Files Changed</h3></summary>

| Filename | Overview |
|----------|----------|
| .github/workflows/release.yml | Single-line change to the Odysseus jq
config mutation; adds `.changelog = "@changesets/cli/changelog"`
alongside the existing `.baseBranch = "odysseus"` assignment. No issues
found. |

</details>

<details><summary><h3>Sequence Diagram</h3></summary>

<a href="#gh-light-mode-only">

```mermaid
%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
    participant GHA as GitHub Actions
    participant JQ as jq (config patch)
    participant CS as changesets/action@v1
    participant CLI as @changesets/cli
    participant CL as @changesets/cli/changelog
    participant NPM as npm registry

    GHA->>JQ: "set baseBranch=odysseus + changelog=@changesets/cli/changelog"
    JQ-->>GHA: patched config.json (temp)
    GHA->>CS: version: pnpm run version-packages
    CS->>CLI: changeset version
    CLI->>CL: getReleaseLine() (git-only, no GraphQL)
    CL-->>CLI: commit-based changelog entries
    CLI-->>CS: CHANGELOGs + bumped versions
    CS->>GHA: open/update release PR
    GHA->>CS: publish: pnpm run release
    CS->>NPM: publish odysseus prerelease packages
```

</a>
<a href="#gh-dark-mode-only">

```mermaid
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
    participant GHA as GitHub Actions
    participant JQ as jq (config patch)
    participant CS as changesets/action@v1
    participant CLI as @changesets/cli
    participant CL as @changesets/cli/changelog
    participant NPM as npm registry

    GHA->>JQ: "set baseBranch=odysseus + changelog=@changesets/cli/changelog"
    JQ-->>GHA: patched config.json (temp)
    GHA->>CS: version: pnpm run version-packages
    CS->>CLI: changeset version
    CLI->>CL: getReleaseLine() (git-only, no GraphQL)
    CL-->>CLI: commit-based changelog entries
    CLI-->>CS: CHANGELOGs + bumped versions
    CS->>GHA: open/update release PR
    GHA->>CS: publish: pnpm run release
    CS->>NPM: publish odysseus prerelease packages
```

</a>
</details>

<sub>Reviews (1): Last reviewed commit: ["ci: avoid github changelog for
odysseus
..."](5bd34b2)
| [Re-trigger
Greptile](https://app.greptile.com/api/retrigger?id=40235893)</sub>

<!-- /greptile_comment -->
## What

Removes orphaned packages from `pnpm-lock.yaml` that no workspace
manifest references:
`arktype`, `@ark/schema`, `@ark/util`, `arkregex`,
`@react-native/new-app-screen`, `@react-native/typescript-config` (plus
the incidental peer-dependency hash reshuffles pnpm emits when
re-resolving).

## Why

These are leftovers from a prior dependency removal. `pnpm install
--frozen-lockfile` (what CI runs) validates that the lockfile
*satisfies* every `package.json`, but it does **not** prune orphan
entries — so they sat in the committed lockfile passing CI until a
non-frozen `pnpm install` garbage-collected them.

Verified:
- No `package.json` under `packages/*`, `tests/apps/**`, or `examples/*`
references any of these packages.
- `pnpm install --frozen-lockfile` passes against the pruned lockfile.

## Note

Pure lockfile hygiene — no source or `package.json` changes. Split out
from the gt-react/internal removal PR so that change's diff stays
focused.

<!-- codesmith:footer -->
---
<a
href="https://app.blacksmith.sh/generaltranslation/codesmith/gt/pr/1671"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://pr-comments-assets.blacksmith.sh/codesmith/view-with-codesmith-dark-v2.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://pr-comments-assets.blacksmith.sh/codesmith/view-with-codesmith-light-v2.svg"><img
alt="View with Codesmith"
src="https://pr-comments-assets.blacksmith.sh/codesmith/view-with-codesmith-dark-v2.svg"></picture></a>
<a
href="https://backend.blacksmith.sh/track/enable-autofix?expires=1785114467&installation_id=127936663&pr_number=1671&repository=generaltranslation%2Fgt&return_to=https%3A%2F%2Fgithub.com%2Fgeneraltranslation%2Fgt%2Fpull%2F1671&signature=642ad62cf989b1ac3bc17b516b1f1cd49e54aeae0f60170ac9489cafd64c808f"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://pr-comments-assets.blacksmith.sh/codesmith/autofix-with-codesmith-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://pr-comments-assets.blacksmith.sh/codesmith/autofix-with-codesmith-light.svg"><img
alt="Autofix with Codesmith"
src="https://pr-comments-assets.blacksmith.sh/codesmith/autofix-with-codesmith-dark.svg"></picture></a>
<sup>Need help on this PR? Tag <code>/codesmith</code> with what you
need. Autofix is disabled.</sup>

<!-- codesmith:autofix:disabled -->
<!-- /codesmith:footer -->
This PR was opened by the [Changesets
release](https://github.com/changesets/action) GitHub action. When
you're ready to do a release, you can merge this and the packages will
be published to npm automatically. If you're not ready to do a release
yet, that's fine, whenever you add more changesets to odysseus, this PR
will be updated.

⚠️⚠️⚠️⚠️⚠️⚠️

`odysseus` is currently in **pre mode** so this branch has prereleases
rather than normal releases. If you want to exit prereleases, run
`changeset pre exit` on `odysseus`.

⚠️⚠️⚠️⚠️⚠️⚠️

# Releases
## gt-next@7.0.0-odysseus.3

### Patch Changes

- [#1659](#1659)
[`807dc30`](807dc30)
Thanks [@ErnestM1234](https://github.com/ErnestM1234)! - Add a
locale-aware Link component for Next.js applications.

Transpile gt-next in Next apps so package runtime config is compiled
with app env values. See #1661.
## @generaltranslation/gt-next-lint@15.0.0-odysseus.3

### Patch Changes

- Updated dependencies
[[`10e064c`](10e064c)]:
  - gt-next@7.0.0-odysseus.3

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
> **Stacked on #1671** (`chore: prune stale lockfile entries`).
Review/merge that first; this PR's base will be retargeted to `odysseus`
once it lands, leaving only the changes below.

## What

`gt-react/internal` was a wholesale-deprecated re-export facade (62
symbols, all marked `@deprecated`, "use public gt-react exports
instead"). `gt-next` was its **only** remaining consumer (12 source
files). This removes the facade and migrates gt-next to the canonical
source.

- **Migrate 12 gt-next files** off `gt-react/internal` →
`@generaltranslation/react-core/pure` (where the facade itself sourced
every one of these symbols), with correct value/`type` import splits.
Added `@generaltranslation/react-core` as a direct dependency of gt-next
(it was transitive).
- **Update 3 test mocks** that targeted `gt-react/internal` (edge
middleware tests + `NextI18nCache`) to mock
`@generaltranslation/react-core/pure` instead; drop the stale
`gt-react/internal` entry from the `rscLeaks` source map and the
`react-package` export assertions.
- **Delete `packages/react/src/internal.ts`** and remove its wiring:
`package.json` `exports`/`typesVersions`/`compilerOptions` paths and the
`tsdown.config.mts` entry.
- **Fix gt-react's own `createBrowserConditionStore`**, which reached
through `../internal` for cookie names → now imports from
`../cookie-names` directly.

## Verification

- Builds clean: `react-core`, `gt-react`, `gt-next`.
- Tests pass: `gt-react` 12, `gt-next` 191 JS + 295 swc/rust.
- Lint passes; zero `gt-react/internal` references remain in
code/config.

Net **−497 / +23** lines (excluding the lockfile entry).

<!-- codesmith:footer -->
---
<a
href="https://app.blacksmith.sh/generaltranslation/codesmith/gt/pr/1672"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://pr-comments-assets.blacksmith.sh/codesmith/view-with-codesmith-dark-v2.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://pr-comments-assets.blacksmith.sh/codesmith/view-with-codesmith-light-v2.svg"><img
alt="View with Codesmith"
src="https://pr-comments-assets.blacksmith.sh/codesmith/view-with-codesmith-dark-v2.svg"></picture></a>
<a
href="https://backend.blacksmith.sh/track/enable-autofix?expires=1785114502&installation_id=127936663&pr_number=1672&repository=generaltranslation%2Fgt&return_to=https%3A%2F%2Fgithub.com%2Fgeneraltranslation%2Fgt%2Fpull%2F1672&signature=bc8c0a947ecf3b109bef9efc2e458a52d351a067e22a8b71850df6cdbd577ac7"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://pr-comments-assets.blacksmith.sh/codesmith/autofix-with-codesmith-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://pr-comments-assets.blacksmith.sh/codesmith/autofix-with-codesmith-light.svg"><img
alt="Autofix with Codesmith"
src="https://pr-comments-assets.blacksmith.sh/codesmith/autofix-with-codesmith-dark.svg"></picture></a>
<sup>Need help on this PR? Tag <code>/codesmith</code> with what you
need. Autofix is disabled.</sup>

<!-- codesmith:autofix:disabled -->
<!-- /codesmith:footer -->

<!-- greptile_comment -->

<h3>Greptile Summary</h3>

This PR removes the deprecated `gt-react/internal` re-export facade (451
lines, 62 symbols all marked `@deprecated`) and migrates its sole
remaining consumer (`gt-next`) directly to the canonical
`@generaltranslation/react-core/pure` package. A bonus fix corrects a
self-referential import in `createBrowserConditionStore.ts`, which was
reaching through `../internal` for cookie-name constants it could have
obtained from `../cookie-names` directly.

- **12 gt-next source files** updated: `gt-react/internal` →
`@generaltranslation/react-core/pure`, with proper `import type` splits
for every symbol that is only needed as a type.
- **3 test mocks** retargeted to `@generaltranslation/react-core/pure`;
stale `gt-react/internal` entry removed from the `rscLeaks` source map
and the `react-package` export assertions.
- **Facade deleted in full**: `packages/react/src/internal.ts`, its
`package.json` `exports`/`typesVersions`/`compilerOptions` wiring, and
the `tsdown.config.mts` entry point are all removed.

<details open><summary><h3>Confidence Score: 5/5</h3></summary>

All changed files are straightforward import-path migrations with no
behavioral changes; builds and test suites are reported green.

Every change is a mechanical substitution of one import path for
another, verified by the author against a full build and two separate
test suites (191 JS + 295 swc/rust). The `type` qualifiers on newly
type-only imports are correct throughout. The
`createBrowserConditionStore.ts` fix removes a subtle intra-package
circular dependency. Nothing here affects runtime logic.

No files require special attention; the migration is uniform and
consistent across all 12 source files.
</details>

<details><summary><h3>Important Files Changed</h3></summary>

| Filename | Overview |
|----------|----------|
| packages/react/src/internal.ts | Deleted entirely — 451-line
deprecated re-export facade removed as planned; no remaining consumers.
|
| packages/react/src/condition-store/createBrowserConditionStore.ts |
Fixed intra-package circular import: cookie-name constants now sourced
from `../cookie-names` directly instead of the now-deleted `../internal`
facade. |
| packages/next/src/i18n-cache/NextI18nCache.ts | Migrated
`mergeDictionaries`, `Dictionary`, and `DictionaryEntry` imports from
`gt-react/internal` to `@generaltranslation/react-core/pure`; type-only
imports correctly tagged. |
| packages/next/src/index.types.ts | Migrated `msg`, `decodeMsg`,
`decodeOptions`, `derive`, `declareVar`, `decodeVars`, `_Messages`,
`mFallback`, and `gtFallback` to `@generaltranslation/react-core/pure`;
`_Messages` correctly changed to a `type` import. |
| packages/next/src/middleware-dir/createNextMiddleware.ts | Single
value import of `defaultLocaleCookieName` redirected to
`@generaltranslation/react-core/pure`; no logic changes. |
| packages/next/src/i18n-cache/__tests__/NextI18nCache.test.ts | Mock
for `mergeDictionaries` retargeted to
`@generaltranslation/react-core/pure`; type-only imports from the same
module need no mock entry. |
| packages/react/package.json | Removed `./internal` from `exports`,
`typesVersions`, and `compilerOptions.paths`; all other export entries
unchanged. |
| packages/next/package.json | Added `@generaltranslation/react-core` as
an explicit direct dependency (was previously transitive via
`gt-react`). |
| packages/react/tsdown.config.mts | Removed `src/internal.ts` from the
build entry list; remaining entries unchanged. |

</details>

<details><summary><h3>Flowchart</h3></summary>

<a href="#gh-light-mode-only">

```mermaid
%%{init: {'theme': 'neutral'}}%%
flowchart TD
    subgraph before["Before (deprecated path)"]
        A["gt-next source files\n(12 files)"] -->|"import from"| B["gt-react/internal\n(facade, 451 lines,\n62 re-exports)"]
        B -->|"re-exports from"| C["@generaltranslation/react-core/pure\n(canonical source)"]
        D["gt-react/src/condition-store/\ncreateBrowserConditionStore.ts"] -->|"import cookie names\nfrom"| B
    end
    subgraph after["After (direct path)"]
        E["gt-next source files\n(12 files)"] -->|"import from"| F["@generaltranslation/react-core/pure\n(canonical source)"]
        G["gt-react/src/condition-store/\ncreateBrowserConditionStore.ts"] -->|"import cookie names\nfrom"| H["gt-react/src/cookie-names.ts"]
        I["gt-react/internal.ts"] -->|"DELETED"| X["❌"]
    end
```

</a>
<a href="#gh-dark-mode-only">

```mermaid
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
    subgraph before["Before (deprecated path)"]
        A["gt-next source files\n(12 files)"] -->|"import from"| B["gt-react/internal\n(facade, 451 lines,\n62 re-exports)"]
        B -->|"re-exports from"| C["@generaltranslation/react-core/pure\n(canonical source)"]
        D["gt-react/src/condition-store/\ncreateBrowserConditionStore.ts"] -->|"import cookie names\nfrom"| B
    end
    subgraph after["After (direct path)"]
        E["gt-next source files\n(12 files)"] -->|"import from"| F["@generaltranslation/react-core/pure\n(canonical source)"]
        G["gt-react/src/condition-store/\ncreateBrowserConditionStore.ts"] -->|"import cookie names\nfrom"| H["gt-react/src/cookie-names.ts"]
        I["gt-react/internal.ts"] -->|"DELETED"| X["❌"]
    end
```

</a>
</details>

<sub>Reviews (1): Last reviewed commit: ["refactor(next): remove
deprecated
gt-rea..."](9ca07e7)
| [Re-trigger
Greptile](https://app.greptile.com/api/retrigger?id=40236172)</sub>

<!-- /greptile_comment -->
## Summary
- Expand the Changesets fixed version group to include react-core, next,
react, react-native, and tanstack-start.
- Keep the existing CLI fixed group unchanged.

## Testing
- `pnpm format -- .changeset/config.json`
- Verified with a temporary local Changesets version run that a future
patch to one package syncs all five related packages to the same
prerelease version.

No changeset added.

<!-- codesmith:footer -->
---
<a
href="https://app.blacksmith.sh/generaltranslation/codesmith/gt/pr/1680"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://pr-comments-assets.blacksmith.sh/codesmith/view-with-codesmith-dark-v2.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://pr-comments-assets.blacksmith.sh/codesmith/view-with-codesmith-light-v2.svg"><img
alt="View with Codesmith"
src="https://pr-comments-assets.blacksmith.sh/codesmith/view-with-codesmith-dark-v2.svg"></picture></a>
<a
href="https://backend.blacksmith.sh/track/enable-autofix?expires=1785120952&installation_id=127936663&pr_number=1680&repository=generaltranslation%2Fgt&return_to=https%3A%2F%2Fgithub.com%2Fgeneraltranslation%2Fgt%2Fpull%2F1680&signature=1b2e4cdc281afeb25ea4aa19521cda7dd14fc7b3beec917330a29793fcdb8315"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://pr-comments-assets.blacksmith.sh/codesmith/autofix-with-codesmith-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://pr-comments-assets.blacksmith.sh/codesmith/autofix-with-codesmith-light.svg"><img
alt="Autofix with Codesmith"
src="https://pr-comments-assets.blacksmith.sh/codesmith/autofix-with-codesmith-dark.svg"></picture></a>
<sup>Need help on this PR? Tag <code>/codesmith</code> with what you
need. Autofix is disabled.</sup>

<!-- codesmith:autofix:disabled -->
<!-- /codesmith:footer -->
## Summary
- Delete internal barrel-only `index.ts` and barrel `types.ts` files
outside package entrypoints.
- Rewrite imports and entrypoint re-exports to point directly at
defining source files.
- Preserve package entrypoints such as root `src/index.ts` and public
`src/types.ts` exports.
- Add patch changeset `.changeset/remove-source-barrel-exports.md`.

## Testing
- `pnpm build`
- `pnpm --filter @generaltranslation/compiler --filter
generaltranslation --filter gt-i18n --filter gt-node --filter gt-sanity
test`
- `pnpm lint`

<!-- codesmith:footer -->
---
<a
href="https://app.blacksmith.sh/generaltranslation/codesmith/gt/pr/1677"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://pr-comments-assets.blacksmith.sh/codesmith/view-with-codesmith-dark-v2.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://pr-comments-assets.blacksmith.sh/codesmith/view-with-codesmith-light-v2.svg"><img
alt="View with Codesmith"
src="https://pr-comments-assets.blacksmith.sh/codesmith/view-with-codesmith-dark-v2.svg"></picture></a>
<a
href="https://backend.blacksmith.sh/track/enable-autofix?expires=1785119624&installation_id=127936663&pr_number=1677&repository=generaltranslation%2Fgt&return_to=https%3A%2F%2Fgithub.com%2Fgeneraltranslation%2Fgt%2Fpull%2F1677&signature=811838818a55cee8af653538368b8cc8125df83bcb381038594fde160bda29cd"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://pr-comments-assets.blacksmith.sh/codesmith/autofix-with-codesmith-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://pr-comments-assets.blacksmith.sh/codesmith/autofix-with-codesmith-light.svg"><img
alt="Autofix with Codesmith"
src="https://pr-comments-assets.blacksmith.sh/codesmith/autofix-with-codesmith-dark.svg"></picture></a>
<sup>Need help on this PR? Tag <code>/codesmith</code> with what you
need. Autofix is disabled.</sup>

<!-- codesmith:autofix:disabled -->
<!-- /codesmith:footer -->

<!-- greptile_comment -->

<h3>Greptile Summary</h3>

This refactor removes internal barrel-only `index.ts` and `types.ts`
files scattered throughout the monorepo's packages (`cli`, `compiler`,
`core`, `i18n`, `node`, `sanity`) and replaces them with direct imports
pointing at the defining source files. Public package entrypoints
(`src/index.ts`, `src/types.ts`, `src/internal.ts`, `src/fallbacks.ts`)
are preserved and updated in place.

- Barrel files deleted include `packages/i18n/src/i18n-cache/index.ts`,
`packages/node/src/helpers/index.ts`,
`packages/sanity/src/serialization/index.ts`, and 10+ others across the
packages; all call-sites within the repo are updated in the same commit.
- Multi-level re-export chains (e.g. `src/index.ts` →
`translation-functions/derive/index.ts` → `generaltranslation/internal`)
are collapsed into single-hop `export { ... } from
'generaltranslation/internal'` statements.
- Test files are updated in lock-step to import from concrete source
paths, and a search across all packages finds no remaining consumers of
the deleted barrel modules.

<details open><summary><h3>Confidence Score: 5/5</h3></summary>

All barrel files are cleanly removed with every call-site in the repo
updated in lock-step; no export gaps found.

The change is purely structural — no logic is added or removed. A manual
cross-check of every deleted barrel against the rest of the codebase
found no remaining consumers, and the multi-hop re-export chains are
faithfully collapsed into equivalent single-hop exports. Tests and the
public package entrypoints are untouched in semantics.

No files require special attention; all 44 changed files consistently
apply the same mechanical import-path update.
</details>

<details><summary><h3>Important Files Changed</h3></summary>

| Filename | Overview |
|----------|----------|
| packages/i18n/src/internal.ts | Expands two barrel-level wildcard
re-exports into 10+ named direct exports, preserving the exact same
public API surface. |
| packages/i18n/src/index.ts | Replaces barrel re-exports for msg,
derive, fallbacks, and helpers with direct file imports; collapses
derive chain to a single hop from generaltranslation/internal. |
| packages/node/src/index.ts | Expands three barrel re-exports (setup,
translation-functions, helpers) into explicit named exports; all symbols
from the old barrels are accounted for. |
| packages/sanity/src/index.ts | Updates imports of
BaseDocumentSerializer, BaseDocumentDeserializer, BaseDocumentMerger,
and related types to point directly at their defining files after the
serialization barrel is deleted. |
| packages/compiler/src/passes/basePass.ts | Replaces namespace import
`import * as p from '../processing'` with 14 individual named imports
from specific files; functional behavior is identical. |
| packages/core/src/internal.ts | Inlines the seven exports from the
deleted derive/index.ts barrel directly, preserving the same public
surface. |
| packages/sanity/src/serialization/index.ts | Deleted — all prior
consumers within the package have been updated to import from the
concrete source files directly. |
| packages/node/src/types.ts | Inlines six type re-exports from the
deleted translation-functions/types.ts barrel directly from
gt-i18n/types, preserving the full type surface. |

</details>

<details><summary><h3>Flowchart</h3></summary>

<a href="#gh-light-mode-only">

```mermaid
%%{init: {'theme': 'neutral'}}%%
flowchart TD
    subgraph Before["Before: Multi-hop barrel chains"]
        A1["node/src/index.ts"] -->|"export * from"| B1["node/src/translation-functions/index.ts"]
        B1 -->|"export from"| C1["gt-i18n (index)"]
        A1 -->|"export * from"| B2["node/src/helpers/index.ts"]
        B2 -->|"export from"| C1
        A1 -->|"export * from"| B3["node/src/setup/index.ts"]
        B3 -->|"export from"| D1["setup/initializeGT.ts"]

        E1["i18n/src/index.ts"] -->|"export * from"| F1["translation-functions/derive/index.ts"]
        F1 -->|"export from"| G1["generaltranslation/internal"]

        H1["sanity/src/index.ts"] -->|"from"| I1["serialization/index.ts"]
        I1 -->|"export from"| J1["serialization/BaseDocumentMerger.ts"]
    end

    subgraph After["After: Direct single-hop imports"]
        A2["node/src/index.ts"] -->|"export from"| C2["gt-i18n (index)"]
        A2 -->|"export from"| D2["setup/initializeGT.ts"]
        A2 -->|"export from"| D3["setup/withGT.ts"]
        A2 -->|"export from"| E2["helpers/getRequestLocale.ts"]

        F2["i18n/src/index.ts"] -->|"export from"| G2["generaltranslation/internal"]

        H2["sanity/src/index.ts"] -->|"from"| J2["serialization/BaseDocumentMerger.ts"]
        H2 -->|"from"| K2["serialization/serialize/index.ts"]
        H2 -->|"from"| L2["serialization/deserialize/BaseDocumentDeserializer.ts"]
    end
```

</a>
<a href="#gh-dark-mode-only">

```mermaid
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
    subgraph Before["Before: Multi-hop barrel chains"]
        A1["node/src/index.ts"] -->|"export * from"| B1["node/src/translation-functions/index.ts"]
        B1 -->|"export from"| C1["gt-i18n (index)"]
        A1 -->|"export * from"| B2["node/src/helpers/index.ts"]
        B2 -->|"export from"| C1
        A1 -->|"export * from"| B3["node/src/setup/index.ts"]
        B3 -->|"export from"| D1["setup/initializeGT.ts"]

        E1["i18n/src/index.ts"] -->|"export * from"| F1["translation-functions/derive/index.ts"]
        F1 -->|"export from"| G1["generaltranslation/internal"]

        H1["sanity/src/index.ts"] -->|"from"| I1["serialization/index.ts"]
        I1 -->|"export from"| J1["serialization/BaseDocumentMerger.ts"]
    end

    subgraph After["After: Direct single-hop imports"]
        A2["node/src/index.ts"] -->|"export from"| C2["gt-i18n (index)"]
        A2 -->|"export from"| D2["setup/initializeGT.ts"]
        A2 -->|"export from"| D3["setup/withGT.ts"]
        A2 -->|"export from"| E2["helpers/getRequestLocale.ts"]

        F2["i18n/src/index.ts"] -->|"export from"| G2["generaltranslation/internal"]

        H2["sanity/src/index.ts"] -->|"from"| J2["serialization/BaseDocumentMerger.ts"]
        H2 -->|"from"| K2["serialization/serialize/index.ts"]
        H2 -->|"from"| L2["serialization/deserialize/BaseDocumentDeserializer.ts"]
    end
```

</a>
</details>

<sub>Reviews (1): Last reviewed commit: ["refactor: remove internal
barrel
exports"](1e56780)
| [Re-trigger
Greptile](https://app.greptile.com/api/retrigger?id=40241819)</sub>

<!-- /greptile_comment -->
This PR was opened by the [Changesets
release](https://github.com/changesets/action) GitHub action. When
you're ready to do a release, you can merge this and the packages will
be published to npm automatically. If you're not ready to do a release
yet, that's fine, whenever you add more changesets to odysseus, this PR
will be updated.

⚠️⚠️⚠️⚠️⚠️⚠️

`odysseus` is currently in **pre mode** so this branch has prereleases
rather than normal releases. If you want to exit prereleases, run
`changeset pre exit` on `odysseus`.

⚠️⚠️⚠️⚠️⚠️⚠️

# Releases
## gt@2.14.51-odysseus.1

### Patch Changes

- [#1677](#1677)
[`87d6320`](87d6320)
Thanks [@ErnestM1234](https://github.com/ErnestM1234)! - Remove internal
source barrel exports and update imports to reference defining files
directly.

- Updated dependencies
[[`87d6320`](87d6320)]:
  - generaltranslation@9.0.0-odysseus.1
  - @generaltranslation/python-extractor@0.2.23-odysseus.1
  - @generaltranslation/supported-locales@2.1.2-odysseus.1
## @generaltranslation/compiler@1.3.25-odysseus.1

### Patch Changes

- [#1677](#1677)
[`87d6320`](87d6320)
Thanks [@ErnestM1234](https://github.com/ErnestM1234)! - Remove internal
source barrel exports and update imports to reference defining files
directly.

- Updated dependencies
[[`87d6320`](87d6320)]:
  - generaltranslation@9.0.0-odysseus.1
## generaltranslation@9.0.0-odysseus.1

### Patch Changes

- [#1677](#1677)
[`87d6320`](87d6320)
Thanks [@ErnestM1234](https://github.com/ErnestM1234)! - Remove internal
source barrel exports and update imports to reference defining files
directly.
## gtx-cli@2.14.51-odysseus.1

### Patch Changes

- Updated dependencies
[[`87d6320`](87d6320)]:
  - gt@2.14.51-odysseus.1
## gt-i18n@1.0.0-odysseus.1

### Patch Changes

- [#1677](#1677)
[`87d6320`](87d6320)
Thanks [@ErnestM1234](https://github.com/ErnestM1234)! - Remove internal
source barrel exports and update imports to reference defining files
directly.

- Updated dependencies
[[`87d6320`](87d6320)]:
  - generaltranslation@9.0.0-odysseus.1
  - @generaltranslation/supported-locales@2.1.2-odysseus.1
## locadex@1.0.186-odysseus.1

### Patch Changes

- Updated dependencies
[[`87d6320`](87d6320)]:
  - gt@2.14.51-odysseus.1
## gt-next@11.0.0-odysseus.4

### Patch Changes

- Updated dependencies
[[`87d6320`](87d6320)]:
  - @generaltranslation/compiler@1.3.25-odysseus.1
  - generaltranslation@9.0.0-odysseus.1
  - gt-i18n@1.0.0-odysseus.1
  - gt-react@11.0.0-odysseus.4
  - @generaltranslation/react-core@11.0.0-odysseus.4
  - @generaltranslation/supported-locales@2.1.2-odysseus.1
## @generaltranslation/gt-next-lint@15.0.0-odysseus.4

### Patch Changes

- Updated dependencies []:
  - gt-next@11.0.0-odysseus.4
## gt-node@1.0.0-odysseus.1

### Patch Changes

- [#1677](#1677)
[`87d6320`](87d6320)
Thanks [@ErnestM1234](https://github.com/ErnestM1234)! - Remove internal
source barrel exports and update imports to reference defining files
directly.

- Updated dependencies
[[`87d6320`](87d6320)]:
  - generaltranslation@9.0.0-odysseus.1
  - gt-i18n@1.0.0-odysseus.1
## @generaltranslation/python-extractor@0.2.23-odysseus.1

### Patch Changes

- Updated dependencies
[[`87d6320`](87d6320)]:
  - generaltranslation@9.0.0-odysseus.1
## gt-react@11.0.0-odysseus.4

### Patch Changes

- Updated dependencies
[[`87d6320`](87d6320)]:
  - generaltranslation@9.0.0-odysseus.1
  - gt-i18n@1.0.0-odysseus.1
  - @generaltranslation/react-core@11.0.0-odysseus.4
  - @generaltranslation/supported-locales@2.1.2-odysseus.1
## @generaltranslation/react-core@11.0.0-odysseus.4

### Patch Changes

- Updated dependencies
[[`87d6320`](87d6320)]:
  - generaltranslation@9.0.0-odysseus.1
  - gt-i18n@1.0.0-odysseus.1
  - @generaltranslation/supported-locales@2.1.2-odysseus.1
## gt-react-native@11.0.0-odysseus.4

### Patch Changes

- Updated dependencies
[[`87d6320`](87d6320)]:
  - generaltranslation@9.0.0-odysseus.1
  - gt-i18n@1.0.0-odysseus.1
  - @generaltranslation/react-core@11.0.0-odysseus.4
  - @generaltranslation/supported-locales@2.1.2-odysseus.1
## gt-sanity@2.0.18-odysseus.3

### Patch Changes

- [#1677](#1677)
[`87d6320`](87d6320)
Thanks [@ErnestM1234](https://github.com/ErnestM1234)! - Remove internal
source barrel exports and update imports to reference defining files
directly.

- Updated dependencies
[[`87d6320`](87d6320)]:
  - generaltranslation@9.0.0-odysseus.1
## @generaltranslation/supported-locales@2.1.2-odysseus.1

### Patch Changes

- Updated dependencies
[[`87d6320`](87d6320)]:
  - generaltranslation@9.0.0-odysseus.1
## gt-tanstack-start@11.0.0-odysseus.4

### Patch Changes

- Updated dependencies
[[`87d6320`](87d6320)]:
  - generaltranslation@9.0.0-odysseus.1
  - gt-i18n@1.0.0-odysseus.1
  - gt-react@11.0.0-odysseus.4
  - @generaltranslation/react-core@11.0.0-odysseus.4

<!-- codesmith:footer -->
---
<a
href="https://app.blacksmith.sh/generaltranslation/codesmith/gt/pr/1682"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://pr-comments-assets.blacksmith.sh/codesmith/view-with-codesmith-dark-v2.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://pr-comments-assets.blacksmith.sh/codesmith/view-with-codesmith-light-v2.svg"><img
alt="View with Codesmith"
src="https://pr-comments-assets.blacksmith.sh/codesmith/view-with-codesmith-dark-v2.svg"></picture></a>
<a
href="https://backend.blacksmith.sh/track/enable-autofix?expires=1785123539&installation_id=127936663&pr_number=1682&repository=generaltranslation%2Fgt&return_to=https%3A%2F%2Fgithub.com%2Fgeneraltranslation%2Fgt%2Fpull%2F1682&signature=f79d7524f1943dddc089904896641070e1d7c91e85829ccbc7e97a05a6f1af11"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://pr-comments-assets.blacksmith.sh/codesmith/autofix-with-codesmith-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://pr-comments-assets.blacksmith.sh/codesmith/autofix-with-codesmith-light.svg"><img
alt="Autofix with Codesmith"
src="https://pr-comments-assets.blacksmith.sh/codesmith/autofix-with-codesmith-dark.svg"></picture></a>
<sup>Need help on this PR? Tag <code>/codesmith</code> with what you
need. Autofix is disabled.</sup>

<!-- codesmith:autofix:disabled -->
<!-- /codesmith:footer -->

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
## Summary
- Organize entrypoint exports into component, hook, function, type, and
helper sections.
- Replace re-export-only imports with direct `export ... from` syntax
where the binding is not needed locally.
- Preserve the existing export surface.
- Add patch changeset `.changeset/organize-entrypoint-exports.md`.

## Testing
- `pnpm build`
- `pnpm lint`
- `pnpm --filter generaltranslation test`
- `pnpm --filter gt-next test:js`
- `pnpm --filter gt-react-native test`
- `pnpm --filter gt-sanity test`
- export-name comparison against
`origin/e/odysseus/remove-source-barrel-exports`

<!-- codesmith:footer -->
---
<a
href="https://app.blacksmith.sh/generaltranslation/codesmith/gt/pr/1678"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://pr-comments-assets.blacksmith.sh/codesmith/view-with-codesmith-dark-v2.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://pr-comments-assets.blacksmith.sh/codesmith/view-with-codesmith-light-v2.svg"><img
alt="View with Codesmith"
src="https://pr-comments-assets.blacksmith.sh/codesmith/view-with-codesmith-dark-v2.svg"></picture></a>
<a
href="https://backend.blacksmith.sh/track/enable-autofix?expires=1785120739&installation_id=127936663&pr_number=1678&repository=generaltranslation%2Fgt&return_to=https%3A%2F%2Fgithub.com%2Fgeneraltranslation%2Fgt%2Fpull%2F1678&signature=e72ed5832a40b17bd2c8b3f927104c075ca0d49cef814532bd4d3c26524ac592"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://pr-comments-assets.blacksmith.sh/codesmith/autofix-with-codesmith-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://pr-comments-assets.blacksmith.sh/codesmith/autofix-with-codesmith-light.svg"><img
alt="Autofix with Codesmith"
src="https://pr-comments-assets.blacksmith.sh/codesmith/autofix-with-codesmith-dark.svg"></picture></a>
<sup>Need help on this PR? Tag <code>/codesmith</code> with what you
need. Autofix is disabled.</sup>

<!-- codesmith:autofix:disabled -->
<!-- /codesmith:footer -->

<!-- greptile_comment -->

<h3>Greptile Summary</h3>

This PR reorganizes the entrypoint exports across 9 packages by grouping
them into labeled sections (Components, Hooks, Functions, Types) and
converting the `import … + export {}` pattern to direct `export … from`
syntax where no local binding is needed. No behavioral or API surface
changes are intended.

- **`packages/core`** (`id.ts`, `internal.ts`) and **`packages/format`**
(`types.ts`) now use `export { default as … }` for default-export
re-exports and `export type {}` for pure type re-exports, which is
semantically equivalent to the prior pattern.
- **`packages/next`** (`index.client.ts`, `index.rsc.ts`,
`index.server.ts`), **`packages/react-native`**, and
**`packages/sanity`** now have each entrypoint organized into clearly
labeled sections; modules that are consumed locally *and* re-exported
(e.g., `translateAction`, `documentInternationalization` in sanity)
correctly keep their top-level `import` and use a bare `export { name }`
re-export instead of `export … from`.

<details open><summary><h3>Confidence Score: 5/5</h3></summary>

Safe to merge — purely organizational, no logic or API surface changes.

Every changed file is a package entrypoint barrel. The refactor converts
local import+re-export pairs into direct export … from statements and
groups the results into labeled sections. Default-export re-exports
(getPluralForm, hashTemplate, isVariable, TranslationsTab) were verified
against their source files and all have the expected default exports.
Modules that are consumed locally inside the gtPlugin closure
(translateAction, documentInternationalization) correctly retain their
top-level imports alongside the bare re-export. The export surface was
manually cross-checked against the base branch for all nine files and no
additions or removals were found.

No files require special attention.
</details>

<details><summary><h3>Important Files Changed</h3></summary>

| Filename | Overview |
|----------|----------|
| packages/core/src/id.ts | Converts import+re-export of
hashSource/hashString/hashTemplate to direct export … from syntax; both
source files have confirmed default/named exports. |
| packages/core/src/internal.ts | Switches to direct export … from for
all re-exports;
JsxChildren/_Content/JsxChild/JsxElement/LocaleProperties are now
correctly marked export type (they are pure interfaces); runtime values
(isVariable, encode/decode, etc.) keep value exports. |
| packages/format/src/types.ts | Each type now exports directly from its
canonical source module; CustomMapping, CutoffFormatOptions, Variable,
VariableType, LocaleProperties all preserved; HTML_CONTENT_PROPS value
export unchanged. |
| packages/next/src/index.client.ts | Large gt-react import block
replaced with three direct export sections; local type imports
(GetServerSideProps, WithGTServerSideProps) still present for the
parseLocale/withGTServerSideProps stub functions. |
| packages/next/src/index.rsc.ts | Individual per-component export lines
replace a single combined block; getTranslationsSnapshotRscError import
moved before initializeGT() in source order, but static imports are
always hoisted in ESM so execution order is unchanged. |
| packages/next/src/index.server.ts | parseLocale and
withGTServerSideProps converted from local import+export to direct
export … from; all Components/Hooks/Functions from gt-react preserved
with same names. |
| packages/react-native/src/index.tsx | Two separate hook export blocks
from react-core/hooks consolidated into one alphabetical block;
GTProvider, useSetLocale/Region/EnableI18n,
useLocaleSelector/RegionSelector, useLocaleDirection/VersionId still
exported from their original local modules. |
| packages/sanity/src/index.ts | Imports used locally in the gtPlugin
closure (translateAction, documentInternationalization,
TranslationsTool, SECRETS_NAMESPACE, etc.) are kept as top-level
imports; items only re-exported are converted to direct export … from. |

</details>

<sub>Reviews (1): Last reviewed commit: ["refactor: organize entrypoint
exports"](f5c6c5a)
| [Re-trigger
Greptile](https://app.greptile.com/api/retrigger?id=40242913)</sub>

<!-- /greptile_comment -->
## Summary
- Convert default source exports to named exports in core, next,
react-core, and react-native source files
- Update internal imports, re-exports, and Vitest mocks to use named
exports
- Keep `gt-next/link` default-only to match `next/link`, so swapping
imports only changes the package path
- Keep Next generated fallback modules compatible with both default and
named dictionary/loader exports
- Change `gt-react-native/plugin` to expose the named `plugin` export

## Testing
- pnpm lint
- pnpm build
- pnpm --filter generaltranslation typecheck
- pnpm --filter @generaltranslation/react-core typecheck
- pnpm --filter gt-react build
- pnpm --filter gt-next typecheck
- pnpm --filter gt-react-native typecheck
- pnpm --filter gt-node typecheck
- pnpm --filter gt-tanstack-start typecheck
- pnpm --filter generaltranslation test
- pnpm --filter @generaltranslation/react-core test
- pnpm --filter gt-next test:js
- pnpm --filter gt-react-native test

Note: `pnpm typecheck` currently fails on
`packages/react/src/i18n-cache/BrowserI18nCache.ts` because
`ImportMeta.env` is not typed on this branch; focused typechecks above
pass after building `gt-react`.

Changeset added: `.changeset/remove-default-source-exports.md`.

<!-- greptile_comment -->

<h3>Greptile Summary</h3>

This PR mechanically converts `export default` to named exports across
`packages/core`, `packages/next`, `packages/react-core`, and
`packages/react-native`. All internal imports are updated to match, and
consumer code that dynamically `require()`s these modules already has
backward-compatible resolution (`?.default || ?.namedExport`).

- **Internal source modules** (`translate/`, `utils/`,
`react-core/utils/`) are pure refactors with no behavior change — every
call site is updated in the same PR.
- **`gt-next/link`** keeps a default export (to match `next/link`) but
drops the re-exported named `Link`; all in-repo consumers use `import
Link from 'gt-next/link'` (default), so no breakage.
- **`gt-react-native/plugin`** moves from a default export to a named
`plugin` export, which is a breaking public-API change documented in the
changeset.

<details open><summary><h3>Confidence Score: 5/5</h3></summary>

Safe to merge — this is a mechanical export style refactor with no
behavior changes in internal logic and explicit backward-compatible
fallbacks in every dynamic consumer.

Every changed file follows the same pattern: export default
function/const → export function/const. All internal call sites are
updated in the same PR. Dynamic consumers (getDictionary.ts,
resolveDictionaryLoader.ts, resolveTranslationLoader.ts, initGT.rsc.ts)
already resolve both .default and the new named export. The two
public-API breaks (gt-react-native/plugin default→named, gt-next/link
dropping named Link) are intentional, pre-release, and documented in the
changeset.

No files require special attention. The most externally visible changes
are in packages/react-native/src/plugin.ts (public API) and
packages/next/src/link.ts (drops named re-export), both of which are
intentional and documented.
</details>

<details><summary><h3>Important Files Changed</h3></summary>

| Filename | Overview |
|----------|----------|
| packages/next/src/link.ts | Drops the named `Link` re-export; only
default export remains to mirror `next/link`. All in-repo usages use the
default import. |
| packages/react-native/src/plugin.ts | Public entrypoint
`gt-react-native/plugin` now exports named `plugin` instead of default;
breaking change documented in the changeset. |
| packages/react-native/src/plugin-dir/index.ts | Babel plugin function
renamed from anonymous default to named `plugin` export; re-exported
cleanly from `plugin.ts`. |
| packages/next/src/dictionary/getDictionary.ts | Backward-compatible
resolution: tries `.default` first then `.dictionary` for TS/JS
dictionaries; handles both old and new generated module shapes. |
| packages/next/src/_dictionary.ts | Fallback sentinel changed from
`export default {}` to `export const dictionary = {}`; still throws on
evaluation so the catch block in getDictionary handles it identically. |
| packages/next/src/_load-dictionary.ts | Fallback sentinel changed to
`export const loadDictionary = undefined`; `resolveDictionaryLoader.ts`
already checks `?.default || ?.loadDictionary` for compatibility. |
| packages/next/src/_load-translations.ts | Identical pattern to
`_load-dictionary.ts`; `resolveTranslationLoader.ts` handles both
`.default` and `.loadTranslations`. |
| packages/react-native/src/NativeGtReactNative.ts | TurboModule
instance moved from default export to named `GtReactNative`; both
consumer files (`getNativeLocales.ts`, `nativeStore.ts`) updated in the
same PR. |
| packages/core/src/translate/translateMany.ts | Function overload pair
correctly converted from `export default` to named `export`; both
overload signature and implementation updated consistently. |
| packages/core/src/index.ts | All default-import callers updated to
named-import destructuring; no missed imports found. |
| packages/react-core/src/pure.ts | All `export { default as X }`
re-exports converted to `export { X }`; consistent with updated source
files. |

</details>

<details><summary><h3>Flowchart</h3></summary>

<a href="#gh-light-mode-only">

```mermaid
%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A["gt-next/link\n(public entrypoint)"] -->|"export { Link as default }"| B["link/Link.tsx\nnamed export only"]
    C["gt-react-native/plugin\n(public entrypoint)"] -->|"export { plugin }"| D["plugin-dir/index.ts\nexport function plugin()"]
    E["gt-next/_dictionary\n(webpack alias / fallback)"] -->|"bundledDictionary.default\n|| bundledDictionary.dictionary"| F["getDictionary.ts\nbackward-compatible resolution"]
    G["gt-next/_load-dictionary\n(webpack alias / fallback)"] -->|"?.default || ?.loadDictionary"| H["resolveDictionaryLoader.ts\nbackward-compatible resolution"]
    I["gt-next/_load-translations\n(webpack alias / fallback)"] -->|"?.default || ?.loadTranslations"| J["resolveTranslationLoader.ts\nbackward-compatible resolution"]
    K["gt-next/internal/_getLocale\ngt-next/internal/_getRegion"] -->|"?.default || ?.getLocale/.getRegion"| L["initGT.rsc.ts\nbackward-compatible resolution"]

    style A fill:#ffd700,color:#000
    style C fill:#ffd700,color:#000
    style F fill:#90ee90,color:#000
    style H fill:#90ee90,color:#000
    style J fill:#90ee90,color:#000
    style L fill:#90ee90,color:#000
```

</a>
<a href="#gh-dark-mode-only">

```mermaid
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
    A["gt-next/link\n(public entrypoint)"] -->|"export { Link as default }"| B["link/Link.tsx\nnamed export only"]
    C["gt-react-native/plugin\n(public entrypoint)"] -->|"export { plugin }"| D["plugin-dir/index.ts\nexport function plugin()"]
    E["gt-next/_dictionary\n(webpack alias / fallback)"] -->|"bundledDictionary.default\n|| bundledDictionary.dictionary"| F["getDictionary.ts\nbackward-compatible resolution"]
    G["gt-next/_load-dictionary\n(webpack alias / fallback)"] -->|"?.default || ?.loadDictionary"| H["resolveDictionaryLoader.ts\nbackward-compatible resolution"]
    I["gt-next/_load-translations\n(webpack alias / fallback)"] -->|"?.default || ?.loadTranslations"| J["resolveTranslationLoader.ts\nbackward-compatible resolution"]
    K["gt-next/internal/_getLocale\ngt-next/internal/_getRegion"] -->|"?.default || ?.getLocale/.getRegion"| L["initGT.rsc.ts\nbackward-compatible resolution"]

    style A fill:#ffd700,color:#000
    style C fill:#ffd700,color:#000
    style F fill:#90ee90,color:#000
    style H fill:#90ee90,color:#000
    style J fill:#90ee90,color:#000
    style L fill:#90ee90,color:#000
```

</a>
</details>

<sub>Reviews (2): Last reviewed commit: ["fix: preserve default link
export"](b55b9c4)
| [Re-trigger
Greptile](https://app.greptile.com/api/retrigger?id=40241802)</sub>

<!-- /greptile_comment -->
## What

Removes `@deprecated` symbols that have **no remaining consumers
anywhere in the monorepo** — the kind of public-API shedding appropriate
for the odysseus major.

**core (`generaltranslation`)**
- Deprecated types `_Content`, `Update`, `Request`,
`ContentTranslationResult`, `IcuTranslationResult`,
`JsxTranslationResult`
- The `_Content` re-export from `generaltranslation/internal`

**gt-i18n**
- Renamed-alias re-exports: `I18nManager`, `getI18nManager`,
`setI18nManager`, `I18nManagerConstructorParams`, `I18nManagerConfig`
(use `I18nCache` / `getI18nCache` / `setI18nCache` /
`I18nCacheConstructorParams` / `I18nCacheConfig`)
- The all-deprecated wrapper files `sync-translation-resolution.ts`
(`resolveTranslationSync`, `resolveTranslationSyncWithFallback`) and
`jsx-resolution.ts` (`resolveJsxTranslation`), their barrel exports, and
the standalone test (the non-deprecated `resolveTranslation` /
`resolveJsx` and the separate `I18nCache.resolveTranslationSync` method
are untouched)

Net **−273 lines**.

## Verification

- Full monorepo build passes (21/21).
- Tests pass: core 432, gt-i18n 245, gt-node, react-core 267, gt-react
12, gt-next 191.
- Repo-wide grep (incl. `examples/`, `tests/apps/`, docs, JSON) confirms
zero references to every removed symbol. `Update`/`Request`
name-collisions were checked and are unrelated (web `Request`,
`NextRequest`, etc.).

## Deliberately NOT removed (still live — need a real decision, not
cleanup)

These are deprecated but still wired in, so they're left for separate
review:
- `internalInitializeGTSPA` (re-exported by `react-core/pure`, used by
gt-react)
- core enqueue `_versionId` / `description` (still used across the CLI)
- next `STATIC_REQUEST_FUNCTIONS` / `StaticRequestFunctions` /
`experimentalLocaleResolutionParam` (used by config resolution)
- `I18nCache` lifecycle hooks (`createLifecycleCallbacks`,
`LifecycleCallbacks`, `lifecycle` field)
- Deprecated option fields (`context` / `$_locales` / `$_source`, cli
`maxChars` / `variablesOptions`) that may still be read at runtime for
back-compat

<!-- codesmith:footer -->
---
<a
href="https://app.blacksmith.sh/generaltranslation/codesmith/gt/pr/1674"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://pr-comments-assets.blacksmith.sh/codesmith/view-with-codesmith-dark-v2.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://pr-comments-assets.blacksmith.sh/codesmith/view-with-codesmith-light-v2.svg"><img
alt="View with Codesmith"
src="https://pr-comments-assets.blacksmith.sh/codesmith/view-with-codesmith-dark-v2.svg"></picture></a>
<a
href="https://backend.blacksmith.sh/track/enable-autofix?expires=1785119029&installation_id=127936663&pr_number=1674&repository=generaltranslation%2Fgt&return_to=https%3A%2F%2Fgithub.com%2Fgeneraltranslation%2Fgt%2Fpull%2F1674&signature=23dc09ea52f6cb562ac592f143987364f8b63b6314953604874808046376cc40"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://pr-comments-assets.blacksmith.sh/codesmith/autofix-with-codesmith-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://pr-comments-assets.blacksmith.sh/codesmith/autofix-with-codesmith-light.svg"><img
alt="Autofix with Codesmith"
src="https://pr-comments-assets.blacksmith.sh/codesmith/autofix-with-codesmith-dark.svg"></picture></a>
<sup>Need help on this PR? Tag <code>/codesmith</code> with what you
need. Autofix is disabled.</sup>

<!-- codesmith:autofix:disabled -->
<!-- /codesmith:footer -->

<!-- greptile_comment -->

<h3>Greptile Summary</h3>

This PR removes deprecated public API symbols from the
`generaltranslation` core and `gt-i18n` packages as part of the odysseus
major-version cleanup. No logic changes are made — every removed symbol
was verified to have zero consumers in the monorepo before deletion.

- **core**: Drops deprecated types `_Content`, `Update`, `Request`,
`ContentTranslationResult`, `IcuTranslationResult`, and
`JsxTranslationResult`, plus the `_Content` re-export from
`generaltranslation/internal`.
- **gt-i18n**: Removes the `I18nManager` / `getI18nManager` /
`setI18nManager` renamed aliases and their type counterparts, deletes
the `sync-translation-resolution.ts` and `jsx-resolution.ts` deprecated
wrapper files with their barrel exports and test, and updates two JSDoc
comments that still referenced the old function name.

<details open><summary><h3>Confidence Score: 5/5</h3></summary>

Pure dead-code removal with no logic changes; canonical replacements for
every removed symbol remain in place and all monorepo consumers have
been verified clean.

Every deleted symbol was either a thin wrapper that delegated directly
to an existing non-deprecated function, or a type alias for an existing
type. The underlying implementations (`I18nCache`, `getI18nCache`,
`setI18nCache`, `resolveJsx`, `resolveTranslation`, etc.) are untouched.
The PR description documents a full monorepo build and test pass (21/21
packages, all test suites green) and a repo-wide grep confirming zero
references to any removed symbol.

No files require special attention — all changes are deletions of
confirmed-dead code.
</details>

<details><summary><h3>Important Files Changed</h3></summary>

| Filename | Overview |
|----------|----------|
| packages/core/src/types.ts | Removes six deprecated type definitions
(`_Content`, `Update`, `Request`, `ContentTranslationResult`,
`IcuTranslationResult`, `JsxTranslationResult`) — pure deletion with no
logic change. |
| packages/core/src/internal.ts | Drops `_Content` from the type import
and the `internal` barrel export; no other changes. |
| packages/i18n/src/internal.ts | Removes three deprecated re-exports
(`I18nManager`, `getI18nManager`, `setI18nManager`); the underlying
`I18nCache`, `getI18nCache`, and `setI18nCache` exports remain
untouched. |
| packages/i18n/src/internal-types.ts | Removes deprecated type aliases
`I18nManagerConstructorParams` and `I18nManagerConfig`; canonical types
`I18nCacheConstructorParams` and `I18nCacheConfig` are still exported
above. |
|
packages/i18n/src/translation-functions/internal/sync-translation-resolution.ts
| Deleted entirely — contained only `resolveTranslationSync` and
`resolveTranslationSyncWithFallback`, both deprecated thin wrappers over
`helpers.ts`. |
| packages/i18n/src/translation-functions/internal/jsx-resolution.ts |
Deleted entirely — contained only `resolveJsxTranslation`, a deprecated
one-line wrapper over `resolveJsx`. |
|
packages/i18n/src/translation-functions/internal/__tests__/resolveTranslationSync.test.ts
| Deleted — the four test cases covered `resolveTranslationSync` which
no longer exists. |
| packages/i18n/src/translation-functions/internal/index.ts | Removes
the two barrel re-exports for the deleted `sync-translation-resolution`
and `jsx-resolution` modules. |
| packages/i18n/src/translation-functions/types/functions.ts | Updates a
JSDoc comment to reference `resolveJsx()` instead of the now-deleted
`resolveJsxTranslation()`. |
| packages/i18n/src/translation-functions/types/options.ts | Updates a
JSDoc comment to reference `resolveJsx()` instead of the now-deleted
`resolveJsxTranslation()`. |

</details>

<details><summary><h3>Flowchart</h3></summary>

<a href="#gh-light-mode-only">

```mermaid
%%{init: {'theme': 'neutral'}}%%
flowchart TD
    subgraph core["generaltranslation (core)"]
        direction TB
        A["types.ts\n❌ _Content\n❌ Update\n❌ Request\n❌ ContentTranslationResult\n❌ IcuTranslationResult\n❌ JsxTranslationResult"]
        B["internal.ts\n❌ re-export _Content"]
    end

    subgraph i18n["gt-i18n"]
        direction TB
        C["internal.ts\n❌ I18nManager alias\n❌ getI18nManager alias\n❌ setI18nManager alias"]
        D["internal-types.ts\n❌ I18nManagerConstructorParams alias\n❌ I18nManagerConfig alias"]
        E["sync-translation-resolution.ts\n❌ resolveTranslationSync\n❌ resolveTranslationSyncWithFallback"]
        F["jsx-resolution.ts\n❌ resolveJsxTranslation"]
        G["internal/index.ts\n❌ barrel exports for E & F"]
        H["__tests__/resolveTranslationSync.test.ts\n❌ deleted test file"]
    end

    subgraph keep["Still live (out of scope)"]
        I18nCache["I18nCache ✅"]
        getI18nCache["getI18nCache ✅"]
        setI18nCache["setI18nCache ✅"]
        resolveJsx["resolveJsx ✅"]
        resolveTranslation["resolveTranslation ✅"]
    end

    E --> G
    F --> G
```

</a>
<a href="#gh-dark-mode-only">

```mermaid
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
    subgraph core["generaltranslation (core)"]
        direction TB
        A["types.ts\n❌ _Content\n❌ Update\n❌ Request\n❌ ContentTranslationResult\n❌ IcuTranslationResult\n❌ JsxTranslationResult"]
        B["internal.ts\n❌ re-export _Content"]
    end

    subgraph i18n["gt-i18n"]
        direction TB
        C["internal.ts\n❌ I18nManager alias\n❌ getI18nManager alias\n❌ setI18nManager alias"]
        D["internal-types.ts\n❌ I18nManagerConstructorParams alias\n❌ I18nManagerConfig alias"]
        E["sync-translation-resolution.ts\n❌ resolveTranslationSync\n❌ resolveTranslationSyncWithFallback"]
        F["jsx-resolution.ts\n❌ resolveJsxTranslation"]
        G["internal/index.ts\n❌ barrel exports for E & F"]
        H["__tests__/resolveTranslationSync.test.ts\n❌ deleted test file"]
    end

    subgraph keep["Still live (out of scope)"]
        I18nCache["I18nCache ✅"]
        getI18nCache["getI18nCache ✅"]
        setI18nCache["setI18nCache ✅"]
        resolveJsx["resolveJsx ✅"]
        resolveTranslation["resolveTranslation ✅"]
    end

    E --> G
    F --> G
```

</a>
</details>

<sub>Reviews (1): Last reviewed commit: ["refactor: remove dead
@deprecated
public..."](800d6e6)
| [Re-trigger
Greptile](https://app.greptile.com/api/retrigger?id=40241227)</sub>

<!-- /greptile_comment -->
## Summary
- pass `cacheExpiryTime: null` when initializing client-side i18n caches
in gt-react
- route gt-next browser/client-boundary initialization through a client
initializer that disables cache expiry

## Testing
- `pnpm --filter gt-react... build`
- `pnpm --filter gt-next typecheck`
- `pnpm --filter gt-react test`
- `pnpm --filter gt-next test:js`
- `pnpm lint`

Note: `pnpm --filter gt-react typecheck` is still blocked by the
existing `ImportMeta.env` typing issue in `BrowserI18nCache.ts`.

<!-- codesmith:footer -->
---
<a
href="https://app.blacksmith.sh/generaltranslation/codesmith/gt/pr/1685"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://pr-comments-assets.blacksmith.sh/codesmith/view-with-codesmith-dark-v2.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://pr-comments-assets.blacksmith.sh/codesmith/view-with-codesmith-light-v2.svg"><img
alt="View with Codesmith"
src="https://pr-comments-assets.blacksmith.sh/codesmith/view-with-codesmith-dark-v2.svg"></picture></a>
<a
href="https://backend.blacksmith.sh/track/enable-autofix?expires=1785345577&installation_id=127936663&pr_number=1685&repository=generaltranslation%2Fgt&return_to=https%3A%2F%2Fgithub.com%2Fgeneraltranslation%2Fgt%2Fpull%2F1685&signature=5f4723f456084250913d0be547530fa50d6718fc9d3e22a2b2430b5b15ae684e"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://pr-comments-assets.blacksmith.sh/codesmith/autofix-with-codesmith-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://pr-comments-assets.blacksmith.sh/codesmith/autofix-with-codesmith-light.svg"><img
alt="Autofix with Codesmith"
src="https://pr-comments-assets.blacksmith.sh/codesmith/autofix-with-codesmith-dark.svg"></picture></a>
<sup>Need help on this PR? Tag <code>/codesmith</code> with what you
need. Autofix is disabled.</sup>

<!-- codesmith:autofix:disabled -->
<!-- /codesmith:footer -->

<!-- greptile_comment -->

<h3>Greptile Summary</h3>

This PR disables client-side i18n cache expiry in both `gt-react` and
`gt-next` by introducing thin wrapper initializers that force
`cacheExpiryTime: null` on every client-side cache construction,
preventing translations from being evicted while the browser session is
alive.

- **`gt-react`**: A new `initializeGTClient.ts` wraps
`internalInitializeGTSRA` with `cacheExpiryTime: null`, and
`initializeGTSPA.ts` is updated to pass the same override to
`BrowserI18nCache`. The public `initializeGT` export from `gt-react`'s
client barrel now points to this new wrapper instead of re-exporting
from `@generaltranslation/react-core/pure`.
- **`gt-next`**: A new `initGT.client.ts` wrapper calls `getParams()`
and forwards the params to the core initializer with
`nextI18nCacheParams.cacheExpiryTime` overridden to `null`. Both
`index.client.ts` and `client-boundary.tsx` are updated to import from
this new client-specific module.

<details open><summary><h3>Confidence Score: 5/5</h3></summary>

Safe to merge — the changes are small, mechanically consistent wrappers
that apply a single override (cacheExpiryTime: null) to every
client-side cache initialisation path.

All six changed files follow the same straightforward pattern: introduce
a thin wrapper or change an import to route through a wrapper that
forces cacheExpiryTime to null. No new logic branches, no altered data
flow outside the cache, and the full test suite is reported green by the
author.

No files require special attention. The pre-existing ImportMeta.env
typing issue in BrowserI18nCache.ts (blocking gt-react typecheck) is
explicitly acknowledged in the PR description and is unrelated to these
changes.
</details>

<details><summary><h3>Important Files Changed</h3></summary>

| Filename | Overview |
|----------|----------|
| packages/next/src/setup/initGT.client.ts | New wrapper that reads
params via getParams() and forwards them to the core initializer with
cacheExpiryTime forced to null; logic is correct and TypeScript-clean
(gt-next typecheck passes). |
| packages/next/src/index.client.ts | One-line import path change from
initGT to initGT.client; straightforward and correct. |
| packages/next/src/utils/client-boundary.tsx | Import path updated to
use the new client-specific initializer so the boundary component now
benefits from disabled cache expiry. |
| packages/react/src/setup/initializeGTClient.ts | New thin wrapper that
hardcodes cacheExpiryTime: null on top of any caller-supplied config
before delegating to internalInitializeGTSRA; correctly typed and
appropriately scoped. |
| packages/react/src/index.client.ts | Swaps the initializeGT re-export
from the react-core/pure barrel to the new initializeGTClient wrapper,
ensuring all gt-react consumers get cache-expiry-disabled behaviour by
default. |
| packages/react/src/setup/initializeGTSPA.ts | SPA initializer now
passes cacheExpiryTime: null when constructing BrowserI18nCache,
consistent with the broader client-side cache expiry fix. |

</details>

<details><summary><h3>Sequence Diagram</h3></summary>

<a href="#gh-light-mode-only">

```mermaid
%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
    participant CI as Client Entry (index.client.ts)
    participant CB as ClientBoundary (client-boundary.tsx)
    participant GTC as initGT.client.ts (new)
    participant GT as initGT.ts (core)
    participant NIC as NextI18nCache

    Note over CI,NIC: gt-next client initialization (after PR)

    CI->>GTC: initializeGT()
    CB->>GTC: initializeGT()
    GTC->>GTC: getParams()
    GTC->>GT: coreInitializeGT with cacheExpiryTime null
    GT->>NIC: new NextI18nCache(nextI18nCacheParams)
    Note right of NIC: cacheExpiryTime = null, no expiry

    participant GTCR as initializeGTClient.ts (new)
    participant SRA as internalInitializeGTSRA
    participant GSPA as initializeGTSPA.ts
    participant BIC as BrowserI18nCache

    Note over CI,BIC: gt-react client initialization (after PR)

    CI->>GTCR: initializeGT(config)
    GTCR->>SRA: internalInitializeGTSRA with cacheExpiryTime null
    Note right of SRA: cacheExpiryTime = null, no expiry

    CI->>GSPA: initializeGTSPA(config)
    GSPA->>BIC: new BrowserI18nCache with cacheExpiryTime null
    Note right of BIC: cacheExpiryTime = null, no expiry
```

</a>
<a href="#gh-dark-mode-only">

```mermaid
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
    participant CI as Client Entry (index.client.ts)
    participant CB as ClientBoundary (client-boundary.tsx)
    participant GTC as initGT.client.ts (new)
    participant GT as initGT.ts (core)
    participant NIC as NextI18nCache

    Note over CI,NIC: gt-next client initialization (after PR)

    CI->>GTC: initializeGT()
    CB->>GTC: initializeGT()
    GTC->>GTC: getParams()
    GTC->>GT: coreInitializeGT with cacheExpiryTime null
    GT->>NIC: new NextI18nCache(nextI18nCacheParams)
    Note right of NIC: cacheExpiryTime = null, no expiry

    participant GTCR as initializeGTClient.ts (new)
    participant SRA as internalInitializeGTSRA
    participant GSPA as initializeGTSPA.ts
    participant BIC as BrowserI18nCache

    Note over CI,BIC: gt-react client initialization (after PR)

    CI->>GTCR: initializeGT(config)
    GTCR->>SRA: internalInitializeGTSRA with cacheExpiryTime null
    Note right of SRA: cacheExpiryTime = null, no expiry

    CI->>GSPA: initializeGTSPA(config)
    GSPA->>BIC: new BrowserI18nCache with cacheExpiryTime null
    Note right of BIC: cacheExpiryTime = null, no expiry
```

</a>
</details>

<sub>Reviews (1): Last reviewed commit: ["fix: disable client i18n cache
expiry"](61df180)
| [Re-trigger
Greptile](https://app.greptile.com/api/retrigger?id=40581150)</sub>

<!-- /greptile_comment -->
## Summary

Removes deprecated translation options from `gt-i18n` as part of the
Odysseus major-version cleanup:

- **`context` dictionary option** → use `$context`. Removed from
`DictionaryOptions`, plus the fallback mapping in
`resolveDictionaryLookupOptions` and the validation branch in
`isDictionaryOptions`.
- **`$_locales` inline option** → use `$locale`. Removed from
`InlineTranslationOptionsFields`, the `$locale ?? $_locales` fallbacks
in ICU/string interpolation, and the exclusion list in
`extractVariables`.

## Stale comment/doc cleanup

These reference the same options and were updated for consistency (no
behavior change):

- `next/swc-plugin` comments now reference `$id`/`$context`/`$maxChars`.
- `@generaltranslation/compiler` `StringCollector` —
`TranslationContent` (extracted from `t()` calls) comments now use
`$`-prefixed keys. The parser already reads
`$context`/`$id`/`$maxChars`.
- `next` `tx()` JSDoc corrected to the actual `TxOptions` shape
(`$locale`/`$context`/`$maxChars`), and the
`variables`/`variableOptions` docs fixed to flat top-level variables
(`TxOptions extends FormatVariables`).

Left intentionally bare (not the i18n message-options form):
- JSX-prop `context` (`<T context="...">`, `TranslationJsx`).
- Backend translation-API request metadata `context` (`gt.translate` /
`EntryMetadata`).

## Testing

- `pnpm --filter gt-i18n test` → 244/244 passing.

A changeset (`gt-i18n` major) is included documenting the breaking
removal.

<!-- codesmith:footer -->
---
<a
href="https://app.blacksmith.sh/generaltranslation/codesmith/gt/pr/1687"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://pr-comments-assets.blacksmith.sh/codesmith/view-with-codesmith-dark-v2.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://pr-comments-assets.blacksmith.sh/codesmith/view-with-codesmith-light-v2.svg"><img
alt="View with Codesmith"
src="https://pr-comments-assets.blacksmith.sh/codesmith/view-with-codesmith-dark-v2.svg"></picture></a>
<a
href="https://backend.blacksmith.sh/track/enable-autofix?expires=1785348313&installation_id=127936663&pr_number=1687&repository=generaltranslation%2Fgt&return_to=https%3A%2F%2Fgithub.com%2Fgeneraltranslation%2Fgt%2Fpull%2F1687&signature=ec3064c77242644ba9591dbed52aea3eadf4e7bb487ad4ed311b65d1c215e3c7"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://pr-comments-assets.blacksmith.sh/codesmith/autofix-with-codesmith-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://pr-comments-assets.blacksmith.sh/codesmith/autofix-with-codesmith-light.svg"><img
alt="Autofix with Codesmith"
src="https://pr-comments-assets.blacksmith.sh/codesmith/autofix-with-codesmith-dark.svg"></picture></a>
<sup>Need help on this PR? Tag <code>/codesmith</code> with what you
need. Autofix is disabled.</sup>

<!-- codesmith:autofix:disabled -->
<!-- /codesmith:footer -->

<!-- greptile_comment -->

<h3>Greptile Summary</h3>

This PR removes two deprecated translation options from `gt-i18n` as
part of the Odysseus major-version cleanup: the `context` dictionary key
(replaced by `$context`) and the `$_locales` inline option (replaced by
`$locale`). Comment and JSDoc updates in `next/tx.ts`, the SWC plugin,
and `@generaltranslation/compiler` are also included to align
documentation with the already-live `$`-prefixed API.

- **`context` removal**: `DictionaryOptions` no longer carries
`context`; `resolveDictionaryLookupOptions` no longer maps `context →
$context`; `isDictionaryOptions` no longer validates the `context`
string field. JS callers that still pass `context:` after upgrading will
have it leak as an unknown key (and potentially a template variable)
rather than being silently dropped — a silent regression for un-migrated
users, but intentional at a major version boundary.
- **`$_locales` removal**: Dropped from
`InlineTranslationOptionsFields`, both interpolation fallbacks, and the
`extractVariables` exclusion list. Any un-migrated JS caller passing
`$_locales` will now have that key treated as a user-defined template
variable instead of being ignored.
- All 244 tests pass; a major-version changeset is included.

<details open><summary><h3>Confidence Score: 4/5</h3></summary>

Safe to merge for the Odysseus major version — all breaking removals are
intentional and the test suite passes cleanly.

The changes are a straightforward deprecation removal with 244 passing
tests. The one subtlety worth noting is that un-migrated JS callers
passing the old `context:` key will now have that value leak through
`resolveDictionaryLookupOptions` as part of `rest` rather than being
explicitly stripped. This means `context` could surface as a template
variable name in messages that happen to contain a `{context}`
placeholder — a quiet behavioral change rather than a hard error. The
same applies to `$_locales` no longer being excluded from
`extractVariables`. Both are expected consequences of a major-version
break, but neither has a dedicated migration-path test to document the
new runtime behavior for un-migrated callers.


`packages/i18n/src/i18n-cache/translations-manager/utils/dictionary-helpers.ts`
— the `resolveDictionaryLookupOptions` change silently changes how old
`context:` keys are handled at runtime.
</details>


<details><summary><h3>Important Files Changed</h3></summary>




| Filename | Overview |
|----------|----------|
|
packages/i18n/src/i18n-cache/translations-manager/utils/dictionary-helpers.ts
| Removes deprecated `context`→`$context` mapping in
`resolveDictionaryLookupOptions` and the `context` string validation in
`isDictionaryOptions`. Old JS callers passing `context:` will now have
it leak into `rest` as an unknown key instead of being silently dropped
or mapped. |
| packages/i18n/src/translation-functions/types/options.ts | Removes
deprecated `context` from `DictionaryOptions` and `$_locales` from
`InlineTranslationOptionsFields`. Clean type-level enforcement of the
new API surface. |
| packages/i18n/src/utils/extractVariables.ts | Removes `$_locales` from
the GT-reserved key exclusion list. Any caller (JS runtime) still
passing `$_locales` will now have that key treated as a user template
variable rather than silently dropped. |
|
packages/i18n/src/translation-functions/utils/interpolation/interpolateIcuMessage.ts
| Drops `$_locales` fallback from locale resolution — `options.$locale`
is now the only locale source passed to the ICU formatter. |
| packages/next/src/server-dir/runtime/tx.ts | JSDoc corrected to the
actual `TxOptions` shape (`$locale`, `$context`, `$maxChars`) and
variable interpolation docs updated to reflect flat top-level key
passing. No logic changes. |
| packages/compiler/src/state/StringCollector.ts | JSDoc comments
updated to use `$id`/`$context`/`$maxChars` in examples — no logic
change. |
| .changeset/remove-deprecated-context-locales-options.md |
Major-version changeset for `gt-i18n` documenting both breaking removals
clearly. |

</details>


<details><summary><h3>Flowchart</h3></summary>

<a href="#gh-light-mode-only">

```mermaid
%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A["Dictionary entry\n['Hello', options]"] --> B{isDictionaryOptions?}
    B -- "pass" --> C[resolveDictionaryLookupOptions]
    C --> D["Destructure: { $format, ...rest }"]
    D --> E["Return: { ...rest, $format: ICU }"]

    E --> F{context key present\nin old JS dict?}
    F -- "yes (un-migrated)" --> G["context leaks into rest\n→ treated as template variable"]
    F -- "no" --> H["Clean lookup options\n$context/$format/$maxChars"]

    I["Inline call: gt(msg, options)"] --> J[extractVariables]
    J --> K{$_locales in options?}
    K -- "yes (un-migrated)" --> L["Included as template variable\n(no longer filtered)"]
    K -- "no" --> M["Only user variables returned"]

    N["interpolateIcuMessage / interpolateStringMessage"] --> O["options.$locale\n(no $_locales fallback)"]
```

</a>
<a href="#gh-dark-mode-only">

```mermaid
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
    A["Dictionary entry\n['Hello', options]"] --> B{isDictionaryOptions?}
    B -- "pass" --> C[resolveDictionaryLookupOptions]
    C --> D["Destructure: { $format, ...rest }"]
    D --> E["Return: { ...rest, $format: ICU }"]

    E --> F{context key present\nin old JS dict?}
    F -- "yes (un-migrated)" --> G["context leaks into rest\n→ treated as template variable"]
    F -- "no" --> H["Clean lookup options\n$context/$format/$maxChars"]

    I["Inline call: gt(msg, options)"] --> J[extractVariables]
    J --> K{$_locales in options?}
    K -- "yes (un-migrated)" --> L["Included as template variable\n(no longer filtered)"]
    K -- "no" --> M["Only user variables returned"]

    N["interpolateIcuMessage / interpolateStringMessage"] --> O["options.$locale\n(no $_locales fallback)"]
```

</a>
</details>


<!-- greptile_failed_comments -->
<details open><summary><h3>Comments Outside Diff (1)</h3></summary>

1. `packages/i18n/src/utils/extractVariables.ts`, line 14-24
([link](https://github.com/generaltranslation/gt/blob/ffc7be81dd257454414f23decdb048dae185ab83/packages/i18n/src/utils/extractVariables.ts#L14-L24))

<a href="#"><img alt="P2"
src="https://greptile-static-assets.s3.amazonaws.com/badges/p2.svg?v=9"
align="top"></a> **`$_locales` no longer filtered — becomes a live
template variable**

`$_locales` was previously excluded from `extractVariables` so it could
never accidentally appear as an interpolation variable. After this
removal, any un-migrated JS caller that still passes `$_locales` will
have it treated as a user-defined placeholder. If the message string
contains `{$_locales}` (unlikely but possible), it would be interpolated
with the locale value instead of raising a missing-variable error. This
is consistent with the broader deprecation removal, but unlike the
`$locale` key (which remains excluded), there is no longer any guard
that prevents the old key from leaking into rendered output.

   <details><summary>Prompt To Fix With AI</summary>

   `````markdown
   This is a comment left during a code review.
   Path: packages/i18n/src/utils/extractVariables.ts
   Line: 14-24

   Comment:
   **`$_locales` no longer filtered — becomes a live template variable**

`$_locales` was previously excluded from `extractVariables` so it could
never accidentally appear as an interpolation variable. After this
removal, any un-migrated JS caller that still passes `$_locales` will
have it treated as a user-defined placeholder. If the message string
contains `{$_locales}` (unlikely but possible), it would be interpolated
with the locale value instead of raising a missing-variable error. This
is consistent with the broader deprecation removal, but unlike the
`$locale` key (which remains excluded), there is no longer any guard
that prevents the old key from leaking into rendered output.

   How can I resolve this? If you propose a fix, please make it concise.
   `````
   </details>

<a
href="https://app.greptile.com/ide/claude-code?prompt=This%20is%20a%20comment%20left%20during%20a%20code%20review.%0APath%3A%20packages%2Fi18n%2Fsrc%2Futils%2FextractVariables.ts%0ALine%3A%2014-24%0A%0AComment%3A%0A**%60%24_locales%60%20no%20longer%20filtered%20%E2%80%94%20becomes%20a%20live%20template%20variable**%0A%0A%60%24_locales%60%20was%20previously%20excluded%20from%20%60extractVariables%60%20so%20it%20could%20never%20accidentally%20appear%20as%20an%20interpolation%20variable.%20After%20this%20removal%2C%20any%20un-migrated%20JS%20caller%20that%20still%20passes%20%60%24_locales%60%20will%20have%20it%20treated%20as%20a%20user-defined%20placeholder.%20If%20the%20message%20string%20contains%20%60%7B%24_locales%7D%60%20%28unlikely%20but%20possible%29%2C%20it%20would%20be%20interpolated%20with%20the%20locale%20value%20instead%20of%20raising%20a%20missing-variable%20error.%20This%20is%20consistent%20with%20the%20broader%20deprecation%20removal%2C%20but%20unlike%20the%20%60%24locale%60%20key%20%28which%20remains%20excluded%29%2C%20there%20is%20no%20longer%20any%20guard%20that%20prevents%20the%20old%20key%20from%20leaking%20into%20rendered%20output.%0A%0AHow%20can%20I%20resolve%20this%3F%20If%20you%20propose%20a%20fix%2C%20please%20make%20it%20concise.&repo=generaltranslation%2Fgt&pr=1687&platform=github"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://greptile-static-assets.s3.amazonaws.com/badges/FixInClaudeDark.svg?v=3"><source
media="(prefers-color-scheme: light)"
srcset="https://greptile-static-assets.s3.amazonaws.com/badges/FixInClaude.svg?v=3"><img
alt="Fix in Claude Code"
src="https://greptile-static-assets.s3.amazonaws.com/badges/FixInClaude.svg?v=3"
height="20"></picture></a> <a
href="https://app.greptile.com/api/ide/codex?prompt=IMPORTANT%3A%20Work%20in%20the%20repository%20%22generaltranslation%2Fgt%22%20on%20the%20existing%20branch%20%22refactor%2Fremove-deprecated-i18n-options%22.%20Checkout%20that%20branch%20%E2%80%94%20do%20NOT%20create%20a%20new%20branch%20or%20open%20a%20new%20PR.%20Push%20your%20changes%20to%20%22refactor%2Fremove-deprecated-i18n-options%22.%0A%0AThis%20is%20a%20comment%20left%20during%20a%20code%20review.%0APath%3A%20packages%2Fi18n%2Fsrc%2Futils%2FextractVariables.ts%0ALine%3A%2014-24%0A%0AComment%3A%0A**%60%24_locales%60%20no%20longer%20filtered%20%E2%80%94%20becomes%20a%20live%20template%20variable**%0A%0A%60%24_locales%60%20was%20previously%20excluded%20from%20%60extractVariables%60%20so%20it%20could%20never%20accidentally%20appear%20as%20an%20interpolation%20variable.%20After%20this%20removal%2C%20any%20un-migrated%20JS%20caller%20that%20still%20passes%20%60%24_locales%60%20will%20have%20it%20treated%20as%20a%20user-defined%20placeholder.%20If%20the%20message%20string%20contains%20%60%7B%24_locales%7D%60%20%28unlikely%20but%20possible%29%2C%20it%20would%20be%20interpolated%20with%20the%20locale%20value%20instead%20of%20raising%20a%20missing-variable%20error.%20This%20is%20consistent%20with%20the%20broader%20deprecation%20removal%2C%20but%20unlike%20the%20%60%24locale%60%20key%20%28which%20remains%20excluded%29%2C%20there%20is%20no%20longer%20any%20guard%20that%20prevents%20the%20old%20key%20from%20leaking%20into%20rendered%20output.%0A%0AHow%20can%20I%20resolve%20this%3F%20If%20you%20propose%20a%20fix%2C%20please%20make%20it%20concise.&repo=generaltranslation%2Fgt&pr=1687&platform=github"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://greptile-static-assets.s3.amazonaws.com/badges/FixInCodexDark.svg?v=3"><source
media="(prefers-color-scheme: light)"
srcset="https://greptile-static-assets.s3.amazonaws.com/badges/FixInCodex.svg?v=3"><img
alt="Fix in Codex"
src="https://greptile-static-assets.s3.amazonaws.com/badges/FixInCodex.svg?v=3"
height="20"></picture></a>

</details>

<!-- /greptile_failed_comments -->

<a
href="https://app.greptile.com/ide/claude-code?prompt=Fix%20the%20following%202%20code%20review%20issues.%20Work%20through%20them%20one%20at%20a%20time%2C%20proposing%20concise%20fixes.%0A%0A---%0A%0A%23%23%23%20Issue%201%20of%202%0Apackages%2Fi18n%2Fsrc%2Fi18n-cache%2Ftranslations-manager%2Futils%2Fdictionary-helpers.ts%3A116-124%0A**Old%20%60context%60%20key%20now%20leaks%20into%20lookup%20options**%0A%0APreviously%2C%20%60context%60%20was%20explicitly%20destructured%20out%20of%20%60options%60%20before%20the%20spread%2C%20so%20it%20was%20always%20stripped%20from%20the%20returned%20object.%20After%20this%20change%2C%20any%20object%20that%20still%20carries%20a%20%60context%60%20property%20%28e.g.%20a%20JS%20dictionary%20written%20before%20the%20migration%29%20will%20have%20%60context%60%20included%20in%20%60rest%60%20and%20therefore%20spread%20verbatim%20into%20the%20%60DictionaryLookupOptions%60%20result.%20Downstream%20consumers%20that%20treat%20unknown%20keys%20as%20template%20variables%20will%20then%20expose%20%60context%60%20as%20an%20interpolation%20variable%20%E2%80%94%20if%20the%20message%20contains%20%60%7Bcontext%7D%60%2C%20it%20would%20be%20silently%20filled%20with%20the%20old%20metadata%20string%20rather%20than%20surfacing%20an%20error.%20A%20one-liner%20%60const%20%7B%20%24format%2C%20context%3A%20_context%2C%20...rest%20%7D%20%3D%20options%3B%60%20would%20preserve%20the%20old%20strip-and-discard%20behavior%20without%20resurrecting%20the%20compatibility%20mapping.%0A%0A%23%23%23%20Issue%202%20of%202%0Apackages%2Fi18n%2Fsrc%2Futils%2FextractVariables.ts%3A14-24%0A**%60%24_locales%60%20no%20longer%20filtered%20%E2%80%94%20becomes%20a%20live%20template%20variable**%0A%0A%60%24_locales%60%20was%20previously%20excluded%20from%20%60extractVariables%60%20so%20it%20could%20never%20accidentally%20appear%20as%20an%20interpolation%20variable.%20After%20this%20removal%2C%20any%20un-migrated%20JS%20caller%20that%20still%20passes%20%60%24_locales%60%20will%20have%20it%20treated%20as%20a%20user-defined%20placeholder.%20If%20the%20message%20string%20contains%20%60%7B%24_locales%7D%60%20%28unlikely%20but%20possible%29%2C%20it%20would%20be%20interpolated%20with%20the%20locale%20value%20instead%20of%20raising%20a%20missing-variable%20error.%20This%20is%20consistent%20with%20the%20broader%20deprecation%20removal%2C%20but%20unlike%20the%20%60%24locale%60%20key%20%28which%20remains%20excluded%29%2C%20there%20is%20no%20longer%20any%20guard%20that%20prevents%20the%20old%20key%20from%20leaking%20into%20rendered%20output.%0A%0A&repo=generaltranslation%2Fgt&pr=1687&platform=github"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://greptile-static-assets.s3.amazonaws.com/badges/FixAllInClaudeDark.svg?v=3"><source
media="(prefers-color-scheme: light)"
srcset="https://greptile-static-assets.s3.amazonaws.com/badges/FixAllInClaude.svg?v=3"><img
alt="Fix All in Claude Code"
src="https://greptile-static-assets.s3.amazonaws.com/badges/FixAllInClaude.svg?v=3"
height="20"></picture></a> <a
href="https://app.greptile.com/api/ide/codex?prompt=IMPORTANT%3A%20Work%20in%20the%20repository%20%22generaltranslation%2Fgt%22%20on%20the%20existing%20branch%20%22refactor%2Fremove-deprecated-i18n-options%22.%20Checkout%20that%20branch%20%E2%80%94%20do%20NOT%20create%20a%20new%20branch%20or%20open%20a%20new%20PR.%20Push%20your%20changes%20to%20%22refactor%2Fremove-deprecated-i18n-options%22.%0A%0AFix%20the%20following%202%20code%20review%20issues.%20Work%20through%20them%20one%20at%20a%20time%2C%20proposing%20concise%20fixes.%0A%0A---%0A%0A%23%23%23%20Issue%201%20of%202%0Apackages%2Fi18n%2Fsrc%2Fi18n-cache%2Ftranslations-manager%2Futils%2Fdictionary-helpers.ts%3A116-124%0A**Old%20%60context%60%20key%20now%20leaks%20into%20lookup%20options**%0A%0APreviously%2C%20%60context%60%20was%20explicitly%20destructured%20out%20of%20%60options%60%20before%20the%20spread%2C%20so%20it%20was%20always%20stripped%20from%20the%20returned%20object.%20After%20this%20change%2C%20any%20object%20that%20still%20carries%20a%20%60context%60%20property%20%28e.g.%20a%20JS%20dictionary%20written%20before%20the%20migration%29%20will%20have%20%60context%60%20included%20in%20%60rest%60%20and%20therefore%20spread%20verbatim%20into%20the%20%60DictionaryLookupOptions%60%20result.%20Downstream%20consumers%20that%20treat%20unknown%20keys%20as%20template%20variables%20will%20then%20expose%20%60context%60%20as%20an%20interpolation%20variable%20%E2%80%94%20if%20the%20message%20contains%20%60%7Bcontext%7D%60%2C%20it%20would%20be%20silently%20filled%20with%20the%20old%20metadata%20string%20rather%20than%20surfacing%20an%20error.%20A%20one-liner%20%60const%20%7B%20%24format%2C%20context%3A%20_context%2C%20...rest%20%7D%20%3D%20options%3B%60%20would%20preserve%20the%20old%20strip-and-discard%20behavior%20without%20resurrecting%20the%20compatibility%20mapping.%0A%0A%23%23%23%20Issue%202%20of%202%0Apackages%2Fi18n%2Fsrc%2Futils%2FextractVariables.ts%3A14-24%0A**%60%24_locales%60%20no%20longer%20filtered%20%E2%80%94%20becomes%20a%20live%20template%20variable**%0A%0A%60%24_locales%60%20was%20previously%20excluded%20from%20%60extractVariables%60%20so%20it%20could%20never%20accidentally%20appear%20as%20an%20interpolation%20variable.%20After%20this%20removal%2C%20any%20un-migrated%20JS%20caller%20that%20still%20passes%20%60%24_locales%60%20will%20have%20it%20treated%20as%20a%20user-defined%20placeholder.%20If%20the%20message%20string%20contains%20%60%7B%24_locales%7D%60%20%28unlikely%20but%20possible%29%2C%20it%20would%20be%20interpolated%20with%20the%20locale%20value%20instead%20of%20raising%20a%20missing-variable%20error.%20This%20is%20consistent%20with%20the%20broader%20deprecation%20removal%2C%20but%20unlike%20the%20%60%24locale%60%20key%20%28which%20remains%20excluded%29%2C%20there%20is%20no%20longer%20any%20guard%20that%20prevents%20the%20old%20key%20from%20leaking%20into%20rendered%20output.%0A%0A&repo=generaltranslation%2Fgt&pr=1687&platform=github"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://greptile-static-assets.s3.amazonaws.com/badges/FixAllInCodexDark.svg?v=3"><source
media="(prefers-color-scheme: light)"
srcset="https://greptile-static-assets.s3.amazonaws.com/badges/FixAllInCodex.svg?v=3"><img
alt="Fix All in Codex"
src="https://greptile-static-assets.s3.amazonaws.com/badges/FixAllInCodex.svg?v=3"
height="20"></picture></a>

<details><summary>Prompt To Fix All With AI</summary>

`````markdown
Fix the following 2 code review issues. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 2
packages/i18n/src/i18n-cache/translations-manager/utils/dictionary-helpers.ts:116-124
**Old `context` key now leaks into lookup options**

Previously, `context` was explicitly destructured out of `options` before the spread, so it was always stripped from the returned object. After this change, any object that still carries a `context` property (e.g. a JS dictionary written before the migration) will have `context` included in `rest` and therefore spread verbatim into the `DictionaryLookupOptions` result. Downstream consumers that treat unknown keys as template variables will then expose `context` as an interpolation variable — if the message contains `{context}`, it would be silently filled with the old metadata string rather than surfacing an error. A one-liner `const { $format, context: _context, ...rest } = options;` would preserve the old strip-and-discard behavior without resurrecting the compatibility mapping.

### Issue 2 of 2
packages/i18n/src/utils/extractVariables.ts:14-24
**`$_locales` no longer filtered — becomes a live template variable**

`$_locales` was previously excluded from `extractVariables` so it could never accidentally appear as an interpolation variable. After this removal, any un-migrated JS caller that still passes `$_locales` will have it treated as a user-defined placeholder. If the message string contains `{$_locales}` (unlikely but possible), it would be interpolated with the locale value instead of raising a missing-variable error. This is consistent with the broader deprecation removal, but unlike the `$locale` key (which remains excluded), there is no longer any guard that prevents the old key from leaking into rendered output.


`````

</details>

<sub>Reviews (1): Last reviewed commit: ["fix: format
issue"](ffc7be8)
| [Re-trigger
Greptile](https://app.greptile.com/api/retrigger?id=40596073)</sub>

> Greptile also left **1 inline comment** on this PR.

<!-- /greptile_comment -->
## Summary

`gt-next` was published **CJS-only** (no `import` export condition).
Because the app entry imports `gt-next`, bundlers resolved the whole gt
dependency graph — `gt-react`, `@generaltranslation/react-core`,
`generaltranslation`, `gt-i18n`, `@generaltranslation/format` — through
the `require` condition, pulling CJS variants onto the client. CJS can't
be tree-shaken, so the browser received `gt-next`'s own server/error
code (e.g. ~11 kB of `createErrors` diagnostic strings) plus unused
exports from every dependency.

This PR gives `gt-next` a **dual ESM/CJS build**: it now emits unbundled
`.mjs` alongside `.js`, and every `exports` subpath gains an `import`
condition (preserving `react-server`/`browser`/`default` ordering).
Bundlers now resolve the gt graph as ESM, enabling tree-shaking across
all gt packages.

No public API changes — purely packaging.

## Measured impact

Built a real Next.js app (Turbopack) against these packages and
attributed bundle bytes per module via `next experimental-analyze`. gt's
**client** footprint (deduped across routes):

| | gt total raw | gt total gz |
|---|---|---|
| before (CJS) | 304.9 kB | 96.0 kB |
| after (ESM) | 236.0 kB | **73.9 kB (−23%)** |

`gt-next`'s own client code drops to **0** (including the leaked error
strings); `generaltranslation`, `gt-i18n`, and `gt-react` each shed
their dead exports.

## Notes

- This is the first of two related changes. A follow-up PR will unbundle
`@generaltranslation/react-core` so its `pure`/`components` entries can
also tree-shake — that change reverses an existing "independent
entrypoints" guard and is being reviewed separately. Layered on this PR
it takes the combined reduction to ~40% gz.
- Mirrors the dual-format setup already used by `gt-react`
(`createUseClientBoundaryPlugin` with `.js`/`.mjs` output extensions).

<!-- greptile_comment -->

<h3>Greptile Summary</h3>

This PR gives `gt-next` a dual ESM/CJS build by adding a second tsdown
pass that emits `.mjs` files and wiring every `exports` subpath with an
`import` condition, enabling tree-shaking across the entire gt
dependency graph for downstream bundlers. No public API changes are
made.

- **tsdown config**: A second `createTsdownUnbundleConfig` block is
added with `format: 'esm'`, `clean: false` (to preserve the CJS output),
`outExtensions` mapping to `.mjs`, and `polyfillRequire: false` to
suppress the `createRequire`-from-`node:module` shim that would break
client bundlers (Turbopack/webpack) reachable from ESM init paths.
- **package.json exports**: All subpath exports gain a flat `import`
condition; the root `.` entry nests `import`/`default` inside
`react-server` and `browser` conditions, preserving correct environment
routing.
- **sideEffects**: `index.rsc.*`, `index.client.*`, and
`utils/client-boundary.*` are added to the `sideEffects` list so their
top-level initialization is not eliminated by tree-shaking.

<details open><summary><h3>Confidence Score: 5/5</h3></summary>

Pure packaging change — no runtime logic altered, no API surface
changed, and the dual-build pattern mirrors what gt-react already uses.

All three changed files touch only build configuration and the exports
map. The ESM build is additive (clean: false preserves the CJS
artifacts), polyfillRequire: false is a well-motivated guard against a
concrete Turbopack/webpack breakage, and the sideEffects expansion
correctly covers the newly ESM-exposed init files. TypeScript type
resolution falls back correctly to the CJS .d.ts files when .d.mts
counterparts are absent.

No files require special attention.
</details>


<details><summary><h3>Important Files Changed</h3></summary>




| Filename | Overview |
|----------|----------|
| .changeset/gt-next-esm-build.md | Changeset documenting the ESM/CJS
dual build for gt-next; numbers in changeset (~78 kB) differ slightly
from the PR description table (73.9 kB) but this is not a code concern.
|
| packages/next/package.json | Adds `import` conditions to all subpath
exports pointing to .mjs files; correctly expands sideEffects to cover
index.rsc.* and index.client.* (needed for top-level init); top-level
types ordering looks correct; missing legacy "module" field compared to
gt-react (P2, already noted in a prior review). |
| packages/next/tsdown.config.mts | Adds a second ESM build pass (clean:
false to preserve CJS output, polyfillRequire: false to avoid injecting
a node:module shim, .mjs output extension); mirrors the dual-build
pattern used by gt-react. |

</details>


<details><summary><h3>Flowchart</h3></summary>

<a href="#gh-light-mode-only">

```mermaid
%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A["Bundler imports gt-next"] --> B{Condition check}
    B -->|react-server| C{import condition?}
    C -->|ESM / import| D["index.rsc.mjs ✅ tree-shakeable"]
    C -->|CJS / default| E["index.rsc.js"]
    B -->|browser| F{import condition?}
    F -->|ESM / import| G["index.client.mjs ✅ tree-shakeable"]
    F -->|CJS / default| H["index.client.js"]
    B -->|server/default| I{import condition?}
    I -->|ESM / import| J["index.server.mjs ✅ tree-shakeable"]
    I -->|CJS / default| K["index.server.js"]
    B -->|TypeScript| L["index.types.d.ts"]
```

</a>
<a href="#gh-dark-mode-only">

```mermaid
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
    A["Bundler imports gt-next"] --> B{Condition check}
    B -->|react-server| C{import condition?}
    C -->|ESM / import| D["index.rsc.mjs ✅ tree-shakeable"]
    C -->|CJS / default| E["index.rsc.js"]
    B -->|browser| F{import condition?}
    F -->|ESM / import| G["index.client.mjs ✅ tree-shakeable"]
    F -->|CJS / default| H["index.client.js"]
    B -->|server/default| I{import condition?}
    I -->|ESM / import| J["index.server.mjs ✅ tree-shakeable"]
    I -->|CJS / default| K["index.server.js"]
    B -->|TypeScript| L["index.types.d.ts"]
```

</a>
</details>


<!-- greptile_failed_comments -->
<details open><summary><h3>Comments Outside Diff (1)</h3></summary>

1. `packages/next/package.json`, line 5
([link](https://github.com/generaltranslation/gt/blob/b0b44e681408927a7cc45f1ca0a5a1434c1f603a/packages/next/package.json#L5))

<a href="#"><img alt="P2"
src="https://greptile-static-assets.s3.amazonaws.com/badges/p2.svg?v=9"
align="top"></a> The `"module"` legacy field is present in `gt-react`
(`"module": "./dist/index.server.mjs"`) but absent from `gt-next`.
Webpack 4 and other bundlers that predate the `"exports"` field use
`"module"` as their ESM signal; without it they fall back to `"main"`
(the CJS variant) and the tree-shaking gains won't materialise in those
environments. Since `gt-next` is Next.js-specific and Next.js itself
uses Webpack 5/Turbopack (both `"exports"`-aware), the practical impact
is low — but adding the field would keep the two packages consistent.

   

   <details><summary>Prompt To Fix With AI</summary>

   `````markdown
   This is a comment left during a code review.
   Path: packages/next/package.json
   Line: 5

   Comment:
The `"module"` legacy field is present in `gt-react` (`"module":
"./dist/index.server.mjs"`) but absent from `gt-next`. Webpack 4 and
other bundlers that predate the `"exports"` field use `"module"` as
their ESM signal; without it they fall back to `"main"` (the CJS
variant) and the tree-shaking gains won't materialise in those
environments. Since `gt-next` is Next.js-specific and Next.js itself
uses Webpack 5/Turbopack (both `"exports"`-aware), the practical impact
is low — but adding the field would keep the two packages consistent.

   

   How can I resolve this? If you propose a fix, please make it concise.
   `````
   </details>

Note: If this suggestion doesn't match your team's coding style, reply
to this and let me know. I'll remember it for next time!

<a
href="https://app.greptile.com/ide/claude-code?prompt=This%20is%20a%20comment%20left%20during%20a%20code%20review.%0APath%3A%20packages%2Fnext%2Fpackage.json%0ALine%3A%205%0A%0AComment%3A%0AThe%20%60%22module%22%60%20legacy%20field%20is%20present%20in%20%60gt-react%60%20%28%60%22module%22%3A%20%22.%2Fdist%2Findex.server.mjs%22%60%29%20but%20absent%20from%20%60gt-next%60.%20Webpack%204%20and%20other%20bundlers%20that%20predate%20the%20%60%22exports%22%60%20field%20use%20%60%22module%22%60%20as%20their%20ESM%20signal%3B%20without%20it%20they%20fall%20back%20to%20%60%22main%22%60%20%28the%20CJS%20variant%29%20and%20the%20tree-shaking%20gains%20won't%20materialise%20in%20those%20environments.%20Since%20%60gt-next%60%20is%20Next.js-specific%20and%20Next.js%20itself%20uses%20Webpack%205%2FTurbopack%20%28both%20%60%22exports%22%60-aware%29%2C%20the%20practical%20impact%20is%20low%20%E2%80%94%20but%20adding%20the%20field%20would%20keep%20the%20two%20packages%20consistent.%0A%0A%60%60%60suggestion%0A%20%20%22main%22%3A%20%22dist%2Findex.server.js%22%2C%0A%20%20%22module%22%3A%20%22dist%2Findex.server.mjs%22%2C%0A%60%60%60%0A%0AHow%20can%20I%20resolve%20this%3F%20If%20you%20propose%20a%20fix%2C%20please%20make%20it%20concise.&repo=generaltranslation%2Fgt&pr=1688&platform=github"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://greptile-static-assets.s3.amazonaws.com/badges/FixInClaudeDark.svg?v=3"><source
media="(prefers-color-scheme: light)"
srcset="https://greptile-static-assets.s3.amazonaws.com/badges/FixInClaude.svg?v=3"><img
alt="Fix in Claude Code"
src="https://greptile-static-assets.s3.amazonaws.com/badges/FixInClaude.svg?v=3"
height="20"></picture></a> <a
href="https://app.greptile.com/api/ide/codex?prompt=IMPORTANT%3A%20Work%20in%20the%20repository%20%22generaltranslation%2Fgt%22%20on%20the%20existing%20branch%20%22bg%2Fodysseus-gt-next-esm%22.%20Checkout%20that%20branch%20%E2%80%94%20do%20NOT%20create%20a%20new%20branch%20or%20open%20a%20new%20PR.%20Push%20your%20changes%20to%20%22bg%2Fodysseus-gt-next-esm%22.%0A%0AThis%20is%20a%20comment%20left%20during%20a%20code%20review.%0APath%3A%20packages%2Fnext%2Fpackage.json%0ALine%3A%205%0A%0AComment%3A%0AThe%20%60%22module%22%60%20legacy%20field%20is%20present%20in%20%60gt-react%60%20%28%60%22module%22%3A%20%22.%2Fdist%2Findex.server.mjs%22%60%29%20but%20absent%20from%20%60gt-next%60.%20Webpack%204%20and%20other%20bundlers%20that%20predate%20the%20%60%22exports%22%60%20field%20use%20%60%22module%22%60%20as%20their%20ESM%20signal%3B%20without%20it%20they%20fall%20back%20to%20%60%22main%22%60%20%28the%20CJS%20variant%29%20and%20the%20tree-shaking%20gains%20won't%20materialise%20in%20those%20environments.%20Since%20%60gt-next%60%20is%20Next.js-specific%20and%20Next.js%20itself%20uses%20Webpack%205%2FTurbopack%20%28both%20%60%22exports%22%60-aware%29%2C%20the%20practical%20impact%20is%20low%20%E2%80%94%20but%20adding%20the%20field%20would%20keep%20the%20two%20packages%20consistent.%0A%0A%60%60%60suggestion%0A%20%20%22main%22%3A%20%22dist%2Findex.server.js%22%2C%0A%20%20%22module%22%3A%20%22dist%2Findex.server.mjs%22%2C%0A%60%60%60%0A%0AHow%20can%20I%20resolve%20this%3F%20If%20you%20propose%20a%20fix%2C%20please%20make%20it%20concise.&repo=generaltranslation%2Fgt&pr=1688&platform=github"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://greptile-static-assets.s3.amazonaws.com/badges/FixInCodexDark.svg?v=3"><source
media="(prefers-color-scheme: light)"
srcset="https://greptile-static-assets.s3.amazonaws.com/badges/FixInCodex.svg?v=3"><img
alt="Fix in Codex"
src="https://greptile-static-assets.s3.amazonaws.com/badges/FixInCodex.svg?v=3"
height="20"></picture></a>
</details>

<!-- /greptile_failed_comments -->

<sub>Reviews (3): Last reviewed commit: ["fix(gt-next): mark
client-boundary as
ha..."](006591d)
| [Re-trigger
Greptile](https://app.greptile.com/api/retrigger?id=40606533)</sub>

<!-- /greptile_comment -->
## Summary
- replace deprecated inline/dictionary option aliases with focused
translation option types
- type dictionary t() options as interpolation variables and dictionary
leaf metadata separately
- trim higher-level public type barrels to expose only
GTTranslationOptions and RuntimeTranslationOptions

## Tests
- pnpm --filter gt-i18n typecheck
- pnpm --filter @generaltranslation/react-core typecheck
- pnpm --filter gt-next typecheck
- pnpm --filter gt-react-native typecheck
- pnpm --filter gt-node typecheck
- pnpm --filter gt-i18n test

<!-- greptile_comment -->

<h3>Greptile Summary</h3>

This PR refactors the translation option type system in the `gt-i18n`
monorepo, replacing a set of deprecated alias types
(`InlineTranslationOptions`, `InlineResolveOptions`,
`DictionaryTranslationOptions`, `BaseTranslationOptions`,
`ResolutionOptions`, `DictionaryOptions`) with a cleaner, focused
hierarchy (`GTTranslationOptions`, `TranslationVariables`,
`TranslationMetadata`, `TranslationOptions`, `LookupOptionsFor`). It
also trims the public-facing type barrels to expose only
`GTTranslationOptions` and `RuntimeTranslationOptions`, hiding internal
plumbing types.

- **Type hierarchy flattened and renamed**:
`InlineTranslationOptionsFields` → `TranslationMetadata`;
`InlineTranslationOptions` → internal `TranslationOptions`; user-facing
`GTTranslationOptions` now deliberately excludes `$_fallback` and
`$_source` while keeping `$_hash`.
- **`t()` options narrowed**: Dictionary `t()` and `useTranslations()`
now accept `TranslationVariables` (plain `Record<string, unknown>`)
instead of `DictionaryTranslationOptions`, reflecting that metadata
lives on the dictionary leaf, not the call site.
- **`MFunctionType` tightened**: The previous `Record<string, any>`
escape-hatch on `m()`'s `options` parameter is replaced with the proper
`GTTranslationOptions`.

<details open><summary><h3>Confidence Score: 5/5</h3></summary>

Safe to merge — this is a pure type-level refactoring with no runtime
logic changes, and the new type hierarchy is structurally equivalent to
what it replaces.

All renamed types resolve to the same structural shapes as their
predecessors at the TypeScript level. The narrowing of
DictionaryLookupOptions (dropping $id, $locale, $_hash fields never
actually used in the dictionary path) is intentional and correct. The
MFunctionType fix (replacing Record<string, any> with
GTTranslationOptions) is a strict improvement. The major-version
changeset is correct since public-facing type names changed.

No files require special attention. The options.ts rewrite is the most
impactful file and it reads cleanly.
</details>

<details><summary><h3>Important Files Changed</h3></summary>

| Filename | Overview |
|----------|----------|
| packages/i18n/src/translation-functions/types/options.ts | Core type
definitions refactored cleanly; new hierarchy is structurally equivalent
to old one; DictionaryLookupOptions intentionally narrowed to remove
fields unused in the dictionary path. |
| packages/i18n/src/translation-functions/types/functions.ts |
MFunctionType now uses GTTranslationOptions instead of Record<string,
any>; all function types updated consistently with the new type names. |
| packages/i18n/src/types.ts | Public barrel trimmed correctly; internal
types like DictionaryLookupOptions removed from public exports; new
types like LookupOptionsFor and DictionaryEntryOptions added. |
|
packages/i18n/src/i18n-cache/translations-manager/utils/dictionary-helpers.ts
| isDictionaryOptions renamed to isDictionaryLeafOptions; now imports
DictionaryEntryOptions/DictionaryLookupOptions from central options.ts
rather than defining them locally. |
| packages/i18n/src/translation-functions/internal/helpers.ts |
ResolutionOptions replaced by LookupOptionsFor across all six resolve
functions; behaviorally equivalent. |
| packages/react-core/src/hooks/useTranslations.ts |
DictionaryTranslationOptions replaced by TranslationVariables in
useTranslations and UseTranslationsFunction; structurally identical
types. |
| packages/i18n/src/translation-functions/msg/decodeOptions.ts | Return
type widened from InlineTranslationOptions to TranslationOptions (the
full internal shape), which is correct since decoded options can carry
any metadata field. |
| .changeset/simplify-translation-option-types.md | All six affected
packages correctly marked as major version bumps in the changeset. |

</details>

<details><summary><h3>Class Diagram</h3></summary>

<a href="#gh-light-mode-only">

```mermaid
%%{init: {'theme': 'neutral'}}%%
classDiagram
    class TranslationVariables {
        <<Record~string,unknown~>>
    }
    class TranslationMetadata {
        +$context?: string
        +$id?: string
        +$format?: StringFormat
        +$locale?: string
        +$_hash?: string
        +$_fallback?: string
        +$_source?: string
        +$maxChars?: number
    }
    class TranslationOptions {
        <<internal full shape>>
    }
    class GTTranslationOptions {
        <<user-facing gt() m()>>
        +$context?: string
        +$id?: string
        +$format?: StringFormat
        +$locale?: string
        +$_hash?: string
        +$maxChars?: number
    }
    class DictionaryEntryOptions {
        <<dictionary leaf metadata>>
        +$context?: string
        +$format?: StringFormat
        +$maxChars?: number
    }
    class DictionaryLookupOptions {
        <<internal>>
        +$format: StringFormat
    }
    class RuntimeTranslationOptions {
        <<user-facing tx()>>
        +$format?: DataFormat
    }
    class EncodedTranslationOptions {
        <<msg() output>>
        +$_hash: string
        +$_source: string
    }
    class LookupOptionsFor~T~ {
        <<generic resolution>>
    }
    TranslationVariables <|-- TranslationOptions : intersects
    TranslationMetadata <|-- TranslationOptions : intersects
    TranslationVariables <|-- GTTranslationOptions : intersects
    TranslationMetadata <|-- GTTranslationOptions : Pick subset
    GTTranslationOptions <|-- EncodedTranslationOptions : extends
    TranslationVariables <|-- DictionaryEntryOptions : intersects
    TranslationMetadata <|-- DictionaryEntryOptions : Pick subset
    DictionaryEntryOptions <|-- DictionaryLookupOptions : extends
    TranslationVariables <|-- RuntimeTranslationOptions : intersects
    TranslationMetadata <|-- RuntimeTranslationOptions : Omit~$id,$format~
    TranslationOptions <|-- LookupOptionsFor~T~ : when T is StringFormat
```

</a>
<a href="#gh-dark-mode-only">

```mermaid
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
classDiagram
    class TranslationVariables {
        <<Record~string,unknown~>>
    }
    class TranslationMetadata {
        +$context?: string
        +$id?: string
        +$format?: StringFormat
        +$locale?: string
        +$_hash?: string
        +$_fallback?: string
        +$_source?: string
        +$maxChars?: number
    }
    class TranslationOptions {
        <<internal full shape>>
    }
    class GTTranslationOptions {
        <<user-facing gt() m()>>
        +$context?: string
        +$id?: string
        +$format?: StringFormat
        +$locale?: string
        +$_hash?: string
        +$maxChars?: number
    }
    class DictionaryEntryOptions {
        <<dictionary leaf metadata>>
        +$context?: string
        +$format?: StringFormat
        +$maxChars?: number
    }
    class DictionaryLookupOptions {
        <<internal>>
        +$format: StringFormat
    }
    class RuntimeTranslationOptions {
        <<user-facing tx()>>
        +$format?: DataFormat
    }
    class EncodedTranslationOptions {
        <<msg() output>>
        +$_hash: string
        +$_source: string
    }
    class LookupOptionsFor~T~ {
        <<generic resolution>>
    }
    TranslationVariables <|-- TranslationOptions : intersects
    TranslationMetadata <|-- TranslationOptions : intersects
    TranslationVariables <|-- GTTranslationOptions : intersects
    TranslationMetadata <|-- GTTranslationOptions : Pick subset
    GTTranslationOptions <|-- EncodedTranslationOptions : extends
    TranslationVariables <|-- DictionaryEntryOptions : intersects
    TranslationMetadata <|-- DictionaryEntryOptions : Pick subset
    DictionaryEntryOptions <|-- DictionaryLookupOptions : extends
    TranslationVariables <|-- RuntimeTranslationOptions : intersects
    TranslationMetadata <|-- RuntimeTranslationOptions : Omit~$id,$format~
    TranslationOptions <|-- LookupOptionsFor~T~ : when T is StringFormat
```

</a>
</details>

<sub>Reviews (3): Last reviewed commit: ["refactor: simplify translation
option
ty..."](4417d04)
| [Re-trigger
Greptile](https://app.greptile.com/api/retrigger?id=40623023)</sub>

<!-- /greptile_comment -->
## Summary

Each `@generaltranslation/react-core` entrypoint (`pure`, `hooks`,
`components`, `components-rsc`, `cookies`) was built as a **single
pre-bundled, minified file**, so importing one component pulled the
whole entry and a downstream bundler couldn't drop unused code.

This PR switches react-core to an **unbundled** build (per-module
output, ESM + CJS): entrypoints become thin re-export barrels over
granular sibling modules. Output filenames change from
`*.cjs.min.cjs`/`*.esm.min.mjs` to `*.cjs`/`*.mjs`, resolved through the
`exports` map — no consumer-facing API change.

## Measured impact

When react-core is consumed via ESM, this lets the bundler tree-shake
the unused half of `pure`/`components`. react-core's **client**
footprint (real Next.js app, `next experimental-analyze`):

| | react-core client raw | react-core client gz |
|---|---|---|
| bundled (before) | 158.2 kB | 49.1 kB |
| unbundled (after) | **82.0 kB** | **33.0 kB** |

> ⚠️ This reduction only materializes when react-core is resolved as
**ESM**. CJS can't be tree-shaken, so on its own this PR barely moves
gzip (it just de-duplicates raw text). It pays off layered on #1688
(`gt-next` ESM build); together the two cut gt's total client bundle
~40% gzip.

## Reviewer note — intentional architecture reversal

This reverses the previous "independent entrypoints **without shared
chunks**" design. The `react-core-package.test.ts` guard that enforced
per-entry bundling is rewritten to assert the new unbundled layout
(entrypoints present in both formats; many granular runtime modules
emitted). The two behavioral export-loading tests are unchanged. All 267
react-core tests pass.

## Validation

- `pnpm --filter @generaltranslation/react-core test` → 267 passing
- Full app build (this + #1688) compiles and runs; gt-react/gt-next
consume the unbundled subpaths via the `exports` map with no changes.

<!-- greptile_comment -->

<h3>Greptile Summary</h3>

This PR switches `@generaltranslation/react-core` from a per-entrypoint
pre-bundled build to an unbundled (per-module) ESM + CJS output,
enabling downstream bundlers to tree-shake unused components and hooks.
The measured client footprint drops from 158 kB → 82 kB raw (49 kB → 33
kB gzip) when consumed as ESM.

- **Build config** (`tsdown.config.mts`): replaces
`createTsdownMinifiedDualFormatConfig` with two
`createTsdownUnbundleConfig` calls (CJS with `dts:true`, ESM with
`clean:false`); `@generaltranslation/format` and `generaltranslation`
subpath imports are intentionally externalized instead of inlined.
- **Package exports** (`package.json`): filename extensions updated from
`*.cjs.min.cjs`/`*.esm.min.mjs` to `*.cjs`/`*.mjs`; `sideEffects: false`
is already set, which is required for tree-shaking to work.
- **Tests**: TypeScript AST-based bundling assertion removed and
replaced with a file-count heuristic asserting the unbundled layout; the
two behavioral load tests (CJS and ESM named-export checks) are kept
unchanged.

<details open><summary><h3>Confidence Score: 5/5</h3></summary>

Safe to merge — the change is a pure build-output restructuring with no
runtime logic changes and no public API surface modifications.

The only changes are to the build configuration, the exports map
filename extensions, the size-limit path, and the test assertions. The
two behavioral load tests that verify named exports from both CJS and
ESM entrypoints are unchanged and confirmed passing (267 tests). The
`sideEffects: false` flag that makes tree-shaking effective was already
present. The dependency externalization (`@generaltranslation/format`,
`generaltranslation` subpaths) is intentional and documented in the
changeset, and both packages remain listed as `dependencies` in
`package.json` so they are guaranteed to be installed.

No files require special attention.
</details>

<details><summary><h3>Important Files Changed</h3></summary>

| Filename | Overview |
|----------|----------|
| packages/react-core/tsdown.config.mts | Switches from
createTsdownMinifiedDualFormatConfig to createTsdownUnbundleConfig; CJS
gets dts:true, ESM gets clean:false; both get minify:true spread in. No
alwaysBundle overrides needed since generaltranslation/format subpaths
are now intentionally externalized. |
| packages/react-core/package.json | Exports map updated from
*.cjs.min.cjs/esm.min.mjs to *.cjs/*.mjs; all five entrypoints updated
consistently; no other consumer-facing changes. |
| packages/react-core/src/__tests__/react-core-package.test.ts | Test
rewritten to assert unbundled layout: barrel presence in both formats
plus a file-count check; TypeScript AST-based bundling check removed;
two behavioral load tests are unchanged. |
| .size-limit.cjs | Path updated from *.esm.min.mjs to *.mjs to match
new output extensions; 48 kB limit preserved. |
| .changeset/react-core-unbundle.md | New patch-level changeset
describing the unbundled build, removed format sub-dependency inlining,
and filename extension changes. |

</details>

<sub>Reviews (2): Last reviewed commit: ["perf(react-core): unbundle
build output
..."](c6441df)
| [Re-trigger
Greptile](https://app.greptile.com/api/retrigger?id=40606757)</sub>

<!-- /greptile_comment -->
This PR was opened by the [Changesets
release](https://github.com/changesets/action) GitHub action. When
you're ready to do a release, you can merge this and the packages will
be published to npm automatically. If you're not ready to do a release
yet, that's fine, whenever you add more changesets to odysseus, this PR
will be updated.

⚠️⚠️⚠️⚠️⚠️⚠️

`odysseus` is currently in **pre mode** so this branch has prereleases
rather than normal releases. If you want to exit prereleases, run
`changeset pre exit` on `odysseus`.

⚠️⚠️⚠️⚠️⚠️⚠️

# Releases
## gt-i18n@1.0.0-odysseus.2

### Major Changes

- [#1687](#1687)
[`41c938c`](41c938c)
Thanks [@bgub](https://github.com/bgub)! - Remove deprecated translation
options. The `context` dictionary option has been removed in favor of
`$context`, and the `$_locales` inline option has been removed in favor
of `$locale`.

- [#1690](#1690)
[`b3c3b9a`](b3c3b9a)
Thanks [@bgub](https://github.com/bgub)! - Simplify translation option
types. Replace deprecated inline and dictionary option aliases with
`GTTranslationOptions`, use interpolation variables for dictionary `t()`
options, and trim higher-level type exports to avoid exposing internal
translation option fields.

### Patch Changes

- Updated dependencies
[[`4b97bc3`](4b97bc3),
[`020c6bd`](020c6bd)]:
  - @generaltranslation/format@0.1.2-odysseus.1
  - generaltranslation@9.0.0-odysseus.2
  - @generaltranslation/supported-locales@2.1.2-odysseus.2
## gt-next@11.0.0-odysseus.5

### Major Changes

- [#1690](#1690)
[`b3c3b9a`](b3c3b9a)
Thanks [@bgub](https://github.com/bgub)! - Simplify translation option
types. Replace deprecated inline and dictionary option aliases with
`GTTranslationOptions`, use interpolation variables for dictionary `t()`
options, and trim higher-level type exports to avoid exposing internal
translation option fields.

### Patch Changes

- [#1685](#1685)
[`795147f`](795147f)
Thanks [@ErnestM1234](https://github.com/ErnestM1234)! - Allow client
i18n cache expiry to default to no expiry while preserving explicit
cache expiry configuration.

- [#1688](#1688)
[`2836b96`](2836b96)
Thanks [@bgub](https://github.com/bgub)! - Ship a dual ESM/CJS build for
`gt-next` and add `import` export conditions.

Previously `gt-next` was published as CJS only (no `import` condition),
so bundlers resolved the entire gt dependency graph (`gt-react`,
`@generaltranslation/react-core`, `generaltranslation`, `gt-i18n`,
`@generaltranslation/format`) through the `require` condition. That
forced CJS variants onto the client, which cannot be tree-shaken —
shipping `gt-next`'s own server/error code and unused dependency exports
to the browser.

`gt-next` now emits unbundled `.mjs` output alongside the existing `.js`
output, and every `exports` subpath exposes an `import` condition.
Bundlers now resolve the gt graph as ESM, enabling tree-shaking across
all gt packages. Measured on a real Next.js app, this cuts gt's total
client bundle by ~19% gzip (≈96 kB → ≈78 kB) with no API changes.

The ESM build sets `polyfillRequire: false` so rolldown does not inject
a `createRequire`-from-`node:module` shim (server-only `require()` calls
are provided `require` by the bundler), and marks the
`index.rsc`/`index.client` entrypoints as having side effects so their
top-level initialization is not tree-shaken away.

- [#1678](#1678)
[`4b97bc3`](4b97bc3)
Thanks [@ErnestM1234](https://github.com/ErnestM1234)! - Organize
package entrypoint exports and replace re-export-only imports with
direct export declarations.

- [#1676](#1676)
[`020c6bd`](020c6bd)
Thanks [@ErnestM1234](https://github.com/ErnestM1234)! - Remove default
exports from package entrypoints and internal source modules.

Use named imports for affected public entrypoints, including `import {
plugin } from 'gt-react-native/plugin'`. The `gt-next/link` entrypoint
keeps its default export to match `next/link`.

- Updated dependencies
[[`795147f`](795147f),
[`4b97bc3`](4b97bc3),
[`020c6bd`](020c6bd),
[`41c938c`](41c938c),
[`b3c3b9a`](b3c3b9a)]:
  - gt-react@11.0.0-odysseus.5
  - @generaltranslation/format@0.1.2-odysseus.1
  - generaltranslation@9.0.0-odysseus.2
  - @generaltranslation/react-core@11.0.0-odysseus.5
  - gt-i18n@1.0.0-odysseus.2
  - @generaltranslation/compiler@1.3.25-odysseus.2
  - @generaltranslation/supported-locales@2.1.2-odysseus.2
## gt-node@1.0.0-odysseus.2

### Major Changes

- [#1690](#1690)
[`b3c3b9a`](b3c3b9a)
Thanks [@bgub](https://github.com/bgub)! - Simplify translation option
types. Replace deprecated inline and dictionary option aliases with
`GTTranslationOptions`, use interpolation variables for dictionary `t()`
options, and trim higher-level type exports to avoid exposing internal
translation option fields.

### Patch Changes

- Updated dependencies
[[`4b97bc3`](4b97bc3),
[`020c6bd`](020c6bd),
[`41c938c`](41c938c),
[`b3c3b9a`](b3c3b9a)]:
  - generaltranslation@9.0.0-odysseus.2
  - gt-i18n@1.0.0-odysseus.2
## gt-react@11.0.0-odysseus.5

### Major Changes

- [#1690](#1690)
[`b3c3b9a`](b3c3b9a)
Thanks [@bgub](https://github.com/bgub)! - Simplify translation option
types. Replace deprecated inline and dictionary option aliases with
`GTTranslationOptions`, use interpolation variables for dictionary `t()`
options, and trim higher-level type exports to avoid exposing internal
translation option fields.

### Patch Changes

- [#1685](#1685)
[`795147f`](795147f)
Thanks [@ErnestM1234](https://github.com/ErnestM1234)! - Allow client
i18n cache expiry to default to no expiry while preserving explicit
cache expiry configuration.

- [#1676](#1676)
[`020c6bd`](020c6bd)
Thanks [@ErnestM1234](https://github.com/ErnestM1234)! - Remove default
exports from package entrypoints and internal source modules.

Use named imports for affected public entrypoints, including `import {
plugin } from 'gt-react-native/plugin'`. The `gt-next/link` entrypoint
keeps its default export to match `next/link`.

- Updated dependencies
[[`4b97bc3`](4b97bc3),
[`020c6bd`](020c6bd),
[`41c938c`](41c938c),
[`b3c3b9a`](b3c3b9a)]:
  - @generaltranslation/format@0.1.2-odysseus.1
  - generaltranslation@9.0.0-odysseus.2
  - @generaltranslation/react-core@11.0.0-odysseus.5
  - gt-i18n@1.0.0-odysseus.2
  - @generaltranslation/supported-locales@2.1.2-odysseus.2
## @generaltranslation/react-core@11.0.0-odysseus.5

### Major Changes

- [#1690](#1690)
[`b3c3b9a`](b3c3b9a)
Thanks [@bgub](https://github.com/bgub)! - Simplify translation option
types. Replace deprecated inline and dictionary option aliases with
`GTTranslationOptions`, use interpolation variables for dictionary `t()`
options, and trim higher-level type exports to avoid exposing internal
translation option fields.

### Patch Changes

- [#1676](#1676)
[`020c6bd`](020c6bd)
Thanks [@ErnestM1234](https://github.com/ErnestM1234)! - Remove default
exports from package entrypoints and internal source modules.

Use named imports for affected public entrypoints, including `import {
plugin } from 'gt-react-native/plugin'`. The `gt-next/link` entrypoint
keeps its default export to match `next/link`.

- Updated dependencies
[[`4b97bc3`](4b97bc3),
[`020c6bd`](020c6bd),
[`41c938c`](41c938c),
[`b3c3b9a`](b3c3b9a)]:
  - @generaltranslation/format@0.1.2-odysseus.1
  - generaltranslation@9.0.0-odysseus.2
  - gt-i18n@1.0.0-odysseus.2
  - @generaltranslation/supported-locales@2.1.2-odysseus.2
## gt-react-native@11.0.0-odysseus.5

### Major Changes

- [#1690](#1690)
[`b3c3b9a`](b3c3b9a)
Thanks [@bgub](https://github.com/bgub)! - Simplify translation option
types. Replace deprecated inline and dictionary option aliases with
`GTTranslationOptions`, use interpolation variables for dictionary `t()`
options, and trim higher-level type exports to avoid exposing internal
translation option fields.

### Patch Changes

- [#1678](#1678)
[`4b97bc3`](4b97bc3)
Thanks [@ErnestM1234](https://github.com/ErnestM1234)! - Organize
package entrypoint exports and replace re-export-only imports with
direct export declarations.

- [#1676](#1676)
[`020c6bd`](020c6bd)
Thanks [@ErnestM1234](https://github.com/ErnestM1234)! - Remove default
exports from package entrypoints and internal source modules.

Use named imports for affected public entrypoints, including `import {
plugin } from 'gt-react-native/plugin'`. The `gt-next/link` entrypoint
keeps its default export to match `next/link`.

- Updated dependencies
[[`4b97bc3`](4b97bc3),
[`020c6bd`](020c6bd),
[`41c938c`](41c938c),
[`b3c3b9a`](b3c3b9a)]:
  - @generaltranslation/format@0.1.2-odysseus.1
  - generaltranslation@9.0.0-odysseus.2
  - @generaltranslation/react-core@11.0.0-odysseus.5
  - gt-i18n@1.0.0-odysseus.2
  - @generaltranslation/supported-locales@2.1.2-odysseus.2
## gt@2.14.51-odysseus.2

### Patch Changes

- Updated dependencies
[[`4b97bc3`](4b97bc3),
[`020c6bd`](020c6bd)]:
  - @generaltranslation/format@0.1.2-odysseus.1
  - generaltranslation@9.0.0-odysseus.2
  - @generaltranslation/python-extractor@0.2.23-odysseus.2
  - @generaltranslation/supported-locales@2.1.2-odysseus.2
## @generaltranslation/compiler@1.3.25-odysseus.2

### Patch Changes

- Updated dependencies
[[`4b97bc3`](4b97bc3),
[`020c6bd`](020c6bd)]:
  - @generaltranslation/format@0.1.2-odysseus.1
  - generaltranslation@9.0.0-odysseus.2
## generaltranslation@9.0.0-odysseus.2

### Patch Changes

- [#1678](#1678)
[`4b97bc3`](4b97bc3)
Thanks [@ErnestM1234](https://github.com/ErnestM1234)! - Organize
package entrypoint exports and replace re-export-only imports with
direct export declarations.

- [#1676](#1676)
[`020c6bd`](020c6bd)
Thanks [@ErnestM1234](https://github.com/ErnestM1234)! - Remove default
exports from package entrypoints and internal source modules.

Use named imports for affected public entrypoints, including `import {
plugin } from 'gt-react-native/plugin'`. The `gt-next/link` entrypoint
keeps its default export to match `next/link`.

- Updated dependencies
[[`4b97bc3`](4b97bc3)]:
  - @generaltranslation/format@0.1.2-odysseus.1
## @generaltranslation/format@0.1.2-odysseus.1

### Patch Changes

- [#1678](#1678)
[`4b97bc3`](4b97bc3)
Thanks [@ErnestM1234](https://github.com/ErnestM1234)! - Organize
package entrypoint exports and replace re-export-only imports with
direct export declarations.
## gtx-cli@2.14.51-odysseus.2

### Patch Changes

- Updated dependencies []:
  - gt@2.14.51-odysseus.2
## locadex@1.0.186-odysseus.2

### Patch Changes

- Updated dependencies []:
  - gt@2.14.51-odysseus.2
## @generaltranslation/gt-next-lint@15.0.0-odysseus.5

### Patch Changes

- Updated dependencies
[[`795147f`](795147f),
[`2836b96`](2836b96),
[`4b97bc3`](4b97bc3),
[`020c6bd`](020c6bd),
[`b3c3b9a`](b3c3b9a)]:
  - gt-next@11.0.0-odysseus.5
## @generaltranslation/python-extractor@0.2.23-odysseus.2

### Patch Changes

- Updated dependencies
[[`4b97bc3`](4b97bc3),
[`020c6bd`](020c6bd)]:
  - generaltranslation@9.0.0-odysseus.2
## gt-sanity@2.0.18-odysseus.4

### Patch Changes

- [#1678](#1678)
[`4b97bc3`](4b97bc3)
Thanks [@ErnestM1234](https://github.com/ErnestM1234)! - Organize
package entrypoint exports and replace re-export-only imports with
direct export declarations.

- Updated dependencies
[[`4b97bc3`](4b97bc3),
[`020c6bd`](020c6bd)]:
  - generaltranslation@9.0.0-odysseus.2
## @generaltranslation/supported-locales@2.1.2-odysseus.2

### Patch Changes

- Updated dependencies
[[`4b97bc3`](4b97bc3),
[`020c6bd`](020c6bd)]:
  - generaltranslation@9.0.0-odysseus.2
## gt-tanstack-start@11.0.0-odysseus.5

### Patch Changes

- Updated dependencies
[[`795147f`](795147f),
[`4b97bc3`](4b97bc3),
[`020c6bd`](020c6bd),
[`41c938c`](41c938c),
[`b3c3b9a`](b3c3b9a)]:
  - gt-react@11.0.0-odysseus.5
  - generaltranslation@9.0.0-odysseus.2
  - @generaltranslation/react-core@11.0.0-odysseus.5
  - gt-i18n@1.0.0-odysseus.2

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
@bgub bgub requested a review from a team as a code owner June 30, 2026 00:12
Base automatically changed from odysseus to main July 4, 2026 02:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants