Skip to content

perf: add runtime translation functions#1782

Open
bgub wants to merge 201 commits into
mainfrom
bg/core-runtime-functions-experiment
Open

perf: add runtime translation functions#1782
bgub wants to merge 201 commits into
mainfrom
bg/core-runtime-functions-experiment

Conversation

@bgub

@bgub bgub commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds a function-based generaltranslation/runtime entrypoint for runtime translation and moves framework runtime paths off the full GT class/admin client.

  • add translate / translateMany runtime functions in generaltranslation/runtime
  • switch gt-i18n, gt-next, gt-react, and @generaltranslation/react-core runtime code to use runtime functions, I18nConfig, or LocaleConfig instead of constructing GT
  • remove the legacy getGTClass / useGTClass runtime surfaces for Odysseus
  • update middleware locale resolution to use LocaleConfig

Public API Changes

Added:

  • generaltranslation/runtime
    • translate
    • translateMany
    • RuntimeTranslateConfig
    • RuntimeTranslateOptions

Removed:

  • gt-i18n: getGTClass
  • gt-react: getGTClass, useGTClass
  • gt-react-native: getGTClass, useGTClass
  • gt-next: getGTClass, useGTClass
  • @generaltranslation/react-core/hooks: useGTClass
  • @generaltranslation/react-core/pure: getGTClass
  • I18nConfig#getGTClass()

Bundle Impact

Measured earlier against ~/code/bengubler.com with pnpm next experimental-analyze --output using temporary site copies:

  • client static chunks: unchanged on that app
  • analyzer module count: 3347 -> 3338 (-9)
  • GT-related module count: 542 -> 537 (-5)
  • normal app routes: about -1.6 KiB gzip per route
  • middleware: about -10.6 KiB gzip
  • aggregate route analyzer gzip: about -32.6 KiB

Verification

  • git diff --check
  • pnpm --filter generaltranslation typecheck
  • pnpm --filter generaltranslation build
  • pnpm --filter generaltranslation test
  • pnpm --filter gt-i18n typecheck
  • pnpm --filter gt-i18n build
  • pnpm --filter gt-i18n test
  • pnpm --filter @generaltranslation/react-core typecheck
  • pnpm --filter @generaltranslation/react-core build
  • pnpm --filter @generaltranslation/react-core test
  • pnpm --filter gt-react typecheck
  • pnpm --filter gt-react build
  • pnpm --filter gt-react test
  • pnpm --filter gt-react-native typecheck
  • pnpm --filter gt-next typecheck
  • pnpm --filter gt-next build
  • pnpm --filter gt-next test:js

Greptile Summary

Introduces a lightweight generaltranslation/runtime entrypoint with translate / translateMany functions and migrates all framework runtime paths (gt-i18n, gt-next, gt-react, gt-react-native, @generaltranslation/react-core) off the full GT class. The middleware locale resolver is also switched from GT to the smaller LocaleConfig.

  • New packages/core/src/runtime.ts exports translate, translateMany, RuntimeTranslateConfig, RuntimeTranslateOptions, and re-exports all @generaltranslation/format utilities, becoming the single runtime import target.
  • I18nConfig replaces getGTClass() with first-class translate(), translateMany(), and getRegionProperties() methods; the createTranslateManyFactory helper is updated to accept a plain function instead of a GT-shaped object.
  • All getGTClass / useGTClass exports are removed from every framework surface as breaking changes (covered by the changeset).

Confidence Score: 4/5

The refactor is well-scoped and the removed getGTClass/useGTClass surfaces are cleanly excised across all packages; the two concerns are minor and neither blocks the core translation flow under normal operation.

The migration from the full GT class to LocaleConfig + runtime functions is consistent and the test suite passes. The translate wrapper's unchecked results[0] access and the redundant Array.isArray branch in I18nConfig.translateMany are the only non-trivial findings, both limited to the newly added code.

packages/core/src/runtime.ts (the translate bounds check) and packages/i18n/src/i18n-config/I18nConfig.ts (the duplicate translateMany branches) are worth a second look before merge.

Important Files Changed

Filename Overview
packages/core/src/runtime.ts New entrypoint for runtime translation; translate delegates to translateMany and returns results[0] without a bounds check, which could surface undefined to callers if the API returns an empty result.
packages/i18n/src/i18n-config/I18nConfig.ts Removes getGTClass and adds translate, translateMany, and getRegionProperties; the translateMany implementation has two identical branches (only the Array.isArray type-guard differs), making the conditional dead code.
packages/next/src/middleware-dir/createNextMiddleware.ts Replaces new GT({...}) with new LocaleConfig({...}) for locale validation; behaviour is equivalent since only BCP 47 validity and approve-list checks are used.
packages/next/src/middleware-dir/utils.ts Type of gt parameter changed to LocaleConfig; all call sites updated consistently with no logic changes.
packages/i18n/src/i18n-cache/translations-manager/utils/createTranslateMany.ts Replaces TranslateManyClient object type with a plain function type RuntimeTranslateMany; callers updated accordingly.
packages/react-core/src/hooks/useInternalRegionSelector.ts Drops getGTClass and calls i18nConfig.getRegionProperties(r, locale) directly; locale (current user locale) is now explicitly forwarded as targetLocale, which is the intended display context.
packages/react-core/src/components/variables/Currency.shared.ts Drops intermediate gt instance; calls getI18nConfig().formatCurrency() with an added explicit undefined for the intermediate parameter — straightforward refactor.
packages/core/package.json Adds ./runtime export conditions (CJS + ESM) and corresponding TypeScript path mappings; follows the existing export pattern exactly.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[Client / Framework Code] --> B{translate or translateMany?}
    B -->|translate| C["translate(source, options, config)"]
    B -->|translateMany| D["translateMany(sources, options, config)"]
    C --> D
    D --> E[validateRuntimeAuth\napiKey + projectId check]
    E --> F[new LocaleConfig\nsourceLocale + locales + customMapping]
    F --> G[Resolve targetLocale\nfrom options or config]
    G --> H["_translateMany(sources, resolvedOptions, config)"]
    H --> I[GT API]

    J[I18nConfig.translate] --> K["runtimeTranslate(source, options, getRuntimeTranslateConfig())"]
    K --> C
    L[I18nConfig.translateMany] --> M["runtimeTranslateMany(sources, options, getRuntimeTranslateConfig())"]
    M --> D

    N[Middleware] --> O[new LocaleConfig\nfrom envParams]
    O --> P["localeConfig.isValidLocale()\nlocaleConfig.determineLocale()"]
    P --> Q[Locale resolution\n& routing]

    R[Component Variables\nCurrency/DateTime/Num/RelativeTime] --> S["getI18nConfig().formatXxx()"]
    S --> T[LocaleConfig formatting methods]
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"}}}%%
flowchart TD
    A[Client / Framework Code] --> B{translate or translateMany?}
    B -->|translate| C["translate(source, options, config)"]
    B -->|translateMany| D["translateMany(sources, options, config)"]
    C --> D
    D --> E[validateRuntimeAuth\napiKey + projectId check]
    E --> F[new LocaleConfig\nsourceLocale + locales + customMapping]
    F --> G[Resolve targetLocale\nfrom options or config]
    G --> H["_translateMany(sources, resolvedOptions, config)"]
    H --> I[GT API]

    J[I18nConfig.translate] --> K["runtimeTranslate(source, options, getRuntimeTranslateConfig())"]
    K --> C
    L[I18nConfig.translateMany] --> M["runtimeTranslateMany(sources, options, getRuntimeTranslateConfig())"]
    M --> D

    N[Middleware] --> O[new LocaleConfig\nfrom envParams]
    O --> P["localeConfig.isValidLocale()\nlocaleConfig.determineLocale()"]
    P --> Q[Locale resolution\n& routing]

    R[Component Variables\nCurrency/DateTime/Num/RelativeTime] --> S["getI18nConfig().formatXxx()"]
    S --> T[LocaleConfig formatting methods]
Loading

Fix All in Claude Code Fix All in Codex

Prompt To Fix All With AI
Fix the following 2 code review issues. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 2
packages/core/src/runtime.ts:67-68
The `translate` wrapper returns `results[0]` directly. If `_translateMany` ever produces an empty array (e.g., a malformed API response), callers receive `undefined` at runtime despite the declared return type of `TranslationResult | TranslationError`. A guard here prevents a silent type violation from propagating.

```suggestion
  const results = await translateMany([source], options, config, timeout);
  if (!results[0]) {
    throw new Error('translate: no result returned for the provided source');
  }
  return results[0];
```

### Issue 2 of 2
packages/i18n/src/i18n-config/I18nConfig.ts:160-179
Both branches of the `Array.isArray` guard call `runtimeTranslateMany` with the same arguments — the conditional is dead code. The `runtimeTranslateMany` implementation already accepts `TranslateManyEntry[] | Record<string, TranslateManyEntry>`, so TypeScript overload resolution handles the type narrowing without any runtime branching.

```suggestion
  async translateMany(
    sources: TranslateManyEntry[] | Record<string, TranslateManyEntry>,
    options: RuntimeTranslateOptions,
    timeout?: number
  ): Promise<TranslateManyResult | Record<string, TranslationResult>> {
    return await runtimeTranslateMany(
      sources as TranslateManyEntry[],
      options,
      this.getRuntimeTranslateConfig(),
      timeout
    );
  }
```

Reviews (1): Last reviewed commit: "perf: add runtime translation functions" | Re-trigger Greptile

Greptile also left 2 inline comments on this PR.

bgub and others added 24 commits June 30, 2026 15:20
## Summary
- move `gtServicesEnabled` onto the shared `createGlobalSingleton`
registry
- preserve the existing
`globalThis.__generaltranslation.i18n.gtServicesEnabled` namespace/key
- keep uninitialized reads defaulting to `false`
- add a gt-i18n patch changeset

## Notes
- Repeated setup with the same resolved boolean remains quiet.
- The shared singleton warning now fires if setup flips the flag between
enabled and disabled in the same JS realm, which should indicate a
meaningful config change rather than normal runtime behavior.

## Tests
- pnpm --filter gt-i18n test --
src/globals/__tests__/getGTServicesEnabled.test.ts
src/i18n-config/__tests__/singleton-operations.test.ts
- pnpm --filter gt-i18n typecheck
- pnpm exec oxlint packages/i18n/src/globals/getGTServicesEnabled.ts
packages/i18n/src/globals/__tests__/getGTServicesEnabled.test.ts
- pnpm exec oxfmt --check .changeset/gt-services-enabled-singleton.md
packages/i18n/src/globals/getGTServicesEnabled.ts
packages/i18n/src/globals/__tests__/getGTServicesEnabled.test.ts
- git diff --check

<!-- greptile_comment -->

<h3>Greptile Summary</h3>

This PR removes the standalone `setupGTServicesEnabled` global and folds
the `gtServicesEnabled` flag directly into the `I18nConfig` class, which
is already managed via the shared `createGlobalSingleton` registry. All
downstream callers (Next, Node, React, React Native) drop their separate
`setupGTServicesEnabled` call and now rely solely on
`initializeI18nConfig`.

- `cacheUrl` is promoted into `I18nConfigParams` so the
`resolveGTServicesEnabled` logic (which needs it) can live entirely
inside `I18nConfig`'s constructor without a separate parameter bag.
- `validateI18nConfigParams` is updated to accept the already-resolved
`gtServicesEnabled` boolean as an explicit argument instead of reading
the old global, eliminating the circular dependency between the
validator and the global setter.
- All tests pass and a new case pins the "returns false before config is
initialized" contract.

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

Safe to merge; the refactoring correctly consolidates a previously
split-brain flag into a single, already-tested singleton.

The change removes a parallel global write and re-homes the flag
computation inside I18nConfig's constructor, which is the natural owner.
All call sites are updated, the validation function is now testable in
isolation, and the uninitialized-default contract is now verified by a
test. No behavioral regressions identified.

packages/next/src/setup/shared.ts — the NextSetupI18nConfigParams alias
can be simplified now that cacheUrl is part of I18nConfigParams, but
this has no runtime impact.
</details>

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

| Filename | Overview |
|----------|----------|
| packages/i18n/src/globals/getGTServicesEnabled.ts | Simplified to a
thin wrapper over the I18nConfig singleton; correctly returns false
before initialization. setupGTServicesEnabled removed. |
| packages/i18n/src/i18n-config/I18nConfig.ts | Adds cacheUrl to
I18nConfigParams, moves resolveGTServicesEnabled logic here, stores
result as a private field. Clean consolidation. |
| packages/i18n/src/i18n-config/validation.ts | Eliminates the global
read in favor of an explicit parameter; no logic change, avoids the need
to read the global singleton during construction. |
| packages/next/src/setup/shared.ts | Removes the separate
gtservicesEnabledParams block; adds cacheUrl directly to
i18nConfigParams. Introduces NextSetupI18nConfigParams which is now
redundant given cacheUrl is already in I18nConfigParams. |
| packages/i18n/src/globals/__tests__/getGTServicesEnabled.test.ts |
Updated to use initializeI18nConfig; adds the previously-missing test
for the uninitialized default (false). Addresses prior review comment. |
| packages/i18n/src/i18n-config/__tests__/singleton-operations.test.ts |
Removes the gtServicesEnabled field from the singleton shape type and
adds a dedicated test verifying the flag is stored and retrievable via
the config singleton. |
| packages/next/src/setup/initGT.ts | Drops the separate
setupGTServicesEnabled call; initializeI18nConfig now owns the full
setup. Correctly uses NextSetupI18nConfigParams. |
| packages/node/src/setup/initializeGT.ts | Removes
setupGTServicesEnabled call; initializeI18nConfig takes full
responsibility. Clean one-line removal. |

</details>

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

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

```mermaid
%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A["initializeGT() / initializeI18nConfig()"] --> B["new I18nConfig(params)"]
    B --> C["resolveGTServicesEnabled(params)\n(getLoadTranslationsType | getTranslationApiType)"]
    C --> D["gtServicesEnabled: boolean"]
    D --> E["validateI18nConfigParams(params, gtServicesEnabled)"]
    D --> F["this.gtServicesEnabled = gtServicesEnabled"]
    B --> G["setI18nConfig(instance)\ncreateGlobalSingleton registry"]
    G --> H["globalThis.__generaltranslation.i18n.i18nConfig"]
    H --> I["getGTServicesEnabled()\n-> isI18nConfigInitialized()\n  ? getI18nConfig().isGTServicesEnabled()\n  : false"]
```

</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["initializeGT() / initializeI18nConfig()"] --> B["new I18nConfig(params)"]
    B --> C["resolveGTServicesEnabled(params)\n(getLoadTranslationsType | getTranslationApiType)"]
    C --> D["gtServicesEnabled: boolean"]
    D --> E["validateI18nConfigParams(params, gtServicesEnabled)"]
    D --> F["this.gtServicesEnabled = gtServicesEnabled"]
    B --> G["setI18nConfig(instance)\ncreateGlobalSingleton registry"]
    G --> H["globalThis.__generaltranslation.i18n.i18nConfig"]
    H --> I["getGTServicesEnabled()\n-> isI18nConfigInitialized()\n  ? getI18nConfig().isGTServicesEnabled()\n  : false"]
```

</a>
</details>

<sub>Reviews (2): Last reviewed commit: ["refactor: store gt services
flag on
i18n..."](eeb2452)
| [Re-trigger
Greptile](https://app.greptile.com/api/retrigger?id=40948338)</sub>

<!-- /greptile_comment -->
## Summary
- replace the hand-rolled react-core context global registry with
createGlobalSingleton
- preserve the existing reactCore/gtContext global slot and lazy
creation behavior

## Testing
- pnpm exec oxlint packages/react-core/src/context/context.ts
- pnpm exec oxfmt --check packages/react-core/src/context/context.ts
- git diff --check
- pnpm --filter @generaltranslation/react-core test
- pnpm --filter @generaltranslation/react-core typecheck
- pnpm --filter gt-react test
- pnpm --filter gt-react typecheck

<!-- greptile_comment -->

<h3>Greptile Summary</h3>

This PR refactors `packages/react-core/src/context/context.ts` to
replace three hand-rolled TypeScript interfaces and the
`getReactCoreGlobals()` helper with the shared `createGlobalSingleton`
utility from `gt-i18n/internal`, reducing boilerplate while preserving
the same `globalThis.__generaltranslation.reactCore.gtContext` slot.

- The module-level `gtContextSingleton` constant mirrors the old
`getReactCoreGlobals()` plumbing exactly — same `namespace`
(`reactCore`) and `key` (`gtContext`) — so the global slot is unchanged
across package instances.
- `getGTContext()` preserves the lazy-creation pattern: it calls
`isInitialized()` before `set()`, so `createContext` is still invoked at
most once, and the new `set()` overwrite-warning path is never reached
through normal usage.

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

Safe to merge — the refactor is a direct mechanical substitution of
equivalent logic with no functional change.

The global slot (__generaltranslation.reactCore.gtContext) is identical
to what the old hand-rolled helper produced. The lazy-init guard
(isInitialized() → set() → get()) matches the old ??= semantics
precisely, and set() is only called when the slot is empty so the new
overwrite-warning path is unreachable in normal use. The removed
TypeScript types were internal implementation detail only. Nothing in
the public API or runtime behavior changes.

No files require special attention.
</details>

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

| Filename | Overview |
|----------|----------|
| packages/react-core/src/context/context.ts | Replaces hand-rolled
global-registry types and helper with createGlobalSingleton;
namespace/key alignment is preserved, lazy-init behavior is equivalent.
|

</details>

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

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

```mermaid
%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
    participant Caller
    participant getGTContext
    participant gtContextSingleton
    participant globalThis

    Caller->>getGTContext: getGTContext()
    getGTContext->>gtContextSingleton: isInitialized()
    gtContextSingleton->>globalThis: globalThis.__generaltranslation["reactCore"]["gtContext"]
    globalThis-->>gtContextSingleton: undefined (first call)
    gtContextSingleton-->>getGTContext: false

    getGTContext->>getGTContext: createContext(undefined)
    getGTContext->>gtContextSingleton: set(ctx)
    gtContextSingleton->>globalThis: store ctx at ["reactCore"]["gtContext"]

    getGTContext->>gtContextSingleton: get()
    gtContextSingleton->>globalThis: read ["reactCore"]["gtContext"]
    globalThis-->>gtContextSingleton: ctx
    gtContextSingleton-->>getGTContext: ctx
    getGTContext-->>Caller: ctx

    Note over Caller,globalThis: Subsequent calls — slot already populated
    Caller->>getGTContext: getGTContext()
    getGTContext->>gtContextSingleton: isInitialized()
    gtContextSingleton->>globalThis: read ["reactCore"]["gtContext"]
    globalThis-->>gtContextSingleton: ctx
    gtContextSingleton-->>getGTContext: true
    getGTContext->>gtContextSingleton: get()
    gtContextSingleton-->>getGTContext: ctx
    getGTContext-->>Caller: ctx
