Bumps [i18next](https://github.com/i18next/i18next) from 26.0.3 to 26.3.4. <details> <summary>Release notes</summary> <p><em>Sourced from <a href="https://github.com/i18next/i18next/releases">i18next's releases</a>.</em></p> <blockquote> <h2>v26.3.4</h2> <ul> <li>fix(security): <code>deepExtend</code> (used by <code>addResourceBundle(..., deep, overwrite)</code>) no longer recurses into inherited properties. It checked key existence with the <code>in</code> operator, which walks the prototype chain, so a source key matching an inherited built-in (e.g. <code>hasOwnProperty</code>, <code>toString</code>) caused recursion into the shared <code>Object.prototype</code> function and, with <code>overwrite: true</code>, could overwrite e.g. <code>Object.prototype.hasOwnProperty.call</code> with a non-callable value — corrupting a shared built-in process-wide (DoS). Existence is now checked with <code>Object.prototype.hasOwnProperty.call</code>, so such keys are copied as plain own data instead. This complements the existing <code>__proto__</code>/<code>constructor</code> guard and is also strictly more correct for an own-property merge. Only affects applications that pass attacker-controlled data with <code>deep: true</code> and <code>overwrite: true</code>; no standard backend/integration does this. Distinct from CVE-2026-48713 / CVE-2026-48714 (different packages, <code>setPath</code> mechanism). Thanks to zx (Jace) for the responsible disclosure.</li> </ul> <h2>v26.3.3</h2> <ul> <li>fix(types): selector <code>t($ => $.arr, { returnObjects: true, context })</code> on a JSON array of <strong>heterogeneous</strong> objects now preserves each element's full shape (e.g. <code>{ transKey1: string; transKey2: string }[]</code>) instead of collapsing to a union of partial element types. Two type-level causes: (1) <code>FilterKeys</code> evaluated the whole array element type at once, so <code>keyof (A | B)</code> only saw the keys common to every element — it now distributes over the object union and filters each element independently; (2) when TypeScript merges mismatched array element types it injects phantom optional <code>undefined</code> keys (e.g. <code>transKey1_withContext?: undefined</code> on elements that don't define it), which the context-detection helpers mistook for real context variants — they now skip keys typed as <code>undefined</code>. Also adds a dedicated <code>context</code> + <code>returnObjects: true</code> selector overload using <code>const Fn</code> + <code>ReturnType<Fn></code>, so <code>Target</code> is no longer collapsed to <code>unknown</code> via <code>ApplyTarget</code>. Resolves Problem 1 of <a href="https://redirect.github.com/i18next/i18next/issues/2398">#2398</a> (Problem 2 was already fixed on master). Thanks <a href="https://github.com/sauravgupta-dotcom"><code>@sauravgupta-dotcom</code></a> (<a href="https://redirect.github.com/i18next/i18next/pull/2438">#2438</a>). Fixes <a href="https://redirect.github.com/i18next/i18next/issues/2398">#2398</a>.</li> </ul> <h2>v26.3.2</h2> <ul> <li>fix: chained formatters with a parenthesised option that contains the format separator (e.g. <code>join(separator: ', ')</code>) now work at <strong>any</strong> position in the chain, not just first. Previously the comma-in-parens reassembly only repaired <code>formats[0]</code>, so <code>{{v, uppercase, join(separator: ', ')}}</code> split the <code>join(...)</code> option on the inner comma and never rejoined it, producing corrupt output. Replaced the first-position-only repair with a position-independent pass that re-joins fragments until each open paren closes. Thanks <a href="https://github.com/spokodev"><code>@spokodev</code></a> (<a href="https://redirect.github.com/i18next/i18next/pull/2437">#2437</a>).</li> </ul> <h2>v26.3.1</h2> <ul> <li>fix(types): <code>t()</code> with a <code>keyPrefix</code> no longer pollutes its return type with sibling keys' values. A regression in 26.3.0 — the <code>[Res] extends [never]</code> guards added to <code>KeysBuilderWithReturnObjects</code> / <code>KeysBuilderWithoutReturnObjects</code> turned the builders into deferred conditional types, so <code>KeyPrefix<Ns></code> stopped resolving to a literal union and <code>keyPrefix</code> inference widened to the whole namespace. Symptom: <code>useTranslation(ns, { keyPrefix: 'a.b' })</code> then <code>t('title')</code> would resolve to <code>'<a.b>.title' | '<other.path>.title' | ...</code> instead of just the scoped value. Affected every <code>react-i18next</code> user using <code>keyPrefix</code>. Restored to the eager 26.2.0 form. The same-namespace conflict handling from <a href="https://redirect.github.com/i18next/i18next/issues/2434">#2434</a> still works via <code>_DropConflictKeys</code> at the merge layer (in <code>options.d.ts</code>). Thanks <a href="https://github.com/aaronrosenthal"><code>@aaronrosenthal</code></a> (<a href="https://redirect.github.com/i18next/i18next/pull/2436">#2436</a>).</li> </ul> <h2>v26.3.0</h2> <ul> <li>feat(types): introduce <code>ResourceNamespaceMap</code> — a separate mergeable augmentation surface for namespace resource types, designed for monorepos where multiple packages each want to contribute their own namespaces. Previously, every package had to coordinate on a single <code>CustomTypeOptions.resources</code> declaration (or fall back to typing dependency namespaces as <code>any</code>) because <code>resources</code> is a single property of an interface and TypeScript reports TS2717 when two declarations of the same property disagree. The new interface merges naturally across <code>declare module 'i18next'</code> blocks, so each package can ship its own <code>i18next.d.ts</code> independently. Per-property merge handles same-namespace contributions from multiple packages, and same-key/different-literal conflicts are silently dropped to avoid poisoning <code>t()</code> overload resolution. Fully backwards-compatible — existing <code>CustomTypeOptions.resources</code> augmentations continue to work, and both surfaces can coexist. Scalar options (<code>defaultNS</code>, <code>returnNull</code>, <code>enableSelector</code>, etc.) still belong on <code>CustomTypeOptions</code>. Thanks <a href="https://github.com/sh3xu"><code>@sh3xu</code></a> (<a href="https://redirect.github.com/i18next/i18next/pull/2434">#2434</a>). Fixes <a href="https://redirect.github.com/i18next/i18next/issues/2409">#2409</a>.</li> </ul> <h2>v26.2.0</h2> <ul> <li>feat(types): new <code>parseInterpolation</code> TypeOption (default <code>true</code>). When set to <code>false</code> in <code>CustomTypeOptions</code>, the type-level extractor stops parsing translation strings for <code>{{variable}}</code> patterns. Required by <code>i18next-icu</code> users — the default extractor mistakes ICU MessageFormat nested-brace plurals like <code>{count, plural, one {{count} row} other {{count} rows}}</code> for an interpolation block and demands a phantom variable name. The flag is type-only; runtime interpolation is governed by <code>InterpolationOptions</code> and is unaffected. Fixes <a href="https://redirect.github.com/i18next/i18next-icu/issues/85">i18next-icu#85</a>.</li> <li>fix(types): expose <code>enableSelector</code> on <code>InitOptions</code> so <code>i18next.init({ enableSelector: 'strict' })</code> typechecks without a module augmentation. The runtime already reads <code>opts?.enableSelector</code> from init options; this lands the matching type declaration next to the other selector-resolution knobs. Accepts <code>false | true | 'optimize' | 'strict'</code>. Thanks <a href="https://github.com/Faithfinder"><code>@Faithfinder</code></a> (<a href="https://redirect.github.com/i18next/i18next/pull/2431">#2431</a>)</li> </ul> <h2>v26.1.0</h2> <ul> <li>feat: <code>enableSelector: 'strict'</code> (TypeOptions + runtime option). Opt-in mode that drops the flattened-primary form from <code>NsResource</code> at the type level — every namespace (primary included) is exposed only under its own key on <code>$</code>, uniformly across single- and multi-ns hooks. At runtime, a leading selector path segment matching the scope's namespace list is always rewritten as a namespace prefix, including the primary. Eliminates the silent-miss surface area where <code>t($ => $.primary.foo)</code> typechecks but doesn't resolve under the default mode (see <a href="https://redirect.github.com/i18next/i18next/issues/2429">#2429</a>). Backward-compatible: default <code>enableSelector: false | true | 'optimize'</code> behavior is unchanged. Note: strict mode is incompatible with the <a href="https://redirect.github.com/i18next/i18next/issues/2405">#2405</a> pattern (keys whose names match sibling namespaces) — those users should stay on default mode.</li> </ul> <h2>v26.0.10</h2> <ul> <li>feat: <code>getFixedT</code> accepts a fourth optional <code>fixedOpts</code> argument carrying <code>scopeNs</code> — the full namespace list the bound <code>t</code> was created for. The selector API uses <code>scopeNs</code> to detect when a path's first segment is a namespace prefix, <strong>without</strong> changing resolution scope. Resolution still uses the bound <code>ns</code> (a single primary string in the typical react-i18next setup), so plain <code>t('key')</code> lookups stay isolated to the primary namespace exactly as before — only <code>t($ => $.secondaryNs.foo)</code> selectors now route correctly under <code>useTranslation([nsA, nsB])</code>. Fixes the runtime side of <a href="https://redirect.github.com/i18next/i18next/issues/2429">#2429</a> for the <code>react-i18next</code> default-<code>nsMode</code> case. The 4th argument is opt-in: existing 3-arg <code>getFixedT(lng, ns, keyPrefix)</code> callers see no behavior change.</li> </ul> <h2>v26.0.9</h2> <ul> <li>fix(types): unformatted interpolation values are now typed as <code>string | number</code> (was <code>string</code>). i18next stringifies values at runtime, so requiring callers to wrap numbers in <code>String(...)</code> for plain <code>{{var}}</code> placeholders was unnecessary friction — and could mask the real problem when a non-string value was passed alongside multiple interpolation slots (the <code>t()</code> overload resolution would fall through to the 3-arg form and report a confusing "not assignable to string" error against the options object). Typed format specifiers like <code>{{x, number}}</code>, <code>{{x, currency}}</code>, <code>{{x, datetime}}</code>, etc. keep their precise types; this only relaxes the no-format default. The <code>count</code> variable remains <code>number</code>-only</li> </ul> <h2>v26.0.8</h2> <ul> <li>fix(types): restore the pre-v25.10.4 <code>ExistsFunction</code> shape so plain arrow functions can again be assigned to <code>ExistsFunction</code>-typed variables (TypeScript cannot infer type predicates through multi-overload assignment). Direct <code>i18next.exists(key)</code> calls still narrow <code>key</code> to <code>SelectorKey</code> — the predicate is now declared inline on <code>i18n.exists</code>. Custom wrappers that want the narrowing can type themselves as <code>typeof i18next.exists</code> <a href="https://redirect.github.com/i18next/i18next/issues/2425">2425</a></li> </ul> <h2>v26.0.7</h2> <ul> <li>fix: when a plural lookup misses, the <code>missingKey</code> debug log now shows the actual plural-resolved key (e.g. <code>foo.bar_many</code> for Polish <code>count: 14</code>) instead of the base key — making it obvious which plural category was expected and missing <a href="https://redirect.github.com/i18next/i18next/issues/2423">2423</a></li> <li>chore: drop <code>@babel/runtime</code> runtime dependency. The build no longer generates any <code>@babel/runtime</code> imports, so the package is unused by consumers. Rollup now uses <code>babelHelpers: 'bundled'</code> so any helpers that are ever needed in the future will be inlined rather than imported externally <a href="https://redirect.github.com/i18next/i18next/issues/2424">2424</a></li> <li>chore: stop emitting <code>dist/esm/i18next.bundled.js</code>. It was byte-identical to <code>dist/esm/i18next.js</code> because no helpers were being imported <a href="https://redirect.github.com/i18next/i18next/issues/2424">2424</a></li> </ul> <h2>v26.0.6</h2> <p>Security release — all issues found via an internal audit. GHSA advisory filed after release.</p> <ul> <li>security: warn when a translation string combines <code>escapeValue: false</code> with interpolated variables inside a <code>$t(key, { ... "{{var}}" ... })</code> nesting-options block. In that narrow combination, attacker-controlled string values containing <code>"</code> can break out of the JSON options literal and inject additional nesting options (e.g. redirect <code>lng</code>/<code>ns</code>). The default <code>escapeValue: true</code> configuration is unaffected because HTML-escaping neutralises the quote before <code>JSON.parse</code>. See the security docs for mitigation guidance (GHSA-TBD)</li> <li>security: apply <code>regexEscape</code> to <code>unescapePrefix</code> / <code>unescapeSuffix</code> on par with the other interpolation delimiters. Prevents ReDoS (catastrophic-backtracking) when a misconfigured delimiter contains regex metacharacters, and fixes silent breakage of the <code>{{- var}}</code> syntax when the delimiter contains characters like <code>(</code>, <code>[</code>, <code>.</code></li> <li>security: strip CR/LF/NUL and other C0/C1 control characters from string log arguments to prevent log forging via user-controlled translation keys, language codes, namespaces, or interpolation variable names (CWE-117)</li> <li>chore: ignore <code>.env*</code> and <code>*.pem</code>/<code>*.key</code> files in <code>.gitignore</code></li> </ul> <h2>v26.0.5</h2> <ul> <li>fix: <code>cloneInstance().changeLanguage()</code> no longer fails to update language state when the target language is not yet loaded — a race between <code>init()</code>'s deferred <code>load()</code> and the user's <code>changeLanguage()</code> could overwrite <code>isLanguageChangingTo</code>, causing <code>setLngProps</code> to be skipped <a href="https://redirect.github.com/i18next/i18next/issues/2422">2422</a></li> </ul> <h2>v26.0.4</h2> <ul> <li>fix(types): inline formatting options like <code>{{price, currency(EUR)}}</code> are now correctly resolved to their base format type (e.g. <code>number</code> for <code>currency</code>) instead of falling back to <code>string</code> <a href="https://redirect.github.com/i18next/i18next/issues/2378">2378</a></li> </ul> </blockquote> </details> <details> <summary>Changelog</summary> <p><em>Sourced from <a href="https://github.com/i18next/i18next/blob/master/CHANGELOG.md">i18next's changelog</a>.</em></p> <blockquote> <h2>26.3.4</h2> <ul> <li>fix(security): <code>deepExtend</code> (used by <code>addResourceBundle(..., deep, overwrite)</code>) no longer recurses into inherited properties. It checked key existence with the <code>in</code> operator, which walks the prototype chain, so a source key matching an inherited built-in (e.g. <code>hasOwnProperty</code>, <code>toString</code>) caused recursion into the shared <code>Object.prototype</code> function and, with <code>overwrite: true</code>, could overwrite e.g. <code>Object.prototype.hasOwnProperty.call</code> with a non-callable value — corrupting a shared built-in process-wide (DoS). Existence is now checked with <code>Object.prototype.hasOwnProperty.call</code>, so such keys are copied as plain own data instead. This complements the existing <code>__proto__</code>/<code>constructor</code> guard and is also strictly more correct for an own-property merge. Only affects applications that pass attacker-controlled data with <code>deep: true</code> and <code>overwrite: true</code>; no standard backend/integration does this. Distinct from CVE-2026-48713 / CVE-2026-48714 (different packages, <code>setPath</code> mechanism). See advisory <a href="https://github.com/i18next/i18next/security/advisories/GHSA-6jcc-5g8w-32mx">GHSA-6jcc-5g8w-32mx</a>, CVSS 5.9 (<code>CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:N/I:L/A:H</code>). Thanks to zx (Jace) <a href="https://github.com/manus-use"><code>@manus-use</code></a> for the responsible disclosure.</li> </ul> <h2>26.3.3</h2> <ul> <li>fix(types): selector <code>t($ => $.arr, { returnObjects: true, context })</code> on a JSON array of <strong>heterogeneous</strong> objects now preserves each element's full shape (e.g. <code>{ transKey1: string; transKey2: string }[]</code>) instead of collapsing to a union of partial element types. Two type-level causes: (1) <code>FilterKeys</code> evaluated the whole array element type at once, so <code>keyof (A | B)</code> only saw the keys common to every element — it now distributes over the object union and filters each element independently; (2) when TypeScript merges mismatched array element types it injects phantom optional <code>undefined</code> keys (e.g. <code>transKey1_withContext?: undefined</code> on elements that don't define it), which the context-detection helpers mistook for real context variants — they now skip keys typed as <code>undefined</code>. Also adds a dedicated <code>context</code> + <code>returnObjects: true</code> selector overload using <code>const Fn</code> + <code>ReturnType<Fn></code>, so <code>Target</code> is no longer collapsed to <code>unknown</code> via <code>ApplyTarget</code>. Resolves Problem 1 of <a href="https://redirect.github.com/i18next/i18next/issues/2398">#2398</a> (Problem 2 was already fixed on master). Thanks <a href="https://github.com/sauravgupta-dotcom"><code>@sauravgupta-dotcom</code></a> (<a href="https://redirect.github.com/i18next/i18next/pull/2438">#2438</a>). Fixes <a href="https://redirect.github.com/i18next/i18next/issues/2398">#2398</a>.</li> </ul> <h2>26.3.2</h2> <ul> <li>fix: chained formatters with a parenthesised option that contains the format separator (e.g. <code>join(separator: ', ')</code>) now work at <strong>any</strong> position in the chain, not just first. Previously the comma-in-parens reassembly only repaired <code>formats[0]</code>, so <code>{{v, uppercase, join(separator: ', ')}}</code> split the <code>join(...)</code> option on the inner comma and never rejoined it, producing corrupt output. Replaced the first-position-only repair with a position-independent pass that re-joins fragments until each open paren closes. Thanks <a href="https://github.com/spokodev"><code>@spokodev</code></a> (<a href="https://redirect.github.com/i18next/i18next/pull/2437">#2437</a>).</li> </ul> <h2>26.3.1</h2> <ul> <li>fix(types): <code>t()</code> with a <code>keyPrefix</code> no longer pollutes its return type with sibling keys' values. A regression in 26.3.0 — the <code>[Res] extends [never]</code> guards added to <code>KeysBuilderWithReturnObjects</code> / <code>KeysBuilderWithoutReturnObjects</code> turned the builders into deferred conditional types, so <code>KeyPrefix<Ns></code> stopped resolving to a literal union and <code>keyPrefix</code> inference widened to the whole namespace. Symptom: <code>useTranslation(ns, { keyPrefix: 'a.b' })</code> then <code>t('title')</code> would resolve to <code>'<a.b>.title' | '<other.path>.title' | ...</code> instead of just the scoped value. Affected every <code>react-i18next</code> user using <code>keyPrefix</code>. Restored to the eager 26.2.0 form. The same-namespace conflict handling from <a href="https://redirect.github.com/i18next/i18next/issues/2434">#2434</a> still works via <code>_DropConflictKeys</code> at the merge layer (in <code>options.d.ts</code>). Thanks <a href="https://github.com/aaronrosenthal"><code>@aaronrosenthal</code></a> (<a href="https://redirect.github.com/i18next/i18next/pull/2436">#2436</a>).</li> </ul> <h2>26.3.0</h2> <ul> <li>feat(types): introduce <code>ResourceNamespaceMap</code> — a separate mergeable augmentation surface for namespace resource types, designed for monorepos where multiple packages each want to contribute their own namespaces. Previously, every package had to coordinate on a single <code>CustomTypeOptions.resources</code> declaration (or fall back to typing dependency namespaces as <code>any</code>) because <code>resources</code> is a single property of an interface and TypeScript reports TS2717 when two declarations of the same property disagree. The new interface merges naturally across <code>declare module 'i18next'</code> blocks, so each package can ship its own <code>i18next.d.ts</code> independently. Per-property merge handles same-namespace contributions from multiple packages, and same-key/different-literal conflicts are silently dropped to avoid poisoning <code>t()</code> overload resolution. Fully backwards-compatible — existing <code>CustomTypeOptions.resources</code> augmentations continue to work, and both surfaces can coexist. Scalar options (<code>defaultNS</code>, <code>returnNull</code>, <code>enableSelector</code>, etc.) still belong on <code>CustomTypeOptions</code>. Thanks <a href="https://github.com/sh3xu"><code>@sh3xu</code></a> (<a href="https://redirect.github.com/i18next/i18next/pull/2434">#2434</a>). Fixes <a href="https://redirect.github.com/i18next/i18next/issues/2409">#2409</a>.</li> </ul> <h2>26.2.0</h2> <ul> <li>feat(types): new <code>parseInterpolation</code> TypeOption (default <code>true</code>). When set to <code>false</code> in <code>CustomTypeOptions</code>, the type-level extractor stops parsing translation strings for <code>{{variable}}</code> patterns. Required by <code>i18next-icu</code> users — the default extractor mistakes ICU MessageFormat nested-brace plurals like <code>{count, plural, one {{count} row} other {{count} rows}}</code> for an interpolation block and demands a phantom variable name. The flag is type-only; runtime interpolation is governed by <code>InterpolationOptions</code> and is unaffected. Fixes <a href="https://redirect.github.com/i18next/i18next-icu/issues/85">i18next-icu#85</a>.</li> <li>fix(types): expose <code>enableSelector</code> on <code>InitOptions</code> so <code>i18next.init({ enableSelector: 'strict' })</code> typechecks without a module augmentation. The runtime already reads <code>opts?.enableSelector</code> from init options; this lands the matching type declaration next to the other selector-resolution knobs. Accepts <code>false | true | 'optimize' | 'strict'</code>. Thanks <a href="https://github.com/Faithfinder"><code>@Faithfinder</code></a> (<a href="https://redirect.github.com/i18next/i18next/pull/2431">#2431</a>)</li> </ul> <h2>26.1.0</h2> <ul> <li>feat: <code>enableSelector: 'strict'</code> (TypeOptions + runtime option). Opt-in mode that drops the flattened-primary form from <code>NsResource</code> at the type level — every namespace (primary included) is exposed only under its own key on <code>$</code>, uniformly across single- and multi-ns hooks. At runtime, a leading selector path segment matching the scope's namespace list is always rewritten as a namespace prefix, including the primary. Eliminates the silent-miss surface area where <code>t($ => $.primary.foo)</code> typechecks but doesn't resolve under the default mode (see <a href="https://redirect.github.com/i18next/i18next/issues/2429">#2429</a>). Backward-compatible: default <code>enableSelector: false | true | 'optimize'</code> behavior is unchanged. Note: strict mode is incompatible with the <a href="https://redirect.github.com/i18next/i18next/issues/2405">#2405</a> pattern (keys whose names match sibling namespaces) — those users should stay on default mode.</li> </ul> <h2>26.0.10</h2> <ul> <li>feat: <code>getFixedT</code> accepts a fourth optional <code>fixedOpts</code> argument carrying <code>scopeNs</code> — the full namespace list the bound <code>t</code> was created for. The selector API uses <code>scopeNs</code> to detect when a path's first segment is a namespace prefix, <strong>without</strong> changing resolution scope. Resolution still uses the bound <code>ns</code> (a single primary string in the typical react-i18next setup), so plain <code>t('key')</code> lookups stay isolated to the primary namespace exactly as before — only <code>t($ => $.secondaryNs.foo)</code> selectors now route correctly under <code>useTranslation([nsA, nsB])</code>. Fixes the runtime side of <a href="https://redirect.github.com/i18next/i18next/issues/2429">#2429</a> for the <code>react-i18next</code> default-<code>nsMode</code> case. The 4th argument is opt-in: existing 3-arg <code>getFixedT(lng, ns, keyPrefix)</code> callers see no behavior change.</li> </ul> <h2>26.0.9</h2> <ul> <li>fix(types): unformatted interpolation values are now typed as <code>string | number</code> (was <code>string</code>). i18next stringifies values at runtime, so requiring callers to wrap numbers in <code>String(...)</code> for plain <code>{{var}}</code> placeholders was unnecessary friction — and could mask the real problem when a non-string value was passed alongside multiple interpolation slots (the <code>t()</code> overload resolution would fall through to the 3-arg form and report a confusing "not assignable to string" error against the options object). Typed format specifiers like <code>{{x, number}}</code>, <code>{{x, currency}}</code>, <code>{{x, datetime}}</code>, etc. keep their precise types; this only relaxes the no-format default. The <code>count</code> variable remains <code>number</code>-only</li> </ul> <h2>26.0.8</h2> <ul> <li>fix(types): restore the pre-v25.10.4 <code>ExistsFunction</code> shape so plain arrow functions can again be assigned to <code>ExistsFunction</code>-typed variables (TypeScript cannot infer type predicates through multi-overload assignment). Direct <code>i18next.exists(key)</code> calls still narrow <code>key</code> to <code>SelectorKey</code> — the predicate is now declared inline on <code>i18n.exists</code>. Custom wrappers that want the narrowing can type themselves as <code>typeof i18next.exists</code> <a href="https://redirect.github.com/i18next/i18next/issues/2425">2425</a></li> </ul> <h2>26.0.7</h2> <ul> <li>fix: when a plural lookup misses, the <code>missingKey</code> debug log now shows the actual plural-resolved key (e.g. <code>foo.bar_many</code> for Polish <code>count: 14</code>) instead of the base key — making it obvious which plural category was expected and missing <a href="https://redirect.github.com/i18next/i18next/issues/2423">2423</a></li> <li>chore: drop <code>@babel/runtime</code> runtime dependency. The build no longer generates any <code>@babel/runtime</code> imports, so the package is unused by consumers. Rollup now uses <code>babelHelpers: 'bundled'</code> so any helpers that are ever needed in the future will be inlined rather than imported externally <a href="https://redirect.github.com/i18next/i18next/issues/2424">2424</a></li> <li>chore: stop emitting <code>dist/esm/i18next.bundled.js</code>. It was byte-identical to <code>dist/esm/i18next.js</code> because no helpers were being imported <a href="https://redirect.github.com/i18next/i18next/issues/2424">2424</a></li> </ul> <h2>26.0.6</h2> <p>Security release — all issues found via an internal audit.</p> <!-- raw HTML omitted --> </blockquote> <p>... (truncated)</p> </details> <details> <summary>Commits</summary> <ul> <li><a href="https://github.com/i18next/i18next/commit/817ede5146e6d366645982885b0a729861d10a3a"><code>817ede5</code></a> 26.3.4</li> <li><a href="https://github.com/i18next/i18next/commit/46d0dd80b505b6b69feccebbe7d9c4fd66c8f85c"><code>46d0dd8</code></a> build</li> <li><a href="https://github.com/i18next/i18next/commit/642137b7786d83661ec3ee53027798dc4b81d30a"><code>642137b</code></a> fix(security): prevent deepExtend from recursing into inherited built-ins</li> <li><a href="https://github.com/i18next/i18next/commit/cf7080d363c0bc775681215bba6513a3610dee8b"><code>cf7080d</code></a> 26.3.3</li> <li><a href="https://github.com/i18next/i18next/commit/05fd7be281a3c2175da16c9e867a91d95e034889"><code>05fd7be</code></a> build</li> <li><a href="https://github.com/i18next/i18next/commit/cc0bd80943eae4f45867abbd78c0ae8dd964bb82"><code>cc0bd80</code></a> changelog: 26.3.3 entry for <a href="https://redirect.github.com/i18next/i18next/issues/2438">#2438</a></li> <li><a href="https://github.com/i18next/i18next/commit/9cbfa6353b6ab726c42be1e6ed52aab5b0601612"><code>9cbfa63</code></a> fix(types): preserve selector returnObjects shape with context (<a href="https://redirect.github.com/i18next/i18next/issues/2438">#2438</a>)</li> <li><a href="https://github.com/i18next/i18next/commit/3b047122f5ad5b99a29f9138bb8542de125c8783"><code>3b04712</code></a> 26.3.2</li> <li><a href="https://github.com/i18next/i18next/commit/fc20f5dd5ae9744045942e135e93dd787e4bc869"><code>fc20f5d</code></a> changelog: 26.3.2 entry for <a href="https://redirect.github.com/i18next/i18next/issues/2437">#2437</a></li> <li><a href="https://github.com/i18next/i18next/commit/6901e04f07f0eec1848242d8af26509ab5438920"><code>6901e04</code></a> fix: reassemble comma-in-parens formatters at any chain position (<a href="https://redirect.github.com/i18next/i18next/issues/2437">#2437</a>)</li> <li>Additional commits viewable in <a href="https://github.com/i18next/i18next/compare/v26.0.3...v26.3.4">compare view</a></li> </ul> </details> <br /> [](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) --- <details> <summary>Dependabot commands and options</summary> <br /> You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot show <dependency name> ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself) </details> Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Demo · Documentation · Quickstart · GitHub · Releases
Tournament system meant to be easy to use. Bracket is written in async Python (with FastAPI) and Vite as frontend using the Mantine library.
It has the following features:
- Supports single elimination, round-robin and swiss formats.
- Build your tournament structure with multiple stages that can have multiple groups/brackets in them.
- Drag-and-drop matches to different courts or reschedule them to another start time.
- Various dashboard pages are available that can be presented to the public, customized with a logo.
- Create/update teams, and add players to teams.
- Create multiple clubs, with multiple tournaments per club.
- Swiss tournaments can be handled dynamically, with automatic scheduling of matches.
Live Demo
A demo is available for free at https://www.bracketapp.nl/demo. The demo lasts for 30 minutes, after which your data will de deleted.
Quickstart
To quickly run bracket to see how it works, clone it and run docker compose up:
git clone git@github.com:evroon/bracket.git
cd bracket
sudo docker compose up -d
This will start the backend and frontend of Bracket, as well as a postgres instance. You should now be able to view bracket at http://localhost:3000. You can log in with the following credentials:
- Username:
test@example.org - Password:
aeGhoe1ahng2Aezai0Dei6Aih6dieHoo.
To insert dummy rows into the database, run:
docker exec bracket-backend uv run --no-dev ./cli.py create-dev-db
See also the quickstart docs.
Usage
Read the usage guide for how to organize a tournament in Bracket from start to finish.
Configuration
Read the configuration docs for how to configure Bracket.
Bracket's backend is configured using .env files (prod.env for production, dev.env for development etc.).
But you can also configure Bracket using environment variables directly, for example by specifying them in the docker-compose.yml.
The frontend doesn't can be configured by environment variables as well, as well as .env files using Vite's way of loading environment variables.
Running Bracket in production
Read the deployment docs for how to deploy Bracket and run it in production.
Bracket can be run in Docker or by itself (using uv and pnpm).
Development setup
Read the development docs for how to run Bracket for development.
Prerequisites are pnpm, postgresql and uv to run the frontend, database and backend.
Translations
Based on your browser settings, your language should be automatically detected and loaded. For now, there's no manual way of choosing a different language.
Supported Languages
To add/refine translations, Crowdin is used. See the docs for more information.
More screenshots
Help
If you're having trouble getting Bracket up and running, or have a question about usage or configuration, feel free to ask. The best place to do this is by creating a Discussion.
Supporting Bracket
If you're using Bracket and would like to help support its development, that would be greatly appreciated!
Several areas that we need a bit of help with at the moment are:
- ⭐ Star Bracket on GitHub
- 🌐 Translating: Help make Bracket available to non-native English speakers by adding your language (via crowdin)
- 📣 Spread the word by sharing Bracket to help new users discover it
- 🖥️ Submit a PR to add a new feature, fix a bug, extend/update the docs or something else
See the contribution docs for more information on how to contribute
Contributors
|
Erik Vroon |
Null |
Nicolas Vanheuverzwijn |
Sevi C |
Max Ricketts-Uy |
Danny Piper |
|
Byte |
BachErik |
Amin NAIRI |
Felipe Gomes De Melo |
IzStriker |
Jon Miller |
|
Oscar Tobar Rios |
Raphael Le Goaller |
License
Bracket is licensed under AGPL-v3.0.
Please note that any contributions also fall under this license.
See LICENSE



