New Proposal for handling strings languages: Introduction

This proposal is intended to make easier to develop multilingual-ready themes and plugins. As 98% of WordPress sites are single-language, our goal is to enable multilingual functionality, while avoiding complexity for single-language sites.

The intention is to allow themes and plugins to indicate what elements, which they create, will require translation on multilingual sites. Different multilingual plugins can use that information and implement the multilingual operation in whatever way they choose.

Custom post types and taxonomy

When running a multilingual site, custom posts and taxonomy can be ‘translatable’ or not. If items of a custom type are translatable, the user expects to see different content per language. In some cases, even if the site is multilingual, certain CPTs should be language-indifferent. For example, if a site offers ‘downloads’, they will probably be the same, no matter in what language visitors browse the site.

Custom fields and ‘option’ entries

Custom fields and ‘options’ are a bit more complex than CPTs. Custom fields may require translation, may need to be the same for all languages or should be language-indifferent.

How to set language information

Developers will have two options to indicate if elements require translation:

  • Using JSON files
  • Using filters

JSON files allow to pre-compile the language information for elements. These files offer zero overhead during runtime for single-language sites. When running in a multilingual site, the multilingual plugin will read these JSON files and ‘know’ what elements require translation. JSON files are best for ‘static’ elements, which the theme or plugin always create. For example, a real estate theme may create CPTs for ‘properties’ and some fields for properties. A JSON file in the theme folder will indicate that these elements require translation.
Filters are best for dynamic elements, which cannot be determined when developing the code. They add minimal overhead during runtime (to go through filters that nothing uses). A plugin that creates field according to user inputs will need to use these filters to indicate language information, as these fields are not known when coding the plugin.

Example – announcing translatable texts in WooCommerce

The following example, shows a configuration file for translating post meta and ‘option’ entries in WooCommerce.

This would be the JSON file for WooCommerce:

{
  "post-meta": [
      {
        "action": "copy",
        "name": "_backorders"
      },
      {
        "action": "copy",
        "name": "_pricing_rules"
      }
      {
        "action": "translate",
        "name": "_crosssell_ids"
      },
      ...
    ]
  ,
  "options": [
      { "name": "woocommerce_shop_page_title" },
      {
        "name": "woocommerce_new_order_settings",
        "key": [
          { "name": "subject" },
          { "name": "heading" }
        ]
      },
      ...
      { "name": "woocommerce_demo_store_notice" },
    ]
}

You can see that the custom fields ‘_backorders’ and ‘_pricing_rules’ need to be copied between languages. The field ‘_crosssell_ids’ is user-translatable.

We can implement the same using API calls, like these:

// Get the theme option
$cross_sell_ids = get_option( 'my_theme_footer_text' );
$cross_sell_ids = get_translated_string( 'option', 'my_theme_footer_text', $cross_sell_ids );
// Or...
$cross_sell_ids = apply_filters( 'translated_string', get_option( 'my_theme_footer_text' ), 'option', 'my_theme_footer_text' );

You can read the complete technical proposal in the following Google Doc: https://goo.gl/Ynp1ek.

Proposal: filter for translating any non gettext string

Options and user meta may need to be translated in many cases.

Let’s consider 3 scenarios:

  1. Site settings such as Site Title, Tagline
  2. Theme settings such as a footer disclaimer
  3. User bio

1 is stored in wp_options and likely is the same for 2 (if author is using $wp_customizer or wp_options).

3 is stored in wp_usermeta.

Very similar to how gettext function work, we could consider to define a convention for a new filter.

WordPress core won’t need any change on its code for implement this, because this would have to be implemented by themes and plugins authors (or multilingual plugins).

This filter will need 2 arguments:

  1. The string to filter (of course)
  2. An associative array
    The idea of using an associative array rather than a given list of arguments, lies on the fact that this could allow to extend the amount and type of data this filter can receive.
    Also this helps keeping the signature small.
    • domain: is the equivalent of a text-domain
    • context: as for gettext, we may need to provide a context
    • name: this is a way to identify the string we want to translate: not necessarily needed, but if we store these translation somewhere, we most likely need that)

I still have no ideas of what this filter handle could be named (suggestions are welcome!), but let’s suppose it will be ‘translate_string’.

Possible implementations of the 3 scenarios could be:

  1. WP domain:
    • $translated_blogname = apply_filters( 'translate_string', get_bloginfo('name'), array( 'domain' => 'WP', 'name' => 'blogname' ) );
    • $translated_tagline = apply_filters( 'translate_string', get_bloginfo('description'), array( 'domain' => 'WP', 'name' => 'blogdescription' ) );
  2. Theme domain (where domain would be the theme slug):
    • $translated_footer_disclaimer = apply_filters( 'translate_string', $wp_customize->get_setting( 'footer_disclaimer' ), array( 'domain' => 'theme-slug', 'name' => 'footer_disclaimer' ) );
  3. user-meta domain (here I’m making up a custom domain):
    • $translated_user_bio = apply_filters( 'translate_string', get_usermeta($user_id, 'description'), array( 'domain' => 'user-meta', 'context' => $user_id, 'name' => 'description' ) );