Skip to main content
Multilingual associations in Joomla
On this page
# Topics

Multilingual associations in Joomla

16 June 2026

You built your website in two languages. A visitor reads your English "About" page, clicks the language switcher to read it in Dutch, and lands on the Dutch home page instead of the Dutch "About" page. That small, frustrating jump is exactly the problem Multilingual Associations solve.

An association is a link that tells Joomla: this English article and that Dutch article are the same page in two languages. Once Joomla knows that, the language switcher keeps the reader on the same content, and search engines learn which pages are translations of each other.

Associations are the glue that turns several single-language items into one piece of content with many faces.

This article starts with the basics for owners and editors, moves on to the setup work for administrators, and ends with the database, the PHP API, and the SEO details for developers. The goal is simple: help you understand associations well enough to build a multilingual Joomla site that behaves correctly.

1. The Basics

1.1 What an Association Is

An association is a record that groups together the equivalent versions of one item across your installed languages. If you have an English article, a Dutch article, and a German article that all say the same thing, an association ties the three together as one logical page.

The key idea: associations do not translate anything. You still write each translation yourself, as a separate article in its own language. The association simply records "these belong together" so the rest of Joomla can act on that knowledge.

An association is not a translation. It is a statement that two or more items already are translations of one another.

1.2 The Problem They Solve

On a multilingual site, the language switcher has to answer one question every time a visitor clicks it: "Where is this same page in the other language?" Without associations, Joomla cannot answer, so it does the only safe thing and sends the visitor to that language's home page.

Visitor on   /en/about-us
clicks       "Nederlands"
        |
        v
 association exists?
   +----------------+------------------+
  yes                                  no
   |                                    |
 go to /nl/over-ons               go to /nl/  (the Dutch home page)
 (same page, other language)      (visitor is lost)

1.3 What You Can Associate

Out of the box, Joomla supports associations for the core content types. Each is linked to its equivalents through the same mechanism:

Item typeComponent
Articles com_content
Categories com_categories (used by content, contacts, news feeds, banners)
Contacts com_contact
News feeds com_newsfeeds
Menu items com_menus
Site modules com_modules

Each of these types registers itself with the multilingual framework, so they all link translations the same way. Tags and custom fields are not associated: they can carry a language, but Joomla does not group them into translation sets. Modules are a special case covered in section 4: they are mostly controlled by language assignment, yet site modules can also be associated.

1.4 Three Words People Mix Up

Multilingual Joomla has three terms that sound alike. Keeping them apart saves a lot of confusion:

