Tag: i18n

  • Future of POT generators

    One of the first big problems that I faced when I started with WordPress was how to generate  a POT file. In the meantime have been able to look at all of the different tools and see their shortcoming with challenges that I faced when generating a POT file.

    Challenges with POT generation

    Recently I have been working on projects where the POT file generation needed to be different. Here are three examples:

    I was working on Responsive and Responsive Pro. Responsive is currently translated in about 45 languages. A large majority of the strings in Responsive Pro were already translated in Responsive. So to reduce the workload for the translators I created a separate POT for the pro folder which is only in the pro version.

    Another example from CyberChimps where we use a framework for all of our themes except for Responsive. The framework is the core folder and it is in every theme. If we created the POT files the standard way then we would have all of the same strings across all of our themes which would create extra work for the translators. We could use a constant and define that in the functions.php but it is not allowed in the current WordPress.org theme review guidelines. The core has it’s own text domain, pot file and translations.

    A third example that I came across is when using TGM Plugin Activation. TGM Plugin Activation has it’s own strings with it’s own text domain tgmpa. With the current POT generation solutions the strings will be added to the POT but the translations will not load as the text domain is never loaded. A solution could be to just to load your own version of the strings with your own strings. I know a number of plugins and themes use this library. TGM Plugin Activation should load it’s text domain and have it’s own language folder. We as a community can contribute to it and then themes and plugin are able to profit from the standard strings being fully translated.

    Shortcomings of current generators

    I also find makepot.php is too narrow minded to use as it expects that the folder name is the same as text domain. There are multiple times where it is not so in developement. I develop the Flowplayer HTML5 WordPress plugin and the GitHub repo is named wordpress-flowplayer as it make sense to separate it from the Flowplayer player GitHub repo which is part of the same GitHub organisation. So when I use makepot it thinks the text domain is wordpress-flowplayer instead of flowplayer5 which is the WordPress.org plugin slug. Another case would be if when developing a plugin in the same WordPress.org svn structure. The main plugin is most times in the trunk folder and makepot.php would then think that the theme slug should be trunk.

    Poedit Pro handles text domains great. It check for the header info Text Domain and uses that to define the text domain. It currently supports the WordPress translators comments but the not the plugin or theme meta data yet. This is still a very manual process of creating the POT. It also uses the folder structure to choose the files where the strings are pulled from.

    Both makepot.php and Poedit use the location of the files when choosing which strings to add to the POT but neither solution can yet exclude folders.

    In November 2013 Stephen Harris released a new solution grunt-pot. I was really pleased with this solution as it includes an option to define the files that you want and do not want to scan. The thing that is missing is that it does not support fetching WordPress meta data and translator comments.

    Future POT generator

    The perfect POT generator  for me would be one that used the text domain to choose which strings get added to POT file. The solution would need to able to define the location of the files, text domain, and the location of the language folder. For WordPress it would be very important that the translator comments and plugin/theme meta data are also included in the POT. The solution should be built using grunt as it works with every OS can be easily integrated in a build workflow.

  • Why a string text domain is not a must

    A discussion that I keep coming across with i18n in WordPress is the use of variables and constants for text domains. Everyone keeps on linking to Mark Jaquith’s post “Translating WordPress Plugins and Themes: Don’t Get Clever” and Otto’s post “Internationalization: You’re probably doing it wrong” Here are some quotes from Mark’s post:

    GNU gettext is not a PHP parser. It can’t read variables or constants. It only reads strings.

    True but it does not matter as you can still can create a POT without knowing the text domain. I have made a POT with Poedit and makepot.php while having a variable for a text domain.

    Mark mentions at the bottom of the post:

    Well, it won’t break your plugin, but it could make it harder to be used with automated translation tools.

    From the comments:

    …this will work when doing once-off manual generation. But if you want to, say, automate the generation across, say, the entire WordPress.org plugin repository, you’re going to need to know the text domain programmatically.

    I don’t agree. Makepot.php from WordPress tools uses the folder structure to build the pot. It does not care what text domain that is being used or if even a text domain is being used. WordPress has been creating new POT files for every version using makepot.php and WordPress core does not have any text domain. If I had to create a pot file for every plugin on WordPress.org plugin repository I would simply create a script that would run makepot.php on every plugin folder.

    Otto has mentioned that a text domain should not use variable or constant because it would not work with the new language packs. When I asked him what code WordPress.org would use to generate the POT files, he said through the makepot.php code.

    I do not see a reason why using the text domain to generate the POT files is better when doing mass generation.  A couple issues I see arising when doing this is when a plugin/theme has multiple text domains or has an incorrect text domain. So not to cause cross contamination of strings the system would still need to search only the folder of the specific plugin which would also work with the current makepot.pot and text domains that are variables or constants

    Conclusion

    Even though I do not agree with Mark and Otto on their reasoning I would not recommend using variables and constants for the text domain. I do see that at times that constants and variables do make sense but all of the pros and cons do need to be well thought through. The reasons why I would recommend using a string for the text domain are:

    • The text domain should constant and not changing.
    • It takes the same amount of time to write a constant or variable for the text domain as for a string.
    • Having strings across multiple classes can add extra code.
    • You see straight away which plugin the text domain belongs to and reduces the risk of bugs.
    • You can also use tools like grunt-checktextdomain to check if the text domains are all valid.
    • The text domain should be used for future POT generation because a plugin might need multiple text domains. More on this in my next post.

    Update 1: It is a must to use a string for the text domain if you want to support language packs when hosting your plugin/theme on the WordPress.org responsitory.

  • How to load theme and plugin translations

    You may ask yourself what is so special about this post. Does not the codex cover load_plugin_textdomain() and load_theme_textdomain() but this posts will show what else you can do with it.

    With this code for the theme and plugin you can add the translations in different places. The two reasons for this are:

    1. This allows for the translation to be placed in multiple places when the theme/plugin author does not add the translations to the theme/plugin and not have them deleted in every update.
    2. This also allows for the translations to be edited without needing to maintain the whole translation. For example there might be only one string that needs to be different for a site and so can be added to language directory before the theme/plugin language directory.

    How do .mo files work?

    So with the translation in the mo files if two of the same translations are loaded only the first translation will be displayed. So that is why in these code examples the plugin/theme language folders are loaded last. With if you only need to change a single string you would only need to add a single translation string in the directory that is loaded before.

    For the theme there are three different places; the WordPress language directory, the child theme language directory and the parent theme language directory.

    <?php
    function theme_name_setup(){
    $domain = ‘theme-name’;
    // wp-content/languages/theme-name/de_DE.mo
    load_theme_textdomain( $domain, trailingslashit( WP_LANG_DIR ) . $domain );
    // wp-content/themes/child-theme-name/languages/de_DE.mo
    load_theme_textdomain( $domain, get_stylesheet_directory() . ‘/languages’ );
    // wp-content/themes/theme-name/languages/de_DE.mo
    load_theme_textdomain( $domain, get_template_directory() . ‘/languages’ );
    }
    add_action( ‘after_setup_theme’, ‘theme_name_setup’ );
    view raw functions.php hosted with ❤ by GitHub

    For the plugin there are two different places; the WordPress language directory and the plugin language directory.

    <?php
    function plugin_name_load_plugin_textdomain() {
    $domain = ‘plugin-name’;
    $locale = apply_filters( ‘plugin_locale’, get_locale(), $domain );
    // wp-content/languages/plugin-name/plugin-name-de_DE.mo
    load_textdomain( $domain, trailingslashit( WP_LANG_DIR ) . $domain . ‘/’ . $domain . ‘-‘ . $locale . ‘.mo’ );
    // wp-content/plugins/plugin-name/languages/plugin-name-de_DE.mo
    load_plugin_textdomain( $domain, FALSE, basename( dirname( __FILE__ ) ) . ‘/languages/’ );
    }
    add_action( ‘init’, ‘plugin_name_load_plugin_textdomain’ );
    view raw plugin-name.php hosted with ❤ by GitHub

    WordPress language directory

    The WordPress language directory can be found in wp-content. The code that we used would mean we would need a plugin or theme directory in the language directory for the translations.
    wp-content
    – languages
    – – plugin-name
    – – – plugin-name-de_DE.po
    – – – plugin-name-de_DE.mo
    – – theme-name
    – – – theme-name-de_DE.po
    – – – theme-name-de_DE.mo

    WordPress 3.7

    With the changes in WordPress 3.7 it will add support for automatically installing the right language files and keeping them up to date. These translations will be stored in the WordPress language directory under one directory for themes and another for plugins. The standard directory structure will look like this.
    wp-content
    – languages
    – – plugins
    – – – plugin-name-de_DE.po
    – – – plugin-name-de_DE.mo
    – – themes
    – – – theme-name-de_DE.po
    – – – theme-name-de_DE.mo

    You might ask yourself how this code will work with WordPress 3.7. What ever you are doing in now to load the text domain will work fine in WordPress 3.7. As the code uses the WordPress language directory but not the themes or plugins directory in the WordPress language directory all will be fine.

    WordPress checks if there are translation is in plugin/theme and if not then it will load the translation from WordPress language directory. If the translation are in the custom location in the WordPress language directory then the translations will be merged but the translations from the custom location will used unless not defined.

    In the future plugin and themes on WordPress.org repository will be able to update translations separately to the plugin or theme updates(This will launched separately to WordPress 3.7).

    Otto has a great post on this that must be checked out.

    Here is how you could conditionally load only one translation file and all of them one after each other.

    <?php
    function theme_name_setup(){
    $domain = ‘theme-name’;
    if ( $loaded = load_theme_textdomain( $domain, trailingslashit( WP_LANG_DIR ) . $domain ) ) {
    return $loaded;
    } elseif ( $loaded = load_theme_textdomain( $domain, get_stylesheet_directory() . ‘/languages’ ) {
    return $loaded;
    } else {
    load_theme_textdomain( $domain, get_template_directory() . ‘/languages’ );
    }
    }
    add_action( ‘after_setup_theme’, ‘theme_name_setup’ );
    view raw functions.php hosted with ❤ by GitHub
    <?php
    function plugin_name_load_plugin_textdomain() {
    $domain = ‘plugin-name’;
    $locale = apply_filters( ‘plugin_locale’, get_locale(), $domain );
    if ( $loaded = load_textdomain( $domain, trailingslashit( WP_LANG_DIR ) . $domain . ‘/’ . $domain . ‘-‘ . $locale . ‘.mo’ ) ) {
    return $loaded;
    } else {
    load_plugin_textdomain( $domain, FALSE, basename( dirname( __FILE__ ) ) . ‘/languages/’ );
    }
    }
    add_action( ‘init’, ‘plugin_name_load_plugin_textdomain’ );
    view raw plugin-name.php hosted with ❤ by GitHub
  • Help: WordPress i18n Tools

    When I was at WordCamp Europe 2013 I listened to Otto’s talk on internationalisation. I have had time to play around with the WordPress i18n tools. I would like to add a few more features and see how the code can be improved.

    I have set the tools on GitHub and I am looking for people to help me. I have started on cleaning up the code to the WordPress coding standards and found and implemented a fix for the strict standards error. A few issues that I see can be found on the repo issues. I think the ultimate goal would be to make a plugin out of it. This way everyone can use it easily.

  • How to update translations quickly

    This is for Windows users.

    Set Up WebTranslateIt Synchronization Tool

    I use webtranslateit for managing all of the theme and plugin translation. Up till recently I have always download the files in a zip and then extracted them to the language folder.

    Recently I looked for a way to improve automation and found the WebTranslateIt Synchronization Tool. It is very easy to set up.

    The installation process is explained on GitHub.

    Setup .mo file generation

    To automate the conversion of the mo files you will need to install Cygwin. It is a simple installation process but takes some time to install.

    Then create a file called potomo.sh. Then add this code:

    Then go to the language folder and open the file .wti. Replace # after_pull: "touch tmp/restart.txt" with after_pull: "I: &amp; cd I:/path/to/folder/<wbr />languages &amp; c:/cygwin/bin/bash -c /cygdrive/i/path/to/<wbr />script/directory/potomo.<wbr />sh".

    Run Script

    To be able to run the whole thing Start Command Prompt with Ruby. Then move to the location of the language folder and type wti pull