* fix(admin): use ISO 639-1 da for Danish admin locale
Rename admin translation bundles from dk to da, normalize legacy dk in
app config, custom translation keys, localStorage, and stored user language.
Closes#25761
* fix(admin): add dk json fallback and admin user language migration
Register internal migration admin::migrate-prefered-language-dk-to-da to
rewrite admin_users.prefered_language from dk to da.
Export importLocaleJsonWithLegacyDkFallback from strapi-admin and use it in
core loadAdminTrads plus every bundled plugin registerTrads so third-party
plugins that still ship only dk.json keep loading when the UI locale is da.
Add @strapi/admin devDependency where the helper is imported from JS or
packages that did not already list it.
Refs #26322
* fix(types): add importLocaleJson to strapi-admin registerTrads typing
* fix(admin): expand plugin locale list and merge under canonical codes
Moves legacy dk handling into StrapiApp.loadTrads (flatMap + uniq + normalize merge) so registerTrads keeps a stable API without importLocaleJson. Core admin bundles use try/catch dynamic imports per locale code.
* fix(admin): route plugin translations through importLocaleJson
Pass StrapiApp.importLocaleJson into registerTrads so core and third-party plugins share legacy locale JSON fallback (da -> dk) and one-shot deprecation warnings. Relax admin test console.warn filter for those messages. Refs #26322.
* fix(admin): keep plugin locale fallback internal
---------
Co-authored-by: Bassel Kanso <basselkanso82@gmail.com>
* chore(eslint): migrate .eslintrc + .eslintignore to .eslintrc.cjs
Consolidates the per-package ESLint setup into a single typed
`.eslintrc.cjs` file across the monorepo (177 files touched).
Why
- `.eslintignore` is removed in ESLint v9/v10. Folding ignore patterns
into `ignorePatterns` inside the rc file is the supported migration
path and unblocks future ESLint upgrades (which will likely be needed
alongside the in-flight TypeScript 6 migration).
- `.cjs` extension is explicit about module format, so the configs keep
working if any package later adopts `"type": "module"` (no CJS/ESM
surprises).
- Wrapping each config in `// @ts-check` + a `Linter.Config`-typed
`const config` block gives editor autocomplete and catches typos.
Surfaced two real duplicates in
`eslint-config-custom/back/typescript.js`
(`node/no-unsupported-features/es-syntax`,
`node/no-missing-import`), now deduplicated.
- `extends` references switched from the `custom/*` shorthand to the
explicit `eslint-config-custom/*` package name. ESLint resolves both,
but the explicit form makes the source unambiguous.
- Added `coverage/` to `ignorePatterns` so coverage output produced
inside a workspace is not linted.
Misc
- `examples/kitchensink` had ESLint configured but no `lint` script;
added one so the config actually runs.
- `nx.json` hash inputs updated:
- removed `.eslintignore` (no longer exists)
- replaced `{projectRoot}/.eslintrc.js` with
`{projectRoot}/**/.eslintrc.cjs` so nested package configs are also
part of the cache key (the previous glob missed them).
- `packages/utils/upgrade/tsconfig.eslint.json` include path updated
from `.eslintrc.js` to `.eslintrc.cjs`.
Verification
- `eslint --print-config` resolves cleanly on sampled back/front
packages (admin, users-permissions, upload).
- Full `pnpm -r lint` is clean.
* chore(eslint): set tsconfigRootDir for vscode-eslint typed linting
CLI lint resolved tsconfig paths via cwd, but vscode-eslint runs from
the workspace root and could not locate per-package tsconfigs. Set
parserOptions.tsconfigRootDir to __dirname in each package's
.eslintrc.cjs so the editor resolves typed-lint configs correctly.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* chore(eslint): self-ignore configs, scope coverage to jest packages
- Replace `env: { es6: true }` workaround with `.eslintrc.cjs` in
ignorePatterns so eslint never lints the config file itself
(root cause of the "const is not a keyword" parse error in es5)
- Add `.eslintrc.cjs` to ignorePatterns across all configs that
were missing it
- Restrict `coverage/` ignorePatterns to workspace roots that
actually run jest; drop from non-test packages and admin/server
subdir configs (tests run from the workspace root)
* chore(deps): bump msw to 1.3.5 and typedoc to 0.25.13
Widens TypeScript peer ranges in msw (>= 4.4.x) and typedoc (adds 5.4.x),
unblocking a future bump to typescript 5.6 required by react-intl 7.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* chore(deps): bump msw to 2.13.4 across admin test suites
Migrates 8 workspaces (admin, content-manager, content-releases, upload,
review-workflows, plugin-i18n, plugin-documentation, plugin-users-permissions)
from msw 1.3.5 to 2.13.4; bumps email's unused dep for consistency.
Handler changes:
- rest.X((req, res, ctx) => res(ctx.json(X))) → http.X(() => HttpResponse.json(X))
- res(ctx.status(N)) → new HttpResponse(null, { status: N })
- res.once(...) → http.X(path, handler, { once: true })
- res.networkError(...) → HttpResponse.error()
- await req.json<T>() / casts → http.METHOD<Params, Body>(...) generics
- RequestHandler → HttpHandler
- Empty 200 responses use HttpResponse.json({}) to avoid cross-realm
SyntaxError instanceof check in getFetchClient.ts
Jest setup (per msw frequent-issues guide):
- jest-preset.front.js: customExportConditions: [''] so msw/node resolves
under jsdom; .mjs swc transform; ESM allow-list for msw deps
(rettime, @open-draft, outvariant, until-async, etc.)
- packages/admin-test-utils/src/environment.ts: polyfill Node fetch +
streams + BroadcastChannel + URL into jsdom global. Blob/File/FormData
intentionally left as jsdom's — Strapi app code threads them through
jsdom-only APIs (FileReader, Image, HTMLFormElement) where realm matters.
Comment in env.ts documents the trade-off.
Verified vs develop+msw@1.3.5 baseline: 0 net regressions across all 7
workspaces with msw test files; TypeScript clean.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Revert "chore(deps): bump typedoc to 0.25.13"
The typedoc bump was bundled with the msw 1.3.5 bump in 39d169cfbb but
isn't required for the msw v2 migration. Reverting the docs/ and
core/types typedoc dep back to 0.25.10 to keep this branch focused on
the msw upgrade.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* chore(tests): type msw handler params via generics
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* chore(tests): address review feedback on msw v2 migration
- env.ts: snapshot jsdom globals in setup(), restore in teardown() so
per-test mutations cannot leak across files in a Jest worker.
- env.ts: collapse polyfill list and value map into a single NODE_GLOBALS
source of truth; document the fetch trade-off (Node undici vs jsdom).
- handlers: replace `<never, Body>` generics with `<Record<string, never>,
Body>` (msw v2 convention; `{}` blocked by ban-types).
- email: drop unused msw devDep entirely (zero source usage in workspace).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* chore(tests): use realm-agnostic SyntaxError check; drop URL polyfill
Two follow-ups to the msw v2 migration:
- `getFetchClient.responseInterceptor` now matches `error?.name === 'SyntaxError'`
rather than `instanceof SyntaxError`. Constructor identity differs across JS realms
(under jsdom + Node fetch, an empty 200 throws a Node-realm `SyntaxError` that
fails the jsdom-realm `instanceof` check; the same is true in browsers when the
Response originates from a service worker / iframe). Name comparison is realm-
agnostic. With this in place, empty 200 handlers can return `new HttpResponse()`
again (closer to v1's `res(ctx.status(200))`) instead of the `HttpResponse.json({})`
workaround.
- Drop `URL` / `URLSearchParams` from the env.ts polyfill list. jsdom already
provides browser-spec versions, and Node's `URL.createObjectURL` only accepts
Node-realm `Blob` — replacing it broke the "from computer" upload path in
`LogoInput.tsx`. MSW v2's interceptor doesn't require these to come from Node.
env.ts comments restructured: all polyfill rationale lives in one top-of-file block
so the body is short and the trade-offs are documented in one place.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* chore(tests): switch content-type-builder jest env to shared polyfilled env
`aiClient.test.ts` constructs `new Response(...)` which jsdom does not provide
spec-compliantly. Align CTB with the other admin test suites by using
`@strapi/admin-test-utils/environment`, which polyfills Node's
`fetch`/`Request`/`Response`/streams into the jsdom global.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(tests): make msw v2 + undici body + jsdom realms work together
Three regressions surfaced by running the full front test suite under
jsdom + Node's undici fetch polyfill:
1. `TypeError: Body is unusable: Body has already been read` in every
handler doing `await request.json()`. MSW v2's `RequestHandler.run()`
calls `request.clone()` before invoking the resolver (to keep a copy
for post-hoc logging). Under Node's undici, `Request.clone()` tees the
underlying `ReadableStream` and locks the original body, so the
resolver's `.json()` throws. Fix in `environment.ts`: wrap the global
`Request` constructor to stash the stringified body on every instance
and patch `Request.prototype.json` / `.text` to return the stash when
present. Handlers now get the body regardless of how many times msw
clones internally.
2. `expect.any(Array)` and `toStrictEqual` failing with "serializes to
the same string" against msw response bodies. `Response.prototype.json`
runs Node's `JSON.parse`, so the resulting objects carry Node-realm
`Object`/`Array` prototypes; jsdom's `instanceof`/strict-equality
matchers reject them. Patch `Response.prototype.json` to parse with
the jsdom-realm `JSON`.
3. `useWidgets > should handle network errors gracefully` expected the
toast text "Network error", which msw v1's `res.networkError('Network
error')` surfaced verbatim. v2's `HttpResponse.error()` drops the
custom message and fetch throws `TypeError: Failed to fetch`; update
the assertion accordingly.
Also: `UIDInput` uses `jest.useFakeTimers()`. Adding `doNotFake:
['queueMicrotask', 'setImmediate']` keeps per-test runtime at ~1s
instead of ~10s (plain fake timers stall undici's internal microtasks
between request emission and handler resolution), and an `afterEach`
restores real timers so a failing test cannot leak fake timers into
the next one.
Verified with `nx run-many --target=test:front --parallel=1` across all
12 projects — all suites green.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* test(admin): assert toast container in Widgets error tests
Replace text-match (`findByText('Server error' | 'Failed to fetch')`) with
`[data-sonner-toast]` container assertion. Decouples the test from toast
copy and from msw v1/v2 network-error message differences.
Addresses @jhoward1994's review comment on PR #26065.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* test(admin): assert toast text, scope msw handlers, use jsdom env
- Widgets tests: match actual toast text (Server error / fetch failure
regex) instead of data-sonner-toast container.
- ConfirmBulkActionDialog: mark msw runtime handlers { once: true } so
they do not leak across cases.
- content-type-builder: jest testEnvironment back to jsdom.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* chore(tests): split jest env patches into modules
Refactor packages/admin-test-utils/src/environment.ts by extracting
the msw v2 + undici body + jsdom realm shims into dedicated modules
under src/patches/ (node-globals, request-body-stash,
response-json-realm, types).
Co-Authored-By: Jamie Howard <jhoward1994@gmail.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Add missing Dutch translations across all core packages and plugins,
bringing coverage from ~50% to ~100%.
Changes:
- core/admin: 301 new translations added (564 → 863 keys)
- core/content-manager: 169 new translations added (190 → 359 keys)
- core/content-type-builder: 62 new translations added (226 → 288 keys)
- core/upload: new nl.json with 220 translations
- core/email: 43 translations added (was empty)
- core/content-releases: new nl.json with 104 translations
- core/review-workflows: new nl.json with 15 translations
- plugins/users-permissions: 39 new translations added (42 → 81 keys)
- plugins/i18n: new nl.json with 84 translations
- plugins/cloud: new nl.json with 19 translations
Also standardizes existing translations to use informal "je/jouw"
consistently (was mixed "u/uw" and "je/jouw"), matching Strapi's
informal, developer-friendly tone of voice.
* future(upload): add folder creation functionality in AssetsPage
* future(upload): update CreateFolderDialog to use props for open state and folder name
* future(upload): front/e2e tests for folder creation + error handling
* future(upload): folder list/grid view + navigation
* future(upload): streamline folder navigation
* future(upload): update hover background primary100
* future(upload): set Page.Main width to 100%
* future(upload): refactor folder display to use grid layout in AssetsGrid
* future(upload): enhance folder title handling
* future(upload): add unit tests for folder navigation and title hooks
* future(upload): rename FolderItemComponent to FolderCard
* future(upload): dropfilesmessage to show current folder name
* future(upload): remove width attribute
* fix: improve subnav on mobile so it works with banner
* fix: subnav is scrollable even on tablet
* fix: layout with banner
* fix: sticky banner; tests
---------
Co-authored-by: Adrien Lepoutre <thewebsdoor@gmail.com>
* future(upload): dnd upload file
* fix: positionning of the info message
* fix: added e2e test
* fix: drop files anywhere in the whole page
* fix: created context for dnd
fix: updated test to make it work on safari
* fix: remove translations
* fix: rename and refactor context for dnd upload
* fix: use ref instead of querySelector to target content
* fix(upload): prevent asset deletion when clicking cancel in RemoveAssetDialog
* test: check handleCloseSpy not called with null
* test(e2e): add cancel deletion test
* test(e2e): fix flaky test
---------
Co-authored-by: Ben Irvin <ben@innerdvations.com>