Actions

J2.5

Difference between revisions of "Making a Language Pack for Joomla"

From Joomla! Documentation

Line 40: Line 40:
  
 
<source lang="xml"><?xml version="1.0" encoding="utf-8" ?>
 
<source lang="xml"><?xml version="1.0" encoding="utf-8" ?>
<install version="1.6" client="administrator" type="language" method="upgrade">
+
<install version="1.6" client="administrator" type="language" method="upgrade"> // change to client="site" if site pack
 
     <name>French (Fr)</name>
 
     <name>French (Fr)</name>
 
     <tag>fr-FR</tag>
 
     <tag>fr-FR</tag>
Line 57: Line 57:
 
<filename> .....ini</filename>
 
<filename> .....ini</filename>
 
[...]
 
[...]
 +
                <filename>fr-FR.lib_joomla.ini</filename> // Normally in administrator pack. !! If this is only a site pack then add in site !!
 
<filename>fr-FR.ini</filename>
 
<filename>fr-FR.ini</filename>
 
<filename file="meta">fr-FR.xml</filename> // Note the file="meta" tag, telling it's the basic xml holding info about the pack. Note file="meta"
 
<filename file="meta">fr-FR.xml</filename> // Note the file="meta" tag, telling it's the basic xml holding info about the pack. Note file="meta"
 
                 <filename file="meta">install.xml</filename> '''//Mandatory! This file lets uninstall a language.''' Note file="meta"
 
                 <filename file="meta">install.xml</filename> '''//Mandatory! This file lets uninstall a language.''' Note file="meta"
<filename>fr-FR.localise.php</filename> // in the site pack
+
<filename>fr-FR.localise.php</filename> // normally in the site pack. If the language has only an admin pack, put in the admin pack.
 
       </files>
 
       </files>
 
<media destination="fr-FR"> //if one needs to add a specific calendar
 
<media destination="fr-FR"> //if one needs to add a specific calendar

Revision as of 06:07, 29 December 2010

This is a Tutorial explaining how to create your language packs in 1.6.

fr-FR is taken as an example of language prefix.

The package is composed of 3 files, then zipped:

Naming of the pack

fr-FR_joomla_lang_full_1.6.0v1.zip

fr-FR_joomla_lang_site_1.6.0v1.zip if only site pack

1. An fr-FR.install.xml file

<?xml version="1.0" encoding="UTF-8" ?>
<extension type="package" version="1.6">
<name>French Language Pack</name>
<packagename>fr-FR</packagename>
<version>1.6</version>
<url></url>
<packager></packager>
<packagerurl></packagerurl>
<description>1.6 Joomla French Language Package</description>
<files>
        <file type="language" client="site" id="fr-FR">site_fr-FR.zip</file>
        <file type="language" client="admin" id="fr-FR">admin_fr-FR.zip</file>
</files>
</extension>

2. One zip per client (i.e. site and admin)

site_fr-FR.zip

admin_fr-FR.zip

Content of the client's zip (example below is for admin)

a) an install.xml

<?xml version="1.0" encoding="utf-8" ?>
<install version="1.6" client="administrator" type="language" method="upgrade"> // change to client="site" if site pack
    <name>French (Fr)</name>
    <tag>fr-FR</tag>
    <version>1.6</version>
    <creationDate>2010-08-01</creationDate>
    <author>French translation team : Joomla!fr</author>
    <authorEmail>traduction@joomla.fr</authorEmail>
    <authorUrl>www.joomla.fr</authorUrl>
    <copyright>Copyright (C) 2005 - 2010 Joomla.fr et Open Source Matters. Tous droits réservés</copyright>
    <license>GNU General Public License version 2 or later; see LICENSE.txt</license>
    <description>French language pack for Joomla! 1.6</description>
    <files>
                <filename>index.html</filename>
                <filename>fr-FR.com_admin.ini</filename>
                <filename>fr-FR.com_admin.sys.ini</filename>
                <filename> .....ini</filename>
                [...]
                <filename>fr-FR.lib_joomla.ini</filename> // Normally in administrator pack. !! If this is only a site pack then add in site !!
                <filename>fr-FR.ini</filename>
                <filename file="meta">fr-FR.xml</filename> // Note the file="meta" tag, telling it's the basic xml holding info about the pack. Note file="meta"
                <filename file="meta">install.xml</filename> '''//Mandatory! This file lets uninstall a language.''' Note file="meta"
                <filename>fr-FR.localise.php</filename> // normally in the site pack. If the language has only an admin pack, put in the admin pack.
      </files>
        <media destination="fr-FR"> //if one needs to add a specific calendar
            <filename>index.html</filename>
                <filename>js/index.html</filename>
                <filename>js/calendar-setup.js</filename>
                <filename>js/calendar.js</filename>
        </media>
