Skip to content

How to Install WordPress Languages Files

# Current state of language management

# Locking in language versions?

With Composer (Bedrock site) the plugin versions are already locked-in. So it naturally makes sense to also lock-in the plugin languages.

The Composer WordPress language packs project by Koodimonni offers composer packages for plugin languages.

However, providing all languages for each plugin version results in a very large amount of packages. Because of this practical issue, the site offers language packages for only a subset of plugins, so chances are, that languages for some plugins your site is using are not available there (e.g. Redirection plugin)

# Using custom Composer installers

There are some custom Composer installers which simply download the latest languages for core, plugins and themes. However, this approach doesn't allow any locking-in of language versions. One may end up with different translations for same site release.

Using wp language command on Trellis deploys

At the time of writing there is no way yet for locking-in languages of core, plugins and themes (for all plugins and themes), so we are stuck with installing the latest language available.

The best approach is using the official mechanisms, which would be the wp language subcommand.

# Setup deploy hooks

We use the finalize-after deploy hook for installing, activating and updating the core/plugins/themes languages of a site for the languages en_GB, de_DE_formal and de_DE:


# Install + activate languages
- name: Install core languages en_GB de_DE
  command: wp language core install en_GB de_DE
    chdir: "{{ deploy_helper.current_path }}"

- name: Install (and activate) core language de_DE_formal
  command: wp language core install de_DE_formal --activate
    chdir: "{{ deploy_helper.current_path }}"

- name: Install plugins languages en_GB de_DE de_DE_formal
  command: wp language plugin install --all en_GB de_DE de_DE_formal
    chdir: "{{ deploy_helper.current_path }}"

- name: Install themes languages en_GB de_DE de_DE_formal
  command: wp language theme install --all en_GB de_DE de_DE_formal
    chdir: "{{ deploy_helper.current_path }}"

# Update installed languages
- name: Update installed core languages
  command: wp language core update
    chdir: "{{ deploy_helper.current_path }}"

- name: Update plugins languages
  command: wp language plugin --all update
    chdir: "{{ deploy_helper.current_path }}"

- name: Install themes languages
  command: wp language theme --all update
    chdir: "{{ deploy_helper.current_path }}"

(All these wp commands are idempotent, they only install/update when it is required.)

In the first part the required languages are installed for core, plugins and themes. In the second part all the installed languages of core, plugins and themes are updated.

# Removing no longer needed languages

Note: If you ever want to remove a language, you can do this here, too. Ideally the language is removed before updating as this removes an unnecessary update of a language that is removed anyway.

# Initial deploy (non-setup site)

Many wp commands including wp language don't work on a WordPress site that is installed but not had been set up yet.

Error: The site you have requested is not installed.
Run `wp core install` to create database tables.

For making an initial deploy possible, the WordPress site has to be setup at the beginning of deploy. You may overwrite everything using a transfer script or backup/restore plugin, etc. This is only important here for being able to install/update the languages on initial deploy.


- name: Install WP (required for installing languages on non-transferred site)
  command: wp core {{ project.multisite.enabled | default(false) | ternary('multisite-install', 'install') }}
           --url="{{ site_env.wp_home }}"
           {% if project.multisite.enabled | default(false) %}
           --base="{{ project.multisite.base_path | default('/') }}"
           --subdomains="{{ project.multisite.subdomains | default('false') }}"
           {% endif %}
           --title="{{ project.site_title | default(site) }}"
           --admin_user="{{ project.admin_user | default('admin') }}"
           --admin_password="{{ vault_wordpress_sites[site].admin_password }}"
           --admin_email="{{ project.admin_email }}"
    chdir: "{{ deploy_helper.current_path }}"
  register: wp_install
  changed_when: "'WordPress is already installed.' not in wp_install.stdout and 'The network already exists.' not in wp_install.stdout"

# Add deploy hooks

For making trellis actually using these new deploy hooks, they need to be added: groups_vars/all/main.yml:

# Deploy hooks
  - "{{ playbook_dir }}/deploy-hooks/sites/{{ site }}-build-before.yml" # build + upload theme assets

  - "{{ playbook_dir }}/roles/deploy/hooks/build-after.yml" # built-in

  - "{{ playbook_dir }}/roles/deploy/hooks/finalize-before.yml" # built-in

  - "{{ playbook_dir }}/roles/deploy/hooks/finalize-after.yml" # built-in
  - "{{ playbook_dir }}/deploy-hooks/finalize-after.yml" # finish site setup for installing languages
  - "{{ playbook_dir }}/deploy-hooks/sites/{{ site }}-finalize-after.yml" # install + update languages

# Improve performance / prevent "translation downtimes"

By default, for each new release the languages would have to be reinstalled, during that time the site can appear partially untranslated (falling back to English by default, or to the language setup by a language fallback plugin).

For preventing this "translation downtime" and for making a language update instead of install possible, the languages folder can be added to project_copy_folders, so it is copied between releases.


  - vendor
  - web/app/languages # copy languages between releases

# Language fallback

By experience, for languages with a formal and non-formal variation, plugins are often only translated for one of these variations. It is possible to fall back at least to the non-formal variation by using a language fallback plugin. With such a plugin installed and enabled, when a string is not translated in current language, it is looked up from the fallback language first, instead of falling back immediately to English.


Last updated

Support Roots

Help us continue to build and maintain our open source projects. We’re a small team of independent developers and every little bit helps.

Sponsor Roots on GitHub