<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="text/css" href="http://commons.oreilly.com/wiki/skins/common/feed.css?97"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/">
	<channel>
		<title>PHP Cookbook/Internationalization and Localization - Revision history</title>
		<link>http://commons.oreilly.com/wiki/index.php?title=PHP_Cookbook/Internationalization_and_Localization&amp;action=history</link>
		<description>Revision history for this page on the wiki</description>
		<language>en</language>
		<generator>MediaWiki 1.11.0</generator>
		<lastBuildDate>Sun, 19 May 2013 01:17:40 GMT</lastBuildDate>
		<item>
			<title>Docbook2Wiki: Initial conversion from Docbook</title>
			<link>http://commons.oreilly.com/wiki/index.php?title=PHP_Cookbook/Internationalization_and_Localization&amp;diff=7328&amp;oldid=prev</link>
			<description>&lt;p&gt;Initial conversion from Docbook&lt;/p&gt;

			&lt;table style=&quot;background-color: white; color:black;&quot;&gt;
			&lt;col class='diff-marker' /&gt;
			&lt;col class='diff-content' /&gt;
			&lt;col class='diff-marker' /&gt;
			&lt;col class='diff-content' /&gt;
			&lt;tr&gt;
				&lt;td colspan='2' style=&quot;background-color: white; color:black;&quot;&gt;←Older revision&lt;/td&gt;
				&lt;td colspan='2' style=&quot;background-color: white; color:black;&quot;&gt;Revision as of 13:36, 7 March 2008&lt;/td&gt;
			&lt;/tr&gt;
		&lt;/table&gt;</description>
			<pubDate>Fri, 07 Mar 2008 13:36:04 GMT</pubDate>			<dc:creator>Docbook2Wiki</dc:creator>			<comments>http://commons.oreilly.com/wiki/index.php/Talk:PHP_Cookbook/Internationalization_and_Localization</comments>		</item>
		<item>
			<title>Evanlenz: 1 revision(s)</title>
			<link>http://commons.oreilly.com/wiki/index.php?title=PHP_Cookbook/Internationalization_and_Localization&amp;diff=3135&amp;oldid=prev</link>
			<description>&lt;p&gt;1 revision(s)&lt;/p&gt;

			&lt;table style=&quot;background-color: white; color:black;&quot;&gt;
			&lt;col class='diff-marker' /&gt;
			&lt;col class='diff-content' /&gt;
			&lt;col class='diff-marker' /&gt;
			&lt;col class='diff-content' /&gt;
			&lt;tr&gt;
				&lt;td colspan='2' style=&quot;background-color: white; color:black;&quot;&gt;←Older revision&lt;/td&gt;
				&lt;td colspan='2' style=&quot;background-color: white; color:black;&quot;&gt;Revision as of 22:30, 6 March 2008&lt;/td&gt;
			&lt;/tr&gt;
		&lt;/table&gt;</description>
			<pubDate>Thu, 06 Mar 2008 22:30:13 GMT</pubDate>			<dc:creator>Evanlenz</dc:creator>			<comments>http://commons.oreilly.com/wiki/index.php/Talk:PHP_Cookbook/Internationalization_and_Localization</comments>		</item>
		<item>
			<title>Docbook2Wiki: Initial conversion from Docbook</title>
			<link>http://commons.oreilly.com/wiki/index.php?title=PHP_Cookbook/Internationalization_and_Localization&amp;diff=3134&amp;oldid=prev</link>
			<description>&lt;p&gt;Initial conversion from Docbook&lt;/p&gt;
