Repository navigation
chore(deps): update i18next-cli to v1.80.4 - #3563
Merged
Merged
Conversation
renovate
Bot
force-pushed
the
renovate/i18next-cli-1.x
branch
2 times, most recently
from
October 29, 2026 10:25
ef3f9bb to
aa759d3
Compare
renovate
Bot
force-pushed
the
renovate/i18next-cli-1.x
branch
from
October 29, 2026 10:45
aa759d3 to
8609ae2
Compare
setchy
approved these changes
Oct 29, 2026
|
Contributor
|
🎉 This pull request is included in Gitify v8.0.0! 🎉 Downloads for macOS, Windows, and Linux are available on the release page. Thanks for helping make Gitify better! |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.



This PR contains the following updates:
1.74.1→1.80.4Release Notes
i18next/i18next-cli (i18next-cli)
v1.80.4Compare Source
t('key', 'Default')andt('key', 'Default', { … })were left untouched while the translation files were renamed, so the nextextractbrought the old key back. Moving such a key to another namespace keeps the default value:
t('key', 'Default', { ns: 'ns2' })."Loading..._one"inru/translation.json,extractalso wrote a nested copy,{ "Loading": { "": { "": { "_one": … } } } }; the samehappened for generated forms (
_few,_many), preserved_zeroforms, context variants andpreservePatterns.A file that already has these nested copies loses them on the next
extract.v1.80.3Compare Source
"Loading..."to"Saving..."left the old key in place and wrote{ "Saving": { "": { "": { "": "Loading..." } } } }; renamingcommon.okto"Okay, thanks."wrote{ "Okay, thanks": { "": "OK" } }. The new key is now written where the nextextractputs it (flat for"Loading...", natural-language and<Trans>keys), and the old key is removed howeverthe file stores it. #311
v1.80.2Compare Source
"Loading..."or"Hello world. Bye"as one flat key, butsyncsplit them on the separator, so a secondary filewith
{ "Loading...": "Lädt..." }became{ "Loading": { "": { "": { "": "" } } } }and the translation was lost.syncnow writes them flat, andstatus,localizeandrename-keyfind their values too. A secondary filealready broken this way gets its flat key back (with an empty value) on the next
sync.#310
{{ name}}) are trimmed, andenableSelectorno longerthrows on plural keys whose base name is
constructor,toString, … or also an object(i18next-resources-for-ts 2.1.2).
v1.80.1Compare Source
<Trans>interpolation variable namedformatis extracted.<Trans>Export as {{ format }}</Trans>was extracted as
Export as, because react-i18next readformatas the formatter name and dropped the variable;it is now
Export as {{format}}, as react-i18next 17.1.8 renders it. Requires react-i18next^17.1.8.v1.80.0Compare Source
interpolations by name (
{{name}},{{- name}},{{val, number}}), nested$t(key)references and tags (<0>,<1/>,<bold>), except the basic HTML react-i18next keeps (transKeepBasicHtmlNodesFor). A translation thatrenamed or dropped a placeholder is reported with file, key and line, e.g.
greeting: missing {{name}}, unexpected {{nom}} (compared with "en"). Plural forms are compared as a group, so{{count}}may be left out of_oneor_zero. A warning by default, so existing CI runs keep passing;lint.checkTranslationPlaceholders: 'error'fails the run,'off'disables it. Until now only ICU and MF2 projectshad this check.
de) are marked≈ (identical to en)and counted in the summary.status.checkIdentical:'warn'(default) still counts them astranslated and keeps the exit code,
'error'counts them as untranslated,'off'disables it. Locales of theprimary's language (
en-GBwithen), values without letters, URLs, e-mail addresses and short all-caps words(
OK,FAQ) are skipped; other values that stay the same go intostatus.ignoreKeys.lint --update-baselinewrites the current issues toi18next-lint-baseline.json(lint.baselineforanother path). While the file exists,
lintreports and fails only on new issues, so a codebase with existinghardcoded strings can run
lintin CI. Issues are matched by file, type and text, not by line.{{-name}}was read as a parameter-name, sot('Hi {{-name}}', { name })reported two false interpolation issues;{{- name}}and names outside[A-Za-z0-9_.-](
{{größe}}) were not read at all.transKeepBasicHtmlNodesFordefaults to react-i18next's['br', 'strong', 'i', 'p'].lintto check the placeholders.v1.79.1Compare Source
.mjs(src/**/*.{js,jsx,mjs,ts,tsx}), ininit, in theconfig detected for a project without one, and in
migrate-configwhen the old config has noinput. Existingconfigs do not change. #309
v1.79.0Compare Source
.mjsfile with JSX is parsed with the same JSX fallback as a.jsfile,including the constants pre-scan of
extract. Before, it failed with a syntax error andextractexited with 1.#309
v1.78.0Compare Source
messageFormat: 'mf2',lintwarns when whitespace at the start or end of a pattern differs fromthe primary language. MF2 keeps a space inside
{{ }}as text, so{{ {$count} files}}shows it in the app. Awarning does not fail the run.
_(ru_RU,pt_BR) is read asru-RU,pt-BRfor the plural categories. Before,Intlrejected it and onlyotherwas required, so aru_RUplural with onlyoneandotherpassed.otherplus every category a whole number below one million selects(was 0 to 1000), the same rule as Locize; exact selectors count for a category only when it selects a few small
numbers. No locale's result changes with today's CLDR data.
otherinstead of*,{{ {$count}instead of
{{{$count}, and spaces inside{{ }}.v1.77.0Compare Source
extract.messageFormat: 'icu'or'mf2', a newly generatedi18next.d.tssetsparseInterpolation: 'icu'or'mf2'when the project's i18next is 26.5.0 or later, sot()options are type-checkedagainst the arguments of each message. With i18next 26.2.0 to 26.4.x it still sets
parseInterpolation: false. Theversion is read from
node_modules/i18next/package.json, from the working directory upwards (hoisted monorepoinstalls count); without an installed i18next it stays
false. The warning for an existingi18next.d.tswithoutparseInterpolationsuggests the value for your i18next version, and an existing file that still setsfalsealthough your i18next supports the typed value gets a hint.
parseInterpolation: 'icu'/'mf2'.v1.76.0Compare Source
extract.messageFormatoption,'i18next'(default),'icu'(i18next-icu)or
'mf2'(i18next-mf2), for the syntax of your translation values.messageFormat: 'icu'or'mf2',extractwrites one key per message.t('photos', '{count, plural, ...}', { count })gavephotos_oneandphotos_other(plus_fewand_manyfor languages like Polish),keys i18next-icu and i18next-mf2 never read, and
contextgave_malekeys; now it givesphotos.$t()in a valueis no longer read as nesting, and the children of
<Trans>are written with{name}(ICU) or{$name}(MF2)instead of
{{name}}, which ICU and MF2 rendered as text.IcuTransandIcuTransWithoutContextare extracted, also when imported under another name (key fromi18nKey, ordefaultTranslationwhen there is none), and so are theTrans,Plural,SelectandSelectOrdinalcomponents of
react-i18next/icu.macro, with the message the macro builds (<Trans>Welcome, <strong>{name}</strong>!</Trans>givesWelcome, <0>{name}</0>!). Before,IcuTranswasnot extracted, and the macro
<Trans>was read as react-i18nextTransand written asWelcome, <strong>{{name}}</strong>!, which the compiledIcuTransrendered as text.messageFormat: 'icu'or'mf2', a plural or select message counts as one key, sostatusnolonger reports missing
_fewand_manykeys.messageFormat: 'icu'or'mf2',instrumentwrites{name}/{$name}placeholders, acount === 1 ? … : …ternary becomes one plural message instead ofdefaultValue_one/defaultValue_other, andthe i18n init file it creates registers
i18next-icu(withintl-messageformatin the install hint) ori18next-mf2.messageFormat: 'icu'or'mf2', the text around converted placeholders (Transvalues,instrument) isescaped:
File '{{name}}' not foundbecomesFile ''{name}' not found, because an apostrophe before{starts quotedtext in ICU and
'{name}'would render as the text{name}; literal braces are quoted (ICU) or escaped (MF2).messageFormat: 'icu'or'mf2',lintparses every value in the translation files of alllocales and reports, with file, key and line (JSON files):
greeting: ICU syntax error: EXPECT_ARGUMENT_CLOSING_BRACE at column 7),with a hint when the value contains i18next's
{{{name}is caught (#is not an argument)pluralandselectordinal(MF2:.matchon:numberor:integer). Allowed are the locale's CLDR categories, required areotherplus every category a whole number from0 to 1000 selects:
runeedsone,fewandmany, whilefr,es,itandptmay leave outmany(only from1,000,000) and
csitsmany(fractions only), andzeroinenis reported as never used. Exact selectors like=0are allowed and count for a category when they cover every whole number it is used for (=1coversoneinen, not inru, whereoneis also 21, 31, ...). For a localeIntldoes not know onlyotheris required.messageFormat: 'icu'or'mf2', thet()interpolation check reads the arguments with the sameparser. ICU braces like
male {{name}}are no longer taken for{{name}}interpolation (which reported thegenderpassed to a select as unused), and
t('greeting', 'Hello {name}', { nmae })is reported.messageFormat: 'icu'or'mf2', a newly generatedi18next.d.tssetsparseInterpolation: false(i18next 26.2.0+), so ICU and MF2 plurals no longer break the type-safe
t. For an existing file without it,typesprints a warning.
stops working,
IcuTrans,parseInterpolation: falseandextract.messageFormat.@formatjs/icu-messageformat-parser(the parser i18next-icu uses through intl-messageformat) andmessageformat(the MF2 implementation i18next-mf2 uses). They are only loaded when
messageFormatis'icu'or'mf2'.v1.75.3Compare Source
const params = { count },t('key', params)now extracts the plural keys (and readscontext,ns,defaultValuethe same way),where it used to extract only
key. A variable a file declares also no longer resolves to a same-namedvariable of another file:
t('key', overwrittenParams)tooky=trueas its default value from anunrelated function-local
overwrittenParamsarray elsewhere. Fixes#307.
v1.75.2Compare Source
Two generated OpenAPI clients that both export
componentsresolved to whichever file was read first,so
import { components as githubComponents } from './github-api'andimport { components as digitalOceanComponents } from './digital-ocean-api'extracted the keys of only oneof them, and moving files around changed which. A name now resolves to the module the file imports it
from, for renamed and plain imports, namespace imports (
GH.components),.jsimport specifiers andtsconfig
pathsaliases. Imports that cannot be traced to a scanned file (packages, re-exporting barrels)still resolve by name as before. Fixes #306.
v1.75.1Compare Source
import * as Types from './types', a qualified type ((color: Types.Color) => …,x as Types.Color,FC<Types.Props>,declare const s: Types.Status) and a namespace member (Types.SIZES.map(…),Types.Status.Done,t(Types.LABELS.save),Object.keys(Types.LABELS)) now extract their keys, alsofor a namespace re-exported from a barrel (
export * as Shared from './types'). Fixes#305.
as constobject resolves in the files that import it:t(LABELS.save)andObject.keys(LABELS)only worked in the declaring file before.keyof typeof MAPresolves to the map's keys, sot(`label.${key}`)withdeclare const key: keyof typeof LABELSextractslabel.saveandlabel.cancel. This branch never matched before.v1.75.0Compare Source
extract.maxTypeDepthoption sets how many levels of nested object members arefollowed when a dynamic key reads a typed value (default: 5). Higher values cost time and memory on
large recursive types such as OpenAPI schemas. Requested in
#302.
wrapper type.
type Pet = components['schemas']['Pet']gets the full depth belowPet, where thecomponents/schemaswrapper used to take two of the five levels.response types work:
operations['getPet']['responses'][200]['content']['application/json']andop.responses[200].content['application/json'].statusresolved nothing before.v1.74.8Compare Source
// t(`QUARTER.${q}`)extractedQUARTER.${q}and// t(`status.${deepObject.status}`)extractedstatus.${deepObject.status}. Such a comment now keeps the existing keys it matches (QUARTER.1,QUARTER.2, …) even withremoveUnusedKeys, the way a commented static key keeps its key, while code canstill add new matching keys. A pattern without any static part (
`${a}.${b}`) is ignored. Fixes#304.
v1.74.7Compare Source
i18nKeyon<Trans t={t}>now routes into the hook's namespaces the same wayt()does, matching react-i18next 17.0.16. Withconst { t } = useTranslation(['ns1', 'ns2']),<Trans t={t} i18nKey={$ => $.ns2.message} />was filed asns1:ns2.message; it is now filed asns2:message, liket($ => $.ns2.message). Undertypes.enableSelector: 'strict'a leading primarynamespace is stripped too (
useTranslation('only')with$ => $.only.keyfilesonly:key). Reported inreact-i18next#1933.
React.FC<Props>(alsoFC,FunctionComponent),forwardRef<Ref, Props>(…)ormemo<Props>(…)are typed byProps, and iterating a typed member bindsthe element.
props.colors.map((color) => t(\Color.${color}`))withcolors: Color[]extracted nothing before unlesscolorwas asserted withas Color. Arrays of objects work the same way:props.items.map(({ kind }) => …),for (const item of props.items)and a destructured({ items }) => items.map(…)`. Fixes #303.v1.74.6Compare Source
does.
([1, 2, 3, 4] as const).map((q) => t(\QUARTER.${q}`)),for (const unit of ['day', 'hour'] as const)`and inline arrays of objects extracted nothing before. Fixes
#301.
t(\barStatus.${deepObject.Bar.status}`)andt(`fooBarStatus.${deepObject.Foo.Bar.status}`)now extract the nested union's values, also through references to other types, optional chaining (pet.category?.kind), destructuring (const { kind } = pet.category) and indexed access types (DeepObject['Bar']['status'],components['schemas']['Pet']), so types generated by openapi-typescript work. Nested members are followed up to five levels below the declared type, as incomponents.schemas.Pet.category.kind`.Fixes #302.
v1.74.5Compare Source
Color[],readonly Color[],Array<Color>or an alias of one) now resolves to the union's values, socolors.map((color) => t(\color.${color}`))andfor (const color of colors)extract every key instead of nothing. Before, this only worked withcolor as Color`. Fixes#300.
v1.74.4Compare Source
useTranslation(confirmEmail ? ['reset-email-page', 'reset-page'] : 'reset-page')filedt('form.title')underdefaultNS, sostatusreported it as absent andextractremoved theexisting translations from
reset-pageandreset-email-page. The key is now extracted into theprimary namespace of every branch (here
reset-email-pageandreset-page), also for<Trans t={t}>. Fixes #299.v1.74.3Compare Source
fix(extract): dynamic keys typed by a declaration in a file without any translation call
resolve again when that declaration builds on one from a file that sorts after it. In 1.74.2,
type Kind = Base | 'extra'inapi/types.tswithBasedeclared inshared/base.tsexpandedt(`kind.${kind}`)tokind.extraonly, and the same happened to interface members,function return types and functions returning an enum member declared further down the list.
Such declarations are now resolved again once every file has been scanned, which also removes
the file-order dependence this had before 1.74.2.
feat(extract): more implicitly typed values expand to their finite set of keys. Fixes
#298:
const animal = getAnimal(); t(`animal.${animal.type}`), also throughconst { type } = getAnimal(),const kind = animal.typeorgetAnimal().type.type Animal = Duck | Doggivesanimal.typethevalues of both) and generic object types (
type AnimalBase<T extends AnimalType> = { type: T }used as
AnimalBase<'DUCK'>, or bare, which falls back to the parameter's default orconstraint).
(typeof COLOR)[keyof typeof COLOR]for anas constobject or an enum.colors[i], without an annotation on the variable they are assigned to.Narrowing is not followed: after
if (color !== 'BLUE'),color.BLUEis still extracted.fix(extract): shorthand options resolve like their long form.
t('k', { ns })filed the keyunder the default namespace,
{ defaultValue }was ignored anduseTranslation('ns', { keyPrefix })lost its prefix. Thanks to@aaronshaf for reporting it in
#291.
v1.74.2Compare Source
globhappens to return theinput files. When two call sites give one key different defaults (
t('k', 'Adding...')in onefile,
t('k', 'Importing...')in another), the first file visited wins, so an unchanged sourcetree could produce a different catalog from run to run and flip CI checks that compare against a
committed catalog. Files are now visited in sorted path order, which makes the winner stable. If
your sources contain such conflicts, the kept default may change once with this release. To
hear about them instead of silently keeping one default, set
extract.warnOnConflictsto'warn'or'error'. Fixes#296.
TFunction,getFixedTor Transcomponent (as configured via
functions,useTranslationNamesandtransComponents) are nolonger parsed and walked a second time for keys. The constants pre-scan still reads every file,
so types and
as constvalues declared in such files keep resolving dynamic keys elsewhere.Plugins with an
onVisitNodehook still see every file. On large trees where most files hold notranslations this cuts extraction time considerably. Fixes
#297.
Configuration
📅 Schedule: (UTC)
* 0-3 * * 1)🚦 Automerge: Disabled by config. Please merge this manually once you are satisfied.
♻ Rebasing: Whenever PR is behind base branch, or you tick the rebase/retry checkbox.
🔕 Ignore: Close this PR and you won't be reminded about this update again.
This PR was generated by Mend Renovate. View the repository job log.