</install>

b. All the ini files Beware of new formatting!

Double quotes in the value should be written as "_QQ_" or as &quot;

c. the fr-FR.xml

<?xml version="1.0" encoding="utf-8"?>
<metafile version="1.6"  client="administrator" method="upgrade" >
        <tag>fr-FR</tag>
        <name>French (FR)</name>
        <description>French administrator language for Joomla 1.6</description>
        <version>1.6</version>
        <creationDate>2010-08-01</creationDate>
        <author>French translation team : Joomla!fr</author>
        <authorEmail>traduction@joomla.fr</authorEmail>
        <authorUrl>www.joomla.fr</authorUrl>
        <copyright>Copyright (C) 2005 - 2010 Open Source Matters &amp;  Joomla.fr. All rights reserved.</copyright>
        <license>GNU General Public License version 2 or later; see LICENSE.txt</license>
        <metadata>
                <name>French (FR)</name>
                <tag>fr-FR</tag>
                <rtl>0</rtl>
                <locale>fr_FR.utf8, fr_FR.UTF-8, fr_FR.UTF-8@euro, French_Standard,french, fr_FR, fre_FR, fr</locale> //the locale is used to sort translated lists when present on the server.
                <firstDay>1</firstDay>  // used for the Calendar icon to specify what is the first day of the week in that language. 0 is Sunday, 1 is Monday, etc.
        </metadata>
        <params />
</metafile>

c. the fr-FR.localise.php (in the site pack)

Explanation of this file:

It replaces the fr-FR.ignore.php and it can be customized depending on languages.

  • Ignore search words.
  • Define upper and lower limit of search words length.
  • Define number of characters to display for the result of the search.
  • Define specific plural functionality for some languages where the value of the string can change depending on the count (Russian for example).
  • Define custom transliteration (i.e. when NOT using the Unicode URLS parameter in Global Configuration) to ensure proper change of some alphabets to ascii for the alias used when SEF is on. (The transliteration which is now default in 1.6 should take care of all latin-based languages.)
  • Define a custom calendar by adding a function as well as some js files


Example of a basic fr-FR.localise.php (where custom transliteration is NOT implemented)

<?php
/**
 * @version             $Id: fr-FR.localise.php 15628 2010-03-27 05:20:29Z infograf768 $
 * @copyright   Copyright (C) 2005 - 2010 Open Source Matters, Inc. All rights reserved.
 * @license             GNU General Public License version 2 or later; see LICENSE.txt
 */
 
/**
 * fr-FR localise class
 *
 * @package             Joomla.Site
 * @since               1.6
 */
