30 Jan 2013

Supporting (X)GETTEXT on Windows - From the Ground Up

Background Info

So, you may have fiddled with GETTEXT in your php files or you may have used PoEdit to translate an app or a site. Well, that's pretty much as far as you can go with these unless you want to go further by using XGETTEXT on the command line to produce your own .pot file for distribution (or your own use, come to that).
If you read a previous article: Creating .pot Files in PoEdit for Translators, you'll remember that PoEdit can create these for you - well, it can, but not fully-featured .pot files. PoEdit has been developed to create and edit .po files, not .pot files, which I got from the horse's mouth, author Václav. Slavik. So what's the difference?

.pot vs .po Files

.pot files are meant as templates for producing other localizations and shouldn't be used to create the all important .mo files. W e can use Gnu's (X)GETTEXT.

Get XGETTEXT for Windows

XGETTEXT works off the Unix command line, so it can't be run natively on Windows, but the clever people at Gnu have provided a Windows version here - GnuWin32.
Follow the link, download and install the package.
You may install it anywhere, but don't place it in the root directory. When installed, you may have something like the image on the right.
Congratulations, you've now installed XGETTEXT. Well. Almost.
In order to get ease of use out of XGETTEXT, you'll need to make the executable commands available from everywhere, other than just the gnuwin32/bin/ directory. For this, we need to add a path to our Windows Environment Variables.

Add a Path to gnuwin32

  1. Right-click on Computer (or My Computer) in the Start Menu choose Properties in the displayed shortcut menu.
  2. On the Properties dialog, click on the Advanced Systems Settings link:
  3. Then, press the Environment Variables... button on the Advanced tab.
  4. Then on the next dialog, choose Path in the System Variables box (or use User variables, if you only want to add XGETTEXT for yourself). Then press Edit...
  5. Now add a path to the directory where the gettext executables live. For me it was: C:\Program Files (x86)\gnuwin32\bin, but your installation may be different. You must separate this path from the last existing one with a semi-colon (;):
  6. Ok, now we should be done with setting up XGETTEXT on Windows - we just need to test it, so open up the command prompt (cmd.exe) and lets type:
    C:\> xgettext --help
    You should then see something that ends like this:

XGETTEXT Commands and Options

I've been informed on many a website that the XGETTEXT documentation is excellent. I really don't want to disagree, but unless you're a unix-junkie and have experience in decoding hieroglyphics, you won't find this easy-to-follow documentation. It gave me a nosebleed! Mind you, it may say more about me than the documentation. So, how do you get it to work? Well, it's surprisingly easy if you ignore the --help menu, which by the way, also makes very little sense.
You may think that I'm being a bit unfair, and yes, I suppose I am, since once you understand the various options and the syntax, it does kind of make sense.

Examples of Command Line Statements to Build a (.pot) File

