One of our language packs is missing

This weekend I moved one of our Moodle environments from one AWS account to another. That’s a straightforward exercise: deploy the new infrastructure, sync the uploads, import the database, and update DNS. It took less than an hour and there weren’t any hiccups.

Until today, when we noticed that our user provisioner had stopped working. The root cause? A missing language pack. Read on…

Language packs

In a Moodle environment, language packs allow you to deliver the interface in a different language. When you’re building plugins, any strings that you output to the screen are delivered through a string function, so that translators can provide alternatives through the AMOS platform. The base lang is British English (en), which can then be overridden by other packs. The packs themselves are collections of PHP files with string arrays. This is an extract from the American English (en_us) language pack:

1
2
3
$string['adminhelpthemes'] = 'Choose how the site looks (colors, fonts etc)';
$string['advancedfilter'] = 'Advanced filter';
$string['cancelled'] = 'Canceled';

Compare with the same strings in the default pack:

1
2
3
4
5
$string['adminhelpthemes'] = 'Choose how the site looks (colours, fonts etc)';
...
$string['advancedfilter'] = 'Advanced search';
...
$string['cancelled'] = 'Cancelled';

In the second, I’ve omitted all the strings that don’t differ between American and British English.

Data

Language packs are stored in the moodledata/lang directory. This is a typical lang directory:

1
2
3
4
5
6
7
8
drwxrwx--- 2 apache ec2-user  67584 Dec  1 09:13 de
drwxrwx--- 2 apache ec2-user 14336 Nov 20 09:13 en_us
drwxrwx--- 2 apache ec2-user 6144 Jan 21 2018 en_us_local
drwxrwx--- 2 apache ec2-user 71680 Nov 27 09:13 es
drwxrwx--- 2 apache ec2-user 6144 Oct 29 08:13 es_ar
drwxrwx--- 2 apache ec2-user 108544 Nov 27 09:14 es_mx
drwxrwx--- 2 apache ec2-user 71680 Nov 27 09:14 fr
drwxrwx--- 2 apache ec2-user 38912 Dec 2 09:13 ja

The en_us_local directory contains overrides that we’ve made from the interface (in this case, we’ve only changed one string, and we did so almost six years ago). The timestamps vary because translators are always providing updates to strings, and Moodle has a scheduled task (\tool_langimport\task\update_langpacks_task) that checks for updates every night.

This is all relevant, because when I used rsync to update moodledata I neglected to do the language packs. I’d deliberately drilled down to moodledata/filedir to avoid getting all the cache directories. This saved time, but it also meant that we went live with only the default English language pack (en).

Problem

A member of our integrations team alerted me on Monday that the user provisioner wasn’t working. That was a surprise; I’d done a minor version bump on core Moodle but the environment and database were the same. The provisioner uses Moodle’s webservices to create new users, via the core_user_create_users function. The error wasn’t too helpful: Exception: invalidparameter - Invalid parameter value detected. We went down a bunch of blind alleys related to reproducing the error locally with Postman, which led us to thinking there might be a problem with the token (there wasn’t).

Finally, we enabled debugging in the hope that we’d get a more helpful error message, and we did:

Exception: invalidparameter - Invalid parameter value detected (users => Invalid parameter value detected (lang => Invalid parameter value detected (Invalid external api parameter: the value is “en_us”, the server was expecting “lang” type): Invalid external api parameter: the value is “en_us”, the server was expecting “lang” type): lang => Invalid parameter value detected (Invalid external api parameter: the value is “en_us”, the server was expecting “lang” type): Invalid external api parameter: the value is “en_us”, the server was expecting “lang” type)

I had a sinking feeling and realized what the error meant. I navigated to Site administration > Language settings and sure enough, Moodle was complaining that our setting for “Default language”, “English (United States) (en_us)”, was invalid because the language pack wasn’t installed. Installing the language pack (and the other language packs) resolved the error with the provisioner.

Next time

Language packs have a limited footprint in the database; user preferences will remember which one you prefer, and the default for the system is stored. Otherwise, they don’t self-heal: you need to either copy the files or reinstall from the interface. Local customizations must be copied.

As a follow-up, I think the default language pack being missing should raise more flags for the system administrator. I’m not sure what form that should take.