abstract class fr_FRLocalise {    //// !!!! NOTE the use of fr_FR for the class !!!// do the same for your language prefix.
        /**
         * Returns the potential suffixes for a specific number of items
         *
         * @param       int $count  The number of items.
         * @return      array  An array of potential suffixes.
         * @since       1.6
         */
        public static function getPluralSuffixes($count) {
                if ($count == 0) {
                        $return =  array('0');
                }
                elseif($count == 1) {
                        $return =  array('1');
                }
                else {
                        $return = array('MORE');
                }
                return $return;
        }
        /**
         * Returns the ignored search words
         *
         * @return      array  An array of ignored search words.
         * @since       1.6
         */
        public static function getIgnoredSearchWords() {
                $search_ignore = array();
                $search_ignore[] = "et";
                $search_ignore[] = "si";
                $search_ignore[] = "ou";
                return $search_ignore;
        }
        /**
         * Returns the lower length limit of search words
         *
         * @return      integer  The lower length limit of search words.
         * @since       1.6
         */
        public static function getLowerLimitSearchWord() {
                return 3;
        }
        /**
         * Returns the upper length limit of search words
         *
         * @return      integer  The upper length limit of search words.
         * @since       1.6
         */
        public static function getUpperLimitSearchWord() {
                return 20;
        }
        /**
         * Returns the number of chars to display when searching
         *
         * @return      integer  The number of chars to display when searching.
         * @since       1.6
         */
        public static function getSearchDisplayedCharactersNumber() {
                return 200;
        }
}

Example of the function to add when custom transliteration is desired

/**
         * This method processes a string and replaces all accented UTF-8 characters by unaccented
         * ASCII-7 "equivalents"
         *
         * @param       string  $string The string to transliterate
         * @return      string  The transliteration of the string
         * @since       1.6
         */
        public static function transliterate($string)
        {
                $str = JString::strtolower($string);
 
                //Specific language transliteration.
                //This one is for latin 1, latin supplement , extended A, Cyrillic, Greek
 
                $glyph_array = array(
                'a'             =>      'à,á,â,ã,ä,å,ā,ă,ą,ḁ,α,ά',
                'ae'    =>      'æ',
                'b'             =>      'β,б',
                'c'             =>      'ç,ć,ĉ,ċ,č,ч,ћ,ц',
                'ch'    =>      'ч',
                'd'             =>      'ď,đ,Ð,д,ђ,δ,ð',
                'dz'    =>      'џ',
                'e'             =>      'è,é,ê,ë,ē,ĕ,ė,ę,ě,э,ε,έ',
                'f'             =>      'ƒ,ф',
                'g'             =>      'ğ,ĝ,ğ,ġ,ģ,г,γ',
                'h'             =>      'ĥ,ħ,Ħ,х',
                'i'             =>      'ì,í,î,ï,ı,ĩ,ī,ĭ,į,и,й,ъ,ы,ь,η,ή',
                'ij'    =>      'ij',
                'j'             =>      'ĵ',
                'ja'    =>      'я',
                'ju'    =>      'яю',
                'k'             =>      'ķ,ĸ,κ',
                'l'             =>      'ĺ,ļ,ľ,ŀ,ł,л,λ',
                'lj'    =>      'љ',
                'm'             =>      'μ',
                'n'             =>      'ñ,ņ,ň,ʼn,ŋ,н,ν',
                'nj'    =>      'њ',
                'o'             =>      'ò,ó,ô,õ,ø,ō,ŏ,ő,ο,ό,ω,ώ',
                'oe'    =>      'œ,ö',
                'p'             =>      'п,π',
                'ph'    =>      'φ',
                'ps'    =>      'ψ',
                'r'             =>      'ŕ,ŗ,ř,р,ρ,σ,ς',
                's'             =>      'ş,ś,ŝ,ş,š,с',
                'ss'    =>      'ß,ſ',
                'sh'    =>      'ш',
                'shch'  =>      'щ',
                't'             =>      'ţ,ť,ŧ,τ',
                'th'    =>      'θ',
                'u'             =>      'ù,ú,û,ü,ũ,ū,ŭ,ů,ű,ų,у',
                'v'             =>      'в',
                'w'             =>      'ŵ',
                'x'             =>      'χ,ξ',
                'y'             =>      'ý,þ,ÿ,ŷ',
                'z'             =>      'ź,ż,ž,з,ж,ζ'
                );
 
                foreach( $glyph_array as $letter => $glyphs ) {
                        $glyphs = explode( ',', $glyphs );
                        $str = str_replace( $glyphs, $letter, $str );
                }
 
                return $str;
        }