TermMeaning
Language pack The interface translation (buttons, labels). Files on disk.
Content language A language your content can be written in (a row in #__languages).
Association A link saying two content items are the same page in different languages.

This article is about the third one. It assumes the first two already exist.

Back to top

2. Prerequisites: A Multilingual Site

Associations only appear and only work when the site is set up for multiple languages. Four things must be in place first.

2.1 Install Content Languages

Under System → Install → Languages, install each language pack you need. Then, under System → Manage → Content Languages, create one Content Language per language and publish it. The content language carries the URL code (the sef, such as en or nl), the language tag (en-GB, nl-NL), and the flag image.

System → Manage → Content Languages
   ├─ English (en-GB)   sef: en   published
   └─ Nederlands (nl-NL) sef: nl   published

2.2 Enable the Language Filter Plugin

The system plugin plg_system_languagefilter is the engine of the whole multilingual system. Enable it under System → Manage → Plugins. When it is off, there is no language switcher, no language URL prefix, and no Associations tab anywhere.

2.3 Assign Content to a Language

Every article, category, menu item, and module has a Language field. To take part in an association, an item must be set to one specific language, not the special value All.

Language settingBehaviour
All Shown in every language. Cannot be associated, and does not need to be.
English (en-GB) Shown only in English. Can be associated with a Dutch and a German item.

Use All for things that are identical in every language (a logo module, a contact email). Use a specific language for anything you actually translate.

2.4 Turn On "Item Associations"

Open the Language Filter plugin and check the parameter Item Associations. Set it to Yes. This single switch decides whether the switcher and the hreflang tags use associations at all:

Language Filter plugin
   ├─ Item Associations: Yes   → switcher follows associations, hreflang tags added
   └─ Item Associations: No    → switcher always jumps to the home page
Back to top

3. Creating and Managing Associations

There are two places to build associations: inside an item's edit screen, and inside the dedicated Multilingual Associations component. Both write to the same place.

3.1 The Associations Tab

Open any associable item (for example an article) and look for the Associations tab. It shows one row per other content language. For each language you can either pick an existing item or create a new translation:

Edit Article "About Us" (English)
   └─ Associations tab
        Nederlands (nl-NL): [ Select or create the Dutch version ]
        Deutsch (de-DE):    [ Select or create the German version ]

If the Associations tab is missing, one of the four prerequisites in section 2 is not met, almost always the Language Filter plugin or the item's language being set to All.

3.2 The Multilingual Associations Component

For real translation work, the editor tab is slow. The dedicated tool at System → Manage → Multilingual Associations (the component com_associations) gives you a side-by-side editor:

Multilingual Associations
   ├─ Item Type:        Articles
   ├─ Source language:  English (en-GB)
   └─ Target language:  Nederlands (nl-NL)

   +-------------------------+   +-------------------------+
   |  English article        |   |  Dutch article          |
   |  (read-only reference)  |   |  (edit / create here)   |
   +-------------------------+   +-------------------------+

You pick an item type and two languages, click a source item on the left, and edit or create its counterpart on the right. This is the fastest way to translate a whole site section without losing your place.

3.3 One Item Per Language, One Group

Two rules govern every association group:

  • An association group holds at most one item per language. You cannot link two English articles together.
  • An item belongs to one association group per type. Joining a new group removes it from the old one.
Back to top

4. Menus, Home Pages, and Modules

Articles are the easy part. The navigation around them needs more care, and this is where most multilingual sites break.

4.1 One Menu Per Language

The clean pattern is one menu per language: a "Main Menu (English)" and a "Hoofdmenu (Nederlands)". Each menu's items are set to that language. You then associate equivalent menu items across the menus, exactly like articles, on the menu item's Associations tab.

4.2 A Home Page Per Language (Required)

Every content language must have its own default menu item (its home page), set to that language. This is not optional. If the default language has no home menu item, the front-end shows a hard error:

Error: there is no home menu item for the default language.

So a two-language site has two "Home" menu items, each marked Default, each in its own language, and the two associated together.

4.3 Modules: Assigned by Language First

For modules, the Language value does the heavy lifting. Joomla publishes only the modules that match the active language, so day to day you control modules by assigning each one a language rather than by associating them. A common setup:

ModuleLanguageResult
Logo / banner image All One module, shown everywhere
Welcome text (English) en-GB Shown only on English pages
Welkomstekst (Dutch) nl-NL Shown only on Dutch pages

That said, Joomla 6 does support module associations for site modules. When the Language Filter is active, a site module's editor shows an Associations toolbar button, and the type "Modules" appears in the Multilingual Associations component. Use it when you want the tooling to track which modules are translations of each other; administrator modules cannot be associated.

4.4 The Language Switcher Module

Publish the Language Switcher module (mod_languages) so visitors can change language. Set its own language to All so it shows on every page. It is this module, combined with associations, that delivers the experience from section 1.2.

Back to top

5. The Language Switcher and Fallback Behaviour

It helps to know exactly what the switcher does when a visitor clicks another language, because the behaviour changes with your settings.

5.1 The Decision the Switcher Makes

Visitor clicks another language
        |
        v
 Item Associations = Yes ?
   +-----------------+-----------------+
  no                                  yes
   |                                    |
 go to that language's            association for THIS item exists ?
 home page                          +---------------+--------------+
                                   yes                            no
                                    |                              |
                              go to the associated            go to that language's
                              item (same page)                home page

5.2 Why a Switch Lands on the Home Page

If a click always lands on the home page even though associations are on, the current page simply has no association for the target language yet. The fix is to create the missing translation and associate it. The switcher is only as complete as your associations are.

5.3 Items Set to "All"

An item whose language is All appears in every language, so there is nothing to switch to. The switcher stays on the same URL. That is correct behaviour, not a bug.

Back to top

6. Associations and the Router

The language switcher decides which item to go to. The router decides what the URL looks like. These are two different jobs, and seeing the split makes routing problems much easier to debug.

6.1 Two Jobs, Two Subsystems

QuestionAnswered by
Where is this page in the other language? Associations (the shared key)
What is the clean URL for that page? The SEF router + Language Filter plugin

6.2 The Flow When a Language Switches

Visitor on /en/about-us  (article id 5)
        |
        v
 association lookup    → nl-NL item is article id 8
        |
        v
 router builds the URL for id 8 in nl-NL
        |
        v
 /nl/over-ons          (sef prefix "nl" comes from the content language)

The URL prefix /nl/ is not stored in the association. It comes from the content language sef code (section 2.1), which the Language Filter plugin adds to every front-end URL. The association only supplies the target id; the router turns that id into the path.

6.3 Why Menu Associations Drive Routing

The router prefers to build a URL from a menu item, because the menu item defines the route segment. When an article has no associated menu item in the target language, the router has to fall back to a generic route, which is often uglier or lands on the wrong page. This is why associating menu items (section 4) matters as much as associating articles: it gives the router a clean anchor in every language.

Back to top

7. Under the Hood: the #__associations Table

All associations, for every content type, live in a single, deceptively small table: #__associations. Understanding its three columns explains the whole feature.

ColumnHolds
id The id of the associated item (for an article, the #__content.id).
context What kind of item it is, such as com_content.item (see section 8).
key A 32-character hash that is the same for every item in one group.

7.1 The key Is the Glue

There is no "pairs" table and no left/right columns. Items belong together because they share the same key hash. Joomla computes that hash when you save an association, and every member of the group stores it:

id   context           key
---  ----------------  --------------------------------
5    com_content.item  1a79a4d60de6718e8e5b326e338ae533   <- English article (id 5)
8    com_content.item  1a79a4d60de6718e8e5b326e338ae533   <- Dutch article   (id 8)
12   com_content.item  1a79a4d60de6718e8e5b326e338ae533   <- German article  (id 12)

To find the translations of article 5, Joomla reads its key, then selects every other row with that same key and context. Three articles, three rows, one shared hash.

7.2 The Primary Key

The primary key is the pair (context, id), with an index on key. That composite key enforces rule 3.3: a given item appears once per context, so it can never sit in two association groups at the same time.

SELECT id
FROM   #__associations
WHERE  context = 'com_content.item'
AND    key = (
    SELECT key FROM #__associations
    WHERE context = 'com_content.item' AND id = 5
);
-- returns 5, 8, 12 : the whole translation set

7.3 What Is NOT Here

The table stores only ids, contexts, and hashes. The actual article language lives in #__content.language; the title lives in #__content.title. The associations table is a pure index of relationships, nothing more.

Back to top

8. Association Contexts

The context column is how one table serves every content type at once. Each associable type registers a context string, and Joomla only ever compares rows that share the same context.

ContextItem type
com_content.item Articles
com_categories.item Categories (all components that use categories)
com_contact.item Contacts
com_newsfeeds.item News feeds
com_menus.item Menu items
com_modules.item Site modules

Because comparison is scoped to a context, an article (id 5, com_content.item) and a menu item (id 5, com_menus.item) never collide, even though they share the numeric id. The context keeps every type in its own namespace inside one table.

Back to top

9. The PHP API

Developers rarely touch #__associations directly. Joomla provides a small helper class plus per-component helpers that return ready-to-use data.

9.1 Is the Feature Even On?

Before reading associations, check that the site is configured for them. The check folds together "is the Language Filter plugin enabled" and "is Item Associations set to Yes":

use Joomla\CMS\Language\Associations;

if (Associations::isEnabled()) {
    // associations are active on this site
}

9.2 Reading a Group

The core helper returns the translations of one item, keyed by language code:

use Joomla\CMS\Language\Associations;

$associations = Associations::getAssociations(
    'com_content',          // the extension
    '#__content',           // the item's table
    'com_content.item',     // the association context
    $articleId              // the id of the current item
);

// $associations is keyed by language tag:
//   [ 'nl-NL' => (object) ['id' => 8,  ...],
//     'de-DE' => (object) ['id' => 12, ...] ]

9.3 The Front-end Helper (URLs Ready to Print)

For building a switcher or a list of "read this in another language" links, each component ships a helper that returns SEF URLs, not just ids:

use Joomla\Component\Content\Site\Helper\AssociationHelper;

$links = AssociationHelper::getAssociations($articleId, 'article');

// [ 'nl-NL' => 'index.php?option=com_content&view=article&id=8&lang=nl-NL',
//   'de-DE' => 'index.php?option=com_content&view=article&id=12&lang=de-DE' ]

This is exactly what the Language Switcher module calls under the hood to turn each flag into a working link.

Back to top

10. Adding Associations to Your Own Component

If you build a component with translatable items, you can plug into the same system. The Multilingual Associations component discovers support automatically by looking for one helper class.

10.1 Ship an AssociationsHelper Class

Provide a class named AssociationsHelper in your component's administrator Helper namespace. It extends the abstract base AssociationExtensionHelper, which already implements AssociationExtensionInterface for you. Joomla finds it by convention:

namespace Joomla\Component\Example\Administrator\Helper;

use Joomla\CMS\Association\AssociationExtensionHelper;

class AssociationsHelper extends AssociationExtensionHelper
{
    protected $extension = 'com_example';
    protected $itemTypes = ['item'];
    protected $associationsSupport = true;

    public function getType($typeName = '')
    {
        // describe the 'item' type: its tables, fields, and views
    }

    public function getAssociationsForItem($id = 0, $view = null)
    {
        // return the association group for one item, keyed by language
    }
}

The interface itself only requires two methods: hasAssociationsSupport() (the base class answers it from $associationsSupport) and getAssociationsForItem(). The getType() method describes your item's tables and fields so the side-by-side editor can render them.

When your edit form includes an associations field, the framework writes the #__associations rows for you on save, using your component name as the context base (for example com_example.item). You do not compute the hash yourself; the table class does it.

10.3 Use the Behaviour, Not Raw SQL

Read associations through Associations::getAssociations() rather than querying the table directly. The helper handles the context, the published state, the access level, and the fallback to the default language, all of which are easy to get wrong by hand.

Back to top

11. Performance on Large Sites

A worry on big multilingual sites is that all this lookup work slows pages down. In practice associations are one of the cheaper parts of the system, because of how the table is built.

11.1 The Lookup Is a Single Indexed Read

Finding the translations of an item is two indexed queries, not a scan. The primary key (context, id) finds the item's key instantly, and the idx_key index finds every sibling in the group:

#__associations indexes
   ├─ PRIMARY (context, id)   → find THIS item's key
   └─ idx_key (key)           → find the rest of the group

Even with hundreds of thousands of rows, each lookup touches only the few rows that share one key. The cost grows with the size of one translation group (a handful of languages), not with the size of the whole site.

11.2 What Actually Costs Time

  • Rendering each language URL: the router work in section 6 is heavier than the association read. Joomla caches built routes to soften this.
  • Per-language menus: keeping one menu per language keeps each menu small, which keeps menu and route caches efficient.
  • Many languages: ten languages means up to ten rows per group. This multiplies row count but not lookup cost, because the index still jumps straight to one group.

11.3 Practical Advice

Leave the standard indexes in place, enable Joomla's caching, and do not query #__associations in a loop. If you need many items' associations at once, gather the ids first and read them in as few calls as possible rather than one call per item.

Back to top

12. Web Services API and Headless

This is a common question for headless and decoupled setups, and the honest answer is that associations are mostly an editorial and runtime concern.

  • There is no dedicated public com_associations REST endpoint in core Joomla. The Multilingual Associations screen is an admin-side editing tool, not a data API.
  • You read translated content through the normal component endpoints, one language at a time, for example:
curl -H "X-Joomla-Token: <token>" \
  "https://example.test/api/index.php/v1/content/articles/8"

To rebuild the relationships in a headless front-end, the most reliable source is the #__associations table itself: select the rows that share a key within a context, then fetch each item by id through its component endpoint. Treat the table as the source of truth and join on the key.

Back to top

13. SEO and Metadata

Associations are not only a navigation convenience. They are the single most important multilingual SEO signal Joomla produces.

When Item Associations are on and a page has associations, Joomla adds hreflang alternate links to the page head. These tell Google which URLs are translations of each other, so the right language version appears in the right country's results:

<link rel="alternate" hreflang="en-GB" href="https://example.test/en/about-us">
<link rel="alternate" hreflang="nl-NL" href="https://example.test/nl/over-ons">
<link rel="alternate" hreflang="x-default" href="https://example.test/en/about-us">

The x-default entry points at your default language and is the version shown to visitors whose language you do not target. Joomla adds it automatically.

13.2 No Associations Means No hreflang

If a page has no associations, it gets no alternate links. Search engines then treat your English and Dutch pages as unrelated, and may even read near-duplicate translations as duplicate content. Complete associations are how you avoid that.

13.3 Clean Per-language URLs

The Language Filter plugin gives each language its own URL prefix (/en/, /nl/) from the content language sef code. Associations make sure the switcher links between those clean URLs rather than dumping users at the root, which keeps both visitors and crawlers on coherent paths.

Back to top

14. Migration Considerations

When you move a site to Joomla 6 from an older version, associations usually survive the database upgrade untouched. Most "lost translation" reports after a migration are configuration problems, not data loss. Walk this checklist before assuming the data is broken.

14.1 What to Check After a Migration

CheckWhy it breaks switching
Language Filter plugin enabled If off, no switcher and no Associations tab appear at all.
Item Associations = Yes If reset to No, every switch jumps to the home page.
Item language assignments Items reset to All drop out of their groups silently.
One Default home per language A missing default home throws a fatal front-end error.
Menu associations Missing menu links push the router onto fallback routes.
Third-party association helpers Extensions must extend AssociationExtensionHelper for the modern API.

14.2 The Data Is Probably Fine

The #__associations table carries over as-is, so the key groups from your old site still exist. A quick sanity check confirms it:

SELECT context, COUNT(*) AS rows, COUNT(DISTINCT key) AS groups
FROM   #__associations
GROUP  BY context;
-- healthy: each context shows several rows per group, not one row per group

If a context shows almost as many groups as rows, many groups have only one member, which means associations were created but their partners lost their language assignment during the move. Fix the language fields, and the existing keys link up again.

Back to top

15. Common Mistakes and Pitfalls

15.1 No Associations Tab

Symptom: an article's edit screen has no Associations tab.

Fix: enable the Language Filter plugin, make sure you have at least two published content languages, and set the item's Language to a specific language instead of All.

15.2 Switcher Always Goes Home

Symptom: clicking another language always lands on its home page.

Fix: set Item Associations to Yes in the Language Filter plugin, then create the missing translation and associate it with the current page.

15.3 "No Home Menu Item for the Default Language"

Symptom: the front-end shows a fatal error about a missing default home.

Fix: create one Default home menu item per content language, each set to its own language, and associate them together.

15.4 Content Set to "All"

Symptom: an item cannot be associated, or the same text appears in every language.

Fix: use All only for genuinely shared items. Anything you translate must be set to a specific language so it can join an association group.

15.5 Associating Unpublished or Restricted Items

Symptom: the switcher link exists but leads to a 404 or an access-denied page.

Fix: make sure every item in an association group is published and shares a public access level. The helper hides associations the visitor cannot view.

15.6 Editing the Table by Hand

Symptom: hand-inserted rows with a guessed key do not link items.

Fix: let Joomla compute the key through the Associations tab or the component. A mismatched hash silently breaks the group.

Back to top

16. Best Practices

If you remember only a few things from this article, remember these:

  • Set up the foundation first: content languages, the Language Filter plugin, and Item Associations = Yes, before you create content.
  • One menu and one Default home per language, all associated. This prevents the most common multilingual error.
  • Associate as you translate, using the Multilingual Associations component. An untranslated page is a dead end for the switcher.
  • Reserve "All" for truly shared items like a logo or a global module. Everything you translate gets a specific language.
  • Trust the tools: never hand-edit #__associations. Let Joomla compute the key.
  • Check the hreflang output on a few pages. If the alternate links are missing, your associations are incomplete, and your multilingual SEO is leaking.
Back to top

17. Quick Reference

PREREQUISITES   Language Filter plugin ON + 2 published content languages
TURN ON         Language Filter plugin -> Item Associations: Yes
ASSOCIATE       open item -> Associations tab -> pick/create per language
BULK EDIT       System -> Manage -> Multilingual Associations (com_associations)
HOME PAGES      one Default menu item per language, associated
MODULES         assigned by Language; site modules can also be associated
SWITCHER        publish mod_languages (Language: All)
ROUTER          association finds the id ; router builds the /xx/ URL
DATABASE        #__associations (id, context, key) ; same key = one group
CONTEXTS        com_content.item, com_menus.item, com_categories.item, ...
PHP CHECK       Associations::isEnabled()
PHP READ        Associations::getAssociations('com_content','#__content',
                  'com_content.item', $id)
FRONT-END URLS  AssociationHelper::getAssociations($id, 'article')
SEO             associations -> hreflang alternate + x-default links
RULE            one item per language per group ; one group per item
Back to top

18. Summary

Multilingual Associations are the layer that turns a pile of single-language items into a coherent multilingual website. They do not translate your content; they record that your translations belong together, and then Joomla uses that knowledge everywhere:

  • For visitors: the language switcher keeps them on the same page instead of dropping them at the home page.
  • For editors: the Associations tab and the Multilingual Associations component make linking translations quick.
  • For administrators: the Language Filter plugin, per-language menus, and one Default home per language hold the system together.
  • For developers: a single #__associations table, scoped by context and grouped by a shared key, plus the Associations helper class, expose everything cleanly.
  • For SEO: complete associations produce the hreflang alternate links that tell search engines which pages are translations of one another.

Once you understand the prerequisites, the Associations tab, the shared key in the database, and the role of the Language Filter plugin, you can build and debug a multilingual Joomla site with confidence.

If your language switcher keeps sending visitors to the home page, or your translated pages are missing their hreflang tags, the cause almost always lives in this layer. It pays to get associations right early, because they quietly decide whether your multilingual site feels like one website or several disconnected ones.

Back to top
Multilingual associations in Joomla
Peter Martin
Peter Martin

Joomla specialist and Linux admin for fast, secure and scalable websites.