diff options
Diffstat (limited to 'lib/phptal/PHPTAL/Php/Attribute/I18N')
-rw-r--r-- | lib/phptal/PHPTAL/Php/Attribute/I18N/Attributes.php | 118 | ||||
-rw-r--r-- | lib/phptal/PHPTAL/Php/Attribute/I18N/Data.php | 36 | ||||
-rw-r--r-- | lib/phptal/PHPTAL/Php/Attribute/I18N/Domain.php | 50 | ||||
-rw-r--r-- | lib/phptal/PHPTAL/Php/Attribute/I18N/Name.php | 47 | ||||
-rw-r--r-- | lib/phptal/PHPTAL/Php/Attribute/I18N/Source.php | 48 | ||||
-rw-r--r-- | lib/phptal/PHPTAL/Php/Attribute/I18N/Target.php | 43 | ||||
-rw-r--r-- | lib/phptal/PHPTAL/Php/Attribute/I18N/Translate.php | 130 |
7 files changed, 472 insertions, 0 deletions
diff --git a/lib/phptal/PHPTAL/Php/Attribute/I18N/Attributes.php b/lib/phptal/PHPTAL/Php/Attribute/I18N/Attributes.php new file mode 100644 index 0000000..5eb3fac --- /dev/null +++ b/lib/phptal/PHPTAL/Php/Attribute/I18N/Attributes.php @@ -0,0 +1,118 @@ +<?php +/** + * PHPTAL templating engine + * + * PHP Version 5 + * + * @category HTML + * @package PHPTAL + * @author Laurent Bedubourg <lbedubourg@motion-twin.com> + * @author Kornel Lesiński <kornel@aardvarkmedia.co.uk> + * @license http://www.gnu.org/licenses/lgpl.html GNU Lesser General Public License + * @version SVN: $Id$ + * @link http://phptal.org/ + */ + +/** + * i18n:attributes + * + * This attribute will allow us to translate attributes of HTML tags, such + * as the alt attribute in the img tag. The i18n:attributes attribute + * specifies a list of attributes to be translated with optional message + * IDs? for each; if multiple attribute names are given, they must be + * separated by semi-colons. Message IDs? used in this context must not + * include whitespace. + * + * Note that the value of the particular attributes come either from the + * HTML attribute value itself or from the data inserted by tal:attributes. + * + * If an attibute is to be both computed using tal:attributes and translated, + * the translation service is passed the result of the TALES expression for + * that attribute. + * + * An example: + * + * <img src="http://foo.com/logo" alt="Visit us" + * tal:attributes="alt here/greeting" + * i18n:attributes="alt" + * /> + * + * + * In this example, let tal:attributes set the value of the alt attribute to + * the text "Stop by for a visit!". This text will be passed to the + * translation service, which uses the result of language negotiation to + * translate "Stop by for a visit!" into the requested language. The example + * text in the template, "Visit us", will simply be discarded. + * + * Another example, with explicit message IDs: + * + * <img src="../icons/uparrow.png" alt="Up" + * i18n:attributes="src up-arrow-icon; alt up-arrow-alttext" + * > + * + * Here, the message ID up-arrow-icon will be used to generate the link to + * an icon image file, and the message ID up-arrow-alttext will be used for + * the "alt" text. + * + * + * + * @package PHPTAL + * @subpackage Php.attribute.i18n + */ +class PHPTAL_Php_Attribute_I18N_Attributes extends PHPTAL_Php_Attribute +{ + public function before(PHPTAL_Php_CodeWriter $codewriter) + { + // split attributes to translate + foreach ($codewriter->splitExpression($this->expression) as $exp) { + list($qname, $key) = $this->parseSetExpression($exp); + + // if the translation key is specified and not empty (but may be '0') + if (strlen($key)) { + // we use it and replace the tag attribute with the result of the translation + $code = $this->_getTranslationCode($codewriter, $key); + } else { + $attr = $this->phpelement->getAttributeNode($qname); + if (!$attr) throw new PHPTAL_TemplateException("Unable to translate attribute $qname, because there is no translation key specified", + $this->phpelement->getSourceFile(), $this->phpelement->getSourceLine()); + + if ($attr->getReplacedState() === PHPTAL_Dom_Attr::NOT_REPLACED) { + $code = $this->_getTranslationCode($codewriter, $attr->getValue()); + } elseif ($attr->getReplacedState() === PHPTAL_Dom_Attr::VALUE_REPLACED && $attr->getOverwrittenVariableName()) { + // sadly variables won't be interpolated in this translation + $code = 'echo '.$codewriter->escapeCode($codewriter->getTranslatorReference(). '->translate('.$attr->getOverwrittenVariableName().', false)'); + } else { + throw new PHPTAL_TemplateException("Unable to translate attribute $qname, because other TAL attributes are using it", + $this->phpelement->getSourceFile(), $this->phpelement->getSourceLine()); + } + } + $this->phpelement->getOrCreateAttributeNode($qname)->overwriteValueWithCode($code); + } + } + + public function after(PHPTAL_Php_CodeWriter $codewriter) + { + } + + /** + * @param key - unescaped string (not PHP code) for the key + */ + private function _getTranslationCode(PHPTAL_Php_CodeWriter $codewriter, $key) + { + $code = ''; + if (preg_match_all('/\$\{(.*?)\}/', $key, $m)) { + array_shift($m); + $m = array_shift($m); + foreach ($m as $name) { + $code .= "\n".$codewriter->getTranslatorReference(). '->setVar('.$codewriter->str($name).','.PHPTAL_Php_TalesInternal::compileToPHPExpression($name).');'; // allow more complex TAL expressions + } + $code .= "\n"; + } + + // notice the false boolean which indicate that the html is escaped + // elsewhere looks like an hack doesn't it ? :) + $code .= 'echo '.$codewriter->escapeCode($codewriter->getTranslatorReference().'->translate('.$codewriter->str($key).', false)'); + return $code; + } +} + diff --git a/lib/phptal/PHPTAL/Php/Attribute/I18N/Data.php b/lib/phptal/PHPTAL/Php/Attribute/I18N/Data.php new file mode 100644 index 0000000..bad310f --- /dev/null +++ b/lib/phptal/PHPTAL/Php/Attribute/I18N/Data.php @@ -0,0 +1,36 @@ +<?php +/** + * PHPTAL templating engine + * + * PHP Version 5 + * + * @category HTML + * @package PHPTAL + * @author Laurent Bedubourg <lbedubourg@motion-twin.com> + * @author Kornel Lesiński <kornel@aardvarkmedia.co.uk> + * @license http://www.gnu.org/licenses/lgpl.html GNU Lesser General Public License + * @version SVN: $Id$ + * @link http://phptal.org/ + */ + +/** + * i18n:data + * + * Since TAL always returns strings, we need a way in ZPT to translate + * objects, the most obvious case being DateTime objects. The data attribute + * will allow us to specify such an object, and i18n:translate will provide + * us with a legal format string for that object. If data is used, + * i18n:translate must be used to give an explicit message ID, rather than + * relying on a message ID computed from the content. + * + * + * + * @package PHPTAL + * @subpackage Php.attribute.i18n + */ +class PHPTAL_Php_Attribute_I18N_Data extends PHPTAL_Php_Attribute +{ + public function before(PHPTAL_Php_CodeWriter $codewriter){} + public function after(PHPTAL_Php_CodeWriter $codewriter){} +} + diff --git a/lib/phptal/PHPTAL/Php/Attribute/I18N/Domain.php b/lib/phptal/PHPTAL/Php/Attribute/I18N/Domain.php new file mode 100644 index 0000000..92ece11 --- /dev/null +++ b/lib/phptal/PHPTAL/Php/Attribute/I18N/Domain.php @@ -0,0 +1,50 @@ +<?php +/** + * PHPTAL templating engine + * + * PHP Version 5 + * + * @category HTML + * @package PHPTAL + * @author Laurent Bedubourg <lbedubourg@motion-twin.com> + * @author Kornel Lesiński <kornel@aardvarkmedia.co.uk> + * @license http://www.gnu.org/licenses/lgpl.html GNU Lesser General Public License + * @version SVN: $Id$ + * @link http://phptal.org/ + */ + +/** + * i18n:domain + * + * The i18n:domain attribute is used to specify the domain to be used to get + * the translation. If not specified, the translation services will use a + * default domain. The value of the attribute is used directly; it is not + * a TALES expression. + * + * @package PHPTAL + * @subpackage Php.attribute.i18n + */ +class PHPTAL_Php_Attribute_I18N_Domain extends PHPTAL_Php_Attribute +{ + public function before(PHPTAL_Php_CodeWriter $codewriter) + { + // ensure a domain stack exists or create it + $codewriter->doIf('!isset($_i18n_domains)'); + $codewriter->pushCode('$_i18n_domains = array()'); + $codewriter->doEnd('if'); + + $expression = $codewriter->interpolateTalesVarsInString($this->expression); + + // push current domain and use new domain + $code = '$_i18n_domains[] = '.$codewriter->getTranslatorReference().'->useDomain('.$expression.')'; + $codewriter->pushCode($code); + } + + public function after(PHPTAL_Php_CodeWriter $codewriter) + { + // restore domain + $code = $codewriter->getTranslatorReference().'->useDomain(array_pop($_i18n_domains))'; + $codewriter->pushCode($code); + } +} + diff --git a/lib/phptal/PHPTAL/Php/Attribute/I18N/Name.php b/lib/phptal/PHPTAL/Php/Attribute/I18N/Name.php new file mode 100644 index 0000000..8a8f4e7 --- /dev/null +++ b/lib/phptal/PHPTAL/Php/Attribute/I18N/Name.php @@ -0,0 +1,47 @@ +<?php +/** + * PHPTAL templating engine + * + * PHP Version 5 + * + * @category HTML + * @package PHPTAL + * @author Laurent Bedubourg <lbedubourg@motion-twin.com> + * @author Kornel Lesiński <kornel@aardvarkmedia.co.uk> + * @license http://www.gnu.org/licenses/lgpl.html GNU Lesser General Public License + * @version SVN: $Id$ + * @link http://phptal.org/ + */ + +/** i18n:name + * + * Name the content of the current element for use in interpolation within + * translated content. This allows a replaceable component in content to be + * re-ordered by translation. For example: + * + * <span i18n:translate=''> + * <span tal:replace='here/name' i18n:name='name' /> was born in + * <span tal:replace='here/country_of_birth' i18n:name='country' />. + * </span> + * + * would cause this text to be passed to the translation service: + * + * "${name} was born in ${country}." + * + * + * @package PHPTAL + * @subpackage Php.attribute.i18n + */ +class PHPTAL_Php_Attribute_I18N_Name extends PHPTAL_Php_Attribute +{ + public function before(PHPTAL_Php_CodeWriter $codewriter) + { + $codewriter->pushCode('ob_start()'); + } + + public function after(PHPTAL_Php_CodeWriter $codewriter) + { + $codewriter->pushCode($codewriter->getTranslatorReference().'->setVar('.$codewriter->str($this->expression).', ob_get_clean())'); + } +} + diff --git a/lib/phptal/PHPTAL/Php/Attribute/I18N/Source.php b/lib/phptal/PHPTAL/Php/Attribute/I18N/Source.php new file mode 100644 index 0000000..9575fae --- /dev/null +++ b/lib/phptal/PHPTAL/Php/Attribute/I18N/Source.php @@ -0,0 +1,48 @@ +<?php +/** + * PHPTAL templating engine + * + * PHP Version 5 + * + * @category HTML + * @package PHPTAL + * @author Laurent Bedubourg <lbedubourg@motion-twin.com> + * @author Kornel Lesiński <kornel@aardvarkmedia.co.uk> + * @license http://www.gnu.org/licenses/lgpl.html GNU Lesser General Public License + * @version SVN: $Id$ + * @link http://phptal.org/ + */ + + +/** + * i18n:source + * + * The i18n:source attribute specifies the language of the text to be + * translated. The default is "nothing", which means we don't provide + * this information to the translation services. + * + * + * @package PHPTAL + * @subpackage Php.attribute.i18n + */ +class PHPTAL_Php_Attribute_I18N_Source extends PHPTAL_Php_Attribute +{ + public function before(PHPTAL_Php_CodeWriter $codewriter) + { + // ensure that a sources stack exists or create it + $codewriter->doIf('!isset($_i18n_sources)'); + $codewriter->pushCode('$_i18n_sources = array()'); + $codewriter->end(); + + // push current source and use new one + $codewriter->pushCode('$_i18n_sources[] = ' . $codewriter->getTranslatorReference(). '->setSource('.$codewriter->str($this->expression).')'); + } + + public function after(PHPTAL_Php_CodeWriter $codewriter) + { + // restore source + $code = $codewriter->getTranslatorReference().'->setSource(array_pop($_i18n_sources))'; + $codewriter->pushCode($code); + } +} + diff --git a/lib/phptal/PHPTAL/Php/Attribute/I18N/Target.php b/lib/phptal/PHPTAL/Php/Attribute/I18N/Target.php new file mode 100644 index 0000000..9cf2a67 --- /dev/null +++ b/lib/phptal/PHPTAL/Php/Attribute/I18N/Target.php @@ -0,0 +1,43 @@ +<?php +/** + * PHPTAL templating engine + * + * PHP Version 5 + * + * @category HTML + * @package PHPTAL + * @author Laurent Bedubourg <lbedubourg@motion-twin.com> + * @author Kornel Lesiński <kornel@aardvarkmedia.co.uk> + * @license http://www.gnu.org/licenses/lgpl.html GNU Lesser General Public License + * @version SVN: $Id$ + * @link http://phptal.org/ + */ + +/** + * i18n:target + * + * The i18n:target attribute specifies the language of the translation we + * want to get. If the value is "default", the language negotiation services + * will be used to choose the destination language. If the value is + * "nothing", no translation will be performed; this can be used to suppress + * translation within a larger translated unit. Any other value must be a + * language code. + * + * The attribute value is a TALES expression; the result of evaluating the + * expression is the language code or one of the reserved values. + * + * Note that i18n:target is primarily used for hints to text extraction + * tools and translation teams. If you had some text that should only be + * translated to e.g. German, then it probably shouldn't be wrapped in an + * i18n:translate span. + * + * + * @package PHPTAL + * @subpackage Php.attribute.i18n + */ +class PHPTAL_Php_Attribute_I18N_Target extends PHPTAL_Php_Attribute +{ + public function before(PHPTAL_Php_CodeWriter $codewriter){} + public function after(PHPTAL_Php_CodeWriter $codewriter){} +} + diff --git a/lib/phptal/PHPTAL/Php/Attribute/I18N/Translate.php b/lib/phptal/PHPTAL/Php/Attribute/I18N/Translate.php new file mode 100644 index 0000000..a0e26c2 --- /dev/null +++ b/lib/phptal/PHPTAL/Php/Attribute/I18N/Translate.php @@ -0,0 +1,130 @@ +<?php +/** + * PHPTAL templating engine + * + * PHP Version 5 + * + * @category HTML + * @package PHPTAL + * @author Laurent Bedubourg <lbedubourg@motion-twin.com> + * @author Kornel Lesiński <kornel@aardvarkmedia.co.uk> + * @license http://www.gnu.org/licenses/lgpl.html GNU Lesser General Public License + * @version SVN: $Id$ + * @link http://phptal.org/ + */ + +/** + * ZPTInternationalizationSupport + * + * i18n:translate + * + * This attribute is used to mark units of text for translation. If this + * attribute is specified with an empty string as the value, the message ID + * is computed from the content of the element bearing this attribute. + * Otherwise, the value of the element gives the message ID. + * + * + * @package PHPTAL + * @subpackage Php.attribute.i18n + */ +class PHPTAL_Php_Attribute_I18N_Translate extends PHPTAL_Php_Attribute_TAL_Content +{ + public function before(PHPTAL_Php_CodeWriter $codewriter) + { + $escape = true; + $this->_echoType = PHPTAL_Php_Attribute::ECHO_TEXT; + if (preg_match('/^(text|structure)(?:\s+(.*)|\s*$)/', $this->expression, $m)) { + if ($m[1]=='structure') { $escape=false; $this->_echoType = PHPTAL_Php_Attribute::ECHO_STRUCTURE; } + $this->expression = isset($m[2])?$m[2]:''; + } + + $this->_prepareNames($codewriter, $this->phpelement); + + // if no expression is given, the content of the node is used as + // a translation key + if (strlen(trim($this->expression)) == 0) { + $key = $this->_getTranslationKey($this->phpelement, !$escape, $codewriter->getEncoding()); + $key = trim(preg_replace('/\s+/sm'.($codewriter->getEncoding()=='UTF-8'?'u':''), ' ', $key)); + if ('' === trim($key)) { + throw new PHPTAL_TemplateException("Empty translation key", + $this->phpelement->getSourceFile(), $this->phpelement->getSourceLine()); + } + $code = $codewriter->str($key); + } else { + $code = $codewriter->evaluateExpression($this->expression); + if (is_array($code)) + return $this->generateChainedContent($codewriter, $code); + + $code = $codewriter->evaluateExpression($this->expression); + } + + $codewriter->pushCode('echo '.$codewriter->getTranslatorReference().'->translate('.$code.','.($escape ? 'true':'false').');'); + } + + public function after(PHPTAL_Php_CodeWriter $codewriter) + { + } + + public function talesChainPart(PHPTAL_Php_TalesChainExecutor $executor, $exp, $islast) + { + $codewriter = $executor->getCodeWriter(); + + $escape = !($this->_echoType == PHPTAL_Php_Attribute::ECHO_STRUCTURE); + $exp = $codewriter->getTranslatorReference()."->translate($exp, " . ($escape ? 'true':'false') . ')'; + if (!$islast) { + $var = $codewriter->createTempVariable(); + $executor->doIf('!phptal_isempty('.$var.' = '.$exp.')'); + $codewriter->pushCode("echo $var"); + $codewriter->recycleTempVariable($var); + } else { + $executor->doElse(); + $codewriter->pushCode("echo $exp"); + } + } + + private function _getTranslationKey(PHPTAL_Dom_Node $tag, $preserve_tags, $encoding) + { + $result = ''; + foreach ($tag->childNodes as $child) { + if ($child instanceof PHPTAL_Dom_Text) { + if ($preserve_tags) { + $result .= $child->getValueEscaped(); + } else { + $result .= $child->getValue($encoding); + } + } elseif ($child instanceof PHPTAL_Dom_Element) { + if ($attr = $child->getAttributeNodeNS('http://xml.zope.org/namespaces/i18n', 'name')) { + $result .= '${' . $attr->getValue() . '}'; + } else { + + if ($preserve_tags) { + $result .= '<'.$child->getQualifiedName(); + foreach ($child->getAttributeNodes() as $attr) { + if ($attr->getReplacedState() === PHPTAL_Dom_Attr::HIDDEN) continue; + + $result .= ' '.$attr->getQualifiedName().'="'.$attr->getValueEscaped().'"'; + } + $result .= '>'.$this->_getTranslationKey($child, $preserve_tags, $encoding) . '</'.$child->getQualifiedName().'>'; + } else { + $result .= $this->_getTranslationKey($child, $preserve_tags, $encoding); + } + } + } + } + return $result; + } + + private function _prepareNames(PHPTAL_Php_CodeWriter $codewriter, PHPTAL_Dom_Node $tag) + { + foreach ($tag->childNodes as $child) { + if ($child instanceof PHPTAL_Dom_Element) { + if ($child->hasAttributeNS('http://xml.zope.org/namespaces/i18n', 'name')) { + $child->generateCode($codewriter); + } else { + $this->_prepareNames($codewriter, $child); + } + } + } + } +} + |