Here is an example of a function to add in the localise.php file in order to implement a custom calendar for fa-IR (Persian language)

/**
 * fa-IR Date class
 *
 * @package             Joomla.Site
 * @since               1.6
 */
jimport('joomla.utilities.date');
class fa_IRDate extends JDate {
        const DAY_NUMBER        = "\x027\x03";
        const DAY_NUMBER2       = "\x030\x03";
        const DAY_YEAR          = "\x032\x03";
        const MONTH_ABBR        = "\x033\x03";
        const MONTH_NAME        = "\x034\x03";
        const MONTH_NUMBER      = "\x035\x03";
        const MONTH_NUMBER2     = "\x036\x03";
        const MONTH_LENGTH      = "\x037\x03";
        const YEAR_ABBR         = "\x040\x03";
        const YEAR_NAME         = "\x041\x03";
        const AM_LOWER          = "\x042\x03";
        const AM_UPPER          = "\x043\x03";
        const PERSIAN_EPOCH     = 1948320.5;
 
        protected static $month_names   = array("فروردين","ارديبهشت","خرداد","تیر","مرداد","شهریور","مهر","آبان","آذر","دی","بهمن","اسفند");
 
 
        /**
         * Gets the date as a formatted string.
         *
         * @param       string  The date format specification string (see {@link PHP_MANUAL#date})
         * @param       boolean True to return the date string in the local time zone, false to return it in GMT.
         * @return      string  The date string in the french republican calendar (see @link{http://en.wikipedia.org/wiki/French_Republican_Calendar}).
         * @since       1.6
         */
        public function calendar($format, $local = false)
        {
                // Do string replacements for date format options that can be translated.
                $format = preg_replace('/(^|[^\\\])d/', "\\1".self::DAY_NUMBER2, $format);
                $format = preg_replace('/(^|[^\\\])j/', "\\1".self::DAY_NUMBER, $format);
                $format = preg_replace('/(^|[^\\\])z/', "\\1".self::DAY_YEAR, $format);
                $format = preg_replace('/(^|[^\\\])M/', "\\1".self::MONTH_ABBR, $format);
                $format = preg_replace('/(^|[^\\\])F/', "\\1".self::MONTH_NAME, $format);
                $format = preg_replace('/(^|[^\\\])n/', "\\1".self::MONTH_NUMBER, $format);
                $format = preg_replace('/(^|[^\\\])m/', "\\1".self::MONTH_NUMBER2, $format);
                $format = preg_replace('/(^|[^\\\])t/', "\\1".self::MONTH_LENGTH, $format);
                $format = preg_replace('/(^|[^\\\])y/', "\\1".self::YEAR_ABBR, $format);
                $format = preg_replace('/(^|[^\\\])Y/', "\\1".self::YEAR_NAME, $format);
                $format = preg_replace('/(^|[^\\\])a/', "\\1".self::AM_LOWER, $format);
                $format = preg_replace('/(^|[^\\\])A/', "\\1".self::AM_UPPER, $format);
 
                // Format the date.
                $return = parent::calendar($format, $local);
 
                $jd = gregoriantojd($this->month, $this->day, $this->year);
                $jalaliDate = self::jd_to_persian($jd);
                $m = $jalaliDate['mon'];
                $d = $jalaliDate['day'];
                $y = $jalaliDate['year'];
 
                // Manually modify the strings in the formated time.
                if (strpos($return, self::DAY_NUMBER) !== false) {
                        $return = str_replace(self::DAY_NUMBER, $d , $return);
                }
                if (strpos($return, self::DAY_NUMBER2) !== false) {
                        $return = str_replace(self::DAY_NUMBER2, sprintf("%02d",$d), $return);
                }
                if (strpos($return, self::DAY_YEAR) !== false) {
                        $return = str_replace(self::DAY_YEAR, $jd - self::persian_to_jd(1,1,$y)+1, $return);
                }
                if (strpos($return, self::MONTH_ABBR) !== false) {
                        $return = str_replace(self::MONTH_ABBR, self::$month_names[$m-1] , $return);
                }
                if (strpos($return, self::MONTH_NAME) !== false) {
                        $return = str_replace(self::MONTH_NAME, self::$month_names[$m-1] , $return);
                }
                if (strpos($return, self::MONTH_NUMBER) !== false) {
                        $return = str_replace(self::MONTH_NUMBER, $m , $return);
                }
                if (strpos($return, self::MONTH_NUMBER2) !== false) {
                        $return = str_replace(self::MONTH_NUMBER2, sprintf("%02d", $m) , $return);
                }
                if (strpos($return, self::MONTH_LENGTH) !== false) {
                        $return = str_replace(self::MONTH_LENGTH, $m < 7 ? 31 : $m < 12 ? 30 : self::leap_persian($y) ? 30 : 29 , $return);
                }
                if (strpos($return, self::YEAR_ABBR) !== false) {
                        $return = str_replace(self::YEAR_ABBR, sprintf("%02d",$y % 100), $return);
                }
                if (strpos($return, self::YEAR_NAME) !== false) {
                        $return = str_replace(self::YEAR_NAME, $y, $return);
                }
                if (strpos($return, self::AM_LOWER) !== false) {
                        $return = str_replace(self::AM_LOWER, $this->format('a',$local)=='pm' ? 'ب ظ' : 'ق ظ', $return);
                }
                if (strpos($return, self::AM_UPPER) !== false) {
                        $return = str_replace(self::AM_UPPER, $this->format('a',$local)=='pm' ? 'ب ظ' : 'ق ظ', $return);
                }
 
                return $return;
        }
        public static function jd_to_persian($jd)
 