Before I just give a blind example, I'll run through the basic structure:
ElementDescription
Set Directories-D
e.g. -D c:/xampp/htdocs/timetabler/includes/*.php c:/xampp/htdocs/timetabler/mainsite/*.php
This describes a list of all the directories that hold files that contain your gettext strings. Directories are separated by a space.
Adding translator comments -c
A plain -c will add all php comments that precede the gettext strings as 'Notes for Translators'.
-c[tag]
e.g. -cTRANSLATORS
You may add a tag to this which will only extract the comments starting with this string.
You should note that successive comments between this and gettext strings will be included too. The tag option is useful if you have general comments directly preceding your specific translator comments. This prevents them from being included.
Sorting strings by file location -F
This will group gettext strings according to directories.
Search for strings -k[keywordspec]
e.g. -k__
This gets the keywords, like a prefix, for which to search to identify gettext strings, e.g. __ refers to something like echo _('hello'); so the __ means _(...).
Apply encoding --from-code=name
e.g. --from-code=UTF-8
You should use this if your files may contain non-ASCII text.
File language -L langname
e.g. -L php
This helps xgettext to know how to parse the files containing the gettext strings and comments.
Output location -p directory
e.g.1 -p c:/xampp/htdocs/timetabler/lang (absolute path)
e.g.2 -p timetabler/lang (relative reference)
This sets the directory for saving the output .po file. If this is not included, the file will be saved to the relative directory from where the xgettext command is run.
Output filename -o filename
e.g. -o mytemplate
The setting above will create a file called mytemplate.po.
Add filename and line info-n
This generates the #: filename: line lines in the .pot file, which can be displayed in PoEdit by right-clicking the string to be translated.
So armed with the codes above, you should now be able to enter something like the following at the command prompt:
C:\> xgettext -n -c -D c:/xampp/htdocs/diafol/includes/*.php c:/xampp/htdocs/diafol/config/*.php --from-code=UTF-8 -k__ -L php -p c:/xampp/htdocs/diafol/lang/orig -o mytemplate.pot
Breaking down this into parts, we're trying to:
  1. Add filename lines to the output file (-n).
  2. Add translator comments to the output file -c. No particular tag.
  3. GETTEXT strings can be found in files with .php extension in c:/xampp/htdocs/diafol/includes/ and c:/xampp/htdocs/diafol/config/ directories.
  4. Fix the encoding to UTF-8 --from-code=UTF-8
  5. Search the .php files for the _(...) format strings (-k__).
  6. Let XGETTEXT know that you're using input files using the php language (-L php).
  7. Output the new file to the c:/xampp/htdocs/diafol/lang/orig directory.
  8. Name the file mytemplate.pot (-o mytemplate.pot)

Plural Forms

One thing that I have not covered is the 'plural forms' aspect. A simple case could be with regard to something like: '1 click' or '2 clicks', where there are a number of formats depending on how many subjects that you have. This can be quite complicated as different languages will have different plural forms. For example, English is relatively simple: 'click' singular would be applied to just the digit 1, but all others 0,2,3,4,5... as 'clicks', but other languages may have a very different pattern.
So how do we go about treating plural forms properly? It seems that we have to get a bit crafty in our source files, using the ngettext keyword. I have to admit, I struggled to find decent resources on this with php, but with a bit of fiddling, I believe that I managed to find a working method.
If XGETTEXT knows that we're working with php files, it should automatically pick up the ngettext keyword, so DON'T include this as a keyword in the command line. If you do, you may find that you produce a single string entry for translation and not a plural entry.
Here's a trivial example of ngettext() in a source file:
printf(ngettext("%d file", "%d files", $num), $num);
Once you've run your XGETETXT in the command line, open up the new .pot in a text editor and you should see something like this:
#: c:/xampp/htdocs/timetabler/includes/step1.php:20
#, php-format
msgid "%d file"
msgid_plural "%d files"
msgstr[0] ""
msgstr[1] ""
As far as manipulating creating the .pot file via the command line, we're done, but let's look at a better example. The following code is pretty poor with regard to php, but it's just for illustration purposes, with the focus on ngettext() and _().
<?php
//temp display variables
$num= 1;
$username= "diafol";
$email = "diafol@example.com";

//Form label
$user_label = _("Username:");
//Form label
$email_label = _("Email:");
//Form button label
$submit_label = _("Change Email");
//You must include %d as is 
$lastvisit = ngettext("Your last visit was %d day ago", "Your last visit was %d days ago:", $num);
?>
  
<p><?php printf($lastvisit,$num);?></p>   
<form id="profile" name="profile" action="scripts/profile_handler.php" method="post">
 <label for="username"><?php echo $user_label;?></label>
 <input id="username" name="username" value="<?php echo $username;?>" disabled="disabled" />
    <label for="chemail"><?php echo $email_label;?></label>
 <input id="chemail" name="chemail" value="<?php echo $email;?>" />
    <input type="submit" name="submitprofile" id="submitprofile" value="<?php echo $submit_label;?>" />
</form>
This code gives the following form:
If we change the $num variable to:
//temp display variables
$num= 7;
You should see:
So, we can confirm that the printf()/ngettext() is working as expected in the native language. Okay, here comes the magic bit again, lets run our XGETTEXT:
xgettext -n -c -D c:/xampp/htdocs/timetabler/includes/*.php c:/xampp/htdocs/
timetabler/config/*.php --from-code=UTF-8 -k__  -L php -p c:/xampp/htdocs/timetabler/lang -o messages.pot
This should produce a file messages.pot with something like the following content:
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2013-01-30 00:18+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=CHARSET\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n"

#. Form label
#: c:/xampp/htdocs/timetabler/includes/step1.php:8
msgid "Username:"
msgstr ""

#. Form label
#: c:/xampp/htdocs/timetabler/includes/step1.php:10
msgid "Email:"
msgstr ""

#. Form button label
#: c:/xampp/htdocs/timetabler/includes/step1.php:12
msgid "Change Email"
msgstr ""

#. You must include %d as is
#: c:/xampp/htdocs/timetabler/includes/step1.php:14
#, php-format
msgid "Your last visit was %d day ago"
msgid_plural "Your last visit was %d days ago:"
msgstr[0] ""
msgstr[1] ""
We can see the 'Notes for Translators' comments with the #.-prefixed lines, the filename: line comments, prefixed by #: and the plural format with the msgid_plural key.
We are now ready to edit the file in order to make it distributable. We should edit certain parts of the messages.pot file directly.

Editing Parts of the .pot file Directly

Open up the messages.pot file in a text editor and apply your info, for example, the default:
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
Could be changed to this:
# Translation File for diafolCode TIMETABLER. Download the package from http://www.example.com/downloads).
# Copyright (C) 2013 ALAN DAVIES
# This file is distributed under the same license as the diafolCode TIMETABLER package.
# ALAN DAVIES <diafol@example.com>, 2013.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: diafolCode Timetabler 1.01b\n"
"Report-Msgid-Bugs-To: http://www.diafolcode-bugs.example.com\n"
Finally, we've produced our finished messages.pot file. It is now ready for distribution. Either as a downloadable .pot file, or as part of the whole package, if this is to be distributed.

24 Jan 2013

The Forthcoming Death of MySQL*

Before you start panicking – no, MySQL databases aren't dead, just that pHp's way of manipulating these databases has changed. At the time of writing, we're at php version 5.4.11 and the php manual states:
This extension is deprecated as of PHP 5.5.0, and will be removed in the future.

So, what do we do?

Well, there seems to be two main alternatives, both of which have been banging about for some time: mysqli and PDO.

Why the Change?

I suppose you ask 10 people and you get 10 diffrent answers, but for me it's the mysql_query() function, which is a potential hole-digger from the point of view of SQL Injection. We usually protect ourselves with the old mysql_real_escape_string() function on our inputs and think that we're safe. This is covered in greater detail elsewhere, so without plagiarizing the sources, I recommend a read of the following: Both PDO and Mysqli allow parameterized queries and follow an object orientated approach, which makes handling queries a lot safer and easier.

Which one Should you Use?

Each has its fanboys and detractors, but both will get the job done. Personally, I prefer the PDO syntax and I can easily create wrapper classes for it. For a quick syntax check of these, you can check out the php manual: For some examples of use, pritaeas has some nice ones over on Daniweb:

Wrappers?

For those of you interested in wrapping up PDO so that it has a nice user-friendly client interface, there are many examples out there. One of my favourites when I started was from php-pdo-wrapper-class located on Google Code. Since then, I tend to roll my own, usually as singletons. This is a contentious thing to do and there are many good arguments against using singletons in general. However, in my experience, programming is often a compromise between many factors, which often include convenience.

21 Jan 2013

Bind Events to Dynamically-Added Elements with jQuery's .on()

This post follows one from another - Adding Form Controls Dynamically with jQuery. AT the end of that post, I mentioned that dynamically added form elements can often not have pre-defined events assigned to them as these elements are added after page load. Here's an example of adding a few textboxes to a form along with an associated button for incrementing the textbox value. This is just a trivial example, just to show the issues that can arise.
<form id="frm_numbers" method="post" action="anotherpage.php">
    <label for="tb_addinputs">No. Inputs to Add: </label> 
    <input id="tb_addinputs" type="number" min="1" max="6" value="1" /> 
    <button id="btn_addinputs">Add Inputs</button><br />
    <button id="btn_sync" style="display:none;">Sync All To First</button><br />
    <input type="submit" value="Upload" name="submit" id="submit" />
</form>
Here's some of the accompanying javascript, which all works fine:
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
<script>
$(function(){
    //Extend native from http://bit.ly/11MheU1
    String.prototype.repeat = function(num) {
        return new Array(isNaN(num)? 1 : ++num).join(this);
    }
        
    //Add specific number of controls and show sync button
    $('#btn_addinputs').click(function(e){
        e.preventDefault();
        addNewTextInputs();
        $('#btn_sync').show();
    });
    
    //general function to add text inputs    
    function addNewTextInputs(){
        var str = '<label>MyNumber</label> ';
        str += '<input class="tb_numbers" type="number" value="0" />';
        str += '<button class="btn_addten">+10</button><br />';
        var numToAdd = parseInt($('#tb_addinputs').val());
        $('#btn_sync').before(str.repeat(numToAdd));
    }

    //sync button click to make all text inputs equal to the first one
    $('#btn_sync').click(function(e){
        e.preventDefault();    
        if($('.tb_numbers').length > 1)$('.tb_numbers').val($('.tb_numbers').eq(0).val());
    });
});
So, if we add 5 inputs, we get this:
OK, so far so good. If we manually change the inputs and press 'sync', again everything works fine.
Now the problem arises when we try to attach a .click event to the 'Add Ten' buttons, so that they can add 10 to the value in the associated number input. If the button was present on page load, then we could do this:
$('.btn_addten').click(function(e){
    e.preventDefault();
    num = parseInt($(this).prev().val()) + 10;
    $(this).prev().val(num);        
});
Unfortunately, this won't work, so we have to resort to using the .on() method, which is no hardship:
$("#frm_numbers").on("click", "btn_addten", function(e){
    e.preventDefault();
    num = parseInt($(this).prev().val()) + 10;
    $(this).prev().val(num);
});
Magically this does the business - click the 'Add Ten' button and the associated number input increments by 10.
OK, you can argue that this isn't the most inspiring of scripts, but at least it shows how you can bind an event to a dynamically-added element.

20 Jan 2013

Adding Form Controls Dynamically with jQuery

Let's have a look at an example, where we use a link to add file upload controls to our form.
Initially, we can create our html form thus:
<form id="uploadform" method="post" action="anotherpage.php" enctype="multipart/form-data">
    <p><a href="#" id="addupload">Add another upload control</a></p>
    <label>Upload an image:</label> <input type="file" name="file[]" class="imageupload" /> <br />
    <input type="submit" value="Upload" name="submit" id="submit" />
</form>
The resulting form should look like this - note no CSS/styling so it looks horrible! Also using <br /> tags in forms isn't the best way to do it, but this is only an example.
If we now add a very simple piece of jQuery-javascript, we should be able to add controls easily:
<form id="uploadform" method="post" action="anotherpage.php" enctype="multipart/form-data">
    <p><a href="#" id="addupload">Add another upload control</a></p>
    <label>Upload an image:</label> <input type="file" name="file[]" class="imageupload" /><br />
    <input type="submit" value="Upload" name="submit" id="submit" />
</form>

<script src="//ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
<script>
$(function(){
    $('#addupload').click(function(){
        var addControl = '<label>Upload an image:</label>';
        addControl += ' <input type="file" name="file[]" class="imageupload" /><br />';
        $('#submit').before(addControl); 
    });
});
</script>
You'll notice that the submit button (#submit) acts as a hook so we can use the .before() method.
If you click the link twice and browse for files, you should see something like the following:
NB: this is all well and good if you just need basic form functionality, but if you have javascript running off these controls, then the newly added controls will not function as expected. This is because they were created after the page was fully loaded. Don't fret, there is a way around this limitation. I will create another post looking at the .on() method.

Creating .pot Files in PoEdit for Translators

This post carries on from a previous post on GETTEXT. It assumes that you have downloaded and installed PoEdit and that you have set up GETTEXT strings in your files.

What's a .pot File?

.pot files allow you to create a template for translators in order to create their own .po files, which can then be sent back to you or if you have an application that you allow users to download, they can be used on their own versions.

.pot files have the same file format as .po files, except that they don't contain any translated strings.

Therefore you create them exactly as you would a .po file from the source (your site files).

  1. Fire up PoEdit and open up File | Preferences... to get the dialog below. On the Editor tab, have a look at the settings, and you may wish to set them as the following:
  2. Now, create a new catalogue, File | New catalogue.... Fill in the main properties:
    Then, enter the paths to check for GETTEXT strings:
    Once you click, OK, save the file with a .pot extension:
Now you're set up with a .pot file. You can now distribute the .pot file itself. Translators can create their own .po files from the File | New catalogue from POT file...

Updating to a new Version of PHP in XAMPP (Windows)

Still working on on old version of pHp, due to an old install of XAMPP? Hmm, I was too until I realised that some functions just didn't work. In my case it was the DateTime object and its failure to calculate the difference in the number of days between two dates. On further investigation, I realised that I had to have the VC9 build as opposed to the old and knackered VC6. Okay, armed with that info I sought to update pHp. Hmm (again) - didn't seem to be an easy way to do it, although I found a few references, it all looked a bit daunting. I didn't want to get caught up with pHp's binaries, so a I was looking at a fresh install of XAMPP. This actually proved to be far less of a problem than I thought, although it did take a little while. Here goes:
  1. Back up your data folders and virtual hosts file (assuming that XAMPP is installed in root):
    • C:\xampp\htdocs
    • C:\xampp\mysql\data
    • C:\xampp\apache\conf\extra\httpd-vhosts.conf
  2. It may be worth making a backup of your php.ini file too, just to check that you have the same configuration - although, don't just blindly use the old version as there may be differences.
  3. Once you've backed up all the important stuff, uninstall XAMPP. XAMPP comes with its own uninstaller, so use that. It may ask you if you need to keep htdocs and mysql data, but you can now safely answer no to those.
  4. Once uninstalled, hop over to the XAMPP site and download the version of your choice.
  5. Install XAMPP and copy over the htdocs and mysql data files to the new directories and then reinstate your virtual hosts in the httpd-vhosts.conf file. Ensure that the paths in that file are the same as the new installation. For example:
    NameVirtualHost *:80
        <VirtualHost *:80>
            DocumentRoot "C:/xampp/htdocs"
            ServerName localhost
        </VirtualHost>
    
        <VirtualHost *:80>
            DocumentRoot "C:/xampp/htdocs/grapenun"
            ServerName grapenun.local
            <Directory "C:/xampp/htdocs/grapenun">
              Order allow,deny
              Allow from all
            </Directory>
        </VirtualHost>
    
    If your new xampp installation is not at C:/xampp/, then you'll need to change all the references in the file.
  6. Fire up the XAMPP control panel - it may look different to the old panel - and start Apache and MySQL. Be patient - Apache sometimes has a little think about it before telling you it's started!

Apache #2: Pretty URLs and .htaccess mod_rewrite

How do I get Pretty URLs?

Ever seen professional sites with tidy urls, like www.example.com/products/small-dinosaur? Well, this is likely due to an Apache rewrite, which 'prettifies' the url - the underlying url is probably something like: www.example.com/products.php?id=small-dinosaur

What are .htaccess Files?

You should avoid using .htaccess files completely if you have access to httpd main server config file.[from the official Apache site]
If you have access to the httpd.conf file in Apache's conf directory (well, it's there in the XAMPP version, yours may be elsewhere), you can do your stuff there. However, many people get a bit twitchy with changing the main config file - if you're prone to a bit of twitchiness, make a backup copy first. If you're looking for this file on your shared hosting site, then you'll probably be disappointed.
Anyway... so what's the problem with .htaccess? Well nothing really, it's just a little bit slower than httpd.
In order to create a .htaccess file, you just need a text editor - remember to save it with a blank filename and the htaccess extension. Alternatively, save as htaccess.txt and then rename the file.
Hold on... I haven't mentioned what these .htaccess files are yet, have I? OK, the good people of Wikipedia have an entry, but in general, .htaccess files are used to:
  • rewrite urls
  • serve error responses, like 404 or 301
  • authenticate or block users
  • control caching
We'll just be looking at the first use in this post. Let's kick off and have a look at a few examples. BTW - there are some really nice rewrite generators out there - my favourite at the moment is GenerateIt.

Examples

Say we want to convert:

http://www.example.com/product.php?id=7&name=teddy_bear into:

http://www.example.com/products/7/teddy_bear

We'd need the following code in the .htaccess file:

Options +FollowSymlinks
RewriteEngine On
RewriteRule ^products/([^/]*)/([^/]*)$ /product.php?id=$1&name=$2 [L]

Here's another:

http://www.example.com/index.php?page=toys&category=dolls into:

http://www.example.com/toys/dolls/

Here's the code:

Options +FollowSymlinks
RewriteEngine On
RewriteRule ^([^/]*)/([^/]*)/$ /index.php?page=$1&category=$2 [L]

Where do I put the File?

.htaccess files are active in the directory into which they're placed and all underlying subdirectories, so if you have site-wide directives, you can place the file in your public root.

Further Resources


19 Jan 2013

pHp Localization with GETTEXT on Windows

What's GETTEXT?

Have you, like me built multilingual sites with multiple language files holding strings in multidimensional arrays, only to find synching the adding, editing and deleting items across files to be almost impossible? Well, the answer may be to use GETTEXT. Although this basically does the same thing - create separate files for all your localizations, it does have a number of advantages:
  • you can write pretty much plain text in your html, as opposed to providing a tricky array item, e.g. echo __('this is plain text') vs. echo $lang['plain_text'].
  • localizations are kept in .po and .mo files which can be modified and updated via editors like PoEdit, making the process relatively painless.
  • these editors scan your .php files for __('...') and update the po files for you.
  • if translation strings are not found in the target language, the default language string is used instead - although not ideal, at least you don't have to worry too much if a localization is only 99% complete - it will still display some flavour of text. For this reason, the original texts in the php files are usually written in the main language of your target audience, which may or may not be English.
  • if your localizations are disseminated amongst a number of translators or you have a lot of localizations, you can create .pot files, which you or your users can then use as a template for creating the .po files.
  • .po files can be uploaded to Pootle, a Python-based translation platform
  • It's fast - I mean really fast. Different to arrays, you don't have to load the whole localization!
So, this approach should help you keep a better handle on your localizations and their updating.

How do I Create po/mo Localizations?

There are two main ways to create .po files - either directly from the source files, or from a .pot file. From source would probably be easier in the case of a simple bilingual site, maintained entirely by one person for example. The .pot file, as mentioned, would be best for the outsourced or many languages situation.
Let's look at setting up a page with GETTEXT text in a simple page.
<?php include('includes/config.php'); //contains our gettext setup - see later?>
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title><?php echo _('This is the page title'); ?></title>
</head>
<body>
<h2><?php echo _('This is the main page title'); ?></h2>
<p><?php echo _('Enter your intro text here'); ?></p>
</body>
</html>
OK, from the above we can see 3 translation strings. If you've downloaded and installed PoEdit, follow these steps to create a translation from source.
  1. Create a directory structure for your localizations:
    That will create localization areas for Welsh (cy_GB) and Italian (it_IT). You could name the localizations directory anything you want, e.g. langs, locales, l10n, etc - it's not important.
  2. Next open up PoEdit and create a new catalog and add some catalog properties:
    The image above shows an imaginary language, Xoravian - you'd enter the name of the language for the localisation. The charsets will usually be set to UTF-8 and the plural forms can be gleaned from following the link near the textbox.
  3. On the second tab, save the basename as '.', then give the paths you need to check for strings in the paths textbox:
  4. To finish the catalog properties, just check the keywords:
    The usual suspects that you may want to use are:
    • __
    • gettext
    If you use Wordpress, you may wish to add _e.
  5. Once you've entered all the properties, save the .po file to the appropriate LC_MESSAGES directory (let's save it as lang.po into the Italian directory):
  6. Next, PoEdit will run an update to grab all the gettext strings in the paths you've entered. If successful a list of strings will appear. As below:
    In addition, your directory, should now contain two new files:

That's it, we're done creating the localization file. Now all that we need to do is enter the translation strings and save.

Need the create from .pot approach? I'll create that as a new post on PoEdit soon, but in short, just create a .po file as above and name it lang.pot. They have the same file formats.

Getting GETTEXT to work in Windows

OK, that last bit looked a bit complicated, but getting GETTEXT to play nicely with Windows is a bit of a nightmare and documentation is very patchy. So, I hope the following is useful and works for you.

In the first code snippet, we came across:

<?php include('includes/config.php'); //contains our gettext setup - see later?>

So here now is the all-important code in the includes/config.php file to get everything to work:

<?php
    /*  
    check url for lang parameter and limit the allowed languages,
    otherwise fallback to default text
     */ 
    if (isset($_GET['lang']) && in_array($_GET['lang'],array('it_IT','cy_GB')){
        $lang = $_GET['lang'];
        putenv("LC_ALL=$lang");
        setlocale(LC_ALL, $lang);
        $fn = 'lang'; //the name of the .po file
        bindtextdomain($fn, "./localization");
        bind_textdomain_codeset($fn, 'UTF-8');
        textdomain($fn);
    }
?>

18 Jan 2013

Apache #1: Set up Virtual Hosts on Windows

For Windows

In order to set up your PC to work with multiple local sites, you need to find two files:

  • C:\Windows\System32\drivers\etc\hosts
  • C:\xampp\apache\conf\extra\httpd-vhosts.conf

Note that the second file may be in a different location, depending on how and where Apache is installed. The above shows the location when XAMPP installed to root.

hosts file

You'll notice that this file does not have an extension. It may contain the following when you open it up for the first time:

# Copyright (c) 1993-2009 Microsoft Corp.
#
# This is a sample HOSTS file used by Microsoft TCP/IP for Windows.
#
# This file contains the mappings of IP addresses to host names. Each
# entry should be kept on an individual line. The IP address should
# be placed in the first column followed by the corresponding host name.
# The IP address and the host name should be separated by at least one
# space.
#
# Additionally, comments (such as these) may be inserted on individual
# lines or following the machine name denoted by a '#' symbol.
#
# For example:
#
#      102.54.94.97     rhino.acme.com          # source server
#       38.25.63.10     x.acme.com              # x client host
 
# localhost name resolution is handled within DNS itself.
 127.0.0.1       localhost
 ::1             localhost

Now add the server name of your choice. I usually keep this simple, adding a '.local' so that I can recognise it easily as a local server in the address bar. For example a site called 'example.com' could have a local server name of 'example.local', but there is no need too keep these similar, you could call the server name something like 'myamazingsite.local' if you really wanted to. So, in order to add server names, you can do something like this:

# localhost name resolution is handled within DNS itself.
127.0.0.1       localhost
::1             localhost
# Add virtuals below

    127.0.0.1  diafol.local
    127.0.0.1  chemistry.local

httpd-vhosts.conf

OK, that the first bit done. Now we add an entry to the vhosts file. When you first open the file, it may look like this:

# Virtual Hosts
#
# If you want to maintain multiple domains/hostnames on your
# machine you can setup VirtualHost containers for them. Most configurations
# use only name-based virtual hosts so the server doesn't need to worry about
# IP addresses. This is indicated by the asterisks in the directives below.
#
# Please see the documentation at 
# <URL:http://httpd.apache.org/docs/2.2/vhosts/>
# for further details before you try to setup virtual hosts.
#
# You may use the command line option '-S' to verify your virtual host
# configuration.

#
# Use name-based virtual hosting.
#
##NameVirtualHost *:80

#
# VirtualHost example:
# Almost any Apache directive may go into a VirtualHost container.
# The first VirtualHost section is used for all requests that do not
# match a ServerName or ServerAlias in any VirtualHost> block.
#
##<VirtualHost *:80>
  ##ServerAdmin postmaster@dummy-host.localhost
  ##DocumentRoot "C:/xampp/htdocs/dummy-host.localhost"
  ##ServerName dummy-host.localhost
  ##ServerAlias www.dummy-host.localhost
  ##ErrorLog "logs/dummy-host.localhost-error.log"
  ##CustomLog "logs/dummy-host.localhost-access.log" combined
##</VirtualHost>

##<VirtualHost *:80>
  ##ServerAdmin postmaster@dummy-host2.localhost
  ##DocumentRoot "C:/xampp/htdocs/dummy-host2.localhost"
  ##ServerName dummy-host2.localhost
  ##ServerAlias www.dummy-host2.localhost
  ##ErrorLog "logs/dummy-host2.localhost-error.log"
  ##CustomLog "logs/dummy-host2.localhost-access.log" combined
##</VirtualHost>

In order to add the two sites from the hosts files, simple append something like the following to the end of the file:

NameVirtualHost *:80
<VirtualHost *:80>
  DocumentRoot "C:\xampp\htdocs"
  ServerName localhost
</VirtualHost>
<VirtualHost *:80>
  DocumentRoot "C:\xampp\htdocs\diafol"
  ServerName diafol.local
  <Directory "C:\xampp\htdocs\diafol">
    Order allow,deny
    Allow from all
  </Directory>
</VirtualHost>
<VirtualHost *:80>
  DocumentRoot "C:\xampp\htdocs\chemistry_advanced"
  ServerName chemistry.local
  <Directory "C:\xampp\htdocs\chemistry_advanced">
    Order allow,deny
    Allow from all
  </Directory>
</VirtualHost>

Remember to restart Apache!

13 Jan 2013

PHP #1: Output Data to the Page

echo and print

In order to display data to the screen, you need to use one of two language constructs, either echo or print. To all intents and purposes, they're the same - use whichever you prefer the look of - I tend to use echo.

<?php  
    echo "Hello World!";  
?>  

Alternatively use print:

<?php  
    print "Hello World!";  
?>  

You should find that you get identical outputs:

Hello World!

There are other ways to output data to the screen, including print_r() and var_dump(). However, these tend to be used for checking internal data, such as variables.

Escaping Characters

As pHp requires either single quotes or double quotes around string (text) data in order to display it to the screen, text containing quotes can be problematic, as they can break the code, for example:

<?php  
   echo "They all shouted "Help!", in unison";  
?>  

This would result in an error, such as:

Parse error: syntax error, unexpected T_STRING, expecting ',' or ';' in C:\xampp\htdocs\mysite\index.php on line 2

In much the same way, the following snippet, would also raise the same error:

<?php  
    echo 'Don't ever call me that!';  
?>  

Notice that single quotes and apostrophes tend to be the same character. For this reason alone, coders often tend to use double quotes for static text. There is no problem with enclosing double quotes within single quotes and vice-versa. So, the following are fine:

<?php  
    echo 'They all shouted "Help!", in unison';  
    echo "Don't ever call me that!";  
?>  

OK, so far, so good, but what if some text contains both single quotes/apostrophes and double quotes? Well, in this case we need to escape the offending characters. This is done simply be placing a backslash character in front of the character. Escaping all quotes - just in case - is safe to do, as the backslash character is not displayed on the screen. So each of the following will display the text as intended:

<?php  
    echo 'They all shouted "Help!", in unison. Don\'t ever call me that!';  
    echo "They all shouted \"Help!\", in unison. Don't ever call me that!";  
    echo 'They all shouted \"Help!\", in unison. Don\'t ever call me that!';  
    echo "They all shouted \"Help!\", in unison. Don\'t ever call me that!";  
?>  

So Which is the Best to Use?

You may read many things about the evils of complicated concatenation or the added time required to parse variables, etc, etc, yawn, yawn. Forget it, it doesn't really matter, to me, it's a question of convenience. It can however depend on the context of what you're outputting - if you want to output variables, or not.

<?php  
    $num_pigs = 10;  
    echo "I have $num_pigs pigs on my farm.";  
?>  

The snippet above will display the following:

I have 10 pigs on my farm.

<?php  
    $num_pigs = 10;  
    echo 'All $num_pigs pigs squeal in unison';  
?>  

The snippet above will display the following:

All $num_pigs pigs squeal in unison.

Notice that single quotes take variable names literally.

So how do we display variables within our text using single quotes? Well, we fall back on concatenation. This simply means 'join-up this bit and the next bit' and we use the dot operator (.):

<?php  
    $num_hairs = 3;  
    echo 'I have ' . $num_hairs . ' hairs on the top of my head.';  
?>  

This will give:

I have 3 hairs on the top of my head.

Concatenation isn't the sole preserve of the single quoted text, you can use it with all sorts of bits and bobs:

<?php  
    $num_frogs = 100;  
    echo "I have " . $num_frogs . ' bouncing around inside my head.';  
?>  

A Further Example with Double Quotes

Sometimes you'll need to brace out your variables embedded within the text as they may be array variables:

<?php  
    echo "I have {$currency['dollars']}$dollar_cost in my pocket";  
?>  

The braces {...} essentially protect the variable and ensure that it is not affected by following text it can isolate things like array variables. You can only use braces within double quotes. For an actual explanation of braces, see the php manual, as it is too lengthy to include in this beginner's tutorial.

Heredoc and Nowdoc Syntax

pHp is very flexible as it allows you to mix php code and html. This however is also a weakness as it can lead to very untidy code, which is difficult to maintain. There are instances however, when this is just too convenient to ignore. Enter the Heredoc and Nowdoc Syntaxes (BTW - what is the plural of syntax??).

The heredoc syntax behaves just like double quoted text, but without the quotes themselves. In order to set up heredoc text, you need to set an identifier. This can be pretty much anything you want, but it must be in the following format - with the closing identifier in the first column in the last line - no spaces or anything - before it. The following example has DIAFOL as the identifier.

<?php  
echo <<<DIAFOL
    This is probably the worst game of rugby I've ever seen in my life.
    Could you imagine anything worse than Wales losing to Brynaman U11's?
    It was "shocking". The score was $brynaman_score : $wales_score
DIAFOL;
?>  

The code above would output the following, assuming $brynaman_score was 100 and $wales_score was 0:

This is probably the worst game of rugby I've ever seen in my life. Could you imagine anything worse than Wales losing to Brynaman U11's? It was "shocking". The score was 100 : 0

You'll notice a couple of things:

  1. You don't need to worry about escaping double or single quotes. Yipee!
  2. Line breaks within the heredoc syntax are not respected - not even \n characters. Boo!

In order to maintain line breaks, you'll need to introduce <br /> tags or just use <p> tags. Remember this will just spit out html or plain text. So, to maintain line breaks:

<?php  
echo <<<DIAFOL
    This is probably the worst game of rugby I've ever seen in my life.<br />
    Could you imagine anything worse than Wales losing to Brynaman U11's?<br />
    It was "shocking". The score was $brynaman_score : $wales_score
DIAFOL;
?>  

Or even:

<?php  
echo <<<DIAFOL
    <p>This is probably the worst game of rugby I've ever seen in my life.</p>
    <p>Could you imagine anything worse than Wales losing to Brynaman U11's?</p>
    <p>It was "shocking". The score was $brynaman_score : $wales_score</p>
DIAFOL;
?>  

The nowdoc syntax is to single quoted text as the heredoc syntax is to double quoted text. The only difference here is that we use single quotes to enclose the open identifier:

<?php  
echo <<<'DIAFOL'
    This is probably the worst game of rugby I've ever seen in my life.
    Could you imagine anything worse than Wales losing to Brynaman U11's?
    It was "shocking". The score was $brynaman_score : $wales_score
DIAFOL;
?>  

This will output the following:

This is probably the worst game of rugby I've ever seen in my life. Could you imagine anything worse than Wales losing to Brynaman U11's? It was "shocking". The score was $brynaman_score : $wales_score

You'll notice a couple of things again:

  1. Just like heredoc - no need to escape quotes and line breaks not respected.
  2. Variables are literal.

You may be wondering just how useful that is - and to be honest, for day to day stuff, probably not very. However, if you're writing an article or trying to explain the workings of some code or other, having nowdoc NOT try to parse your variables is quite handy. To achieve the same thing in heredoc, you need to escape the $ symbol of variables:

<?php  
echo <<<DIAFOL
    This is probably the worst game of rugby I've ever seen in my life.
    Could you imagine anything worse than Wales losing to Brynaman U11's?
    It was "shocking". The score was \$brynaman_score : \$wales_score
DIAFOL;
?>  

OK, that's the end of this first tute on pHp. I hope it was useful - if it was, stroke my ego and leave some love - else - leave a comment anyway and tell me where I went wrong.