```

</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 Caller
    participant getGTContext
    participant gtContextSingleton
    participant globalThis

    Caller->>getGTContext: getGTContext()
    getGTContext->>gtContextSingleton: isInitialized()
    gtContextSingleton->>globalThis: globalThis.__generaltranslation["reactCore"]["gtContext"]
    globalThis-->>gtContextSingleton: undefined (first call)
    gtContextSingleton-->>getGTContext: false

    getGTContext->>getGTContext: createContext(undefined)
    getGTContext->>gtContextSingleton: set(ctx)
    gtContextSingleton->>globalThis: store ctx at ["reactCore"]["gtContext"]

    getGTContext->>gtContextSingleton: get()
    gtContextSingleton->>globalThis: read ["reactCore"]["gtContext"]
    globalThis-->>gtContextSingleton: ctx
    gtContextSingleton-->>getGTContext: ctx
    getGTContext-->>Caller: ctx

    Note over Caller,globalThis: Subsequent calls — slot already populated
    Caller->>getGTContext: getGTContext()
    getGTContext->>gtContextSingleton: isInitialized()
    gtContextSingleton->>globalThis: read ["reactCore"]["gtContext"]
    globalThis-->>gtContextSingleton: ctx
    gtContextSingleton-->>getGTContext: true
    getGTContext->>gtContextSingleton: get()
    gtContextSingleton-->>getGTContext: ctx
    getGTContext-->>Caller: ctx
```

</a>
</details>