        {
 
                //var $year, $month, $day, $depoch, $cycle, $cyear, $ycycle,
 
                //    $aux1, $aux2, $yday;
 
 
 
                $jd = floor($jd) + 0.5;
 
 
 
                $depoch = $jd - self::persian_to_jd(1, 1, 475);
 
                $cycle = floor($depoch / 1029983);
 
                $cyear = $depoch % 1029983;
 
                if ($cyear == 1029982) {
 
                    $ycycle = 2820;
 
                } else {
 
                    $aux1 = floor($cyear / 366);
 
                    $aux2 = $cyear % 366;
 
                    $ycycle = floor(((2134 * $aux1) + (2816 * $aux2) + 2815) / 1028522) +
 
                                $aux1 + 1;
 
                }
 
                $year = $ycycle + (2820 * $cycle) + 474;
 
                if ($year <= 0) {
 
                    $year--;
 
                }
 
                $yday = ($jd - self::persian_to_jd(1, 1, $year)) + 1;
 
                $month = ($yday <= 186) ? ceil($yday / 31) : ceil(($yday - 6) / 30);
 
                $day = ($jd - self::persian_to_jd($month, 1, $year)) + 1;
 
                return array('year'=>$year, 'mon'=>$month,'day'=> $day);
 
        }
        public static function persian_to_jd($month, $day, $year)
 
        {
 
                //var $epbase, $epyear;
 
                $epbase = $year - (($year >= 0) ? 474 : 473);
 
                $epyear = 474 + $epbase % 2820;
 
 
 
                return $day +
 
                        (($month <= 7) ?
 
                            (($month - 1) * 31) :
 
                            ((($month - 1) * 30) + 6)
 
                        ) +
 
                        floor((($epyear * 682) - 110) / 2816) +
 
                        ($epyear - 1) * 365 +
 
                        floor($epbase / 2820) * 1029983 +
 
                        self::PERSIAN_EPOCH;
 
        }
 
        public static function leap_persian($year) {
 
            return (((((($year - (($year > 0) ? 474 : 473)) % 2820) + 474) + 38) * 682) % 2816) < 682;
 
        }
 
}

One has also to add the right calendar.js files in the package.