We have a WP multi-site with two distinct languages, it doesn't really matter what those languages are, but in this case it was English GB (:uk:) and Welsh (:wales:).
We tend to develop custom code via mu-plugins, this means we can often have a lot of mu-plugins but we would like to share a text domain. This is because we have a single client, all of the mu-plugins are for this client and thus it makes sense to have a single text domain for all of their custom code. This is analagous, from a text domain point of view, to if you just did everything within a single theme. Similarly, we'd use the same text domain for the custom theme we have built for the client.
So the setup I'm trying to achieve is as such:
mu-plugins
project-plugin
another-project-plugin
themes
project-theme
languages
project.pot
project-cy.po
project-cy.mo
project-en_GB.po
project-en_GB.mo
I created a few bash scripts to help achieve this:
makepot.sh - generate a project level .pot
file, taking into account each mu-plugin and the theme.
#!/usr/bin/env bash
# Creates pot files for each of the mu-plugins and the theme.
# WARNING: It will overwrite the existing pot file so use with caution.
in_array() {
local haystack=${1}[@]
local needle=${2}
for i in ${!haystack}; do
if [[ ${i} == ${needle} ]]; then
return 0
fi
done
return 1
}
ignored_plugins=(
"000-internal-stuff-you-may-want-to-ignore"
"any-other-plugin-you-wish-to-ignore"
)
location=wp-content/languages/your-desired-domain-here.pot
iter=0
merge='';
for dir in ./wp-content/mu-plugins/* ; do
# only for directories, and not symlinks
if [[ -d "$dir" && ! -L "$dir" ]]; then
# skip any plugins we are wanting to ignore
if in_array ignored_plugins "$(basename $dir)"; then
echo "skipping ${dir}"
continue
fi
echo "Making POT file for ${dir}";
if [[ $iter > 0 ]]; then
merge='--merge';
fi;
bin/docker/wp i18n make-pot $dir $location --package-name="Your project name" --domain=your-desired-domain-here $merge
((iter++))
fi;
done
echo "Making POT file for ./wp-content/themes/your-theme-name-here"
bin/docker/wp i18n make-pot ./wp-content/themes/your-theme-name-here $location --package-name=Your project name --domain=your-desired-domain-here --merge
pot2po.sh - copy across the .pot
file to .po
files (if they don't already exist)
#!/usr/bin/env bash
# Copies .pot files to the .po equivalents
# It won't copy over existing files, so if you need to re-generate, you need to delete the .po file first.
locales=(en_GB cy)
for i in "${locales[@]}"; do
if [ ! -f "wp-content/languages/your-desired-domain-here-${i}.po" ]; then
echo "languages/your-desired-domain-here-${i}.po does not exist, creating"
cp -n wp-content/languages/your-desired-domain-here.pot "wp-content/languages/your-desired-domain-here-${i}.po"
else
echo "wp-content/languages/your-desired-domain-here-${i}.po already exists, skipping"
fi
done
po2mo.sh - compile the .po
files to .mo
files.
#!/usr/bin/env bash
# Compiles .po to .mo files.
for file in wp-content/languages/your-desired-domain-*.po ; do
#dest="${file::-2}mo" - does not work in old bash and macs
#solution: https://superuser.com/a/1363835
dest="${file::$((${#file} - 2))}mo"
msgfmt -o "$dest" "$file"
done
echo -e "Done.\n"
They could be tidied up a bit, text domain in a .env or something, but for quick and dirty it's not bad.
With that in place we then need to load the translations. We have a 000-init.php
type thing within mu-plugins
which loads with all our initialisation type stuff, in there we have anything that needs to run after plugins_loaded
, so we load the text domain in there:
add_action(
'plugins_loaded',
static function() {
// ...
// Note: get_locale() here is filtered by multilingualpress and they use their own locales.
// Most of the time this will match with WordPress's but for Welsh they differ, cy vs cy_GB.
// For loading the translation file we want the WP one, so check and correct if that is the case.
$locale = get_locale();
if ( $locale === 'cy_GB' ) {
$locale = 'cy';
}
load_textdomain( 'your-desired-domain-here', WP_LANG_DIR . '/your-desired-domain-here-' . $locale . '.mo' );
}
);
Yeah so MLP use their own locales which for some locales (such as welsh) differs slightly to the WP ones. Not the end of the world.
So we can now use __( 'blah', 'your-desired-domain-here' )
throughout our mu-plugins and theme and, with the scripts above, have this all go to a single, project level translation file(s) (.pot|po|mo
).
We ignore everything in wp-content/languages
apart from these files mentioned above:
/wp-content/languages/*
!/wp-content/languages/your-desired-domain-here.pot
!/wp-content/languages/your-desired-domain-here*.po
!/wp-content/languages/your-desired-domain-here*.mo
And we use wp-translation-downloader to handle core and plugin translations. We included this in our deployment script so we don't have to store them in our repository.