Adding wp language files on deploy

What is the best way to add wp language files when deploying? I added a task with wp core language install ... to the finalize-after.yml hook for now. Is it possible to include this as a custom hook from deploy-hooks somehow?

Maybe look into this solution as well: Install&Update wordpress languages with composer.

But yeah, you could use hooks to copy/move files around. Or maybe project_shared_children too:


I tried this. It doesn’t work (for me).

Would finalize_after be the right place for this? I was actually wondering if I could - include: myhook.yml a custom hook file there instead of modifying finalize-after.yml. Is this possible as the hooks are included themselves?

You mean to make languages shared like uploads? Might be a good idea, but still I’d like to install languages automatically (and update them after deploy). Maybe an option for language handling built into trellis might make sense?

I’m not entirely sure what’s involved in installing/updating language files.

finalize-after is only required for tasks that require the symlink to be done/deploy finished. This is usually things that actually go through the WP site.

If it’s just copying files it could likely be done before then.

Yeah unfortunately you can’t “extend” a hook we already defined like finalize-after.yml. You’d have to define your own hook and copy the contents of that hook and then add your own stuff.

Related threads:

I’m using wp-cli for this like:
wp core language install de_DE
I guess the build_after hook should work as well, wp-cli would only run in the original folder, not the symlinked one, right?
Handling languages as a shared folder might make sense, though, you would only need to run: wp core language update then after the deploy.

I also want to use this hook - and installing + updating the languages should all happen before symlinking current to this new folder - because otherwise there would be a period of time during language pack installation + update where the site is suddenly in fallback English instead in the selected language.
P.S.: I always also have to run language update for installing the plugin + theme language files.

Yeah build_after does sound like a good fit for this :thumbsup:

Thinking about it, the best way might be to have a shared languages folder where the desired languages are only installed at the first install (no need to install them over and over again), maybe there could be a check if a core language file exists) and otherwise only run wp core language update for every deploy. And this should be in the share_after hook then. Will try this.

Even more ideal would be using composer packages for the language packs of each theme/plugin, currently only those are only available for the most popular plugins/core themes, see Install&Update wordpress languages with composer.

Mabye one could solve this by implementing new composer types which will query the WordPress API directly? It would avoid pulling all possible permutations of plugin/theme version + language pack versions.

As I said, this method didn’t work too well for me. Besides the problem with --no-scripts, on every other deploy or so languages simply did not install. Don’t know why. I had to manually delete the files from vendor on the server and run composer update to get them installed. Maybe a composer cache problem.

I don’t know. Generally you’d just want to add a language and install its files for core and all installed plugins in their current version (if available). Exactly what wp core language update does. I don’t see much sense in dealing with different plugin/language packages and versions here.

I get an error message when trying the command in build-after hook:

- name: Install language de_DE
  command: wp core language install de_DE
    chdir: "{{ deploy_helper.new_release_path }}"

In build-after:

Error: The site you have requested is not installed.
Run `wp core install`.
fatal: [webstaging]: FAILED! => {"changed": true, "cmd": ["wp", "core", "language", "install", "de_DE"], "delta": "0:00:00.379546", "end": "2017-04-02 14:31:09.035257", "failed": true, "rc": 1, "start": "2017-04-02 14:31:08.655711", "stderr": "Error: The site you have requested is not installed.\nRun `wp core install`.", "stdout": "", "stdout_lines": [], "warnings": []}

It worked for me there as far as I remember. You put the command at the end of the hook file, after composer, right?

Anyway, I went with the shared languages folder now and the share-after hook (which also has the advantage of not having to change the build-after hook). I also added a list var to wordpress_sites.yml for it, so it is configurable and some code that uninstalls languages when they are removed from that list. Works quite well in my tests so far. I can post it here if you’re interested.

Yes, I am very interested in this. I use language packs in multiple sites and deploying changes often.

There you go:

Let me know if it works for you (and if not).


It can be more simple.

In finalize-after hook add at the end of block:

  - name: Install WP language es_ES
    command: wp core language install es_ES --activate
      chdir: "{{ deploy_helper.current_path }}"

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

And in roles/deploy/defaults/main.yml:

  - path: web/app/uploads
    src: uploads
  - path: web/app/languages
    src: languages

Sharing languages folder the install only work on first deploy and on successive deploys is skipped (by wp-cli).
The second task update (currently active) language for updated core/themes/plugins.


Sure, I had something like this as well, but I don’t want this hard-coded into the hook, prefer to have it configurable as a list in wordpress_sites.

Didn’t know this, but makes sense. It puts out a warning, though. It’s no big deal to check for the existance of the po-file, also taught me some Ansible syntax :slight_smile:

What ansible code can be added to avoid the warning?

You can have deploy hooks per site:

In ‘roles/deploy/defaults/main.yml’:

  - "{{ playbook_dir }}/roles/deploy/hooks/finalize-after.yml" # built-in
  - "{{ playbook_dir }}/deploy-hooks/sites/{{ site }}-finalize-after.yml"

Then as shown by pacotole further above in this thread,
e.g. for site in ‘deploy-hooks/sites/’:

  - name: Finish WP installation (required for installing languages on initial deploy)
    command: wp core install --skip-email --title="The WordPress site" --admin_user=theuser --admin_password="the password"
      chdir: "{{ deploy_helper.current_path }}"

  - name: Install WP language es_ES
    command: wp core language install es_ES --activate
      chdir: "{{ deploy_helper.current_path }}"

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

Edit: I had an error when deploying the first time (to a fresh staging system). The reason was that the WordPress installation has to be finished. I added a wp cli command for this before the command for installing the languages (wp core install --[...]) (wp core is idempotent, it won’t reconfigure an existing configured WordPress installation).


Updated approach:

Ensure finished WordPress setup and installing additonal languages in finalize-after site hook:
In case of formal variant languages it can be helpful to also install the non-formal language as a fallback because plugins can be disproportionally translated for the non-formal variant.

- 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"

  - name: Install WP language de_DE
  command: wp core language install de_DE
    chdir: "{{ deploy_helper.current_path }}"

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

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

The username / password for WordPress setup can reflect the real credentials (before transferring the database to system, e.g. using a plugin instead, which in turn requires an admin login) are stored in vault.yml which should be the correct place for secrets:

# Variables to accompany `group_vars/production/wordpress_sites.yml`
# Note: the site name (``) must match up with the site name in the above file.
      db_password: "example_dbpassword"
      # Generate your keys here:
      auth_key: "generateme"
      secure_auth_key: "generateme"
      logged_in_key: "generateme"
      nonce_key: "generateme"
      auth_salt: "generateme"
      secure_auth_salt: "generateme"
      logged_in_salt: "generateme"
      nonce_salt: "generateme"
    admin_user: "wordpress-admin-username"
    admin_password: "wordpress-admin-password"

And finally for speeding up and preventing gaps of missing language for longer duration during deploy:
roles/deploy/defaults/main.yml (at line 14):

# There are certain folders you'll want to copy from release to release to speed up deploys.
# Examples: Composer's `vendor` folder, npm's `node_modules`.
# These should not be part of project_shared_children since dependencies need to be atomic and tied to a deploy.
  - vendor
  - web/app/languages
1 Like