initial work on translations, covering the PHP side of it
This commit is contained in:
@@ -1162,4 +1162,9 @@ class RainTpl_SyntaxException extends RainTpl_Exception{
|
||||
}
|
||||
}
|
||||
|
||||
// shorthand translate function for use in templates
|
||||
function t() {
|
||||
return call_user_func_array(array('i18n', 'translate'), func_get_args());
|
||||
}
|
||||
|
||||
// -- end
|
||||
|
||||
219
lib/i18n.php
Normal file
219
lib/i18n.php
Normal file
@@ -0,0 +1,219 @@
|
||||
<?php
|
||||
/**
|
||||
* ZeroBin
|
||||
*
|
||||
* a zero-knowledge paste bin
|
||||
*
|
||||
* @link http://sebsauvage.net/wiki/doku.php?id=php:zerobin
|
||||
* @copyright 2012 Sébastien SAUVAGE (sebsauvage.net)
|
||||
* @license http://www.opensource.org/licenses/zlib-license.php The zlib/libpng License
|
||||
* @version 0.20
|
||||
*/
|
||||
|
||||
/**
|
||||
* i18n
|
||||
*
|
||||
* provides internationalization tools like translation, browser language detection, etc.
|
||||
*/
|
||||
class i18n
|
||||
{
|
||||
/**
|
||||
* translation cache
|
||||
*
|
||||
* @access private
|
||||
* @static
|
||||
* @var array
|
||||
*/
|
||||
private static $_translations = array();
|
||||
|
||||
/**
|
||||
* translate a string, alias for translate()
|
||||
*
|
||||
* @access public
|
||||
* @static
|
||||
* @param string $messageId
|
||||
* @param mixed $args one or multiple parameters injected into placeholders
|
||||
* @return string
|
||||
*/
|
||||
public static function _($messageId)
|
||||
{
|
||||
return call_user_func_array(array('i18n', 'translate'), func_get_args());
|
||||
}
|
||||
|
||||
/**
|
||||
* translate a string
|
||||
*
|
||||
* @access public
|
||||
* @static
|
||||
* @param string $messageId
|
||||
* @param mixed $args one or multiple parameters injected into placeholders
|
||||
* @return string
|
||||
*/
|
||||
public static function translate($messageId)
|
||||
{
|
||||
if (empty($messageId))
|
||||
{
|
||||
return $messageId;
|
||||
}
|
||||
if (count(self::$_translations) === 0) {
|
||||
self::loadTranslations();
|
||||
}
|
||||
if (!array_key_exists($messageId, self::$_translations))
|
||||
{
|
||||
self::$_translations[$messageId] = $messageId;
|
||||
}
|
||||
$args = func_get_args();
|
||||
$args[0] = self::$_translations[$messageId];
|
||||
return call_user_func_array('sprintf', $args);
|
||||
}
|
||||
|
||||
/**
|
||||
* loads translations
|
||||
*
|
||||
* From: http://stackoverflow.com/questions/3770513/detect-browser-language-in-php#3771447
|
||||
*
|
||||
* @access protected
|
||||
* @static
|
||||
* @return void
|
||||
*/
|
||||
public static function loadTranslations()
|
||||
{
|
||||
// find a matching translation file
|
||||
$availableLanguages = array();
|
||||
$path = PUBLIC_PATH . DIRECTORY_SEPARATOR . 'i18n';
|
||||
$i18n = dir($path);
|
||||
while (false !== ($file = $i18n->read()))
|
||||
{
|
||||
if (preg_match('/^([a-z]{2}).json$/', $file, $match) === 1)
|
||||
{
|
||||
$availableLanguages[] = $match[1];
|
||||
}
|
||||
}
|
||||
|
||||
$match = self::_getMatchingLanguage(
|
||||
self::getBrowserLanguages(), $availableLanguages
|
||||
);
|
||||
// load translations
|
||||
if ($match != 'en')
|
||||
{
|
||||
self::$_translations = json_decode(
|
||||
file_get_contents($path . DIRECTORY_SEPARATOR . $match . '.json'),
|
||||
true
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* detect the clients supported languages and return them ordered by preference
|
||||
*
|
||||
* From: http://stackoverflow.com/questions/3770513/detect-browser-language-in-php#3771447
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function getBrowserLanguages()
|
||||
{
|
||||
$languages = array();
|
||||
if (array_key_exists('HTTP_ACCEPT_LANGUAGE', $_SERVER))
|
||||
{
|
||||
$languageRanges = explode(',', trim($_SERVER['HTTP_ACCEPT_LANGUAGE']));
|
||||
foreach ($languageRanges as $languageRange) {
|
||||
if (preg_match(
|
||||
'/(\*|[a-zA-Z0-9]{1,8}(?:-[a-zA-Z0-9]{1,8})*)(?:\s*;\s*q\s*=\s*(0(?:\.\d{0,3})|1(?:\.0{0,3})))?/',
|
||||
trim($languageRange), $match
|
||||
))
|
||||
{
|
||||
if (!isset($match[2]))
|
||||
{
|
||||
$match[2] = '1.0';
|
||||
}
|
||||
else
|
||||
{
|
||||
$match[2] = (string) floatval($match[2]);
|
||||
}
|
||||
if (!isset($languages[$match[2]]))
|
||||
{
|
||||
$languages[$match[2]] = array();
|
||||
}
|
||||
$languages[$match[2]][] = strtolower($match[1]);
|
||||
}
|
||||
}
|
||||
krsort($languages);
|
||||
}
|
||||
return $languages;
|
||||
}
|
||||
|
||||
/**
|
||||
* compares two language preference arrays and returns the preferred match
|
||||
*
|
||||
* From: http://stackoverflow.com/questions/3770513/detect-browser-language-in-php#3771447
|
||||
*
|
||||
* @param array $acceptedLanguages
|
||||
* @param array $availableLanguages
|
||||
* @return string
|
||||
*/
|
||||
protected static function _getMatchingLanguage($acceptedLanguages, $availableLanguages) {
|
||||
$matches = array();
|
||||
$any = false;
|
||||
foreach ($acceptedLanguages as $acceptedQuality => $acceptedValues) {
|
||||
$acceptedQuality = floatval($acceptedQuality);
|
||||
if ($acceptedQuality === 0.0) continue;
|
||||
foreach ($availableLanguages as $availableValue)
|
||||
{
|
||||
$availableQuality = 1.0;
|
||||
foreach ($acceptedValues as $acceptedValue)
|
||||
{
|
||||
if ($acceptedValue === '*')
|
||||
{
|
||||
$any = true;
|
||||
}
|
||||
$matchingGrade = self::_matchLanguage($acceptedValue, $availableValue);
|
||||
if ($matchingGrade > 0)
|
||||
{
|
||||
$q = (string) ($acceptedQuality * $availableQuality * $matchingGrade);
|
||||
if (!isset($matches[$q]))
|
||||
{
|
||||
$matches[$q] = array();
|
||||
}
|
||||
if (!in_array($availableValue, $matches[$q]))
|
||||
{
|
||||
$matches[$q][] = $availableValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (count($matches) === 0 && $any)
|
||||
{
|
||||
if (count($availableLanguages) > 0)
|
||||
{
|
||||
$matches['1.0'] = $availableLanguages;
|
||||
}
|
||||
}
|
||||
if (count($matches) === 0)
|
||||
{
|
||||
return 'en';
|
||||
}
|
||||
krsort($matches);
|
||||
$topmatches = current($matches);
|
||||
return current($topmatches);
|
||||
}
|
||||
|
||||
/**
|
||||
* compare two language IDs and return the degree they match
|
||||
*
|
||||
* From: http://stackoverflow.com/questions/3770513/detect-browser-language-in-php#3771447
|
||||
*
|
||||
* @param string $a
|
||||
* @param string $b
|
||||
* @return float
|
||||
*/
|
||||
protected static function _matchLanguage($a, $b) {
|
||||
$a = explode('-', $a);
|
||||
$b = explode('-', $b);
|
||||
for ($i=0, $n=min(count($a), count($b)); $i<$n; $i++)
|
||||
{
|
||||
if ($a[$i] !== $b[$i]) break;
|
||||
}
|
||||
return $i === 0 ? 0 : (float) $i / count($a);
|
||||
}
|
||||
}
|
||||
@@ -93,7 +93,7 @@ class zerobin
|
||||
{
|
||||
if (version_compare(PHP_VERSION, '5.2.6') < 0)
|
||||
{
|
||||
throw new Exception('ZeroBin requires php 5.2.6 or above to work. Sorry.', 1);
|
||||
throw new Exception(i18n::_('ZeroBin requires php 5.2.6 or above to work. Sorry.'), 1);
|
||||
}
|
||||
|
||||
// in case stupid admin has left magic_quotes enabled in php.ini
|
||||
@@ -156,7 +156,7 @@ class zerobin
|
||||
$this->_conf = parse_ini_file(PATH . 'cfg' . DIRECTORY_SEPARATOR . 'conf.ini', true);
|
||||
foreach (array('main', 'model') as $section) {
|
||||
if (!array_key_exists($section, $this->_conf)) {
|
||||
throw new Exception("ZeroBin requires configuration section [$section] to be present in configuration file.", 2);
|
||||
throw new Exception(i18n::_('ZeroBin requires configuration section [%s] to be present in configuration file.', $section), 2);
|
||||
}
|
||||
}
|
||||
$this->_model = $this->_conf['model']['class'];
|
||||
@@ -184,12 +184,12 @@ class zerobin
|
||||
* Store new paste or comment
|
||||
*
|
||||
* POST contains:
|
||||
* data (mandatory) = json encoded SJCL encrypted text (containing keys: iv,salt,ct)
|
||||
* data (mandatory) = json encoded SJCL encrypted text (containing keys: iv,v,iter,ks,ts,mode,adata,cipher,salt,ct)
|
||||
*
|
||||
* All optional data will go to meta information:
|
||||
* expire (optional) = expiration delay (never,5min,10min,1hour,1day,1week,1month,1year,burn) (default:never)
|
||||
* opendiscusssion (optional) = is the discussion allowed on this paste ? (0/1) (default:0)
|
||||
* nickname (optional) = in discussion, encoded SJCL encrypted text nickname of author of comment (containing keys: iv,salt,ct)
|
||||
* nickname (optional) = in discussion, encoded SJCL encrypted text nickname of author of comment (containing keys: iv,v,iter,ks,ts,mode,adata,cipher,salt,ct)
|
||||
* parentid (optional) = in discussion, which comment this comment replies to.
|
||||
* pasteid (optional) = in discussion, which paste this comment belongs to.
|
||||
*
|
||||
@@ -208,9 +208,10 @@ class zerobin
|
||||
{
|
||||
$this->_return_message(
|
||||
1,
|
||||
'Please wait ' .
|
||||
$this->_conf['traffic']['limit'] .
|
||||
' seconds between each post.'
|
||||
i18n::_(
|
||||
'Please wait %d seconds between each post.',
|
||||
$this->_conf['traffic']['limit']
|
||||
)
|
||||
);
|
||||
return;
|
||||
}
|
||||
@@ -221,9 +222,10 @@ class zerobin
|
||||
{
|
||||
$this->_return_message(
|
||||
1,
|
||||
'Paste is limited to ' .
|
||||
filter::size_humanreadable($sizelimit) .
|
||||
' of encrypted data.'
|
||||
i18n::_(
|
||||
'Paste is limited to %s of encrypted data.',
|
||||
filter::size_humanreadable($sizelimit)
|
||||
)
|
||||
);
|
||||
return;
|
||||
}
|
||||
@@ -232,15 +234,18 @@ class zerobin
|
||||
if (!sjcl::isValid($data)) return $this->_return_message(1, 'Invalid data.');
|
||||
|
||||
// Read additional meta-information.
|
||||
$meta=array();
|
||||
$meta = array();
|
||||
|
||||
// Read expiration date
|
||||
if (!empty($_POST['expire']))
|
||||
{
|
||||
$selected_expire = (string) $_POST['expire'];
|
||||
if (array_key_exists($selected_expire, $this->_conf['expire_options'])) {
|
||||
if (array_key_exists($selected_expire, $this->_conf['expire_options']))
|
||||
{
|
||||
$expire = $this->_conf['expire_options'][$selected_expire];
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
$expire = $this->_conf['expire_options'][$this->_conf['expire']['default']];
|
||||
}
|
||||
if ($expire > 0) $meta['expire_date'] = time() + $expire;
|
||||
@@ -575,23 +580,25 @@ class zerobin
|
||||
// label all the expiration options
|
||||
$expire = array();
|
||||
foreach ($this->_conf['expire_options'] as $key => $value) {
|
||||
$expire[$key] = array_key_exists($key, $this->_conf['expire_labels']) ?
|
||||
$expire[$key] = i18n::_(
|
||||
array_key_exists($key, $this->_conf['expire_labels']) ?
|
||||
$this->_conf['expire_labels'][$key] :
|
||||
$key;
|
||||
$key
|
||||
);
|
||||
}
|
||||
|
||||
$page = new RainTPL;
|
||||
$page::$path_replace = false;
|
||||
// we escape it here because ENT_NOQUOTES can't be used in RainTPL templates
|
||||
$page->assign('CIPHERDATA', htmlspecialchars($this->_data, ENT_NOQUOTES));
|
||||
$page->assign('ERROR', $this->_error);
|
||||
$page->assign('STATUS', $this->_status);
|
||||
$page->assign('ERROR', i18n::_($this->_error));
|
||||
$page->assign('STATUS', i18n::_($this->_status));
|
||||
$page->assign('VERSION', self::VERSION);
|
||||
$page->assign('DISCUSSION', $this->_getMainConfig('discussion', true));
|
||||
$page->assign('OPENDISCUSSION', $this->_getMainConfig('opendiscussion', true));
|
||||
$page->assign('SYNTAXHIGHLIGHTING', $this->_getMainConfig('syntaxhighlighting', true));
|
||||
$page->assign('SYNTAXHIGHLIGHTINGTHEME', $this->_getMainConfig('syntaxhighlightingtheme', ''));
|
||||
$page->assign('NOTICE', $this->_getMainConfig('notice', ''));
|
||||
$page->assign('NOTICE', i18n::_($this->_getMainConfig('notice', '')));
|
||||
$page->assign('BURNAFTERREADINGSELECTED', $this->_getMainConfig('burnafterreadingselected', false));
|
||||
$page->assign('PASSWORD', $this->_getMainConfig('password', true));
|
||||
$page->assign('BASE64JSVERSION', $this->_getMainConfig('base64version', '2.1.9'));
|
||||
@@ -629,7 +636,7 @@ class zerobin
|
||||
$result = array('status' => $status);
|
||||
if ($status)
|
||||
{
|
||||
$result['message'] = $message;
|
||||
$result['message'] = i18n::_($message);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user