Main menu

Modelling a bundle configuration as XML

For a general description of the bundle definition model, see the configuration settings. This page is aimed at developers. It describes how a bundle configuration can be defined as XML.

Self-describing bundles

Consider a theme that declares a single text domain. You may have seen file headers like this in style.css:

/*
Theme Name: My cool theme
Text Domain: cooltheme
Domain Path: /languages/
*/

This is as far as WordPress is able to describe the localization setup of a bundle. By following convention we might assume a few more things. For example, we may expect to find a translation template at "languages/cooltheme.pot". However, there's no guarantee the author followed convention and no way for them to tell us if they did something different.

We could start inventing new header tags, like "Template Path: /languages/template.pot" but let's not mess with WordPress standards. A more powerful schema would let us do a whole lot more. Here's how a Loco XML definition could describe the same theme:

<?xml version="1.0" encoding="utf-8"?>
<bundle>
  <domain name="cooltheme">
    <project>
      <target>
        <directory>languages</directory>
      </target>
      <template>
        <file>languages/cooltheme.pot</file>
      </template>
    </project>
  </domain>
</bundle>

You'll notice that our single definition is three elements deep: bundle > domain > project. That may seem over complicated for three simple bits of information, but it provides scope for more complex situations.

Multiple text domains

Let's get a bit more complex. Below we've added a framework that lives inside the theme, but has its own translations:

<?xml version="1.0" encoding="utf-8"?>
<bundle>
  <domain name="cooltheme">
    <project>
      <source>
        <exclude>
            <directory>lib</directory>
        </exclude>
      </source>
      <target>
        <directory>languages</directory>
      </target>
      <template>
        <file>languages/cooltheme.pot</file>
      </template>
    </project>
  </domain>
  <domain name="cf">
    <project name="Some cool framework">
      <source>
        <directory>lib/src</directory>
      </source>
      <target>
        <directory>lib/i18n</directory>
      </target>
      <template>
        <file>lib/i18n/messages.pot</file>
      </template>
    </project>
  </domain>
</bundle>

This tells Loco some new things about the bundle:

  • There are two separate sets of translations now;
  • The lib/src directory contains strings in the "cf" domain;
  • The lib directory won't contain any strings in the "cooltheme" domain;
  • The framework has an unconventional template name.

Multiple projects in the same domain

Although we've added multiple sets of translations, the <project> element still seems a little redundant. The purpose of the project grouping is to support the splitting of text domains into multiple files.

Themes and plugins are unlikely to (and probably shouldn't) split text domains across multiple files, but for a real world example of doing exactly this look no further than WordPress itself. The "default" domain in WordPress core is split into three sets of files. The WordPress core config is a bit long to display here, so let's stick to our example and split the framework into two projects using the same text domain:

<?xml version="1.0" encoding="utf-8"?>
<bundle>
  <domain name="cooltheme">
    <project>
      <!-- As above -->
    </project>
  </domain>
  <domain name="cf">
    <project name="Some cool framework" slug="">
      <source>
        <directory>lib/src</directory>
        <exclude>
            <file>lib/src/admin.php</file>
        </exclude>
      </source>
      <target>
        <directory>lib/i18n</directory>
      </target>
      <template>
        <file>lib/i18n/messages.pot</file>
      </template>
    </project>
    <project name="Framework admin page" slug="admin">
      <source>
        <directory>lib/src/admin.php</directory>
      </source>
      <target>
        <directory>lib/i18n</directory>
      </target>
      <template>
        <file>lib/i18n/admin.pot</file>
      </template>
    </project>
  </domain>
</bundle>

This tells Loco some new things about the framework's "cf" domain:

  • Strings should be separately extracted into admin.pot from admin.php;
  • The main messages template must exclude admin.php (this won't be done implicitly);
  • Language files will be named <locale>.mo and admin-<locale>.mo (see project "slug" attribute);
Project slug attribute

That last point about the "slug" attribute could benefit from some clarification. WordPress generally expects plugin and theme translation files to be named after the text domain. This ensures that the load_theme_textdomain and load_plugin_textdomain functions work as expected. Authors are free however to call load_textdomain directly and so name their files whatever they like. Loco's project "slug" property is a file prefix that allows us to handle unconventional naming.

Although this isn't an example of very good practice, it shows the power and flexibility of the model. There are thousands of themes and plugins out there. Just because something's a bad idea, doesn't mean we shouldn't be able to describe it.

XML reference

For general notes on these properties see the configuration settings. The following is how they should be expressed as XML:

<bundle name="">

  • The document element;
  • The name attribute optionally overrides the bundle name for display purposes.

<domain name="">

  • Immediate child of <bundle>;
  • Groups all projects that share the same domain name;
  • Name attribute is mandatory.

<project name="" slug="">

  • Immediate child of <domain>;
  • Groups distinct sets of translatable strings and their related resources;
  • Name is optional for the first project, mandatory for all others;
  • If slug attribute is absent it will default to the domain name;

<source>

  • Immediate child of <project>;
  • Locations of PHP files containing translatable strings in the text domain.

<target>

  • Immediate child of <project>;
  • Location[s] where official translation files are stored in the bundle;
  • Do not add system paths such as WP_LANG_DIR. System paths are always added internally.

<template locked="true">

  • Immediate child of <project>;
  • The location of a single POT file for a given project. if absent will default to "<DomainPath>/<slug>.pot";
  • The "locked" attribute optionally prevents end user modification of the template.

<exclude>

  • Immediate child of <target>, <source> or <bundle>;
  • Excludes paths you want ignored from the current context.

<directory> and <file>

  • Immediate children of any element that describes filesystem paths;
  • Each element contains a single file path string;
  • Paths should be relative to the bundle root;
  • Wildcards and glob patterns are NOT supported.

Authors

If you're a theme or plugin developer, please consider shipping a loco.xml file in the root of your bundles. This will make your work automatically compatible with Loco. Thousands of non-technical translators will benefit without even knowing it.

If you've configured a popular bundle but you're not the author, please share it with us.