This project is not covered by Drupal’s security advisory policy.
Purpose
flexible_admin_language adds a dedicated language_admin language type that
appears on admin/config/regional/language/detection alongside the built-in
Interface and Content language types. When enabled and customized, the
administration language is resolved by its own negotiation stack and then
bridged into the interface language on admin routes and configured paths.
Why This Exists
Drupal core hard-codes administration language detection into the interface
language stack via the language-user-admin plugin. That plugin only activates
for users with administration permissions and only on Drupal-flagged admin
routes. There is no way to configure a global fallback language for
administration, extend coverage to non-admin routes such as login pages,
or extend admin language detection with other types of plugins.
Many contributed projects were written to work around this but all solve
slightly different problems and neither provides an extensible framework for
others to build on. Rather than solving one or two specific cases only,
this module replaces the core approach with a first-class language type that
site builders can configure the same way they configure Content language:
with a "Customize" checkbox, a dedicated method table, and separate
configuration forms.
How It Works
The language_admin type
When the "Customize administration language detection" checkbox is checked on
admin/config/regional/language/detection, the language_admin type runs its
own negotiation stack. Two plugins are available:
- User preferred administration language — returns the language set on the
user's account under "Administration pages language". This field only appears
on account forms when this plugin is enabled. This reproduces the core
'Account administration language' option but without an admin path
restriction. - Global administration language — returns a site-wide default language
configured atadmin/config/regional/language/admin-language. Can be set to
the site default language, a specific language, or left disabled so this
method has no effect.
When the checkbox is unchecked, language_admin is not separately negotiated.
The bridge plugin
A plugin called Administration language sits in the Interface language
detection table. It activates on admin routes and configured paths, calls
getCurrentLanguage('language_admin') to run the administration language
stack, and returns that result as the interface language. On all other pages it
returns FALSE so the rest of the interface language stack runs normally.
The "Customize administration language" checkbox and the bridge plugin's
"Enabled" checkbox are kept in sync: enabling one automatically enables the
other, and disabling one disables the other.
Path configuration
The bridge plugin has a settings page at
admin/config/regional/language/admin-language-bridge with two sections:
- Anonymous and unprivileged users — path patterns where the
administration language applies for all users regardless of permissions.
Such pages could be login pages for examples where that is expected to
show up in the admin language. - Users with administration permissions — additional path patterns where
the administration language applies for users with the
access administration pagesorview the administration themepermissions.
These are additive on top of all Drupal-flagged admin routes (which also
limit to those two permissions already).
Comparison with related approaches
Drupal core: language-user-admin plugin
Core adds a single negotiation plugin to the Interface language stack. It
activates only for users with access administration pages or
view the administration theme and only on Drupal-flagged admin routes. It
reads the per-user preferred_admin_langcode field but provides no global
fallback, no path pattern configuration, and no way to extend coverage to
non-admin routes such as login pages. The detection settings appear in the
Interface language table with no separate section or "Customize" checkbox of
their own.
This module disables that plugin and replaces it entirely.
admin_user_language
This contributed module is complementary infrastructure rather than a
negotiation method. It does not add a plugin to any language stack; instead it
uses hook_entity_presave to automatically set preferred_admin_langcode on
user accounts based on a global site configuration. The settings form lets site
administrators choose a specific language or mirror whatever the user has set as
their preferred frontend language. A prevent_user_override option hides the
preferred_admin_langcode field on account edit forms and forces the globally
configured language on all users, removing per-user control entirely.
Because it only manages the preferred_admin_langcode field, it still depends
on core's language-user-admin plugin (or a replacement like this module) to
actually apply that preference during language negotiation, which could be
fragile if the user was not yet updated.
On its own the module does nothing for the language of the current request.
admin_language_negotiation
This contributed module is effectively a near-clone of core's
language-user-admin plugin with a single difference: the permission gate.
Where core checks access administration pages or
view the administration theme, this module checks a custom permission
admin_language_negotiation detection. This lets site builders grant
administration language negotiation to roles that do not have full
administration access without granting them any administration capability.
Beyond the permission change the behavior is identical to core: it works only
within the Interface language type, it reads getPreferredAdminLangcode(), it
activates only on Drupal-flagged admin routes via AdminContext::isAdminRoute(),
and it provides no global fallback, no path pattern configuration, and no way to
extend coverage to non-admin routes. Unless finer permission control over the
negotiation trigger is the only requirement, there is minimal value over core's
built-in method.
administration_language_negotiation
This contributed module adds a single Interface language negotiation plugin
protected by a custom permission: use administration language negotiation.
Only users with this permission are affected; for everyone else the plugin
returns FALSE and the rest of the Interface stack runs normally.
The plugin reads the per-user administration language preference and has a
use_default_lang option: when enabled the plugin falls back to the site
default language when no per-user preference is set; when disabled it passes
through to the next method in the Interface stack instead. A condition plugin
system lets site builders attach conditions (role, path, request path) that
must all pass before the plugin fires. A built-in path condition allows to
exclude certain paths even when they are admin paths.
Like core, it works entirely within the Interface language type — there is
no separate language_admin type — so the detection settings are mixed into
the Interface language table and cannot be managed independently.
The key differentiator is the condition plugin system, which is not available
in any of the other solutions. The module also requires locale as a
dependency.
Comparison table
Our module takes a different architectural approach: a dedicated language type
with its own method table, two positive list path patterns (all-users and
privileged), a global default language option, no custom permission
requirement, and no additional dependencies.
| Feature | core | admin_user_language |
admin_language_negotiation |
administration_language_negotiation |
flexible_admin_language |
|---|---|---|---|---|---|
Separate language_admin type |
No | No | No | No | Yes |
| Per-user preference | Always | Optional | Always | Always | Optional |
| Global fallback language | No | Yes (auto-set, stored on users) | No | Site default only | Site default or specific language |
| Coverage for non-admin-route paths for admins | No | No | No | No | Could support with custom plugin (two allowlists) |
| Coverage for anonymous / unprivileged users | No | No | No | Could support with custom plugin | Yes (path based) |
| Exclude paths from admin language | No | No | No | Yes, built-in plugin | No |
| Custom permission required | No | No | admin_language_negotiation detection |
use administration language negotiation |
No |
| Optionally don't allow per user control | No | Yes | No | No | Yes |
locale dependency |
No | No | No | Yes | No |
| Extensible via contrib plugins | No | No | No | Yes (condition plugins only) | Yes (negotiation methods only) |
Dependencies
The module only depends on Language and User modules from core, no other dependencies or requirements.
Project information
- Project categories: Administration tools, Multilingual
- Created by gábor hojtsy on , updated
This project is not covered by the security advisory policy.
Use at your own risk! It may have publicly disclosed vulnerabilities.
