Skip to content

Setting Up Language Packs

Omnalingo depends on WordPress language packs (.mo, .po, and .l10n.php files) for gettext string detection and gap analysis. Without them, WooCommerce and plugin UI strings either show in the source language or fall through to AI translation unnecessarily. This guide covers how the download process works and how to fix it when it goes wrong.

Omnalingo’s GettextGapService checks whether each gettext string on a page already has coverage in the installed .mo or .l10n.php file for the target language. Strings with coverage are counted toward translation progress but are not stored in Omnalingo’s database — WordPress handles them. Strings without coverage are stored and queued for AI translation.

If the language pack for WooCommerce has not been downloaded, GettextGapService will find no coverage for WooCommerce strings and send all of them to the AI — wasting API quota on strings that WooCommerce already translates. Getting language packs in place before scanning saves API credits and produces more consistent translations.

Language pack downloading is handled by LanguagePackService (src/Service/LanguagePackService.php) via a three-phase process triggered automatically when you add a new target language:

Phase 1 — Core language packs: Calls WordPress’s wp_download_language_pack($locale) for each locale. Downloads .mo, .po, and .l10n.php files for WordPress core into wp-content/languages/. This must complete first because WordPress needs these files to correctly query api.wordpress.org for plugin and theme packs.

Phase 2 — Update checks: Deletes the _site_transient_update_plugins and _site_transient_update_themes options to force a fresh API query. This ensures newly added locales are included in the check.

Phase 3 — Plugin and theme packs: Downloads translation packs for all active plugins and the active theme (plus its parent). Uses WordPress’s Language_Pack_Upgrader internally.

Download progress is stored in the omnalingo_langpack_status WordPress option and polled by the admin sidebar widget.

SELECT option_value FROM wp_options WHERE option_name = 'omnalingo_langpack_status';
status valueMeaning
startingDownload initiated, about to begin Phase 1
downloading_corePhase 1 in progress
checking_updatesPhase 2 in progress
downloading_packsPhase 3 in progress
completeAll phases finished
errorPre-flight filesystem check failed
idle / no rowNo active download

A stale-detection mechanism in LanguagePackService::getStatus() automatically clears any active state that has been running for more than 5 minutes. If the widget is still showing a spinner after 5 minutes, the process crashed — wait for auto-clear or delete the option manually.

To see which locale files are present on disk:

Terminal window
ls wp-content/languages/ | grep de

Expected files for German (de_DE):

  • de_DE.mo — WordPress core binary translation file
  • de_DE.po — WordPress core source translation file
  • de_DE.l10n.php — WordPress 6.5+ performant format (may not be present on older WP)
Terminal window
ls wp-content/languages/plugins/ | grep de_DE
ls wp-content/languages/themes/ | grep de_DE

Plugin packs follow the naming convention pluginslug-de_DE.mo. For WooCommerce: woocommerce-de_DE.mo.

The most common failure. LanguagePackService runs wp_can_install_language_pack() as a pre-flight check. If this returns false, status is set to error and the download aborts immediately.

Fix: Check write permissions on wp-content/languages/:

Terminal window
ls -la wp-content/languages/

The directory must be writable by the web server user (typically www-data or apache). If it is not:

Terminal window
chmod 755 wp-content/languages/
chown www-data:www-data wp-content/languages/

Core Pack Downloaded But Plugin/Theme Packs Missing

Section titled “Core Pack Downloaded But Plugin/Theme Packs Missing”

Phase 3 depends on the update_plugins and update_themes transients returning data that includes the newly added locale. On recently migrated or cached sites, these transients may contain stale data.

Fix: Delete the transients manually:

DELETE FROM wp_options
WHERE option_name IN (
'_site_transient_update_plugins',
'_site_transient_update_themes'
);

Then save the language settings again — SettingsController::updateSettings() will detect the locale is still missing and trigger a fresh download.

The admin widget polls GET /wp-json/omnalingo/v1/settings/language-packs/status. If the status option has been in an active state for more than 5 minutes, LanguagePackService::getStatus() clears it automatically. If the widget is stuck before the 5-minute threshold:

  1. Check the plugin log for [ERROR] entries from LanguagePackService.
  2. Clear the status manually:
    DELETE FROM wp_options WHERE option_name = 'omnalingo_langpack_status';
  3. Retry via the REST API:
    Terminal window
    curl -X POST -H "X-WP-Nonce: $NONCE" \
    https://yoursite.com/wp-json/omnalingo/v1/settings/language-packs

Language files live in wp-content/languages/ and are typically not included in code-only deployments. After migrating a WordPress site to a new server, language files may be absent even though Omnalingo’s settings still show languages as configured.

Automatic recovery: Simply save the language settings in wp-admin → Omnalingo → Settings → Languages once. SettingsController::updateSettings() compares configured locales against get_available_languages() (which reads the filesystem). Any missing locales trigger a download automatically.

Manual verification before saving:

Terminal window
wp eval 'echo implode("\n", get_available_languages());'

If the target locale (e.g., de_DE) is not listed, it needs to be downloaded.

Manually Downloading Language Packs via WP-CLI

Section titled “Manually Downloading Language Packs via WP-CLI”

If you need to force a download outside of the Omnalingo UI:

Terminal window
# Download WordPress core pack
wp language core install de_DE
# Download all plugin packs for a locale
wp language plugin install --all de_DE
# Download active theme pack
wp language theme install twentytwentyfour de_DE

After manually installing packs, save the language settings in Omnalingo once — the health check will confirm they are present and skip re-downloading.

After language packs are installed, run a scan and then check whether gettext strings are being classified correctly:

SELECT type, COUNT(*) FROM wp_omnalingo_strings GROUP BY type;

If you see a large number of gettext-type strings with no corresponding html strings, something is wrong with the string classification — check the scan setup. A healthy scan on a WooCommerce site typically shows a mix of html and gettext types.