The last 26.x minor before v27, which follows in about a week. It adds the APIs v27 builds on, and with debug: true it warns wherever a setup relies on something v27 changes. Most of the v27 defaults are existing options, so you can try them now:
i18next.init({
debug: true, // prints the warnings below
fallbackLng: 'en', // v27: fallbackLng defaults to false, set your source language (no implicit 'dev')
returnEmptyString: false, // v27 default: an empty string falls back like a missing key
cleanCode: true, // close to v27, which canonicalizes codes without a region ('EN' becomes 'en')
});declare module 'i18next' {
interface CustomTypeOptions {
strictInterpolationChecks: true; // v27 default
returnEmptyString: false; // v27 default
}
}The other v27 changes (/ no longer escaped in values, a logger and post-processors per instance, the key instead of a sentence for an object hit, the dotted-language store calls and *Escaped options removed) have no switch; the debug warnings below show where they apply.
- feat: with the default
skipOnVariables: true, a missing interpolation variable is now logged in debug mode, once per string and variable:missed to pass in variable name for interpolating Hello {{name}}. The raw{{name}}reached the UI without any log line, even withdebug: true, because the warning only ran withskipOnVariables: false. A variable passed asundefinedand amissingInterpolationHandlerstill log nothing. - feat:
i18next.hydrate(resources, lng)puts resources rendered on the server into the store and changes to the server's language, each only the first time. It does what react-i18next'suseSSRdid by writingresourceStore.data,options.ns,isInitialized,initializedStoreOnceandinitializedLanguageOncedirectly, and sets the same flags, so integrations can switch to it and stay compatible with code that still reads or writes them. On a clone it does nothing, asuseSSRdoes: a clone shares its parent's store (one clone per request with i18next-http-middleware). Called beforeinit(), it logs a warning and changes nothing. - feat:
services.utils.reloadResources()lets a backend reload its bundles through i18next (asi18next.reloadResources(), with retries and theloadedevent), instead of callingbackendConnector.read()andloaded()itself as thereloadIntervalof i18next-http-backend and i18next-chained-backend does. Without languages (nolng, no detector, the defaultfallbackLng),reloadResources()reloads nothing instead of throwingCannot read properties of undefined (reading 'forEach'); a backend that also reloadspreloadpasses its languages. - feat: backends and async language detectors can declare their calling convention with
callbackApi(an instance or static property).true:read(language, namespace, callback),create(languages, namespace, key, fallbackValue, callback, options)anddetect(callback);false: they return a promise or a value, andcreategets the options as its fifth argument. Without it, i18next keeps guessing from the number of parameters (Function.length), which default and rest parameters change: i18next-chained-backend'screate(..., clb = () => {}, opts = {})has length 4 and never got its options (isUpdate), and i18next-http-backend's callback form ofcreatehas length 5, the length reserved for the callback-less form. Guessing is deprecated: in debug mode an unmarked backend or async detector logs a deprecation warning, once perinit(). - feat:
isSafeLanguageCode(code)is exported for plugins that take a language from a request, a URL or storage: letters, digits,-,_,@and.(no..), at most 128 characters, not a prototype key. It is not a BCP 47 check, soen_US,zh_Hant_TW,sr@latinandde_DE.UTF-8pass; markup, slashes and spaces do not. The language detector, i18next-http-middleware and i18next-http-backend each have their own rule today. - feat:
use()logs a warning in debug mode when a second backend, language detector,i18nFormator formatter replaces the first one; only one of each is used per instance, and losing an ICU setup this way was silent. Asuse()usually runs beforeinit(), the warning is printed onceinit()has set up the logger. - feat(types):
ParseKeysWithoutVariables<Ns, KPrefix>, the keyst()takes without options: their string has no variables, and a plural key is left out when its forms takecount. A wrapper typed with it, like(key: ParseKeysWithoutVariables<'common'>) => t(key), compiles withstrictInterpolationCheckswithout a cast. - feat(types):
defaultVariablesinCustomTypeOptionsdeclares the variablesinterpolation.defaultVariablesfills in at runtime (defaultVariables: { appName: string }). They become optional in thet()options, also withstrictInterpolationChecks; passed, they keep their type. - feat(types):
interpolationFormatSeparatorinCustomTypeOptionsfor a custominterpolation.formatSeparator: the types split{{price, currency}}at a hard-coded,, so withformatSeparator: ';'the format was not recognized. The per-callskipInterpolationoption is typed, andreturnedObjectHandlermay return a value (what it returns is whatt()returns; it was typed as returningvoid). - feat(types):
SelectorFn,ApplyTarget,ConstrainTargetandGetSourceare exported on purpose, under their current names. react-i18next imports them forTrans; they were importable only because every declaration in the type files counts as exported. - fix(types): with
nsSeparator: falseinCustomTypeOptions, the key union no longer containsnsfalsekey-style keys for the other namespaces, and a key that contains the textfalse(isfalsey) is no longer split at it, which typed itst()result asnever. - fix: with clones (i18next-http-middleware makes one per request),
saveMissingnow checks that the namespace is loaded for the instance that misses the key. EachcloneInstance()pointed the parent'sservices.utils.hasLoadedNamespaceat the new clone, and the backend connector, which all clones share, used that one: a key missing in one request was not saved (did not save key ... as the namespace ... was not yet loaded) while a newer request was still loading its language, and the parent'sservices.utilsanswered for the newest clone. A clone'sservices.utilsis now bound to the clone, and the parent keeps its own. A directservices.backendConnector.saveMissing()call, like the missing keys route of i18next-http-middleware, still checks the newest clone (follow-up to #1413). - fix:
dir()without a language (beforeinit(), or with no language resolved) returns'ltr'instead of'rtl'(discussion #2135). - fix:
t()beforeinit()returns an empty string, as typed (with a warning when debug logging is already on); it returnedundefined. - fix:
off('a b', listener)removes the listener from both events, ason('a b', listener)adds it to both. It looked for an event named'a b', soonce('a b', listener)never removed its listener and ran it on every event. - deprecate: dotted languages in the resource store calls (
getResource('en.translation.key'),addResource('en.translation.key', value),addResourceBundle('en.translation', resources)) log a deprecation warning in debug mode; they are removed in v27. Pass language, namespace and key as separate arguments. - deprecate:
interpolation.prefixEscaped,suffixEscaped,nestingPrefixEscapedandnestingSuffixEscaped(inserted into the RegExp unescaped) log a deprecation warning in debug mode and are marked@deprecated; they are removed in v27. Pass the literal delimiters asprefix,suffix,nestingPrefixandnestingSuffix. - deprecate:
compatibilityJSONis marked@deprecated(it has no effect since v24) and removed from the types in v27. - deprecate: a language code without a region that v27 canonicalizes differently logs a deprecation warning in debug mode:
'EN'stays'EN'today and becomes'en'in v27,'iw'becomes'he'.cleanCode: truealready lower-cases them. - deprecate: when
fallbackLngis not set, the first request for the default fallback languagedev(loading it, or saving missing keys to it) logs a notice in debug mode: v27 defaults tofallbackLng: falseand no longer requestsdev. SetfallbackLngto your source language, or to'dev'to keep the current behavior. - deprecate: in debug mode, a language code that comes from
lng,changeLanguage()or the language detector, is not configured (supportedLngs,fallbackLng,preloador the resources) and failsisSafeLanguageCodelogs a deprecation warning, once per code: withoutsupportedLngs, a detector that returns"><img src=x onerror=alert(1)>makes that stringi18next.language, and apps put it into<html lang>. Nothing changes in 26.7; v27 is planned to refuse such codes and continue as with an unsupported code. Language codes are still not bound to a format. - deprecate: the logger and post-processors are shared by all instances in v26 and per instance from v27 (clones keep their parent's). In debug mode, an instance that runs a post-processor only another instance registered with
use()logs a deprecation warning (calluse()on this instance too), and a non-cloneinit()that replaces the logger another instance set withuse()logs one through that logger. - deprecate: in debug mode, a value with a
/under the default escape logs that v27 no longer escapes/($10/monthrenders as$10/monthtoday; not withescapeValue: false, where react-i18next'sTransescapes one call itself and decodes the entities afterwards), and an object or array hit withoutreturnObjectslogs that v27 returns the key instead of the sentencekey '... (en)' returned an object instead of string.. - chore: the package is published from a
fileswhitelist instead of.npmignore, which had to mirror every local or secret file by hand. The tarball is unchanged. The rooti18next.jsandi18next.min.js(copies ofdist/umd/, still published for the unversioned unpkg URLs) are built bynpm run buildand no longer tracked in git. - ci: the public types are checked on TypeScript 5.0, the lowest version of the peer range.