<sub>Reviews (1): Last reviewed commit: ["refactor: use shared singleton
for
react..."](46bec0e)
| [Re-trigger
Greptile](https://app.greptile.com/api/retrigger?id=40949786)</sub>

<!-- /greptile_comment -->
## Summary
- re-export the missing platform-neutral
@generaltranslation/react-core/pure helpers from gt-react-native
- expose ReactI18nCache and ReactI18nCacheParams from gt-react-native to
match gt-react parity

## Testing
- pnpm --filter gt-react-native test
- pnpm --filter gt-react-native build
- pnpm exec oxfmt --check packages/react-native/src/index.tsx
- git diff --check
- confirmed the new names appear in
packages/react-native/dist/module/index.d.ts
- AST parity check: no @generaltranslation/react-core/pure exports
remain present in gt-react but missing from gt-react-native

## Notes
- pnpm --filter gt-react-native typecheck currently fails in untouched
src/hooks/selectors.ts with TS2742 about an inferred non-portable return
type.

<!-- greptile_comment -->

<h3>Greptile Summary</h3>

This PR adds four previously missing re-exports (`getDefaultLocale`,
`getGTClass`, `getLocales`, `getVersionId`) from
`@generaltranslation/react-core/pure` to the `gt-react-native` public
surface, closing part of the gap with `gt-react`.

- Four helpers that were already available in
`@generaltranslation/react-core/pure` are now forwarded through
`gt-react-native/src/index.tsx`, matching the corresponding block in
`gt-react/src/index.client.ts`.
- `ReactI18nCache` and `ReactI18nCacheParams` are explicitly listed in
the PR description as being added, but no re-export for them appears in
the actual diff; they remain absent from `gt-react-native`.
- `getLocaleProperties` is present in both `pure.ts` and `gt-react`'s
public API but is still not forwarded by `gt-react-native`, making the
"AST parity check: zero gaps remain" claim in the PR description
inaccurate.

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

Safe to merge for the four symbols that were added, but the PR does not
fully deliver on two explicit claims in its description.

The four new re-exports are correct and low-risk. However,
ReactI18nCache and ReactI18nCacheParams are specifically called out in
the PR description as being added, yet they appear nowhere in the diff.
getLocaleProperties is also absent despite the stated AST parity
guarantee. Users or internal tooling that depends on these symbols will
still find them missing from gt-react-native after this PR merges.

packages/react-native/src/index.tsx — needs the ReactI18nCache,
ReactI18nCacheParams, and getLocaleProperties re-exports that are
described but not present.
</details>


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




| Filename | Overview |
|----------|----------|
| packages/react-native/src/index.tsx | Adds four missing re-exports
(getDefaultLocale, getGTClass, getLocales, getVersionId) from
@generaltranslation/react-core/pure, but two items explicitly named in
the PR description (ReactI18nCache, ReactI18nCacheParams) and one more
expected by the parity claim (getLocaleProperties) are still absent. |

</details>


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

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

```mermaid
%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A["@generaltranslation/react-core/pure"]
    A -->|"getDefaultLocale, getGTClass,\ngetLocales, getVersionId\n(ADDED by this PR)"| B["gt-react-native/src/index.tsx"]
    A -->|"getDefaultLocale, getGTClass,\ngetLocales, getVersionId,\ngetLocaleProperties,\nReactI18nCache, ReactI18nCacheParams\n(already present)"| C["gt-react/src/index.client.ts"]
    A -->|"getLocaleProperties missing\nReactI18nCache missing\nReactI18nCacheParams missing"| B
    B --> D["gt-react-native consumers"]
    C --> E["gt-react consumers"]
```

</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["@generaltranslation/react-core/pure"]
    A -->|"getDefaultLocale, getGTClass,\ngetLocales, getVersionId\n(ADDED by this PR)"| B["gt-react-native/src/index.tsx"]
    A -->|"getDefaultLocale, getGTClass,\ngetLocales, getVersionId,\ngetLocaleProperties,\nReactI18nCache, ReactI18nCacheParams\n(already present)"| C["gt-react/src/index.client.ts"]
    A -->|"getLocaleProperties missing\nReactI18nCache missing\nReactI18nCacheParams missing"| B
    B --> D["gt-react-native consumers"]
    C --> E["gt-react consumers"]
```

</a>
</details>


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

1. `packages/react-native/src/index.tsx`, line 63-73
([link](https://github.com/generaltranslation/gt/blob/302e395609b6783dfa98e5e526bf815ae502bc4c/packages/react-native/src/index.tsx#L63-L73))

<a href="#"><img alt="P1"
src="https://greptile-static-assets.s3.amazonaws.com/badges/p1.svg?v=9"
align="top"></a> **ReactI18nCache / ReactI18nCacheParams not actually
exported**

The PR description states "expose ReactI18nCache and
ReactI18nCacheParams from gt-react-native to match gt-react parity", but
neither symbol appears anywhere in the post-PR `index.tsx`. Both are
exported from `@generaltranslation/react-core/pure` (see `pure.ts` lines
79–81) and are re-exported from `gt-react` (`index.client.ts` lines
116–118), but no corresponding re-export was added here. This leaves a
concrete API gap between `gt-react` and `gt-react-native` despite the
stated goal.

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

   `````markdown
   This is a comment left during a code review.
   Path: packages/react-native/src/index.tsx
   Line: 63-73

   Comment:
   **ReactI18nCache / ReactI18nCacheParams not actually exported**

The PR description states "expose ReactI18nCache and
ReactI18nCacheParams from gt-react-native to match gt-react parity", but
neither symbol appears anywhere in the post-PR `index.tsx`. Both are
exported from `@generaltranslation/react-core/pure` (see `pure.ts` lines
79–81) and are re-exported from `gt-react` (`index.client.ts` lines
116–118), but no corresponding re-export was added here. This leaves a
concrete API gap between `gt-react` and `gt-react-native` despite the
stated goal.

   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%2Freact-native%2Fsrc%2Findex.tsx%0ALine%3A%2063-73%0A%0AComment%3A%0A**ReactI18nCache%20%2F%20ReactI18nCacheParams%20not%20actually%20exported**%0A%0AThe%20PR%20description%20states%20%22expose%20ReactI18nCache%20and%20ReactI18nCacheParams%20from%20gt-react-native%20to%20match%20gt-react%20parity%22%2C%20but%20neither%20symbol%20appears%20anywhere%20in%20the%20post-PR%20%60index.tsx%60.%20Both%20are%20exported%20from%20%60%40generaltranslation%2Freact-core%2Fpure%60%20%28see%20%60pure.ts%60%20lines%2079%E2%80%9381%29%20and%20are%20re-exported%20from%20%60gt-react%60%20%28%60index.client.ts%60%20lines%20116%E2%80%93118%29%2C%20but%20no%20corresponding%20re-export%20was%20added%20here.%20This%20leaves%20a%20concrete%20API%20gap%20between%20%60gt-react%60%20and%20%60gt-react-native%60%20despite%20the%20stated%20goal.%0A%0AHow%20can%20I%20resolve%20this%3F%20If%20you%20propose%20a%20fix%2C%20please%20make%20it%20concise.&repo=generaltranslation%2Fgt&pr=1734&platform=github"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://greptile-static-assets.s3.amazonaws.com/badges/FixInClaudeDark.svg?v=4"><source
media="(prefers-color-scheme: light)"
srcset="https://greptile-static-assets.s3.amazonaws.com/badges/FixInClaude.svg?v=4"><img
alt="Fix in Claude Code"
src="https://greptile-static-assets.s3.amazonaws.com/badges/FixInClaude.svg?v=4"></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%2Freact-native-pure-parity%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%2Freact-native-pure-parity%22.%0A%0AThis%20is%20a%20comment%20left%20during%20a%20code%20review.%0APath%3A%20packages%2Freact-native%2Fsrc%2Findex.tsx%0ALine%3A%2063-73%0A%0AComment%3A%0A**ReactI18nCache%20%2F%20ReactI18nCacheParams%20not%20actually%20exported**%0A%0AThe%20PR%20description%20states%20%22expose%20ReactI18nCache%20and%20ReactI18nCacheParams%20from%20gt-react-native%20to%20match%20gt-react%20parity%22%2C%20but%20neither%20symbol%20appears%20anywhere%20in%20the%20post-PR%20%60index.tsx%60.%20Both%20are%20exported%20from%20%60%40generaltranslation%2Freact-core%2Fpure%60%20%28see%20%60pure.ts%60%20lines%2079%E2%80%9381%29%20and%20are%20re-exported%20from%20%60gt-react%60%20%28%60index.client.ts%60%20lines%20116%E2%80%93118%29%2C%20but%20no%20corresponding%20re-export%20was%20added%20here.%20This%20leaves%20a%20concrete%20API%20gap%20between%20%60gt-react%60%20and%20%60gt-react-native%60%20despite%20the%20stated%20goal.%0A%0AHow%20can%20I%20resolve%20this%3F%20If%20you%20propose%20a%20fix%2C%20please%20make%20it%20concise.&repo=generaltranslation%2Fgt&pr=1734&platform=github"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://greptile-static-assets.s3.amazonaws.com/badges/FixInCodexDark.svg?v=4"><source
media="(prefers-color-scheme: light)"
srcset="https://greptile-static-assets.s3.amazonaws.com/badges/FixInCodex.svg?v=4"><img
alt="Fix in Codex"
src="https://greptile-static-assets.s3.amazonaws.com/badges/FixInCodex.svg?v=4"></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%2Freact-native%2Fsrc%2Findex.tsx%3A63-73%0A**ReactI18nCache%20%2F%20ReactI18nCacheParams%20not%20actually%20exported**%0A%0AThe%20PR%20description%20states%20%22expose%20ReactI18nCache%20and%20ReactI18nCacheParams%20from%20gt-react-native%20to%20match%20gt-react%20parity%22%2C%20but%20neither%20symbol%20appears%20anywhere%20in%20the%20post-PR%20%60index.tsx%60.%20Both%20are%20exported%20from%20%60%40generaltranslation%2Freact-core%2Fpure%60%20%28see%20%60pure.ts%60%20lines%2079%E2%80%9381%29%20and%20are%20re-exported%20from%20%60gt-react%60%20%28%60index.client.ts%60%20lines%20116%E2%80%93118%29%2C%20but%20no%20corresponding%20re-export%20was%20added%20here.%20This%20leaves%20a%20concrete%20API%20gap%20between%20%60gt-react%60%20and%20%60gt-react-native%60%20despite%20the%20stated%20goal.%0A%0A%23%23%23%20Issue%202%20of%202%0Apackages%2Freact-native%2Fsrc%2Findex.tsx%3A47-50%0A**getLocaleProperties%20still%20absent%20despite%20parity%20claim**%0A%0A%60gt-react%60%20%28%60index.client.ts%60%20line%2085%29%20exports%20%60getLocaleProperties%60%20from%20%60%40generaltranslation%2Freact-core%2Fpure%60%2C%20and%20%60pure.ts%60%20%28line%20118%29%20confirms%20the%20export%20exists.%20The%20PR%20description%20claims%20%22AST%20parity%20check%3A%20no%20%60%40generaltranslation%2Freact-core%2Fpure%60%20exports%20remain%20present%20in%20gt-react%20but%20missing%20from%20gt-react-native%22%2C%20but%20%60getLocaleProperties%60%20is%20still%20missing%20here.%20Omitting%20it%20leaves%20callers%20who%20upgrade%20from%20%60gt-react%60%20to%20%60gt-react-native%60%20without%20a%20drop-in%20equivalent.%0A%0A%60%60%60suggestion%0A%20%20getDefaultLocale%2C%0A%20%20getFormatLocales%2C%0A%20%20getGTClass%2C%0A%20%20getLocaleProperties%2C%0A%20%20getLocales%2C%0A%60%60%60%0A%0A&repo=generaltranslation%2Fgt&pr=1734&platform=github"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://greptile-static-assets.s3.amazonaws.com/badges/FixAllInClaudeDark.svg?v=4"><source
media="(prefers-color-scheme: light)"
srcset="https://greptile-static-assets.s3.amazonaws.com/badges/FixAllInClaude.svg?v=4"><img
alt="Fix All in Claude Code"
src="https://greptile-static-assets.s3.amazonaws.com/badges/FixAllInClaude.svg?v=4"></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%2Freact-native-pure-parity%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%2Freact-native-pure-parity%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%2Freact-native%2Fsrc%2Findex.tsx%3A63-73%0A**ReactI18nCache%20%2F%20ReactI18nCacheParams%20not%20actually%20exported**%0A%0AThe%20PR%20description%20states%20%22expose%20ReactI18nCache%20and%20ReactI18nCacheParams%20from%20gt-react-native%20to%20match%20gt-react%20parity%22%2C%20but%20neither%20symbol%20appears%20anywhere%20in%20the%20post-PR%20%60index.tsx%60.%20Both%20are%20exported%20from%20%60%40generaltranslation%2Freact-core%2Fpure%60%20%28see%20%60pure.ts%60%20lines%2079%E2%80%9381%29%20and%20are%20re-exported%20from%20%60gt-react%60%20%28%60index.client.ts%60%20lines%20116%E2%80%93118%29%2C%20but%20no%20corresponding%20re-export%20was%20added%20here.%20This%20leaves%20a%20concrete%20API%20gap%20between%20%60gt-react%60%20and%20%60gt-react-native%60%20despite%20the%20stated%20goal.%0A%0A%23%23%23%20Issue%202%20of%202%0Apackages%2Freact-native%2Fsrc%2Findex.tsx%3A47-50%0A**getLocaleProperties%20still%20absent%20despite%20parity%20claim**%0A%0A%60gt-react%60%20%28%60index.client.ts%60%20line%2085%29%20exports%20%60getLocaleProperties%60%20from%20%60%40generaltranslation%2Freact-core%2Fpure%60%2C%20and%20%60pure.ts%60%20%28line%20118%29%20confirms%20the%20export%20exists.%20The%20PR%20description%20claims%20%22AST%20parity%20check%3A%20no%20%60%40generaltranslation%2Freact-core%2Fpure%60%20exports%20remain%20present%20in%20gt-react%20but%20missing%20from%20gt-react-native%22%2C%20but%20%60getLocaleProperties%60%20is%20still%20missing%20here.%20Omitting%20it%20leaves%20callers%20who%20upgrade%20from%20%60gt-react%60%20to%20%60gt-react-native%60%20without%20a%20drop-in%20equivalent.%0A%0A%60%60%60suggestion%0A%20%20getDefaultLocale%2C%0A%20%20getFormatLocales%2C%0A%20%20getGTClass%2C%0A%20%20getLocaleProperties%2C%0A%20%20getLocales%2C%0A%60%60%60%0A%0A&repo=generaltranslation%2Fgt&pr=1734&platform=github"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://greptile-static-assets.s3.amazonaws.com/badges/FixAllInCodexDark.svg?v=4"><source
media="(prefers-color-scheme: light)"
srcset="https://greptile-static-assets.s3.amazonaws.com/badges/FixAllInCodex.svg?v=4"><img
alt="Fix All in Codex"
src="https://greptile-static-assets.s3.amazonaws.com/badges/FixAllInCodex.svg?v=4"></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/react-native/src/index.tsx:63-73
**ReactI18nCache / ReactI18nCacheParams not actually exported**

The PR description states "expose ReactI18nCache and ReactI18nCacheParams from gt-react-native to match gt-react parity", but neither symbol appears anywhere in the post-PR `index.tsx`. Both are exported from `@generaltranslation/react-core/pure` (see `pure.ts` lines 79–81) and are re-exported from `gt-react` (`index.client.ts` lines 116–118), but no corresponding re-export was added here. This leaves a concrete API gap between `gt-react` and `gt-react-native` despite the stated goal.

### Issue 2 of 2
packages/react-native/src/index.tsx:47-50
**getLocaleProperties still absent despite parity claim**

`gt-react` (`index.client.ts` line 85) exports `getLocaleProperties` from `@generaltranslation/react-core/pure`, and `pure.ts` (line 118) confirms the export exists. The PR description claims "AST parity check: no `@generaltranslation/react-core/pure` exports remain present in gt-react but missing from gt-react-native", but `getLocaleProperties` is still missing here. Omitting it leaves callers who upgrade from `gt-react` to `gt-react-native` without a drop-in equivalent.

```suggestion
  getDefaultLocale,
  getFormatLocales,
  getGTClass,
  getLocaleProperties,
  getLocales,
```


`````

</details>

<sub>Reviews (2): Last reviewed commit: ["fix: mirror pure exports in
react
native"](302e395)
| [Re-trigger
Greptile](https://app.greptile.com/api/retrigger?id=40951736)</sub>

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

<!-- /greptile_comment -->
## Summary
- remove unused CLI console/format exports and an unused auth header
utility
- remove redundant gt-i18n internal type re-export
- remove unused react-core pure/condition-store exports and auth env
types

## Tests
- pnpm --filter gt typecheck
- pnpm --filter gt-i18n typecheck
- pnpm --filter @generaltranslation/react-core typecheck
- git diff --check

<!-- greptile_comment -->

<h3>Greptile Summary</h3>

This PR removes dead internal exports across three packages (`gt`,
`gt-i18n`, `@generaltranslation/react-core`) — string constants, a
utility function, type definitions, and re-export aliases that have no
remaining consumers in the codebase.

- **CLI (`gt`)**: Removes unused `noSupportedFormatError` string, the
`SUPPORTED_DATA_FORMATS` array, and deletes the entire `headers.ts`
utility file containing `getAuthHeaders`.
- **i18n**: Removes a redundant named re-export of `TranslationMetadata`
that was already covered by the preceding `export type *` wildcard.
- **react-core**: Removes the `getConditionStore as
getReadonlyConditionStore` alias (all consumers use the `WithFallback`
variant) and the `AuthFromEnvParams`/`AuthFromEnvReturn` type
definitions and their public re-exports.

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

All removals are confirmed dead code with no remaining consumers — safe
to merge.

Every removed export was verified against the full codebase: no
remaining imports of `noSupportedFormatError`, `SUPPORTED_DATA_FORMATS`,
`getAuthHeaders`, `getReadonlyConditionStore` (bare alias),
`AuthFromEnvParams`, or `AuthFromEnvReturn`. The `TranslationMetadata`
named re-export was redundant due to the existing `export type *`
wildcard. The accompanying typecheck CI steps cover each affected
package.

No files require special attention — all changes are straightforward
dead-code removals.
</details>

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

| Filename | Overview |
|----------|----------|
| packages/cli/src/console/index.ts | Removes `noSupportedFormatError`
constant — confirmed unused across the codebase. |
| packages/cli/src/formats/files/aggregateFiles.ts | Removes
`SUPPORTED_DATA_FORMATS` exported constant — confirmed unused across the
codebase. |
| packages/cli/src/utils/headers.ts | Deletes entire file containing
`getAuthHeaders` utility — confirmed no imports of this file or function
remain anywhere. |
| packages/i18n/src/internal-types.ts | Removes redundant explicit
re-export of `TranslationMetadata`, which is already covered by the
preceding `export type *` wildcard from the same source file. |
| packages/react-core/src/condition-store/singleton-operations.ts |
Removes `getConditionStore as getReadonlyConditionStore` alias from the
export block; all consumers in the codebase use the `WithFallback`
variant, so this alias had zero consumers. |
| packages/react-core/src/pure.ts | Removes `AuthFromEnvParams` and
`AuthFromEnvReturn` type re-exports — both types are being deleted at
source and have no remaining consumers. |
| packages/react-core/src/utils/types.ts | Deletes `AuthFromEnvParams`
and `AuthFromEnvReturn` type definitions — confirmed unused across the
entire codebase. |

</details>

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

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

```mermaid
%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[Dead Export Removal PR] --> B[packages/cli]
    A --> C[packages/i18n]
    A --> D[packages/react-core]

    B --> B1["console/index.ts\n- noSupportedFormatError ❌"]
    B --> B2["formats/files/aggregateFiles.ts\n- SUPPORTED_DATA_FORMATS ❌"]
    B --> B3["utils/headers.ts\n- getAuthHeaders() ❌ (file deleted)"]

    C --> C1["internal-types.ts\n- TranslationMetadata (redundant re-export) ❌\n  already covered by export type *"]

    D --> D1["condition-store/singleton-operations.ts\n- getConditionStore as getReadonlyConditionStore ❌\n  all callers use WithFallback variant"]
    D --> D2["pure.ts\n- AuthFromEnvParams ❌\n- AuthFromEnvReturn ❌"]
    D --> D3["utils/types.ts\n- AuthFromEnvParams type def ❌\n- AuthFromEnvReturn type def ❌"]
```

</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[Dead Export Removal PR] --> B[packages/cli]
    A --> C[packages/i18n]
    A --> D[packages/react-core]

    B --> B1["console/index.ts\n- noSupportedFormatError ❌"]
    B --> B2["formats/files/aggregateFiles.ts\n- SUPPORTED_DATA_FORMATS ❌"]
    B --> B3["utils/headers.ts\n- getAuthHeaders() ❌ (file deleted)"]

    C --> C1["internal-types.ts\n- TranslationMetadata (redundant re-export) ❌\n  already covered by export type *"]

    D --> D1["condition-store/singleton-operations.ts\n- getConditionStore as getReadonlyConditionStore ❌\n  all callers use WithFallback variant"]
    D --> D2["pure.ts\n- AuthFromEnvParams ❌\n- AuthFromEnvReturn ❌"]
    D --> D3["utils/types.ts\n- AuthFromEnvParams type def ❌\n- AuthFromEnvReturn type def ❌"]
```

</a>
</details>

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

<!-- /greptile_comment -->
## Summary
- add explicit portable selector hook result types in react-core
- reuse those types in gt-react-native selector hooks to avoid
non-nameable declaration output
- add a patch changeset for react-core and gt-react-native

## Checks
- pnpm --filter @generaltranslation/react-core typecheck
- pnpm --filter gt-react typecheck
- pnpm --filter gt-react-native typecheck
- pnpm format
- git diff --check

<!-- greptile_comment -->

<h3>Greptile Summary</h3>

This PR adds explicit TypeScript return types to the
`useInternalLocaleSelector` and `useInternalRegionSelector` hooks in
`react-core`, then reuses those exported types in the `gt-react-native`
selector wrappers to fix TS2742 errors that occur in project-reference
builds when TypeScript tries to name inferred types that reach into
non-portable bundled declarations from `generaltranslation`.

- **`react-core`**: Introduces `InternalLocaleSelectorResult` and
`InternalRegionSelectorResult` types, annotates the hook functions with
those types, and re-exports them from the hooks barrel (`hooks.ts`).
- **`gt-react-native`**: Updates `useLocaleSelector` and
`useRegionSelector` to declare explicit return types as intersections of
the newly exported core result types with their respective setter
signatures.
- **Changeset**: A patch bump for both `@generaltranslation/react-core`
and `gt-react-native` is included.

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

Pure TypeScript type-annotation additions with no runtime behavior
changes; safe to merge.

All changes are additive type exports and explicit return-type
annotations. The declared types accurately reflect the objects each hook
already returned. No logic, runtime paths, or public APIs are altered.

No files require special attention.
</details>

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

| Filename | Overview |
|----------|----------|
| packages/react-core/src/hooks/useInternalLocaleSelector.ts | Adds
explicit InternalLocaleSelectorResult type and annotates the function's
return type; types accurately match the actual returned shape. |
| packages/react-core/src/hooks/useInternalRegionSelector.ts | Adds
InternalRegionSelectorResult type and annotates the function's return;
all five fields match the returned object exactly. |
| packages/react-core/src/hooks.ts | Re-exports the two new result types
from the hooks barrel; no functional changes. |
| packages/react-native/src/hooks/selectors.ts | Annotates
useLocaleSelector and useRegionSelector with explicit intersection
return types reusing the newly exported core types; resolves TS2742 for
project-reference builds. |
| .changeset/react-native-selector-return-types.md | Correct patch
changeset targeting both @generaltranslation/react-core and
gt-react-native. |

</details>

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

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

```mermaid
%%{init: {'theme': 'neutral'}}%%
classDiagram
    class InternalLocaleSelectorResult {
        +string locale
        +string[] locales
        +getLocaleProperties(locale: string) LocaleProperties
    }

    class InternalRegionSelectorResult {
        +string|undefined region
        +string[] regions
        +Map~string,RegionData~ regionData
        +string locale
        +string localeRegion
    }

    class useLocaleSelector {
        +InternalLocaleSelectorResult & setLocale
    }

    class useRegionSelector {
        +InternalRegionSelectorResult & setLocale & setRegion
    }

    useLocaleSelector --|> InternalLocaleSelectorResult : extends
    useRegionSelector --|> InternalRegionSelectorResult : extends
```

</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 InternalLocaleSelectorResult {
        +string locale
        +string[] locales
        +getLocaleProperties(locale: string) LocaleProperties
    }

    class InternalRegionSelectorResult {
        +string|undefined region
        +string[] regions
        +Map~string,RegionData~ regionData
        +string locale
        +string localeRegion
    }

    class useLocaleSelector {
        +InternalLocaleSelectorResult & setLocale
    }

    class useRegionSelector {
        +InternalRegionSelectorResult & setLocale & setRegion
    }

    useLocaleSelector --|> InternalLocaleSelectorResult : extends
    useRegionSelector --|> InternalRegionSelectorResult : extends
```

</a>
</details>

<sub>Reviews (1): Last reviewed commit: ["fix: add portable selector
return
types"](b5303b1)
| [Re-trigger
Greptile](https://app.greptile.com/api/retrigger?id=40965389)</sub>

<!-- /greptile_comment -->
## Summary
- pass prebuilt child renderers into createRenderPreparedT
- keep renderPreparedT shared logic free of child renderer construction
- add a patch changeset for react-core

## Checks
- pnpm --filter @generaltranslation/react-core typecheck
- pnpm --filter @generaltranslation/react-core test
- pnpm format
- git diff --check

<!-- greptile_comment -->

<h3>Greptile Summary</h3>

This PR refactors the render pipeline so that `createRenderPreparedT`
receives already-constructed `renderDefaultChildren` and
`renderTranslatedChildren` instances rather than re-creating them
internally from `renderVariable`. The result is one fewer
`createRenderTranslatedChildren` call per pipeline instantiation and a
cleaner separation of concerns inside `renderPreparedT.shared.ts`.

- `createRenderPipeline.ts` now passes the already-built child renderers
to `createRenderPreparedT`, eliminating the internal construction that
previously happened inside the factory.
- `renderPreparedT.shared.ts` drops its factory imports and accepts the
pre-built renderer functions directly, keeping the module free of any
renderer-construction logic.

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

Safe to merge — purely structural refactor with no behavioral changes to
the rendering logic.

The refactor correctly wires the shared child renderers through
createRenderPipeline and the updated createRenderPreparedT signature.
createRenderTranslatedChildren still constructs its own private
renderDefaultChildren internally (unchanged file), so deduplication is
only partial — the PR achieves its stated goal for renderPreparedT but
leaves one duplicate in renderTranslatedChildren.

renderTranslatedChildren.shared.tsx still builds a private
renderDefaultChildren internally; a follow-up could pass the pre-built
instance in if full sharing is desired.
</details>

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

| Filename | Overview |
|----------|----------|
| packages/react-core/src/utils/rendering/createRenderPipeline.ts |
Updated to pass pre-built renderDefaultChildren and
renderTranslatedChildren to createRenderPreparedT instead of
renderVariable; logic is straightforward and all four pipeline members
are correctly wired. |
| packages/react-core/src/utils/rendering/renderPreparedT.shared.ts |
Factory signature changed from { renderVariable } to {
renderDefaultChildren, renderTranslatedChildren }; now type-imports the
arg types rather than the constructors, and removes internal
child-renderer construction — no logic changes. |
| .changeset/react-core-render-pipeline-dedup.md | Adds a patch
changeset for @generaltranslation/react-core describing the
deduplication of child renderer construction. |

</details>

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

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

```mermaid
%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[createRenderPipeline] --> B[createRenderVariable\nrenderVariable]
    B --> C[createRenderDefaultChildren\nrenderDefaultChildren ①]
    B --> D[createRenderTranslatedChildren\nrenderTranslatedChildren]
    D --> E[createRenderDefaultChildren\nrenderDefaultChildren ② internal]
    C --> F[createRenderPreparedT\nrenderPreparedT]
    D --> F
    F --> G[RenderPipeline\n renderVariable\n renderDefaultChildren ①\n renderTranslatedChildren\n renderPreparedT]

    style E fill:#ffe0b2,stroke:#f57c00
    style C fill:#c8e6c9,stroke:#388e3c
    style F fill:#c8e6c9,stroke:#388e3c
```

</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[createRenderPipeline] --> B[createRenderVariable\nrenderVariable]
    B --> C[createRenderDefaultChildren\nrenderDefaultChildren ①]
    B --> D[createRenderTranslatedChildren\nrenderTranslatedChildren]
    D --> E[createRenderDefaultChildren\nrenderDefaultChildren ② internal]
    C --> F[createRenderPreparedT\nrenderPreparedT]
    D --> F
    F --> G[RenderPipeline\n renderVariable\n renderDefaultChildren ①\n renderTranslatedChildren\n renderPreparedT]

    style E fill:#ffe0b2,stroke:#f57c00
    style C fill:#c8e6c9,stroke:#388e3c
    style F fill:#c8e6c9,stroke:#388e3c
```

</a>
</details>

<sub>Reviews (1): Last reviewed commit: ["refactor: share render
pipeline child
re..."](142f626)
| [Re-trigger
Greptile](https://app.greptile.com/api/retrigger?id=40971275)</sub>

<!-- /greptile_comment -->
## Summary
- remove stale package metadata for non-emitted subpaths
- preserve gt-next link client-boundary files in sideEffects
- align gt-tanstack-start legacy main/module fields with the server
export fallback
- add a patch changeset for gt-next, gt-react, and gt-tanstack-start

## Checks
- pnpm --filter gt-tanstack-start typecheck
- pnpm --filter gt-tanstack-start build
- pnpm --filter gt-next test -- packageExports
- pnpm --filter gt-react test -- react-package
- pnpm format
- git diff --check

<!-- greptile_comment -->

<h3>Greptile Summary</h3>

This PR cleans up stale package metadata across three packages and fixes
a `main`/`module` field misalignment in `gt-tanstack-start`.

- **`gt-next`**: Adds `./dist/link.*` and `./dist/link/Link.*` to
`sideEffects` to prevent bundlers from tree-shaking client-boundary
files; removes `./compiler` and `./eslint-plugin` subpath exports that
had no emitted build output.
- **`gt-react`**: Removes the stale `./setup` entry from `typesVersions`
and `compilerOptions.paths` — no corresponding `.d.cts` was emitted.
- **`gt-tanstack-start`**: Aligns legacy `main`/`module` fields to point
at `index.server.*` (matching the `exports` default fallback), removes
the empty `src/types.ts` file and its `./types` subpath export, and
drops it from the tsdown build entries.

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

Safe to merge — all changes are metadata cleanup and a legacy
entry-point alignment with no runtime logic touched.

Every changed file is either a package.json, a build config, or an empty
source file. The removed subpath exports (./compiler, ./eslint-plugin,
./setup, ./types) have no consumers anywhere in the repo. The
gt-tanstack-start main/module alignment correctly matches what the
exports default fallback already served, fixing a pre-existing mismatch
rather than introducing a behavior change. The sideEffects additions
protect client-boundary link files from being dropped by tree-shakers.

No files require special attention.
</details>

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

| Filename | Overview |
|----------|----------|
| .changeset/clean-package-config-metadata.md | Patch changeset covering
gt-next, gt-react, and gt-tanstack-start — correctly scoped as a patch
for metadata-only and bug-fix alignment changes. |
| packages/next/package.json | Adds ./dist/link.* and ./dist/link/Link.*
to sideEffects to prevent tree-shaking of client-boundary files; removes
stale ./compiler and ./eslint-plugin subpath exports that had no
corresponding build output. |
| packages/react/package.json | Removes the stale ./setup typesVersions
and compilerOptions.paths entries that had no corresponding emitted
output. |
| packages/tanstack-start/package.json | Aligns legacy main/module
fields with the server export fallback; removes the stale ./types
subpath export and its typesVersions/compilerOptions entries. |
| packages/tanstack-start/src/types.ts | Deleted empty file (zero-byte)
whose corresponding build entry and package.json subpath export are
removed in the same PR. |
| packages/tanstack-start/tsdown.config.mts | Removes src/types.ts from
the build entry list, consistent with the file deletion and package.json
cleanup. |

</details>

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

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

```mermaid
%%{init: {'theme': 'neutral'}}%%
flowchart TD
    subgraph gt-next
        N1["sideEffects\n+ ./dist/link.*\n+ ./dist/link/Link.*"] --> N2["Client-boundary files\npreserved from tree-shaking"]
        N3["Remove ./compiler export"] --> N4["No emitted build output"]
        N5["Remove ./eslint-plugin export"] --> N4
    end
    subgraph gt-react
        R1["Remove ./setup from\ntypesVersions & paths"] --> R2["No emitted .d.cts"]
    end
    subgraph gt-tanstack-start
        T1["main/module: index.client.*\n→ index.server.*"] --> T2["Aligned with exports\ndefault fallback"]
        T3["Remove ./types subpath"] --> T4["src/types.ts deleted\n(was empty)"]
        T5["tsdown: remove\nsrc/types.ts entry"] --> T4
    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 gt-next
        N1["sideEffects\n+ ./dist/link.*\n+ ./dist/link/Link.*"] --> N2["Client-boundary files\npreserved from tree-shaking"]
        N3["Remove ./compiler export"] --> N4["No emitted build output"]
        N5["Remove ./eslint-plugin export"] --> N4
    end
    subgraph gt-react
        R1["Remove ./setup from\ntypesVersions & paths"] --> R2["No emitted .d.cts"]
    end
    subgraph gt-tanstack-start
        T1["main/module: index.client.*\n→ index.server.*"] --> T2["Aligned with exports\ndefault fallback"]
        T3["Remove ./types subpath"] --> T4["src/types.ts deleted\n(was empty)"]
        T5["tsdown: remove\nsrc/types.ts entry"] --> T4
    end
```

</a>
</details>

<sub>Reviews (1): Last reviewed commit: ["fix: clean up package
metadata"](17799f3)
| [Re-trigger
Greptile](https://app.greptile.com/api/retrigger?id=40967115)</sub>

<!-- /greptile_comment -->
## Summary
- Set translation cache expiry to 0 when Next.js Cache Components are
enabled
- Treat gt-i18n cache entries with expiresAt 0 as immediately expired
without calling Date.now()
- Use a file-level "use cache" remote translation loader for default
Cache Components translation loading, while preserving custom
loadTranslations()
- Add gt-i18n and gt-next patch changesets and focused tests

## Testing
- pnpm exec oxfmt --check packages/next/src/config.ts
packages/next/src/config-dir/props/withGTConfigProps.ts
packages/next/src/errors/cacheComponents.ts
packages/next/src/setup/shared.ts
packages/next/src/setup/__tests__/shared.test.ts
packages/next/src/utils/createCacheComponentsLoadTranslations.ts
packages/next/src/utils/loadCacheComponentsTranslations.ts
packages/next/src/utils/__tests__/createCacheComponentsLoadTranslations.test.ts
packages/i18n/src/i18n-cache/translations-manager/ResourceCache.ts
packages/i18n/src/i18n-cache/translations-manager/__tests__/ResourceCache.test.ts
.changeset/cache-components-disable-expiry.md
- pnpm --filter gt-next test:js -- src/__tests__/config.test.ts
src/setup/__tests__/shared.test.ts
src/utils/__tests__/createCacheComponentsLoadTranslations.test.ts
- pnpm --filter gt-i18n test --
src/i18n-cache/translations-manager/__tests__/ResourceCache.test.ts
src/i18n-cache/__tests__/I18nCache.test.ts
- pnpm --filter gt-next typecheck
- pnpm --filter gt-i18n typecheck
- pnpm --filter gt-i18n build
- pnpm --filter gt-next build:no-swc-plugin
- pnpm build:next-app-router-locale-routing-use-cache
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.5

### Patch Changes

- 4a5f8e8: Remove unused internal exports and dead utility code.
## @generaltranslation/compiler@1.3.25-odysseus.5

### Patch Changes

- 432fa49: Support dev hot reload lookups for server `getGT` strings.

`getGT` can now receive compiler-injected message metadata and prefetch
missing translations through the runtime cache in development. `gt-next`
forwards the server request conditions into this path so App Router
server strings can participate in hot reload translation updates.

Compiler-injected `getGT` and `useGT` preload messages now emit the same
sugar metadata keys used by runtime lookup options.
## gtx-cli@2.14.51-odysseus.5

### Patch Changes

- Updated dependencies [4a5f8e8]
  - gt@2.14.51-odysseus.5
## gt-i18n@1.0.0-odysseus.5

### Patch Changes

- 432fa49: Use Next.js caching semantics for Cache Components by
disabling GT cache expiry and development hot reload runtime
translation.

Async translation and dictionary lookup boundaries now keep synchronous
access to the loaded snapshot, so APIs like `getGT` and
`getTranslations` can still resolve strings after cache expiry is
delegated to Next.js.

Global singleton setup now preserves the first initialized instance
instead of replacing it on later initialization attempts.

- 432fa49: Support dev hot reload lookups for server `getGT` strings.

`getGT` can now receive compiler-injected message metadata and prefetch
missing translations through the runtime cache in development. `gt-next`
forwards the server request conditions into this path so App Router
server strings can participate in hot reload translation updates.

Compiler-injected `getGT` and `useGT` preload messages now emit the same
sugar metadata keys used by runtime lookup options.

- 933916e: Store the GT services enabled flag on the i18n config
singleton.
- 4a5f8e8: Remove unused internal exports and dead utility code.
- 083d306: Remove deprecated i18n cache lifecycle hooks and unused cache
events.

The cache subscription surface now only exposes
`translations-cache-miss`, which is used for runtime translation
updates. Deprecated lifecycle constructor callbacks and unused
locale/dictionary cache hit/miss events have been removed.
## locadex@1.0.186-odysseus.5

### Patch Changes

- Updated dependencies [4a5f8e8]
  - gt@2.14.51-odysseus.5
## gt-next@11.0.0-odysseus.10

### Patch Changes

- 432fa49: Use Next.js caching semantics for Cache Components by
disabling GT cache expiry and development hot reload runtime
translation.

Async translation and dictionary lookup boundaries now keep synchronous
access to the loaded snapshot, so APIs like `getGT` and
`getTranslations` can still resolve strings after cache expiry is
delegated to Next.js.

Global singleton setup now preserves the first initialized instance
instead of replacing it on later initialization attempts.

- 07f74f0: Clean up stale package metadata and align TanStack Start
package entry points.
- 432fa49: Support dev hot reload lookups for server `getGT` strings.

`getGT` can now receive compiler-injected message metadata and prefetch
missing translations through the runtime cache in development. `gt-next`
forwards the server request conditions into this path so App Router
server strings can participate in hot reload translation updates.

Compiler-injected `getGT` and `useGT` preload messages now emit the same
sugar metadata keys used by runtime lookup options.

- 33d4be2: Point the `react-server` export condition at the RSC-specific
declaration file so TypeScript no longer exposes client-only hooks in
React Server Components.
- 75dc650: Remove the unused `getDomain` request-function plumbing from
`gt-next`.

`getDomain` was scaffolded (a throw-only `internal/_getDomain` stub, a
`getDomainPath` config prop, a `getDomain` entry in the request-function
registry, and a `_GENERALTRANSLATION_CUSTOM_GET_DOMAIN_ENABLED` build
env var) but never wired to any runtime consumer — no `getDomain()`
request helper exists and nothing reads the env var. Removes the stub
module, the `./internal/_getDomain` export, the config prop/registry
entries, and the dead env var. `getLocale`/`getRegion` are unchanged.

- 8e0a992: Remove the deprecated SSG and experimental-locale-resolution
code paths from `gt-next`.

Drops the long-deprecated, runtime-dead config surface:
`experimentalEnableSSG`,
`experimentalLocaleResolution`/`experimentalLocaleResolutionParam`, the
static request functions (`STATIC_REQUEST_FUNCTIONS`/`getStatic*` and
their non-existent `internal/static/*` aliases), and
`disableSSGWarnings`/`getStatic*Path` doc props. Removes
`plugin/checks/ssgChecks.ts`, the experimental branch of
`cacheComponentsChecks`, the related error/warning builders, and the
corresponding build-time env vars (`_GENERALTRANSLATION_ENABLE_SSG`,
`_GENERALTRANSLATION_EXPERIMENTAL_LOCALE_RESOLUTION[_PARAM]`,
`_GENERALTRANSLATION_STATIC_GET_*_ENABLED`) — none of which were read at
runtime. The live cacheComponents checks and
`noLocalesCouldBeDeterminedWarning` are retained.

- Updated dependencies [432fa49]
- Updated dependencies [07f74f0]
- Updated dependencies [432fa49]
- Updated dependencies [ee34fea]
- Updated dependencies [bcba6fd]
- Updated dependencies [933916e]
- Updated dependencies [b7b3eaf]
- Updated dependencies [dfb5fc9]
- Updated dependencies [4a5f8e8]
- Updated dependencies [288c9f8]
- Updated dependencies [083d306]
  - gt-i18n@1.0.0-odysseus.5
  - gt-react@11.0.0-odysseus.10
  - @generaltranslation/compiler@1.3.25-odysseus.5
  - @generaltranslation/react-core@11.0.0-odysseus.10
## @generaltranslation/gt-next-lint@15.0.0-odysseus.10

### Patch Changes

- Updated dependencies [432fa49]
- Updated dependencies [07f74f0]
- Updated dependencies [432fa49]
- Updated dependencies [33d4be2]
- Updated dependencies [75dc650]
- Updated dependencies [8e0a992]
  - gt-next@11.0.0-odysseus.10
## gt-node@1.0.0-odysseus.5

### Patch Changes

- Updated dependencies [432fa49]
- Updated dependencies [432fa49]
- Updated dependencies [933916e]
- Updated dependencies [4a5f8e8]
- Updated dependencies [083d306]
  - gt-i18n@1.0.0-odysseus.5
## gt-react@11.0.0-odysseus.10

### Patch Changes

- 07f74f0: Clean up stale package metadata and align TanStack Start
package entry points.
- ee34fea: Use the shared runtime environment helper for browser cache
dev-mode checks so `gt-react` typechecks without Vite ambient types.
- bcba6fd: Fix `useVersionId()` throwing and `useLocaleDirection()`
requiring a locale argument in the client and server entrypoints.

`useVersionId()` now returns the current version id (instead of throwing
the react-core "not implemented" error), and `useLocaleDirection()` once
again accepts an optional locale that defaults to the current locale.
The shared implementation now lives in
`@generaltranslation/react-core/hooks`, so `gt-react` and
`gt-react-native` use the same behavior; the RSC entrypoint keeps its
stricter signatures.

- 288c9f8: Remove the deprecated `internalInitializeGTSPA` export from
`@generaltranslation/react-core/pure` and the downstream `gt-react`
server/types surfaces. Use `initializeGTSPA` from `gt-react` for browser
SPA initialization.
- Updated dependencies [432fa49]
- Updated dependencies [432fa49]
- Updated dependencies [bcba6fd]
- Updated dependencies [933916e]
- Updated dependencies [b7b3eaf]
- Updated dependencies [dfb5fc9]
- Updated dependencies [4a5f8e8]
- Updated dependencies [288c9f8]
- Updated dependencies [083d306]
  - gt-i18n@1.0.0-odysseus.5
  - @generaltranslation/react-core@11.0.0-odysseus.10
## @generaltranslation/react-core@11.0.0-odysseus.10

### Patch Changes

- bcba6fd: Fix `useVersionId()` throwing and `useLocaleDirection()`
requiring a locale argument in the client and server entrypoints.

`useVersionId()` now returns the current version id (instead of throwing
the react-core "not implemented" error), and `useLocaleDirection()` once
again accepts an optional locale that defaults to the current locale.
The shared implementation now lives in
`@generaltranslation/react-core/hooks`, so `gt-react` and
`gt-react-native` use the same behavior; the RSC entrypoint keeps its
stricter signatures.

- b7b3eaf: Share render pipeline child renderers with prepared
translation rendering.
- dfb5fc9: Add portable selector hook return types for React Native
declarations.
- 4a5f8e8: Remove unused internal exports and dead utility code.
- 288c9f8: Remove the deprecated `internalInitializeGTSPA` export from
`@generaltranslation/react-core/pure` and the downstream `gt-react`
server/types surfaces. Use `initializeGTSPA` from `gt-react` for browser
SPA initialization.
- Updated dependencies [432fa49]
- Updated dependencies [432fa49]
- Updated dependencies [933916e]
- Updated dependencies [4a5f8e8]
- Updated dependencies [083d306]
  - gt-i18n@1.0.0-odysseus.5
## gt-react-native@11.0.0-odysseus.10

### Patch Changes

- bcba6fd: Fix `useVersionId()` throwing and `useLocaleDirection()`
requiring a locale argument in the client and server entrypoints.

`useVersionId()` now returns the current version id (instead of throwing
the react-core "not implemented" error), and `useLocaleDirection()` once
again accepts an optional locale that defaults to the current locale.
The shared implementation now lives in
`@generaltranslation/react-core/hooks`, so `gt-react` and
`gt-react-native` use the same behavior; the RSC entrypoint keeps its
stricter signatures.

- dfb5fc9: Add portable selector hook return types for React Native
declarations.
- Updated dependencies [432fa49]
- Updated dependencies [432fa49]
- Updated dependencies [bcba6fd]
- Updated dependencies [933916e]
- Updated dependencies [b7b3eaf]
- Updated dependencies [dfb5fc9]
- Updated dependencies [4a5f8e8]
- Updated dependencies [288c9f8]
- Updated dependencies [083d306]
  - gt-i18n@1.0.0-odysseus.5
  - @generaltranslation/react-core@11.0.0-odysseus.10
## gt-tanstack-start@11.0.0-odysseus.10

### Patch Changes

- 07f74f0: Clean up stale package metadata and align TanStack Start
package entry points.
- Updated dependencies [432fa49]
- Updated dependencies [07f74f0]
- Updated dependencies [432fa49]
- Updated dependencies [ee34fea]
- Updated dependencies [bcba6fd]
- Updated dependencies [933916e]
- Updated dependencies [b7b3eaf]
- Updated dependencies [dfb5fc9]
- Updated dependencies [4a5f8e8]
- Updated dependencies [288c9f8]
- Updated dependencies [083d306]
  - gt-i18n@1.0.0-odysseus.5
  - gt-react@11.0.0-odysseus.10
  - @generaltranslation/react-core@11.0.0-odysseus.10

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
## Summary
- Preserve compiler-collected format metadata on useGT/getGT preload
entries.
- Add a regression test for await getGT() with $format: STRING.

## Testing
- pnpm --filter @generaltranslation/compiler test
- pnpm --filter @generaltranslation/compiler typecheck
- pnpm --filter @generaltranslation/compiler format

No changeset added per request.

<!-- greptile_comment -->

<h3>Greptile Summary</h3>

This PR fixes a bug where the `format` field was not being included in
the preload metadata injected into `getGT`/`useGT` call expressions
during the compiler's injection pass. It also adds a regression test and
refactors shared test helper logic.

- **`injectCallbackDeclaratorFunctionParameters.ts`**: Adds a `format`
property to the `objectExpression` built for each `translationContent`
entry, matching the same conditional-spread pattern already used for
`id`, `context`, and `maxChars`.
- **`runtimeTranslatePass.test.ts`**: Extracts a reusable
`getObjectPropertyValue` helper, adds a `getPreloadMessageValue` helper
for asserting into the preload array structure, and adds a dedicated
test case verifying `format` is forwarded through the pipeline.

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

Safe to merge — the change is a minimal, well-tested addition of a
missing field to the preload metadata injection.

The production change exactly mirrors the established pattern for other
optional fields and the new test exercises the full compiler pipeline
end-to-end. No existing behaviour is altered; only the previously
dropped `format` field is now forwarded.

No files require special attention. The single style note in the test
helper does not affect correctness.
</details>

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

| Filename | Overview |
|----------|----------|
|
packages/compiler/src/transform/injection/injectCallbackDeclaratorFunctionParameters.ts
| Adds `format` property injection into the preload metadata object;
follows the identical conditional-spread pattern used for other optional
fields (`id`, `context`, `maxChars`) — correct and minimal. |
| packages/compiler/src/passes/__tests__/runtimeTranslatePass.test.ts |
Adds test coverage for the format field in getGT preload metadata; uses
`expect()` assertions inside a utility helper, which can produce
harder-to-diagnose failure messages when assertions fail mid-helper. |

</details>

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

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

```mermaid
%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
    participant Source as Source File
    participant Collection as collectionPass
    participant State as TransformState stringCollector
    participant Injection as injectionPass
    participant AST as Final AST getGT

    Source->>Collection: traverse AST
    Collection->>State: "store content {hash, message, format, ...}"
    Source->>Injection: traverse AST (varDeclarator for getGT)
    Injection->>State: getTranslationContent(id)
    State-->>Injection: "[{hash, message, format, ...}]"
    Injection->>AST: inject arguments as array of objects (now includes format field)
```

</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 Source as Source File
    participant Collection as collectionPass
    participant State as TransformState stringCollector
    participant Injection as injectionPass
    participant AST as Final AST getGT

    Source->>Collection: traverse AST
    Collection->>State: "store content {hash, message, format, ...}"
    Source->>Injection: traverse AST (varDeclarator for getGT)
    Injection->>State: getTranslationContent(id)
    State-->>Injection: "[{hash, message, format, ...}]"
    Injection->>AST: inject arguments as array of objects (now includes format field)
```

</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%0Apackages%2Fcompiler%2Fsrc%2Fpasses%2F__tests__%2FruntimeTranslatePass.test.ts%3A160-169%0AUsing%20%60expect%28%29%60%20inside%20a%20non-test%20utility%20function%20can%20produce%20confusing%20failure%20output%20%E2%80%94%20when%20the%20assertion%20throws%2C%20Jest%2FVitest%20reports%20the%20failure%20site%20as%20being%20inside%20%60getPreloadMessageValue%60%2C%20making%20it%20hard%20to%20tell%20which%20test%20case%20failed%20or%20what%20the%20actual%20received%20value%20was.%20Prefer%20returning%20%60undefined%60%20on%20unexpected%20shapes%20and%20letting%20the%20call-site%20assertions%20fail%20with%20clear%20context.%0A%0A%60%60%60suggestion%0Afunction%20getPreloadMessageValue%28%0A%20%20call%3A%20t.CallExpression%2C%0A%20%20key%3A%20string%0A%29%3A%20string%20%7C%20number%20%7C%20undefined%20%7B%0A%20%20const%20arg%20%3D%20call.arguments%5B0%5D%3B%0A%20%20if%20%28!t.isArrayExpression%28arg%29%29%20return%20undefined%3B%0A%20%20const%20message%20%3D%20%28arg%20as%20t.ArrayExpression%29.elements%5B0%5D%3B%0A%20%20if%20%28!t.isObjectExpression%28message%29%29%20return%20undefined%3B%0A%20%20return%20getObjectPropertyValue%28message%20as%20t.ObjectExpression%2C%20key%29%3B%0A%7D%0A%60%60%60%0A%0A&repo=generaltranslation%2Fgt&pr=1743&platform=github"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://greptile-static-assets.s3.amazonaws.com/badges/FixAllInClaudeDark.svg?v=4"><source
media="(prefers-color-scheme: light)"
srcset="https://greptile-static-assets.s3.amazonaws.com/badges/FixAllInClaude.svg?v=4"><img
alt="Fix All in Claude Code"
src="https://greptile-static-assets.s3.amazonaws.com/badges/FixAllInClaude.svg?v=4"></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%2Fgetgt-preload-format%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%2Fgetgt-preload-format%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%0Apackages%2Fcompiler%2Fsrc%2Fpasses%2F__tests__%2FruntimeTranslatePass.test.ts%3A160-169%0AUsing%20%60expect%28%29%60%20inside%20a%20non-test%20utility%20function%20can%20produce%20confusing%20failure%20output%20%E2%80%94%20when%20the%20assertion%20throws%2C%20Jest%2FVitest%20reports%20the%20failure%20site%20as%20being%20inside%20%60getPreloadMessageValue%60%2C%20making%20it%20hard%20to%20tell%20which%20test%20case%20failed%20or%20what%20the%20actual%20received%20value%20was.%20Prefer%20returning%20%60undefined%60%20on%20unexpected%20shapes%20and%20letting%20the%20call-site%20assertions%20fail%20with%20clear%20context.%0A%0A%60%60%60suggestion%0Afunction%20getPreloadMessageValue%28%0A%20%20call%3A%20t.CallExpression%2C%0A%20%20key%3A%20string%0A%29%3A%20string%20%7C%20number%20%7C%20undefined%20%7B%0A%20%20const%20arg%20%3D%20call.arguments%5B0%5D%3B%0A%20%20if%20%28!t.isArrayExpression%28arg%29%29%20return%20undefined%3B%0A%20%20const%20message%20%3D%20%28arg%20as%20t.ArrayExpression%29.elements%5B0%5D%3B%0A%20%20if%20%28!t.isObjectExpression%28message%29%29%20return%20undefined%3B%0A%20%20return%20getObjectPropertyValue%28message%20as%20t.ObjectExpression%2C%20key%29%3B%0A%7D%0A%60%60%60%0A%0A&repo=generaltranslation%2Fgt&pr=1743&platform=github"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://greptile-static-assets.s3.amazonaws.com/badges/FixAllInCodexDark.svg?v=4"><source
media="(prefers-color-scheme: light)"
srcset="https://greptile-static-assets.s3.amazonaws.com/badges/FixAllInCodex.svg?v=4"><img
alt="Fix All in Codex"
src="https://greptile-static-assets.s3.amazonaws.com/badges/FixAllInCodex.svg?v=4"></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
packages/compiler/src/passes/__tests__/runtimeTranslatePass.test.ts:160-169
Using `expect()` inside a non-test utility function can produce confusing failure output — when the assertion throws, Jest/Vitest reports the failure site as being inside `getPreloadMessageValue`, making it hard to tell which test case failed or what the actual received value was. Prefer returning `undefined` on unexpected shapes and letting the call-site assertions fail with clear context.

```suggestion
function getPreloadMessageValue(
  call: t.CallExpression,
  key: string
): string | number | undefined {
  const arg = call.arguments[0];
  if (!t.isArrayExpression(arg)) return undefined;
  const message = (arg as t.ArrayExpression).elements[0];
  if (!t.isObjectExpression(message)) return undefined;
  return getObjectPropertyValue(message as t.ObjectExpression, key);
}
```

`````

</details>

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

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

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

- Stop passing `locale={false}` through to `next/link` after
`gt-next/link` localizes hrefs.
- Add regression coverage that verifies localized links do not forward
`locale` to the underlying Link component.

## Testing

- `pnpm exec oxfmt --check packages/next/src/link/Link.tsx
packages/next/src/link/__tests__/Link.test.tsx`
- `pnpm exec oxlint packages/next/src/link/Link.tsx
packages/next/src/link/__tests__/Link.test.tsx`
- `pnpm --filter gt-next typecheck`
- `pnpm --filter gt-next exec vitest run
src/link/__tests__/Link.test.tsx`
- `pnpm --filter gt-next test:js`
- `pnpm --filter gt-next build:no-swc-plugin`

<!-- greptile_comment -->

<h3>Greptile Summary</h3>

This PR removes the explicit `locale={false}` forwarding to `next/link`
after `gt-next/link` has already localized the href, and adds a
regression test to verify the behavior. The `locale` prop is a Pages
Router i18n feature that is not supported (and could produce warnings)
in the Next.js App Router.

- `renderLink` now omits `locale` entirely from the props passed to
`NextLink`; since `locale` is already destructured out of
`ResolvedLinkProps`, the fix is achieved by simply dropping the explicit
`locale={false}` override.
- Two vitest cases are added: one for the default path (locale
auto-detected from `useLocale`) and one for the opt-out path
(`locale={false}`), both asserting that `locale` does not appear on the
rendered `NextLink` props.

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

Safe to merge — the change is a one-line removal of a prop that was
already redundant and could cause warnings in App Router.

The `locale` prop was already being destructured out of
`ResolvedLinkProps` before `...props` was spread, so the explicit
`locale={false}` override was the only way it reached `NextLink`.
Removing it is a clean, correct fix. The new tests cover both the
default-locale and opt-out paths and confirm `locale` does not appear on
the rendered `NextLink` props.

No files require special attention.
</details>

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

| Filename | Overview |
|----------|----------|
| packages/next/src/link/Link.tsx | Removes the explicit
`locale={false}` prop forwarded to NextLink; locale is already
destructured out and localizeHref handles all prefixing, so the fix is
correct and minimal. |
| packages/next/src/link/__tests__/Link.test.tsx | New test file with
two cases covering the default locale and locale=false paths; mock setup
and assertions correctly validate that locale is not forwarded to
NextLink. |

</details>

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

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

```mermaid
%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
    participant User as User Code
    participant GTLink as gt-next Link
    participant renderLink as renderLink
    participant localizeHref as localizeHref
    participant NextLink as next/link

    User->>GTLink: "href="/home""
    GTLink->>GTLink: "locale = useLocale() returns "fr""
    GTLink->>renderLink: "href="/home", locale="fr", ...props"
    renderLink->>localizeHref: "/home", "fr"
    localizeHref-->>renderLink: "/fr/home"
    renderLink->>NextLink: "href="/fr/home", ...props (locale omitted)"

    User->>GTLink: "href="/legal", locale=false"
    GTLink->>renderLink: "href="/legal", locale=false, ...props"
    renderLink->>localizeHref: "/legal", false
    localizeHref-->>renderLink: "/legal" unchanged
    renderLink->>NextLink: "href="/legal", ...props (locale omitted)"
```

</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 User as User Code
    participant GTLink as gt-next Link
    participant renderLink as renderLink
    participant localizeHref as localizeHref
    participant NextLink as next/link

    User->>GTLink: "href="/home""
    GTLink->>GTLink: "locale = useLocale() returns "fr""
    GTLink->>renderLink: "href="/home", locale="fr", ...props"
    renderLink->>localizeHref: "/home", "fr"
    localizeHref-->>renderLink: "/fr/home"
    renderLink->>NextLink: "href="/fr/home", ...props (locale omitted)"

    User->>GTLink: "href="/legal", locale=false"
    GTLink->>renderLink: "href="/legal", locale=false, ...props"
    renderLink->>localizeHref: "/legal", false
    localizeHref-->>renderLink: "/legal" unchanged
    renderLink->>NextLink: "href="/legal", ...props (locale omitted)"
```

</a>
</details>

<sub>Reviews (1): Last reviewed commit: ["fix: stop forwarding locale
prop from
ne..."](69b133c)
| [Re-trigger
Greptile](https://app.greptile.com/api/retrigger?id=41022058)</sub>

<!-- /greptile_comment -->
## Summary\n- make sync `t()` honor the writable condition store's
`enableI18n` flag\n- return source/default-locale output when i18n is
disabled\n- update `t` tests for the condition-store shape and disabled
behavior\n\n## Checks\n- `pnpm --filter gt-i18n typecheck`\n- `pnpm
--filter gt-i18n test -- --runInBand`

<!-- greptile_comment -->

<h3>Greptile Summary</h3>

This PR fixes the synchronous `t()` function to honour the `enableI18n`
flag from the writable condition store, mirroring the guard already
present in async helpers like `getTranslations`, `getMessages`, and
`getGT`. When i18n is disabled both the string-overload and
tagged-template-literal code paths now fall back to
`getDefaultLocale()`, returning source-language output instead of a
translation.

- **`t.ts`**: The condition store is now fetched once per call;
`getEnableI18n()` gates locale resolution in both `t()` and
`handleTaggedTemplateLiteralTranslation()`, and the now-unused
`getLocale` import is replaced with `getDefaultLocale`.
- **`t.test.ts`**: All existing mocks are updated to include
`getEnableI18n`, and a new test asserts that source output is returned
when i18n is disabled — though only for the string overload; the
tagged-template-literal disabled branch has no dedicated test.

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

Safe to merge; the core logic change is straightforward and consistent
with how other translation helpers already handle the enableI18n flag.

Both code paths in t.ts are correctly guarded, and the implementation
matches the existing pattern in the async helpers. The only gap is a
missing test for the tagged-template-literal branch when i18n is
disabled — the feature works, but a future regression there would go
undetected by the suite.

t.test.ts — the tagged-template-literal disabled-i18n scenario has no
test coverage.
</details>

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

| Filename | Overview |
|----------|----------|
| packages/i18n/src/translation-functions/t.ts | Adds enableI18n guard
to both the string and tagged-template-literal code paths; drops unused
getLocale import and reads the condition store once per call. |
| packages/i18n/src/translation-functions/__tests__/t.test.ts | Updates
all existing mocks to include getEnableI18n and adds a new test for the
disabled-i18n string branch; the tagged-template-literal disabled case
is untested. |

</details>

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

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

```mermaid
%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[t called] --> B{string or template?}
    B -->|string| C[get conditionStore]
    B -->|template| D[get conditionStore]
    C --> E{enableI18n?}
    E -->|true| G[use explicit locale or conditionStore locale]
    E -->|false| H[use defaultLocale]
    D --> F{enableI18n?}
    F -->|true| I[use conditionStore locale]
    F -->|false| J[use defaultLocale]
    G --> K[resolveStringContentWithFallback]
    H --> K
    I --> L[resolveStringContent with fallback]
    J --> L
    K --> M[return string]
    L --> M
```

</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[t called] --> B{string or template?}
    B -->|string| C[get conditionStore]
    B -->|template| D[get conditionStore]
    C --> E{enableI18n?}
    E -->|true| G[use explicit locale or conditionStore locale]
    E -->|false| H[use defaultLocale]
    D --> F{enableI18n?}
    F -->|true| I[use conditionStore locale]
    F -->|false| J[use defaultLocale]
    G --> K[resolveStringContentWithFallback]
    H --> K
    I --> L[resolveStringContent with fallback]
    J --> L
    K --> M[return string]
    L --> M
```

</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%0Apackages%2Fi18n%2Fsrc%2Ftranslation-functions%2F__tests__%2Ft.test.ts%3A164-183%0A**Missing%20disabled-i18n%20test%20for%20the%20tagged-template%20branch**%0A%0AThe%20new%20%60'returns%20the%20source%20when%20i18n%20is%20disabled'%60%20test%20only%20exercises%20the%20string%20overload%20%28%60t%28message%2C%20options%29%60%29.%20%60handleTaggedTemplateLiteralTranslation%60%20received%20the%20same%20%60getEnableI18n%28%29%60%20gate%2C%20but%20there%20is%20no%20corresponding%20test%20that%20calls%20%60%60%20t%60Hello%20%24%7Bname%7D!%60%20%60%60%20with%20%60getEnableI18n%3A%20%28%29%20%3D%3E%20false%60.%20If%20a%20regression%20is%20introduced%20in%20that%20branch%2C%20the%20suite%20will%20not%20catch%20it.%0A%0A&repo=generaltranslation%2Fgt&pr=1744&platform=github"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://greptile-static-assets.s3.amazonaws.com/badges/FixAllInClaudeDark.svg?v=4"><source
media="(prefers-color-scheme: light)"
srcset="https://greptile-static-assets.s3.amazonaws.com/badges/FixAllInClaude.svg?v=4"><img
alt="Fix All in Claude Code"
src="https://greptile-static-assets.s3.amazonaws.com/badges/FixAllInClaude.svg?v=4"></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%2Fenable-i18n-t-disabled%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%2Fenable-i18n-t-disabled%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%0Apackages%2Fi18n%2Fsrc%2Ftranslation-functions%2F__tests__%2Ft.test.ts%3A164-183%0A**Missing%20disabled-i18n%20test%20for%20the%20tagged-template%20branch**%0A%0AThe%20new%20%60'returns%20the%20source%20when%20i18n%20is%20disabled'%60%20test%20only%20exercises%20the%20string%20overload%20%28%60t%28message%2C%20options%29%60%29.%20%60handleTaggedTemplateLiteralTranslation%60%20received%20the%20same%20%60getEnableI18n%28%29%60%20gate%2C%20but%20there%20is%20no%20corresponding%20test%20that%20calls%20%60%60%20t%60Hello%20%24%7Bname%7D!%60%20%60%60%20with%20%60getEnableI18n%3A%20%28%29%20%3D%3E%20false%60.%20If%20a%20regression%20is%20introduced%20in%20that%20branch%2C%20the%20suite%20will%20not%20catch%20it.%0A%0A&repo=generaltranslation%2Fgt&pr=1744&platform=github"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://greptile-static-assets.s3.amazonaws.com/badges/FixAllInCodexDark.svg?v=4"><source
media="(prefers-color-scheme: light)"
srcset="https://greptile-static-assets.s3.amazonaws.com/badges/FixAllInCodex.svg?v=4"><img
alt="Fix All in Codex"
src="https://greptile-static-assets.s3.amazonaws.com/badges/FixAllInCodex.svg?v=4"></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
packages/i18n/src/translation-functions/__tests__/t.test.ts:164-183
**Missing disabled-i18n test for the tagged-template branch**

The new `'returns the source when i18n is disabled'` test only exercises the string overload (`t(message, options)`). `handleTaggedTemplateLiteralTranslation` received the same `getEnableI18n()` gate, but there is no corresponding test that calls `` t`Hello ${name}!` `` with `getEnableI18n: () => false`. If a regression is introduced in that branch, the suite will not catch it.

`````

</details>

<sub>Reviews (1): Last reviewed commit: ["fix: respect disabled i18n in
t"](8c74826)
| [Re-trigger
Greptile](https://app.greptile.com/api/retrigger?id=41005495)</sub>

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

<!-- /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
## @generaltranslation/compiler@1.3.25-odysseus.6

### Patch Changes

- c34cab3: Preserve `$format` in compiler-injected `getGT` and `useGT`
preload metadata so preloaded runtime lookups use the same hash format
as the original translation call.
## gt-next@11.0.0-odysseus.11

### Patch Changes

- fe7dfd3: Stop forwarding `locale={false}` from `gt-next/link` to the
underlying Next.js link after localizing the href.

This avoids React DOM warnings in newer Next.js versions where the
control prop can reach the rendered anchor.

- Updated dependencies [c34cab3]
  - @generaltranslation/compiler@1.3.25-odysseus.6
  - @generaltranslation/react-core@11.0.0-odysseus.11
  - gt-react@11.0.0-odysseus.11
## @generaltranslation/gt-next-lint@15.0.0-odysseus.11

### Patch Changes

- Updated dependencies [fe7dfd3]
  - gt-next@11.0.0-odysseus.11
## gt-react@11.0.0-odysseus.11

### Patch Changes

- @generaltranslation/react-core@11.0.0-odysseus.11
## gt-react-native@11.0.0-odysseus.11

### Patch Changes

- @generaltranslation/react-core@11.0.0-odysseus.11
## gt-tanstack-start@11.0.0-odysseus.11

### Patch Changes

- @generaltranslation/react-core@11.0.0-odysseus.11
- gt-react@11.0.0-odysseus.11
## @generaltranslation/react-core@11.0.0-odysseus.11

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
## Summary
- replace repeated determineLocale/defaultLocale fallback code with
resolveSupportedLocale
- keep behavior equivalent while reducing repeated locale resolution
logic

## Verification
- pnpm --filter gt-react test -- BrowserConditionStore
- pnpm --filter gt-react typecheck
- pnpm --filter gt-react-native typecheck
- pnpm --filter gt-node typecheck
- pnpm --filter gt-i18n typecheck

<!-- greptile_comment -->

<h3>Greptile Summary</h3>

This PR consolidates the repeated `determineLocale(x) ||
getDefaultLocale()` two-step fallback pattern into a single call to
`resolveSupportedLocale`, which encapsulates the same logic inside
`I18nConfig`. The change touches six production files across the
`gt-i18n`, `gt-node`, `gt-react`, and `gt-react-native` packages plus
the matching test mock.

- The refactor is behaviorally equivalent: `resolveSupportedLocale`
delegates to `determineSupportedLocaleWithConfig` (which calls the same
`determineLocale` path) and falls back to `defaultLocale`, mirroring the
old pattern exactly.
- The `resolveLocale` private helper in `createBrowserConditionStore.ts`
is correctly inlined away since it was a one-liner wrapping the old
pattern.
- The test mock retains the now-unused `determineLocale` and
`getDefaultLocale` stubs; trimming them would keep the mock in sync with
what the code actually exercises.

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

Safe to merge — the refactor is a mechanical substitution of an
equivalent one-liner across six files with no logic changes.

Every changed call site replaces a two-step fallback with
resolveSupportedLocale, which internally performs the same
determineLocale-then-defaultLocale logic, confirmed by reading
I18nConfig. The only finding is dead stubs in the test mock.

The test mock in BrowserConditionStore.test.ts retains stubs that are no
longer called; worth tidying but not a blocker.
</details>


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




| Filename | Overview |
|----------|----------|
| packages/i18n/src/condition-store/ReadonlyConditionStore.ts | Replaces
two-step determineLocale+getDefaultLocale fallback with
resolveSupportedLocale; behaviorally equivalent. |
| packages/i18n/src/condition-store/WritableConditionStore.ts | Same
single-line refactor in setLocale; no behavioral change. |
| packages/node/src/async-i18n-cache/AsyncConditionStore.ts | Local
resolveLocale helper simplified to one line using
resolveSupportedLocale; equivalent logic. |
| packages/react-native/src/utils/resolveLocale.ts | resolveLocale
utility simplified; null coalescing to undefined preserved, behavior
unchanged. |
| packages/react/src/condition-store/BrowserConditionStore.ts | Three
call sites updated in constructor, updateLocale, and getBrowserLocale;
all equivalent to prior pattern. |
|
packages/react/src/condition-store/__tests__/BrowserConditionStore.test.ts
| Adds resolveSupportedLocale to the mock; retains now-unused
determineLocale and getDefaultLocale stubs. |
| packages/react/src/condition-store/createBrowserConditionStore.ts |
Removes the now-redundant resolveLocale private helper; determineLocale
inlines the call directly. |

</details>


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

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

```mermaid
%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
    participant Caller
    participant ConditionStore
    participant I18nConfig

    Note over Caller,I18nConfig: Before (old pattern)
    Caller->>ConditionStore: setLocale(locale)
    ConditionStore->>I18nConfig: determineLocale(locale)
    I18nConfig-->>ConditionStore: "string | undefined"
    alt undefined
        ConditionStore->>I18nConfig: getDefaultLocale()
        I18nConfig-->>ConditionStore: defaultLocale
    end
    ConditionStore-->>Caller: resolved locale

    Note over Caller,I18nConfig: After (new pattern)
    Caller->>ConditionStore: setLocale(locale)
    ConditionStore->>I18nConfig: resolveSupportedLocale(locale)
    I18nConfig->>I18nConfig: determineSupportedLocaleWithConfig(candidates, this)
    I18nConfig->>I18nConfig: determineLocale(candidates) OR defaultLocale
    I18nConfig-->>ConditionStore: string (never undefined)
    ConditionStore-->>Caller: resolved locale
```

</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 Caller
    participant ConditionStore
    participant I18nConfig

    Note over Caller,I18nConfig: Before (old pattern)
    Caller->>ConditionStore: setLocale(locale)
    ConditionStore->>I18nConfig: determineLocale(locale)
    I18nConfig-->>ConditionStore: "string | undefined"
    alt undefined
        ConditionStore->>I18nConfig: getDefaultLocale()
        I18nConfig-->>ConditionStore: defaultLocale
    end
    ConditionStore-->>Caller: resolved locale

    Note over Caller,I18nConfig: After (new pattern)
    Caller->>ConditionStore: setLocale(locale)
    ConditionStore->>I18nConfig: resolveSupportedLocale(locale)
    I18nConfig->>I18nConfig: determineSupportedLocaleWithConfig(candidates, this)
    I18nConfig->>I18nConfig: determineLocale(candidates) OR defaultLocale
    I18nConfig-->>ConditionStore: string (never undefined)
    ConditionStore-->>Caller: resolved locale
```

</a>
</details>


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

1.
`packages/react/src/condition-store/__tests__/BrowserConditionStore.test.ts`,
line 10-21
([link](https://github.com/generaltranslation/gt/blob/602339b6c47568cbb07904df5fbe6c894344e091/packages/react/src/condition-store/__tests__/BrowserConditionStore.test.ts#L10-L21))

<a href="#"><img alt="P2"
src="https://greptile-static-assets.s3.amazonaws.com/badges/p2.svg?v=9"
align="top"></a> After the refactor, `BrowserConditionStore` no longer
calls `determineLocale` or `getDefaultLocale` — only
`resolveSupportedLocale` is invoked. Leaving the unused stubs in the
mock is harmless but creates noise for future readers who may expect
these methods to be exercised.

   

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

   `````markdown
   This is a comment left during a code review.
Path:
packages/react/src/condition-store/__tests__/BrowserConditionStore.test.ts
   Line: 10-21

   Comment:
After the refactor, `BrowserConditionStore` no longer calls
`determineLocale` or `getDefaultLocale` — only `resolveSupportedLocale`
is invoked. Leaving the unused stubs in the mock is harmless but creates
noise for future readers who may expect these methods to be exercised.

   

   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%2Freact%2Fsrc%2Fcondition-store%2F__tests__%2FBrowserConditionStore.test.ts%0ALine%3A%2010-21%0A%0AComment%3A%0AAfter%20the%20refactor%2C%20%60BrowserConditionStore%60%20no%20longer%20calls%20%60determineLocale%60%20or%20%60getDefaultLocale%60%20%E2%80%94%20only%20%60resolveSupportedLocale%60%20is%20invoked.%20Leaving%20the%20unused%20stubs%20in%20the%20mock%20is%20harmless%20but%20creates%20noise%20for%20future%20readers%20who%20may%20expect%20these%20methods%20to%20be%20exercised.%0A%0A%60%60%60suggestion%0Avi.mock%28'gt-i18n%2Finternal'%2C%20%28%29%20%3D%3E%20%28%7B%0A%20%20getI18nConfig%3A%20%28%29%20%3D%3E%20%28%7B%0A%20%20%20%20resolveSupportedLocale%3A%20%28locale%3F%3A%20string%20%7C%20string%5B%5D%29%20%3D%3E%20%7B%0A%20%20%20%20%20%20const%20resolved%20%3D%20Array.isArray%28locale%29%20%3F%20locale%5B0%5D%20%3A%20locale%3B%0A%20%20%20%20%20%20return%20resolved%20%7C%7C%20'en'%3B%0A%20%20%20%20%7D%2C%0A%20%20%7D%29%2C%0A%7D%29%29%3B%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=1750&platform=github"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://greptile-static-assets.s3.amazonaws.com/badges/FixInClaudeDark.svg?v=4"><source
media="(prefers-color-scheme: light)"
srcset="https://greptile-static-assets.s3.amazonaws.com/badges/FixInClaude.svg?v=4"><img
alt="Fix in Claude Code"
src="https://greptile-static-assets.s3.amazonaws.com/badges/FixInClaude.svg?v=4"></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-resolve-supported-locale%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-resolve-supported-locale%22.%0A%0AThis%20is%20a%20comment%20left%20during%20a%20code%20review.%0APath%3A%20packages%2Freact%2Fsrc%2Fcondition-store%2F__tests__%2FBrowserConditionStore.test.ts%0ALine%3A%2010-21%0A%0AComment%3A%0AAfter%20the%20refactor%2C%20%60BrowserConditionStore%60%20no%20longer%20calls%20%60determineLocale%60%20or%20%60getDefaultLocale%60%20%E2%80%94%20only%20%60resolveSupportedLocale%60%20is%20invoked.%20Leaving%20the%20unused%20stubs%20in%20the%20mock%20is%20harmless%20but%20creates%20noise%20for%20future%20readers%20who%20may%20expect%20these%20methods%20to%20be%20exercised.%0A%0A%60%60%60suggestion%0Avi.mock%28'gt-i18n%2Finternal'%2C%20%28%29%20%3D%3E%20%28%7B%0A%20%20getI18nConfig%3A%20%28%29%20%3D%3E%20%28%7B%0A%20%20%20%20resolveSupportedLocale%3A%20%28locale%3F%3A%20string%20%7C%20string%5B%5D%29%20%3D%3E%20%7B%0A%20%20%20%20%20%20const%20resolved%20%3D%20Array.isArray%28locale%29%20%3F%20locale%5B0%5D%20%3A%20locale%3B%0A%20%20%20%20%20%20return%20resolved%20%7C%7C%20'en'%3B%0A%20%20%20%20%7D%2C%0A%20%20%7D%29%2C%0A%7D%29%29%3B%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=1750&platform=github"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://greptile-static-assets.s3.amazonaws.com/badges/FixInCodexDark.svg?v=4"><source
media="(prefers-color-scheme: light)"
srcset="https://greptile-static-assets.s3.amazonaws.com/badges/FixInCodex.svg?v=4"><img
alt="Fix in Codex"
src="https://greptile-static-assets.s3.amazonaws.com/badges/FixInCodex.svg?v=4"></picture></a>

</details>

<!-- /greptile_failed_comments -->

<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%0Apackages%2Freact%2Fsrc%2Fcondition-store%2F__tests__%2FBrowserConditionStore.test.ts%3A10-21%0AAfter%20the%20refactor%2C%20%60BrowserConditionStore%60%20no%20longer%20calls%20%60determineLocale%60%20or%20%60getDefaultLocale%60%20%E2%80%94%20only%20%60resolveSupportedLocale%60%20is%20invoked.%20Leaving%20the%20unused%20stubs%20in%20the%20mock%20is%20harmless%20but%20creates%20noise%20for%20future%20readers%20who%20may%20expect%20these%20methods%20to%20be%20exercised.%0A%0A%60%60%60suggestion%0Avi.mock%28'gt-i18n%2Finternal'%2C%20%28%29%20%3D%3E%20%28%7B%0A%20%20getI18nConfig%3A%20%28%29%20%3D%3E%20%28%7B%0A%20%20%20%20resolveSupportedLocale%3A%20%28locale%3F%3A%20string%20%7C%20string%5B%5D%29%20%3D%3E%20%7B%0A%20%20%20%20%20%20const%20resolved%20%3D%20Array.isArray%28locale%29%20%3F%20locale%5B0%5D%20%3A%20locale%3B%0A%20%20%20%20%20%20return%20resolved%20%7C%7C%20'en'%3B%0A%20%20%20%20%7D%2C%0A%20%20%7D%29%2C%0A%7D%29%29%3B%0A%60%60%60%0A%0A&repo=generaltranslation%2Fgt&pr=1750&platform=github"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://greptile-static-assets.s3.amazonaws.com/badges/FixAllInClaudeDark.svg?v=4"><source
media="(prefers-color-scheme: light)"
srcset="https://greptile-static-assets.s3.amazonaws.com/badges/FixAllInClaude.svg?v=4"><img
alt="Fix All in Claude Code"
src="https://greptile-static-assets.s3.amazonaws.com/badges/FixAllInClaude.svg?v=4"></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-resolve-supported-locale%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-resolve-supported-locale%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%0Apackages%2Freact%2Fsrc%2Fcondition-store%2F__tests__%2FBrowserConditionStore.test.ts%3A10-21%0AAfter%20the%20refactor%2C%20%60BrowserConditionStore%60%20no%20longer%20calls%20%60determineLocale%60%20or%20%60getDefaultLocale%60%20%E2%80%94%20only%20%60resolveSupportedLocale%60%20is%20invoked.%20Leaving%20the%20unused%20stubs%20in%20the%20mock%20is%20harmless%20but%20creates%20noise%20for%20future%20readers%20who%20may%20expect%20these%20methods%20to%20be%20exercised.%0A%0A%60%60%60suggestion%0Avi.mock%28'gt-i18n%2Finternal'%2C%20%28%29%20%3D%3E%20%28%7B%0A%20%20getI18nConfig%3A%20%28%29%20%3D%3E%20%28%7B%0A%20%20%20%20resolveSupportedLocale%3A%20%28locale%3F%3A%20string%20%7C%20string%5B%5D%29%20%3D%3E%20%7B%0A%20%20%20%20%20%20const%20resolved%20%3D%20Array.isArray%28locale%29%20%3F%20locale%5B0%5D%20%3A%20locale%3B%0A%20%20%20%20%20%20return%20resolved%20%7C%7C%20'en'%3B%0A%20%20%20%20%7D%2C%0A%20%20%7D%29%2C%0A%7D%29%29%3B%0A%60%60%60%0A%0A&repo=generaltranslation%2Fgt&pr=1750&platform=github"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://greptile-static-assets.s3.amazonaws.com/badges/FixAllInCodexDark.svg?v=4"><source
media="(prefers-color-scheme: light)"
srcset="https://greptile-static-assets.s3.amazonaws.com/badges/FixAllInCodex.svg?v=4"><img
alt="Fix All in Codex"
src="https://greptile-static-assets.s3.amazonaws.com/badges/FixAllInCodex.svg?v=4"></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
packages/react/src/condition-store/__tests__/BrowserConditionStore.test.ts:10-21
After the refactor, `BrowserConditionStore` no longer calls `determineLocale` or `getDefaultLocale` — only `resolveSupportedLocale` is invoked. Leaving the unused stubs in the mock is harmless but creates noise for future readers who may expect these methods to be exercised.

```suggestion
vi.mock('gt-i18n/internal', () => ({
  getI18nConfig: () => ({
    resolveSupportedLocale: (locale?: string | string[]) => {
      const resolved = Array.isArray(locale) ? locale[0] : locale;
      return resolved || 'en';
    },
  }),
}));
```


`````

</details>

<sub>Reviews (1): Last reviewed commit: ["refactor: use supported locale
resolver"](602339b)
| [Re-trigger
Greptile](https://app.greptile.com/api/retrigger?id=41016695)</sub>

<!-- /greptile_comment -->
## Summary
- replace React Native server-render initializeGT implementation with
the shared react-core implementation
- preserve the exported InitializeGTParams type

## Verification
- pnpm --filter gt-react-native typecheck

<!-- greptile_comment -->

<h3>Greptile Summary</h3>

This PR removes the duplicated `initializeGT` implementation in the
React Native package and replaces it with a re-export of
`internalInitializeGTSRA` from `@generaltranslation/react-core/pure`,
which is already exported from that module under both names. The public
`InitializeGTParams` type and the `initializeGT` function name are fully
preserved.

- The two implementations were byte-for-byte identical
(`initializeI18nConfig(config, 'server-render')` + `new
ReactI18nCache(config)` + `setReactI18nCache`), so the change is a pure
deduplication with no behavioral difference.
- `InitializeGTParams` continues to be exported as `I18nConfigParams &
ReactI18nCacheParams`, matching the parameter type of
`internalInitializeGTSRA`, so the public API is unchanged.

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

Safe to merge — the change is a pure deduplication with no behavioral
difference and the public API is fully preserved.

The deleted inline implementation and the re-exported
internalInitializeGTSRA perform exactly the same two steps in the same
order, and internalInitializeGTSRA is already a stable export of
react-core/pure. The InitializeGTParams type and the initializeGT name
are both preserved, so no downstream consumer needs to change.

No files require special attention.
</details>

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

| Filename | Overview |
|----------|----------|
| packages/react-native/src/setup/initializeGT.ts | Replaces the inline
implementation of initializeGT with a direct re-export of
internalInitializeGTSRA from react-core/pure; preserves the exported
InitializeGTParams type; the two implementations are byte-for-byte
identical so no behavior changes. |

</details>

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

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

```mermaid
%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
    participant Caller as React Native caller
    participant File as initializeGT.ts (react-native)
    participant Core as react-core/pure (internalInitializeGTSRA)
    participant I18nConfig as initializeI18nConfig
    participant Cache as ReactI18nCache / setReactI18nCache

    Caller->>File: initializeGT(config)
    Note over File: re-export → delegates immediately
    File->>Core: internalInitializeGTSRA(config)
    Core->>I18nConfig: initializeI18nConfig(config, 'server-render')
    Core->>Cache: new ReactI18nCache(config)
    Core->>Cache: setReactI18nCache(i18nCache)
```

</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 Caller as React Native caller
    participant File as initializeGT.ts (react-native)
    participant Core as react-core/pure (internalInitializeGTSRA)
    participant I18nConfig as initializeI18nConfig
    participant Cache as ReactI18nCache / setReactI18nCache

    Caller->>File: initializeGT(config)
    Note over File: re-export → delegates immediately
    File->>Core: internalInitializeGTSRA(config)
    Core->>I18nConfig: initializeI18nConfig(config, 'server-render')
    Core->>Cache: new ReactI18nCache(config)
    Core->>Cache: setReactI18nCache(i18nCache)
```

</a>
</details>

<sub>Reviews (1): Last reviewed commit: ["refactor: reuse react core
initializeGT"](4027d1c)
| [Re-trigger
Greptile](https://app.greptile.com/api/retrigger?id=41016618)</sub>

<!-- /greptile_comment -->
## Summary
- consolidate duplicated TanStack Start entrypoint exports through a
shared barrel
- keep the client entrypoint as the only source file with the use-client
directive
- remove the redundant react-server export condition that already
matched the server fallback
- add a patch changeset for gt-tanstack-start

## Checks
- pnpm --filter gt-tanstack-start typecheck
- pnpm --filter gt-tanstack-start build
- pnpm --filter gt-tanstack-start test
- pnpm format
- git diff --check

<!-- greptile_comment -->

<h3>Greptile Summary</h3>

This PR consolidates the three `gt-tanstack-start` source entrypoints
(`index.client.ts`, `index.server.ts`, `index.types.ts`) into a single
`index.ts` barrel, relying on `gt-react`'s existing `browser` and
`react-server` export conditions to handle the client/server boundary
rather than duplicating them at the `gt-tanstack-start` level.

- **Barrel consolidation**: `index.server.ts` is renamed to `index.ts`
(content unchanged); the `index.client.ts` (which had the `'use client'`
directive) and `index.types.ts` (type-only duplicate) are deleted.
- **Package.json simplification**: The `react-server` and `browser`
export conditions are removed from `gt-tanstack-start`'s exports — both
were effectively redundant because `gt-react` (which is never bundled
into this package) already declares those conditions on its own built
artifacts, so bundlers pick up `gt-react`'s `'use client'` markers
transitively.
- **Size-limit update**: The two separate client and server size-limit
entries are collapsed into a single `tanstack('gt-tanstack-start',
'index')` check at the same 52 kB ceiling.

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

Safe to merge — this is a pure structural refactor with no logic or API
changes.

The deleted files (index.client.ts, index.types.ts) were exact
duplicates of what the surviving index.ts (renamed from index.server.ts)
already exports, just with different directives. The removed browser and
react-server export conditions on gt-tanstack-start were redundant
because gt-react is never bundled into this package and already declares
those conditions on its own built artifacts — Vite/Vinxi apply export
conditions build-wide, so gt-react's 'use client' markers are still
visible to the RSC transform. No runtime behavior changes.

No files require special attention.
</details>

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

| Filename | Overview |
|----------|----------|
| packages/tanstack-start/src/index.ts | Renamed from index.server.ts
with no content changes; now the sole barrel re-exporting gt-react
symbols |
| packages/tanstack-start/package.json | Removes react-server and
browser export conditions (both were redundant given gt-react's own
conditions); consolidates main/module/types to index.* |
| packages/tanstack-start/tsdown.config.mts | Reduces entry list from
three files to a single src/index.ts; no logic changes |
| .size-limit.cjs | Removes the node-platform tanstackNode helper and
replaces two separate client/server size entries with a single
tanstack('gt-tanstack-start', 'index') entry at the same 52 kB limit |
| .changeset/tanstack-start-shared-barrel.md | Patch changeset
documenting the entrypoint consolidation for gt-tanstack-start |

</details>

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

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

```mermaid
%%{init: {'theme': 'neutral'}}%%
flowchart TD
    subgraph Before
        A1["gt-tanstack-start\n(browser condition)"] --> B1["index.client.mjs\n('use client')"]
        A2["gt-tanstack-start\n(react-server condition)"] --> B2["index.server.mjs"]
        A3["gt-tanstack-start\n(default condition)"] --> B3["index.server.mjs"]
        B1 --> C1["gt-react (browser)\nindex.client.mjs"]
        B2 --> C2["gt-react (react-server)\nindex.rsc.mjs"]
        B3 --> C3["gt-react (default)\nindex.server.mjs"]
    end
    subgraph After
        D1["gt-tanstack-start\n(any condition)"] --> E1["index.mjs\n(no 'use client')"]
        E1 --> F1["gt-react resolves via\nits own conditions"]
        F1 --> G1["browser → index.client.mjs\n('use client')"]
        F1 --> G2["react-server → index.rsc.mjs"]
        F1 --> G3["default → index.server.mjs\n('use client')"]
    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
        A1["gt-tanstack-start\n(browser condition)"] --> B1["index.client.mjs\n('use client')"]
        A2["gt-tanstack-start\n(react-server condition)"] --> B2["index.server.mjs"]
        A3["gt-tanstack-start\n(default condition)"] --> B3["index.server.mjs"]
        B1 --> C1["gt-react (browser)\nindex.client.mjs"]
        B2 --> C2["gt-react (react-server)\nindex.rsc.mjs"]
        B3 --> C3["gt-react (default)\nindex.server.mjs"]
    end
    subgraph After
        D1["gt-tanstack-start\n(any condition)"] --> E1["index.mjs\n(no 'use client')"]
        E1 --> F1["gt-react resolves via\nits own conditions"]
        F1 --> G1["browser → index.client.mjs\n('use client')"]
        F1 --> G2["react-server → index.rsc.mjs"]
        F1 --> G3["default → index.server.mjs\n('use client')"]
    end
```

</a>
</details>

<sub>Reviews (3): Last reviewed commit: ["fix: remove tanstack start
types
subpath"](8ccbd3c)
| [Re-trigger
Greptile](https://app.greptile.com/api/retrigger?id=40968210)</sub>

<!-- /greptile_comment -->
## Summary
- merge `main` into `odysseus` and adapt the locale validation feature
to the current `I18nConfig` singleton flow
- fall back to the default locale with a warning when `gt-next` receives
an invalid request locale
- expose `isLocaleSupported()` from the root `gt-next` entrypoint
variants instead of `gt-next/server`

## Tests
- `pnpm exec oxfmt --check packages/cli/CHANGELOG.md
packages/cli/src/api/__tests__/downloadFileBatch.test.ts
packages/cli/src/api/downloadFileBatch.ts
packages/cli/src/formats/files/__tests__/fileMapping.test.ts
packages/cli/src/formats/files/fileMapping.ts
packages/gtx-cli/CHANGELOG.md packages/locadex/CHANGELOG.md
packages/next/src/condition-store/AsyncConditionStore.ts
packages/next/src/errors/createErrors.ts
packages/next/src/index.client.ts packages/next/src/index.rsc.ts
packages/next/src/index.server.ts packages/next/src/index.types.ts
packages/next/src/request/__tests__/localeValidation.test.ts
packages/next/src/request/localeValidation.ts
packages/next/src/request/registerLocale.ts`
- `pnpm exec oxlint
packages/cli/src/api/__tests__/downloadFileBatch.test.ts
packages/cli/src/api/downloadFileBatch.ts
packages/cli/src/formats/files/__tests__/fileMapping.test.ts
packages/cli/src/formats/files/fileMapping.ts
packages/next/src/condition-store/AsyncConditionStore.ts
packages/next/src/errors/createErrors.ts
packages/next/src/index.client.ts packages/next/src/index.rsc.ts
packages/next/src/index.server.ts packages/next/src/index.types.ts
packages/next/src/request/__tests__/localeValidation.test.ts
packages/next/src/request/localeValidation.ts
packages/next/src/request/registerLocale.ts`
- `pnpm --dir packages/next exec vitest run
src/request/__tests__/localeValidation.test.ts
src/__tests__/packageExports.test.ts`
- `pnpm --filter gt-next typecheck`
- `pnpm --dir packages/cli exec vitest run
src/api/__tests__/downloadFileBatch.test.ts
src/formats/files/__tests__/fileMapping.test.ts
--config=./vitest.config.ts`
- `pnpm --dir packages/core exec vitest run
src/translate/utils/__tests__/apiRequest.test.ts
--config=./vitest.config.ts`
- `pnpm --filter gt-next test:js`

<!-- greptile_comment -->

<h3>Greptile Summary</h3>

This PR merges the locale-validation feature from `main` into the
`odysseus` branch, adapting it to the `I18nConfig` singleton API, and
also fixes a long-standing lockfile bug where absolute output paths were
persisted instead of relative ones.

- **Locale validation**: Introduces `resolveLocaleOrDefault` and
`isLocaleSupported` in a new `localeValidation.ts` module; invalid or
unsupported request locales now fall back to the default locale with a
`console.warn` diagnostic instead of silently using whatever was passed.
- **API surface**: `isLocaleSupported` is promoted from `gt-next/server`
to all three root entrypoint variants (`index.client.ts`,
`index.rsc.ts`, `index.server.ts`) with a matching stub in
`index.types.ts`.
- **Lockfile path fix**: `downloadFileBatch` and `fileMapping` now store
`getRelative(outputPath)` rather than the absolute path, fixing
cross-machine portability of `gt-lock.json`.

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

Safe to merge — all changed paths have direct test coverage and no
regressions were identified.

The locale-validation logic is straightforward, well-tested (happy path,
fallback, case-normalisation, `registerLocale` integration), and the
`resolveLocaleOrDefault` fallback is always safe (it always returns a
string). The lockfile path fix is a contained one-liner with a
confirming test. No pre-existing behaviours are removed, only guarded.

No files require special attention.
</details>

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

| Filename | Overview |
|----------|----------|
| packages/next/src/request/localeValidation.ts | New file implementing
locale validation with `resolveLocaleOrDefault` (falls back with a
warning) and `isLocaleSupported` (boolean predicate). Logic is correct
and tested. |
| packages/next/src/condition-store/AsyncConditionStore.ts | Threads
`resolveLocaleOrDefault` through `getLocale()` for the non-registered
path; replaces deprecated `getGTClass().determineLocale()` with
`determineSupportedLocale()`. Changes are correct. |
| packages/next/src/request/registerLocale.ts | Simplified to delegate
validation to `resolveLocaleOrDefault`, removing the direct dependency
on `getGTClass`. Behaviour is correct and tested. |
| packages/next/src/request/__tests__/localeValidation.test.ts |
Comprehensive new test suite covering happy paths, fallback behaviour,
case-normalisation, and `registerLocale` integration. |
| packages/next/src/index.client.ts | Adds `isLocaleSupported` export.
Mirrors identical changes in `index.rsc.ts` and `index.server.ts`. |
| packages/next/src/index.types.ts | Adds placeholder
`isLocaleSupported` declaration that throws the standard types-file
error, consistent with all other stubs in this file. |
| packages/cli/src/api/downloadFileBatch.ts | Single-line fix: stores
`getRelative(outputPath)` instead of the absolute `outputPath` in the
lockfile, fixing cross-machine portability. |
| packages/cli/src/formats/files/fileMapping.ts | Applies the same
relative-path fix for GTJSON template file mappings, consistent with the
`downloadFileBatch` change. |
| packages/next/src/errors/createErrors.ts | Adds
`createInvalidRequestLocaleWarning` diagnostic factory, cleanly
following the existing error-creation pattern. |

</details>

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

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

```mermaid
%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A["Incoming locale\n(request param / header / cookie)"] --> B{{"resolveLocaleOrDefault(locale)"}}
    B --> C{"typeof locale === 'string'\n&& length > 0?"}
    C -- No --> G["return resolveAliasLocale(defaultLocale)"]
    C -- Yes --> D{{"i18nConfig.determineSupportedLocale(locale)"}}
    D -- found --> E["return resolveAliasLocale(supportedLocale)"]
    D -- undefined --> F["console.warn(createInvalidRequestLocaleWarning)"]
    F --> G

    subgraph Callers
        R["registerLocale(locale)"] --> B
        AC["AsyncConditionStore.getLocale()\n(no registered locale)"] --> B
    end

    subgraph isLocaleSupported
        IS["isLocaleSupported(locale)"] --> DS{{"determineSupportedLocale(locale)"}}
        DS -- found --> T["return true"]
        DS -- undefined --> F2["return false"]
    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
    A["Incoming locale\n(request param / header / cookie)"] --> B{{"resolveLocaleOrDefault(locale)"}}
    B --> C{"typeof locale === 'string'\n&& length > 0?"}
    C -- No --> G["return resolveAliasLocale(defaultLocale)"]
    C -- Yes --> D{{"i18nConfig.determineSupportedLocale(locale)"}}
    D -- found --> E["return resolveAliasLocale(supportedLocale)"]
    D -- undefined --> F["console.warn(createInvalidRequestLocaleWarning)"]
    F --> G

    subgraph Callers
        R["registerLocale(locale)"] --> B
        AC["AsyncConditionStore.getLocale()\n(no registered locale)"] --> B
    end

    subgraph isLocaleSupported
        IS["isLocaleSupported(locale)"] --> DS{{"determineSupportedLocale(locale)"}}
        DS -- found --> T["return true"]
        DS -- undefined --> F2["return false"]
    end
```

</a>
</details>

<sub>Reviews (2): Last reviewed commit: ["fix: return boolean from
locale support
..."](963f2f9)
| [Re-trigger
Greptile](https://app.greptile.com/api/retrigger?id=41163876)</sub>

<!-- /greptile_comment -->

---------

Co-authored-by: Ben Gubler <me@bengubler.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: fernando-aviles <fernando@generaltranslation.com>
Co-authored-by: Brian Lou <69982825+brian-lou@users.noreply.github.com>
## Summary
- extract duplicated useSyncExternalStore subscription filtering for
tracked translation and dictionary lookups
- leave missing-translation queue behavior unchanged

## Verification
- pnpm --filter @generaltranslation/react-core typecheck

<!-- greptile_comment -->

<h3>Greptile Summary</h3>

This PR extracts the duplicated `useSyncExternalStore` subscription
logic across three tracked-lookup resolver hooks into a single shared
hook, `useSubscribeToTrackedLookups`. The three callers now pass their
store subscribe method and key function as arguments instead of each
embedding identical inline helpers.

- **New shared hook** (`useSubscribeToTrackedLookups.ts`): generic over
the lookup type `L`, accepts the subscribe method and key extractor as
parameters, and owns the `versionRef` / `subscribe` / `getSnapshot`
wiring that was previously copy-pasted in each file.
- **Three callers updated** (`useTrackedTranslationResolver`,
`useTrackedDictionaryResolver`, `useTrackedDictionaryObjResolver`):
their local `useSubscribeToLookups` functions are deleted and replaced
with a call to the shared hook; imports are trimmed accordingly.

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

Pure deduplication refactor — no behavioral change, only the shared
subscription logic is extracted into a single hook.

The store subscribe methods are arrow function class fields, making them
stable references per store instance. getDictionaryListenerKey and
getTranslateListenerKey are module-level functions and also stable. The
versionRef is scoped inside the shared hook so each call site gets its
own counter, preserving independent invalidation. No logic was altered —
only location and structure changed.

No files require special attention. The new
useSubscribeToTrackedLookups.ts is the only net-new code and it is a
straightforward extraction.
</details>

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

| Filename | Overview |
|----------|----------|
|
packages/react-core/src/hooks/external-store/useSubscribeToTrackedLookups.ts
| New shared hook extracting the tracked-lookup subscription pattern;
generic, correctly wired with stable deps, and own versionRef per call
site. |
|
packages/react-core/src/hooks/external-store/useTrackedTranslationResolver.ts
| Local useSubscribeToLookups helper removed; now delegates to the
shared hook with i18nStore.subscribeToTranslationEvents (stable
arrow-field) and module-level getTranslateListenerKey. |
|
packages/react-core/src/hooks/external-store/useTrackedDictionaryResolver.ts
| Duplicate useSubscribeToLookups removed; delegates to shared hook with
subscribeToDictionaryEntryEvents and getDictionaryListenerKey. |
|
packages/react-core/src/hooks/external-store/useTrackedDictionaryObjResolver.ts
| Duplicate useSubscribeToLookups removed; delegates to shared hook with
subscribeToDictionaryObjectEvents and getDictionaryListenerKey. |

</details>

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

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

```mermaid
%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
    participant Caller as Resolver Hook
    participant Shared as useSubscribeToTrackedLookups
    participant Store as I18nStore
    participant React as useSyncExternalStore

    Caller->>Caller: init trackedKeysRef (Set)
    Caller->>Shared: (trackedKeysRef, subscribeToEvents, getListenerKey)
    Shared->>Shared: create versionRef
    Shared->>React: useSyncExternalStore(subscribe, getSnapshot)
    React->>Store: subscribe(listener)
    Store-->>React: Unsubscribe fn

    note over Store,Shared: On store event
    Store->>Shared: onEvent(lookup)
    Shared->>Shared: getListenerKey(lookup) in trackedKeysRef?
    alt key tracked
        Shared->>Shared: versionRef++
        Shared->>React: listener() re-render
    else key not tracked
        Shared-->>Store: skip
    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"}}}%%
sequenceDiagram
    participant Caller as Resolver Hook
    participant Shared as useSubscribeToTrackedLookups
    participant Store as I18nStore
    participant React as useSyncExternalStore

    Caller->>Caller: init trackedKeysRef (Set)
    Caller->>Shared: (trackedKeysRef, subscribeToEvents, getListenerKey)
    Shared->>Shared: create versionRef
    Shared->>React: useSyncExternalStore(subscribe, getSnapshot)
    React->>Store: subscribe(listener)
    Store-->>React: Unsubscribe fn

    note over Store,Shared: On store event
    Store->>Shared: onEvent(lookup)
    Shared->>Shared: getListenerKey(lookup) in trackedKeysRef?
    alt key tracked
        Shared->>Shared: versionRef++
        Shared->>React: listener() re-render
    else key not tracked
        Shared-->>Store: skip
    end
```

</a>
</details>

<sub>Reviews (1): Last reviewed commit: ["refactor: share tracked lookup
subscript..."](69f8f08)
| [Re-trigger
Greptile](https://app.greptile.com/api/retrigger?id=41016405)</sub>

<!-- /greptile_comment -->
## Summary
- Update the global singleton warning to state that the singleton was
already initialized.
- Update singleton warning tests to match the preserved-instance
behavior.

## Tests
- pnpm build
- pnpm --filter gt-i18n test
- pnpm --filter @generaltranslation/react-core test
- pnpm format
- pnpm lint

<!-- greptile_comment -->

<h3>Greptile Summary</h3>

This PR fixes a misleading warning message in the global singleton
factory: the old message said "Overwriting global X singleton instance"
but the code actually **preserves** the existing instance and returns
early without overwriting. The new message "Global X singleton instance
was already initialized" accurately reflects the preserved-instance
behavior.

- `createGlobalSingleton.ts`: Changes the `whatHappened` string in the
`set` guard from "Overwriting…" to "was already initialized" to match
the actual no-op behavior when a duplicate `set` is detected.
- Five test files updated to assert the corrected warning text across
`i18nConfig`, `i18nCache`, `i18nStore`, `conditionStore`, and the base
singleton factory.

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

Safe to merge — the change is a single-line warning message correction
with no behavioral impact; all affected tests were updated consistently.

The only production code change is the warning string in
`createGlobalSingleton.ts`; the singleton's preserved-instance logic
(`return` without overwriting) is unchanged. All five downstream test
files were updated in lockstep to match the new message, and the tests
continue to assert that the original value is retained after a duplicate
`set`.

No files require special attention. The one minor follow-up is the stale
"overwrite warning" phrasing left in the JSDoc comment of
`createGlobalSingleton.ts`.
</details>

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

| Filename | Overview |
|----------|----------|
| packages/i18n/src/globals/createGlobalSingleton.ts | Warning message
corrected from "Overwriting" to "was already initialized" to accurately
reflect the preserved-instance behavior; a minor JSDoc comment still
uses the old "overwrite warning" phrasing. |
| packages/i18n/src/globals/__tests__/createGlobalSingleton.test.ts |
Test assertion updated to match the new warning text; also verifies the
original value is preserved after a duplicate set. |
| packages/i18n/src/i18n-config/__tests__/singleton-operations.test.ts |
Warning assertion updated to match new message text; no other logic
changes. |
| packages/i18n/src/i18n-cache/__tests__/singleton-operations.test.ts |
Warning assertion updated to match new message text; no other logic
changes. |
|
packages/i18n/src/condition-store/__tests__/createConditionStoreSingleton.test.ts
| Warning assertion updated to match new message text; no other logic
changes. |
|
packages/react-core/src/i18n-store/__tests__/singleton-operations.test.ts
| Warning assertion updated to match new message text; no other logic
changes. |

</details>

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

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

```mermaid
%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A["singleton.set(next)"] --> B{Is slot already\noccupied AND\nnext !== existing?}
    B -- Yes --> C["console.warn\n'Global X singleton instance\nwas already initialized'"]
    C --> D["return — existing\ninstance preserved"]
    B -- No --> E["ns[key] = next\nsingleton stored"]
```

</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["singleton.set(next)"] --> B{Is slot already\noccupied AND\nnext !== existing?}
    B -- Yes --> C["console.warn\n'Global X singleton instance\nwas already initialized'"]
    C --> D["return — existing\ninstance preserved"]
    B -- No --> E["ns[key] = next\nsingleton stored"]
```

</a>
</details>

<sub>Reviews (2): Last reviewed commit: ["fix: correct global singleton
warning"](0464de0)
| [Re-trigger
Greptile](https://app.greptile.com/api/retrigger?id=41183771)</sub>

<!-- /greptile_comment -->
## Summary
- Initialize gt-next server entrypoints through a shared server
initializer.
- Move request locale registration into `AsyncConditionStore` so
`/server` and package root entrypoints use the same global
condition-store singleton instead of separate module-level async stores.
- Keep request locale validation from latest `odysseus` while routing
registered locales through the shared condition store.
- Add explicit module isolation in the entrypoint test so server import
side effects do not depend on Vitest module cache order.

## Testing
- `pnpm --filter gt-next exec vitest run
src/__tests__/rscComponentWrappers.test.tsx
src/request/__tests__/localeValidation.test.ts`
- `pnpm --filter gt-next build`
- `pnpm --filter gt-next test:js`
- `pnpm lint`
- `pnpm build:next-pages-router` in
`~/Documents/dev/gt-testing-apps-wt/e-odysseus-server-entrypoint-initializer`

## Notes
- Merged latest `origin/odysseus`.
- Added a patch changeset for `gt-next`.
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@11.0.0-odysseus.12

### Patch Changes

- 31884d2: Initialize gt-next server entrypoints with a shared server
condition store.
- Updated dependencies [7be23bc]
  - @generaltranslation/react-core@11.0.0-odysseus.12
  - gt-react@11.0.0-odysseus.12
## @generaltranslation/gt-next-lint@15.0.0-odysseus.12

### Patch Changes

- Updated dependencies [31884d2]
  - gt-next@11.0.0-odysseus.12
## gt-react@11.0.0-odysseus.12

### Patch Changes

- Updated dependencies [7be23bc]
  - @generaltranslation/react-core@11.0.0-odysseus.12
## @generaltranslation/react-core@11.0.0-odysseus.12

### Patch Changes

- 7be23bc: Share tracked lookup subscription handling across resolver
hooks.
## gt-react-native@11.0.0-odysseus.12

### Patch Changes

- Updated dependencies [7be23bc]
  - @generaltranslation/react-core@11.0.0-odysseus.12
## gt-tanstack-start@11.0.0-odysseus.12

### Patch Changes

- cde28be: Consolidate TanStack Start package entrypoint exports.
- Updated dependencies [7be23bc]
  - @generaltranslation/react-core@11.0.0-odysseus.12
  - gt-react@11.0.0-odysseus.12

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
## Summary
- Read VITE_GT_PROJECT_ID for Vite React initializers when credentials
are not explicitly supplied.
- Read VITE_GT_DEV_API_KEY only in development and keep Vite env access
static so production replacement behaves correctly.
- Avoid whole-object import.meta.env reads in runtime environment
detection so production client bundles do not materialize dev-only VITE
values.

## Tests
- pnpm --filter gt-i18n test -- getRuntimeEnvironment
- pnpm --filter gt-react test --
src/setup/__tests__/runtimeCredentials.test.ts
- pnpm --filter gt-i18n typecheck
- pnpm --filter gt-react typecheck
- pnpm --filter gt-react... build
- VITE_GT_PROJECT_ID=gt_project_probe_static_access
VITE_GT_DEV_API_KEY=gt_dev_secret_should_not_ship pnpm build:vite-react
- rg confirmed the production Vite bundle includes the project ID
sentinel and not the dev API key sentinel

## Changeset
- Patch changeset added for gt-react and gt-i18n.

<!-- greptile_comment -->

<h3>Greptile Summary</h3>

This PR fixes credential reading for Vite-based React initializers
(`initializeGTSPA`, `initializeGTSRA`, `initializeGTSRAClient`) and
refactors the server entry point to properly separate SPA and SRA
initialization paths.

- **Vite credential injection**: `runtimeCredentials.ts` reads
`VITE_GT_PROJECT_ID` in all environments; `VITE_GT_DEV_API_KEY` is
guarded by both a runtime `getRuntimeEnvironment()` check and an inner
`import.meta.env.DEV` ternary so the value is dead code in production
Vite bundles and does not appear in the output.
- **Granular `import.meta.env` access**: `getRuntimeEnvironment.ts` now
reads `.env?.MODE` and `.env?.DEV` as individual properties rather than
materialising the whole `import.meta.env` object, preventing bundlers
from pulling all env values into the production bundle.
- **Server entry point guard**: `index.server.ts` now exports a stub
`initializeGTSPA` that throws a diagnostic error, replacing the
previously live re-export, and routes server-rendered callers to
`initializeGTSRA`/`initializeGT` instead.

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

Safe to merge — the changes are narrowly scoped to credential injection
and entry-point routing with no impact on translation or rendering
logic.

The credential resolution strategy is correctly implemented: projectId
flows through in all builds, devApiKey is excluded from production Vite
bundles via the static DEV guard, and all three initializer paths (SPA,
SRA, SRAClient) consistently apply the same logic. The server-entry stub
throws a clear diagnostic rather than silently misbehaving. Tests cover
the three key scenarios and the integration test validates the stub
behaviour against the built package.

No files require special attention.
</details>

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

| Filename | Overview |
|----------|----------|
| packages/react/src/setup/runtimeCredentials.ts | New file implementing
Vite credential resolution; correctly guards devApiKey behind both a
runtime environment check and an import.meta.env.DEV guard to prevent
the value from shipping in production bundles. |
| packages/i18n/src/utils/getRuntimeEnvironment.ts | Switches from
reading import.meta.env as a whole object to per-property access via
readImportMetaEnv helpers, preventing bundlers from materialising all
env properties in production output. |
| packages/react/src/index.server.ts | Replaces the re-exported
initializeGTSPA with a stub that throws a diagnostic error when called
from the server entry point, and exports the new initializeGTSRA as
initializeGT. |
| packages/react/src/setup/initializeGTSRA.ts | New thin wrapper around
internalInitializeGTSRA that injects runtime credentials before
delegating, consistent with the SPA and SRAClient paths. |
| packages/react/src/setup/initializeGTSRAClient.ts | Wraps the config
spread (cacheExpiryTime: null, ...config) with addRuntimeCredentials
before passing to internalInitializeGTSRA; spread order is preserved
correctly. |
| packages/react/src/setup/__tests__/runtimeCredentials.test.ts | New
tests covering development credential injection, explicit credential
precedence, and production mode exclusion of devApiKey. |
| packages/react/src/__tests__/react-package.test.ts | Adds an
integration test verifying that calling initializeGTSPA from the server
entry point throws the expected diagnostic error. |

</details>

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

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

```mermaid
%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[initializeGTSPA / initializeGTSRA / initializeGTSRAClient] --> B[addRuntimeCredentials]
    B --> C{config.projectId set?}
    C -- yes --> D[keep explicit projectId]
    C -- no --> E[readImportMetaVite\nimport.meta.env.VITE_GT_PROJECT_ID]
    E -- value --> D
    E -- undefined --> F[readProcessEnvViteProjectId\nprocess.env.VITE_GT_PROJECT_ID]
    F --> D
    B --> G{getRuntimeEnvironment\n=== development?}
    G -- no --> H[devApiKey = undefined]
    G -- yes --> I{import.meta.env.DEV?\nstatic: false in prod}
    I -- false/undefined --> J[readProcessEnvViteDevApiKey\nprocess.env.VITE_GT_DEV_API_KEY]
    I -- true --> K[readImportMetaVite\nimport.meta.env.VITE_GT_DEV_API_KEY]
    K -- value --> L[devApiKey resolved]
    K -- undefined --> J
    J --> L
    D --> M[merged config → internalInitialize*]
    L --> M
    H --> M
```

</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[initializeGTSPA / initializeGTSRA / initializeGTSRAClient] --> B[addRuntimeCredentials]
    B --> C{config.projectId set?}
    C -- yes --> D[keep explicit projectId]
    C -- no --> E[readImportMetaVite\nimport.meta.env.VITE_GT_PROJECT_ID]
    E -- value --> D
    E -- undefined --> F[readProcessEnvViteProjectId\nprocess.env.VITE_GT_PROJECT_ID]
    F --> D
    B --> G{getRuntimeEnvironment\n=== development?}
    G -- no --> H[devApiKey = undefined]
    G -- yes --> I{import.meta.env.DEV?\nstatic: false in prod}
    I -- false/undefined --> J[readProcessEnvViteDevApiKey\nprocess.env.VITE_GT_DEV_API_KEY]
    I -- true --> K[readImportMetaVite\nimport.meta.env.VITE_GT_DEV_API_KEY]
    K -- value --> L[devApiKey resolved]
    K -- undefined --> J
    J --> L
    D --> M[merged config → internalInitialize*]
    L --> M
    H --> M
```

</a>
</details>

<sub>Reviews (3): Last reviewed commit: ["fix: read vite runtime
credentials"](2b0845f)
| [Re-trigger
Greptile](https://app.greptile.com/api/retrigger?id=41159195)</sub>

<!-- /greptile_comment -->

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Performance Alert ⚠️

Possible performance regression was detected for benchmark 'Middleware Benchmarks'.
Benchmark result of this commit is worse than the previous benchmark result exceeding threshold 1.50.

Benchmark suite Current: 1fc3256 Previous: c1ad1f7 Ratio
gt-next > e2e > middleware: redirect-chain-fr-about > elapsed 669 ms 128 ms 5.23
gt-next > e2e > middleware: redirect-chain-fr-about > ttfb 575.5999999999767 ms 17.29999999998836 ms 33.27
gt-next > e2e > middleware: redirect-chain-fr-about > domContentLoaded 585 ms 26.39999999999418 ms 22.16
gt-next > e2e > middleware: redirect-chain-fr-about > load 661 ms 119.29999999998836 ms 5.54

This comment was automatically generated by workflow using github-action-benchmark.

CC: @generaltranslation/core

@bgub bgub marked this pull request as ready for review July 2, 2026 17:23
@bgub bgub requested a review from a team as a code owner July 2, 2026 17:23
Comment on lines +67 to +68
const results = await translateMany([source], options, config, timeout);
return results[0];

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 The translate wrapper returns results[0] directly. If _translateMany ever produces an empty array (e.g., a malformed API response), callers receive undefined at runtime despite the declared return type of TranslationResult | TranslationError. A guard here prevents a silent type violation from propagating.

Suggested change
const results = await translateMany([source], options, config, timeout);
return results[0];
const results = await translateMany([source], options, config, timeout);
if (!results[0]) {
throw new Error('translate: no result returned for the provided source');
}
return results[0];
Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/core/src/runtime.ts
Line: 67-68

Comment:
The `translate` wrapper returns `results[0]` directly. If `_translateMany` ever produces an empty array (e.g., a malformed API response), callers receive `undefined` at runtime despite the declared return type of `TranslationResult | TranslationError`. A guard here prevents a silent type violation from propagating.

```suggestion
  const results = await translateMany([source], options, config, timeout);
  if (!results[0]) {
    throw new Error('translate: no result returned for the provided source');
  }
  return results[0];
```

How can I resolve this? If you propose a fix, please make it concise.

Fix in Claude Code Fix in Codex

Comment on lines +160 to +179
async translateMany(
sources: TranslateManyEntry[] | Record<string, TranslateManyEntry>,
options: RuntimeTranslateOptions,
timeout?: number
): Promise<TranslateManyResult | Record<string, TranslationResult>> {
if (Array.isArray(sources)) {
return await runtimeTranslateMany(
sources,
options,
this.getRuntimeTranslateConfig(),
timeout
);
}
return await runtimeTranslateMany(
sources,
options,
this.getRuntimeTranslateConfig(),
timeout
);
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Both branches of the Array.isArray guard call runtimeTranslateMany with the same arguments — the conditional is dead code. The runtimeTranslateMany implementation already accepts TranslateManyEntry[] | Record<string, TranslateManyEntry>, so TypeScript overload resolution handles the type narrowing without any runtime branching.

Suggested change
async translateMany(
sources: TranslateManyEntry[] | Record<string, TranslateManyEntry>,
options: RuntimeTranslateOptions,
timeout?: number
): Promise<TranslateManyResult | Record<string, TranslationResult>> {
if (Array.isArray(sources)) {
return await runtimeTranslateMany(
sources,
options,
this.getRuntimeTranslateConfig(),
timeout
);
}
return await runtimeTranslateMany(
sources,
options,
this.getRuntimeTranslateConfig(),
timeout
);
}
async translateMany(
sources: TranslateManyEntry[] | Record<string, TranslateManyEntry>,
options: RuntimeTranslateOptions,
timeout?: number
): Promise<TranslateManyResult | Record<string, TranslationResult>> {
return await runtimeTranslateMany(
sources as TranslateManyEntry[],
options,
this.getRuntimeTranslateConfig(),
timeout
);
}
Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/i18n/src/i18n-config/I18nConfig.ts
Line: 160-179

Comment:
Both branches of the `Array.isArray` guard call `runtimeTranslateMany` with the same arguments — the conditional is dead code. The `runtimeTranslateMany` implementation already accepts `TranslateManyEntry[] | Record<string, TranslateManyEntry>`, so TypeScript overload resolution handles the type narrowing without any runtime branching.

```suggestion
  async translateMany(
    sources: TranslateManyEntry[] | Record<string, TranslateManyEntry>,
    options: RuntimeTranslateOptions,
    timeout?: number
  ): Promise<TranslateManyResult | Record<string, TranslationResult>> {
    return await runtimeTranslateMany(
      sources as TranslateManyEntry[],
      options,
      this.getRuntimeTranslateConfig(),
      timeout
    );
  }
```

How can I resolve this? If you propose a fix, please make it concise.

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!

Fix in Claude Code Fix in Codex

Base automatically changed from odysseus to main July 4, 2026 02:25
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