Migrate from Polylang
Polylang keeps one post for each language. easyi18n keeps one post and shows its translation under the language URL. The two cannot run together, so a site moves from one to the other in a single step: a WP-CLI command that keeps every translated address working and retires the extra posts.
What the migration moves
For each post that has translations in Polylang, the command:
- gives the post in the default language the slug of each published
translation, so
/es/sobre-nosotros/keeps answering and now shows that post; - moves the translated posts to the trash;
- keeps a page tree in place, so a translated child page keeps its address under its translated parent;
- carries the menu Polylang assigned to each theme location in each language, and serves it under that language's URLs;
- redirects the other address forms Polylang could use, such as
/language/es/…, to the address easyi18n serves, with a 301.
It does not move the translated text. After the migration a translated
address shows the post in its source language until you
translate and publish it in easyi18n. During that time
the page is marked noindex and is left out of the alternate links, as
Pages without a translation
describes. The translations Polylang held stay in the trashed posts, where you
can read or copy them until WordPress empties the trash.
Translated categories and tags are counted and not migrated. Their names are translated in easyi18n as strings. The strings you translated in Polylang's own screen are not migrated either.
Before you start
The command refuses to run, and writes nothing, unless all of this holds. It names the first condition that fails.
| Refusal | What it needs |
|---|---|
polylang-active | Polylang is deactivated. Do not delete it: its data is what the command reads |
polylang-option | Polylang's settings are still in the database |
default-lang | Polylang's default language is the base language of the easyi18n project |
langs | Every language that has a published translation is a target language of the project |
url-mode | Pretty permalinks, and Polylang set the language from the content or from a directory in the URL. Subdomains and separate domains are not supported |
url-mode-changed | Polylang's URL settings are the ones the first run recorded |
content-enabled | Translate content is off in the plugin. Switch it on after the migration |
trash-disabled | The trash is on. With EMPTY_TRASH_DAYS set to 0, WordPress deletes instead of trashing |
schema | The plugin's database tables are up to date. Load any page of the site and try again |
locked | No import or other migration is running. Try again in a few minutes |
So the order is: connect the project and verify that its languages match Polylang's, take a backup of the database, deactivate Polylang, then run the command.
Run it
The command reports by default and writes only with --apply:
wp easyi18n polylang-migrate
wp easyi18n polylang-migrate --apply
Both print a report. First a census of what the site holds:
url mode: default_lang=en force_lang=1 hide_default=1 rewrite=1 media_support=0 pretty_permalinks=true (accepted)
language en: 42 published (page 12, post 30); 3 in no group (post 3)
language es: 40 published (page 12, post 28); 0 in no group
translated term groups: 9 (not migrated)
menu locations carried: primary (en, es)
Then one line for each group of translations it leaves alone, with the
reason, and a total. The count of menu items is printed by --apply only:
skipped group 118: slug-taken
40 groups, 39 migrated, 4 menu items repointed.
Read the dry run before applying. A skipped group is not touched: its posts stay as they are. Fix the cause and run the command again; it picks up where it left off and does not redo a migrated group.
| Reason | Meaning |
|---|---|
malformed | Polylang's record of the group cannot be read |
post-type | A post of the group is of a type easyi18n does not translate |
base-not-published | The post in the default language is not published |
base-password | The post in the default language has a password |
tree-mismatch, child-language, sibling-name | The page tree of a translation does not line up with the tree of the default language |
trashed-unmarked | A translation was already in the trash before the migration. Restore it or delete it permanently |
slug-taken | The translated slug is reserved, too long, or already used by another post |
assign-refused, trash-failed | A write failed while applying. With trash-failed the address already works and the translated post is still published |
The dry run cannot foresee a failed write, so it can announce more migrated
groups than --apply then reports.
After the migration
- Open a few translated addresses. Each shows its post, in the source language for now.
- Switch on Translate content, translate and publish. The slugs the migration carried stay as they are.
- Replace Polylang's language switcher with the plugin's.
- Keep Polylang installed and deactivated until you are sure. The redirects and the menus do not depend on it: the migration copied what they need.
Do not change Polylang's URL settings after the first --apply. The
redirects follow the settings recorded at that moment.
What cannot be undone
WordPress empties the trash after EMPTY_TRASH_DAYS, 30 days unless the site
changed it. That deletes each retired post for good, with its comments and
with the menu items that pointed at it. Copy what you need out of the trash
before then.
Until then the migration can be reversed by hand, because nothing of Polylang's is deleted. It is a database task: the slugs the migration gave to the default-language posts have to be removed before a retired post is restored, and a post restored from the Trash screen comes back as a draft.