addTranslation() to add module translations // Make the locale and translation adapter available to the rest of the Zend Framework Zend_Registry::set('Zend_Locale', $locale); Zend_Registry::set('Zend_Translate', $translate); // Extract language settings from the translation file global $DATE_FORMAT; // I18N: This is the format string for full dates. See http://php.net/date for codes $DATE_FORMAT=self::noop('%j %F %Y'); global $TIME_FORMAT; // I18N: This a the format string for the time-of-day. See http://php.net/date for codes $TIME_FORMAT=self::noop('%g:%i:%s%a'); global $ALPHABET_upper; // Alphabetic sorting sequence (upper-case letters), used by webtrees to sort strings $ALPHABET_upper=self::noop('ALPHABET_upper=ABCDEFGHIJKLMNOPQRSTUVWXYZ'); list(, $ALPHABET_upper)=explode('=', $ALPHABET_upper); global $ALPHABET_lower; // Alphabetic sorting sequence (lower-case letters), used by webtrees to sort strings $ALPHABET_lower=self::noop('ALPHABET_lower=abcdefghijklmnopqrstuvwxyz'); list(, $ALPHABET_lower)=explode('=', $ALPHABET_lower); global $WEEK_START; // I18N: This is the first day of the week on calendars. 0=Sunday, 1=Monday... $WEEK_START=self::noop('WEEK_START=0'); list(, $WEEK_START)=explode('=', $WEEK_START); global $DICTIONARY_SORT; // I18N: 1=>ignore diacrics when sorting, 0=>letters with diacritics are distinct $DICTIONARY_SORT=self::noop('DICTIONARY_SORT=1'); list(, $DICTIONARY_SORT)=explode('=', $DICTIONARY_SORT); global $TEXT_DIRECTION; $localeData=Zend_Locale_Data::getList($locale, 'layout'); $TEXT_DIRECTION=$localeData['characters']=='right-to-left' ? 'rtl' : 'ltr'; self::$locale=$locale; self::$dir=$TEXT_DIRECTION; // I18N: This is a space separated list of initial letters for lists of names, etc. Multi-letter characters are OK, e.g. "A B C CS D DZ DZS E F G GY H ..." You may use upper/lowers case, such as "D Dz Dzs". self::$alphabet=i18n::translate('A B C D E F G H I J K L M N O P Q R S T U V W X Y Z'); // I18N: This is the name of the MySQL collation that applies to your language. A list is available at http://dev.mysql.com/doc/refman/5.0/en/charset-unicode-sets.html self::$collation=i18n::translate('utf8_unicode_ci'); // I18N: This is the puncutation symbol used to separate the first items in a list. e.g. the in "red, green, yellow and blue" self::$list_separator=i18n::noop('LANGUAGE_LIST_SEPARATOR'); // I18N: This is the puncutation symbol used to separate the final items in a list. e.g. the and in "red, green, yellow and blue" self::$list_separator_last=i18n::noop('LANGUAGE_LIST_SEPARATOR_LAST'); return $locale; } // Check which languages are installed static public function installed_languages() { if (isset($_SESSION['installed_languages'])) { return $_SESSION['installed_languages']; } else { $_SESSION['installed_languages']=array(); $d=opendir(WT_ROOT.'language'); while (($f=readdir($d))!==false) { if (preg_match('/^([a-z][a-z][a-z]?(@[a-z]+|_[A-Z][A-Z])?)\.mo$/', $f, $match)) { // TODO: gettext() and ZF use different standards for locale names :-( if ($match[1]=='sr@latin' || $match[1]=='zh_CN') { // TODO: continue; } $_SESSION['installed_languages'][$match[1]]=Zend_Locale::getTranslation($match[1], 'language', $match[1]); } } closedir($d); if (empty($_SESSION['installed_languages'])) { die('There are no lanuages installed. You must include at least one xx.mo file in /language/'); } uasort($_SESSION['installed_languages'], 'utf8_strcasecmp'); return $_SESSION['installed_languages']; } } // Generate i18n markup for the tag, e.g lang="ar" dir="RTL" static public function html_markup() { $localeData=Zend_Locale_Data::getList(self::$locale, 'layout'); $dir=$localeData['characters']=='right-to-left' ? 'RTL' : 'LTR'; list($lang)=explode('_', self::$locale); return 'lang="'.$lang.'" xml:lang="'.$lang.'" dir="'.$dir.'"'; } // echo i18n::translate('Hello World!'); // echo i18n::translate('The %s sat on the mat', 'cat'); static public function translate(/* var_args */) { $args=func_get_args(); $args[0]=Zend_Registry::get('Zend_Translate')->_($args[0]); foreach ($args as &$arg) { if (is_array($arg)) { $arg=i18n::make_list($arg); } } // TODO: for each embedded string, if the text-direction is the opposite of the // page language, then wrap it in <r; on LTR pages and &rtl; on RTL pages. // This will ensure that non/weakly direction characters in the main string // are displayed correctly by the browser's BIDI algorithm. return call_user_func_array('sprintf', $args); } // Context sensitive version of translate. // echo i18n::translate_c('NOMINATIVE', 'January'); // echo i18n::translate_c('GENITIVE', 'January'); static public function translate_c(/* var_args */) { $args=func_get_args(); $msgid=$args[0]."\x04".$args[1]; $msgtxt=Zend_Registry::get('Zend_Translate')->_($msgid); if ($msgtxt==$msgid) { $msgtxt=$args[1]; } $args[0]=$msgtxt; unset ($args[1]); foreach ($args as &$arg) { if (is_array($arg)) { $arg=i18n::make_list($arg); } } // TODO: for each embedded string, if the text-direction is the opposite of the // page language, then wrap it in <r; on LTR pages and &rtl; on RTL pages. // This will ensure that non/weakly direction characters in the main string // are displayed correctly by the browser's BIDI algorithm. return call_user_func_array('sprintf', $args); } // Similar to translate, but do perform "no operation" on it. // This is necessary to fetch a format string (containing % characters) without // performing sustitution of arguments. static public function noop($string) { return Zend_Registry::get('Zend_Translate')->_($string); } // echo i18n::plural('There is an error', 'There are errors', $num_errors); // echo i18n::plural('There is one error', 'There are %d errors', $num_errors); // echo i18n::plural('There is %$1d %$2s cat', 'There are %$1d %$2s cats', $num, $num, $colour); static public function plural(/* var_args */) { $args=func_get_args(); $string=Zend_Registry::get('Zend_Translate')->plural($args[0], $args[1], $args[2]); array_splice($args, 0, 3, array($string)); return call_user_func_array('sprintf', $args); } // Convert an array to a list. For example // array("red", "green", "yellow", "blue") => "red, green, yellow and blue" static public function make_list($array) { // TODO: for each array element, if the text-direction is the opposite of the // page language, then wrap it in <r; on LTR pages and &rtl; on RTL pages. // This will ensure that non/weakly direction characters in the main string // are displayed correctly by the browser's BIDI algorithm. $n=count($array); switch ($n) { case 0: return ''; case 1: return $array(0); default: return implode(self::$list_separator, array_slice($array, 0, $n-1)).self::$list_separator_last.$array[$n-1]; } } // Convert a GEDCOM age string into translated_text // NB: The import function will have normalised this, so we don't need // to worry about badly formatted strings static public function gedcom_age($string) { switch ($string) { case 'STILLBORN': // I18N: Description of someone's age at an event. e.g Died 14 Jan 1900 (stillborn) return i18n::translate('(stillborn)'); case 'INFANT': // I18N: Description of someone's age at an event. e.g Died 14 Jan 1900 (in infancy) return i18n::translate('(in infancy)'); case 'CHILD': // I18N: Description of someone's age at an event. e.g Died 14 Jan 1900 (in childhood) return i18n::translate('(in childhood)'); } $age=array(); if (preg_match('/(\d+)y/', $string, $match)) { // I18N: Part of an age string. e.g 5 years, 4 months and 3 days $years=$match[1]; $age[]=i18n::plural('%d year', '%d years', $years, $years); } else { $years=-1; } if (preg_match('/(\d+)m/', $string, $match)) { // I18N: Part of an age string. e.g 5 years, 4 months and 3 days $age[]=i18n::plural('%d month', '%d months', $match[1], $match[1]); } if (preg_match('/(\d+)w/', $string, $match)) { // I18N: Part of an age string. e.g 7 weeks and 3 days $age[]=i18n::plural('%d week', '%d weeks', $match[1], $match[1]); } if (preg_match('/(\d+)d/', $string, $match)) { // I18N: Part of an age string. e.g 5 years, 4 months and 3 days $age[]=i18n::plural('%d day', '%d days', $match[1], $match[1]); } // If an age is just a number of years, only show the number if (count($age)==1 && $years>=0) { $age=$years; } if ($age) { if (!substr_compare($string, '<', 0, 1)) { // I18N: Description of someone's age at an event. e.g Died 14 Jan 1900 (aged less than 21 years) return i18n::translate('(aged less than %s)', $age); } elseif (!substr_compare($string, '>', 0, 1)) { // I18N: Description of someone's age at an event. e.g Died 14 Jan 1900 (aged more than 21 years) return i18n::translate('(aged more than %s)', $age); } else { // I18N: Description of someone's age at an event. e.g Died 14 Jan 1900 (aged 43 years) return i18n::translate('(aged %s)', $age); } } else { // Not a valid string? return i18n::translate('(aged %s)', $string); } } // 5=>fifth, 9=>ninth, etc. Used for Nth cousins, etc. static function ordinal_word($n) { switch ($n) { case 1: return i18n::translate('first'); case 2: return i18n::translate('second'); case 3: return i18n::translate('third'); case 4: return i18n::translate('fourth'); case 5: return i18n::translate('fifth'); case 6: return i18n::translate('sixth'); case 7: return i18n::translate('seventh'); case 8: return i18n::translate('eighth'); case 9: return i18n::translate('ninth'); case 10: return i18n::translate('tenth'); case 11: return i18n::translate('eleventh'); case 12: return i18n::translate('twelfth'); case 13: return i18n::translate('thirteenth'); case 14: return i18n::translate('fourteenth'); case 15: return i18n::translate('fifteenth'); default: return /* I18N: Generalisation of first, second, third, ... */i18n::translate('%d x', $n); } } // Convert a number of seconds into a relative time. e.g. 630 => "10 hours, 30 minutes ago" static function time_ago($seconds) { $year=365*24*60*60; $month=30*24*60*60; $day=24*60*60; $hour=60*60; $minute=60; // TODO: Display two units (years+months), (months+days), etc. // This requires "contexts". i.e. "%d months" has a different translation // in different contexts. // We must AVOID combining phrases to make sentences. if ($seconds>$year) { $years=floor($seconds/$year); return i18n::plural('%d year ago', '%d years ago', $years, $years); } elseif ($seconds>$month) { $months=floor($seconds/$month); return i18n::plural('%d month ago', '%d months ago', $months, $months); } elseif ($seconds>$day) { $days=floor($seconds/$day); return i18n::plural('%d day ago', '%d days ago', $days, $days); } elseif ($seconds>$hour) { $hours=floor($seconds/$hour); return i18n::plural('%d hour ago', '%d hours ago', $hours, $hours); } elseif ($seconds>$minute) { $minutes=floor($seconds/$minute); return i18n::plural('%d minute ago', '%d minutes ago', $minutes, $minutes); } else { return i18n::plural('%d second ago', '%d seconds ago', $seconds, $seconds); } } }