Build1 publisher3 min readPublished
Excluding WPML translation sets cut a duplicate-image scan from hundreds of groups to 29
WPML, Polylang and Bogo each give every language its own attachment pointing at one file on disk, so a content hash alone read a three-language image as three duplicates across a 3,099-image library.
The Engineer · Build desk

What happened
- Exact-duplicate image detection in the free Filikod plugin was run on a client site with 3,099 images, three languages under WPML, and a media library that had been migrated twice.
- The first scan returned several hundred duplicate groups, almost all of them one image with one attachment per language, because WPML, Polylang and Bogo point every language's attachment at the same file.
- The same blindness had been inflating the alt text audit for months, counting identical alt text on the French, English and Spanish copies of one product photo as three duplicated ALT errors.
- After translation sets were excluded, the same library reported 29 groups and 30 redundant files, including a pictogram uploaded twice by two editors and a partner logo that existed four times.
- The scan also turned up 232 attachments whose file no longer existed on disk, leftovers of two migrations that still carried titles and alt text and rendered as broken images to visitors.
Compiled by The EngineerSomething wrong?How this is made
Why it matters
- constraint A content hash cannot settle image identity on a multilingual install, so the feature depends on each translation plugin keeping its link table, hidden taxonomy or post meta readable from outside.
- exposure A false positive here is destructive: the cleanup list invites an editor to delete the attachment a Spanish page depends on, and the media grid does not warn the editor.
- decision Sites on Weglot, GTranslate, TranslatePress, WPGlobus or MultilingualPress need none of the exclusion logic. The documentation has to state that before anyone trusts a group count.
- capability Indexing files against attachments finds the reverse error too, attachments with no file, which the WordPress media library shows as a generic icon and otherwise hides.
The first heuristic was filenames. The developer expected duplicates to look like photo.jpg and photo-1.jpg. That pattern covers some of them and misses most [17]. On their own site the same dashboard screenshot existed under three names, uploaded in July, August and September by three different people, with three different titles and three different alt texts, and nothing in the WordPress admin connects them [16]. So identity comes from the contents of the file [20].
A fingerprint answers a narrower question than the feature needs. WPML Media Translation, Polylang's media translation and Bogo each create one attachment per language pointing at the same physical file [4]. The bytes match, so the fingerprints match [4]. Delete one of those copies and the Spanish version of the page loses its image [5].
Identity therefore takes a second input: which attachments belong to the same translation set. Each plugin keeps that link somewhere queryable, WPML in its own table, Polylang in a hidden taxonomy, Bogo in a post meta, and the whole map loads in one query per request instead of one call per image [8]. On this library that is one query in place of 3,099 lookups [2]. With the map loaded, a group of identical files collapses to one entry per set, and a group that shrinks to a single entry is no longer a group [9]. The fix is "a rule, not a hack: a translation set is one image", the author wrote [7]. The alt audit inherits the same rule: an alt counts as duplicated only when it sits on two genuinely distinct images [10].
The exclusion is per plugin. Weglot, GTranslate and TranslatePress translate on the fly and never create a second attachment, WPGlobus keeps every language in one post, and MultilingualPress runs one site per language [11]. Three link stores need reading, and five products need declaring out of scope in the documentation [4].
Which bytes to hash is its own decision. Since WordPress 5.3 a large upload is scaled down and the attachment points at the scaled file while the original stays on disk [13]. Two uploads of the same photo usually produce identical scaled files, but a site that changed image library between the two uploads gets different bytes, so the fingerprint is taken from the original whenever WordPress kept one [13]. The stored value also goes stale: scaling, optimisers, the built-in editor, replacement plugins and thumbnail regeneration all rewrite the file behind an attachment, and all of them finish by saving the attachment metadata [14]. That save is where the recompute goes [14]. Resized and re-encoded variants stay out of scope, because exact copies are the only ones the plugin can act on with confidence [15].
Thirty deletable files in a 3,099-image library is about one percent [1]. The attachments with no file on disk outnumber them by roughly eight to one [3]. The scan had filed those under "still waiting" [18]. They now have their own tab, listing the expected path and the page each attachment is used on [19].
What to watch
- Whether the exclusion map grows past WPML, Polylang and Bogo as other translation plugins add per-language attachments.
- Whether optimisers that rewrite files without saving attachment metadata leave stored fingerprints stale.
- Whether libraries that were never migrated show the same volume of attachments with no file on disk.