refactor(cli): dedupe locale path prefixing#1740
Conversation
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.6 ### Patch Changes - Updated dependencies [c1aa794] - Updated dependencies [e0ace5b] - @generaltranslation/react-core@11.0.0-odysseus.6 - gt-react@11.0.0-odysseus.6 ## @generaltranslation/gt-next-lint@15.0.0-odysseus.6 ### Patch Changes - gt-next@11.0.0-odysseus.6 ## gt-react@11.0.0-odysseus.6 ### Patch Changes - Updated dependencies [c1aa794] - Updated dependencies [e0ace5b] - @generaltranslation/react-core@11.0.0-odysseus.6 ## @generaltranslation/react-core@11.0.0-odysseus.6 ### Patch Changes - c1aa794: Trigger a new Odysseus prerelease after updating the release workflow. - e0ace5b: Build `@generaltranslation/react-core` as unbundled, tree-shakeable modules. Each entrypoint (`pure`, `hooks`, `components`, `components-rsc`, `cookies`) was previously emitted as a single pre-bundled, minified file, so a consumer importing one component pulled the entire entry. The package now builds unbundled (per-module) output in both ESM and CJS: entrypoints are thin re-export barrels over granular sibling modules, allowing a downstream bundler to drop unused components and hooks. The build also stops inlining `generaltranslation`/`@generaltranslation/format` into react-core's output, so they resolve to their single shared copy instead of being duplicated in react-core (they are already loaded standalone). This removes the duplication and keeps the per-module declarations referencing the dependency packages directly, so inferred types stay portable for downstream packages. When consumed via ESM (see the companion `gt-next` ESM change), tree-shaking plus de-duplication cuts react-core's client footprint substantially. Output filenames change from `*.cjs.min.cjs`/`*.esm.min.mjs` to `*.cjs`/`*.mjs` (resolved through the `exports` map, so no consumer-facing API change). The package-shape test is updated to assert the new unbundled layout. ## gt-react-native@11.0.0-odysseus.6 ### Patch Changes - Updated dependencies [c1aa794] - Updated dependencies [e0ace5b] - @generaltranslation/react-core@11.0.0-odysseus.6 ## gt-tanstack-start@11.0.0-odysseus.6 ### Patch Changes - Updated dependencies [c1aa794] - Updated dependencies [e0ace5b] - @generaltranslation/react-core@11.0.0-odysseus.6 - gt-react@11.0.0-odysseus.6 Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
## Summary
Removes the v8-era backwards-compatibility shim from
`generaltranslation`. Deletes `src/backwards-compatability/` and its 26
re-exports from `generaltranslation/internal`:
- `dataConversion.ts` — `getNew*`/`getOld*` JSX/variable/branch/GTProp
format converters
- `oldTypes.ts` — `Old*` type definitions
- `typeChecking.ts` — `isOld*`/`isNew*` guards
- `oldHashJsxChildren.ts` — `oldHashJsxChildren`/`oldHashString` (legacy
hashers)
These bridged the pre-v8 JSX/variable format, which is no longer
produced. **Verified zero consumers** across all `packages/*/src` — the
only references were `internal.ts` (the barrel) itself. ~477 LOC removed
from the `/internal` entry.
`@noble/hashes` stays a dependency (the live `id/hashSource.ts` still
uses it).
## Verification
- grep across the monorepo: none of the 26 symbols (or the module paths)
are imported anywhere outside `backwards-compatability/` +
`internal.ts`.
- `generaltranslation` builds; **432 tests pass** (3 skipped).
- Dependents build clean: `gt-i18n`, `@generaltranslation/react-core`,
`gt-react`, `gt-next`.
Part of an odysseus (next-major) dead-code/legacy-removal sweep.
<!-- greptile_comment -->
<h3>Greptile Summary</h3>
This PR removes the v8-era backwards-compatibility shim from
`generaltranslation` — 4 source files and 26 re-exports are deleted from
the `generaltranslation/internal` entry point. The PR description
confirms zero consumers in the monorepo and all dependent packages build
clean after the removal.
- Deletes `packages/core/src/backwards-compatability/` (dataConversion,
oldTypes, typeChecking, oldHashJsxChildren) and scrubs their 26
re-exports from `src/internal.ts`.
- The `patch` changeset bump is safe here because a separate
`odysseus-major-version-bumps.md` changeset already carries a `major`
bump for `generaltranslation`, and changesets tooling always uses the
highest bump across all pending changesets.
<details open><summary><h3>Confidence Score: 5/5</h3></summary>
Safe to merge — this is a pure deletion of dead code with no active
consumers anywhere in the monorepo.
All four deleted files were confirmed to have zero imports outside their
own directory and the internal barrel. The remaining exports in
internal.ts are untouched. The @noble/hashes dependency is retained for
the live hash implementation. The patch changeset bump is harmless
because a separate odysseus-major-version-bumps.md already locks in a
major bump for generaltranslation.
No files require special attention.
</details>
<details><summary><h3>Important Files Changed</h3></summary>
| Filename | Overview |
|----------|----------|
| .changeset/trim-core-backwards-compat.md | Adds a patch changeset;
safe because odysseus-major-version-bumps.md already ensures a major
bump for this package. |
| packages/core/src/backwards-compatability/dataConversion.ts | Deleted
— 14 old↔new JSX/variable format converter functions with zero remaining
consumers. |
| packages/core/src/backwards-compatability/oldHashJsxChildren.ts |
Deleted — legacy oldHashJsxChildren and oldHashString functions;
@noble/hashes stays as a live dependency via id/hashSource.ts. |
| packages/core/src/backwards-compatability/oldTypes.ts | Deleted — Old*
type definitions (OldBranchType, OldGTProp, OldJsxElement,
OldVariableObject, etc.) no longer needed. |
| packages/core/src/backwards-compatability/typeChecking.ts | Deleted —
isOld*/isNew* type guards for the old JSX format, no longer referenced
anywhere. |
| packages/core/src/internal.ts | Removed 26 backwards-compat
re-exports; remaining exports are unaffected. |
</details>
<details><summary><h3>Flowchart</h3></summary>
<a href="#gh-light-mode-only">
```mermaid
%%{init: {'theme': 'neutral'}}%%
flowchart TD
A["generaltranslation/internal"] --> B["Active Exports (kept)"]
A --> C["backwards-compatability/ (deleted)"]
B --> B1["settings, logging, locales"]
B --> B2["utils: isVariable, minify, base64, etc."]
B --> B3["derive: decodeVars, declareVar, derive, indexVars, extractVars, condenseVars"]
C --> C1["dataConversion.ts\n14 getNew*/getOld* converters"]
C --> C2["oldTypes.ts\n7 Old* type definitions"]
C --> C3["typeChecking.ts\n3 isOld*/isNew* guards"]
C --> C4["oldHashJsxChildren.ts\noldHashJsxChildren + oldHashString"]
style C fill:#ffcccc,stroke:#cc0000
style C1 fill:#ffdddd
style C2 fill:#ffdddd
style C3 fill:#ffdddd
style C4 fill:#ffdddd
style B fill:#ccffcc,stroke:#006600
```
</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/internal"] --> B["Active Exports (kept)"]
A --> C["backwards-compatability/ (deleted)"]
B --> B1["settings, logging, locales"]
B --> B2["utils: isVariable, minify, base64, etc."]
B --> B3["derive: decodeVars, declareVar, derive, indexVars, extractVars, condenseVars"]
C --> C1["dataConversion.ts\n14 getNew*/getOld* converters"]
C --> C2["oldTypes.ts\n7 Old* type definitions"]
C --> C3["typeChecking.ts\n3 isOld*/isNew* guards"]
C --> C4["oldHashJsxChildren.ts\noldHashJsxChildren + oldHashString"]
style C fill:#ffcccc,stroke:#cc0000
style C1 fill:#ffdddd
style C2 fill:#ffdddd
style C3 fill:#ffdddd
style C4 fill:#ffdddd
style B fill:#ccffcc,stroke:#006600
```
</a>
</details>
<sub>Reviews (1): Last reviewed commit: ["refactor(core): remove v8
backwards-comp..."](bacce40)
| [Re-trigger
Greptile](https://app.greptile.com/api/retrigger?id=40685817)</sub>
<!-- /greptile_comment -->
Three files with **zero importers** anywhere (grep-verified):
gt-tanstack-start `condition-store/WritableConditionStore.ts` (orphaned
local copy — the package uses gt-i18n's writable store), gt-react-native
`utils/utils.ts` (`readAuthFromEnv`), and gt-i18n
`translations-manager/utils/types/translations-manager.ts`
(`TranslationsManagerConfig` type). All three packages build clean. Part
of the odysseus dead-code sweep.
<!-- greptile_comment -->
<h3>Greptile Summary</h3>
This PR removes three orphaned source files that had zero importers
anywhere in their respective packages, as part of the odysseus dead-code
sweep. All three packages continue to build cleanly after the deletions.
-
**`packages/tanstack-start/src/condition-store/WritableConditionStore.ts`**:
A local duplicate of the writable condition store singleton already
provided by `gt-i18n/internal`; the package never wired this copy up.
- **`packages/react-native/src/utils/utils.ts`**: Contained a
`readAuthFromEnv` helper with no callers anywhere in the package.
-
**`packages/i18n/src/i18n-cache/translations-manager/utils/types/translations-manager.ts`**:
An unreferenced `TranslationsManagerConfig` type definition that was
never imported.
<details open><summary><h3>Confidence Score: 5/5</h3></summary>
All three deletions are verified dead code with zero importers in their
packages — this change carries no functional risk.
Each deleted file was grep-confirmed to have no consumers anywhere in
its package or in the wider monorepo. The tanstack-start file was a
duplicate of functionality already provided by gt-i18n/internal, the
react-native helper had no callers, and the i18n type was never
imported. A changeset is included for all three packages.
No files require special attention.
</details>
<details><summary><h3>Important Files Changed</h3></summary>
| Filename | Overview |
|----------|----------|
| .changeset/trim-orphan-files.md | New changeset documenting the
patch-level deletion of three dead files across gt-tanstack-start,
gt-react-native, and gt-i18n. |
|
packages/i18n/src/i18n-cache/translations-manager/utils/types/translations-manager.ts
| Deleted unreferenced TranslationsManagerConfig type — grep-confirmed
zero importers in the entire i18n package. |
| packages/react-native/src/utils/utils.ts | Deleted orphaned
readAuthFromEnv function — grep-confirmed zero consumers in the entire
react-native package. |
| packages/tanstack-start/src/condition-store/WritableConditionStore.ts
| Deleted orphaned local WritableConditionStore singleton — the package
already uses the equivalent export from gt-i18n/internal, so this
duplicate was dead code. |
</details>
<details><summary><h3>Flowchart</h3></summary>
<a href="#gh-light-mode-only">
```mermaid
%%{init: {'theme': 'neutral'}}%%
flowchart TD
A["Dead-code sweep"] --> B["gt-tanstack-start"]
A --> C["gt-react-native"]
A --> D["gt-i18n"]
B --> B1["condition-store/WritableConditionStore.ts\n(duplicate of gt-i18n/internal export)"]
B1 --> B2["🗑 Deleted — 0 importers"]
C --> C1["utils/utils.ts\n(readAuthFromEnv)"]
C1 --> C2["🗑 Deleted — 0 importers"]
D --> D1["translations-manager/utils/types/translations-manager.ts\n(TranslationsManagerConfig type)"]
D1 --> D2["🗑 Deleted — 0 importers"]
```
</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-code sweep"] --> B["gt-tanstack-start"]
A --> C["gt-react-native"]
A --> D["gt-i18n"]
B --> B1["condition-store/WritableConditionStore.ts\n(duplicate of gt-i18n/internal export)"]
B1 --> B2["🗑 Deleted — 0 importers"]
C --> C1["utils/utils.ts\n(readAuthFromEnv)"]
C1 --> C2["🗑 Deleted — 0 importers"]
D --> D1["translations-manager/utils/types/translations-manager.ts\n(TranslationsManagerConfig type)"]
D1 --> D2["🗑 Deleted — 0 importers"]
```
</a>
</details>
<sub>Reviews (1): Last reviewed commit: ["refactor: remove three
orphaned dead
fil..."](824bfda)
| [Re-trigger
Greptile](https://app.greptile.com/api/retrigger?id=40687047)</sub>
<!-- /greptile_comment -->
`hashTemplate` (`generaltranslation/id`) has **zero consumers** anywhere (grep-verified). Deletes the file + its barrel export; `hashSource`/`hashString` untouched. core builds + tests pass; dependents build. Part of the odysseus dead-code sweep. <!-- greptile_comment --> <h3>Greptile Summary</h3> Removes the `hashTemplate` function and its barrel export from `generaltranslation/id`. A codebase-wide grep confirms zero consumers of this export remain after the deletion. - Deletes `packages/core/src/id/hashTemplate.ts` (a thin wrapper around `stableStringify` + `hashString`) and removes its re-export from `packages/core/src/id.ts`. - Adds a patch-level changeset entry; `hashSource` and `hashString` are unaffected. <details open><summary><h3>Confidence Score: 5/5</h3></summary> Safe to merge — removes a dead export with no remaining consumers in the repository. The deleted function was a thin, self-contained wrapper with zero callers anywhere in the codebase. The remaining exports in the barrel file are untouched, the changeset is correctly scoped as a patch, and no dependents reference the removed symbol. No files require special attention. </details> <details><summary><h3>Important Files Changed</h3></summary> | Filename | Overview | |----------|----------| | .changeset/trim-core-hashtemplate.md | New changeset documenting the patch-level removal of hashTemplate from the generaltranslation package. | | packages/core/src/id.ts | Barrel export for the id subpath — removes the hashTemplate re-export; hashSource and hashString remain untouched. | | packages/core/src/id/hashTemplate.ts | Deleted file — contained the hashTemplate implementation (a thin wrapper around stableStringify + hashString). Confirmed zero consumers remain in the repo. | </details> <sub>Reviews (1): Last reviewed commit: ["refactor(core): remove unused hashTempla..."](520b242) | [Re-trigger Greptile](https://app.greptile.com/api/retrigger?id=40686968)</sub> <!-- /greptile_comment -->
Removes `isVariableObject`, `renderSkeleton`, and `reactHasUse` (+ their modules) from `@generaltranslation/react-core/pure` — **zero consumers** anywhere (grep-verified). More dead code off the client-shipped `/pure` entry. react-core + gt-react + gt-next build; react-core tests pass. (Companion to #1699.) Part of the odysseus dead-code sweep. <!-- greptile_comment --> <h3>Greptile Summary</h3> This PR removes three unused exports — `isVariableObject`, `renderSkeleton`, and `reactHasUse` — along with their source files from the `@generaltranslation/react-core/pure` client-shipped entry point. Grep-verification confirms no consumers exist anywhere in the monorepo. - `pure.ts`: three `export { … }` lines removed; the remaining exports are unaffected. - Three source files deleted: `isVariableObject.ts`, `renderSkeleton.tsx`, and `reactHasUse.ts`, each confirmed orphaned. - A patch-level changeset entry documents the removal for downstream consumers. <details open><summary><h3>Confidence Score: 5/5</h3></summary> Safe to merge — strictly removes dead code with no remaining consumers in the monorepo. All three deleted symbols (`isVariableObject`, `renderSkeleton`, `reactHasUse`) have zero import sites anywhere in the repository, confirmed by grep. The pure.ts entry point drops only the three export lines and is otherwise unchanged. The changeset is correctly marked as a patch. There are no logic changes and no risk of breakage for existing consumers. No files require special attention. </details> <details><summary><h3>Important Files Changed</h3></summary> | Filename | Overview | |----------|----------| | packages/react-core/src/pure.ts | Three dead export lines removed; remaining exports intact and unaffected. | | packages/react-core/src/utils/promises/reactHasUse.ts | File deleted; boolean utility had zero consumers in the monorepo. | | packages/react-core/src/utils/rendering/isVariableObject.ts | File deleted; type-guard had zero consumers in the monorepo. | | packages/react-core/src/utils/rendering/renderSkeleton.tsx | File deleted; skeleton renderer returned a single empty string and had zero consumers. | | .changeset/trim-react-core-pure-exports.md | Patch-level changeset entry accurately describes the three removed exports. | </details> <details><summary><h3>Flowchart</h3></summary> <a href="#gh-light-mode-only"> ```mermaid %%{init: {'theme': 'neutral'}}%% flowchart TD A["@generaltranslation/react-core/pure"] --> B["pure.ts (entry point)"] B --> C["Remaining exports\n(dictionaries, rendering utils, types…)"] B -. "removed" .-> D["isVariableObject\n❌ deleted"] B -. "removed" .-> E["renderSkeleton\n❌ deleted"] B -. "removed" .-> F["reactHasUse\n❌ deleted"] style D stroke:#f66,stroke-dasharray:5 5 style E stroke:#f66,stroke-dasharray:5 5 style F stroke:#f66,stroke-dasharray:5 5 ``` </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"] --> B["pure.ts (entry point)"] B --> C["Remaining exports\n(dictionaries, rendering utils, types…)"] B -. "removed" .-> D["isVariableObject\n❌ deleted"] B -. "removed" .-> E["renderSkeleton\n❌ deleted"] B -. "removed" .-> F["reactHasUse\n❌ deleted"] style D stroke:#f66,stroke-dasharray:5 5 style E stroke:#f66,stroke-dasharray:5 5 style F stroke:#f66,stroke-dasharray:5 5 ``` </a> </details> <sub>Reviews (1): Last reviewed commit: ["refactor(react-core): remove unused /pur..."](b8c1840) | [Re-trigger Greptile](https://app.greptile.com/api/retrigger?id=40686898)</sub> <!-- /greptile_comment -->
Removes `condition-store/localeResolver.ts` and its `gt-i18n/internal`
exports (`determineSupportedLocale`, `resolveSupportedLocale`,
`createLocaleResolver`). These were thin wrappers over
`getI18nConfig().<method>()` with **zero consumers** — callers use the
`I18nConfig` methods directly. Verified via grep across all packages;
the `LocaleCandidates` type re-export is preserved (sourced from
`I18nConfig`). gt-i18n + react-core + gt-next + gt-react-native build;
238 gt-i18n tests pass. Part of the odysseus dead-code sweep.
<!-- greptile_comment -->
<h3>Greptile Summary</h3>
This PR removes the `condition-store/localeResolver.ts` module and its
three `/internal` exports (`determineSupportedLocale`,
`resolveSupportedLocale`, `createLocaleResolver`) from `gt-i18n`, along
with their test file and changeset entry. The functions were thin
wrappers over `getI18nConfig().<method>()` with no consumers across the
monorepo — verified here as well.
- `packages/i18n/src/condition-store/localeResolver.ts` and its tests
are deleted; the `LocaleCandidates` type re-export in `internal.ts` is
redirected to its real source (`i18n-config/I18nConfig`), preserving the
public API.
- The only existing caller
(`tanstack-start/src/functions/parseLocale.ts`) already calls
`getI18nConfig().resolveSupportedLocale()` directly, confirming no
breakage.
<details open><summary><h3>Confidence Score: 5/5</h3></summary>
Safe to merge — removes dead code only, with no callers anywhere in the
monorepo.
All three deleted functions were pure pass-throughs to getI18nConfig()
methods. The one external caller (tanstack-start/parseLocale.ts) already
calls the underlying I18nConfig methods directly and is unaffected. The
LocaleCandidates type re-export is correctly preserved from its real
source. No logic changes, no API breakage.
No files require special attention.
</details>
<details><summary><h3>Important Files Changed</h3></summary>
| Filename | Overview |
|----------|----------|
| packages/i18n/src/condition-store/localeResolver.ts | Deleted — three
thin wrapper functions with no consumers; safe removal. |
| packages/i18n/src/condition-store/__tests__/localeResolver.test.ts |
Deleted — test file for the removed module; no residual coverage gaps
since the underlying I18nConfig methods retain their own tests. |
| packages/i18n/src/internal.ts | Removed three named exports from the
deleted module; `LocaleCandidates` type re-export is correctly
redirected to its canonical source. |
| .changeset/trim-i18n-localeresolver.md | Patch-level changeset entry;
description is accurate and complete. |
</details>
<details><summary><h3>Flowchart</h3></summary>
<a href="#gh-light-mode-only">
```mermaid
%%{init: {'theme': 'neutral'}}%%
flowchart TD
A["gt-i18n/internal (public entry)"] --> B["LocaleCandidates type\n(re-exported from I18nConfig)"]
A -. "REMOVED" .-> C["determineSupportedLocale\nresolveSupportedLocale\ncreateLocaleResolver"]
C -. "were wrappers over" .-> D["getI18nConfig()"]
D --> E["I18nConfig instance\n.determineSupportedLocale()\n.resolveSupportedLocale()"]
F["tanstack-start/parseLocale.ts"] --> D
F -- "calls directly" --> E
```
</a>
<a href="#gh-dark-mode-only">
```mermaid
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
A["gt-i18n/internal (public entry)"] --> B["LocaleCandidates type\n(re-exported from I18nConfig)"]
A -. "REMOVED" .-> C["determineSupportedLocale\nresolveSupportedLocale\ncreateLocaleResolver"]
C -. "were wrappers over" .-> D["getI18nConfig()"]
D --> E["I18nConfig instance\n.determineSupportedLocale()\n.resolveSupportedLocale()"]
F["tanstack-start/parseLocale.ts"] --> D
F -- "calls directly" --> E
```
</a>
</details>
<sub>Reviews (1): Last reviewed commit: ["refactor(gt-i18n): remove
unused
localeR..."](04e6541)
| [Re-trigger
Greptile](https://app.greptile.com/api/retrigger?id=40686757)</sub>
<!-- /greptile_comment -->
Removes 8 long-`@deprecated` `I18nCache` methods that duplicated
`I18nConfig`/loader APIs and had **no callers**: `getDefaultLocale`,
`getLocales`, `getCustomMapping`, `getGTClass`, `getTranslationLoader`,
`resolveTranslationSync`, `getTranslations`, `getTranslationResolver`.
The TS build across gt-i18n + all consumers confirms nothing called
them. Removes now-unused imports and the tests that covered the removed
methods; 236 gt-i18n tests pass. Part of the odysseus dead-code sweep
(major version — deprecated API removal).
<!-- greptile_comment -->
<h3>Greptile Summary</h3>
This PR removes 8 long-deprecated `I18nCache` methods that were thin
pass-throughs to `I18nConfig` or existing cache methods
(`lookupTranslation`, `loadTranslations`, `getLookupTranslation`), along
with their unused imports (`GT`, `TranslationsLoader`) and the tests
that covered them.
- **`I18nCache.ts`**: Deletes `getDefaultLocale`, `getLocales`,
`getCustomMapping`, `getGTClass`, `getTranslationLoader`,
`resolveTranslationSync`, `getTranslations`, and
`getTranslationResolver`. No other logic is touched.
- **Tests**: Removes the dedicated `resolveTranslationSync.test.ts` file
entirely and strips the corresponding assertions from
`I18nCache.test.ts`; the 236 remaining tests continue to cover all
active behavior.
- **Changeset**: Typed as `patch`, which is safe because
`odysseus-major-version-bumps.md` already carries a `major` bump for
`gt-i18n` that takes precedence at release time.
<details open><summary><h3>Confidence Score: 5/5</h3></summary>
Safe to merge — removes only dead, deprecated pass-through methods with
no callers in the codebase.
Every removed method was a one-liner delegating to either
getI18nConfig() or an existing I18nCache method; no business logic was
changed. The corresponding tests are correctly removed. The patch
changeset is absorbed by the existing major odysseus bump, so semver is
unaffected. No imports, types, or runtime paths outside the deleted code
are touched.
No files require special attention.
</details>
<details><summary><h3>Important Files Changed</h3></summary>
| Filename | Overview |
|----------|----------|
| packages/i18n/src/i18n-cache/I18nCache.ts | Removes 8 deprecated
pass-through methods and their now-unused GT/TranslationsLoader imports;
remaining code is untouched. |
| packages/i18n/src/i18n-cache/__tests__/I18nCache.test.ts | Removes
tests for the 8 deleted methods; the remaining test suite continues to
cover active I18nCache behavior. |
| packages/i18n/src/i18n-cache/__tests__/resolveTranslationSync.test.ts
| Entire file deleted — it tested only resolveTranslationSync and
getTranslations, both of which are removed in this PR. |
| .changeset/trim-i18ncache-deprecated.md | Changeset typed as patch;
safe because odysseus-major-version-bumps.md already carries a major
bump for gt-i18n that will take precedence. |
</details>
<details><summary><h3>Flowchart</h3></summary>
<a href="#gh-light-mode-only">
```mermaid
%%{init: {'theme': 'neutral'}}%%
flowchart TD
subgraph BEFORE["Before (deprecated methods present)"]
A1[Caller] -->|getDefaultLocale / getLocales / getCustomMapping| B1[I18nCache]
A2[Caller] -->|getGTClass| B1
A3[Caller] -->|getTranslationLoader| B1
A4[Caller] -->|resolveTranslationSync| B1
A5[Caller] -->|getTranslations / getTranslationResolver| B1
B1 -->|delegates| C1[getI18nConfig]
B1 -->|delegates| C2[lookupTranslation / loadTranslations / getLookupTranslation]
end
subgraph AFTER["After (deprecated methods removed)"]
D1[Caller] -->|getI18nConfig directly| E1[I18nConfig]
D2[Caller] -->|lookupTranslation / loadTranslations / getLookupTranslation| E2[I18nCache active API]
end
```
</a>
<a href="#gh-dark-mode-only">
```mermaid
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
subgraph BEFORE["Before (deprecated methods present)"]
A1[Caller] -->|getDefaultLocale / getLocales / getCustomMapping| B1[I18nCache]
A2[Caller] -->|getGTClass| B1
A3[Caller] -->|getTranslationLoader| B1
A4[Caller] -->|resolveTranslationSync| B1
A5[Caller] -->|getTranslations / getTranslationResolver| B1
B1 -->|delegates| C1[getI18nConfig]
B1 -->|delegates| C2[lookupTranslation / loadTranslations / getLookupTranslation]
end
subgraph AFTER["After (deprecated methods removed)"]
D1[Caller] -->|getI18nConfig directly| E1[I18nConfig]
D2[Caller] -->|lookupTranslation / loadTranslations / getLookupTranslation| E2[I18nCache active API]
end
```
</a>
</details>
<sub>Reviews (1): Last reviewed commit: ["refactor(gt-i18n): remove
deprecated
I18..."](35ba67c)
| [Re-trigger
Greptile](https://app.greptile.com/api/retrigger?id=40687579)</sub>
<!-- /greptile_comment -->
This PR was opened by the [Changesets release](https://github.com/changesets/action) GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to odysseus, this PR will be updated.⚠️ ⚠️ ⚠️ ⚠️ ⚠️ ⚠️ `odysseus` is currently in **pre mode** so this branch has prereleases rather than normal releases. If you want to exit prereleases, run `changeset pre exit` on `odysseus`.⚠️ ⚠️ ⚠️ ⚠️ ⚠️ ⚠️ # Releases ## gt@2.14.51-odysseus.3 ### Patch Changes - Updated dependencies [b1eef00] - generaltranslation@9.0.0-odysseus.3 - @generaltranslation/python-extractor@0.2.23-odysseus.3 - @generaltranslation/supported-locales@2.1.2-odysseus.3 ## @generaltranslation/compiler@1.3.25-odysseus.3 ### Patch Changes - Updated dependencies [b1eef00] - generaltranslation@9.0.0-odysseus.3 ## generaltranslation@9.0.0-odysseus.3 ### Patch Changes - b1eef00: Remove the v8-era backwards-compatibility shim from `generaltranslation`. Deletes `src/backwards-compatability/` (`dataConversion`, `oldTypes`, `typeChecking`, `oldHashJsxChildren`) and its 26 re-exports from `generaltranslation/internal` — the old↔new JSX/variable-format converters, legacy type guards, and the legacy hashers. These had no consumers anywhere in the libraries; the old format they bridged is no longer produced. Safe to drop in the next major. ~477 LOC removed from the `/internal` entry. ## gtx-cli@2.14.51-odysseus.3 ### Patch Changes - gt@2.14.51-odysseus.3 ## gt-i18n@1.0.0-odysseus.3 ### Patch Changes - b765174: Remove three orphaned, never-imported files: - `gt-tanstack-start`: `condition-store/WritableConditionStore.ts` (an orphaned local copy; the package uses gt-i18n's writable condition store). - `gt-react-native`: `utils/utils.ts` (`readAuthFromEnv`, no consumers). - `gt-i18n`: `i18n-cache/translations-manager/utils/types/translations-manager.ts` (unreferenced `TranslationsManagerConfig` type). - Updated dependencies [b1eef00] - generaltranslation@9.0.0-odysseus.3 - @generaltranslation/supported-locales@2.1.2-odysseus.3 ## locadex@1.0.186-odysseus.3 ### Patch Changes - gt@2.14.51-odysseus.3 ## gt-next@11.0.0-odysseus.7 ### Patch Changes - Updated dependencies [b1eef00] - Updated dependencies [b765174] - Updated dependencies [07bfb00] - generaltranslation@9.0.0-odysseus.3 - gt-i18n@1.0.0-odysseus.3 - @generaltranslation/react-core@11.0.0-odysseus.7 - @generaltranslation/compiler@1.3.25-odysseus.3 - gt-react@11.0.0-odysseus.7 - @generaltranslation/supported-locales@2.1.2-odysseus.3 ## @generaltranslation/gt-next-lint@15.0.0-odysseus.7 ### Patch Changes - gt-next@11.0.0-odysseus.7 ## gt-node@1.0.0-odysseus.3 ### Patch Changes - Updated dependencies [b1eef00] - Updated dependencies [b765174] - generaltranslation@9.0.0-odysseus.3 - gt-i18n@1.0.0-odysseus.3 ## @generaltranslation/python-extractor@0.2.23-odysseus.3 ### Patch Changes - Updated dependencies [b1eef00] - generaltranslation@9.0.0-odysseus.3 ## gt-react@11.0.0-odysseus.7 ### Patch Changes - Updated dependencies [b1eef00] - Updated dependencies [b765174] - Updated dependencies [07bfb00] - generaltranslation@9.0.0-odysseus.3 - gt-i18n@1.0.0-odysseus.3 - @generaltranslation/react-core@11.0.0-odysseus.7 - @generaltranslation/supported-locales@2.1.2-odysseus.3 ## @generaltranslation/react-core@11.0.0-odysseus.7 ### Patch Changes - 07bfb00: Remove the unused dictionary-injection helpers from `@generaltranslation/react-core/pure`. `collectUntranslatedEntries`, `injectAndMerge`, `injectEntry`, `injectFallbacks`, `injectHashes`, `injectTranslations`, `getSubtree`, `getSubtreeWithCreation`, and `stripMetadataFromEntries` were a self-referential cluster with no consumers anywhere in the library or its packages. Removing them (and their modules/tests) trims ~415 LOC of dead code from the client-shipped `/pure` entry. The still-used dictionary helpers (`getDictionaryEntry`, `getEntryAndMetadata`, `mergeDictionaries`, `flattenDictionary`, `indexDict`, `isDictionaryEntry`) are unchanged. - Updated dependencies [b1eef00] - Updated dependencies [b765174] - generaltranslation@9.0.0-odysseus.3 - gt-i18n@1.0.0-odysseus.3 - @generaltranslation/supported-locales@2.1.2-odysseus.3 ## gt-react-native@11.0.0-odysseus.7 ### Patch Changes - b765174: Remove three orphaned, never-imported files: - `gt-tanstack-start`: `condition-store/WritableConditionStore.ts` (an orphaned local copy; the package uses gt-i18n's writable condition store). - `gt-react-native`: `utils/utils.ts` (`readAuthFromEnv`, no consumers). - `gt-i18n`: `i18n-cache/translations-manager/utils/types/translations-manager.ts` (unreferenced `TranslationsManagerConfig` type). - Updated dependencies [b1eef00] - Updated dependencies [b765174] - Updated dependencies [07bfb00] - generaltranslation@9.0.0-odysseus.3 - gt-i18n@1.0.0-odysseus.3 - @generaltranslation/react-core@11.0.0-odysseus.7 - @generaltranslation/supported-locales@2.1.2-odysseus.3 ## gt-sanity@2.0.18-odysseus.5 ### Patch Changes - Updated dependencies [b1eef00] - generaltranslation@9.0.0-odysseus.3 ## @generaltranslation/supported-locales@2.1.2-odysseus.3 ### Patch Changes - Updated dependencies [b1eef00] - generaltranslation@9.0.0-odysseus.3 ## gt-tanstack-start@11.0.0-odysseus.7 ### Patch Changes - b765174: Remove three orphaned, never-imported files: - `gt-tanstack-start`: `condition-store/WritableConditionStore.ts` (an orphaned local copy; the package uses gt-i18n's writable condition store). - `gt-react-native`: `utils/utils.ts` (`readAuthFromEnv`, no consumers). - `gt-i18n`: `i18n-cache/translations-manager/utils/types/translations-manager.ts` (unreferenced `TranslationsManagerConfig` type). - Updated dependencies [b1eef00] - Updated dependencies [b765174] - Updated dependencies [07bfb00] - generaltranslation@9.0.0-odysseus.3 - gt-i18n@1.0.0-odysseus.3 - @generaltranslation/react-core@11.0.0-odysseus.7 - gt-react@11.0.0-odysseus.7 Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
`validateLocales` was defined but never wired into config validation
(which runs
`validateLoadTranslations`/`validateTranslationApi`/`validateDictionary`)
and has **zero consumers** anywhere (grep-verified). Deletes the file +
its test. gt-i18n builds and tests pass. Part of the odysseus dead-code
sweep.
<!-- greptile_comment -->
<h3>Greptile Summary</h3>
This PR removes the `validateLocales` function and its test from
`gt-i18n` as part of a dead-code sweep. The function validated BCP 47
locale codes but was never wired into `validateConfig`, which only calls
`validateLoadTranslations`, `validateTranslationApi`, and
`validateDictionary`.
- Deletes `validateLocales.ts` and its accompanying Vitest test file —
confirmed zero consumers in the repo.
- Adds a patch-level changeset entry documenting the removal.
<details open><summary><h3>Confidence Score: 5/5</h3></summary>
Straightforward dead-code removal with no runtime impact — the deleted
function was never invoked by any code path.
The deleted file was never imported by validateConfig or any other
module; the grep confirms zero consumers remain. Removing it and its
test leaves the validation pipeline unchanged.
No files require special attention.
</details>
<details><summary><h3>Important Files Changed</h3></summary>
| Filename | Overview |
|----------|----------|
|
packages/i18n/src/i18n-cache/validation/config-validation/validateLocales.ts
| Dead-code validator deleted; confirmed it was never imported by
validateConfig or any other consumer. |
|
packages/i18n/src/i18n-cache/validation/config-validation/__tests__/validateLocales.test.ts
| Test file for the deleted validator removed; no orphaned references
remain. |
| .changeset/trim-i18n-validatelocales.md | Patch-level changeset entry
accurately describes the removal. |
</details>
<details><summary><h3>Flowchart</h3></summary>
<a href="#gh-light-mode-only">
```mermaid
%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[validateConfig] --> B[validateLoadTranslations]
A --> C[validateTranslationApi]
A --> D[validateDictionary]
E["validateLocales (deleted)"] -.->|"was never called"| A
style E stroke-dasharray: 5 5,color:#999,fill:#f9f9f9
```
</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[validateConfig] --> B[validateLoadTranslations]
A --> C[validateTranslationApi]
A --> D[validateDictionary]
E["validateLocales (deleted)"] -.->|"was never called"| A
style E stroke-dasharray: 5 5,color:#999,fill:#f9f9f9
```
</a>
</details>
<sub>Reviews (1): Last reviewed commit: ["refactor(gt-i18n): remove
unused
validat..."](43fb20e)
| [Re-trigger
Greptile](https://app.greptile.com/api/retrigger?id=40686819)</sub>
<!-- /greptile_comment -->
Removes 21 error/warning builders from `errors/createErrors.ts` that
have **zero consumers** anywhere (grep-verified each). ~155 LOC of dead
error-string code, some of which shipped to bundles. The 29 still-used
builders are unchanged. gt-next builds; 191 tests pass. Part of the
odysseus dead-code sweep.
<!-- greptile_comment -->
<h3>Greptile Summary</h3>
This PR removes 21 dead error/warning builder functions from
`packages/next/src/errors/createErrors.ts` (~155 LOC), along with the
now-unused `formatDiagnosticErrorDetails` import. The 29 remaining
builders are untouched and the changeset is correctly scoped as a patch.
- **Dead code removed**: 21 exported functions
(`createDictionaryTranslationError`, `gtProviderUseClientError`,
`noInitGTWarn`, `runtimeTranslationTimeoutWarning`, and 17 others) that
had zero consumers in the repo were deleted.
- **Import cleaned up**: `formatDiagnosticErrorDetails` was the only
import whose sole callers (`createStringRenderError` /
`createStringRenderWarning`) were among the deleted functions; it was
correctly removed in the same commit.
<details open><summary><h3>Confidence Score: 5/5</h3></summary>
Pure dead-code removal with no changes to active error paths; safe to
merge.
Every deleted symbol was confirmed to have zero consumers, the unused
import was correctly cleaned up in the same commit, and the 29
still-used builders are untouched.
No files require special attention.
</details>
<details><summary><h3>Important Files Changed</h3></summary>
| Filename | Overview |
|----------|----------|
| packages/next/src/errors/createErrors.ts | Removes 21 unused exported
error/warning builders and the now-orphaned
`formatDiagnosticErrorDetails` import; all 29 remaining builders and
their consumers are unchanged. |
| .changeset/trim-next-createerrors.md | Correct patch-level changeset
enumerating all 21 removed symbols; no issues. |
</details>
<details><summary><h3>Flowchart</h3></summary>
<a href="#gh-light-mode-only">
```mermaid
%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[createErrors.ts\n29 remaining builders] --> B[createGtNextDiagnostic]
A --> C[createGtNextPluginDiagnostic]
B --> D[diagnostics.ts]
C --> D
DEL["❌ Removed — 21 unused builders\n(createStringRenderError, gtProviderUseClientError,\nnoInitGTWarn, runtimeTranslationTimeoutWarning,\ncreateStringRenderWarning, dictionaryDisabledError,\ncreateTranslationLoadingWarning, … +14 more)"]
DEL -. was calling .-> FMT["❌ formatDiagnosticErrorDetails\n(import also removed)"]
FMT -. was imported from .-> D
```
</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[createErrors.ts\n29 remaining builders] --> B[createGtNextDiagnostic]
A --> C[createGtNextPluginDiagnostic]
B --> D[diagnostics.ts]
C --> D
DEL["❌ Removed — 21 unused builders\n(createStringRenderError, gtProviderUseClientError,\nnoInitGTWarn, runtimeTranslationTimeoutWarning,\ncreateStringRenderWarning, dictionaryDisabledError,\ncreateTranslationLoadingWarning, … +14 more)"]
DEL -. was calling .-> FMT["❌ formatDiagnosticErrorDetails\n(import also removed)"]
FMT -. was imported from .-> D
```
</a>
</details>
<!-- greptile_failed_comments -->
<details open><summary><h3>Comments Outside Diff (1)</h3></summary>
1. `packages/next/src/errors/createErrors.ts`, line 4-8
([link](https://github.com/generaltranslation/gt/blob/3d7fd6cda7f5364f90381634aa4975fa717c1616/packages/next/src/errors/createErrors.ts#L4-L8))
<a href="#"><img alt="P2"
src="https://greptile-static-assets.s3.amazonaws.com/badges/p2.svg?v=9"
align="top"></a> The `formatDiagnosticErrorDetails` import is now
unused. The only two callers — `createStringRenderError` and
`createStringRenderWarning` — were both removed in this PR, leaving the
import as dead code. TypeScript (and any lint rule for `no-unused-vars`)
will flag this.
<details><summary>Prompt To Fix With AI</summary>
`````markdown
This is a comment left during a code review.
Path: packages/next/src/errors/createErrors.ts
Line: 4-8
Comment:
The `formatDiagnosticErrorDetails` import is now unused. The only two
callers — `createStringRenderError` and `createStringRenderWarning` —
were both removed in this PR, leaving the import as dead code.
TypeScript (and any lint rule for `no-unused-vars`) will flag this.
How can I resolve this? If you propose a fix, please make it concise.
`````
</details>
<a
href="https://app.greptile.com/ide/claude-code?prompt=This%20is%20a%20comment%20left%20during%20a%20code%20review.%0APath%3A%20packages%2Fnext%2Fsrc%2Ferrors%2FcreateErrors.ts%0ALine%3A%204-8%0A%0AComment%3A%0AThe%20%60formatDiagnosticErrorDetails%60%20import%20is%20now%20unused.%20The%20only%20two%20callers%20%E2%80%94%20%60createStringRenderError%60%20and%20%60createStringRenderWarning%60%20%E2%80%94%20were%20both%20removed%20in%20this%20PR%2C%20leaving%20the%20import%20as%20dead%20code.%20TypeScript%20%28and%20any%20lint%20rule%20for%20%60no-unused-vars%60%29%20will%20flag%20this.%0A%0A%60%60%60suggestion%0Aimport%20%7B%0A%20%20createGtNextDiagnostic%2C%0A%20%20createGtNextPluginDiagnostic%2C%0A%7D%20from%20'.%2Fdiagnostics'%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=1707&platform=github"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://greptile-static-assets.s3.amazonaws.com/badges/FixInClaudeDark.svg?v=3"><source
media="(prefers-color-scheme: light)"
srcset="https://greptile-static-assets.s3.amazonaws.com/badges/FixInClaude.svg?v=3"><img
alt="Fix in Claude Code"
src="https://greptile-static-assets.s3.amazonaws.com/badges/FixInClaude.svg?v=3"
height="20"></picture></a> <a
href="https://app.greptile.com/api/ide/codex?prompt=IMPORTANT%3A%20Work%20in%20the%20repository%20%22generaltranslation%2Fgt%22%20on%20the%20existing%20branch%20%22bg%2Fodysseus-trim-next-createerrors%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-trim-next-createerrors%22.%0A%0AThis%20is%20a%20comment%20left%20during%20a%20code%20review.%0APath%3A%20packages%2Fnext%2Fsrc%2Ferrors%2FcreateErrors.ts%0ALine%3A%204-8%0A%0AComment%3A%0AThe%20%60formatDiagnosticErrorDetails%60%20import%20is%20now%20unused.%20The%20only%20two%20callers%20%E2%80%94%20%60createStringRenderError%60%20and%20%60createStringRenderWarning%60%20%E2%80%94%20were%20both%20removed%20in%20this%20PR%2C%20leaving%20the%20import%20as%20dead%20code.%20TypeScript%20%28and%20any%20lint%20rule%20for%20%60no-unused-vars%60%29%20will%20flag%20this.%0A%0A%60%60%60suggestion%0Aimport%20%7B%0A%20%20createGtNextDiagnostic%2C%0A%20%20createGtNextPluginDiagnostic%2C%0A%7D%20from%20'.%2Fdiagnostics'%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=1707&platform=github"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://greptile-static-assets.s3.amazonaws.com/badges/FixInCodexDark.svg?v=3"><source
media="(prefers-color-scheme: light)"
srcset="https://greptile-static-assets.s3.amazonaws.com/badges/FixInCodex.svg?v=3"><img
alt="Fix in Codex"
src="https://greptile-static-assets.s3.amazonaws.com/badges/FixInCodex.svg?v=3"
height="20"></picture></a>
</details>
<!-- /greptile_failed_comments -->
<sub>Reviews (2): Last reviewed commit: ["refactor(gt-next): drop
now-unused
forma..."](646c8b8)
| [Re-trigger
Greptile](https://app.greptile.com/api/retrigger?id=40687204)</sub>
<!-- /greptile_comment -->
This PR was opened by the [Changesets release](https://github.com/changesets/action) GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to odysseus, this PR will be updated.⚠️ ⚠️ ⚠️ ⚠️ ⚠️ ⚠️ `odysseus` is currently in **pre mode** so this branch has prereleases rather than normal releases. If you want to exit prereleases, run `changeset pre exit` on `odysseus`.⚠️ ⚠️ ⚠️ ⚠️ ⚠️ ⚠️ # Releases ## gt@2.14.51-odysseus.4 ### Patch Changes - Updated dependencies [26faa87] - generaltranslation@9.0.0-odysseus.4 - @generaltranslation/python-extractor@0.2.23-odysseus.4 - @generaltranslation/supported-locales@2.1.2-odysseus.4 ## @generaltranslation/compiler@1.3.25-odysseus.4 ### Patch Changes - Updated dependencies [26faa87] - generaltranslation@9.0.0-odysseus.4 ## generaltranslation@9.0.0-odysseus.4 ### Patch Changes - 26faa87: Remove the unused `hashTemplate` export from `generaltranslation/id`. It had no consumers anywhere in the libraries. `hashSource`/`hashString` are unchanged. ## gtx-cli@2.14.51-odysseus.4 ### Patch Changes - gt@2.14.51-odysseus.4 ## gt-i18n@1.0.0-odysseus.4 ### Patch Changes - 270b821: Remove the unused `condition-store/localeResolver` module from `gt-i18n/internal`. `determineSupportedLocale`, `resolveSupportedLocale`, and `createLocaleResolver` were thin wrappers over `getI18nConfig().<method>()`. They had no consumers — callers (e.g. tanstack-start) use the `I18nConfig` methods directly via `getI18nConfig()`. Removing the module trims dead indirection from the `/internal` entry; the `LocaleCandidates` type re-export is unaffected (re-exported from its real source). - bffaa67: Remove the unused `validateLocales` config validator from `gt-i18n`. `validateLocales` was defined but never called (config validation runs `validateLoadTranslations`, `validateTranslationApi`, and `validateDictionary`) and had no consumers anywhere. Dead code removed. - d602065: Remove deprecated methods from `gt-i18n`'s `I18nCache`. Dropped the long-`@deprecated` cache methods that duplicated `I18nConfig`/loader APIs: `getDefaultLocale`, `getLocales`, `getCustomMapping`, `getGTClass`, `getTranslationLoader`, `resolveTranslationSync`, `getTranslations`, and `getTranslationResolver`. None were called anywhere (consumers use `getI18nConfig()` / `lookupTranslation` / `loadTranslations`). Removes the methods, their now-unused imports, and the tests that covered them. - Updated dependencies [26faa87] - generaltranslation@9.0.0-odysseus.4 - @generaltranslation/supported-locales@2.1.2-odysseus.4 ## locadex@1.0.186-odysseus.4 ### Patch Changes - gt@2.14.51-odysseus.4 ## gt-next@11.0.0-odysseus.8 ### Patch Changes - Updated dependencies [26faa87] - Updated dependencies [270b821] - Updated dependencies [bffaa67] - Updated dependencies [d602065] - Updated dependencies [6da26e8] - generaltranslation@9.0.0-odysseus.4 - gt-i18n@1.0.0-odysseus.4 - @generaltranslation/react-core@11.0.0-odysseus.8 - @generaltranslation/compiler@1.3.25-odysseus.4 - gt-react@11.0.0-odysseus.8 - @generaltranslation/supported-locales@2.1.2-odysseus.4 ## @generaltranslation/gt-next-lint@15.0.0-odysseus.8 ### Patch Changes - gt-next@11.0.0-odysseus.8 ## gt-node@1.0.0-odysseus.4 ### Patch Changes - Updated dependencies [26faa87] - Updated dependencies [270b821] - Updated dependencies [bffaa67] - Updated dependencies [d602065] - generaltranslation@9.0.0-odysseus.4 - gt-i18n@1.0.0-odysseus.4 ## @generaltranslation/python-extractor@0.2.23-odysseus.4 ### Patch Changes - Updated dependencies [26faa87] - generaltranslation@9.0.0-odysseus.4 ## gt-react@11.0.0-odysseus.8 ### Patch Changes - Updated dependencies [26faa87] - Updated dependencies [270b821] - Updated dependencies [bffaa67] - Updated dependencies [d602065] - Updated dependencies [6da26e8] - generaltranslation@9.0.0-odysseus.4 - gt-i18n@1.0.0-odysseus.4 - @generaltranslation/react-core@11.0.0-odysseus.8 - @generaltranslation/supported-locales@2.1.2-odysseus.4 ## @generaltranslation/react-core@11.0.0-odysseus.8 ### Patch Changes - 6da26e8: Remove three unused exports from `@generaltranslation/react-core/pure`: `isVariableObject`, `renderSkeleton`, and `reactHasUse`. None had consumers anywhere in the libraries. Trims dead code from the client-shipped `/pure` entry. - Updated dependencies [26faa87] - Updated dependencies [270b821] - Updated dependencies [bffaa67] - Updated dependencies [d602065] - generaltranslation@9.0.0-odysseus.4 - gt-i18n@1.0.0-odysseus.4 - @generaltranslation/supported-locales@2.1.2-odysseus.4 ## gt-react-native@11.0.0-odysseus.8 ### Patch Changes - Updated dependencies [26faa87] - Updated dependencies [270b821] - Updated dependencies [bffaa67] - Updated dependencies [d602065] - Updated dependencies [6da26e8] - generaltranslation@9.0.0-odysseus.4 - gt-i18n@1.0.0-odysseus.4 - @generaltranslation/react-core@11.0.0-odysseus.8 - @generaltranslation/supported-locales@2.1.2-odysseus.4 ## gt-sanity@2.0.18-odysseus.6 ### Patch Changes - Updated dependencies [26faa87] - generaltranslation@9.0.0-odysseus.4 ## @generaltranslation/supported-locales@2.1.2-odysseus.4 ### Patch Changes - Updated dependencies [26faa87] - generaltranslation@9.0.0-odysseus.4 ## gt-tanstack-start@11.0.0-odysseus.8 ### Patch Changes - Updated dependencies [26faa87] - Updated dependencies [270b821] - Updated dependencies [bffaa67] - Updated dependencies [d602065] - Updated dependencies [6da26e8] - generaltranslation@9.0.0-odysseus.4 - gt-i18n@1.0.0-odysseus.4 - @generaltranslation/react-core@11.0.0-odysseus.8 - gt-react@11.0.0-odysseus.8 Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
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.9 ### Patch Changes - cf3e961: Remove 21 unused error/warning builders from `gt-next`'s `errors/createErrors.ts`. The following had no consumers anywhere: `createDictionaryTranslationError`, `createInvalidDictionaryEntryWarning`, `createInvalidDictionaryTranslationEntryWarning`, `createInvalidIcuDictionaryEntryError`, `createInvalidIcuDictionaryEntryWarning`, `createMismatchingHashWarning`, `createNoEntryFoundWarning`, `createRequiredPrefixError`, `createStringRenderError`, `createStringRenderWarning`, `createStringTranslationError`, `createTranslationLoadingWarning`, `dictionaryDisabledError`, `dictionaryNotFoundWarning`, `gtProviderUseClientError`, `missingVariablesError`, `noInitGTWarn`, `runtimeTranslationTimeoutWarning`, `txUseClientError`, `unresolvedGetLocaleBuildError`, `usingDefaultsWarning`. ~155 LOC of dead error-string code removed. - @generaltranslation/react-core@11.0.0-odysseus.9 - gt-react@11.0.0-odysseus.9 ## @generaltranslation/gt-next-lint@15.0.0-odysseus.9 ### Patch Changes - Updated dependencies [cf3e961] - gt-next@11.0.0-odysseus.9 ## gt-react@11.0.0-odysseus.9 ### Patch Changes - @generaltranslation/react-core@11.0.0-odysseus.9 ## gt-react-native@11.0.0-odysseus.9 ### Patch Changes - @generaltranslation/react-core@11.0.0-odysseus.9 ## gt-tanstack-start@11.0.0-odysseus.9 ### Patch Changes - @generaltranslation/react-core@11.0.0-odysseus.9 - gt-react@11.0.0-odysseus.9 ## @generaltranslation/react-core@11.0.0-odysseus.9 Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
## Summary
- key generated GTJSON/source entries by content hash instead of custom
id
- keep custom ids as metadata while excluding them from calculated
hashes
- remove id-based duplicate rejection now that ids are metadata-only
## Tests
- pnpm --filter gt test -- src/translation/__tests__/parse.test.ts
src/extraction/__tests__/postProcess.test.ts
src/formats/files/__tests__/collectFiles.test.ts
- pnpm --filter gt typecheck
- pnpm --filter @generaltranslation/compiler test --
src/utils/__tests__/calculateHash.test.ts
- pnpm --filter @generaltranslation/compiler typecheck
- pnpm --filter generaltranslation test --
src/translate/__tests__/translateMany.test.ts
- pnpm --filter generaltranslation typecheck
- pnpm --filter gt-i18n typecheck
- pnpm --filter @generaltranslation/react-core typecheck
<!-- greptile_comment -->
<h3>Greptile Summary</h3>
This PR changes how translation entries are keyed: from using a custom
`id` (when present) as the primary lookup key to using a content-based
hash that excludes the `id`. The `id` is now kept purely as metadata to
aid debugging and duplicate validation, but it no longer participates in
hash computation.
- All hash-computation call sites (`calculateHashes`,
`createDictionaryUpdates`, `hashMessage`, `registerUseGTCallback`,
`registerStandaloneTranslation`, `translateMany`) remove `id` from the
fields passed to `hashSource`, making hashes stable across different
user-supplied IDs.
- `collectFiles.ts` and `inline.ts` are updated to key data exclusively
by hash rather than falling back to `id`; `parse.ts` retains ID
validation but now only rejects entries whose IDs map to *different*
hashes (conflicting content), rather than treating the ID itself as the
storage key.
- New tests explicitly verify that two entries with different IDs but
identical content produce identical hashes.
<details open><summary><h3>Confidence Score: 5/5</h3></summary>
Safe to merge; the change is consistently applied across all
hash-computation sites and the ID validation in parse.ts correctly
catches conflicting-id errors without relying on id as a storage key.
All six call sites that previously included `id` in hash computation
have been updated in lockstep. Dictionary entries, inline entries,
runtime translation, and the i18n utility all follow the same new
contract. New tests explicitly verify the id-exclusion behavior and the
conflicting-id error path still fires correctly.
No files require special attention.
</details>
<details><summary><h3>Important Files Changed</h3></summary>
| Filename | Overview |
|----------|----------|
| packages/core/src/translate/translateMany.ts | Replaces the broad
`...metadata` spread in the fallback hash computation with explicit
`context` and `maxChars` fields, correctly excluding `id` and other
irrelevant metadata fields from the hash. |
| packages/cli/src/translation/parse.ts | Retains ID-conflict validation
(same id, different hashes → error + filter) while removing the
id-as-primary-key role; the filter and error path remain semantically
correct under the new scheme. |
| packages/compiler/src/utils/calculateHash.ts | Removes `id` from the
parameter type and both `_hashSource` call sites; the previously noted
stale `id?: string` in the type signature is now fully cleaned up. |
| packages/i18n/src/utils/hashMessage.ts | Removes `$id` from the
`hashSource` call; the `$_hash` fast-path and `$context`/`$maxChars`
handling are unchanged. |
| packages/cli/src/formats/files/collectFiles.ts | Now keys both
`fileData` and `fileMetadata` exclusively by hash; entries without a
computed hash are silently dropped, which is safe because all pipeline
stages guarantee a hash before reaching this point. |
| packages/cli/src/extraction/postProcess.ts | Removes the `id` field
from the `hashSource` call in `calculateHashes`, making this the
canonical point where id-agnostic hashes are computed for inline
updates. |
| packages/cli/src/cli/inline.ts | Removes the `id`-first keying branch
in `handleGenerateSourceCommand`; safe because
`aggregateInlineTranslations` always populates `metadata.hash` before
this loop runs. |
</details>
<details><summary><h3>Flowchart</h3></summary>
<a href="#gh-light-mode-only">
```mermaid
%%{init: {'theme': 'neutral'}}%%
flowchart TD
A["Source entry\n(source, id?, context?, maxChars?)"] --> B["hashSource()\nexcludes id"]
B --> C["content hash H"]
C --> D{hash already\nprecomputed?}
D -- "yes (metadata.hash)" --> E["use precomputed hash"]
D -- "no" --> B
E --> F["fileData[H] = source\nfileMetadata[H] = {id, hash, ...}"]
F --> G["Upload to GT API\nkeyed by H"]
G --> H["Translation result\nkeyed by H"]
subgraph "ID Validation (parse.ts)"
I["Same id, same hash"] --> J["Both pass through\n(deduplicated downstream by H)"]
K["Same id, diff hash"] --> L["Error + filtered out"]
end
A --> I
A --> K
```
</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["Source entry\n(source, id?, context?, maxChars?)"] --> B["hashSource()\nexcludes id"]
B --> C["content hash H"]
C --> D{hash already\nprecomputed?}
D -- "yes (metadata.hash)" --> E["use precomputed hash"]
D -- "no" --> B
E --> F["fileData[H] = source\nfileMetadata[H] = {id, hash, ...}"]
F --> G["Upload to GT API\nkeyed by H"]
G --> H["Translation result\nkeyed by H"]
subgraph "ID Validation (parse.ts)"
I["Same id, same hash"] --> J["Both pass through\n(deduplicated downstream by H)"]
K["Same id, diff hash"] --> L["Error + filtered out"]
end
A --> I
A --> K
```
</a>
</details>
<!-- greptile_failed_comments -->
<details open><summary><h3>Comments Outside Diff (1)</h3></summary>
1. `packages/react-core/src/utils/dictionaries/injectHashes.ts`, line
13-40
([link](https://github.com/generaltranslation/gt/blob/100b40afe648c7e2d60756415c1284d39e396b72/packages/react-core/src/utils/dictionaries/injectHashes.ts#L13-L40))
<a href="#"><img alt="P2"
src="https://greptile-static-assets.s3.amazonaws.com/badges/p2.svg?v=9"
align="top"></a> The `id` parameter and `wholeId` variable are now
functionally dead code: `wholeId` is built on every iteration and passed
to recursive calls, but nothing in the recursion chain uses it to affect
the hash output. Passing `injectHashes(dict, 'custom.prefix')` produces
identical hashes to `injectHashes(dict)` (confirmed by the updated
test). The parameter and variable can be removed entirely to avoid
confusion.
<details><summary>Prompt To Fix With AI</summary>
`````markdown
This is a comment left during a code review.
Path: packages/react-core/src/utils/dictionaries/injectHashes.ts
Line: 13-40
Comment:
The `id` parameter and `wholeId` variable are now functionally dead
code: `wholeId` is built on every iteration and passed to recursive
calls, but nothing in the recursion chain uses it to affect the hash
output. Passing `injectHashes(dict, 'custom.prefix')` produces identical
hashes to `injectHashes(dict)` (confirmed by the updated test). The
parameter and variable can be removed entirely to avoid confusion.
How can I resolve this? If you propose a fix, please make it concise.
`````
</details>
<a
href="https://app.greptile.com/ide/claude-code?prompt=This%20is%20a%20comment%20left%20during%20a%20code%20review.%0APath%3A%20packages%2Freact-core%2Fsrc%2Futils%2Fdictionaries%2FinjectHashes.ts%0ALine%3A%2013-40%0A%0AComment%3A%0AThe%20%60id%60%20parameter%20and%20%60wholeId%60%20variable%20are%20now%20functionally%20dead%20code%3A%20%60wholeId%60%20is%20built%20on%20every%20iteration%20and%20passed%20to%20recursive%20calls%2C%20but%20nothing%20in%20the%20recursion%20chain%20uses%20it%20to%20affect%20the%20hash%20output.%20Passing%20%60injectHashes%28dict%2C%20'custom.prefix'%29%60%20produces%20identical%20hashes%20to%20%60injectHashes%28dict%29%60%20%28confirmed%20by%20the%20updated%20test%29.%20The%20parameter%20and%20variable%20can%20be%20removed%20entirely%20to%20avoid%20confusion.%0A%0A%60%60%60suggestion%0Aexport%20function%20injectHashes%28%0A%20%20dictionary%3A%20Dictionary%0A%29%3A%20%7B%20dictionary%3A%20Dictionary%3B%20updateDictionary%3A%20boolean%20%7D%20%7B%0A%20%20let%20updateDictionary%20%3D%20false%3B%0A%20%20Object.entries%28dictionary%29.forEach%28%28%5Bkey%2C%20value%5D%29%20%3D%3E%20%7B%0A%20%20%20%20if%20%28isDictionaryEntry%28value%29%29%20%7B%0A%20%20%20%20%20%20%2F%2F%20eslint-disable-next-line%20prefer-const%0A%20%20%20%20%20%20let%20%7B%20entry%2C%20metadata%20%7D%20%3D%20getEntryAndMetadata%28value%29%3B%0A%20%20%20%20%20%20if%20%28!metadata%3F.%24_hash%29%20%7B%0A%20%20%20%20%20%20%20%20metadata%20%7C%7C%3D%20%7B%7D%3B%0A%20%20%20%20%20%20%20%20metadata.%24_hash%20%3D%20hashSource%28%7B%0A%20%20%20%20%20%20%20%20%20%20source%3A%20indexVars%28entry%29%2C%0A%20%20%20%20%20%20%20%20%20%20...%28metadata%3F.%24context%20%26%26%20%7B%20context%3A%20metadata.%24context%20%7D%29%2C%0A%20%20%20%20%20%20%20%20%20%20...%28metadata%3F.%24maxChars%20!%3D%20null%20%26%26%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20maxChars%3A%20Math.abs%28metadata.%24maxChars%29%2C%0A%20%20%20%20%20%20%20%20%20%20%7D%29%2C%0A%20%20%20%20%20%20%20%20%20%20dataFormat%3A%20'ICU'%2C%0A%20%20%20%20%20%20%20%20%7D%29%3B%0A%20%20%20%20%20%20%20%20set%28dictionary%2C%20key%2C%20%5Bentry%2C%20metadata%5D%29%3B%0A%20%20%20%20%20%20%20%20updateDictionary%20%3D%20true%3B%0A%20%20%20%20%20%20%7D%0A%20%20%20%20%7D%20else%20%7B%0A%20%20%20%20%20%20const%20%7B%20updateDictionary%3A%20updateFlag%20%7D%20%3D%20injectHashes%28value%29%3B%0A%20%20%20%20%20%20updateDictionary%20%3D%20updateDictionary%20%7C%7C%20updateFlag%3B%0A%20%20%20%20%7D%0A%20%20%7D%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=1696&platform=github"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://greptile-static-assets.s3.amazonaws.com/badges/FixInClaudeDark.svg?v=3"><source
media="(prefers-color-scheme: light)"
srcset="https://greptile-static-assets.s3.amazonaws.com/badges/FixInClaude.svg?v=3"><img
alt="Fix in Claude Code"
src="https://greptile-static-assets.s3.amazonaws.com/badges/FixInClaude.svg?v=3"
height="20"></picture></a> <a
href="https://app.greptile.com/api/ide/codex?prompt=IMPORTANT%3A%20Work%20in%20the%20repository%20%22generaltranslation%2Fgt%22%20on%20the%20existing%20branch%20%22bg%2Fhash-translation-keys%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%2Fhash-translation-keys%22.%0A%0AThis%20is%20a%20comment%20left%20during%20a%20code%20review.%0APath%3A%20packages%2Freact-core%2Fsrc%2Futils%2Fdictionaries%2FinjectHashes.ts%0ALine%3A%2013-40%0A%0AComment%3A%0AThe%20%60id%60%20parameter%20and%20%60wholeId%60%20variable%20are%20now%20functionally%20dead%20code%3A%20%60wholeId%60%20is%20built%20on%20every%20iteration%20and%20passed%20to%20recursive%20calls%2C%20but%20nothing%20in%20the%20recursion%20chain%20uses%20it%20to%20affect%20the%20hash%20output.%20Passing%20%60injectHashes%28dict%2C%20'custom.prefix'%29%60%20produces%20identical%20hashes%20to%20%60injectHashes%28dict%29%60%20%28confirmed%20by%20the%20updated%20test%29.%20The%20parameter%20and%20variable%20can%20be%20removed%20entirely%20to%20avoid%20confusion.%0A%0A%60%60%60suggestion%0Aexport%20function%20injectHashes%28%0A%20%20dictionary%3A%20Dictionary%0A%29%3A%20%7B%20dictionary%3A%20Dictionary%3B%20updateDictionary%3A%20boolean%20%7D%20%7B%0A%20%20let%20updateDictionary%20%3D%20false%3B%0A%20%20Object.entries%28dictionary%29.forEach%28%28%5Bkey%2C%20value%5D%29%20%3D%3E%20%7B%0A%20%20%20%20if%20%28isDictionaryEntry%28value%29%29%20%7B%0A%20%20%20%20%20%20%2F%2F%20eslint-disable-next-line%20prefer-const%0A%20%20%20%20%20%20let%20%7B%20entry%2C%20metadata%20%7D%20%3D%20getEntryAndMetadata%28value%29%3B%0A%20%20%20%20%20%20if%20%28!metadata%3F.%24_hash%29%20%7B%0A%20%20%20%20%20%20%20%20metadata%20%7C%7C%3D%20%7B%7D%3B%0A%20%20%20%20%20%20%20%20metadata.%24_hash%20%3D%20hashSource%28%7B%0A%20%20%20%20%20%20%20%20%20%20source%3A%20indexVars%28entry%29%2C%0A%20%20%20%20%20%20%20%20%20%20...%28metadata%3F.%24context%20%26%26%20%7B%20context%3A%20metadata.%24context%20%7D%29%2C%0A%20%20%20%20%20%20%20%20%20%20...%28metadata%3F.%24maxChars%20!%3D%20null%20%26%26%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20maxChars%3A%20Math.abs%28metadata.%24maxChars%29%2C%0A%20%20%20%20%20%20%20%20%20%20%7D%29%2C%0A%20%20%20%20%20%20%20%20%20%20dataFormat%3A%20'ICU'%2C%0A%20%20%20%20%20%20%20%20%7D%29%3B%0A%20%20%20%20%20%20%20%20set%28dictionary%2C%20key%2C%20%5Bentry%2C%20metadata%5D%29%3B%0A%20%20%20%20%20%20%20%20updateDictionary%20%3D%20true%3B%0A%20%20%20%20%20%20%7D%0A%20%20%20%20%7D%20else%20%7B%0A%20%20%20%20%20%20const%20%7B%20updateDictionary%3A%20updateFlag%20%7D%20%3D%20injectHashes%28value%29%3B%0A%20%20%20%20%20%20updateDictionary%20%3D%20updateDictionary%20%7C%7C%20updateFlag%3B%0A%20%20%20%20%7D%0A%20%20%7D%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=1696&platform=github"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://greptile-static-assets.s3.amazonaws.com/badges/FixInCodexDark.svg?v=3"><source
media="(prefers-color-scheme: light)"
srcset="https://greptile-static-assets.s3.amazonaws.com/badges/FixInCodex.svg?v=3"><img
alt="Fix in Codex"
src="https://greptile-static-assets.s3.amazonaws.com/badges/FixInCodex.svg?v=3"
height="20"></picture></a>
</details>
<!-- /greptile_failed_comments -->
<sub>Reviews (3): Last reviewed commit: ["fix: remove ignored compiler
hash id
par..."](b193060)
| [Re-trigger
Greptile](https://app.greptile.com/api/retrigger?id=40680642)</sub>
<!-- /greptile_comment -->
…1709) `getDomain` was scaffolded but never wired to any runtime consumer — there's no `getDomain()` request helper, nothing reads `_GENERALTRANSLATION_CUSTOM_GET_DOMAIN_ENABLED`, and no package imports it (grep-verified). Removes: the throw-only `internal/_getDomain` stub + its `./internal/_getDomain` export (and typesVersions/paths), the `getDomainPath` config prop, the `getDomain` entries in the request-function registry/aliases/config-key map, and the dead build env var. `getLocale`/`getRegion` are untouched. Static (`getStaticDomain`) + experimental references are left for the follow-up SSG/experimental cleanup. gt-next builds; 191 tests pass (updated the env-var-completeness test). Part of the odysseus dead-code sweep. <!-- greptile_comment --> <h3>Greptile Summary</h3> This PR removes dead-code scaffolding for a `getDomain` request function that was never wired to a runtime consumer in `gt-next`. Every reference to `getDomain` in the request-function registry, config-key map, build env var, package exports, type declarations, and the throw-only stub module is cleaned up in one coherent sweep. - Deletes `packages/next/src/internal/_getDomain.ts` (stub that always threw), removes its `package.json` exports/typesVersions/paths entries, and drops the `./internal/_getDomain` size-limit entry. - Removes `getDomainPath` from `BaseWithGTConfigProps`, `REQUEST_FUNCTION_TO_CONFIG_KEY`, `REQUEST_FUNCTION_ALIASES`, `REQUEST_FUNCTIONS`, and the `_GENERALTRANSLATION_CUSTOM_GET_DOMAIN_ENABLED` build env var from `withGTConfig`. - Updates the env-var-completeness test to match; `getStaticDomain` and experimental-feature references are explicitly deferred to a follow-up cleanup. <details open><summary><h3>Confidence Score: 5/5</h3></summary> Safe to merge — removes code that was never reachable at runtime. Every deleted symbol (getDomain registry entry, getDomainPath prop, _getDomain stub, build env var, package exports) was scaffolding that was never consumed by any runtime path. The remaining getLocale/getRegion plumbing is untouched, getStaticDomain references are intentionally preserved, and the test suite was updated to match. There are no behavior changes, no API surface breaks for published consumers, and no missing cleanup in the changed files. No files require special attention. </details> <details><summary><h3>Important Files Changed</h3></summary> | Filename | Overview | |----------|----------| | packages/next/src/internal/_getDomain.ts | Deleted — throw-only stub that was never called by any consumer; safe to remove. | | packages/next/package.json | Removes the ./internal/_getDomain export, typesVersions, and paths entry; consistent with the deleted source file. | | packages/next/src/config-dir/props/withGTConfigProps.ts | Removes getDomainPath from BaseWithGTConfigProps and REQUEST_FUNCTION_TO_CONFIG_KEY; clean removal. | | packages/next/src/config-dir/utils/resolveRequestFunctionPaths.ts | Removes getDomain from REQUEST_FUNCTION_ALIASES; getStaticDomain alias is correctly preserved. | | packages/next/src/config.ts | Removes the getDomainPath JSDoc param and _GENERALTRANSLATION_CUSTOM_GET_DOMAIN_ENABLED env-var injection; static/experimental variants left intact. | | packages/next/src/request/types.ts | Removes getDomain from REQUEST_FUNCTIONS tuple; RequestFunctions type now correctly reflects only getLocale and getRegion. | | packages/next/src/__tests__/config.test.ts | Drops _GENERALTRANSLATION_CUSTOM_GET_DOMAIN_ENABLED from the env-var completeness assertion; test remains valid. | | .changeset/trim-next-getdomain.md | Patch-level changeset with accurate description of the removed scaffolding. | | .size-limit.cjs | Removes size-limit entry for the now-deleted internal/_getDomain export; no other changes. | </details> <details><summary><h3>Flowchart</h3></summary> <a href="#gh-light-mode-only"> ```mermaid %%{init: {'theme': 'neutral'}}%% flowchart TD A[withGTConfig] --> B{resolveRequestFunctionPaths} B --> C[getLocale path] B --> D[getRegion path] B -.->|REMOVED| E[getDomain path] C --> F[_GENERALTRANSLATION_CUSTOM_GET_LOCALE_ENABLED] D --> G[_GENERALTRANSLATION_CUSTOM_GET_REGION_ENABLED] E -.->|REMOVED| H[_GENERALTRANSLATION_CUSTOM_GET_DOMAIN_ENABLED] B --> I[getStaticLocale path] B --> J[getStaticRegion path] B --> K[getStaticDomain path] I --> L[_GENERALTRANSLATION_STATIC_GET_LOCALE_ENABLED] J --> M[_GENERALTRANSLATION_STATIC_GET_REGION_ENABLED] K --> N[_GENERALTRANSLATION_STATIC_GET_DOMAIN_ENABLED] style E fill:#ffcccc,color:#800000 style H fill:#ffcccc,color:#800000 ``` </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[withGTConfig] --> B{resolveRequestFunctionPaths} B --> C[getLocale path] B --> D[getRegion path] B -.->|REMOVED| E[getDomain path] C --> F[_GENERALTRANSLATION_CUSTOM_GET_LOCALE_ENABLED] D --> G[_GENERALTRANSLATION_CUSTOM_GET_REGION_ENABLED] E -.->|REMOVED| H[_GENERALTRANSLATION_CUSTOM_GET_DOMAIN_ENABLED] B --> I[getStaticLocale path] B --> J[getStaticRegion path] B --> K[getStaticDomain path] I --> L[_GENERALTRANSLATION_STATIC_GET_LOCALE_ENABLED] J --> M[_GENERALTRANSLATION_STATIC_GET_REGION_ENABLED] K --> N[_GENERALTRANSLATION_STATIC_GET_DOMAIN_ENABLED] style E fill:#ffcccc,color:#800000 style H fill:#ffcccc,color:#800000 ``` </a> </details> <sub>Reviews (2): Last reviewed commit: ["chore: drop gt-next/internal/\_getDomain ..."](803f44f) | [Re-trigger Greptile](https://app.greptile.com/api/retrigger?id=40689026)</sub> <!-- /greptile_comment -->
…tion (#1712) Removes the long-`@deprecated`, runtime-dead SSG/experimental config surface (major version): - `experimentalEnableSSG`, `experimentalLocaleResolution`/`Param`, `disableSSGWarnings`, `getStatic*Path` doc props - `STATIC_REQUEST_FUNCTIONS`/`getStatic*` + their `internal/static/*` aliases (which **never existed**) - `plugin/checks/ssgChecks.ts` (whole file) + the experimental branch of `cacheComponentsChecks` - the now-unused error/warning builders in `errors/ssg.ts` and `errors/cacheComponents.ts` - the build env vars `_GENERALTRANSLATION_ENABLE_SSG`, `_GENERALTRANSLATION_EXPERIMENTAL_LOCALE_RESOLUTION[_PARAM]`, `_GENERALTRANSLATION_STATIC_GET_*_ENABLED` — verified **write-only** (never read at runtime) **Kept** (live): the cacheComponents checks, `createCacheComponentsMissingRequestFunctionsWarning`, `cacheComponentsNonLocalTranslationsWarning`, and `noLocalesCouldBeDeterminedWarning`. −276 LOC. gt-next builds; 190 tests pass. >⚠️ **Stacked on #1709** (getDomain removal) since they touch overlapping config regions. Base will be retargeted to `odysseus` once #1709 merges. <!-- greptile_comment --> <h3>Greptile Summary</h3> This PR removes the long-deprecated SSG and experimental locale-resolution code surface from `gt-next`. All removed symbols (`experimentalEnableSSG`, `experimentalLocaleResolution`/`Param`, `STATIC_REQUEST_FUNCTIONS`/`StaticRequestFunctions`, `getStatic*` paths) were verified write-only at runtime before deletion. - Deletes `ssgChecks.ts` and its error/warning builders in `errors/ssg.ts` and `errors/cacheComponents.ts`, trims the `cacheComponentsChecks` signature (drops `mergedConfig`), removes three static-function webpack/turbopack aliases and six dead build env vars, and cleans up the corresponding type/default exports. - Retains the live `cacheComponentsChecks` logic, `noLocalesCouldBeDeterminedWarning`, and the `createCacheComponentsMissingRequestFunctionsWarning` path, all of which continue to work unchanged. <details open><summary><h3>Confidence Score: 5/5</h3></summary> Safe to merge — the change is a clean removal of runtime-dead code with no functional regressions to the live paths. All removed symbols were verified write-only at runtime (never read back after assignment). No references to deleted types, constants, or functions remain in the codebase. The live cacheComponents checks, noLocalesCouldBeDeterminedWarning, and webpack/turbopack alias plumbing for getLocale/getRegion are fully intact. Tests were updated to match and 190 tests continue to pass. No files require special attention. </details> <details><summary><h3>Important Files Changed</h3></summary> | Filename | Overview | |----------|----------| | packages/next/src/plugin/checks/ssgChecks.ts | File deleted entirely; all callers in config.ts have been removed and no remaining references exist. | | packages/next/src/plugin/checks/cacheComponentsChecks.ts | Removed the experimentalLocaleResolution branch and mergedConfig parameter; live checks are intact and correct. | | packages/next/src/config-dir/utils/resolveRequestFunctionPaths.ts | Removed STATIC_REQUEST_FUNCTIONS loop and static aliases; remaining REQUEST_FUNCTIONS loop is unchanged. | | packages/next/src/errors/ssg.ts | Stripped all SSG-specific error/warning builders; only noLocalesCouldBeDeterminedWarning (still used at runtime) remains. | | packages/next/src/errors/cacheComponents.ts | Removed experimentalLocaleResolution-related errors/warnings; retained active warning builders. | | packages/next/src/request/types.ts | Removed STATIC_REQUEST_FUNCTIONS constant and StaticRequestFunctions type; no remaining imports. | | packages/next/src/utils/constants.ts | File deleted; its only export was used only in defaultWithGTConfigProps.ts, which has been updated. | | packages/next/src/config.ts | Removed ssgChecks call, six static env vars, and mergedConfig arg from cacheComponentsChecks; remaining build pipeline is correct. | | packages/next/src/config-dir/props/withGTConfigProps.ts | Removed three deprecated experimental props from BaseWithGTConfigProps. | | packages/next/src/config-dir/props/defaultWithGTConfigProps.ts | Removed three deprecated default values and their import; type updated consistently. | | packages/next/src/__tests__/config.test.ts | Test updated to remove six deleted env var keys; no coverage gaps on retained env vars. | | packages/next/src/plugin/checks/__tests__/cacheComponentsChecks.test.ts | Removed experimentalLocaleResolution test case; remaining tests cover the live warning paths. | </details> <details><summary><h3>Flowchart</h3></summary> <a href="#gh-light-mode-only"> ```mermaid %%{init: {'theme': 'neutral'}}%% flowchart TD A["withGTConfig()"] --> B["resolveRequestFunctionPaths()"] B -->|"getLocale / getRegion only"| C["requestFunctionPaths"] A --> D["cacheComponentsChecks()"] D --> E{"cacheComponents enabled?"} E -->|Yes| F["warn if missing getLocale / getRegion"] E -->|Yes| G["warn if no local translations"] E -->|No| H["skip"] A --> I["emit env vars"] ``` </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["withGTConfig()"] --> B["resolveRequestFunctionPaths()"] B -->|"getLocale / getRegion only"| C["requestFunctionPaths"] A --> D["cacheComponentsChecks()"] D --> E{"cacheComponents enabled?"} E -->|Yes| F["warn if missing getLocale / getRegion"] E -->|Yes| G["warn if no local translations"] E -->|No| H["skip"] A --> I["emit env vars"] ``` </a> </details> <sub>Reviews (2): Last reviewed commit: ["refactor(gt-next): remove deprecated SSG..."](6679ac7) | [Re-trigger Greptile](https://app.greptile.com/api/retrigger?id=40690515)</sub> <!-- /greptile_comment -->
## Summary - remove deprecated I18nCache lifecycle constructor callbacks and lifecycle adapter files - trim cache subscription constants/types down to translations-cache-miss - simplify cache managers by removing unused lifecycle callback plumbing - update React/React Core/Node params types and tests for the narrower event API ## Test plan - pnpm --filter gt-i18n test -- --runInBand - pnpm --filter gt-i18n typecheck - git diff --check <!-- greptile_comment --> <h3>Greptile Summary</h3> This PR strips the deprecated lifecycle callback API from `gt-i18n`'s cache layer, leaving `translations-cache-miss` as the sole subscription event. All lifecycle plumbing files are deleted and the constructor params types are de-generified throughout. - Deletes four `lifecycle-hooks/` files (`createLifecycleCallbacks`, `subscribeLifecycleCallbacks`, their types, and their tests) and removes all hit/miss event constants except `TRANSLATIONS_CACHE_MISS_EVENT_NAME`. - Simplifies `TranslationsCache`, `DictionaryCache`, `ResourceCache`, and `LocalesCache` constructors by replacing the `lifecycle` object with a single `onMiss` / `onTranslationsCacheMiss` callback where needed. - Updates `I18nCacheConstructorParams` to be non-generic (drops the `TranslationValue` param and the deprecated `lifecycle` field), and propagates that simplification to `ReactI18nCacheParams`, `BrowserI18nCacheParams`, and `InitializeGTParams`. <details open><summary><h3>Confidence Score: 4/5</h3></summary> The change is a clean deletion of deprecated code with no functional regressions on the surviving event path. All lifecycle plumbing is fully removed with no dangling imports. The sole surviving event (translations-cache-miss) is correctly wired through the generic type chain. The only open question is whether a patch bump is appropriate for the removal of previously-exported symbols from the internal path — consumers relying on those names would see a compile break on upgrade. .changeset/trim-i18n-cache-events.md — confirm the bump type matches your versioning policy for deprecated internal API removals. </details> <details><summary><h3>Important Files Changed</h3></summary> | Filename | Overview | |----------|----------| | packages/i18n/src/i18n-cache/event-subscription/types.ts | Trims 8 event constants and the BaseEvent/EventName aliases; renames BaseEvent to EventMap = object (loosens constraint from Record<string, unknown>). Only TRANSLATIONS_CACHE_MISS_EVENT_NAME and I18nEvents survive. | | packages/i18n/src/i18n-cache/I18nCache.ts | Constructor de-generified to I18nCacheConstructorParams (non-generic); lifecycle wiring removed and replaced with a direct onTranslationsCacheMiss lambda that emits the sole remaining event. Type-safe through the TranslationValue generic on the class itself. | | packages/i18n/src/i18n-cache/translations-manager/LocalesCache.ts | Replaces the I18nCacheLifecycleCallbacks bundle with a single onTranslationsCacheMiss?: LocalesTranslationsCacheMissCallback. ResourceCache calls for translations/dictionaries no longer receive lifecycle objects. | | packages/i18n/src/i18n-cache/translations-manager/TranslationsCache.ts | Replaces the full LifecycleParam with a simple onMiss?: TranslationsCacheMissCallback. Only the miss path is retained; the hit callback is removed. | | packages/i18n/src/i18n-cache/translations-manager/ResourceCache.ts | Removes lifecycle param from constructor and deletes onHit/onMiss call sites. Cache get/load behaviour is unchanged. | | packages/i18n/src/i18n-cache/types.ts | I18nCacheConstructorParams loses its TranslationValue generic and the lifecycle field; LifecycleCallbacks removed from re-exports. | | packages/i18n/src/internal.ts | Narrows exported event constants from four to one (TRANSLATIONS_CACHE_MISS_EVENT_NAME). Consumers using the removed constants via gt-i18n/internal will see a breaking change. | | .changeset/trim-i18n-cache-events.md | Marks gt-i18n as patch. Removes exported symbols that were @deprecated; whether patch vs minor is correct depends on the project's semver policy for internal paths. | </details> <details><summary><h3>Sequence Diagram</h3></summary> <a href="#gh-light-mode-only"> ```mermaid %%{init: {'theme': 'neutral'}}%% sequenceDiagram participant App participant I18nCache participant LocalesCache participant TranslationsCache App->>I18nCache: subscribe('translations-cache-miss', cb) App->>I18nCache: lookupTranslationWithFallback(locale, msg, opts) I18nCache->>LocalesCache: getOrLoadTranslations(locale) LocalesCache->>TranslationsCache: lookupOrTranslate(key) Note over TranslationsCache: remote fetch resolves TranslationsCache->>LocalesCache: onMiss(hash, translation) LocalesCache->>I18nCache: onTranslationsCacheMiss(locale, hash, translation) I18nCache->>I18nCache: "emit('translations-cache-miss', {locale, hash, translation})" I18nCache->>App: "cb({locale, hash, translation})" ``` </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 App participant I18nCache participant LocalesCache participant TranslationsCache App->>I18nCache: subscribe('translations-cache-miss', cb) App->>I18nCache: lookupTranslationWithFallback(locale, msg, opts) I18nCache->>LocalesCache: getOrLoadTranslations(locale) LocalesCache->>TranslationsCache: lookupOrTranslate(key) Note over TranslationsCache: remote fetch resolves TranslationsCache->>LocalesCache: onMiss(hash, translation) LocalesCache->>I18nCache: onTranslationsCacheMiss(locale, hash, translation) I18nCache->>I18nCache: "emit('translations-cache-miss', {locale, hash, translation})" I18nCache->>App: "cb({locale, hash, translation})" ``` </a> </details> <a href="https://app.greptile.com/ide/claude-code?prompt=Fix%20the%20following%201%20code%20review%20issue.%20Work%20through%20them%20one%20at%20a%20time%2C%20proposing%20concise%20fixes.%0A%0A---%0A%0A%23%23%23%20Issue%201%20of%201%0A.changeset%2Ftrim-i18n-cache-events.md%3A1-3%0AThe%20changeset%20is%20marked%20as%20%60patch%60%2C%20but%20this%20removes%20three%20previously-exported%20symbols%20from%20%60gt-i18n%2Finternal%60%20%28%60LOCALES_CACHE_MISS_EVENT_NAME%60%2C%20%60DICTIONARY_CACHE_MISS_EVENT_NAME%60%2C%20%60LOCALES_DICTIONARY_CACHE_MISS_EVENT_NAME%60%29%20and%20one%20from%20%60gt-i18n%2Finternal%2Ftypes%60%20%28%60LifecycleCallbacks%60%29%2C%20along%20with%20the%20%60lifecycle%60%20field%20on%20%60I18nCacheConstructorParams%60.%20Any%20consumer%20that%20imported%20those%20symbols%20%E2%80%94%20even%20though%20they%20were%20%60%40deprecated%60%20%E2%80%94%20will%20get%20a%20compile%20error%20after%20upgrade.%20A%20%60minor%60%20bump%20would%20be%20the%20safer%20choice%20to%20signal%20removal%20of%20previously-published%20API%20surface.%0A%0A%60%60%60suggestion%0A---%0A%22gt-i18n%22%3A%20minor%0A---%0A%60%60%60%0A%0A&repo=generaltranslation%2Fgt&pr=1716&platform=github"><picture><source media="(prefers-color-scheme: dark)" srcset="https://greptile-static-assets.s3.amazonaws.com/badges/FixAllInClaudeDark.svg?v=3"><source media="(prefers-color-scheme: light)" srcset="https://greptile-static-assets.s3.amazonaws.com/badges/FixAllInClaude.svg?v=3"><img alt="Fix All in Claude Code" src="https://greptile-static-assets.s3.amazonaws.com/badges/FixAllInClaude.svg?v=3" height="20"></picture></a> <a href="https://app.greptile.com/api/ide/codex?prompt=IMPORTANT%3A%20Work%20in%20the%20repository%20%22generaltranslation%2Fgt%22%20on%20the%20existing%20branch%20%22bg%2Fodysseus-trim-i18n-cache-events%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-trim-i18n-cache-events%22.%0A%0AFix%20the%20following%201%20code%20review%20issue.%20Work%20through%20them%20one%20at%20a%20time%2C%20proposing%20concise%20fixes.%0A%0A---%0A%0A%23%23%23%20Issue%201%20of%201%0A.changeset%2Ftrim-i18n-cache-events.md%3A1-3%0AThe%20changeset%20is%20marked%20as%20%60patch%60%2C%20but%20this%20removes%20three%20previously-exported%20symbols%20from%20%60gt-i18n%2Finternal%60%20%28%60LOCALES_CACHE_MISS_EVENT_NAME%60%2C%20%60DICTIONARY_CACHE_MISS_EVENT_NAME%60%2C%20%60LOCALES_DICTIONARY_CACHE_MISS_EVENT_NAME%60%29%20and%20one%20from%20%60gt-i18n%2Finternal%2Ftypes%60%20%28%60LifecycleCallbacks%60%29%2C%20along%20with%20the%20%60lifecycle%60%20field%20on%20%60I18nCacheConstructorParams%60.%20Any%20consumer%20that%20imported%20those%20symbols%20%E2%80%94%20even%20though%20they%20were%20%60%40deprecated%60%20%E2%80%94%20will%20get%20a%20compile%20error%20after%20upgrade.%20A%20%60minor%60%20bump%20would%20be%20the%20safer%20choice%20to%20signal%20removal%20of%20previously-published%20API%20surface.%0A%0A%60%60%60suggestion%0A---%0A%22gt-i18n%22%3A%20minor%0A---%0A%60%60%60%0A%0A&repo=generaltranslation%2Fgt&pr=1716&platform=github"><picture><source media="(prefers-color-scheme: dark)" srcset="https://greptile-static-assets.s3.amazonaws.com/badges/FixAllInCodexDark.svg?v=3"><source media="(prefers-color-scheme: light)" srcset="https://greptile-static-assets.s3.amazonaws.com/badges/FixAllInCodex.svg?v=3"><img alt="Fix All in Codex" src="https://greptile-static-assets.s3.amazonaws.com/badges/FixAllInCodex.svg?v=3" height="20"></picture></a> <details><summary>Prompt To Fix All With AI</summary> `````markdown Fix the following 1 code review issue. Work through them one at a time, proposing concise fixes. --- ### Issue 1 of 1 .changeset/trim-i18n-cache-events.md:1-3 The changeset is marked as `patch`, but this removes three previously-exported symbols from `gt-i18n/internal` (`LOCALES_CACHE_MISS_EVENT_NAME`, `DICTIONARY_CACHE_MISS_EVENT_NAME`, `LOCALES_DICTIONARY_CACHE_MISS_EVENT_NAME`) and one from `gt-i18n/internal/types` (`LifecycleCallbacks`), along with the `lifecycle` field on `I18nCacheConstructorParams`. Any consumer that imported those symbols — even though they were `@deprecated` — will get a compile error after upgrade. A `minor` bump would be the safer choice to signal removal of previously-published API surface. ```suggestion --- "gt-i18n": minor --- ``` ````` </details> <sub>Reviews (1): Last reviewed commit: ["refactor(gt-i18n): trim cache lifecycle ..."](54e113a) | [Re-trigger Greptile](https://app.greptile.com/api/retrigger?id=40704542)</sub> > Greptile also left **1 inline comment** on this PR. <!-- /greptile_comment -->
## Summary
- update the cacheComponents warning to say explicit request functions
are required during prerendering
- remove stale wording about automatic root parameter detection
## Tests
- pnpm --filter gt-next test --
src/plugin/checks/__tests__/cacheComponentsChecks.test.ts
- git diff --check
<!-- greptile_comment -->
<h3>Greptile Summary</h3>
This PR updates one error message string in the `cacheComponents`
diagnostics module, replacing stale deprecation wording with a clearer
explanation of why explicit request functions are required during
prerendering.
- The `wayOut` field in
`createCacheComponentsMissingRequestFunctionsWarning` now reads
"cacheComponents requires explicit request functions because request
values cannot be inferred safely during prerendering," making it more
actionable than the previous reference to deprecated automatic root
parameter detection.
<details open><summary><h3>Confidence Score: 5/5</h3></summary>
This is a one-line string change in a diagnostic helper — no logic is
altered and no runtime behavior changes.
The only change is the wayOut string inside an error-diagnostic factory.
The new wording is more accurate and actionable than what it replaces,
and no code path, type signature, or behavior is affected.
No files require special attention.
</details>
<details><summary><h3>Important Files Changed</h3></summary>
| Filename | Overview |
|----------|----------|
| packages/next/src/errors/cacheComponents.ts | Single-line update to
the `wayOut` field of the missing-request-functions diagnostic —
replaces the stale "Automatic root parameter detection is deprecated"
message with a clearer explanation about prerendering safety
requirements. |
</details>
<details><summary><h3>Flowchart</h3></summary>
<a href="#gh-light-mode-only">
```mermaid
%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[cacheComponents enabled] --> B{Explicit request functions configured?}
B -- Yes --> C[Proceed with prerendering]
B -- No --> D[createCacheComponentsMissingRequestFunctionsWarning]
D --> E["whatHappened: cacheComponents enabled but getLocale()/getRegion() not configured"]
D --> F["wayOut: explicit request functions required — values cannot be inferred during prerendering"]
D --> G["fix: add getLocale.ts / getRegion.ts or configure getLocalePath / getRegionPath"]
```
</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[cacheComponents enabled] --> B{Explicit request functions configured?}
B -- Yes --> C[Proceed with prerendering]
B -- No --> D[createCacheComponentsMissingRequestFunctionsWarning]
D --> E["whatHappened: cacheComponents enabled but getLocale()/getRegion() not configured"]
D --> F["wayOut: explicit request functions required — values cannot be inferred during prerendering"]
D --> G["fix: add getLocale.ts / getRegion.ts or configure getLocalePath / getRegionPath"]
```
</a>
</details>
<sub>Reviews (1): Last reviewed commit: ["fix: clarify cache components
request
fu..."](41aa42b)
| [Re-trigger
Greptile](https://app.greptile.com/api/retrigger?id=40885818)</sub>
<!-- /greptile_comment -->
## Summary
- remove the deprecated `internalInitializeGTSPA` implementation from
`@generaltranslation/react-core/pure`
- stop re-exporting `internalInitializeGTSPA` from `gt-react`
server/types entrypoints
- keep `gt-react`'s public `initializeGTSPA` browser initializer intact
- add a changeset for `@generaltranslation/react-core` and `gt-react`
## Tests
- pnpm --filter @generaltranslation/react-core typecheck
- pnpm --filter @generaltranslation/react-core test
- pnpm --filter gt-react test
- pnpm --filter @generaltranslation/react-core build
- pnpm --filter gt-react build
- git diff --check
## Notes
- `pnpm --filter gt-react typecheck` still fails on the pre-existing
`src/i18n-cache/BrowserI18nCache.ts(98,22): Property env does not exist
on type ImportMeta` issue; this line is unchanged from
`origin/odysseus`.
<!-- greptile_comment -->
<h3>Greptile Summary</h3>
This PR removes the deprecated `internalInitializeGTSPA` function from
`@generaltranslation/react-core/pure` and stops re-exporting it from
`gt-react`'s server and types entrypoints. The public `initializeGTSPA`
in `gt-react` is left intact as the supported replacement.
- Deletes `packages/react-core/src/setup/initializeGTSPA.ts` and its
export from `pure.ts`.
- Removes the `internalInitializeGTSPA` re-export from `gt-react`'s
`index.server.ts` and `index.types.ts` surfaces.
- Adds a changeset marking both `@generaltranslation/react-core` and
`gt-react` as `patch`.
<details open><summary><h3>Confidence Score: 4/5</h3></summary>
Safe to merge — the deleted code had no remaining callers in the repo
and the public initializeGTSPA surface is unaffected.
The removal is clean: no references to internalInitializeGTSPA survive
in the codebase after the deletion, and the gt-react public SPA
initializer is untouched. The only open question is whether the patch
bump in the changeset adequately signals to external consumers that a
previously-published symbol has been removed.
.changeset/remove-react-core-spa-initializer.md — worth a second look on
the bump type choice.
</details>
<details><summary><h3>Important Files Changed</h3></summary>
| Filename | Overview |
|----------|----------|
| .changeset/remove-react-core-spa-initializer.md | Documents the
removal as a patch bump; removing a re-exported symbol from a published
package is technically a breaking change regardless of deprecation. |
| packages/react-core/src/pure.ts | Drops the internalInitializeGTSPA
export; the rest of the file is untouched and correct. |
| packages/react-core/src/setup/initializeGTSPA.ts | Deprecated file
fully deleted; no remaining references in the codebase. |
| packages/react/src/index.server.ts | Removes the
internalInitializeGTSPA re-export; the public initializeGTSPA is still
re-exported from its own source file. |
| packages/react/src/index.types.ts | Mirrors the server entrypoint
change; internalInitializeGTSPA removed, initializeGTSPA (public)
remains. |
</details>
<details><summary><h3>Flowchart</h3></summary>
<a href="#gh-light-mode-only">
```mermaid
%%{init: {'theme': 'neutral'}}%%
flowchart TD
A["gt-react index.client.ts"] -->|"initializeGTSPA (kept)"| B["packages/react/src/setup/initializeGTSPA.ts"]
C["gt-react index.server.ts"] -->|"initializeGTSPA (kept)"| B
D["gt-react index.types.ts"] -->|"initializeGTSPA (kept)"| B
B --> E["@generaltranslation/react-core/pure"]
E --> F["BrowserI18nCache / I18nStore / etc."]
G["@generaltranslation/react-core/pure"] -->|"internalInitializeGTSPA REMOVED"| H["packages/react-core/src/setup/initializeGTSPA.ts DELETED"]
I["gt-react index.server.ts"] -->|"internalInitializeGTSPA re-export REMOVED"| G
J["gt-react index.types.ts"] -->|"internalInitializeGTSPA re-export REMOVED"| G
```
</a>
<a href="#gh-dark-mode-only">
```mermaid
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
A["gt-react index.client.ts"] -->|"initializeGTSPA (kept)"| B["packages/react/src/setup/initializeGTSPA.ts"]
C["gt-react index.server.ts"] -->|"initializeGTSPA (kept)"| B
D["gt-react index.types.ts"] -->|"initializeGTSPA (kept)"| B
B --> E["@generaltranslation/react-core/pure"]
E --> F["BrowserI18nCache / I18nStore / etc."]
G["@generaltranslation/react-core/pure"] -->|"internalInitializeGTSPA REMOVED"| H["packages/react-core/src/setup/initializeGTSPA.ts DELETED"]
I["gt-react index.server.ts"] -->|"internalInitializeGTSPA re-export REMOVED"| G
J["gt-react index.types.ts"] -->|"internalInitializeGTSPA re-export REMOVED"| G
```
</a>
</details>
<a
href="https://app.greptile.com/ide/claude-code?prompt=Fix%20the%20following%201%20code%20review%20issue.%20Work%20through%20them%20one%20at%20a%20time%2C%20proposing%20concise%20fixes.%0A%0A---%0A%0A%23%23%23%20Issue%201%20of%201%0A.changeset%2Fremove-react-core-spa-initializer.md%3A2-3%0A**Changeset%20bump%20type%20may%20be%20too%20low**%0A%0ABoth%20packages%20are%20marked%20as%20%60patch%60%2C%20but%20removing%20a%20re-exported%20symbol%20from%20a%20published%20package%20is%20a%20breaking%20change%20under%20semver%20%E2%80%94%20any%20downstream%20consumer%20that%20imported%20%60internalInitializeGTSPA%60%20directly%20from%20%60gt-react%60's%20server%20or%20types%20entrypoints%20will%20get%20a%20silent%20compile-time%20break%20after%20upgrading.%20The%20%60internal%60%20prefix%20and%20%60%40deprecated%60%20tag%20strongly%20signal%20this%20wasn't%20meant%20for%20external%20use%2C%20but%20they%20don't%20prevent%20consumption.%20If%20there's%20a%20published%20version%20of%20%60gt-react%60%20that%20exposed%20this%20symbol%2C%20consider%20bumping%20to%20%60minor%60%20%28or%20%60major%60%20if%20following%20strict%20semver%29%20to%20surface%20the%20removal%20clearly%20in%20changelogs%20and%20dependency%20ranges.%0A%0A&repo=generaltranslation%2Fgt&pr=1722&platform=github"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://greptile-static-assets.s3.amazonaws.com/badges/FixAllInClaudeDark.svg?v=3"><source
media="(prefers-color-scheme: light)"
srcset="https://greptile-static-assets.s3.amazonaws.com/badges/FixAllInClaude.svg?v=3"><img
alt="Fix All in Claude Code"
src="https://greptile-static-assets.s3.amazonaws.com/badges/FixAllInClaude.svg?v=3"
height="20"></picture></a> <a
href="https://app.greptile.com/api/ide/codex?prompt=IMPORTANT%3A%20Work%20in%20the%20repository%20%22generaltranslation%2Fgt%22%20on%20the%20existing%20branch%20%22bg%2Fremove-react-core-spa-initializer%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%2Fremove-react-core-spa-initializer%22.%0A%0AFix%20the%20following%201%20code%20review%20issue.%20Work%20through%20them%20one%20at%20a%20time%2C%20proposing%20concise%20fixes.%0A%0A---%0A%0A%23%23%23%20Issue%201%20of%201%0A.changeset%2Fremove-react-core-spa-initializer.md%3A2-3%0A**Changeset%20bump%20type%20may%20be%20too%20low**%0A%0ABoth%20packages%20are%20marked%20as%20%60patch%60%2C%20but%20removing%20a%20re-exported%20symbol%20from%20a%20published%20package%20is%20a%20breaking%20change%20under%20semver%20%E2%80%94%20any%20downstream%20consumer%20that%20imported%20%60internalInitializeGTSPA%60%20directly%20from%20%60gt-react%60's%20server%20or%20types%20entrypoints%20will%20get%20a%20silent%20compile-time%20break%20after%20upgrading.%20The%20%60internal%60%20prefix%20and%20%60%40deprecated%60%20tag%20strongly%20signal%20this%20wasn't%20meant%20for%20external%20use%2C%20but%20they%20don't%20prevent%20consumption.%20If%20there's%20a%20published%20version%20of%20%60gt-react%60%20that%20exposed%20this%20symbol%2C%20consider%20bumping%20to%20%60minor%60%20%28or%20%60major%60%20if%20following%20strict%20semver%29%20to%20surface%20the%20removal%20clearly%20in%20changelogs%20and%20dependency%20ranges.%0A%0A&repo=generaltranslation%2Fgt&pr=1722&platform=github"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://greptile-static-assets.s3.amazonaws.com/badges/FixAllInCodexDark.svg?v=3"><source
media="(prefers-color-scheme: light)"
srcset="https://greptile-static-assets.s3.amazonaws.com/badges/FixAllInCodex.svg?v=3"><img
alt="Fix All in Codex"
src="https://greptile-static-assets.s3.amazonaws.com/badges/FixAllInCodex.svg?v=3"
height="20"></picture></a>
<details><summary>Prompt To Fix All With AI</summary>
`````markdown
Fix the following 1 code review issue. Work through them one at a time, proposing concise fixes.
---
### Issue 1 of 1
.changeset/remove-react-core-spa-initializer.md:2-3
**Changeset bump type may be too low**
Both packages are marked as `patch`, but removing a re-exported symbol from a published package is a breaking change under semver — any downstream consumer that imported `internalInitializeGTSPA` directly from `gt-react`'s server or types entrypoints will get a silent compile-time break after upgrading. The `internal` prefix and `@deprecated` tag strongly signal this wasn't meant for external use, but they don't prevent consumption. If there's a published version of `gt-react` that exposed this symbol, consider bumping to `minor` (or `major` if following strict semver) to surface the removal clearly in changelogs and dependency ranges.
`````
</details>
<sub>Reviews (1): Last reviewed commit: ["fix: avoid vite import meta
env
types"](12f8dfd)
| [Re-trigger
Greptile](https://app.greptile.com/api/retrigger?id=40886986)</sub>
> Greptile also left **1 inline comment** on this PR.
<!-- /greptile_comment -->
## Summary
- replace direct `import.meta.env` access in `BrowserI18nCache` with the
shared runtime environment helper
- add a `gt-react` patch changeset
## Tests
- pnpm --filter @generaltranslation/react-core build
- pnpm --filter gt-react typecheck
- pnpm --filter gt-react test
- git diff --check
<!-- greptile_comment -->
<h3>Greptile Summary</h3>
This PR removes the direct `import.meta.env?.DEV` access from
`BrowserI18nCache.getLocalStorageTranslationCache` and replaces it with
the shared `getRuntimeEnvironment()` helper from `gt-i18n/internal`,
fixing TypeScript errors that occur when Vite's ambient types are not
present in the project.
- **`BrowserI18nCache.ts`**: The single-line guard
`!import.meta.env?.DEV` is replaced with `getRuntimeEnvironment() !==
'development'`. The helper checks `process.env.NODE_ENV` first (for
webpack/CRA/Node environments), then `import.meta.env.MODE`, then
`import.meta.env.DEV`, defaulting to `'production'` — behavior is
equivalent for Vite and slightly broader for non-Vite bundlers.
- **`.changeset/fix-react-import-meta-env.md`**: A `patch` changeset is
added for `gt-react` to track the release.
<details open><summary><h3>Confidence Score: 5/5</h3></summary>
Safe to merge — a one-line guard swap with no altered control flow in
normal usage.
The change is minimal and the replacement helper consolidates the same
import.meta.env.DEV check alongside additional process.env.NODE_ENV
support. For Vite users the behavior is identical; for non-Vite bundlers
it is strictly more correct. No new runtime paths or error conditions
are introduced.
No files require special attention.
</details>
<details><summary><h3>Important Files Changed</h3></summary>
| Filename | Overview |
|----------|----------|
| .changeset/fix-react-import-meta-env.md | Adds a patch changeset entry
for gt-react, documenting the import.meta.env type fix. |
| packages/react/src/i18n-cache/BrowserI18nCache.ts | Replaces the bare
`import.meta.env?.DEV` guard in `getLocalStorageTranslationCache` with
the shared `getRuntimeEnvironment()` helper; imports updated
accordingly. Behavior is equivalent for Vite and improves coverage for
webpack/Node environments. |
</details>
<details><summary><h3>Flowchart</h3></summary>
<a href="#gh-light-mode-only">
```mermaid
%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[getLocalStorageTranslationCache called] --> B{getRuntimeEnvironment}
B --> C{process.env.NODE_ENV === 'development'?}
C -- Yes --> D[return 'development']
C -- No --> E{import.meta.env.MODE exists?}
E -- Yes --> F{MODE === 'development'?}
F -- Yes --> D
F -- No --> G[return 'production']
E -- No --> H{import.meta.env.DEV === true?}
H -- Yes --> D
H -- No --> G
D --> I[env === 'development' → create/return LocalStorageTranslationCache]
G --> J[env !== 'development' → return undefined]
```
</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[getLocalStorageTranslationCache called] --> B{getRuntimeEnvironment}
B --> C{process.env.NODE_ENV === 'development'?}
C -- Yes --> D[return 'development']
C -- No --> E{import.meta.env.MODE exists?}
E -- Yes --> F{MODE === 'development'?}
F -- Yes --> D
F -- No --> G[return 'production']
E -- No --> H{import.meta.env.DEV === true?}
H -- Yes --> D
H -- No --> G
D --> I[env === 'development' → create/return LocalStorageTranslationCache]
G --> J[env !== 'development' → return undefined]
```
</a>
</details>
<sub>Reviews (1): Last reviewed commit: ["fix: avoid vite import meta
env
types"](c3ac32d)
| [Re-trigger
Greptile](https://app.greptile.com/api/retrigger?id=40888010)</sub>
<!-- /greptile_comment -->
## Summary
- add a shared gt-i18n global singleton helper
- refactor i18n config/cache/condition-store and react-core store
singletons to use it
- preserve internal exports for cross-package singleton sharing
## Tests
- pnpm --filter gt-i18n test
- pnpm --filter @generaltranslation/react-core test
- pnpm --filter gt-i18n typecheck
- pnpm --filter @generaltranslation/react-core typecheck
- git diff --check
<!-- greptile_comment -->
<h3>Greptile Summary</h3>
Introduces a shared `createGlobalSingleton` factory in `gt-i18n` that
centralises the `globalThis.__generaltranslation` registry plumbing
previously duplicated across `i18nConfig`, `i18nCache`,
`conditionStore`, and `react-core`'s `i18nStore`. All four singletons
are refactored to delegate to the new helper, and it is re-exported from
`gt-i18n/internal` so downstream packages can adopt it directly.
- **New factory**
(`packages/i18n/src/globals/createGlobalSingleton.ts`): exposes a
uniform `{ get, set, isInitialized }` interface backed by a namespaced
slot in `globalThis.__generaltranslation`; `get()` and `isInitialized()`
both treat `null` and `undefined` as "not initialized", addressing a
previous inconsistency raised in review.
- **Refactored singletons**: `i18nConfig`, `i18nCache`,
`conditionStore`, and `i18nStore` each replace ~30–50 lines of
near-identical boilerplate with a two-line `createGlobalSingleton` call;
error messages and overwrite warnings are preserved.
- **Internal export**: `createGlobalSingleton` and the `GlobalSingleton`
type are added to `packages/i18n/src/internal.ts`, enabling
cross-package singleton sharing without duplicating the registry logic.
<details open><summary><h3>Confidence Score: 5/5</h3></summary>
Safe to merge — a clean deduplication with no changes to public API
surface or runtime behaviour on the normal path.
All four singletons delegate to the new factory through thin wrappers;
get/set/isInitialized semantics are unchanged for callers. The null-slot
handling in isInitialized() is now correctly addressed (with a matching
test). The only finding is a minor inconsistency in the set()
overwrite-warning guard that only manifests under external registry
tampering.
packages/i18n/src/globals/createGlobalSingleton.ts — the set() overwrite
guard should also exclude null to stay consistent with isInitialized().
</details>
<details><summary><h3>Important Files Changed</h3></summary>
| Filename | Overview |
|----------|----------|
| packages/i18n/src/globals/createGlobalSingleton.ts | New shared
utility that consolidates the globalThis.__generaltranslation registry
plumbing. Minor inconsistency: the set() overwrite-warning guard checks
!== undefined while isInitialized() also excludes null. |
| packages/i18n/src/globals/__tests__/createGlobalSingleton.test.ts |
Unit test covering the null-slot edge case; verifies isInitialized()
returns false and get() throws when the registry slot holds null. |
| packages/react-core/src/i18n-store/singleton-operations.ts | Switches
to importing createGlobalSingleton from gt-i18n/internal;
createI18nStoreNotInitializedError() still calls getI18nConfig() which
is the same cascading behavior as before. |
| packages/i18n/src/internal.ts | Exports createGlobalSingleton and
GlobalSingleton type so react-core (and future packages) can import from
gt-i18n/internal. |
| packages/i18n/src/condition-store/createConditionStoreSingleton.ts |
Inline boilerplate replaced by createGlobalSingleton delegation; no
behavioral changes for the normal path. |
| packages/i18n/src/i18n-cache/singleton-operations.ts | Replaced custom
registry boilerplate with createGlobalSingleton; error message preserved
via the notInitialized factory. |
| packages/i18n/src/i18n-config/singleton-operations.ts | Direct method
exports now forward to i18nConfigSingleton; isI18nConfigInitialized is
now exported (was previously present as a named function). |
</details>
<details><summary><h3>Flowchart</h3></summary>
<a href="#gh-light-mode-only">
```mermaid
%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[Consumer calls get / set / isInitialized] --> B[createGlobalSingleton factory]
B --> C[getNamespace reads globalThis.__generaltranslation namespace bag]
C --> D{Operation}
D -->|get| E{slot is undefined or null?}
E -->|yes| F[invoke notInitialized callback and throw Error]
E -->|no| G[return value as T]
D -->|set| H{slot is not undefined AND not equal to next?}
H -->|yes| I[console.warn overwrite via createDiagnosticMessage]
H -->|no| J[write next to slot]
I --> J
D -->|isInitialized| K{slot is not undefined AND not null?}
K -->|true| L[return true]
K -->|false| M[return false]
subgraph Callers
N[gt-i18n i18nConfig]
O[gt-i18n i18nCache]
P[gt-i18n conditionStore]
Q[react-core i18nStore]
end
Callers --> B
```
</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[Consumer calls get / set / isInitialized] --> B[createGlobalSingleton factory]
B --> C[getNamespace reads globalThis.__generaltranslation namespace bag]
C --> D{Operation}
D -->|get| E{slot is undefined or null?}
E -->|yes| F[invoke notInitialized callback and throw Error]
E -->|no| G[return value as T]
D -->|set| H{slot is not undefined AND not equal to next?}
H -->|yes| I[console.warn overwrite via createDiagnosticMessage]
H -->|no| J[write next to slot]
I --> J
D -->|isInitialized| K{slot is not undefined AND not null?}
K -->|true| L[return true]
K -->|false| M[return false]
subgraph Callers
N[gt-i18n i18nConfig]
O[gt-i18n i18nCache]
P[gt-i18n conditionStore]
Q[react-core i18nStore]
end
Callers --> B
```
</a>
</details>
<sub>Reviews (2): Last reviewed commit: ["fix: treat null singleton
slots as
unini..."](d0f6666)
| [Re-trigger
Greptile](https://app.greptile.com/api/retrigger?id=40882016)</sub>
<!-- /greptile_comment -->
…ction (#1726) ## Summary Two previously-working public hooks regressed on `odysseus`: - **`useVersionId()`** — the client/server entrypoints re-exported react-core's stub, which unconditionally `throw`s. On stable it returned `_versionId` from the provider context, so any app calling it now crashes. - **`useLocaleDirection()`** — the signature became `useLocaleDirection(locale: string)` (required arg). On stable it was `(locale?: string)` defaulting to the current locale; a zero-arg call now builds a locale-agnostic `GT` and throws `noTargetLocaleProvidedError`. ## Fix Add `packages/react/src/hooks/utils.ts` mirroring the existing `gt-react-native` wrappers: - `useVersionId()` → returns `getVersionId()` - `useLocaleDirection(locale?)` → defaults to `useLocale()` when omitted `index.client.ts`, `index.server.ts`, and `index.types.ts` now export both hooks from the wrapper instead of `react-core/hooks`. `index.types.ts` is included because the `browser`/`require`/`import` conditions draw their `.d.ts` from it — otherwise the runtime would be fixed but the published types would still advertise the broken signatures. The `react-server`/RSC entrypoint (`index.rsc.ts`, with its own `index.rsc.d` types) is intentionally left unchanged, since RSC genuinely requires an explicit locale and cannot read provider context. ## Verification - `pnpm --filter gt-react typecheck` — clean - `pnpm --filter gt-react build` — clean; emitted `dist/index.client.d.mts` now declares `useLocaleDirection(locale?: string): 'ltr' | 'rtl'` and `useVersionId(): string | undefined` Changeset included (`gt-react` patch). <!-- greptile_comment --> <h3>Greptile Summary</h3> This patch restores two regressed public hooks in the `odysseus` branch by fixing their implementations directly in `@generaltranslation/react-core` rather than adding per-package wrappers. - **`useVersionId()`** in `react-core/hooks/utils.ts` is changed from unconditionally throwing to calling `getVersionId()`, fixing crashes in any app that calls it through `gt-react` or `gt-react-native`. - **`useLocaleDirection(locale?)`** in `react-core/hooks/utils.ts` now calls `useLocale()` internally and falls back to the current locale when no argument is supplied, restoring the optional-arg signature. - The `react-native` package's local wrapper file (`hooks/utils.ts`) is deleted since `react-core` now provides the correct implementation; `react-native/index.tsx` is updated to re-export directly from `@generaltranslation/react-core/hooks`. The RSC entrypoint (`index.rsc.ts`) is intentionally left unchanged—it retains the stricter required-locale and throwing-`useVersionId` signatures. <details open><summary><h3>Confidence Score: 5/5</h3></summary> Safe to merge — the change restores previously-working hook signatures with a minimal, well-scoped fix in react-core. Both hooks are fixed at the correct abstraction layer (react-core), and every downstream entrypoint (gt-react client/server/types, gt-react-native) automatically benefits without requiring per-package wrappers. The RSC entrypoint is intentionally left with its stricter signatures. Hook call ordering in the updated useLocaleDirection is correct, and the deleted react-native wrapper had no callers outside the package's own index. No files require special attention. </details> <details><summary><h3>Important Files Changed</h3></summary> | Filename | Overview | |----------|----------| | packages/react-core/src/hooks/utils.ts | Core fix: useLocaleDirection now accepts optional locale (defaulting via useLocale()), and useVersionId calls getVersionId() instead of throwing. Hook ordering and memoization are correct. | | packages/react-native/src/hooks/utils.ts | Deleted — its logic now lives in react-core; removal is safe since react-native/index.tsx imports the same symbols from react-core/hooks directly. | | packages/react-native/src/index.tsx | Re-exports useLocaleDirection and useVersionId from @generaltranslation/react-core/hooks instead of the now-deleted local wrapper; public API surface is unchanged. | | .changeset/fix-react-version-id-locale-direction-hooks.md | Patch changeset covering react-core, gt-react, and gt-react-native; description accurately reflects the behavioral fix. | </details> <details><summary><h3>Flowchart</h3></summary> <a href="#gh-light-mode-only"> ```mermaid %%{init: {'theme': 'neutral'}}%% flowchart TD A["gt-react / gt-react-native\nconsumer calls useLocaleDirection(locale?)"] --> B{locale provided?} B -- yes --> C["resolvedLocale = locale"] B -- no --> D["useLocale() → currentLocale\nresolvedLocale = currentLocale"] C --> E["useMemo: getI18nConfig()\n.getGTClass()\n.getLocaleDirection(resolvedLocale)"] D --> E E --> F["returns 'ltr' | 'rtl'"] G["consumer calls useVersionId()"] --> H["getVersionId()\n(from gt-i18n/internal)"] H --> I["returns string | undefined"] J["RSC entrypoint (index.rsc.ts)\nuseVersionId()"] --> K["throws — intentionally\nnot implemented in RSC"] L["RSC entrypoint\nuseLocaleDirection(locale: string)"] --> M["requires explicit locale\nno useLocale() fallback"] ``` </a> <a href="#gh-dark-mode-only"> ```mermaid %%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%% flowchart TD A["gt-react / gt-react-native\nconsumer calls useLocaleDirection(locale?)"] --> B{locale provided?} B -- yes --> C["resolvedLocale = locale"] B -- no --> D["useLocale() → currentLocale\nresolvedLocale = currentLocale"] C --> E["useMemo: getI18nConfig()\n.getGTClass()\n.getLocaleDirection(resolvedLocale)"] D --> E E --> F["returns 'ltr' | 'rtl'"] G["consumer calls useVersionId()"] --> H["getVersionId()\n(from gt-i18n/internal)"] H --> I["returns string | undefined"] J["RSC entrypoint (index.rsc.ts)\nuseVersionId()"] --> K["throws — intentionally\nnot implemented in RSC"] L["RSC entrypoint\nuseLocaleDirection(locale: string)"] --> M["requires explicit locale\nno useLocale() fallback"] ``` </a> </details> <sub>Reviews (2): Last reviewed commit: ["fix(gt-react): restore useVersionId and ..."](922935e) | [Re-trigger Greptile](https://app.greptile.com/api/retrigger?id=40908954)</sub> <!-- /greptile_comment -->
## Summary
- add a react-server-specific types condition for gt-next's root export
- point the condition at `dist/index.rsc.d.ts` so RSC imports no longer
see client-only hooks from `index.types.d.ts`
- add a package export regression test
- add a gt-next patch changeset
## Tests
- pnpm --filter gt-next test -- src/__tests__/packageExports.test.ts
- pnpm --filter gt-next typecheck
- pnpm exec oxlint packages/next/src/__tests__/packageExports.test.ts
- pnpm exec oxfmt --check .changeset/gt-next-react-server-types.md
packages/next/package.json
packages/next/src/__tests__/packageExports.test.ts
- git diff --check
<!-- greptile_comment -->
<h3>Greptile Summary</h3>
This PR fixes incorrect TypeScript types being exposed when `gt-next` is
imported under the `react-server` export condition. By adding a
`"types"` field pointing at `dist/index.rsc.d.ts` inside the
`react-server` export block, TypeScript will now resolve RSC-specific
declarations instead of the general `index.types.d.ts` which includes
client-only hooks.
- **`packages/next/package.json`**: Inserts `"types":
"./dist/index.rsc.d.ts"` as the first key in the `react-server`
condition block, consistent with how other named conditions (e.g.
`./config`, `./server`) order their `types` field ahead of the runtime
entries.
- **`packages/next/src/__tests__/packageExports.test.ts`**: Adds a
regression test that reads `package.json` at test time and asserts both
the RSC-condition `types` path and the fallback `types` path, preventing
silent rollback of this fix.
- **`.changeset/gt-next-react-server-types.md`**: Records a `patch`
changeset describing the fix.
<details open><summary><h3>Confidence Score: 5/5</h3></summary>
Safe to merge — the change is a single-line addition to the exports map
that wires up an already-emitted declaration file, accompanied by a
targeted regression test.
The fix is minimal and mechanical: one new field in `package.json`, a
matching test, and a changeset. `src/index.rsc.ts` already exists and
`tsc --emitDeclarationOnly` will produce `dist/index.rsc.d.ts` as part
of the normal build, so the pointed-at file is not invented. The `types`
key is correctly placed before `import`/`default` inside the
`react-server` condition block, matching the ordering convention used
elsewhere in the exports map. No runtime behavior changes — only
TypeScript's view of the package changes for RSC consumers.
No files require special attention.
</details>
<details><summary><h3>Important Files Changed</h3></summary>
| Filename | Overview |
|----------|----------|
| packages/next/package.json | Adds `"types": "./dist/index.rsc.d.ts"`
as the first entry in the `react-server` export condition; ordering is
correct (types before import/default). The corresponding source
`src/index.rsc.ts` exists and the `tsc --emitDeclarationOnly` step will
produce `dist/index.rsc.d.ts` at build time. |
| packages/next/src/__tests__/packageExports.test.ts | New regression
test that reads `package.json` directly and asserts both the
RSC-specific `types` path and the general `types` fallback. The
`packageRoot` path traversal (3× `dirname`) correctly resolves to
`packages/next`. |
| .changeset/gt-next-react-server-types.md | Correct patch-level
changeset for `gt-next` describing the RSC types fix. |
</details>
<details><summary><h3>Flowchart</h3></summary>
<a href="#gh-light-mode-only">
```mermaid
%%{init: {'theme': 'neutral'}}%%
flowchart TD
A["TypeScript imports 'gt-next'"] --> B{Which condition?}
B -->|react-server condition active| C["types: ./dist/index.rsc.d.ts NEW"]
B -->|browser condition active| D["(no types field — pre-existing)"]
B -->|default| E["types: ./dist/index.types.d.ts"]
C --> F["RSC-safe declarations\n(no client-only hooks)"]
E --> G["General declarations\n(includes client hooks)"]
```
</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["TypeScript imports 'gt-next'"] --> B{Which condition?}
B -->|react-server condition active| C["types: ./dist/index.rsc.d.ts NEW"]
B -->|browser condition active| D["(no types field — pre-existing)"]
B -->|default| E["types: ./dist/index.types.d.ts"]
C --> F["RSC-safe declarations\n(no client-only hooks)"]
E --> G["General declarations\n(includes client hooks)"]
```
</a>
</details>
<sub>Reviews (1): Last reviewed commit: ["fix: use rsc types for gt-next
react-ser..."](15f4c24)
| [Re-trigger
Greptile](https://app.greptile.com/api/retrigger?id=40945802)</sub>
<!-- /greptile_comment -->
## 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 -->
| it('replaces existing known-locale prefixes', () => { | ||
| expect(localizePathSegment('/en/docs/api', 'es', knownLocales)).toBe( | ||
| '/es/docs/api' | ||
| ); |
There was a problem hiding this comment.
Missing test: bare locale-only replacement
The "replaces existing known-locale prefixes" block only covers a multi-segment path (/en/docs/api). The branch that produces unprefixedPath = restSegments.join('/') = '' (i.e., the input is just a locale prefix with nothing after it, like /en) is exercised by both callers but has no dedicated test. A case like localizePathSegment('/en', 'es', knownLocales) → '/es' would confirm that restSegments.join('/') correctly returns an empty string and the function falls through to the trailingSlashWhenEmpty logic rather than emitting 'es/' unexpectedly.
Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/cli/src/utils/__tests__/localizePathSegment.test.ts
Line: 16-19
Comment:
**Missing test: bare locale-only replacement**
The "replaces existing known-locale prefixes" block only covers a multi-segment path (`/en/docs/api`). The branch that produces `unprefixedPath = restSegments.join('/') = ''` (i.e., the input is just a locale prefix with nothing after it, like `/en`) is exercised by both callers but has no dedicated test. A case like `localizePathSegment('/en', 'es', knownLocales)` → `'/es'` would confirm that `restSegments.join('/')` correctly returns an empty string and the function falls through to the `trailingSlashWhenEmpty` logic rather than emitting `'es/'` unexpectedly.
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!
Summary
Checks
Greptile Summary
This PR consolidates duplicate locale-prefixing logic from
localizeMintlifyFrontmatterUrls.tsandprocessOpenApi.tsinto a singlelocalizePathSegmenthelper. Both callers are updated to use the new function, withlocalizeMintlifyFrontmatterUrlspassing{ trailingSlashWhenEmpty: true }to preserve its original trailing-slash behavior for empty URL paths, whileprocessOpenApiomits the option to preserve its no-trailing-slash directory behavior.localizePathSegment.ts): handles leading slash preservation, surrounding whitespace, known-locale prefix replacement, skip patterns (external/anchor/relative), and an optional trailing-slash mode for empty paths./en→/es) noted in a comment.Confidence Score: 4/5
Safe to merge; the refactor faithfully preserves both callers' original behavior and the only gap is a missing test for an edge case that the implementation already handles correctly.
The extracted helper is a clean deduplication with no logic changes. The trailingSlashWhenEmpty option correctly differentiates the two callers' behaviors, and the existing tests confirm the main code paths. The only gap is the missing test for a bare locale-prefix replacement (/en → /es), which is a coverage concern rather than a defect.
The test file at packages/cli/src/utils/tests/localizePathSegment.test.ts would benefit from a test for bare locale-prefix replacement; all other files are straightforward.
Important Files Changed
Flowchart
%%{init: {'theme': 'neutral'}}%% flowchart TD A[localizePathSegment value, targetLocale, knownLocales, options] --> B{trimmed empty or SKIPPABLE_URL_REGEX?} B -- yes --> C[return null] B -- no --> D[extract leadingWhitespace trailingWhitespace leadingSlash pathBody firstSegment + restSegments] D --> E{firstSegment === targetLocale?} E -- yes --> F[reconstruct normalized = leadingWS + leadingSlash + pathBody + trailingWS] F --> G{normalized === value?} G -- yes --> C G -- no --> H[return normalized] E -- no --> I{knownLocales has firstSegment?} I -- yes --> J[unprefixedPath = restSegments.join slash] I -- no --> K[unprefixedPath = pathBody] J --> L{unprefixedPath non-empty?} K --> L L -- yes --> M[localizedPath = targetLocale/unprefixedPath] L -- no --> N{trailingSlashWhenEmpty?} N -- yes --> O[localizedPath = targetLocale/] N -- no --> P[localizedPath = targetLocale] M --> Q[return normalized leadingWS + leadingSlash + localizedPath + trailingWS] O --> Q P --> Q%%{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[localizePathSegment value, targetLocale, knownLocales, options] --> B{trimmed empty or SKIPPABLE_URL_REGEX?} B -- yes --> C[return null] B -- no --> D[extract leadingWhitespace trailingWhitespace leadingSlash pathBody firstSegment + restSegments] D --> E{firstSegment === targetLocale?} E -- yes --> F[reconstruct normalized = leadingWS + leadingSlash + pathBody + trailingWS] F --> G{normalized === value?} G -- yes --> C G -- no --> H[return normalized] E -- no --> I{knownLocales has firstSegment?} I -- yes --> J[unprefixedPath = restSegments.join slash] I -- no --> K[unprefixedPath = pathBody] J --> L{unprefixedPath non-empty?} K --> L L -- yes --> M[localizedPath = targetLocale/unprefixedPath] L -- no --> N{trailingSlashWhenEmpty?} N -- yes --> O[localizedPath = targetLocale/] N -- no --> P[localizedPath = targetLocale] M --> Q[return normalized leadingWS + leadingSlash + localizedPath + trailingWS] O --> Q P --> QPrompt To Fix All With AI
Reviews (1): Last reviewed commit: "refactor(cli): dedupe locale path prefix..." | Re-trigger Greptile