Dosu LogoDosu Logo
Ask
Join our Discord
StrapiPublic
Strapi
DocumentsStrapi
Admin Panel Locale Management
Admin Panel Locale Management
Type
Topic
Status
Published
Created
Jun 29, 2026
Updated
Jun 29, 2026
Created by
Dosu Bot
Updated by
Dosu Bot

Admin Panel Locale Management#

The Strapi admin panel manages UI language through a layered system: a Redux slice holds the active locale, a React Intl provider reads from it to translate strings, and DOM mutations keep the HTML lang attribute in sync. Several recent bug fixes harden this pipeline against race conditions and performance regressions.

Two locales, one admin: The admin UI locale (which language the interface renders in) is stored in Redux and localStorage. The content i18n locale (which locale's content is being edited) is stored in the URL query string as ?plugins[i18n][locale]=<tag>. These are independent and must not be confused.


Redux State: admin_app.language#

The locale is stored in the admin Redux slice (exposed as admin_app) in reducer.ts. The relevant shape is:

language: {
  locale: string; // active BCP-47 tag, e.g. "fr"
  localeNames: Record<string, string>;
}

The setLocale action does three things atomically:

  1. Updates state.language.locale in Redux.
  2. Writes the value to localStorage under the key strapi-admin-language .
  3. Sets document.documentElement.lang via DOM mutation.

Initialization sequence:

  • App startup: StrapiApp.render() reads localStorage (LANGUAGE_LOCAL_STORAGE_KEY), normalizes the value with normalizeAdminLocale(), and seeds the Redux initialState before any React render. App.tsx mirrors this by setting document.documentElement.lang in a one-time useEffect .
  • Post-login: Auth.tsx dispatches setLocale(normalizeAdminLocale(user.preferedLanguage)) whenever user data is loaded, overriding the localStorage default with the per-user preference .

React Intl Layer: LanguageProvider#

LanguageProvider is a thin wrapper around React Intl's IntlProvider. It reads state.admin_app.language.locale via useTypedSelector and deep-merges translations: defaultsDeep(messages[locale], messages.en), so any untranslated key falls back to English.

A onError handler silences MISSING_TRANSLATION errors . Without it, content types with many fields in a non-English locale flood the event loop with hundreds of thrown errors per keystroke, causing 1–3 second typing delays in the Content Manager. Non-MISSING_TRANSLATION errors are still surfaced via console.error.


i18n Locale in Content Manager URLs#

The i18n plugin stores the active content locale in the URL query string as ?plugins[i18n][locale]=<tag>. This is separate from the admin UI locale held in Redux.

Navigation race condition (PR #26167): LeftMenu.tsx previously only set pathname on SubNav.Link targets. The i18n plugin's useEffect re-added the locale query param after mount, leaving one render cycle where locale=undefined — enough to trigger a TypeError: Cannot read properties of undefined (reading 'attributes') crash . The fix reads useLocation().search and forwards the current plugins[i18n][locale] param directly in the to.search property of each SubNav.Link.


Component Schema Race Condition#

useDocumentLayout builds edit layouts by resolving component schemas from the RTK Query cache. During content-type navigation, the content-type configuration response (useGetContentTypeConfigurationQuery) can resolve before the new content type's component schemas are in cache. Accessing components[uid].attributes on an undefined entry throws the same TypeError .

The fix in useDocumentLayout.ts and ComponentConfigurationPage.tsx adds an early-return guard inside the Object.entries(data.components).reduce(...) loop: if components[uid] is absent, the entry is skipped and the useMemo re-runs once schemas load.


Stale Admin Configuration Guard (PR #26625)#

A broader hardening pass addresses the same class of crash — Cannot read properties of undefined — triggered by persisted Content Manager layouts that reference deleted fields, missing metadata, or invalid mainField values. Key additions:

  • A normalizeContentManagerLayout utility strips stale layout entries before they reach render paths.
  • Guards added in Filters.tsx, Media.tsx, DynamicComponent.tsx, and useDocumentLayout.ts for missing attributes, MIME types, and component UIDs.
  • useMenu.ts / useSettingsMenu.ts now guard against missing admin menu/settings arrays.

The fix is intentionally non-mutating — invalid entries are dropped at render time, not written back to the persisted configuration.


Key Files#

FileRole
packages/core/admin/admin/src/reducer.tssetLocale action; LANGUAGE_LOCAL_STORAGE_KEY constant
packages/core/admin/admin/src/components/LanguageProvider.tsxIntlProvider wrapper; reads Redux locale, merges translations
packages/core/admin/admin/src/App.tsxSets document.documentElement.lang on mount
packages/core/admin/admin/src/features/Auth.tsxDispatches setLocale post-login from user.preferedLanguage
packages/core/content-manager/admin/src/components/LeftMenu.tsxPreserves i18n locale in SubNav.Link navigation
packages/core/content-manager/admin/src/hooks/useDocumentLayout.tsGuards against undefined component schema during layout build
packages/core/content-manager/admin/src/utils/layouts/normalizeContentManagerLayout.tsNormalizes stale layout data before render
Documents
Admin Form Validation
Admin Homepage Date Serialization
Admin Modal Management
Admin Panel Locale Management
admin-panel
internationalization
setting-up-admin-panel
Admin User Management
Blocks Editor
Content Manager i18n Preview
locale
Content Manager Pagination
content-manager
Content Manager Preview
preview
Content Manager Query Persistence
content-manager
Content Manager RBAC
rbac
Content Manager URL & Filter State
admin-panel-api
Content Type Builder
content-type-builder
controllers
create-components-for-plugins
draft-and-publish
faq
models
populate-creator-fields
quick-start
store-and-access-data
Database Migration Concurrency and Idempotency
deployment
step-by-step
Date Field Serialization
Discard-Drafts Migration
Document ID Migration
document-service
faq
step-by-step
Document Service Clone & Relation Handling
Document Service Populate Concurrency
Draft & Publish Relation Synchronization
do-not-update-repeatable-components-with-document-service-api
draft-and-publish
lifecycle-hooks-document-service
populate
publishedat-always-set-when-dandp-disabled
relations
status
DynamicZone Stability
components-and-dynamic-zones-do-not-return-id
components-dynamic-zones
no-shared-population-strategy-components-dynamic-zones
EnumerationInput Component
GraphQL Abort Signal Propagation
GraphQL Context Propagation
graphql
internationalization
i18n Locale Validation
internationalization
locale
locale
i18n Locale-Scoped Operations
crud
database-columns
document
i18n-content-manager-locale
internationalization
locale
locale
locale
no-locale-all
parameters
populate
relations
Koa Type Augmentation
Media Library Asset Replace
media-library
Media Library Permissions
Monorepo Module Resolution
Nested Relation Modal Navigation
Relation Modal State Management
relations
Relations Field Ordering & Pagination
Strapi Data Transfer
cli
deployment
Strapi Project Scaffolding
admin-panel
cli
create-a-plugin
deployment
How can I create my first Strapi project on a brand new MacBook with no prior coding experience, including all necessary terminal commands and setup steps?
quick-start
Strapi TypeScript Build Performance
Strapi v5 Plugin API
admin-panel-rbac-store-updated
admin-permissions-for-plugins
breaking-changes
controllers
faq
get-where-removed
helper-plugin
helper-plugin-deprecated
inject-content-manager-component
introduction
introduction-and-faq
model-config-path-uses-uid
pass-data-from-server-to-admin
plugins-migration
rbac
redux-content-manager-app-state
remove-webhook-populate-relations
strapi-imports
Users & Permissions OAuth Flow
Vite Build Configuration
admin-panel
introduction-and-faq
vite
webpack-aliases-removed
Content Manager Mobile and Tablet UI Improvements
FilesManager API Reference
Focal Point Picker in Strapi Media Library
Nested Route File Structure for Strapi Plugins
Plugin-Specific Handling in the Strapi JavaScript Client
Strapi SDK Initialization and Architecture Guide
access-cast-environment-variables
access-configuration-values
adding-support-to-existing-project
admin-panel-customization
advanced-policies
advanced-queries
amazon-s3
api
api-tokens
attributes-and-content-types-names-reserved
audit-logs
auth-zero
authentication
aws-cognito
backend-customization
bulk-operations
bundlers
cas
cli
client
cloudinary
community
configurations
configure-sso
content-api
content-history
content-manager-apis
core-service-methods-use-document-service
cron
custom-fields
customization
data-management
database
database-identifiers-shortened
database-migrations
database-transactions
default-index-removed
default-input-validation
design-system
developing-plugins
development
discord
discord
docker
documentation
documents-and-entries
edit-view-layout-and-list-view-layout-rewritten
email
email-custom-providers
email-nodemailer
entity-service
entity-service-deprecated
environment
error-handling
examples
extension
facebook
favicon
features
fetch
fields
filter
filtering
filters
filters
from-entity-service-to-document-service
functions
github
github
google
google
graphql
graphql-api-updated
guides
homepage
host-port-path
instagram
installation
installing-plugins-via-marketplace
interactive-query-builder
intro
intro
is-supported-image-removed
keycloak
keycloak
koa-body-v6
license-only
linkedin
local-upload
locales-translations
logos
mailgun-provider-variables
media-library-providers
microsoft
middlewares
middlewares
middlewares
middlewares
mysql5-unsupported
new-provider-guide
new-response-format
no-find-page-in-document-service
no-upload-at-entry-creation
okta
only-better-sqlite3-for-sqlite
only-mysql2-package-for-mysql
openapi
order-pagination
order-pagination
patreon
plugin-sdk
plugin-structure
plugins
plugins-extension
policies
policies
populate
populate-select
populating
project-structure
publication-state-removed
query-engine
react-router-dom-6
reddit
register-allowed-fields
releases
removed-support-for-some-env-options
requests-responses
rest
review-workflows
routes
routes
sentry
server
server-api
server-default-log-level
server-proxy
services
services-and-controllers
setup-deployment
single-operations
sort-by-id
sort-pagination
sort-pagination
sso
status
strapi-container
strapi-utils-refactored
strict-requirements-config-files
templates
templates
testing
theme-extension
twitch
twitter
typescript
typescript
understanding-populate
upgrade-to-apollov4
upgrade-tool
upgrades
upload
usage-information
use-document-id
users-and-permissions-providers
users-permissions
vk
webhooks
wysiwyg-editor
yarn-not-default