Skip to content

fix(deps)(deps): bump i18next from 26.7.2 to 27.0.1 - #1933

Merged
github-actions[bot] merged 1 commit into
mainfrom
dependabot/npm_and_yarn/i18next-27.0.1
Nov 10, 2026
Merged

github-actions[bot] merged 1 commit into
mainfrom
dependabot/npm_and_yarn/i18next-27.0.1

Conversation

@dependabot

@dependabot dependabot Bot commented on behalf of github Nov 10, 2026

Copy link
Copy Markdown
Contributor

Bumps i18next from 26.7.2 to 27.0.1.

Release notes

Sourced from i18next's releases.

v27.0.1

  • fix: each cached format keeps at most 1000 formatters, dropping the oldest when full. This covers the built-in formats (number, currency, datetime, relativetime, list) with the default cacheInBuiltFormats: true, and formats added with addCached. The cache key holds all t() options, including the interpolated values, so every new amount, date or name added a formatter that was never released: about 50 MB per 100k values, growing without end in a long-running server. A dropped formatter is made again from the same language and options, so the output does not change. Thanks @​liustack (#2467).

v27.0.0

This is a major breaking release. It changes defaults that most setups had to override, gives every instance its own logger and post-processors, refuses language codes that look like markup or paths, and removes options that were broken or had no effect. 26.7 warned in debug mode about most of the runtime changes. The migration guide has the details; to keep the v26 behavior where an option exists:

i18next.init({
  fallbackLng: 'dev', // or better your source language: there is no implicit 'dev' any more
  returnEmptyString: true, // an empty translation is a translation again
  cleanCode: false, // keep codes without a region as they are ('EN' stays 'EN')
});
declare module 'i18next' {
  interface CustomTypeOptions {
    strictInterpolationChecks: false;
    returnEmptyString: true;
  }
}

Changed defaults

  • fallbackLng defaults to false instead of ['dev']. Without a fallbackLng, i18next no longer requests dev from the backend (/locales/dev/translation.json with i18next-http-backend), and saveMissing sends missing keys for the current language instead of dev. With supportedLngs and no fallbackLng, an unsupported language leaves language and languages unset, as fallbackLng: false already did. Set fallbackLng to your main language, or to 'dev' for the previous behavior.
  • returnEmptyString defaults to false: an empty translation falls back like a missing value (to the fallback language, the default value or the key), as null already does. i18next-cli writes '' for untranslated keys in the other languages, which now fall back instead of rendering blank. An explicit '' default from code still counts: t('key', '') and t('key', { defaultValue: '' }) return ''. Set returnEmptyString: true (in init() and in CustomTypeOptions) if an empty translation is meant to render nothing, for example for a Locize project with "preserve empty strings" on.
  • Language codes without a region get their canonical casing, as codes with one already did: 'EN' becomes 'en'. Only the casing changes: aliases like tl, iw or eng stay as they are ('TL' becomes 'tl'), and so do codes that are not BCP 47 (en_US, my_custom). i18next 26.7.0 and 26.7.1 warned that v27 turns iw into he or tl into fil; that was wrong and 26.7.2 corrects it. iw, tl and the like stay as they are. Set cleanCode: false to keep codes without a region exactly as they are. cleanCode is deprecated.
  • types: strictInterpolationChecks defaults to true: t() needs the options for a string with variables, so a {{name}} cannot be left out of a call. Type a wrapper around t() with ParseKeysWithoutVariables, declare the variables interpolation.defaultVariables fills in with defaultVariables in CustomTypeOptions, or set strictInterpolationChecks: false to keep the v26 types (also for ICU and MF2 messages, whose arguments are required either way). The fixes below close the gap plural keys left.
  • types: returnEmptyString defaults to false in TypeOptions too, so a key whose value is '' is typed as the key.

Changed behavior

  • The logger and the post-processors belong to the instance. use() of a logger or a post-processor on one instance no longer changes the others, and the init() of one instance no longer rewires the logging of all of them. An instance from createInstance() does not see what the default i18next instance (or any other) registered: call use() on each instance. A clone (cloneInstance()) keeps its parent's logger and post-processors, also those its parent registers later, and what it registers itself stays its own. A plugin that calls use() from its own init() (the Locize InContext editor) registers right away, as before. services.logger stays the instance's logger, with forward(). In debug mode, a postProcess name that is not registered on the instance logs a warning.
  • An object or an array without returnObjects is handled like a missing key: t() returns the default value if one is passed, else the key (with appendNamespaceToMissingKey, parseMissingKeyHandler and the post-processors applied), instead of the sentence key '... (en)' returned an object instead of string., which ended up in the UI. It is not saved as missing, since the key exists. The debug warning stays, and returnedObjectHandler still decides what is returned when it is set (returnedObjectHandler: (key) => `key '${key}' returned an object instead of string.` brings a sentence back).
  • addResources() keeps every value: numbers, booleans and objects were dropped silently, only strings and arrays were added.
  • The default escape no longer escapes /: '$10/month' stays '$10/month' instead of '$10&#x2F;month', and dates in react-i18next's Trans no longer render as 17&#x2F;12&#x2F;2025 (discussion 2199). &, <, >, " and ' are still escaped, which covers HTML text and quoted attributes; a / cannot end either. Update snapshots that contain &#x2F;, or pass your own interpolation.escape to keep escaping it.
  • A language code from lng, changeLanguage(), a language detector or loadLanguages() that does not have the shape of isSafeLanguageCode() (letters, digits, -, _, @ and ., no .., at most 128 characters) is refused unless it is configured: listed in supportedLngs, in a fallbackLng that is not a function or in preload, or a language in resources, as given to init(). changeLanguage() continues as with an unsupported code: with the detector's next code, else the fallback language, else none (with fallbackLng: false, a code refused at init() leaves language unset); loadLanguages() skips it. With supportedLngs, the input is matched as before ('de-DE;q=0.9' still gives 'de'), and only an unsafe match is refused, which only nonExplicitSupportedLngs can return ('de-<x>' for a supported 'de'). Without supportedLngs, a detector that returned "><img src=x onerror=alert(1)> made that string i18next.language, which apps put into <html lang>, and backends were asked to load it. BCP 47, POSIX (en_US.UTF-8@euro, sr@latin) and private-use codes (en-x-pirate) pass; to use a code with other characters (+, spaces, /), list it in supportedLngs. In debug mode a refused code logs a warning.
  • createInstance(options) merges the defaults right away, without initializing: before init(), instance.options has the defaults, getFixedT() no longer throws (t() returns '' until init()), and debug: true already shows the warnings before init(). init() still tells the options you set from the defaults, so a keySeparator or nsSeparator passed to createInstance() counts as set by you (#2426). loadNamespaces() before init() does nothing and calls back, as it did for an instance without options.
  • keySeparator: false means flat keys only: a lookup no longer splits a key at the text false (t('nestedfalsec') returned the value of nested.c), and a selector path is joined with . like a key prefix, so selectors work on the flat keys ($ => $['a.b'] and $ => $.a.b look up the flat key 'a.b'; $ => $.nested.c no longer resolves nested, then c). types: with keySeparator: false in CustomTypeOptions, the keys are the flat keys (no nestedfalsec keys), a key containing false is no longer split, and keyPrefix takes the parts of the flat keys before a ., which is how getFixedT() joins it ('nested' for 'nested.c'). A key prefix combined with selectors is not typed for flat keys.

Removed

  • The dotted language in the resource store calls: getResource('en.translation.key'), addResource('en.translation.key', value) and addResourceBundle('en.translation', resources). Pass language, namespace and key as separate arguments; addResource(), addResources() and addResourceBundle() throw a TypeError for the dotted form. A language code with a . (de_DE.UTF-8) is now a language like any other.
  • interpolation.prefixEscaped, suffixEscaped, nestingPrefixEscaped and nestingSuffixEscaped, which were inserted into the RegExp unescaped. Pass the literal delimiters as prefix, suffix, nestingPrefix and nestingSuffix.
  • types: compatibilityJSON (it had no effect since v24).
  • types: the option types of plugins. locizeLastUsed is declared by locize-lastused (5.1.0 and later). ReactOptions stays importable from 'i18next' but is empty: react-i18next fills it in (17.1.13 and later, and 18), so the react options are checked when react-i18next is in the program.
  • types: the _ and $ helpers in typescript/t.d.ts and typescript/options.d.ts ($FirstNamespace, _ValueOf and 84 more) are no longer importable; every declaration of those files counted as exported. The other types of those files (TOptionsBase, TFunctionSignature, ParseTReturn, ...) are now exported on purpose. If you imported one of the hidden helpers, please open an issue: exporting it again does not break anything.
  • The root index.js, a CommonJS shim that the exports map made unreachable.

... (truncated)

Changelog

Sourced from i18next's changelog.

27.0.1

  • fix: each cached format keeps at most 1000 formatters, dropping the oldest when full. This covers the built-in formats (number, currency, datetime, relativetime, list) with the default cacheInBuiltFormats: true, and formats added with addCached. The cache key holds all t() options, including the interpolated values, so every new amount, date or name added a formatter that was never released: about 50 MB per 100k values, growing without end in a long-running server. A dropped formatter is made again from the same language and options, so the output does not change. Thanks @​liustack (#2467).

27.0.0

This is a major breaking release. It changes defaults that most setups had to override, gives every instance its own logger and post-processors, refuses language codes that look like markup or paths, and removes options that were broken or had no effect. 26.7 warned in debug mode about most of the runtime changes. The migration guide has the details; to keep the v26 behavior where an option exists:

i18next.init({
  fallbackLng: 'dev', // or better your source language: there is no implicit 'dev' any more
  returnEmptyString: true, // an empty translation is a translation again
  cleanCode: false, // keep codes without a region as they are ('EN' stays 'EN')
});
declare module 'i18next' {
  interface CustomTypeOptions {
    strictInterpolationChecks: false;
    returnEmptyString: true;
  }
}

Changed defaults

  • fallbackLng defaults to false instead of ['dev']. Without a fallbackLng, i18next no longer requests dev from the backend (/locales/dev/translation.json with i18next-http-backend), and saveMissing sends missing keys for the current language instead of dev. With supportedLngs and no fallbackLng, an unsupported language leaves language and languages unset, as fallbackLng: false already did. Set fallbackLng to your main language, or to 'dev' for the previous behavior.
  • returnEmptyString defaults to false: an empty translation falls back like a missing value (to the fallback language, the default value or the key), as null already does. i18next-cli writes '' for untranslated keys in the other languages, which now fall back instead of rendering blank. An explicit '' default from code still counts: t('key', '') and t('key', { defaultValue: '' }) return ''. Set returnEmptyString: true (in init() and in CustomTypeOptions) if an empty translation is meant to render nothing, for example for a Locize project with "preserve empty strings" on.
  • Language codes without a region get their canonical casing, as codes with one already did: 'EN' becomes 'en'. Only the casing changes: aliases like tl, iw or eng stay as they are ('TL' becomes 'tl'), and so do codes that are not BCP 47 (en_US, my_custom). i18next 26.7.0 and 26.7.1 warned that v27 turns iw into he or tl into fil; that was wrong and 26.7.2 corrects it. iw, tl and the like stay as they are. Set cleanCode: false to keep codes without a region exactly as they are. cleanCode is deprecated.
  • types: strictInterpolationChecks defaults to true: t() needs the options for a string with variables, so a {{name}} cannot be left out of a call. Type a wrapper around t() with ParseKeysWithoutVariables, declare the variables interpolation.defaultVariables fills in with defaultVariables in CustomTypeOptions, or set strictInterpolationChecks: false to keep the v26 types (also for ICU and MF2 messages, whose arguments are required either way). The fixes below close the gap plural keys left.
  • types: returnEmptyString defaults to false in TypeOptions too, so a key whose value is '' is typed as the key.

Changed behavior

  • The logger and the post-processors belong to the instance. use() of a logger or a post-processor on one instance no longer changes the others, and the init() of one instance no longer rewires the logging of all of them. An instance from createInstance() does not see what the default i18next instance (or any other) registered: call use() on each instance. A clone (cloneInstance()) keeps its parent's logger and post-processors, also those its parent registers later, and what it registers itself stays its own. A plugin that calls use() from its own init() (the Locize InContext editor) registers right away, as before. services.logger stays the instance's logger, with forward(). In debug mode, a postProcess name that is not registered on the instance logs a warning.
  • An object or an array without returnObjects is handled like a missing key: t() returns the default value if one is passed, else the key (with appendNamespaceToMissingKey, parseMissingKeyHandler and the post-processors applied), instead of the sentence key '... (en)' returned an object instead of string., which ended up in the UI. It is not saved as missing, since the key exists. The debug warning stays, and returnedObjectHandler still decides what is returned when it is set (returnedObjectHandler: (key) => `key '${key}' returned an object instead of string.` brings a sentence back).
  • addResources() keeps every value: numbers, booleans and objects were dropped silently, only strings and arrays were added.
  • The default escape no longer escapes /: '$10/month' stays '$10/month' instead of '$10&#x2F;month', and dates in react-i18next's Trans no longer render as 17&#x2F;12&#x2F;2025 (discussion 2199). &, <, >, " and ' are still escaped, which covers HTML text and quoted attributes; a / cannot end either. Update snapshots that contain &#x2F;, or pass your own interpolation.escape to keep escaping it.
  • A language code from lng, changeLanguage(), a language detector or loadLanguages() that does not have the shape of isSafeLanguageCode() (letters, digits, -, _, @ and ., no .., at most 128 characters) is refused unless it is configured: listed in supportedLngs, in a fallbackLng that is not a function or in preload, or a language in resources, as given to init(). changeLanguage() continues as with an unsupported code: with the detector's next code, else the fallback language, else none (with fallbackLng: false, a code refused at init() leaves language unset); loadLanguages() skips it. With supportedLngs, the input is matched as before ('de-DE;q=0.9' still gives 'de'), and only an unsafe match is refused, which only nonExplicitSupportedLngs can return ('de-<x>' for a supported 'de'). Without supportedLngs, a detector that returned "><img src=x onerror=alert(1)> made that string i18next.language, which apps put into <html lang>, and backends were asked to load it. BCP 47, POSIX (en_US.UTF-8@euro, sr@latin) and private-use codes (en-x-pirate) pass; to use a code with other characters (+, spaces, /), list it in supportedLngs. In debug mode a refused code logs a warning.
  • createInstance(options) merges the defaults right away, without initializing: before init(), instance.options has the defaults, getFixedT() no longer throws (t() returns '' until init()), and debug: true already shows the warnings before init(). init() still tells the options you set from the defaults, so a keySeparator or nsSeparator passed to createInstance() counts as set by you (#2426). loadNamespaces() before init() does nothing and calls back, as it did for an instance without options.
  • keySeparator: false means flat keys only: a lookup no longer splits a key at the text false (t('nestedfalsec') returned the value of nested.c), and a selector path is joined with . like a key prefix, so selectors work on the flat keys ($ => $['a.b'] and $ => $.a.b look up the flat key 'a.b'; $ => $.nested.c no longer resolves nested, then c). types: with keySeparator: false in CustomTypeOptions, the keys are the flat keys (no nestedfalsec keys), a key containing false is no longer split, and keyPrefix takes the parts of the flat keys before a ., which is how getFixedT() joins it ('nested' for 'nested.c'). A key prefix combined with selectors is not typed for flat keys.

Removed

  • The dotted language in the resource store calls: getResource('en.translation.key'), addResource('en.translation.key', value) and addResourceBundle('en.translation', resources). Pass language, namespace and key as separate arguments; addResource(), addResources() and addResourceBundle() throw a TypeError for the dotted form. A language code with a . (de_DE.UTF-8) is now a language like any other.
  • interpolation.prefixEscaped, suffixEscaped, nestingPrefixEscaped and nestingSuffixEscaped, which were inserted into the RegExp unescaped. Pass the literal delimiters as prefix, suffix, nestingPrefix and nestingSuffix.
  • types: compatibilityJSON (it had no effect since v24).
  • types: the option types of plugins. locizeLastUsed is declared by locize-lastused (5.1.0 and later). ReactOptions stays importable from 'i18next' but is empty: react-i18next fills it in (17.1.13 and later, and 18), so the react options are checked when react-i18next is in the program.
  • types: the _ and $ helpers in typescript/t.d.ts and typescript/options.d.ts ($FirstNamespace, _ValueOf and 84 more) are no longer importable; every declaration of those files counted as exported. The other types of those files (TOptionsBase, TFunctionSignature, ParseTReturn, ...) are now exported on purpose. If you imported one of the hidden helpers, please open an issue: exporting it again does not break anything.

... (truncated)

Commits

Dependabot compatibility score

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 commands and options

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)

Bumps [i18next](https://github.com/i18next/i18next) from 26.7.2 to 27.0.1.
- [Release notes](https://github.com/i18next/i18next/releases)
- [Changelog](https://github.com/i18next/i18next/blob/master/CHANGELOG.md)
- [Commits](i18next/i18next@v26.7.2...v27.0.1)

---
updated-dependencies:
- dependency-name: i18next
  dependency-version: 27.0.1
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
@dependabot dependabot Bot added dependencies Pull requests that update a dependency file security Security related issue labels Nov 10, 2026
@github-actions
github-actions Bot merged commit b051558 into main Nov 10, 2026
11 of 25 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dependencies Pull requests that update a dependency file security Security related issue

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants