Savetheinternet
13 years ago
30 changed files with 2584 additions and 103 deletions
@ -0,0 +1,45 @@ |
|||||
|
<?php |
||||
|
|
||||
|
/* |
||||
|
* This file is part of Twig. |
||||
|
* |
||||
|
* (c) 2010 Fabien Potencier |
||||
|
* |
||||
|
* For the full copyright and license information, please view the LICENSE |
||||
|
* file that was distributed with this source code. |
||||
|
*/ |
||||
|
class Twig_Extensions_Extension_I18n extends Twig_Extension |
||||
|
{ |
||||
|
/** |
||||
|
* Returns the token parser instances to add to the existing list. |
||||
|
* |
||||
|
* @return array An array of Twig_TokenParserInterface or Twig_TokenParserBrokerInterface instances |
||||
|
*/ |
||||
|
public function getTokenParsers() |
||||
|
{ |
||||
|
return array(new Twig_Extensions_TokenParser_Trans()); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Returns a list of filters to add to the existing list. |
||||
|
* |
||||
|
* @return array An array of filters |
||||
|
*/ |
||||
|
public function getFilters() |
||||
|
{ |
||||
|
return array( |
||||
|
'trans' => new Twig_Filter_Function('gettext'), |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Returns the name of the extension. |
||||
|
* |
||||
|
* @return string The extension name |
||||
|
*/ |
||||
|
public function getName() |
||||
|
{ |
||||
|
return 'i18n'; |
||||
|
} |
||||
|
} |
||||
|
|
@ -0,0 +1,72 @@ |
|||||
|
<?php |
||||
|
|
||||
|
class Twig_Extensions_Extension_Tinyboard extends Twig_Extension |
||||
|
{ |
||||
|
/** |
||||
|
* Returns a list of filters to add to the existing list. |
||||
|
* |
||||
|
* @return array An array of filters |
||||
|
*/ |
||||
|
public function getFilters() |
||||
|
{ |
||||
|
return Array( |
||||
|
'filesize' => new Twig_Filter_Function('format_bytes', Array('needs_environment' => false)), |
||||
|
'truncate' => new Twig_Filter_Function('twig_truncate_filter', array('needs_environment' => false)), |
||||
|
'truncate_body' => new Twig_Filter_Function('truncate', array('needs_environment' => false)), |
||||
|
'extension' => new Twig_Filter_Function('twig_extension_filter', array('needs_environment' => false)), |
||||
|
'sprintf' => new Twig_Filter_Function('sprintf', array('needs_environment' => false)), |
||||
|
'capcode' => new Twig_Filter_Function('capcode', array('needs_environment' => false)), |
||||
|
'hasPermission' => new Twig_Filter_Function('twig_hasPermission_filter', array('needs_environment' => false)), |
||||
|
'date' => new Twig_Filter_Function('twig_date_filter', array('needs_environment' => false)), |
||||
|
'poster_id' => new Twig_Filter_Function('poster_id', array('needs_environment' => false)), |
||||
|
'remove_whitespace' => new Twig_Filter_Function('twig_remove_whitespace_filter', array('needs_environment' => false)) |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Returns the name of the extension. |
||||
|
* |
||||
|
* @return string The extension name |
||||
|
*/ |
||||
|
public function getName() |
||||
|
{ |
||||
|
return 'tinyboard'; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
function twig_remove_whitespace_filter($data) { |
||||
|
return preg_replace('/[\t\r\n]/', '', $data); |
||||
|
} |
||||
|
|
||||
|
function twig_date_filter($date, $format) { |
||||
|
return date($format, $date); |
||||
|
} |
||||
|
|
||||
|
function twig_hasPermission_filter($mod, $permission, $board) { |
||||
|
return hasPermission($permission, $board, $mod); |
||||
|
} |
||||
|
|
||||
|
function twig_extension_filter($value, $case_insensitive = true) { |
||||
|
return 'test'; |
||||
|
$ext = substr($value, strrpos($value, '.') + 1); |
||||
|
if($case_insensitive) |
||||
|
$ext = strtolower($ext); |
||||
|
return $ext; |
||||
|
} |
||||
|
|
||||
|
function twig_sprintf_filter( $value, $var) { |
||||
|
return sprintf($value, $var); |
||||
|
} |
||||
|
|
||||
|
function twig_truncate_filter($value, $length = 30, $preserve = false, $separator = '…') { |
||||
|
if (strlen($value) > $length) { |
||||
|
if ($preserve) { |
||||
|
if (false !== ($breakpoint = strpos($value, ' ', $length))) { |
||||
|
$length = $breakpoint; |
||||
|
} |
||||
|
} |
||||
|
return substr($value, 0, $length) . $separator; |
||||
|
} |
||||
|
return $value; |
||||
|
} |
||||
|
|
@ -0,0 +1,130 @@ |
|||||
|
<?php |
||||
|
|
||||
|
/* |
||||
|
* This file is part of Twig. |
||||
|
* |
||||
|
* (c) 2010 Fabien Potencier |
||||
|
* |
||||
|
* For the full copyright and license information, please view the LICENSE |
||||
|
* file that was distributed with this source code. |
||||
|
*/ |
||||
|
|
||||
|
/** |
||||
|
* Represents a trans node. |
||||
|
* |
||||
|
* @package twig |
||||
|
* @author Fabien Potencier <fabien.potencier@symfony-project.com> |
||||
|
*/ |
||||
|
class Twig_Extensions_Node_Trans extends Twig_Node |
||||
|
{ |
||||
|
public function __construct(Twig_NodeInterface $body, Twig_NodeInterface $plural = null, Twig_Node_Expression $count = null, $lineno, $tag = null) |
||||
|
{ |
||||
|
parent::__construct(array('count' => $count, 'body' => $body, 'plural' => $plural), array(), $lineno, $tag); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Compiles the node to PHP. |
||||
|
* |
||||
|
* @param Twig_Compiler A Twig_Compiler instance |
||||
|
*/ |
||||
|
public function compile(Twig_Compiler $compiler) |
||||
|
{ |
||||
|
$compiler->addDebugInfo($this); |
||||
|
|
||||
|
list($msg, $vars) = $this->compileString($this->getNode('body')); |
||||
|
|
||||
|
if (null !== $this->getNode('plural')) { |
||||
|
list($msg1, $vars1) = $this->compileString($this->getNode('plural')); |
||||
|
|
||||
|
$vars = array_merge($vars, $vars1); |
||||
|
} |
||||
|
|
||||
|
$function = null === $this->getNode('plural') ? 'gettext' : 'ngettext'; |
||||
|
|
||||
|
if ($vars) { |
||||
|
$compiler |
||||
|
->write('echo strtr('.$function.'(') |
||||
|
->subcompile($msg) |
||||
|
; |
||||
|
|
||||
|
if (null !== $this->getNode('plural')) { |
||||
|
$compiler |
||||
|
->raw(', ') |
||||
|
->subcompile($msg1) |
||||
|
->raw(', abs(') |
||||
|
->subcompile($this->getNode('count')) |
||||
|
->raw(')') |
||||
|
; |
||||
|
} |
||||
|
|
||||
|
$compiler->raw('), array('); |
||||
|
|
||||
|
foreach ($vars as $var) { |
||||
|
if ('count' === $var->getAttribute('name')) { |
||||
|
$compiler |
||||
|
->string('%count%') |
||||
|
->raw(' => abs(') |
||||
|
->subcompile($this->getNode('count')) |
||||
|
->raw('), ') |
||||
|
; |
||||
|
} else { |
||||
|
$compiler |
||||
|
->string('%'.$var->getAttribute('name').'%') |
||||
|
->raw(' => ') |
||||
|
->subcompile($var) |
||||
|
->raw(', ') |
||||
|
; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
$compiler->raw("));\n"); |
||||
|
} else { |
||||
|
$compiler |
||||
|
->write('echo '.$function.'(') |
||||
|
->subcompile($msg) |
||||
|
; |
||||
|
|
||||
|
if (null !== $this->getNode('plural')) { |
||||
|
$compiler |
||||
|
->raw(', ') |
||||
|
->subcompile($msg1) |
||||
|
->raw(', abs(') |
||||
|
->subcompile($this->getNode('count')) |
||||
|
->raw(')') |
||||
|
; |
||||
|
} |
||||
|
|
||||
|
$compiler->raw(');'); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
protected function compileString(Twig_NodeInterface $body) |
||||
|
{ |
||||
|
if ($body instanceof Twig_Node_Expression_Name || $body instanceof Twig_Node_Expression_Constant) { |
||||
|
return array($body, array()); |
||||
|
} |
||||
|
|
||||
|
$vars = array(); |
||||
|
|
||||
|
if (count($body)) { |
||||
|
$msg = ''; |
||||
|
|
||||
|
foreach ($body as $node) { |
||||
|
if ($node instanceof Twig_Node_Print) { |
||||
|
$n = $node->getNode('expr'); |
||||
|
while ($n instanceof Twig_Node_Expression_Filter) { |
||||
|
$n = $n->getNode('node'); |
||||
|
} |
||||
|
$msg .= sprintf('%%%s%%', $n->getAttribute('name')); |
||||
|
$vars[] = new Twig_Node_Expression_Name($n->getAttribute('name'), $n->getLine()); |
||||
|
} else { |
||||
|
$msg .= $node->getAttribute('data'); |
||||
|
} |
||||
|
} |
||||
|
} else { |
||||
|
$msg = $body->getAttribute('data'); |
||||
|
} |
||||
|
|
||||
|
return array(new Twig_Node(array(new Twig_Node_Expression_Constant(trim($msg), $body->getLine()))), $vars); |
||||
|
} |
||||
|
} |
@ -0,0 +1,80 @@ |
|||||
|
<?php |
||||
|
|
||||
|
/* |
||||
|
* This file is part of Twig. |
||||
|
* |
||||
|
* (c) 2010 Fabien Potencier |
||||
|
* |
||||
|
* For the full copyright and license information, please view the LICENSE |
||||
|
* file that was distributed with this source code. |
||||
|
*/ |
||||
|
class Twig_Extensions_TokenParser_Trans extends Twig_TokenParser |
||||
|
{ |
||||
|
/** |
||||
|
* Parses a token and returns a node. |
||||
|
* |
||||
|
* @param Twig_Token $token A Twig_Token instance |
||||
|
* |
||||
|
* @return Twig_NodeInterface A Twig_NodeInterface instance |
||||
|
*/ |
||||
|
public function parse(Twig_Token $token) |
||||
|
{ |
||||
|
$lineno = $token->getLine(); |
||||
|
$stream = $this->parser->getStream(); |
||||
|
$count = null; |
||||
|
$plural = null; |
||||
|
|
||||
|
if (!$stream->test(Twig_Token::BLOCK_END_TYPE)) { |
||||
|
$body = $this->parser->getExpressionParser()->parseExpression(); |
||||
|
} else { |
||||
|
$stream->expect(Twig_Token::BLOCK_END_TYPE); |
||||
|
$body = $this->parser->subparse(array($this, 'decideForFork')); |
||||
|
if ('plural' === $stream->next()->getValue()) { |
||||
|
$count = $this->parser->getExpressionParser()->parseExpression(); |
||||
|
$stream->expect(Twig_Token::BLOCK_END_TYPE); |
||||
|
$plural = $this->parser->subparse(array($this, 'decideForEnd'), true); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
$stream->expect(Twig_Token::BLOCK_END_TYPE); |
||||
|
|
||||
|
$this->checkTransString($body, $lineno); |
||||
|
|
||||
|
return new Twig_Extensions_Node_Trans($body, $plural, $count, $lineno, $this->getTag()); |
||||
|
} |
||||
|
|
||||
|
public function decideForFork($token) |
||||
|
{ |
||||
|
return $token->test(array('plural', 'endtrans')); |
||||
|
} |
||||
|
|
||||
|
public function decideForEnd($token) |
||||
|
{ |
||||
|
return $token->test('endtrans'); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Gets the tag name associated with this token parser. |
||||
|
* |
||||
|
* @param string The tag name |
||||
|
*/ |
||||
|
public function getTag() |
||||
|
{ |
||||
|
return 'trans'; |
||||
|
} |
||||
|
|
||||
|
protected function checkTransString(Twig_NodeInterface $body, $lineno) |
||||
|
{ |
||||
|
foreach ($body as $i => $node) { |
||||
|
if ( |
||||
|
$node instanceof Twig_Node_Text |
||||
|
|| |
||||
|
($node instanceof Twig_Node_Print && $node->getNode('expr') instanceof Twig_Node_Expression_Name) |
||||
|
) { |
||||
|
continue; |
||||
|
} |
||||
|
|
||||
|
throw new Twig_Error_Syntax(sprintf('The text to be translated with "trans" can only contain references to simple variables'), $lineno); |
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1,3 @@ |
|||||
|
Danilo Segan <[email protected]> |
||||
|
Nico Kaiser <[email protected]> (contributed most changes between 1.0.2 and 1.0.3, bugfix for 1.0.5) |
||||
|
Steven Armstrong <[email protected]> (gettext.inc, leading to 1.0.6) |
@ -0,0 +1,340 @@ |
|||||
|
GNU GENERAL PUBLIC LICENSE |
||||
|
Version 2, June 1991 |
||||
|
|
||||
|
Copyright (C) 1989, 1991 Free Software Foundation, Inc. |
||||
|
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
||||
|
Everyone is permitted to copy and distribute verbatim copies |
||||
|
of this license document, but changing it is not allowed. |
||||
|
|
||||
|
Preamble |
||||
|
|
||||
|
The licenses for most software are designed to take away your |
||||
|
freedom to share and change it. By contrast, the GNU General Public |
||||
|
License is intended to guarantee your freedom to share and change free |
||||
|
software--to make sure the software is free for all its users. This |
||||
|
General Public License applies to most of the Free Software |
||||
|
Foundation's software and to any other program whose authors commit to |
||||
|
using it. (Some other Free Software Foundation software is covered by |
||||
|
the GNU Library General Public License instead.) You can apply it to |
||||
|
your programs, too. |
||||
|
|
||||
|
When we speak of free software, we are referring to freedom, not |
||||
|
price. Our General Public Licenses are designed to make sure that you |
||||
|
have the freedom to distribute copies of free software (and charge for |
||||
|
this service if you wish), that you receive source code or can get it |
||||
|
if you want it, that you can change the software or use pieces of it |
||||
|
in new free programs; and that you know you can do these things. |
||||
|
|
||||
|
To protect your rights, we need to make restrictions that forbid |
||||
|
anyone to deny you these rights or to ask you to surrender the rights. |
||||
|
These restrictions translate to certain responsibilities for you if you |
||||
|
distribute copies of the software, or if you modify it. |
||||
|
|
||||
|
For example, if you distribute copies of such a program, whether |
||||
|
gratis or for a fee, you must give the recipients all the rights that |
||||
|
you have. You must make sure that they, too, receive or can get the |
||||
|
source code. And you must show them these terms so they know their |
||||
|
rights. |
||||
|
|
||||
|
We protect your rights with two steps: (1) copyright the software, and |
||||
|
(2) offer you this license which gives you legal permission to copy, |
||||
|
distribute and/or modify the software. |
||||
|
|
||||
|
Also, for each author's protection and ours, we want to make certain |
||||
|
that everyone understands that there is no warranty for this free |
||||
|
software. If the software is modified by someone else and passed on, we |
||||
|
want its recipients to know that what they have is not the original, so |
||||
|
that any problems introduced by others will not reflect on the original |
||||
|
authors' reputations. |
||||
|
|
||||
|
Finally, any free program is threatened constantly by software |
||||
|
patents. We wish to avoid the danger that redistributors of a free |
||||
|
program will individually obtain patent licenses, in effect making the |
||||
|
program proprietary. To prevent this, we have made it clear that any |
||||
|
patent must be licensed for everyone's free use or not licensed at all. |
||||
|
|
||||
|
The precise terms and conditions for copying, distribution and |
||||
|
modification follow. |
||||
|
|
||||
|
GNU GENERAL PUBLIC LICENSE |
||||
|
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION |
||||
|
|
||||
|
0. This License applies to any program or other work which contains |
||||
|
a notice placed by the copyright holder saying it may be distributed |
||||
|
under the terms of this General Public License. The "Program", below, |
||||
|
refers to any such program or work, and a "work based on the Program" |
||||
|
means either the Program or any derivative work under copyright law: |
||||
|
that is to say, a work containing the Program or a portion of it, |
||||
|
either verbatim or with modifications and/or translated into another |
||||
|
language. (Hereinafter, translation is included without limitation in |
||||
|
the term "modification".) Each licensee is addressed as "you". |
||||
|
|
||||
|
Activities other than copying, distribution and modification are not |
||||
|
covered by this License; they are outside its scope. The act of |
||||
|
running the Program is not restricted, and the output from the Program |
||||
|
is covered only if its contents constitute a work based on the |
||||
|
Program (independent of having been made by running the Program). |
||||
|
Whether that is true depends on what the Program does. |
||||
|
|
||||
|
1. You may copy and distribute verbatim copies of the Program's |
||||
|
source code as you receive it, in any medium, provided that you |
||||
|
conspicuously and appropriately publish on each copy an appropriate |
||||
|
copyright notice and disclaimer of warranty; keep intact all the |
||||
|
notices that refer to this License and to the absence of any warranty; |
||||
|
and give any other recipients of the Program a copy of this License |
||||
|
along with the Program. |
||||
|
|
||||
|
You may charge a fee for the physical act of transferring a copy, and |
||||
|
you may at your option offer warranty protection in exchange for a fee. |
||||
|
|
||||
|
2. You may modify your copy or copies of the Program or any portion |
||||
|
of it, thus forming a work based on the Program, and copy and |
||||
|
distribute such modifications or work under the terms of Section 1 |
||||
|
above, provided that you also meet all of these conditions: |
||||
|
|
||||
|
a) You must cause the modified files to carry prominent notices |
||||
|
stating that you changed the files and the date of any change. |
||||
|
|
||||
|
b) You must cause any work that you distribute or publish, that in |
||||
|
whole or in part contains or is derived from the Program or any |
||||
|
part thereof, to be licensed as a whole at no charge to all third |
||||
|
parties under the terms of this License. |
||||
|
|
||||
|
c) If the modified program normally reads commands interactively |
||||
|
when run, you must cause it, when started running for such |
||||
|
interactive use in the most ordinary way, to print or display an |
||||
|
announcement including an appropriate copyright notice and a |
||||
|
notice that there is no warranty (or else, saying that you provide |
||||
|
a warranty) and that users may redistribute the program under |
||||
|
these conditions, and telling the user how to view a copy of this |
||||
|
License. (Exception: if the Program itself is interactive but |
||||
|
does not normally print such an announcement, your work based on |
||||
|
the Program is not required to print an announcement.) |
||||
|
|
||||
|
These requirements apply to the modified work as a whole. If |
||||
|
identifiable sections of that work are not derived from the Program, |
||||
|
and can be reasonably considered independent and separate works in |
||||
|
themselves, then this License, and its terms, do not apply to those |
||||
|
sections when you distribute them as separate works. But when you |
||||
|
distribute the same sections as part of a whole which is a work based |
||||
|
on the Program, the distribution of the whole must be on the terms of |
||||
|
this License, whose permissions for other licensees extend to the |
||||
|
entire whole, and thus to each and every part regardless of who wrote it. |
||||
|
|
||||
|
Thus, it is not the intent of this section to claim rights or contest |
||||
|
your rights to work written entirely by you; rather, the intent is to |
||||
|
exercise the right to control the distribution of derivative or |
||||
|
collective works based on the Program. |
||||
|
|
||||
|
In addition, mere aggregation of another work not based on the Program |
||||
|
with the Program (or with a work based on the Program) on a volume of |
||||
|
a storage or distribution medium does not bring the other work under |
||||
|
the scope of this License. |
||||
|
|
||||
|
3. You may copy and distribute the Program (or a work based on it, |
||||
|
under Section 2) in object code or executable form under the terms of |
||||
|
Sections 1 and 2 above provided that you also do one of the following: |
||||
|
|
||||
|
a) Accompany it with the complete corresponding machine-readable |
||||
|
source code, which must be distributed under the terms of Sections |
||||
|
1 and 2 above on a medium customarily used for software interchange; or, |
||||
|
|
||||
|
b) Accompany it with a written offer, valid for at least three |
||||
|
years, to give any third party, for a charge no more than your |
||||
|
cost of physically performing source distribution, a complete |
||||
|
machine-readable copy of the corresponding source code, to be |
||||
|
distributed under the terms of Sections 1 and 2 above on a medium |
||||
|
customarily used for software interchange; or, |
||||
|
|
||||
|
c) Accompany it with the information you received as to the offer |
||||
|
to distribute corresponding source code. (This alternative is |
||||
|
allowed only for noncommercial distribution and only if you |
||||
|
received the program in object code or executable form with such |
||||
|
an offer, in accord with Subsection b above.) |
||||
|
|
||||
|
The source code for a work means the preferred form of the work for |
||||
|
making modifications to it. For an executable work, complete source |
||||
|
code means all the source code for all modules it contains, plus any |
||||
|
associated interface definition files, plus the scripts used to |
||||
|
control compilation and installation of the executable. However, as a |
||||
|
special exception, the source code distributed need not include |
||||
|
anything that is normally distributed (in either source or binary |
||||
|
form) with the major components (compiler, kernel, and so on) of the |
||||
|
operating system on which the executable runs, unless that component |
||||
|
itself accompanies the executable. |
||||
|
|
||||
|
If distribution of executable or object code is made by offering |
||||
|
access to copy from a designated place, then offering equivalent |
||||
|
access to copy the source code from the same place counts as |
||||
|
distribution of the source code, even though third parties are not |
||||
|
compelled to copy the source along with the object code. |
||||
|
|
||||
|
4. You may not copy, modify, sublicense, or distribute the Program |
||||
|
except as expressly provided under this License. Any attempt |
||||
|
otherwise to copy, modify, sublicense or distribute the Program is |
||||
|
void, and will automatically terminate your rights under this License. |
||||
|
However, parties who have received copies, or rights, from you under |
||||
|
this License will not have their licenses terminated so long as such |
||||
|
parties remain in full compliance. |
||||
|
|
||||
|
5. You are not required to accept this License, since you have not |
||||
|
signed it. However, nothing else grants you permission to modify or |
||||
|
distribute the Program or its derivative works. These actions are |
||||
|
prohibited by law if you do not accept this License. Therefore, by |
||||
|
modifying or distributing the Program (or any work based on the |
||||
|
Program), you indicate your acceptance of this License to do so, and |
||||
|
all its terms and conditions for copying, distributing or modifying |
||||
|
the Program or works based on it. |
||||
|
|
||||
|
6. Each time you redistribute the Program (or any work based on the |
||||
|
Program), the recipient automatically receives a license from the |
||||
|
original licensor to copy, distribute or modify the Program subject to |
||||
|
these terms and conditions. You may not impose any further |
||||
|
restrictions on the recipients' exercise of the rights granted herein. |
||||
|
You are not responsible for enforcing compliance by third parties to |
||||
|
this License. |
||||
|
|
||||
|
7. If, as a consequence of a court judgment or allegation of patent |
||||
|
infringement or for any other reason (not limited to patent issues), |
||||
|
conditions are imposed on you (whether by court order, agreement or |
||||
|
otherwise) that contradict the conditions of this License, they do not |
||||
|
excuse you from the conditions of this License. If you cannot |
||||
|
distribute so as to satisfy simultaneously your obligations under this |
||||
|
License and any other pertinent obligations, then as a consequence you |
||||
|
may not distribute the Program at all. For example, if a patent |
||||
|
license would not permit royalty-free redistribution of the Program by |
||||
|
all those who receive copies directly or indirectly through you, then |
||||
|
the only way you could satisfy both it and this License would be to |
||||
|
refrain entirely from distribution of the Program. |
||||
|
|
||||
|
If any portion of this section is held invalid or unenforceable under |
||||
|
any particular circumstance, the balance of the section is intended to |
||||
|
apply and the section as a whole is intended to apply in other |
||||
|
circumstances. |
||||
|
|
||||
|
It is not the purpose of this section to induce you to infringe any |
||||
|
patents or other property right claims or to contest validity of any |
||||
|
such claims; this section has the sole purpose of protecting the |
||||
|
integrity of the free software distribution system, which is |
||||
|
implemented by public license practices. Many people have made |
||||
|
generous contributions to the wide range of software distributed |
||||
|
through that system in reliance on consistent application of that |
||||
|
system; it is up to the author/donor to decide if he or she is willing |
||||
|
to distribute software through any other system and a licensee cannot |
||||
|
impose that choice. |
||||
|
|
||||
|
This section is intended to make thoroughly clear what is believed to |
||||
|
be a consequence of the rest of this License. |
||||
|
|
||||
|
8. If the distribution and/or use of the Program is restricted in |
||||
|
certain countries either by patents or by copyrighted interfaces, the |
||||
|
original copyright holder who places the Program under this License |
||||
|
may add an explicit geographical distribution limitation excluding |
||||
|
those countries, so that distribution is permitted only in or among |
||||
|
countries not thus excluded. In such case, this License incorporates |
||||
|
the limitation as if written in the body of this License. |
||||
|
|
||||
|
9. The Free Software Foundation may publish revised and/or new versions |
||||
|
of the General Public License from time to time. Such new versions will |
||||
|
be similar in spirit to the present version, but may differ in detail to |
||||
|
address new problems or concerns. |
||||
|
|
||||
|
Each version is given a distinguishing version number. If the Program |
||||
|
specifies a version number of this License which applies to it and "any |
||||
|
later version", you have the option of following the terms and conditions |
||||
|
either of that version or of any later version published by the Free |
||||
|
Software Foundation. If the Program does not specify a version number of |
||||
|
this License, you may choose any version ever published by the Free Software |
||||
|
Foundation. |
||||
|
|
||||
|
10. If you wish to incorporate parts of the Program into other free |
||||
|
programs whose distribution conditions are different, write to the author |
||||
|
to ask for permission. For software which is copyrighted by the Free |
||||
|
Software Foundation, write to the Free Software Foundation; we sometimes |
||||
|
make exceptions for this. Our decision will be guided by the two goals |
||||
|
of preserving the free status of all derivatives of our free software and |
||||
|
of promoting the sharing and reuse of software generally. |
||||
|
|
||||
|
NO WARRANTY |
||||
|
|
||||
|
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY |
||||
|
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN |
||||
|
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES |
||||
|
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED |
||||
|
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF |
||||
|
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS |
||||
|
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE |
||||
|
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, |
||||
|
REPAIR OR CORRECTION. |
||||
|
|
||||
|
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING |
||||
|
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR |
||||
|
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, |
||||
|
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING |
||||
|
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED |
||||
|
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY |
||||
|
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER |
||||
|
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE |
||||
|
POSSIBILITY OF SUCH DAMAGES. |
||||
|
|
||||
|
END OF TERMS AND CONDITIONS |
||||
|
|
||||
|
How to Apply These Terms to Your New Programs |
||||
|
|
||||
|
If you develop a new program, and you want it to be of the greatest |
||||
|
possible use to the public, the best way to achieve this is to make it |
||||
|
free software which everyone can redistribute and change under these terms. |
||||
|
|
||||
|
To do so, attach the following notices to the program. It is safest |
||||
|
to attach them to the start of each source file to most effectively |
||||
|
convey the exclusion of warranty; and each file should have at least |
||||
|
the "copyright" line and a pointer to where the full notice is found. |
||||
|
|
||||
|
<one line to give the program's name and a brief idea of what it does.> |
||||
|
Copyright (C) <year> <name of author> |
||||
|
|
||||
|
This program is free software; you can redistribute it and/or modify |
||||
|
it under the terms of the GNU General Public License as published by |
||||
|
the Free Software Foundation; either version 2 of the License, or |
||||
|
(at your option) any later version. |
||||
|
|
||||
|
This program is distributed in the hope that it will be useful, |
||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
|
GNU General Public License for more details. |
||||
|
|
||||
|
You should have received a copy of the GNU General Public License |
||||
|
along with this program; if not, write to the Free Software |
||||
|
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
||||
|
|
||||
|
|
||||
|
Also add information on how to contact you by electronic and paper mail. |
||||
|
|
||||
|
If the program is interactive, make it output a short notice like this |
||||
|
when it starts in an interactive mode: |
||||
|
|
||||
|
Gnomovision version 69, Copyright (C) year name of author |
||||
|
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. |
||||
|
This is free software, and you are welcome to redistribute it |
||||
|
under certain conditions; type `show c' for details. |
||||
|
|
||||
|
The hypothetical commands `show w' and `show c' should show the appropriate |
||||
|
parts of the General Public License. Of course, the commands you use may |
||||
|
be called something other than `show w' and `show c'; they could even be |
||||
|
mouse-clicks or menu items--whatever suits your program. |
||||
|
|
||||
|
You should also get your employer (if you work as a programmer) or your |
||||
|
school, if any, to sign a "copyright disclaimer" for the program, if |
||||
|
necessary. Here is a sample; alter the names: |
||||
|
|
||||
|
Yoyodyne, Inc., hereby disclaims all copyright interest in the program |
||||
|
`Gnomovision' (which makes passes at compilers) written by James Hacker. |
||||
|
|
||||
|
<signature of Ty Coon>, 1 April 1989 |
||||
|
Ty Coon, President of Vice |
||||
|
|
||||
|
This General Public License does not permit incorporating your program into |
||||
|
proprietary programs. If your program is a subroutine library, you may |
||||
|
consider it more useful to permit linking proprietary applications with the |
||||
|
library. If this is what you want to do, use the GNU Library General |
||||
|
Public License instead of this License. |
@ -0,0 +1,38 @@ |
|||||
|
PACKAGE = php-gettext-$(VERSION) |
||||
|
VERSION = 1.0.11 |
||||
|
|
||||
|
DIST_FILES = \
|
||||
|
gettext.php \
|
||||
|
gettext.inc \
|
||||
|
streams.php \
|
||||
|
AUTHORS \
|
||||
|
README \
|
||||
|
COPYING \
|
||||
|
Makefile \
|
||||
|
examples/index.php \
|
||||
|
examples/pigs_dropin.php \
|
||||
|
examples/pigs_fallback.php \
|
||||
|
examples/locale/sr_CS/LC_MESSAGES/messages.po \
|
||||
|
examples/locale/sr_CS/LC_MESSAGES/messages.mo \
|
||||
|
examples/locale/de_CH/LC_MESSAGES/messages.po \
|
||||
|
examples/locale/de_CH/LC_MESSAGES/messages.mo \
|
||||
|
examples/update \
|
||||
|
tests/LocalesTest.php \
|
||||
|
tests/ParsingTest.php |
||||
|
|
||||
|
check: |
||||
|
phpunit --verbose tests |
||||
|
|
||||
|
dist: check |
||||
|
if [ -d $(PACKAGE) ]; then \
|
||||
|
rm -rf $(PACKAGE); \
|
||||
|
fi; \
|
||||
|
mkdir $(PACKAGE); \
|
||||
|
if [ -d $(PACKAGE) ]; then \
|
||||
|
cp -rp --parents $(DIST_FILES) $(PACKAGE); \
|
||||
|
tar cvzf $(PACKAGE).tar.gz $(PACKAGE); \
|
||||
|
rm -rf $(PACKAGE); \
|
||||
|
fi; |
||||
|
|
||||
|
clean: |
||||
|
rm -f $(PACKAGE).tar.gz |
@ -0,0 +1,161 @@ |
|||||
|
PHP-gettext 1.0 (https://launchpad.net/php-gettext) |
||||
|
|
||||
|
Copyright 2003, 2006, 2009 -- Danilo "angry with PHP[1]" Segan |
||||
|
Licensed under GPLv2 (or any later version, see COPYING) |
||||
|
|
||||
|
[1] PHP is actually cyrillic, and translates roughly to |
||||
|
"works-doesn't-work" (UTF-8: Ради-Не-Ради) |
||||
|
|
||||
|
|
||||
|
Introduction |
||||
|
|
||||
|
How many times did you look for a good translation tool, and |
||||
|
found out that gettext is best for the job? Many times. |
||||
|
|
||||
|
How many times did you try to use gettext in PHP, but failed |
||||
|
miserably, because either your hosting provider didn't support |
||||
|
it, or the server didn't have adequate locale? Many times. |
||||
|
|
||||
|
Well, this is a solution to your needs. It allows using gettext |
||||
|
tools for managing translations, yet it doesn't require gettext |
||||
|
library at all. It parses generated MO files directly, and thus |
||||
|
might be a bit slower than the (maybe provided) gettext library. |
||||
|
|
||||
|
PHP-gettext is a simple reader for GNU gettext MO files. Those |
||||
|
are binary containers for translations, produced by GNU msgfmt. |
||||
|
|
||||
|
Why? |
||||
|
|
||||
|
I got used to having gettext work even without gettext |
||||
|
library. It's there in my favourite language Python, so I was |
||||
|
surprised that I couldn't find it in PHP. I even Googled for it, |
||||
|
but to no avail. |
||||
|
|
||||
|
So, I said, what the heck, I'm going to write it for this |
||||
|
disguisting language of PHP, because I'm often constrained to it. |
||||
|
|
||||
|
Features |
||||
|
|
||||
|
o Support for simple translations |
||||
|
Just define a simple alias for translate() function (suggested |
||||
|
use of _() or gettext(); see provided example). |
||||
|
|
||||
|
o Support for ngettext calls (plural forms, see a note under bugs) |
||||
|
You may also use plural forms. Translations in MO files need to |
||||
|
provide this, and they must also provide "plural-forms" header. |
||||
|
Please see 'info gettext' for more details. |
||||
|
|
||||
|
o Support for reading straight files, or strings (!!!) |
||||
|
Since I can imagine many different backends for reading in the MO |
||||
|
file data, I used imaginary abstract class StreamReader to do all |
||||
|
the input (check streams.php). For your convenience, I've already |
||||
|
provided two classes for reading files: FileReader and |
||||
|
StringReader (CachedFileReader is a combination of the two: it |
||||
|
loads entire file contents into a string, and then works on that). |
||||
|
See example below for usage. You can for instance use StringReader |
||||
|
when you read in data from a database, or you can create your own |
||||
|
derivative of StreamReader for anything you like. |
||||
|
|
||||
|
|
||||
|
Bugs |
||||
|
|
||||
|
Report them on https://bugs.launchpad.net/php-gettext |
||||
|
|
||||
|
Usage |
||||
|
|
||||
|
Put files streams.php and gettext.php somewhere you can load them |
||||
|
from, and require 'em in where you want to use them. |
||||
|
|
||||
|
Then, create one 'stream reader' (a class that provides functions |
||||
|
like read(), seekto(), currentpos() and length()) which will |
||||
|
provide data for the 'gettext_reader', with eg. |
||||
|
$streamer = new FileStream('data.mo'); |
||||
|
|
||||
|
Then, use that as a parameter to gettext_reader constructor: |
||||
|
$wohoo = new gettext_reader($streamer); |
||||
|
|
||||
|
If you want to disable pre-loading of entire message catalog in |
||||
|
memory (if, for example, you have a multi-thousand message catalog |
||||
|
which you'll use only occasionally), use "false" for second |
||||
|
parameter to gettext_reader constructor: |
||||
|
$wohoo = new gettext_reader($streamer, false); |
||||
|
|
||||
|
From now on, you have all the benefits of gettext data at your |
||||
|
disposal, so may run: |
||||
|
print $wohoo->translate("This is a test"); |
||||
|
print $wohoo->ngettext("%d bird", "%d birds", $birds); |
||||
|
|
||||
|
You might need to pass parameter "-k" to xgettext to make it |
||||
|
extract all the strings. In above example, try with |
||||
|
xgettext -ktranslate -kngettext:1,2 file.php |
||||
|
what should create messages.po which contains two messages for |
||||
|
translation. |
||||
|
|
||||
|
I suggest creating simple aliases for these functions (see |
||||
|
example/pigs.php for how do I do it, which means it's probably a |
||||
|
bad way). |
||||
|
|
||||
|
|
||||
|
Usage with gettext.inc (standard gettext interfaces emulation) |
||||
|
|
||||
|
Check example in examples/pig_dropin.php, basically you include |
||||
|
gettext.inc and use all the standard gettext interfaces as |
||||
|
documented on: |
||||
|
|
||||
|
http://www.php.net/gettext |
||||
|
|
||||
|
The only catch is that you can check return value of setlocale() |
||||
|
to see if your locale is system supported or not. |
||||
|
|
||||
|
|
||||
|
Example |
||||
|
|
||||
|
See in examples/ subdirectory. There are a couple of files. |
||||
|
pigs.php is an example, serbian.po is a translation to Serbian |
||||
|
language, and serbian.mo is generated with |
||||
|
msgfmt -o serbian.mo serbian.po |
||||
|
There is also simple "update" script that can be used to generate |
||||
|
POT file and to update the translation using msgmerge. |
||||
|
|
||||
|
TODO: |
||||
|
|
||||
|
o Improve speed to be even more comparable to the native gettext |
||||
|
implementation. |
||||
|
|
||||
|
o Try to use hash tables in MO files: with pre-loading, would it |
||||
|
be useful at all? |
||||
|
|
||||
|
Never-asked-questions: |
||||
|
|
||||
|
o Why did you mark this as version 1.0 when this is the first code |
||||
|
release? |
||||
|
|
||||
|
Well, it's quite simple. I consider that the first released thing |
||||
|
should be labeled "version 1" (first, right?). Zero is there to |
||||
|
indicate that there's zero improvement and/or change compared to |
||||
|
"version 1". |
||||
|
|
||||
|
I plan to use version numbers 1.0.* for small bugfixes, and to |
||||
|
release 1.1 as "first stable release of version 1". |
||||
|
|
||||
|
This may trick someone that this is actually useful software, but |
||||
|
as with any other free software, I take NO RESPONSIBILITY for |
||||
|
creating such a masterpiece that will smoke crack, trash your |
||||
|
hard disk, and make lasers in your CD device dance to the tune of |
||||
|
Mozart's 40th Symphony (there is one like that, right?). |
||||
|
|
||||
|
o Can I...? |
||||
|
|
||||
|
Yes, you can. This is free software (as in freedom, free speech), |
||||
|
and you might do whatever you wish with it, provided you do not |
||||
|
limit freedom of others (GPL). |
||||
|
|
||||
|
I'm considering licensing this under LGPL, but I *do* want |
||||
|
*every* PHP-gettext user to contribute and respect ideas of free |
||||
|
software, so don't count on it happening anytime soon. |
||||
|
|
||||
|
I'm sorry that I'm taking away your freedom of taking others' |
||||
|
freedom away, but I believe that's neglible as compared to what |
||||
|
freedoms you could take away. ;-) |
||||
|
|
||||
|
Uhm, whatever. |
@ -0,0 +1,27 @@ |
|||||
|
<html> |
||||
|
<head> |
||||
|
<title>PHP-gettext examples</title> |
||||
|
</head> |
||||
|
<body> |
||||
|
<h1>PHP-gettext</h1> |
||||
|
|
||||
|
<h2>Introduction</h2> |
||||
|
<p>PHP-gettext provides a simple gettext replacement that works independently from the system's gettext abilities. |
||||
|
It can read MO files and use them for translating strings.</p> |
||||
|
<p>This version has the ability to cache all strings and translations to speed up the string lookup. |
||||
|
While the cache is enabled by default, it can be switched off with the second parameter in the constructor (e.g. when using very large MO files |
||||
|
that you don't want to keep in memory)</p> |
||||
|
|
||||
|
|
||||
|
<h2>Examples</h2> |
||||
|
<ul> |
||||
|
<li><a href="pigs_dropin.php">PHP-gettext as a dropin replacement</a></li> |
||||
|
<li><a href="pigs_fallback.php">PHP-gettext as a fallback solution</a></li> |
||||
|
</ul> |
||||
|
|
||||
|
<hr /> |
||||
|
<p>Copyright (c) 2003-2006 Danilo Segan</p> |
||||
|
<p>Copyright (c) 2005-2006 Steven Armstrong</p> |
||||
|
|
||||
|
</body> |
||||
|
</html> |
Binary file not shown.
@ -0,0 +1,30 @@ |
|||||
|
# Sample translation for PHP-gettext 1.0 |
||||
|
# Copyright (c) 2003 Danilo Segan <[email protected]> |
||||
|
# |
||||
|
msgid "" |
||||
|
msgstr "" |
||||
|
"Project-Id-Version: pigs\n" |
||||
|
"Report-Msgid-Bugs-To: \n" |
||||
|
"POT-Creation-Date: 2003-10-23 04:50+0200\n" |
||||
|
"PO-Revision-Date: 2003-11-01 23:40+0100\n" |
||||
|
"Last-Translator: Danilo Segan <[email protected]>\n" |
||||
|
"Language-Team: Serbian (sr) <[email protected]>\n" |
||||
|
"MIME-Version: 1.0\n" |
||||
|
"Content-Type: text/plain; charset=UTF-8\n" |
||||
|
"Content-Transfer-Encoding: 8bit\n" |
||||
|
#"Plural-Forms: nplurals=2; plural=n != 1;\n" |
||||
|
|
||||
|
#: pigs.php:19 |
||||
|
msgid "" |
||||
|
"This is how the story goes.\n" |
||||
|
"\n" |
||||
|
msgstr "" |
||||
|
"Und so geht die Geschichte.\n" |
||||
|
"\n" |
||||
|
|
||||
|
#: pigs.php:21 |
||||
|
#, php-format |
||||
|
msgid "%d pig went to the market\n" |
||||
|
msgid_plural "%d pigs went to the market\n" |
||||
|
msgstr[0] "%d Schwein ging zum Markt\n" |
||||
|
msgstr[1] "%d Schweine gingen zum Markt\n" |
Binary file not shown.
@ -0,0 +1,30 @@ |
|||||
|
# Sample translation for PHP-gettext 1.0 |
||||
|
# Copyright (c) 2003,2006 Danilo Segan <[email protected]> |
||||
|
# |
||||
|
msgid "" |
||||
|
msgstr "" |
||||
|
"Project-Id-Version: pigs\n" |
||||
|
"Report-Msgid-Bugs-To: \n" |
||||
|
"POT-Creation-Date: 2003-10-23 04:50+0200\n" |
||||
|
"PO-Revision-Date: 2006-02-02 21:06+0100\n" |
||||
|
"Last-Translator: Danilo Segan <[email protected]>\n" |
||||
|
"Language-Team: Serbian (sr) <[email protected]>\n" |
||||
|
"MIME-Version: 1.0\n" |
||||
|
"Content-Type: text/plain; charset=UTF-8\n" |
||||
|
"Content-Transfer-Encoding: 8bit\n" |
||||
|
"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && " |
||||
|
"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n" |
||||
|
|
||||
|
#: pigs.php:19 |
||||
|
msgid "" |
||||
|
"This is how the story goes.\n" |
||||
|
"\n" |
||||
|
msgstr "Овако иде прича.\n\n" |
||||
|
|
||||
|
#: pigs.php:21 |
||||
|
#, php-format |
||||
|
msgid "%d pig went to the market\n" |
||||
|
msgid_plural "%d pigs went to the market\n" |
||||
|
msgstr[0] "%d мало прасе је отишло на пијац\n" |
||||
|
msgstr[1] "%d мала прасета су отишла на пијац\n" |
||||
|
msgstr[2] "%d малих прасића је отишло на пијац\n" |
@ -0,0 +1,89 @@ |
|||||
|
<?php |
||||
|
/* |
||||
|
Copyright (c) 2003,2004,2005,2009 Danilo Segan <danilo@kvota.net>. |
||||
|
Copyright (c) 2005,2006 Steven Armstrong <sa@c-area.ch> |
||||
|
|
||||
|
This file is part of PHP-gettext. |
||||
|
|
||||
|
PHP-gettext is free software; you can redistribute it and/or modify |
||||
|
it under the terms of the GNU General Public License as published by |
||||
|
the Free Software Foundation; either version 2 of the License, or |
||||
|
(at your option) any later version. |
||||
|
|
||||
|
PHP-gettext is distributed in the hope that it will be useful, |
||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
|
GNU General Public License for more details. |
||||
|
|
||||
|
You should have received a copy of the GNU General Public License |
||||
|
along with PHP-gettext; if not, write to the Free Software |
||||
|
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
||||
|
|
||||
|
*/ |
||||
|
|
||||
|
error_reporting(E_ALL | E_STRICT); |
||||
|
|
||||
|
// define constants |
||||
|
define('PROJECT_DIR', realpath('./')); |
||||
|
define('LOCALE_DIR', PROJECT_DIR .'/locale'); |
||||
|
define('DEFAULT_LOCALE', 'en_US'); |
||||
|
|
||||
|
require_once('../gettext.inc'); |
||||
|
|
||||
|
$supported_locales = array('en_US', 'sr_CS', 'de_CH'); |
||||
|
$encoding = 'UTF-8'; |
||||
|
|
||||
|
$locale = (isset($_GET['lang']))? $_GET['lang'] : DEFAULT_LOCALE; |
||||
|
|
||||
|
// gettext setup |
||||
|
T_setlocale(LC_MESSAGES, $locale); |
||||
|
// Set the text domain as 'messages' |
||||
|
$domain = 'messages'; |
||||
|
bindtextdomain($domain, LOCALE_DIR); |
||||
|
// bind_textdomain_codeset is supported only in PHP 4.2.0+ |
||||
|
if (function_exists('bind_textdomain_codeset')) |
||||
|
bind_textdomain_codeset($domain, $encoding); |
||||
|
textdomain($domain); |
||||
|
|
||||
|
header("Content-type: text/html; charset=$encoding"); |
||||
|
?> |
||||
|
<html> |
||||
|
<head> |
||||
|
<title>PHP-gettext dropin example</title> |
||||
|
</head> |
||||
|
<body> |
||||
|
<h1>PHP-gettext as a dropin replacement</h1> |
||||
|
<p>Example showing how to use PHP-gettext as a dropin replacement for the native gettext library.</p> |
||||
|
<?php |
||||
|
print "<p>"; |
||||
|
foreach($supported_locales as $l) { |
||||
|
print "[<a href=\"?lang=$l\">$l</a>] "; |
||||
|
} |
||||
|
print "</p>\n"; |
||||
|
|
||||
|
if (!locale_emulation()) { |
||||
|
print "<p>locale '$locale' is supported by your system, using native gettext implementation.</p>\n"; |
||||
|
} |
||||
|
else { |
||||
|
print "<p>locale '$locale' is _not_ supported on your system, using the default locale '". DEFAULT_LOCALE ."'.</p>\n"; |
||||
|
} |
||||
|
?> |
||||
|
|
||||
|
<hr /> |
||||
|
|
||||
|
<?php |
||||
|
// using PHP-gettext |
||||
|
print "<pre>"; |
||||
|
print _("This is how the story goes.\n\n"); |
||||
|
for ($number=6; $number>=0; $number--) { |
||||
|
print sprintf(T_ngettext("%d pig went to the market\n", |
||||
|
"%d pigs went to the market\n", $number), |
||||
|
$number ); |
||||
|
} |
||||
|
print "</pre>\n"; |
||||
|
?> |
||||
|
|
||||
|
<hr /> |
||||
|
<p>« <a href="./">back</a></p> |
||||
|
</body> |
||||
|
</html> |
@ -0,0 +1,88 @@ |
|||||
|
<?php |
||||
|
/* |
||||
|
Copyright (c) 2003,2004,2005,2009 Danilo Segan <danilo@kvota.net>. |
||||
|
Copyright (c) 2005,2006 Steven Armstrong <sa@c-area.ch> |
||||
|
|
||||
|
This file is part of PHP-gettext. |
||||
|
|
||||
|
PHP-gettext is free software; you can redistribute it and/or modify |
||||
|
it under the terms of the GNU General Public License as published by |
||||
|
the Free Software Foundation; either version 2 of the License, or |
||||
|
(at your option) any later version. |
||||
|
|
||||
|
PHP-gettext is distributed in the hope that it will be useful, |
||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
|
GNU General Public License for more details. |
||||
|
|
||||
|
You should have received a copy of the GNU General Public License |
||||
|
along with PHP-gettext; if not, write to the Free Software |
||||
|
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
||||
|
|
||||
|
*/ |
||||
|
|
||||
|
error_reporting(E_ALL | E_STRICT); |
||||
|
|
||||
|
// define constants |
||||
|
define('PROJECT_DIR', realpath('./')); |
||||
|
define('LOCALE_DIR', PROJECT_DIR .'/locale'); |
||||
|
define('DEFAULT_LOCALE', 'en_US'); |
||||
|
|
||||
|
require_once('../gettext.inc'); |
||||
|
|
||||
|
$supported_locales = array('en_US', 'sr_CS', 'de_CH'); |
||||
|
$encoding = 'UTF-8'; |
||||
|
|
||||
|
$locale = (isset($_GET['lang']))? $_GET['lang'] : DEFAULT_LOCALE; |
||||
|
|
||||
|
// gettext setup |
||||
|
T_setlocale(LC_MESSAGES, $locale); |
||||
|
// Set the text domain as 'messages' |
||||
|
$domain = 'messages'; |
||||
|
T_bindtextdomain($domain, LOCALE_DIR); |
||||
|
T_bind_textdomain_codeset($domain, $encoding); |
||||
|
T_textdomain($domain); |
||||
|
|
||||
|
header("Content-type: text/html; charset=$encoding"); |
||||
|
?> |
||||
|
<html> |
||||
|
<head> |
||||
|
<title>PHP-gettext fallback example</title> |
||||
|
</head> |
||||
|
<body> |
||||
|
<h1>PHP-gettext as a fallback solution</h1> |
||||
|
<p>Example showing how to use PHP-gettext as a fallback solution if the native gettext library is not available or the system does not support the requested locale.</p> |
||||
|
|
||||
|
<?php |
||||
|
print "<p>"; |
||||
|
foreach($supported_locales as $l) { |
||||
|
print "[<a href=\"?lang=$l\">$l</a>] "; |
||||
|
} |
||||
|
print "</p>\n"; |
||||
|
|
||||
|
if (!locale_emulation()) { |
||||
|
print "<p>locale '$locale' is supported by your system, using native gettext implementation.</p>\n"; |
||||
|
} |
||||
|
else { |
||||
|
print "<p>locale '$locale' is <strong>not</strong> supported on your system, using custom gettext implementation.</p>\n"; |
||||
|
} |
||||
|
?> |
||||
|
|
||||
|
<hr /> |
||||
|
|
||||
|
<?php |
||||
|
// using PHP-gettext |
||||
|
print "<pre>"; |
||||
|
print T_("This is how the story goes.\n\n"); |
||||
|
for ($number=6; $number>=0; $number--) { |
||||
|
print sprintf( T_ngettext("%d pig went to the market\n", |
||||
|
"%d pigs went to the market\n", $number), |
||||
|
$number ); |
||||
|
} |
||||
|
print "</pre>\n"; |
||||
|
?> |
||||
|
|
||||
|
<hr /> |
||||
|
<p>« <a href="./">back</a></p> |
||||
|
</body> |
||||
|
</html> |
@ -0,0 +1,14 @@ |
|||||
|
#!/bin/sh |
||||
|
TEMPLATE=pigs.pot |
||||
|
xgettext -kT_ngettext:1,2 -kT_ -L PHP -o $TEMPLATE pigs_dropin.php |
||||
|
if [ "x$1" = "x-p" ]; then |
||||
|
msgfmt --statistics $TEMPLATE |
||||
|
else |
||||
|
if [ -f $1.po ]; then |
||||
|
msgmerge -o .tmp$1.po $1.po $TEMPLATE |
||||
|
mv .tmp$1.po $1.po |
||||
|
msgfmt --statistics $1.po |
||||
|
else |
||||
|
echo "Usage: $0 [-p|<basename>]" |
||||
|
fi |
||||
|
fi |
@ -0,0 +1,536 @@ |
|||||
|
<?php |
||||
|
/* |
||||
|
Copyright (c) 2005 Steven Armstrong <sa at c-area dot ch> |
||||
|
Copyright (c) 2009 Danilo Segan <danilo@kvota.net> |
||||
|
|
||||
|
Drop in replacement for native gettext. |
||||
|
|
||||
|
This file is part of PHP-gettext. |
||||
|
|
||||
|
PHP-gettext is free software; you can redistribute it and/or modify |
||||
|
it under the terms of the GNU General Public License as published by |
||||
|
the Free Software Foundation; either version 2 of the License, or |
||||
|
(at your option) any later version. |
||||
|
|
||||
|
PHP-gettext is distributed in the hope that it will be useful, |
||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
|
GNU General Public License for more details. |
||||
|
|
||||
|
You should have received a copy of the GNU General Public License |
||||
|
along with PHP-gettext; if not, write to the Free Software |
||||
|
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
||||
|
|
||||
|
*/ |
||||
|
/* |
||||
|
LC_CTYPE 0 |
||||
|
LC_NUMERIC 1 |
||||
|
LC_TIME 2 |
||||
|
LC_COLLATE 3 |
||||
|
LC_MONETARY 4 |
||||
|
LC_MESSAGES 5 |
||||
|
LC_ALL 6 |
||||
|
*/ |
||||
|
|
||||
|
// LC_MESSAGES is not available if php-gettext is not loaded |
||||
|
// while the other constants are already available from session extension. |
||||
|
if (!defined('LC_MESSAGES')) { |
||||
|
define('LC_MESSAGES', 5); |
||||
|
} |
||||
|
|
||||
|
require('streams.php'); |
||||
|
require('gettext.php'); |
||||
|
|
||||
|
|
||||
|
// Variables |
||||
|
|
||||
|
global $text_domains, $default_domain, $LC_CATEGORIES, $EMULATEGETTEXT, $CURRENTLOCALE; |
||||
|
$text_domains = array(); |
||||
|
$default_domain = 'messages'; |
||||
|
$LC_CATEGORIES = array('LC_CTYPE', 'LC_NUMERIC', 'LC_TIME', 'LC_COLLATE', 'LC_MONETARY', 'LC_MESSAGES', 'LC_ALL'); |
||||
|
$EMULATEGETTEXT = 0; |
||||
|
$CURRENTLOCALE = ''; |
||||
|
|
||||
|
/* Class to hold a single domain included in $text_domains. */ |
||||
|
class domain { |
||||
|
var $l10n; |
||||
|
var $path; |
||||
|
var $codeset; |
||||
|
} |
||||
|
|
||||
|
// Utility functions |
||||
|
|
||||
|
/** |
||||
|
* Return a list of locales to try for any POSIX-style locale specification. |
||||
|
*/ |
||||
|
function get_list_of_locales($locale) { |
||||
|
/* Figure out all possible locale names and start with the most |
||||
|
* specific ones. I.e. for sr_CS.UTF-8@latin, look through all of |
||||
|
* sr_CS.UTF-8@latin, sr_CS@latin, sr@latin, sr_CS.UTF-8, sr_CS, sr. |
||||
|
*/ |
||||
|
$locale_names = array(); |
||||
|
$lang = NULL; |
||||
|
$country = NULL; |
||||
|
$charset = NULL; |
||||
|
$modifier = NULL; |
||||
|
if ($locale) { |
||||
|
if (preg_match("/^(?P<lang>[a-z]{2,3})" // language code |
||||
|
."(?:_(?P<country>[A-Z]{2}))?" // country code |
||||
|
."(?:\.(?P<charset>[-A-Za-z0-9_]+))?" // charset |
||||
|
."(?:@(?P<modifier>[-A-Za-z0-9_]+))?$/", // @ modifier |
||||
|
$locale, $matches)) { |
||||
|
|
||||
|
if (isset($matches["lang"])) $lang = $matches["lang"]; |
||||
|
if (isset($matches["country"])) $country = $matches["country"]; |
||||
|
if (isset($matches["charset"])) $charset = $matches["charset"]; |
||||
|
if (isset($matches["modifier"])) $modifier = $matches["modifier"]; |
||||
|
|
||||
|
if ($modifier) { |
||||
|
if ($country) { |
||||
|
if ($charset) |
||||
|
array_push($locale_names, "${lang}_$country.$charset@$modifier"); |
||||
|
array_push($locale_names, "${lang}_$country@$modifier"); |
||||
|
} elseif ($charset) |
||||
|
array_push($locale_names, "${lang}.$charset@$modifier"); |
||||
|
array_push($locale_names, "$lang@$modifier"); |
||||
|
} |
||||
|
if ($country) { |
||||
|
if ($charset) |
||||
|
array_push($locale_names, "${lang}_$country.$charset"); |
||||
|
array_push($locale_names, "${lang}_$country"); |
||||
|
} elseif ($charset) |
||||
|
array_push($locale_names, "${lang}.$charset"); |
||||
|
array_push($locale_names, $lang); |
||||
|
} |
||||
|
|
||||
|
// If the locale name doesn't match POSIX style, just include it as-is. |
||||
|
if (!in_array($locale, $locale_names)) |
||||
|
array_push($locale_names, $locale); |
||||
|
} |
||||
|
return $locale_names; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Utility function to get a StreamReader for the given text domain. |
||||
|
*/ |
||||
|
function _get_reader($domain=null, $category=5, $enable_cache=true) { |
||||
|
global $text_domains, $default_domain, $LC_CATEGORIES; |
||||
|
if (!isset($domain)) $domain = $default_domain; |
||||
|
if (!isset($text_domains[$domain]->l10n)) { |
||||
|
// get the current locale |
||||
|
$locale = _setlocale(LC_MESSAGES, 0); |
||||
|
$bound_path = isset($text_domains[$domain]->path) ? |
||||
|
$text_domains[$domain]->path : './'; |
||||
|
$subpath = $LC_CATEGORIES[$category] ."/$domain.mo"; |
||||
|
|
||||
|
$locale_names = get_list_of_locales($locale); |
||||
|
$input = null; |
||||
|
foreach ($locale_names as $locale) { |
||||
|
$full_path = $bound_path . $locale . "/" . $subpath; |
||||
|
if (file_exists($full_path)) { |
||||
|
$input = new FileReader($full_path); |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (!array_key_exists($domain, $text_domains)) { |
||||
|
// Initialize an empty domain object. |
||||
|
$text_domains[$domain] = new domain(); |
||||
|
} |
||||
|
$text_domains[$domain]->l10n = new gettext_reader($input, |
||||
|
$enable_cache); |
||||
|
} |
||||
|
return $text_domains[$domain]->l10n; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Returns whether we are using our emulated gettext API or PHP built-in one. |
||||
|
*/ |
||||
|
function locale_emulation() { |
||||
|
global $EMULATEGETTEXT; |
||||
|
return $EMULATEGETTEXT; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Checks if the current locale is supported on this system. |
||||
|
*/ |
||||
|
function _check_locale_and_function($function=false) { |
||||
|
global $EMULATEGETTEXT; |
||||
|
if ($function and !function_exists($function)) |
||||
|
return false; |
||||
|
return !$EMULATEGETTEXT; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Get the codeset for the given domain. |
||||
|
*/ |
||||
|
function _get_codeset($domain=null) { |
||||
|
global $text_domains, $default_domain, $LC_CATEGORIES; |
||||
|
if (!isset($domain)) $domain = $default_domain; |
||||
|
return (isset($text_domains[$domain]->codeset))? $text_domains[$domain]->codeset : ini_get('mbstring.internal_encoding'); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Convert the given string to the encoding set by bind_textdomain_codeset. |
||||
|
*/ |
||||
|
function _encode($text) { |
||||
|
$source_encoding = mb_detect_encoding($text); |
||||
|
$target_encoding = _get_codeset(); |
||||
|
if ($source_encoding != $target_encoding) { |
||||
|
return mb_convert_encoding($text, $target_encoding, $source_encoding); |
||||
|
} |
||||
|
else { |
||||
|
return $text; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
|
// Custom implementation of the standard gettext related functions |
||||
|
|
||||
|
/** |
||||
|
* Returns passed in $locale, or environment variable $LANG if $locale == ''. |
||||
|
*/ |
||||
|
function _get_default_locale($locale) { |
||||
|
if ($locale == '') // emulate variable support |
||||
|
return getenv('LANG'); |
||||
|
else |
||||
|
return $locale; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Sets a requested locale, if needed emulates it. |
||||
|
*/ |
||||
|
function _setlocale($category, $locale) { |
||||
|
global $CURRENTLOCALE, $EMULATEGETTEXT; |
||||
|
if ($locale === 0) { // use === to differentiate between string "0" |
||||
|
if ($CURRENTLOCALE != '') |
||||
|
return $CURRENTLOCALE; |
||||
|
else |
||||
|
// obey LANG variable, maybe extend to support all of LC_* vars |
||||
|
// even if we tried to read locale without setting it first |
||||
|
return _setlocale($category, $CURRENTLOCALE); |
||||
|
} else { |
||||
|
if (function_exists('setlocale')) { |
||||
|
$ret = setlocale($category, $locale); |
||||
|
if (($locale == '' and !$ret) or // failed setting it by env |
||||
|
($locale != '' and $ret != $locale)) { // failed setting it |
||||
|
// Failed setting it according to environment. |
||||
|
$CURRENTLOCALE = _get_default_locale($locale); |
||||
|
$EMULATEGETTEXT = 1; |
||||
|
} else { |
||||
|
$CURRENTLOCALE = $ret; |
||||
|
$EMULATEGETTEXT = 0; |
||||
|
} |
||||
|
} else { |
||||
|
// No function setlocale(), emulate it all. |
||||
|
$CURRENTLOCALE = _get_default_locale($locale); |
||||
|
$EMULATEGETTEXT = 1; |
||||
|
} |
||||
|
// Allow locale to be changed on the go for one translation domain. |
||||
|
global $text_domains, $default_domain; |
||||
|
if (array_key_exists($default_domain, $text_domains)) { |
||||
|
unset($text_domains[$default_domain]->l10n); |
||||
|
} |
||||
|
return $CURRENTLOCALE; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Sets the path for a domain. |
||||
|
*/ |
||||
|
function _bindtextdomain($domain, $path) { |
||||
|
global $text_domains; |
||||
|
// ensure $path ends with a slash ('/' should work for both, but lets still play nice) |
||||
|
if (substr(php_uname(), 0, 7) == "Windows") { |
||||
|
if ($path[strlen($path)-1] != '\\' and $path[strlen($path)-1] != '/') |
||||
|
$path .= '\\'; |
||||
|
} else { |
||||
|
if ($path[strlen($path)-1] != '/') |
||||
|
$path .= '/'; |
||||
|
} |
||||
|
if (!array_key_exists($domain, $text_domains)) { |
||||
|
// Initialize an empty domain object. |
||||
|
$text_domains[$domain] = new domain(); |
||||
|
} |
||||
|
$text_domains[$domain]->path = $path; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Specify the character encoding in which the messages from the DOMAIN message catalog will be returned. |
||||
|
*/ |
||||
|
function _bind_textdomain_codeset($domain, $codeset) { |
||||
|
global $text_domains; |
||||
|
$text_domains[$domain]->codeset = $codeset; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Sets the default domain. |
||||
|
*/ |
||||
|
function _textdomain($domain) { |
||||
|
global $default_domain; |
||||
|
$default_domain = $domain; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Lookup a message in the current domain. |
||||
|
*/ |
||||
|
function _gettext($msgid) { |
||||
|
$l10n = _get_reader(); |
||||
|
return _encode($l10n->translate($msgid)); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Alias for gettext. |
||||
|
*/ |
||||
|
function __($msgid) { |
||||
|
return _gettext($msgid); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Plural version of gettext. |
||||
|
*/ |
||||
|
function _ngettext($singular, $plural, $number) { |
||||
|
$l10n = _get_reader(); |
||||
|
return _encode($l10n->ngettext($singular, $plural, $number)); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Override the current domain. |
||||
|
*/ |
||||
|
function _dgettext($domain, $msgid) { |
||||
|
$l10n = _get_reader($domain); |
||||
|
return _encode($l10n->translate($msgid)); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Plural version of dgettext. |
||||
|
*/ |
||||
|
function _dngettext($domain, $singular, $plural, $number) { |
||||
|
$l10n = _get_reader($domain); |
||||
|
return _encode($l10n->ngettext($singular, $plural, $number)); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Overrides the domain and category for a single lookup. |
||||
|
*/ |
||||
|
function _dcgettext($domain, $msgid, $category) { |
||||
|
$l10n = _get_reader($domain, $category); |
||||
|
return _encode($l10n->translate($msgid)); |
||||
|
} |
||||
|
/** |
||||
|
* Plural version of dcgettext. |
||||
|
*/ |
||||
|
function _dcngettext($domain, $singular, $plural, $number, $category) { |
||||
|
$l10n = _get_reader($domain, $category); |
||||
|
return _encode($l10n->ngettext($singular, $plural, $number)); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Context version of gettext. |
||||
|
*/ |
||||
|
function _pgettext($context, $msgid) { |
||||
|
$l10n = _get_reader(); |
||||
|
return _encode($l10n->pgettext($context, $msgid)); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Override the current domain in a context gettext call. |
||||
|
*/ |
||||
|
function _dpgettext($domain, $context, $msgid) { |
||||
|
$l10n = _get_reader($domain); |
||||
|
return _encode($l10n->pgettext($context, $msgid)); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Overrides the domain and category for a single context-based lookup. |
||||
|
*/ |
||||
|
function _dcpgettext($domain, $context, $msgid, $category) { |
||||
|
$l10n = _get_reader($domain, $category); |
||||
|
return _encode($l10n->pgettext($context, $msgid)); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Context version of ngettext. |
||||
|
*/ |
||||
|
function _npgettext($context, $singular, $plural) { |
||||
|
$l10n = _get_reader(); |
||||
|
return _encode($l10n->npgettext($context, $singular, $plural)); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Override the current domain in a context ngettext call. |
||||
|
*/ |
||||
|
function _dnpgettext($domain, $context, $singular, $plural) { |
||||
|
$l10n = _get_reader($domain); |
||||
|
return _encode($l10n->npgettext($context, $singular, $plural)); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Overrides the domain and category for a plural context-based lookup. |
||||
|
*/ |
||||
|
function _dcnpgettext($domain, $context, $singular, $plural, $category) { |
||||
|
$l10n = _get_reader($domain, $category); |
||||
|
return _encode($l10n->npgettext($context, $singular, $plural)); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
|
||||
|
// Wrappers to use if the standard gettext functions are available, |
||||
|
// but the current locale is not supported by the system. |
||||
|
// Use the standard impl if the current locale is supported, use the |
||||
|
// custom impl otherwise. |
||||
|
|
||||
|
function T_setlocale($category, $locale) { |
||||
|
return _setlocale($category, $locale); |
||||
|
} |
||||
|
|
||||
|
function T_bindtextdomain($domain, $path) { |
||||
|
if (_check_locale_and_function()) return bindtextdomain($domain, $path); |
||||
|
else return _bindtextdomain($domain, $path); |
||||
|
} |
||||
|
function T_bind_textdomain_codeset($domain, $codeset) { |
||||
|
// bind_textdomain_codeset is available only in PHP 4.2.0+ |
||||
|
if (_check_locale_and_function('bind_textdomain_codeset')) |
||||
|
return bind_textdomain_codeset($domain, $codeset); |
||||
|
else return _bind_textdomain_codeset($domain, $codeset); |
||||
|
} |
||||
|
function T_textdomain($domain) { |
||||
|
if (_check_locale_and_function()) return textdomain($domain); |
||||
|
else return _textdomain($domain); |
||||
|
} |
||||
|
function T_gettext($msgid) { |
||||
|
if (_check_locale_and_function()) return gettext($msgid); |
||||
|
else return _gettext($msgid); |
||||
|
} |
||||
|
function T_($msgid) { |
||||
|
if (_check_locale_and_function()) return _($msgid); |
||||
|
return __($msgid); |
||||
|
} |
||||
|
function T_ngettext($singular, $plural, $number) { |
||||
|
if (_check_locale_and_function()) |
||||
|
return ngettext($singular, $plural, $number); |
||||
|
else return _ngettext($singular, $plural, $number); |
||||
|
} |
||||
|
function T_dgettext($domain, $msgid) { |
||||
|
if (_check_locale_and_function()) return dgettext($domain, $msgid); |
||||
|
else return _dgettext($domain, $msgid); |
||||
|
} |
||||
|
function T_dngettext($domain, $singular, $plural, $number) { |
||||
|
if (_check_locale_and_function()) |
||||
|
return dngettext($domain, $singular, $plural, $number); |
||||
|
else return _dngettext($domain, $singular, $plural, $number); |
||||
|
} |
||||
|
function T_dcgettext($domain, $msgid, $category) { |
||||
|
if (_check_locale_and_function()) |
||||
|
return dcgettext($domain, $msgid, $category); |
||||
|
else return _dcgettext($domain, $msgid, $category); |
||||
|
} |
||||
|
function T_dcngettext($domain, $singular, $plural, $number, $category) { |
||||
|
if (_check_locale_and_function()) |
||||
|
return dcngettext($domain, $singular, $plural, $number, $category); |
||||
|
else return _dcngettext($domain, $singular, $plural, $number, $category); |
||||
|
} |
||||
|
|
||||
|
function T_pgettext($context, $msgid) { |
||||
|
if (_check_locale_and_function('pgettext')) |
||||
|
return pgettext($context, $msgid); |
||||
|
else |
||||
|
return _pgettext($context, $msgid); |
||||
|
} |
||||
|
|
||||
|
function T_dpgettext($domain, $context, $msgid) { |
||||
|
if (_check_locale_and_function('dpgettext')) |
||||
|
return dpgettext($domain, $context, $msgid); |
||||
|
else |
||||
|
return _dpgettext($domain, $context, $msgid); |
||||
|
} |
||||
|
|
||||
|
function T_dcpgettext($domain, $context, $msgid, $category) { |
||||
|
if (_check_locale_and_function('dcpgettext')) |
||||
|
return dcpgettext($domain, $context, $msgid, $category); |
||||
|
else |
||||
|
return _dcpgettext($domain, $context, $msgid, $category); |
||||
|
} |
||||
|
|
||||
|
function T_npgettext($context, $singular, $plural, $number) { |
||||
|
if (_check_locale_and_function('npgettext')) |
||||
|
return npgettext($context, $singular, $plural, $number); |
||||
|
else |
||||
|
return _npgettext($context, $singular, $plural, $number); |
||||
|
} |
||||
|
|
||||
|
function T_dnpgettext($domain, $context, $singular, $plural, $number) { |
||||
|
if (_check_locale_and_function('dnpgettext')) |
||||
|
return dnpgettext($domain, $context, $singular, $plural, $number); |
||||
|
else |
||||
|
return _dnpgettext($domain, $context, $singular, $plural, $number); |
||||
|
} |
||||
|
|
||||
|
function T_dcnpgettext($domain, $context, $singular, $plural, |
||||
|
$number, $category) { |
||||
|
if (_check_locale_and_function('dcnpgettext')) |
||||
|
return dcnpgettext($domain, $context, $singular, |
||||
|
$plural, $number, $category); |
||||
|
else |
||||
|
return _dcnpgettext($domain, $context, $singular, |
||||
|
$plural, $number, $category); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
|
||||
|
// Wrappers used as a drop in replacement for the standard gettext functions |
||||
|
|
||||
|
if (!function_exists('gettext')) { |
||||
|
function bindtextdomain($domain, $path) { |
||||
|
return _bindtextdomain($domain, $path); |
||||
|
} |
||||
|
function bind_textdomain_codeset($domain, $codeset) { |
||||
|
return _bind_textdomain_codeset($domain, $codeset); |
||||
|
} |
||||
|
function textdomain($domain) { |
||||
|
return _textdomain($domain); |
||||
|
} |
||||
|
function gettext($msgid) { |
||||
|
return _gettext($msgid); |
||||
|
} |
||||
|
function _($msgid) { |
||||
|
return __($msgid); |
||||
|
} |
||||
|
function ngettext($singular, $plural, $number) { |
||||
|
return _ngettext($singular, $plural, $number); |
||||
|
} |
||||
|
function dgettext($domain, $msgid) { |
||||
|
return _dgettext($domain, $msgid); |
||||
|
} |
||||
|
function dngettext($domain, $singular, $plural, $number) { |
||||
|
return _dngettext($domain, $singular, $plural, $number); |
||||
|
} |
||||
|
function dcgettext($domain, $msgid, $category) { |
||||
|
return _dcgettext($domain, $msgid, $category); |
||||
|
} |
||||
|
function dcngettext($domain, $singular, $plural, $number, $category) { |
||||
|
return _dcngettext($domain, $singular, $plural, $number, $category); |
||||
|
} |
||||
|
function pgettext($context, $msgid) { |
||||
|
return _pgettext($context, $msgid); |
||||
|
} |
||||
|
function npgettext($context, $singular, $plural, $number) { |
||||
|
return _npgettext($context, $singular, $plural, $number); |
||||
|
} |
||||
|
function dpgettext($domain, $context, $msgid) { |
||||
|
return _dpgettext($domain, $context, $msgid); |
||||
|
} |
||||
|
function dnpgettext($domain, $context, $singular, $plural, $number) { |
||||
|
return _dnpgettext($domain, $context, $singular, $plural, $number); |
||||
|
} |
||||
|
function dcpgettext($domain, $context, $msgid, $category) { |
||||
|
return _dcpgettext($domain, $context, $msgid, $category); |
||||
|
} |
||||
|
function dcnpgettext($domain, $context, $singular, $plural, |
||||
|
$number, $category) { |
||||
|
return _dcnpgettext($domain, $context, $singular, $plural, |
||||
|
$number, $category); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
?> |
@ -0,0 +1,432 @@ |
|||||
|
<?php |
||||
|
/* |
||||
|
Copyright (c) 2003, 2009 Danilo Segan <danilo@kvota.net>. |
||||
|
Copyright (c) 2005 Nico Kaiser <nico@siriux.net> |
||||
|
|
||||
|
This file is part of PHP-gettext. |
||||
|
|
||||
|
PHP-gettext is free software; you can redistribute it and/or modify |
||||
|
it under the terms of the GNU General Public License as published by |
||||
|
the Free Software Foundation; either version 2 of the License, or |
||||
|
(at your option) any later version. |
||||
|
|
||||
|
PHP-gettext is distributed in the hope that it will be useful, |
||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
|
GNU General Public License for more details. |
||||
|
|
||||
|
You should have received a copy of the GNU General Public License |
||||
|
along with PHP-gettext; if not, write to the Free Software |
||||
|
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
||||
|
|
||||
|
*/ |
||||
|
|
||||
|
/** |
||||
|
* Provides a simple gettext replacement that works independently from |
||||
|
* the system's gettext abilities. |
||||
|
* It can read MO files and use them for translating strings. |
||||
|
* The files are passed to gettext_reader as a Stream (see streams.php) |
||||
|
* |
||||
|
* This version has the ability to cache all strings and translations to |
||||
|
* speed up the string lookup. |
||||
|
* While the cache is enabled by default, it can be switched off with the |
||||
|
* second parameter in the constructor (e.g. whenusing very large MO files |
||||
|
* that you don't want to keep in memory) |
||||
|
*/ |
||||
|
class gettext_reader { |
||||
|
//public: |
||||
|
var $error = 0; // public variable that holds error code (0 if no error) |
||||
|
|
||||
|
//private: |
||||
|
var $BYTEORDER = 0; // 0: low endian, 1: big endian |
||||
|
var $STREAM = NULL; |
||||
|
var $short_circuit = false; |
||||
|
var $enable_cache = false; |
||||
|
var $originals = NULL; // offset of original table |
||||
|
var $translations = NULL; // offset of translation table |
||||
|
var $pluralheader = NULL; // cache header field for plural forms |
||||
|
var $total = 0; // total string count |
||||
|
var $table_originals = NULL; // table for original strings (offsets) |
||||
|
var $table_translations = NULL; // table for translated strings (offsets) |
||||
|
var $cache_translations = NULL; // original -> translation mapping |
||||
|
|
||||
|
|
||||
|
/* Methods */ |
||||
|
|
||||
|
|
||||
|
/** |
||||
|
* Reads a 32bit Integer from the Stream |
||||
|
* |
||||
|
* @access private |
||||
|
* @return Integer from the Stream |
||||
|
*/ |
||||
|
function readint() { |
||||
|
if ($this->BYTEORDER == 0) { |
||||
|
// low endian |
||||
|
$input=unpack('V', $this->STREAM->read(4)); |
||||
|
return array_shift($input); |
||||
|
} else { |
||||
|
// big endian |
||||
|
$input=unpack('N', $this->STREAM->read(4)); |
||||
|
return array_shift($input); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
function read($bytes) { |
||||
|
return $this->STREAM->read($bytes); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Reads an array of Integers from the Stream |
||||
|
* |
||||
|
* @param int count How many elements should be read |
||||
|
* @return Array of Integers |
||||
|
*/ |
||||
|
function readintarray($count) { |
||||
|
if ($this->BYTEORDER == 0) { |
||||
|
// low endian |
||||
|
return unpack('V'.$count, $this->STREAM->read(4 * $count)); |
||||
|
} else { |
||||
|
// big endian |
||||
|
return unpack('N'.$count, $this->STREAM->read(4 * $count)); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Constructor |
||||
|
* |
||||
|
* @param object Reader the StreamReader object |
||||
|
* @param boolean enable_cache Enable or disable caching of strings (default on) |
||||
|
*/ |
||||
|
function gettext_reader($Reader, $enable_cache = true) { |
||||
|
// If there isn't a StreamReader, turn on short circuit mode. |
||||
|
if (! $Reader || isset($Reader->error) ) { |
||||
|
$this->short_circuit = true; |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
// Caching can be turned off |
||||
|
$this->enable_cache = $enable_cache; |
||||
|
|
||||
|
$MAGIC1 = "\x95\x04\x12\xde"; |
||||
|
$MAGIC2 = "\xde\x12\x04\x95"; |
||||
|
|
||||
|
$this->STREAM = $Reader; |
||||
|
$magic = $this->read(4); |
||||
|
if ($magic == $MAGIC1) { |
||||
|
$this->BYTEORDER = 1; |
||||
|
} elseif ($magic == $MAGIC2) { |
||||
|
$this->BYTEORDER = 0; |
||||
|
} else { |
||||
|
$this->error = 1; // not MO file |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
// FIXME: Do we care about revision? We should. |
||||
|
$revision = $this->readint(); |
||||
|
|
||||
|
$this->total = $this->readint(); |
||||
|
$this->originals = $this->readint(); |
||||
|
$this->translations = $this->readint(); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Loads the translation tables from the MO file into the cache |
||||
|
* If caching is enabled, also loads all strings into a cache |
||||
|
* to speed up translation lookups |
||||
|
* |
||||
|
* @access private |
||||
|
*/ |
||||
|
function load_tables() { |
||||
|
if (is_array($this->cache_translations) && |
||||
|
is_array($this->table_originals) && |
||||
|
is_array($this->table_translations)) |
||||
|
return; |
||||
|
|
||||
|
/* get original and translations tables */ |
||||
|
if (!is_array($this->table_originals)) { |
||||
|
$this->STREAM->seekto($this->originals); |
||||
|
$this->table_originals = $this->readintarray($this->total * 2); |
||||
|
} |
||||
|
if (!is_array($this->table_translations)) { |
||||
|
$this->STREAM->seekto($this->translations); |
||||
|
$this->table_translations = $this->readintarray($this->total * 2); |
||||
|
} |
||||
|
|
||||
|
if ($this->enable_cache) { |
||||
|
$this->cache_translations = array (); |
||||
|
/* read all strings in the cache */ |
||||
|
for ($i = 0; $i < $this->total; $i++) { |
||||
|
$this->STREAM->seekto($this->table_originals[$i * 2 + 2]); |
||||
|
$original = $this->STREAM->read($this->table_originals[$i * 2 + 1]); |
||||
|
$this->STREAM->seekto($this->table_translations[$i * 2 + 2]); |
||||
|
$translation = $this->STREAM->read($this->table_translations[$i * 2 + 1]); |
||||
|
$this->cache_translations[$original] = $translation; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Returns a string from the "originals" table |
||||
|
* |
||||
|
* @access private |
||||
|
* @param int num Offset number of original string |
||||
|
* @return string Requested string if found, otherwise '' |
||||
|
*/ |
||||
|
function get_original_string($num) { |
||||
|
$length = $this->table_originals[$num * 2 + 1]; |
||||
|
$offset = $this->table_originals[$num * 2 + 2]; |
||||
|
if (! $length) |
||||
|
return ''; |
||||
|
$this->STREAM->seekto($offset); |
||||
|
$data = $this->STREAM->read($length); |
||||
|
return (string)$data; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Returns a string from the "translations" table |
||||
|
* |
||||
|
* @access private |
||||
|
* @param int num Offset number of original string |
||||
|
* @return string Requested string if found, otherwise '' |
||||
|
*/ |
||||
|
function get_translation_string($num) { |
||||
|
$length = $this->table_translations[$num * 2 + 1]; |
||||
|
$offset = $this->table_translations[$num * 2 + 2]; |
||||
|
if (! $length) |
||||
|
return ''; |
||||
|
$this->STREAM->seekto($offset); |
||||
|
$data = $this->STREAM->read($length); |
||||
|
return (string)$data; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Binary search for string |
||||
|
* |
||||
|
* @access private |
||||
|
* @param string string |
||||
|
* @param int start (internally used in recursive function) |
||||
|
* @param int end (internally used in recursive function) |
||||
|
* @return int string number (offset in originals table) |
||||
|
*/ |
||||
|
function find_string($string, $start = -1, $end = -1) { |
||||
|
if (($start == -1) or ($end == -1)) { |
||||
|
// find_string is called with only one parameter, set start end end |
||||
|
$start = 0; |
||||
|
$end = $this->total; |
||||
|
} |
||||
|
if (abs($start - $end) <= 1) { |
||||
|
// We're done, now we either found the string, or it doesn't exist |
||||
|
$txt = $this->get_original_string($start); |
||||
|
if ($string == $txt) |
||||
|
return $start; |
||||
|
else |
||||
|
return -1; |
||||
|
} else if ($start > $end) { |
||||
|
// start > end -> turn around and start over |
||||
|
return $this->find_string($string, $end, $start); |
||||
|
} else { |
||||
|
// Divide table in two parts |
||||
|
$half = (int)(($start + $end) / 2); |
||||
|
$cmp = strcmp($string, $this->get_original_string($half)); |
||||
|
if ($cmp == 0) |
||||
|
// string is exactly in the middle => return it |
||||
|
return $half; |
||||
|
else if ($cmp < 0) |
||||
|
// The string is in the upper half |
||||
|
return $this->find_string($string, $start, $half); |
||||
|
else |
||||
|
// The string is in the lower half |
||||
|
return $this->find_string($string, $half, $end); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Translates a string |
||||
|
* |
||||
|
* @access public |
||||
|
* @param string string to be translated |
||||
|
* @return string translated string (or original, if not found) |
||||
|
*/ |
||||
|
function translate($string) { |
||||
|
if ($this->short_circuit) |
||||
|
return $string; |
||||
|
$this->load_tables(); |
||||
|
|
||||
|
if ($this->enable_cache) { |
||||
|
// Caching enabled, get translated string from cache |
||||
|
if (array_key_exists($string, $this->cache_translations)) |
||||
|
return $this->cache_translations[$string]; |
||||
|
else |
||||
|
return $string; |
||||
|
} else { |
||||
|
// Caching not enabled, try to find string |
||||
|
$num = $this->find_string($string); |
||||
|
if ($num == -1) |
||||
|
return $string; |
||||
|
else |
||||
|
return $this->get_translation_string($num); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Sanitize plural form expression for use in PHP eval call. |
||||
|
* |
||||
|
* @access private |
||||
|
* @return string sanitized plural form expression |
||||
|
*/ |
||||
|
function sanitize_plural_expression($expr) { |
||||
|
// Get rid of disallowed characters. |
||||
|
$expr = preg_replace('@[^a-zA-Z0-9_:;\(\)\?\|\&=!<>+*/\%-]@', '', $expr); |
||||
|
|
||||
|
// Add parenthesis for tertiary '?' operator. |
||||
|
$expr .= ';'; |
||||
|
$res = ''; |
||||
|
$p = 0; |
||||
|
for ($i = 0; $i < strlen($expr); $i++) { |
||||
|
$ch = $expr[$i]; |
||||
|
switch ($ch) { |
||||
|
case '?': |
||||
|
$res .= ' ? ('; |
||||
|
$p++; |
||||
|
break; |
||||
|
case ':': |
||||
|
$res .= ') : ('; |
||||
|
break; |
||||
|
case ';': |
||||
|
$res .= str_repeat( ')', $p) . ';'; |
||||
|
$p = 0; |
||||
|
break; |
||||
|
default: |
||||
|
$res .= $ch; |
||||
|
} |
||||
|
} |
||||
|
return $res; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Parse full PO header and extract only plural forms line. |
||||
|
* |
||||
|
* @access private |
||||
|
* @return string verbatim plural form header field |
||||
|
*/ |
||||
|
function extract_plural_forms_header_from_po_header($header) { |
||||
|
if (preg_match("/(^|\n)plural-forms: ([^\n]*)\n/i", $header, $regs)) |
||||
|
$expr = $regs[2]; |
||||
|
else |
||||
|
$expr = "nplurals=2; plural=n == 1 ? 0 : 1;"; |
||||
|
return $expr; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Get possible plural forms from MO header |
||||
|
* |
||||
|
* @access private |
||||
|
* @return string plural form header |
||||
|
*/ |
||||
|
function get_plural_forms() { |
||||
|
// lets assume message number 0 is header |
||||
|
// this is true, right? |
||||
|
$this->load_tables(); |
||||
|
|
||||
|
// cache header field for plural forms |
||||
|
if (! is_string($this->pluralheader)) { |
||||
|
if ($this->enable_cache) { |
||||
|
$header = $this->cache_translations[""]; |
||||
|
} else { |
||||
|
$header = $this->get_translation_string(0); |
||||
|
} |
||||
|
$expr = $this->extract_plural_forms_header_from_po_header($header); |
||||
|
$this->pluralheader = $this->sanitize_plural_expression($expr); |
||||
|
} |
||||
|
return $this->pluralheader; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Detects which plural form to take |
||||
|
* |
||||
|
* @access private |
||||
|
* @param n count |
||||
|
* @return int array index of the right plural form |
||||
|
*/ |
||||
|
function select_string($n) { |
||||
|
$string = $this->get_plural_forms(); |
||||
|
$string = str_replace('nplurals',"\$total",$string); |
||||
|
$string = str_replace("n",$n,$string); |
||||
|
$string = str_replace('plural',"\$plural",$string); |
||||
|
|
||||
|
$total = 0; |
||||
|
$plural = 0; |
||||
|
|
||||
|
eval("$string"); |
||||
|
if ($plural >= $total) $plural = $total - 1; |
||||
|
return $plural; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Plural version of gettext |
||||
|
* |
||||
|
* @access public |
||||
|
* @param string single |
||||
|
* @param string plural |
||||
|
* @param string number |
||||
|
* @return translated plural form |
||||
|
*/ |
||||
|
function ngettext($single, $plural, $number) { |
||||
|
if ($this->short_circuit) { |
||||
|
if ($number != 1) |
||||
|
return $plural; |
||||
|
else |
||||
|
return $single; |
||||
|
} |
||||
|
|
||||
|
// find out the appropriate form |
||||
|
$select = $this->select_string($number); |
||||
|
|
||||
|
// this should contains all strings separated by NULLs |
||||
|
$key = $single . chr(0) . $plural; |
||||
|
|
||||
|
|
||||
|
if ($this->enable_cache) { |
||||
|
if (! array_key_exists($key, $this->cache_translations)) { |
||||
|
return ($number != 1) ? $plural : $single; |
||||
|
} else { |
||||
|
$result = $this->cache_translations[$key]; |
||||
|
$list = explode(chr(0), $result); |
||||
|
return $list[$select]; |
||||
|
} |
||||
|
} else { |
||||
|
$num = $this->find_string($key); |
||||
|
if ($num == -1) { |
||||
|
return ($number != 1) ? $plural : $single; |
||||
|
} else { |
||||
|
$result = $this->get_translation_string($num); |
||||
|
$list = explode(chr(0), $result); |
||||
|
return $list[$select]; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
function pgettext($context, $msgid) { |
||||
|
$key = $context . chr(4) . $msgid; |
||||
|
$ret = $this->translate($key); |
||||
|
if (strpos($ret, "\004") !== FALSE) { |
||||
|
return $msgid; |
||||
|
} else { |
||||
|
return $ret; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
function npgettext($context, $singular, $plural, $number) { |
||||
|
$key = $context . chr(4) . $singular; |
||||
|
$ret = $this->ngettext($key, $plural, $number); |
||||
|
if (strpos($ret, "\004") !== FALSE) { |
||||
|
return $singular; |
||||
|
} else { |
||||
|
return $ret; |
||||
|
} |
||||
|
|
||||
|
} |
||||
|
} |
||||
|
|
||||
|
?> |
@ -0,0 +1,167 @@ |
|||||
|
<?php |
||||
|
/* |
||||
|
Copyright (c) 2003, 2005, 2006, 2009 Danilo Segan <danilo@kvota.net>. |
||||
|
|
||||
|
This file is part of PHP-gettext. |
||||
|
|
||||
|
PHP-gettext is free software; you can redistribute it and/or modify |
||||
|
it under the terms of the GNU General Public License as published by |
||||
|
the Free Software Foundation; either version 2 of the License, or |
||||
|
(at your option) any later version. |
||||
|
|
||||
|
PHP-gettext is distributed in the hope that it will be useful, |
||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
|
GNU General Public License for more details. |
||||
|
|
||||
|
You should have received a copy of the GNU General Public License |
||||
|
along with PHP-gettext; if not, write to the Free Software |
||||
|
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
||||
|
|
||||
|
*/ |
||||
|
|
||||
|
|
||||
|
// Simple class to wrap file streams, string streams, etc. |
||||
|
// seek is essential, and it should be byte stream |
||||
|
class StreamReader { |
||||
|
// should return a string [FIXME: perhaps return array of bytes?] |
||||
|
function read($bytes) { |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
// should return new position |
||||
|
function seekto($position) { |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
// returns current position |
||||
|
function currentpos() { |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
// returns length of entire stream (limit for seekto()s) |
||||
|
function length() { |
||||
|
return false; |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
class StringReader { |
||||
|
var $_pos; |
||||
|
var $_str; |
||||
|
|
||||
|
function StringReader($str='') { |
||||
|
$this->_str = $str; |
||||
|
$this->_pos = 0; |
||||
|
} |
||||
|
|
||||
|
function read($bytes) { |
||||
|
$data = substr($this->_str, $this->_pos, $bytes); |
||||
|
$this->_pos += $bytes; |
||||
|
if (strlen($this->_str)<$this->_pos) |
||||
|
$this->_pos = strlen($this->_str); |
||||
|
|
||||
|
return $data; |
||||
|
} |
||||
|
|
||||
|
function seekto($pos) { |
||||
|
$this->_pos = $pos; |
||||
|
if (strlen($this->_str)<$this->_pos) |
||||
|
$this->_pos = strlen($this->_str); |
||||
|
return $this->_pos; |
||||
|
} |
||||
|
|
||||
|
function currentpos() { |
||||
|
return $this->_pos; |
||||
|
} |
||||
|
|
||||
|
function length() { |
||||
|
return strlen($this->_str); |
||||
|
} |
||||
|
|
||||
|
}; |
||||
|
|
||||
|
|
||||
|
class FileReader { |
||||
|
var $_pos; |
||||
|
var $_fd; |
||||
|
var $_length; |
||||
|
|
||||
|
function FileReader($filename) { |
||||
|
if (file_exists($filename)) { |
||||
|
|
||||
|
$this->_length=filesize($filename); |
||||
|
$this->_pos = 0; |
||||
|
$this->_fd = fopen($filename,'rb'); |
||||
|
if (!$this->_fd) { |
||||
|
$this->error = 3; // Cannot read file, probably permissions |
||||
|
return false; |
||||
|
} |
||||
|
} else { |
||||
|
$this->error = 2; // File doesn't exist |
||||
|
return false; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
function read($bytes) { |
||||
|
if ($bytes) { |
||||
|
fseek($this->_fd, $this->_pos); |
||||
|
|
||||
|
// PHP 5.1.1 does not read more than 8192 bytes in one fread() |
||||
|
// the discussions at PHP Bugs suggest it's the intended behaviour |
||||
|
$data = ''; |
||||
|
while ($bytes > 0) { |
||||
|
$chunk = fread($this->_fd, $bytes); |
||||
|
$data .= $chunk; |
||||
|
$bytes -= strlen($chunk); |
||||
|
} |
||||
|
$this->_pos = ftell($this->_fd); |
||||
|
|
||||
|
return $data; |
||||
|
} else return ''; |
||||
|
} |
||||
|
|
||||
|
function seekto($pos) { |
||||
|
fseek($this->_fd, $pos); |
||||
|
$this->_pos = ftell($this->_fd); |
||||
|
return $this->_pos; |
||||
|
} |
||||
|
|
||||
|
function currentpos() { |
||||
|
return $this->_pos; |
||||
|
} |
||||
|
|
||||
|
function length() { |
||||
|
return $this->_length; |
||||
|
} |
||||
|
|
||||
|
function close() { |
||||
|
fclose($this->_fd); |
||||
|
} |
||||
|
|
||||
|
}; |
||||
|
|
||||
|
// Preloads entire file in memory first, then creates a StringReader |
||||
|
// over it (it assumes knowledge of StringReader internals) |
||||
|
class CachedFileReader extends StringReader { |
||||
|
function CachedFileReader($filename) { |
||||
|
if (file_exists($filename)) { |
||||
|
|
||||
|
$length=filesize($filename); |
||||
|
$fd = fopen($filename,'rb'); |
||||
|
|
||||
|
if (!$fd) { |
||||
|
$this->error = 3; // Cannot read file, probably permissions |
||||
|
return false; |
||||
|
} |
||||
|
$this->_str = fread($fd, $length); |
||||
|
fclose($fd); |
||||
|
|
||||
|
} else { |
||||
|
$this->error = 2; // File doesn't exist |
||||
|
return false; |
||||
|
} |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
|
||||
|
?> |
@ -0,0 +1,75 @@ |
|||||
|
<?php |
||||
|
require_once('PHPUnit/Framework.php'); |
||||
|
require_once('gettext.inc'); |
||||
|
|
||||
|
class LocaleTest extends PHPUnit_Framework_TestCase |
||||
|
{ |
||||
|
public function test_setlocale() |
||||
|
{ |
||||
|
putenv("LC_ALL="); |
||||
|
// _setlocale defaults to a locale name from environment variable LANG. |
||||
|
putenv("LANG=sr_RS"); |
||||
|
$this->assertEquals('sr_RS', _setlocale(LC_MESSAGES, 0)); |
||||
|
} |
||||
|
|
||||
|
public function test_setlocale_system() |
||||
|
{ |
||||
|
putenv("LC_ALL="); |
||||
|
// For an existing locale, it never needs emulation. |
||||
|
putenv("LANG=C"); |
||||
|
_setlocale(LC_MESSAGES, ""); |
||||
|
$this->assertEquals(0, locale_emulation()); |
||||
|
} |
||||
|
|
||||
|
public function test_setlocale_emulation() |
||||
|
{ |
||||
|
putenv("LC_ALL="); |
||||
|
// If we set it to a non-existent locale, it still works, but uses |
||||
|
// emulation. |
||||
|
_setlocale(LC_MESSAGES, "xxx_XXX"); |
||||
|
$this->assertEquals('xxx_XXX', _setlocale(LC_MESSAGES, 0)); |
||||
|
$this->assertEquals(1, locale_emulation()); |
||||
|
} |
||||
|
|
||||
|
public function test_get_list_of_locales() |
||||
|
{ |
||||
|
// For a locale containing country code, we prefer |
||||
|
// full locale name, but if that's not found, fall back |
||||
|
// to the language only locale name. |
||||
|
$this->assertEquals(array("sr_RS", "sr"), |
||||
|
get_list_of_locales("sr_RS")); |
||||
|
|
||||
|
// If language code is used, it's the only thing returned. |
||||
|
$this->assertEquals(array("sr"), |
||||
|
get_list_of_locales("sr")); |
||||
|
|
||||
|
// There is support for language and charset only. |
||||
|
$this->assertEquals(array("sr.UTF-8", "sr"), |
||||
|
get_list_of_locales("sr.UTF-8")); |
||||
|
|
||||
|
// It can also split out character set from the full locale name. |
||||
|
$this->assertEquals(array("sr_RS.UTF-8", "sr_RS", "sr"), |
||||
|
get_list_of_locales("sr_RS.UTF-8")); |
||||
|
|
||||
|
// There is support for @modifier in locale names as well. |
||||
|
$this->assertEquals(array("sr_RS.UTF-8@latin", "sr_RS@latin", "sr@latin", |
||||
|
"sr_RS.UTF-8", "sr_RS", "sr"), |
||||
|
get_list_of_locales("sr_RS.UTF-8@latin")); |
||||
|
|
||||
|
// We can pass in only language and modifier. |
||||
|
$this->assertEquals(array("sr@latin", "sr"), |
||||
|
get_list_of_locales("sr@latin")); |
||||
|
|
||||
|
|
||||
|
// If locale name is not following the regular POSIX pattern, |
||||
|
// it's used verbatim. |
||||
|
$this->assertEquals(array("something"), |
||||
|
get_list_of_locales("something")); |
||||
|
|
||||
|
// Passing in an empty string returns an empty array. |
||||
|
$this->assertEquals(array(), |
||||
|
get_list_of_locales("")); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
?> |
@ -0,0 +1,60 @@ |
|||||
|
<?php |
||||
|
require_once('PHPUnit/Framework.php'); |
||||
|
//require_once('gettext.php'); |
||||
|
|
||||
|
class ParsingTest extends PHPUnit_Framework_TestCase |
||||
|
{ |
||||
|
public function test_extract_plural_forms_header_from_po_header() |
||||
|
{ |
||||
|
$parser = new gettext_reader(NULL); |
||||
|
// It defaults to a "Western-style" plural header. |
||||
|
$this->assertEquals( |
||||
|
'nplurals=2; plural=n == 1 ? 0 : 1;', |
||||
|
$parser->extract_plural_forms_header_from_po_header("")); |
||||
|
|
||||
|
// Extracting it from the middle of the header works. |
||||
|
$this->assertEquals( |
||||
|
'nplurals=1; plural=0;', |
||||
|
$parser->extract_plural_forms_header_from_po_header( |
||||
|
"Content-type: text/html; charset=UTF-8\n" |
||||
|
."Plural-Forms: nplurals=1; plural=0;\n" |
||||
|
."Last-Translator: nobody\n" |
||||
|
)); |
||||
|
|
||||
|
// It's also case-insensitive. |
||||
|
$this->assertEquals( |
||||
|
'nplurals=1; plural=0;', |
||||
|
$parser->extract_plural_forms_header_from_po_header( |
||||
|
"PLURAL-forms: nplurals=1; plural=0;\n" |
||||
|
)); |
||||
|
|
||||
|
// It falls back to default if it's not on a separate line. |
||||
|
$this->assertEquals( |
||||
|
'nplurals=2; plural=n == 1 ? 0 : 1;', |
||||
|
$parser->extract_plural_forms_header_from_po_header( |
||||
|
"Content-type: text/html; charset=UTF-8" // note the missing \n here |
||||
|
."Plural-Forms: nplurals=1; plural=0;\n" |
||||
|
."Last-Translator: nobody\n" |
||||
|
)); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* @dataProvider data_provider_test_npgettext |
||||
|
*/ |
||||
|
public function test_npgettext($number, $expected) { |
||||
|
$parser = new gettext_reader(NULL); |
||||
|
$result = $parser->npgettext("context", |
||||
|
"%d pig went to the market\n", |
||||
|
"%d pigs went to the market\n", |
||||
|
$number); |
||||
|
$this->assertSame($expected, $result); |
||||
|
} |
||||
|
public static function data_provider_test_npgettext() { |
||||
|
return array( |
||||
|
array(1, "%d pig went to the market\n"), |
||||
|
array(2, "%d pigs went to the market\n"), |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
} |
||||
|
?> |
Binary file not shown.
@ -0,0 +1,86 @@ |
|||||
|
# SOME DESCRIPTIVE TITLE. |
||||
|
# Copyright (C) YEAR Codegroove.net |
||||
|
# This file is distributed under the same license as the PACKAGE package. |
||||
|
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR. |
||||
|
# |
||||
|
#, fuzzy |
||||
|
msgid "" |
||||
|
msgstr "Project-Id-Version: Tinyboard i18n\n" |
||||
|
"Report-Msgid-Bugs-To: [email protected]\n" |
||||
|
"POT-Creation-Date: 2010-05-28 06:18-0500\n" |
||||
|
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" |
||||
|
"Last-Translator: Tinyboard <[email protected]>\n" |
||||
|
"Language-Team: Tinyboard <[email protected]>\n" |
||||
|
"MIME-Version: 1.0\n" |
||||
|
"Content-Type: text/plain; charset=UTF-8\n" |
||||
|
"Content-Transfer-Encoding: 8bit\n" |
||||
|
|
||||
|
msgid "Name" |
||||
|
msgstr "Ник" |
||||
|
|
||||
|
msgid "Email" |
||||
|
msgstr "Адрес" |
||||
|
|
||||
|
msgid "Subject" |
||||
|
msgstr "Тема" |
||||
|
|
||||
|
msgid "Comment" |
||||
|
msgstr "комментарий" |
||||
|
|
||||
|
msgid "Verification" |
||||
|
msgstr "проверка" |
||||
|
|
||||
|
msgid "File" |
||||
|
msgstr "Файл" |
||||
|
|
||||
|
msgid "Embed" |
||||
|
msgstr "вставлять" |
||||
|
|
||||
|
msgid "Flags" |
||||
|
msgstr "Флаги" |
||||
|
|
||||
|
msgid "Password" |
||||
|
msgstr "Пaроль" |
||||
|
|
||||
|
msgid "(For file deletion.)" |
||||
|
msgstr "(Для удаления файлов.)" |
||||
|
|
||||
|
msgid "Spoiler Image" |
||||
|
msgstr "Спойлер изображение" |
||||
|
|
||||
|
msgid "Return" |
||||
|
msgstr "возвращение" |
||||
|
|
||||
|
msgid "Posting mode: Reply" |
||||
|
msgstr "Режим публикации: Ответить" |
||||
|
|
||||
|
msgid "Reply" |
||||
|
msgstr "Ответить" |
||||
|
|
||||
|
msgid "Sticky" |
||||
|
msgstr "липкий" |
||||
|
|
||||
|
msgid "Lock" |
||||
|
msgstr "Блокировка" |
||||
|
|
||||
|
msgid "Raw HTML" |
||||
|
msgstr "Сырье HTML" |
||||
|
|
||||
|
msgid "Delete Post" |
||||
|
msgstr "удалить сообщение" |
||||
|
|
||||
|
msgid "Delete" |
||||
|
msgstr "удалять" |
||||
|
|
||||
|
msgid "Reason" |
||||
|
msgstr "причина" |
||||
|
|
||||
|
msgid "Report" |
||||
|
msgstr "отчет" |
||||
|
|
||||
|
msgid "Previous" |
||||
|
msgstr "предыдущий" |
||||
|
|
||||
|
msgid "Next" |
||||
|
msgstr "следующий" |
||||
|
|
Loading…
Reference in new issue