{{ message }}
Repository navigation
Releases: i18next/i18next
Releases · i18next/i18next
Release list
v26.5.11
- fix: a nested
false,0or''is now returned when a flat key with the same dotted name also exists. With{ a: { b: false }, 'a.b': 'flat' },getResource('en', 'translation', 'a.b')andt('a.b')returned'flat': the nested result was checked for truthiness, so these present values fell through to the flat-key lookup. Without such a flat key they were already returned. A nestednull, which is not a valid lookup by default (returnNull: false), still falls back to the flat key. Thanks @lprnmns (#2461).
v26.5.10
- fix: when none of the detected languages is in
supportedLngs, the best match no longer picks a different language whose code only starts with the same letters. The last fuzzy check took any supported code starting with the language part, so withsupportedLngs: ['en', 'fil-PH']a Finnish user (fi-FI) got Filipino instead of the fallback language, andha(Hausa) pickedhaw-US(Hawaiian). Other regions of the same language still match as before (de-CH→de-DE,pt-PT→pt-BR,fil→fil-PH). Compatibility note: a two-letter code no longer matches a supported three-letter code that extends it, likear→arz-EG,sw→swh-KEorno→nob-NO; list the two-letter code insupportedLngs(or map it withfallbackLng) if you relied on that. Thanks @Pharex38 (#2460).
v26.5.9
- fix: nested lookups no longer resolve to methods inherited from the prototype, like
constructor,toStringormap. Only the top level was guarded, so{{constructor}}was treated as missing, but{{user.constructor}}rendered the function source (with a class instance, the full source code of the class), and{{user.toString}},{{items.map}}or a class method like{{user.greet}}did the same. The same lookup is used for translation keys whenignoreJSONStructureis on (the default): a nested key named like an inherited method that is missing in the current language (t('roles.constructor')withroles.constructoronly in the fallback language) returnedfunction Object() { [native code] }instead of falling back, andexists('roles.valueOf')returnedtrue. These keys are now missing, so the fallback language,missingInterpolationHandler,saveMissingand the missing key handler apply as for any other missing key. Getters defined on a class ({{user.fullName}}withget fullName()) still work. Nested lookups also no longer walk into the prototype via__proto__, so{{user.__proto__.constructor}}no longer prints the class source either. Thanks @Pharex38 (#2459).
v26.5.8
- fix: arrays returned with
joinArraysnow get the same resolved details as strings. The joined value was extended withlastKeyin the place of the resolved lookup, so withpostProcessPassResolveda post processor got nousedLng,usedNSorexactUsedKey(t('steps', { joinArrays: ' / ' })reportedundefined/undefined:undefined). This affected the subliminal post processor used by the locize InContext editor, which could not map a joined array to its key. The nesting recursion guard did not get the parent key either, so arrays that nest each other (listA: ['a', '$t(listB)'],listB: ['b', '$t(listA)']) threwMaximum call stack size exceeded. They now stop at the guard, as strings do. Ani18nFormatplugin withhandleAsObjectandparsealso no longer throwsCannot read properties of undefined (reading 'usedNS')on joined arrays. Thanks @nk-batman21 (#2458).
v26.5.7
- fix: with the default
cacheInBuiltFormats: true, built-in formats now also work when thet()options contain a BigInt or a circular object. The cache key was built withJSON.stringifyover all options, including the interpolation values, which throws on those, so every built-in format in that call returned the raw value:t('num', { val: 12345678901234567890n })with'Total: {{val, number}}'returned'Total: 12345678901234567890'instead of'Total: 12,345,678,901,234,567,890', and passing an object with a back-reference (like an ORM entity) left all numbers, dates and lists of that call unformatted. When the key cannot be built, the value is now formatted without the cache, as withcacheInBuiltFormats: false. This also applies to formatters added withaddCached. Other calls still use the cache as before. Thanks @nk-batman21 (#2457).
v26.5.6
- fix:
cloneInstance({ forkResourceStore: true })now copies the resources at every level. It copied them only down to the namespace objects, so nested keys and arrays stayed shared with the original instance:addResource()with a nested key (fork.addResource('en', 'translation', 'greeting.hello', 'Hi')) or a deepaddResourceBundle()on the fork also changed the original instance and every other fork. Flat keys were already isolated. Forking a large store takes a bit longer: with 300k keys in nested groups, about 11 ms instead of 3 ms per fork. Clones withoutforkResourceStorestill share the store as before. Thanks @nk-batman21 (#2456, follow-up to #2270).
v26.5.5
- fix: with the default
skipOnVariables: true, a{{placeholder}}inside a value inserted with an unescaped{{- var}}now stays literal, as it already did for{{var}}. The escape pass ran after the unescaped values were written and scanned them again, sot('{{- a}}', { a: '{{b}}', b: 'c' })returned'c'instead of'{{b}}'. With pre-built HTML that carries user text, liket('You may contact {{name}} at {{- emailHTML}}.', { name, emailHTML }), that text could pull another variable of the samet()call into the output. Results withskipOnVariables: false, and values without placeholders, are unchanged. Compatibility note: a{{- var}}value whose placeholders were meant to be filled from the samet()call now keeps them literally;skipOnVariables: falserestores the old behavior. Thanks @nk-batman21 (#2455, reported in #1595).
v26.5.4
- fix: with the default
skipOnVariables: true, a{{placeholder}}inside an interpolated value now stays literal also when the same placeholder appears later in the translation. Each value was written withString.replace, which replaces the first copy of the placeholder text, and that could be the copy inside an earlier value:t('{{name}} has {{count}} items', { name: '{{count}}', count: 5 })returned'5 has {{count}} items'instead of'{{count}} has 5 items'. The value is now written at the position of the matched placeholder. Results withskipOnVariables: falseare unchanged. Thanks @nk-batman21 (#2454, reported in #1595). - fix: with
useRawValueToEscape: true, a non-string value in an unescaped{{- var}}placeholder or withescapeValue: falseno longer throwsval.replace is not a function; it is converted to a string like any other value.
v26.5.3
- fix(types): with the selector API, a plural key now requires
countalso when the call passes an explicitns.t(($) => $.item)already failed withoutcount, butt(($) => $.item, { ns: 'items' })compiled, although at runtime it looks upiteminstead ofitem_one/item_otherand returns the key. It now fails the same way, also withordinal: true,defaultValue, annsarray, a key prefix andi18n.t. Calls withcount,context,returnObjects: trueand arrays of selectors, as well as return types, are unchanged. Thanks @nyarthan (#2452).
v26.5.2
- fix(types): on TypeScript 7, a
t()call with a missing, misspelled or wrongly typed option now reports that option again (Property 'album' is missing,Type 'string' is not assignable to type 'number'), as TypeScript 5 and 6 do. TypeScript 7 elaborated the error against the default-value signature instead and ended in'{ count: 3; albm: "Summer"; }' is not assignable to type 'string': on a failed call TypeScript explains the last of the equally matching signatures, and TypeScript 7 sorts the 3-element[key, defaultValue, options?]last. It is now written as two signatures,[key, defaultValue]and[key, defaultValue, options]. This affects the default key checks with i18next{{name}}messages as well asparseInterpolation: 'icu'and'mf2'. Which calls compile, their return types and the error positions are unchanged on TypeScript 5.9, 6 and 7.