&lt;p&gt;&lt;b&gt;New page&lt;/b&gt;&lt;/p&gt;&lt;div&gt;{{PHP Cookbook/TOC}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Introduction ==&lt;br /&gt;
&lt;br /&gt;
While everyone who programs in PHP has to learn some English eventually to get a handle on its function names and language constructs, PHP can create applications that speak just about any language. Some applications need to be used by speakers of many different languages. Taking an application written for French speakers and making it useful for German speakers is made easier by PHP's support for internationalization and localization.&lt;br /&gt;
&lt;br /&gt;
Internationalization (often abbreviated I18N&amp;lt;ref&amp;gt;The word &amp;quot;internationalization&amp;quot; has 18 letters between the first &amp;quot;i&amp;quot; and the last &amp;quot;n.&amp;quot;&amp;lt;/ref&amp;gt;) is the process of taking an application designed for just one locale and restructuring it so that it can be used in many different locales. Localization (often abbreviated L10N&amp;lt;ref&amp;gt;The word &amp;quot;localization&amp;quot; has 10 letters between the first &amp;quot;l&amp;quot; and the &amp;quot;n.&amp;quot;&amp;lt;/ref&amp;gt;) is the process of adding support for a new locale to an internationalized application.&lt;br /&gt;
&lt;br /&gt;
A locale is a group of settings that describe text formatting and language customs in a particular area of the world. The settings are divided into six categories:&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;tt&amp;gt;LC_COLLATE&amp;lt;/tt&amp;gt;&lt;br /&gt;
: These settings control text sorting: which letters go before and after others in alphabetical order.&lt;br /&gt;
;&amp;lt;tt&amp;gt;LC_CTYPE&amp;lt;/tt&amp;gt;&lt;br /&gt;
: These settings control mapping between uppercase and lowercase letters as well as which characters fall into the different character classes, such as alphanumeric characters.&lt;br /&gt;
;&amp;lt;tt&amp;gt;LC_MONETARY&amp;lt;/tt&amp;gt;&lt;br /&gt;
: These settings describe the preferred format of currency information, such as what character to use as a decimal point and how to indicate negative amounts.&lt;br /&gt;
;&amp;lt;tt&amp;gt;LC_NUMERIC&amp;lt;/tt&amp;gt;&lt;br /&gt;
: These settings describe the preferred format of numeric information, such as how to group numbers and what character is used as a thousands separator.&lt;br /&gt;
;&amp;lt;tt&amp;gt;LC_TIME&amp;lt;/tt&amp;gt;&lt;br /&gt;
: These settings describe the preferred format of time and date information, such as names of months and days and whether to use 24- or 12-hour time.&lt;br /&gt;
;&amp;lt;tt&amp;gt;LC_MESSAGES&amp;lt;/tt&amp;gt;&lt;br /&gt;
: This category contains text messages used by applications that need to display information in multiple languages.&lt;br /&gt;
&lt;br /&gt;
There is also a metacategory, &amp;lt;tt&amp;gt;LC_ALL&amp;lt;/tt&amp;gt;, that encompasses all the categories.&lt;br /&gt;
&lt;br /&gt;
A locale name generally has three components. The first, an abbreviation that indicates a language, is mandatory. For example, &amp;quot;en&amp;quot; for English or &amp;quot;pt&amp;quot; for Portuguese. Next, after an underscore, comes an optional country specifier, to distinguish between different countries that speak different versions of the same language. For example, &amp;quot;en_US&amp;quot; for U.S. English and &amp;quot;en_GB&amp;quot; for British English, or &amp;quot;pt_BR&amp;quot; for Brazilian Portuguese and &amp;quot;pt_PT&amp;quot; for Portuguese Portuguese. Last, after a period, comes an optional character-set specifier. For example, &amp;quot;zh_TW.Big5&amp;quot; for Taiwanese Chinese using the Big5 character set. While most locale names follow these conventions, some don't. One difficulty in using locales is that they can be arbitrarily named. Finding and setting a locale is discussed in [[PHP Cookbook/Internationalization and Localization#Listing Available Locales|Section 16.2]] through [[PHP Cookbook/Internationalization and Localization#Setting the Default Locale|Section 16.4]].&lt;br /&gt;
&lt;br /&gt;
Different techniques are necessary for correct localization of plain text, dates and times, and currency. Localization can also be applied to external entities your program uses, such as images and included files. Localizing these kinds of content is covered in [[PHP Cookbook/Internationalization and Localization#Localizing Text Messages|Section 16.5]] through [[PHP Cookbook/Internationalization and Localization#Localizing Included Files|Section 16.9]].&lt;br /&gt;
&lt;br /&gt;
Systems for dealing with large amounts of localization data are discussed in [[PHP Cookbook/Internationalization and Localization#Managing Localization Resources|Section 16.10]] and [[PHP Cookbook/Internationalization and Localization#Using gettext|Section 16.11]]. [[PHP Cookbook/Internationalization and Localization#Managing Localization Resources|Section 16.10]] shows some simple ways to manage the data, and [[PHP Cookbook/Internationalization and Localization#Using gettext|Section 16.11]] introduces GNU ''gettext'', a full-featured set of tools that provide localization support.&lt;br /&gt;
&lt;br /&gt;
PHP also has limited support for Unicode. Converting data to and from the Unicode UTF-8 encoding is addressed in [[PHP Cookbook/Internationalization and Localization#Reading or Writing Unicode Characters|Section 16.12]].&lt;br /&gt;
&lt;br /&gt;
== Listing Available Locales ==&lt;br /&gt;
&lt;br /&gt;
=== Problem ===&lt;br /&gt;
&lt;br /&gt;
You want to know what locales your system supports.&lt;br /&gt;
&lt;br /&gt;
=== Solution ===&lt;br /&gt;
&lt;br /&gt;
Use the ''locale'' program to list available locales; ''locale -a'' prints the locales your system supports.&lt;br /&gt;
&lt;br /&gt;
=== Discussion ===&lt;br /&gt;
&lt;br /&gt;
On Linux and Solaris systems, you can find &amp;lt;tt&amp;gt;locale&amp;lt;/tt&amp;gt; at ''/usr/bin/locale''. On Windows, locales are listed in the Regional Options section of the Control Panel.&lt;br /&gt;
&lt;br /&gt;
Your mileage varies on other operating systems. BSD, for example, includes locale support but has no ''locale'' program to list locales. BSD locales are often stored in ''/usr/share/locale'', so looking in that directory may yield a list of usable locales.&lt;br /&gt;
&lt;br /&gt;
While the locale system helps with many localization tasks, its lack of standardization can be frustrating. Systems aren't guaranteed to have the same locales or even use the same names for equivalent locales.&lt;br /&gt;
&lt;br /&gt;
=== See Also ===&lt;br /&gt;
&lt;br /&gt;
Your system's ''locale(1)'' manpage.&lt;br /&gt;
&lt;br /&gt;
== Using a Particular Locale ==&lt;br /&gt;
&lt;br /&gt;
=== Problem ===&lt;br /&gt;
&lt;br /&gt;
You want to tell PHP to use the settings of a particular locale.&lt;br /&gt;
&lt;br /&gt;
=== Solution ===&lt;br /&gt;
&lt;br /&gt;
Call &amp;lt;tt&amp;gt;setlocale( )&amp;lt;/tt&amp;gt; with the appropriate category and locale. Here's how to use the &amp;lt;tt&amp;gt;es_US&amp;lt;/tt&amp;gt; (U.S. Spanish) locale for all categories:&lt;br /&gt;
&lt;br /&gt;
 setlocale(LC_ALL,'es_US');&lt;br /&gt;
&lt;br /&gt;
Here's how to use the &amp;lt;tt&amp;gt;de_AT&amp;lt;/tt&amp;gt; (Austrian German) locale for time and date formatting:&lt;br /&gt;
&lt;br /&gt;
 setlocale(LC_TIME,'de_AT');&lt;br /&gt;
&lt;br /&gt;
=== Discussion ===&lt;br /&gt;
&lt;br /&gt;
To find the current locale without changing it, call &amp;lt;tt&amp;gt;setlocale( )&amp;lt;/tt&amp;gt; with a &amp;lt;tt&amp;gt;NULL&amp;lt;/tt&amp;gt; locale:&lt;br /&gt;
&lt;br /&gt;
 print setlocale(LC_ALL,NULL);&lt;br /&gt;
 '''en_US'''&lt;br /&gt;
             &lt;br /&gt;
&lt;br /&gt;
Many systems also support a set of aliases for common locales, listed in a file such as ''/usr/share/locale/locale.alias''. This file is a series of lines including:&lt;br /&gt;
&lt;br /&gt;
 russian         ru_RU.ISO-8859-5&lt;br /&gt;
 slovak          sk_SK.ISO-8859-2&lt;br /&gt;
 slovene         sl_SI.ISO-8859-2&lt;br /&gt;
 slovenian       sl_SI.ISO-8859-2&lt;br /&gt;
 spanish         es_ES.ISO-8859-1&lt;br /&gt;
 swedish         sv_SE.ISO-8859-1&lt;br /&gt;
&lt;br /&gt;
The first column of each line is an alias; the second column shows the locale and character set the alias points to. You can use the alias in calls to &amp;lt;tt&amp;gt;setlocale( )&amp;lt;/tt&amp;gt; instead of the corresponding string the alias points to. For example, you can do:&lt;br /&gt;
&lt;br /&gt;
 setlocale(LC_ALL,'swedish');&lt;br /&gt;
&lt;br /&gt;
instead of:&lt;br /&gt;
&lt;br /&gt;
 setlocale(LC_ALL,'sv_SE.ISO-8859-1');&lt;br /&gt;
&lt;br /&gt;
On Windows, to change the locale, visit the Control Panel. In the Regional Options section, you can pick a new locale and customize its settings.&lt;br /&gt;
&lt;br /&gt;
=== See Also ===&lt;br /&gt;
&lt;br /&gt;
[[PHP Cookbook/Internationalization and Localization#Setting the Default Locale|Section 16.4]] shows how to set a default locale; documentation on &amp;lt;tt&amp;gt;setlocale( )&amp;lt;/tt&amp;gt; at ''http://www.php.net/setlocale''.&lt;br /&gt;
&lt;br /&gt;
== Setting the Default Locale ==&lt;br /&gt;
&lt;br /&gt;
=== Problem ===&lt;br /&gt;
&lt;br /&gt;
You want to set a locale that all your PHP programs can use.&lt;br /&gt;
&lt;br /&gt;
=== Solution ===&lt;br /&gt;
&lt;br /&gt;
At the beginning of a file loaded by the &amp;lt;tt&amp;gt;auto_prepend_file&amp;lt;/tt&amp;gt; configuration directive, call &amp;lt;tt&amp;gt;setlocale( )&amp;lt;/tt&amp;gt; to set your desired locale:&lt;br /&gt;
&lt;br /&gt;
 setlocale(LC_ALL,'es_US');&lt;br /&gt;
&lt;br /&gt;
=== Discussion ===&lt;br /&gt;
&lt;br /&gt;
Even if you set up appropriate environment variables before you start your web server or PHP binary, PHP doesn't change its locale until you call &amp;lt;tt&amp;gt;setlocale( )&amp;lt;/tt&amp;gt;. After setting environment variable &amp;lt;tt&amp;gt;LC_ALL&amp;lt;/tt&amp;gt; to &amp;lt;tt&amp;gt;es_US&amp;lt;/tt&amp;gt;, for example, PHP still runs in the default C locale.&lt;br /&gt;
&lt;br /&gt;
=== See Also ===&lt;br /&gt;
&lt;br /&gt;
[[PHP Cookbook/Internationalization and Localization#Using a Particular Locale|Section 16.3]] shows how to use a particular locale; documentation on &amp;lt;tt&amp;gt;setlocale( )&amp;lt;/tt&amp;gt; at ''http://www.php.net/setlocale'' and &amp;lt;tt&amp;gt;auto_prepend_file&amp;lt;/tt&amp;gt; at ''http://www.php.net/manual/en/configuration.directives.php#ini.auto-prepend-file''.&lt;br /&gt;
&lt;br /&gt;
== Localizing Text Messages ==&lt;br /&gt;
&lt;br /&gt;
=== Problem ===&lt;br /&gt;
&lt;br /&gt;
You want to display text messages in a locale-appropriate language.&lt;br /&gt;
&lt;br /&gt;
=== Solution ===&lt;br /&gt;
&lt;br /&gt;
Maintain a message catalog of words and phrases and retrieve the appropriate string from the message catalog before printing it. Here's a simple message catalog with some foods in American and British English and a function to retrieve words from the catalog:&lt;br /&gt;
&lt;br /&gt;
 $messages = array ('en_US' =&amp;gt; &lt;br /&gt;
              array(&lt;br /&gt;
               'My favorite foods are' =&amp;gt; 'My favorite foods are',&lt;br /&gt;
               'french fries' =&amp;gt; 'french fries',&lt;br /&gt;
               'biscuit'      =&amp;gt; 'biscuit',&lt;br /&gt;
               'candy'        =&amp;gt; 'candy',&lt;br /&gt;
               'potato chips' =&amp;gt; 'potato chips',&lt;br /&gt;
               'cookie'       =&amp;gt; 'cookie',&lt;br /&gt;
               'corn'         =&amp;gt; 'corn',&lt;br /&gt;
               'eggplant'     =&amp;gt; 'eggplant'&lt;br /&gt;
              ),&lt;br /&gt;
            'en_GB' =&amp;gt; &lt;br /&gt;
              array(&lt;br /&gt;
               'My favorite foods are' =&amp;gt; 'My favourite foods are',&lt;br /&gt;
               'french fries' =&amp;gt; 'chips',&lt;br /&gt;
               'biscuit'      =&amp;gt; 'scone',&lt;br /&gt;
               'candy'        =&amp;gt; 'sweets',&lt;br /&gt;
               'potato chips' =&amp;gt; 'crisps',&lt;br /&gt;
               'cookie'       =&amp;gt; 'biscuit',&lt;br /&gt;
               'corn'         =&amp;gt; 'maize',&lt;br /&gt;
               'eggplant'     =&amp;gt; 'aubergine'&lt;br /&gt;
              )&lt;br /&gt;
             );&lt;br /&gt;
 &lt;br /&gt;
 function msg($s) {&lt;br /&gt;
   global $LANG;&lt;br /&gt;
   global $messages;&lt;br /&gt;
   if (isset($messages[$LANG][$s])) {&lt;br /&gt;
     return $messages[$LANG][$s];&lt;br /&gt;
   } else {&lt;br /&gt;
     error_log(&amp;quot;l10n error: LANG: $lang, message: '$s'&amp;quot;);&lt;br /&gt;
   }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
=== Discussion ===&lt;br /&gt;
&lt;br /&gt;
This short program uses the message catalog to print out a list of foods:&lt;br /&gt;
&lt;br /&gt;
 $LANG = 'en_GB';&lt;br /&gt;
 print msg('My favorite foods are').&amp;quot;:\n&amp;quot;;&lt;br /&gt;
 print msg('french fries').&amp;quot;\n&amp;quot;;&lt;br /&gt;
 print msg('potato chips').&amp;quot;\n&amp;quot;;&lt;br /&gt;
 print msg('corn').&amp;quot;\n&amp;quot;;&lt;br /&gt;
 print msg('candy').&amp;quot;\n&amp;quot;;&lt;br /&gt;
 '''My favourite foods are:'''&lt;br /&gt;
                '''chips'''&lt;br /&gt;
                '''crisps'''&lt;br /&gt;
                '''maize'''&lt;br /&gt;
                '''sweets'''&lt;br /&gt;
             &lt;br /&gt;
&lt;br /&gt;
To have the program output in American English instead of British English, just set &amp;lt;tt&amp;gt;$LANG&amp;lt;/tt&amp;gt; to &amp;lt;tt&amp;gt;en_US&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
You can combine the &amp;lt;tt&amp;gt;msg( )&amp;lt;/tt&amp;gt; message retrieval function with &amp;lt;tt&amp;gt;sprintf( )&amp;lt;/tt&amp;gt; to store phrases that require values to be substituted into them. For example, consider the English sentence &amp;quot;I am 12 years old.&amp;quot; In Spanish, the corresponding phrase is &amp;quot;Tengo 12 años.&amp;quot; The Spanish phrase can't be built by stitching together translations of &amp;quot;I am,&amp;quot; the numeral 12, and &amp;quot;years old.&amp;quot; Instead, store them in the message catalogs as &amp;lt;tt&amp;gt;sprintf( )&amp;lt;/tt&amp;gt;-style format strings:&lt;br /&gt;
&lt;br /&gt;
 $messages = array ('en_US' =&amp;gt; array('I am X years old.' =&amp;gt; 'I am %d years old.'),&lt;br /&gt;
                    'es_US' =&amp;gt; array('I am X years old.' =&amp;gt; 'Tengo %d años.')&lt;br /&gt;
             );&lt;br /&gt;
&lt;br /&gt;
You can then pass the results of &amp;lt;tt&amp;gt;msg( )&amp;lt;/tt&amp;gt; to &amp;lt;tt&amp;gt;sprintf( )&amp;lt;/tt&amp;gt; as a format string:&lt;br /&gt;
&lt;br /&gt;
 $LANG = 'es_US';&lt;br /&gt;
 print sprintf(msg('I am X years old.'),12);&lt;br /&gt;
 '''Tengo 12 años'''.&lt;br /&gt;
&lt;br /&gt;
For phrases that require the substituted values to be in a different order in different language, &amp;lt;tt&amp;gt;sprintf( )&amp;lt;/tt&amp;gt; supports changing the order of the arguments:&lt;br /&gt;
&lt;br /&gt;
 $messages = array ('en_US' =&amp;gt; &lt;br /&gt;
                     array('I am X years and Y months old.' =&amp;gt; &lt;br /&gt;
                           'I am %d years and %d months old.'),&lt;br /&gt;
                    'es_US' =&amp;gt;&lt;br /&gt;
                     array('I am X years and Y months old.' =&amp;gt; &lt;br /&gt;
                           'Tengo %2$d meses y %1$d años.')&lt;br /&gt;
             );&lt;br /&gt;
&lt;br /&gt;
With either language, call &amp;lt;tt&amp;gt;sprintf( )&amp;lt;/tt&amp;gt; with the same order of arguments (i.e., first years, then months):&lt;br /&gt;
&lt;br /&gt;
 $LANG = 'es_US';&lt;br /&gt;
 print sprintf(msg('I am X years and Y months old.'),12,7);&lt;br /&gt;
 '''Tengo 7 meses y 12 años'''.&lt;br /&gt;
&lt;br /&gt;
In the format string, &amp;lt;tt&amp;gt;%2$&amp;lt;/tt&amp;gt; tells &amp;lt;tt&amp;gt;sprintf( )&amp;lt;/tt&amp;gt; to use the second argument, and &amp;lt;tt&amp;gt;%1$&amp;lt;/tt&amp;gt; tells it to use the first.&lt;br /&gt;
&lt;br /&gt;
These phrases can also be stored as a function's return value instead of as a string in an array. Storing the phrases as functions removes the need to use &amp;lt;tt&amp;gt;sprintf( )&amp;lt;/tt&amp;gt;. Functions that return a sentence look like this:&lt;br /&gt;
&lt;br /&gt;
 // English version&lt;br /&gt;
 function i_am_X_years_old($age) {&lt;br /&gt;
  return &amp;quot;I am $age years old.&amp;quot;;&lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
 // Spanish version&lt;br /&gt;
 function i_am_X_years_old($age) {&lt;br /&gt;
  return &amp;quot;Tengo $age años.&amp;quot;;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
If some parts of the message catalog belong in an array, and some parts belong in functions, an object is a helpful container for a language's message catalog. A base object and two simple message catalogs look like this:&lt;br /&gt;
&lt;br /&gt;
 class pc_MC_Base {&lt;br /&gt;
   var $messages;&lt;br /&gt;
   var $lang;&lt;br /&gt;
 &lt;br /&gt;
   function msg($s) {&lt;br /&gt;
     if (isset($this-&amp;gt;messages[$s])) {&lt;br /&gt;
       return $this-&amp;gt;messages[$s];&lt;br /&gt;
     } else {&lt;br /&gt;
       error_log(&amp;quot;l10n error: LANG: $this-&amp;gt;lang, message: '$s'&amp;quot;);&lt;br /&gt;
     }&lt;br /&gt;
   }&lt;br /&gt;
 &lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
 class pc_MC_es_US extends pc_MC_Base {&lt;br /&gt;
 &lt;br /&gt;
   function pc_MC_es_US() {&lt;br /&gt;
     $this-&amp;gt;lang = 'es_US';&lt;br /&gt;
     $this-&amp;gt;messages = array ('chicken' =&amp;gt; 'pollo',&lt;br /&gt;
                  'cow'     =&amp;gt; 'vaca',&lt;br /&gt;
                  'horse'   =&amp;gt; 'caballo'&lt;br /&gt;
                  );&lt;br /&gt;
   }&lt;br /&gt;
    &lt;br /&gt;
   function i_am_X_years_old($age) {&lt;br /&gt;
     return &amp;quot;Tengo $age años&amp;quot;;&lt;br /&gt;
   }&lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
 class pc_MC_en_US extends pc_MC_Base {&lt;br /&gt;
   &lt;br /&gt;
   function pc_MC_en_US() {&lt;br /&gt;
     $this-&amp;gt;lang = 'en_US';&lt;br /&gt;
     $this-&amp;gt;messages = array ('chicken' =&amp;gt; 'chicken',&lt;br /&gt;
                  'cow'     =&amp;gt; 'cow',&lt;br /&gt;
                  'horse'   =&amp;gt; 'horse'&lt;br /&gt;
                  );&lt;br /&gt;
   }&lt;br /&gt;
    &lt;br /&gt;
   function i_am_X_years_old($age) {&lt;br /&gt;
     return &amp;quot;I am $age years old.&amp;quot;;&lt;br /&gt;
   }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Each message catalog object extends the &amp;lt;tt&amp;gt;pc_MC_Base&amp;lt;/tt&amp;gt; class to get the &amp;lt;tt&amp;gt;msg( )&amp;lt;/tt&amp;gt; method, and then defines its own messages (in its constructor) and its own functions that return phrases. Here's how to print text in Spanish:&lt;br /&gt;
&lt;br /&gt;
 $MC = new pc_MC_es_US;&lt;br /&gt;
 &lt;br /&gt;
 print $MC-&amp;gt;msg('cow');&lt;br /&gt;
 print $MC-&amp;gt;i_am_X_years_old(15);&lt;br /&gt;
&lt;br /&gt;
To print the same text in English, &amp;lt;tt&amp;gt;$MC&amp;lt;/tt&amp;gt; just needs to be instantiated as a &amp;lt;tt&amp;gt;pc_MC_en_US&amp;lt;/tt&amp;gt; object instead of a &amp;lt;tt&amp;gt;pc_MC_es_US&amp;lt;/tt&amp;gt; object. The rest of the code remains unchanged.&lt;br /&gt;
&lt;br /&gt;
=== See Also ===&lt;br /&gt;
&lt;br /&gt;
The introduction to [[PHP Cookbook/Classes and Objects|Chapter 7]] discusses object inheritance; documentation on &amp;lt;tt&amp;gt;sprintf( )&amp;lt;/tt&amp;gt; at ''http://www.php.net/sprintf''.&lt;br /&gt;
&lt;br /&gt;
== Localizing Dates and Times ==&lt;br /&gt;
&lt;br /&gt;
=== Problem ===&lt;br /&gt;
&lt;br /&gt;
You want to display dates and times in a locale-specific manner.&lt;br /&gt;
&lt;br /&gt;
=== Solution ===&lt;br /&gt;
&lt;br /&gt;
Use &amp;lt;tt&amp;gt;strftime( )&amp;lt;/tt&amp;gt; 's &amp;lt;tt&amp;gt;%c&amp;lt;/tt&amp;gt; format string:&lt;br /&gt;
&lt;br /&gt;
  print strftime('%c');&lt;br /&gt;
&lt;br /&gt;
You can also store &amp;lt;tt&amp;gt;strftime( )&amp;lt;/tt&amp;gt; format strings as messages in your message catalog:&lt;br /&gt;
&lt;br /&gt;
 $MC = new pc_MC_es_US;&lt;br /&gt;
 print strftime($MC-&amp;gt;msg('%Y-%m-%d'));&lt;br /&gt;
&lt;br /&gt;
=== Discussion ===&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;tt&amp;gt;%c&amp;lt;/tt&amp;gt; format string tells &amp;lt;tt&amp;gt;strftime( )&amp;lt;/tt&amp;gt; to return the preferred date and time representation for the current locale. Here's the quickest way to a locale-appropriate formatted time string:&lt;br /&gt;
&lt;br /&gt;
 print strftime('%c');&lt;br /&gt;
&lt;br /&gt;
This code produces a variety of results:&lt;br /&gt;
&lt;br /&gt;
 Tue Aug 13 18:37:11 2002     // in the default C locale&lt;br /&gt;
 mar 13 ago 2002 18:37:11 EDT // in the es_US locale&lt;br /&gt;
 mar 13 aoÛ 2002 18:37:11 EDT // in the fr_FR locale&lt;br /&gt;
&lt;br /&gt;
The formatted time string that &amp;lt;tt&amp;gt;%c&amp;lt;/tt&amp;gt; produces, while locale-appropriate, isn't very flexible. If you just want the time, for example, you must pass a different format string to &amp;lt;tt&amp;gt;strftime( )&amp;lt;/tt&amp;gt;. But these format strings themselves vary in different locales. In some locales, displaying an hour from 1 to 12 with an A.M./P.M. designation may be appropriate, while in others the hour should range from 0 to 23. To display appropriate time strings for a locale, add elements to the locale's &amp;lt;tt&amp;gt;$messages&amp;lt;/tt&amp;gt; array for each time format you want. The key for a particular time format, such as &amp;lt;tt&amp;gt;%H:%M&amp;lt;/tt&amp;gt;, is always the same in each locale. The value, however, can vary, such as &amp;lt;tt&amp;gt;%H:%M&amp;lt;/tt&amp;gt; for 24-hour locales or &amp;lt;tt&amp;gt;%I:%M %P&amp;lt;/tt&amp;gt; for 12-hour locales. Then, look up the appropriate format string and pass it to &amp;lt;tt&amp;gt;strftime( )&amp;lt;/tt&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
 $MC = new pc_MC_es_US;&lt;br /&gt;
 &lt;br /&gt;
 print strftime($MC-&amp;gt;msg('%H:%M'));&lt;br /&gt;
&lt;br /&gt;
Changing the locale doesn't change the time zone, it changes only the formatting of the displayed result.&lt;br /&gt;
&lt;br /&gt;
=== See Also ===&lt;br /&gt;
&lt;br /&gt;
[[PHP Cookbook/Dates and Times#Printing a Date or Time in a Specified Format|Section 3.5]] discusses the format strings that &amp;lt;tt&amp;gt;strftime( )&amp;lt;/tt&amp;gt; accepts; [[PHP Cookbook/Dates and Times#Calculating Time with Time Zones|Section 3.12]] covers changing time zones in your program; documentation on &amp;lt;tt&amp;gt;strftime( )&amp;lt;/tt&amp;gt; at ''http://www.php.net/strftime''.&lt;br /&gt;
&lt;br /&gt;
== Localizing Currency Values ==&lt;br /&gt;
&lt;br /&gt;
=== Problem ===&lt;br /&gt;
&lt;br /&gt;
You want to display currency amounts in a locale-specific format.&lt;br /&gt;
&lt;br /&gt;
=== Solution ===&lt;br /&gt;
&lt;br /&gt;
Use the &amp;lt;tt&amp;gt;pc_format_currency( )&amp;lt;/tt&amp;gt; function, shown in [[PHP Cookbook/Internationalization and Localization#phpckbk-CHP-16-EX-1|Example 16-1]], to produce an appropriately formatted string. For example:&lt;br /&gt;
&lt;br /&gt;
 setlocale(LC_ALL,'fr_CA');&lt;br /&gt;
 print pc_format_currency(-12345678.45);&lt;br /&gt;
 '''(12 345 678,45 $)'''&lt;br /&gt;
             &lt;br /&gt;
&lt;br /&gt;
=== Discussion ===&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;tt&amp;gt;pc_format_currency( )&amp;lt;/tt&amp;gt; function, shown in [[PHP Cookbook/Internationalization and Localization#phpckbk-CHP-16-EX-1|Example 16-1]], gets the currency formatting information from &amp;lt;tt&amp;gt;localeconv( )&amp;lt;/tt&amp;gt; and then uses &amp;lt;tt&amp;gt;number_format( )&amp;lt;/tt&amp;gt; and some logic to construct the correct string.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;phpckbk-CHP-16-EX-1&amp;quot;&amp;gt;&lt;br /&gt;
'''Example 16-1. pc_format_currency'''&lt;br /&gt;
&lt;br /&gt;
 function pc_format_currency($amt) {&lt;br /&gt;
     // get locale-specific currency formatting information &lt;br /&gt;
     $a = localeconv();&lt;br /&gt;
     &lt;br /&gt;
     // compute sign of $amt and then remove it&lt;br /&gt;
     if ($amt &amp;lt; 0) { $sign = -1; } else { $sign = 1; }&lt;br /&gt;
     $amt = abs($amt);&lt;br /&gt;
     // format $amt with appropriate grouping, decimal point, and fractional digits &lt;br /&gt;
     $amt = number_format($amt,$a['frac_digits'],$a['mon_decimal_point'],&lt;br /&gt;
                          $a['mon_thousands_sep']);&lt;br /&gt;
     &lt;br /&gt;
     // figure out where to put the currency symbol and positive or negative signs&lt;br /&gt;
     $currency_symbol = $a['currency_symbol'];&lt;br /&gt;
     // is $amt &amp;gt;= 0 ? &lt;br /&gt;
     if (1 == $sign) {&lt;br /&gt;
         $sign_symbol  = 'positive_sign';&lt;br /&gt;
         $cs_precedes  = 'p_cs_precedes';&lt;br /&gt;
         $sign_posn    = 'p_sign_posn';&lt;br /&gt;
         $sep_by_space = 'p_sep_by_space';&lt;br /&gt;
     } else {&lt;br /&gt;
         $sign_symbol  = 'negative_sign';&lt;br /&gt;
         $cs_precedes  = 'n_cs_precedes';&lt;br /&gt;
         $sign_posn    = 'n_sign_posn';&lt;br /&gt;
         $sep_by_space = 'n_sep_by_space';&lt;br /&gt;
     }&lt;br /&gt;
     if ($a[$cs_precedes]) {&lt;br /&gt;
         if (3 == $a[$sign_posn]) {&lt;br /&gt;
             $currency_symbol = $a[$sign_symbol].$currency_symbol;&lt;br /&gt;
         } elseif (4 == $a[$sign_posn]) {&lt;br /&gt;
             $currency_symbol .= $a[$sign_symbol];&lt;br /&gt;
         }&lt;br /&gt;
         // currency symbol in front &lt;br /&gt;
         if ($a[$sep_by_space]) {&lt;br /&gt;
             $amt = $currency_symbol.' '.$amt;&lt;br /&gt;
         } else {&lt;br /&gt;
             $amt = $currency_symbol.$amt;&lt;br /&gt;
         }&lt;br /&gt;
     } else {&lt;br /&gt;
         // currency symbol after amount &lt;br /&gt;
         if ($a[$sep_by_space]) {&lt;br /&gt;
             $amt .= ' '.$currency_symbol;&lt;br /&gt;
         } else {&lt;br /&gt;
             $amt .= $currency_symbol;&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
     if (0 == $a[$sign_posn]) {&lt;br /&gt;
         $amt = &amp;quot;($amt)&amp;quot;;&lt;br /&gt;
     } elseif (1 == $a[$sign_posn]) {&lt;br /&gt;
         $amt = $a[$sign_symbol].$amt;&lt;br /&gt;
     } elseif (2 == $a[$sign_posn]) {&lt;br /&gt;
         $amt .= $a[$sign_symbol];&lt;br /&gt;
     }&lt;br /&gt;
     return $amt;&lt;br /&gt;
 }&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The code in &amp;lt;tt&amp;gt;pc_format_currency( )&amp;lt;/tt&amp;gt; that puts the currency symbol and sign in the correct place is almost identical for positive and negative amounts; it just uses different elements of the array returned by &amp;lt;tt&amp;gt;localeconv( )&amp;lt;/tt&amp;gt;. The relevant elements of &amp;lt;tt&amp;gt;localeconv( )&amp;lt;/tt&amp;gt;'s returned array are shown in [[PHP Cookbook/Internationalization and Localization#phpckbk-CHP-16-TABLE-1|Table 16-1]].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;phpckbk-CHP-16-TABLE-1&amp;quot;&amp;gt;&lt;br /&gt;
'''Table 16-1. Currency-related information from localeconv( )'''&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; &lt;br /&gt;
|-&lt;br /&gt;
! Array element !! Description&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;currency_symbol&amp;lt;/tt&amp;gt; || Local currency symbol&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;mon_decimal_point&amp;lt;/tt&amp;gt; || Monetary decimal point character&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;mon_thousands_sep&amp;lt;/tt&amp;gt; || Monetary thousands separator&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;positive_sign&amp;lt;/tt&amp;gt; || Sign for positive values&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;negative_sign&amp;lt;/tt&amp;gt; || Sign for negative values&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;frac_digits&amp;lt;/tt&amp;gt; || Number of fractional digits&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;p_cs_precedes&amp;lt;/tt&amp;gt; || 1 if &amp;lt;tt&amp;gt;currency_symbol&amp;lt;/tt&amp;gt; should precede a positive value, 0 if it should follow&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;p_sep_by_space&amp;lt;/tt&amp;gt; || 1 if a space should separate the currency symbol from a positive value, 0 if not&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;n_cs_precedes&amp;lt;/tt&amp;gt; || 1 if &amp;lt;tt&amp;gt;currency_symbol&amp;lt;/tt&amp;gt; should precede a negative value, 0 if it should follow&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;n_sep_by_space&amp;lt;/tt&amp;gt; || 1 if a space should separate &amp;lt;tt&amp;gt;currency_symbol&amp;lt;/tt&amp;gt; from a negative value, 0 if not&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;p_sign_posn&amp;lt;/tt&amp;gt; || Positive sign position:0if parenthesis should surround the quantity and &amp;lt;tt&amp;gt;currency_symbol&amp;lt;/tt&amp;gt;1 if the sign string should precede the quantity and &amp;lt;tt&amp;gt;currency_symbol&amp;lt;/tt&amp;gt;2 if the sign string should follow the quantity and &amp;lt;tt&amp;gt;currency_symbol&amp;lt;/tt&amp;gt;3 if the sign string should immediately precede &amp;lt;tt&amp;gt;currency_symbol&amp;lt;/tt&amp;gt;4 if the sign string should immediately follow &amp;lt;tt&amp;gt;currency_symbol&amp;lt;/tt&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;n_sign_posn&amp;lt;/tt&amp;gt; || Negative sign position: same possible values as &amp;lt;tt&amp;gt;p_sign_posn&amp;lt;/tt&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
There is a function in the C library called &amp;lt;tt&amp;gt;strfmon( )&amp;lt;/tt&amp;gt; that does for currency what &amp;lt;tt&amp;gt;strftime( )&amp;lt;/tt&amp;gt; does for dates and times; however, it isn't implemented in PHP. The &amp;lt;tt&amp;gt;pc_format_currency( )&amp;lt;/tt&amp;gt; function provides most of the same capabilities.&lt;br /&gt;
&lt;br /&gt;
=== See Also ===&lt;br /&gt;
&lt;br /&gt;
[[PHP Cookbook/Numbers#Formatting Numbers|Section 2.10]] also discusses &amp;lt;tt&amp;gt;number_format( )&amp;lt;/tt&amp;gt;; documentation on &amp;lt;tt&amp;gt;localeconv( )&amp;lt;/tt&amp;gt; at ''http://www.php.net/localeconv'' and &amp;lt;tt&amp;gt;number_format( )&amp;lt;/tt&amp;gt; at ''http://www.php.net/number-format''.&lt;br /&gt;
&lt;br /&gt;
== Localizing Images ==&lt;br /&gt;
&lt;br /&gt;
=== Problem ===&lt;br /&gt;
&lt;br /&gt;
You want to display images that have text in them and have that text in a locale-appropriate language.&lt;br /&gt;
&lt;br /&gt;
=== Solution ===&lt;br /&gt;
&lt;br /&gt;
Make an image directory for each locale you want to support, as well as a global image directory for images that have no locale-specific information in them. Create copies of each locale-specific image in the appropriate locale-specific directory. Make sure that the images have the same filename in the different directories. Instead of printing out image URLs directly, use a wrapper function similar to the &amp;lt;tt&amp;gt;msg( )&amp;lt;/tt&amp;gt; function in [[PHP Cookbook/Internationalization and Localization#Localizing Text Messages|Section 16.5]] that prints out locale-specific text.&lt;br /&gt;
&lt;br /&gt;
=== Discussion ===&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;tt&amp;gt;img( )&amp;lt;/tt&amp;gt; wrapper function looks for a locale-specific version of an image first, then a global one. If neither are present, it prints a message to the error log:&lt;br /&gt;
&lt;br /&gt;
 $image_base_path = '/usr/local/www/images';&lt;br /&gt;
 $image_base_url  = '/images';&lt;br /&gt;
 &lt;br /&gt;
 function img($f) {&lt;br /&gt;
     global $LANG;&lt;br /&gt;
     global $image_base_path;&lt;br /&gt;
     global $image_base_url;&lt;br /&gt;
 &lt;br /&gt;
     if (is_readable(&amp;quot;$image_base_path/$LANG/$f&amp;quot;)) {&lt;br /&gt;
         print &amp;quot;$image_base_url/$LANG/$f&amp;quot;;&lt;br /&gt;
     } elseif (is_readable(&amp;quot;$image_base_path/global/$f&amp;quot;)) {&lt;br /&gt;
         print &amp;quot;$image_base_url/global/$f&amp;quot;;&lt;br /&gt;
     } else {&lt;br /&gt;
         error_log(&amp;quot;l10n error: LANG: $lang, image: '$f'&amp;quot;);&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
This function needs to know both the path to the image file in the filesystem (&amp;lt;tt&amp;gt;$image_base_path&amp;lt;/tt&amp;gt;) and the path to the image from the base URL of your site (''/images''). It uses the first to test if the file can be read and the second to construct an appropriate URL for the image.&lt;br /&gt;
&lt;br /&gt;
A localized image must have the same filename in each localization directory. For example, an image that says &amp;quot;New!&amp;quot; on a yellow starburst should be called ''new.gif'' in both the ''images/en_US'' directory and the ''images/es_US'' directory, even though the file ''images/es_US/new.gif'' is a picture of a yellow starburst with &amp;quot;¡Nuevo!&amp;quot; on it.&lt;br /&gt;
&lt;br /&gt;
Don't forget that the &amp;lt;tt&amp;gt;alt&amp;lt;/tt&amp;gt; text you display in your image tags also needs to be localized. A complete localized &amp;lt;tt&amp;gt;&amp;lt;img&amp;gt;&amp;lt;/tt&amp;gt; tag looks like:&lt;br /&gt;
&lt;br /&gt;
 printf('&amp;lt;img src=&amp;quot;%s&amp;quot; alt=&amp;quot;%s&amp;quot;&amp;gt;',img('cancel.png'),msg('Cancel'));&lt;br /&gt;
&lt;br /&gt;
If the localized versions of a particular image have varied dimensions, store image height and width in the message catalog as well:&lt;br /&gt;
&lt;br /&gt;
 printf('&amp;lt;img src=&amp;quot;%s&amp;quot; alt=&amp;quot;%s&amp;quot; height=&amp;quot;%d&amp;quot; width=&amp;quot;%d&amp;quot;&amp;gt;',&lt;br /&gt;
        img('cancel.png'),msg('Cancel'),&lt;br /&gt;
        msg('img-cancel-height'),msg('img-cancel-width'));&lt;br /&gt;
&lt;br /&gt;
The localized messages for &amp;lt;tt&amp;gt;img-cancel-height&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;img-cancel-width&amp;lt;/tt&amp;gt; are not text strings, but integers that describe the dimensions of the ''cancel.png'' image in each locale.&lt;br /&gt;
&lt;br /&gt;
=== See Also ===&lt;br /&gt;
&lt;br /&gt;
[[PHP Cookbook/Internationalization and Localization#Localizing Text Messages|Section 16.5]] discusses locale-specific message catalogs.&lt;br /&gt;
&lt;br /&gt;
== Localizing Included Files ==&lt;br /&gt;
&lt;br /&gt;
=== Problem ===&lt;br /&gt;
&lt;br /&gt;
You want to include locale-specific files in your pages.&lt;br /&gt;
&lt;br /&gt;
=== Solution ===&lt;br /&gt;
&lt;br /&gt;
Dynamically modify the &amp;lt;tt&amp;gt;include_path&amp;lt;/tt&amp;gt; once you've determined the appropriate locale:&lt;br /&gt;
&lt;br /&gt;
 $base = '/usr/local/php-include';&lt;br /&gt;
 $LANG = 'en_US';&lt;br /&gt;
 &lt;br /&gt;
 $include_path = ini_get('include_path');&lt;br /&gt;
 ini_set('include_path',&amp;quot;$base/$LANG:$base/global:$include_path&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
=== Discussion ===&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;tt&amp;gt;$base&amp;lt;/tt&amp;gt; variable holds the name of the base directory for your included localized files. Files that are not locale-specific go in the ''global'' subdirectory of &amp;lt;tt&amp;gt;$base&amp;lt;/tt&amp;gt;, and locale-specific files go in a subdirectory named after their locale (e.g., ''en_US''). Prepending the locale-specific directory and then the global directory to the include path makes them the first two places PHP looks when you include a file. Putting the locale-specific directory first ensures that nonlocalized information is loaded only if localized information isn't available.&lt;br /&gt;
&lt;br /&gt;
This technique is similar to what the &amp;lt;tt&amp;gt;img( )&amp;lt;/tt&amp;gt; function does in the [[PHP Cookbook/Internationalization and Localization#Localizing Images|Section 16.8]]. Here, however, you can take advantage of PHP's &amp;lt;tt&amp;gt;include_path&amp;lt;/tt&amp;gt; feature to have the directory searching happen automatically. For maximum utility, reset &amp;lt;tt&amp;gt;include_path&amp;lt;/tt&amp;gt; as early as possible in your code, preferably at the top of a file loaded via &amp;lt;tt&amp;gt;auto_prepend_file&amp;lt;/tt&amp;gt; on every request.&lt;br /&gt;
&lt;br /&gt;
=== See Also ===&lt;br /&gt;
&lt;br /&gt;
Documentation on &amp;lt;tt&amp;gt;include_path&amp;lt;/tt&amp;gt; at ''http://www.php.net/manual/en/configuration.directives.php#ini.include-path'' and &amp;lt;tt&amp;gt;auto_prepend_file&amp;lt;/tt&amp;gt; at ''http://www.php.net/manual/en/configuration.directives.php#ini.auto-prepend-file''.&lt;br /&gt;
&lt;br /&gt;
== Managing Localization Resources ==&lt;br /&gt;
&lt;br /&gt;
=== Problem ===&lt;br /&gt;
&lt;br /&gt;
You need to keep track of your various message catalogs and images.&lt;br /&gt;
&lt;br /&gt;
=== Solution ===&lt;br /&gt;
&lt;br /&gt;
Two techniques simplify the management of your localization resources. The first is making a new language's object, for example Canadian English, extend from a similar existing language, such as American English. You only have to change the words and phrases in the new object that differ from the original language.&lt;br /&gt;
&lt;br /&gt;
The second technique: to track what phrases still need to be translated in new languages, put stubs in the new language object that have the same value as in your base language. By finding which values are the same in the base language and the new language, you can then generate a list of words and phrases to translate.&lt;br /&gt;
&lt;br /&gt;
=== Discussion ===&lt;br /&gt;
&lt;br /&gt;
The ''catalog-compare.php'' program shown in [[PHP Cookbook/Internationalization and Localization#phpckbk-CHP-16-EX-2|Example 16-2]] prints out messages that are the same in two catalogs, as well as messages that are missing from one catalog but present in another.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;phpckbk-CHP-16-EX-2&amp;quot;&amp;gt;&lt;br /&gt;
'''Example 16-2. catalog-compare.php'''&lt;br /&gt;
&lt;br /&gt;
 $base = 'pc_MC_'.$_SERVER['argv'][1];&lt;br /&gt;
 $other  = 'pc_MC_'.$_SERVER['argv'][2];&lt;br /&gt;
 &lt;br /&gt;
 require 'pc_MC_Base.php';&lt;br /&gt;
 require &amp;quot;$base.php&amp;quot;;&lt;br /&gt;
 require &amp;quot;$other.php&amp;quot;;&lt;br /&gt;
 &lt;br /&gt;
 $base_obj = new $base;&lt;br /&gt;
 $other_obj = new $other;&lt;br /&gt;
 &lt;br /&gt;
 /* Check for messages in the other class that&lt;br /&gt;
  * are the same as the base class or are in&lt;br /&gt;
  * the base class but missing from the other class */ &lt;br /&gt;
 foreach ($base_obj-&amp;gt;messages as $k =&amp;gt; $v) {&lt;br /&gt;
     if (isset($other_obj-&amp;gt;messages[$k])) {&lt;br /&gt;
         if ($v == $other_obj-&amp;gt;messages[$k]) {&lt;br /&gt;
             print &amp;quot;SAME: $k\n&amp;quot;;&lt;br /&gt;
         }&lt;br /&gt;
     } else {&lt;br /&gt;
         print &amp;quot;MISSING: $k\n&amp;quot;;&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
 /* Check for messages in the other class but missing&lt;br /&gt;
  * from the base class */&lt;br /&gt;
 foreach ($other_obj-&amp;gt;messages as $k =&amp;gt; $v) {&lt;br /&gt;
     if (! isset($base_obj-&amp;gt;messages[$k])) {&lt;br /&gt;
         print &amp;quot;MISSING (BASE): $k\n&amp;quot;;&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To use this program, put each message catalog object in a file with the same name as the object (e.g., the &amp;lt;tt&amp;gt;pc_MC_en_US&amp;lt;/tt&amp;gt; class should be in a file named ''pc_MC_en_US.php'', and the &amp;lt;tt&amp;gt;pc_MC_es_US&amp;lt;/tt&amp;gt; class should be in a file named ''pc_MC_es_US.php''). You then call the program with the two locale names as arguments on the command line:&lt;br /&gt;
&lt;br /&gt;
 % '''php catalog-compare.php en_US es_US'''&lt;br /&gt;
             &lt;br /&gt;
&lt;br /&gt;
In a web context, it can be useful to use a different locale and message catalog on a per-request basis. The locale to use may come from the browser (in an &amp;lt;tt&amp;gt;Accept-Language&amp;lt;/tt&amp;gt; header), or it may be explicitly set by the server (different virtual hosts may be set up to display the same content in different languages). If the same code needs to select a message catalog on a per-request basis, the message catalog class can be instantiated like this:&lt;br /&gt;
&lt;br /&gt;
 $classname = &amp;quot;pc_MC_$locale&amp;quot;;&lt;br /&gt;
 &lt;br /&gt;
 require 'pc_MC_Base.php';&lt;br /&gt;
 require $classname.'.php';&lt;br /&gt;
 &lt;br /&gt;
 $MC = new $classname;&lt;br /&gt;
&lt;br /&gt;
=== See Also ===&lt;br /&gt;
&lt;br /&gt;
[[PHP Cookbook/Internationalization and Localization#Localizing Text Messages|Section 16.5]] discusses message catalogs; [[PHP Cookbook/Classes and Objects#Finding the Methods and Properties of an Object|Section 7.11]] for information on finding the methods and properties of an object.&lt;br /&gt;
&lt;br /&gt;
== Using gettext ==&lt;br /&gt;
&lt;br /&gt;
=== Problem ===&lt;br /&gt;
&lt;br /&gt;
You want a comprehensive system to create, manage, and deploy message catalogs.&lt;br /&gt;
&lt;br /&gt;
=== Solution ===&lt;br /&gt;
&lt;br /&gt;
Use PHP's ''gettext'' extension, which allows you to use GNU's ''gettext'' utilities:&lt;br /&gt;
&lt;br /&gt;
 bindtextdomain('gnumeric','/usr/share/locale');&lt;br /&gt;
 textdomain('gnumeric');&lt;br /&gt;
 &lt;br /&gt;
 $languages = array('en_CA','da_DK','de_AT','fr_FR');&lt;br /&gt;
 foreach ($languages as $language) {&lt;br /&gt;
   setlocale(LC_ALL, $language);&lt;br /&gt;
   print gettext(&amp;quot; Unknown formula&amp;quot;).&amp;quot;\n&amp;quot;;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
=== Discussion ===&lt;br /&gt;
&lt;br /&gt;
''gettext'' is a set of tools that makes it easier for your application to produce multilingual messages. Compiling PHP with the &amp;lt;tt&amp;gt;--with-gettext&amp;lt;/tt&amp;gt; option enables functions to retrieve the appropriate text from ''gettext''-format message catalogs, and there are a number of external tools to edit the message catalogs.&lt;br /&gt;
&lt;br /&gt;
With ''gettext'', messages are divided into domains, and all messages for a particular domain are stored in the same file. &amp;lt;tt&amp;gt;bindtextdomain( )&amp;lt;/tt&amp;gt; tells ''gettext'' where to find the message catalog for a particular domain. A call to:&lt;br /&gt;
&lt;br /&gt;
 bindtextdomain('gnumeric','/usr/share/locale') &lt;br /&gt;
&lt;br /&gt;
indicates that the message catalog for the &amp;lt;tt&amp;gt;gnumeric&amp;lt;/tt&amp;gt; domain in the &amp;lt;tt&amp;gt;en_CA&amp;lt;/tt&amp;gt; locale is in the file ''/usr/share/locale/en_CA/LC_MESSAGES/gnumeric.mo''.&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;tt&amp;gt;textdomain('gnumeric')&amp;lt;/tt&amp;gt; function sets the default domain to &amp;lt;tt&amp;gt;gnumeric&amp;lt;/tt&amp;gt;. Calling &amp;lt;tt&amp;gt;gettext( )&amp;lt;/tt&amp;gt; retrieves a message from the default domain. There are other functions, such as &amp;lt;tt&amp;gt;dgettext( )&amp;lt;/tt&amp;gt; , that let you retrieve a message from a different domain. When &amp;lt;tt&amp;gt;gettext( )&amp;lt;/tt&amp;gt; (or &amp;lt;tt&amp;gt;dgettext( )&amp;lt;/tt&amp;gt;) is called, it returns the appropriate message for the current locale. If there's no message in the catalog for the current locale that corresponds to the argument passed to it, &amp;lt;tt&amp;gt;gettext( )&amp;lt;/tt&amp;gt; (or &amp;lt;tt&amp;gt;dgettext( )&amp;lt;/tt&amp;gt;) returns just its argument. As a result, if you haven't translated all your messages, your code prints out English (or whatever your base language is) for those untranslated messages.&lt;br /&gt;
&lt;br /&gt;
Setting the default domain with &amp;lt;tt&amp;gt;textdomain( )&amp;lt;/tt&amp;gt; makes each subsequent retrieval of a message from that domain more concise, because you just have to call &amp;lt;tt&amp;gt;gettext('Good morning')&amp;lt;/tt&amp;gt; instead of &amp;lt;tt&amp;gt;dgettext('domain','Good&amp;lt;/tt&amp;gt; &amp;lt;tt&amp;gt;morning')&amp;lt;/tt&amp;gt;. However, if even &amp;lt;tt&amp;gt;gettext('Good morning')&amp;lt;/tt&amp;gt; is too much typing, you can take advantage of an undocumented function alias: &amp;lt;tt&amp;gt;_( )&amp;lt;/tt&amp;gt; for &amp;lt;tt&amp;gt;gettext( )&amp;lt;/tt&amp;gt;. Instead of &amp;lt;tt&amp;gt;gettext('Good morning')&amp;lt;/tt&amp;gt;, use &amp;lt;tt&amp;gt;_('Good morning')&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The ''gettext'' web site has helpful and detailed information for managing the information flow between programmers and translators and how to efficiently use ''gettext''. It also includes information on other tools you can use to manage your message catalogs, such as a special GNU Emacs mode.&lt;br /&gt;
&lt;br /&gt;
=== See Also ===&lt;br /&gt;
&lt;br /&gt;
Documentation on ''gettext'' at ''http://www.php.net/gettext''; the ''gettext'' library at ''http://www.gnu.org/software/gettext/gettext.html''.&lt;br /&gt;
&lt;br /&gt;
== Reading or Writing Unicode Characters ==&lt;br /&gt;
&lt;br /&gt;
=== Problem ===&lt;br /&gt;
&lt;br /&gt;
You want to read Unicode-encoded characters from a file, database, or form; or, you want to write Unicode-encoded characters.&lt;br /&gt;
&lt;br /&gt;
=== Solution ===&lt;br /&gt;
&lt;br /&gt;
Use &amp;lt;tt&amp;gt;utf8_encode( )&amp;lt;/tt&amp;gt; to convert single-byte ISO-8859-1 encoded characters to UTF-8:&lt;br /&gt;
&lt;br /&gt;
 print utf8_encode('Kurt Gödel is swell.');&lt;br /&gt;
&lt;br /&gt;
Use &amp;lt;tt&amp;gt;utf8_decode( )&amp;lt;/tt&amp;gt; to convert UTF-8 encoded characters to single-byte ISO-8859-1 encoded characters:&lt;br /&gt;
&lt;br /&gt;
 print utf8_decode(&amp;quot;Kurt G\xc3\xb6del is swell.&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
=== Discussion ===&lt;br /&gt;
&lt;br /&gt;
There are 256 possible ASCII characters. The characters between codes 0 and 127 are standardized: control characters, letters and numbers, and punctuation. There are different rules, however, for the characters that codes 128-255 map to. One encoding is called ISO-8859-1, which includes characters necessary for writing most European languages, such as the ö in Gödel or the ñ in pestaña. Many languages, though, require more than 256 characters, and a character set that can express more than one language requires even more characters. This is where Unicode saves the day; its UTF-8 encoding can represent more than a million characters.&lt;br /&gt;
&lt;br /&gt;
This increased functionality comes at the cost of space. ASCII characters are stored in just one byte; UTF-8 encoded characters need up to four bytes. [[PHP Cookbook/Internationalization and Localization#phpckbk-CHP-16-TABLE-2|Table 16-2]] shows the byte representations of UTF-8 encoded characters.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;phpckbk-CHP-16-TABLE-2&amp;quot;&amp;gt;&lt;br /&gt;
'''Table 16-2. UTF-8 byte representation'''&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; &lt;br /&gt;
|-&lt;br /&gt;
! Character code range !! Bytes used !! Byte 1 !! Byte 2 !! Byte 3 !! Byte 4&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;0x00000000 - 0x0000007F&amp;lt;/tt&amp;gt; || 1 || &amp;lt;tt&amp;gt;0xxxxxxx&amp;lt;/tt&amp;gt; ||  ||  || &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;0x00000080 - 0x000007FF&amp;lt;/tt&amp;gt; || 2 || &amp;lt;tt&amp;gt;110xxxxx&amp;lt;/tt&amp;gt; || &amp;lt;tt&amp;gt;10xxxxxx&amp;lt;/tt&amp;gt; ||  || &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;0x00000800 - 0x0000FFFF&amp;lt;/tt&amp;gt; || 3 || &amp;lt;tt&amp;gt;1110xxxx&amp;lt;/tt&amp;gt; || &amp;lt;tt&amp;gt;10xxxxxx&amp;lt;/tt&amp;gt; || &amp;lt;tt&amp;gt;10xxxxxx&amp;lt;/tt&amp;gt; || &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;0x00010000 - 0x001FFFFF&amp;lt;/tt&amp;gt; || 4 || &amp;lt;tt&amp;gt;11110xxx&amp;lt;/tt&amp;gt; || &amp;lt;tt&amp;gt;10xxxxxx&amp;lt;/tt&amp;gt; || &amp;lt;tt&amp;gt;10xxxxxx&amp;lt;/tt&amp;gt; || &amp;lt;tt&amp;gt;10xxxxxx&amp;lt;/tt&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
In [[PHP Cookbook/Internationalization and Localization#phpckbk-CHP-16-TABLE-2|Table 16-2]], the &amp;lt;tt&amp;gt;x&amp;lt;/tt&amp;gt; positions represent bits used for actual character data. The least significant bit is the rightmost bit in the rightmost byte. In multibyte characters, the number of leading 1 bits in the leftmost byte is the same as the number of bytes in the character.&lt;br /&gt;
&lt;br /&gt;
=== See Also ===&lt;br /&gt;
&lt;br /&gt;
Documentation on &amp;lt;tt&amp;gt;utf8_encode( )&amp;lt;/tt&amp;gt; at ''http://www.php.net/utf8-encode'' and &amp;lt;tt&amp;gt;utf8_decode( )&amp;lt;/tt&amp;gt; at ''http://www.php.net/utf8-decode''; more information on Unicode is available at the Unicode Consortium's home page, ''http://www.unicode.org''; the UTF-8 and Unicode FAQ at ''http://www.cl.cam.ac.uk/~mgk25/unicode.html'' is also helpful.&lt;br /&gt;
== Notes ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;references/&amp;gt;&lt;/div&gt;</description>
			<pubDate>Thu, 06 Mar 2008 22:28:45 GMT</pubDate>			<dc:creator>Docbook2Wiki</dc:creator>			<comments>http://commons.oreilly.com/wiki/index.php/Talk:PHP_Cookbook/Internationalization_and_Localization</comments>		</item>
	</channel>
</rss>