From 58dee1a55ac120d8013f15c5a08c13ff9472688a Mon Sep 17 00:00:00 2001 From: JcDenis Date: Wed, 18 Aug 2021 21:42:30 +0200 Subject: [PATCH] Init --- LICENSE | 339 ++++++ _admin.php | 107 ++ _define.php | 24 + _install.php | 59 ++ _prepend.php | 26 + _uninstall.php | 59 ++ icon.png | Bin 0 -> 691 bytes inc/class.dc.translater.php | 1585 ++++++++++++++++++++++++++++ inc/class.translater.proposals.php | 64 ++ inc/class.translater.rest.php | 76 ++ inc/img/field.png | Bin 0 -> 321 bytes inc/img/toggle.png | Bin 0 -> 209 bytes inc/lib.translater.google.php | 97 ++ inc/lib.translater.microsoft.php | 225 ++++ inc/lib.translater.proposal.php | 125 +++ index.php | 1386 ++++++++++++++++++++++++ js/jquery.translater.js | 91 ++ locales/fr/help/help.html | 174 +++ locales/fr/main.lang.php | 402 +++++++ locales/fr/main.po | 537 ++++++++++ locales/fr/resources.php | 16 + release.txt | 86 ++ 22 files changed, 5478 insertions(+) create mode 100644 LICENSE create mode 100644 _admin.php create mode 100644 _define.php create mode 100644 _install.php create mode 100644 _prepend.php create mode 100644 _uninstall.php create mode 100644 icon.png create mode 100644 inc/class.dc.translater.php create mode 100644 inc/class.translater.proposals.php create mode 100644 inc/class.translater.rest.php create mode 100644 inc/img/field.png create mode 100644 inc/img/toggle.png create mode 100644 inc/lib.translater.google.php create mode 100644 inc/lib.translater.microsoft.php create mode 100644 inc/lib.translater.proposal.php create mode 100644 index.php create mode 100644 js/jquery.translater.js create mode 100644 locales/fr/help/help.html create mode 100644 locales/fr/main.lang.php create mode 100644 locales/fr/main.po create mode 100644 locales/fr/resources.php create mode 100644 release.txt diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..d511905 --- /dev/null +++ b/LICENSE @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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 Lesser 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. + + + Copyright (C) + + 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., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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. + + , 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 Lesser General +Public License instead of this License. diff --git a/_admin.php b/_admin.php new file mode 100644 index 0000000..db7739c --- /dev/null +++ b/_admin.php @@ -0,0 +1,107 @@ +blog->settings->addNamespace('translater'); +$core->addBehavior('pluginsToolsTabs',array('translaterAdminBehaviors','pluginsToolsTabs')); +$core->addBehavior('adminCurrentThemeDetails',array('translaterAdminBehaviors','adminCurrentThemeDetails')); +$core->addBehavior('addTranslaterProposalTool',array('translaterAdminBehaviors','addGoogleProposalTool')); +$core->addBehavior('addTranslaterProposalTool',array('translaterAdminBehaviors','addYahooProposalTool')); +$core->addBehavior('addTranslaterProposalTool',array('translaterAdminBehaviors','addMicrosoftProposalTool')); +$core->rest->addFunction('getProposal',array('translaterRest','getProposal')); + +$_menu['Plugins']->addItem( + __('Translater'), + 'plugin.php?p=translater', + 'index.php?pf=translater/icon.png', + preg_match('/plugin.php\?p=translater(&.*)?$/',$_SERVER['REQUEST_URI']), + $core->auth->isSuperAdmin() +); + +class translaterAdminBehaviors +{ + # Plugins tab + public static function pluginsToolsTabs($core) + { + if (!$core->blog->settings->translater->translater_plugin_menu || !$core->auth->isSuperAdmin()) { + return; + } + + echo + '
'. + ''. + ''. + ''. + ''. + ''. + ''. + ''; + + $modules = $core->plugins->getModules(); + + foreach ($modules as $name => $plugin) + { + echo + ''. + ''. + ''. + ''. + ''. + ''. + ''; + } + echo '
 '.__('Name').''.__('Version').''.__('Details').''.__('Author').'
'. + ''.__($plugin['name']).''.$name.''.$plugin['version'].''.$plugin['desc'].''.$plugin['author'].'
'; + } + + # Themes menu + public static function adminCurrentThemeDetails($core,$id,$infos) + { + if (!$core->blog->settings->translater->translater_theme_menu || !$core->auth->isSuperAdmin()) { + return; + } + + $root = path::real($infos['root']); + + if ($id != 'default' && is_dir($root.'/locales')) + { + return + '

'.__('Translate this theme').'

'; + } + } + + # Google Translater tools + public static function addGoogleProposalTool($proposal) + { + $proposal->addTool('googleProposalTool'); + } + + # Yahoo Babelfish tools + public static function addYahooProposalTool($proposal) + { + $proposal->addTool('yahooProposalTool'); + } + + # Microsoft Bing tools + public static function addMicrosoftProposalTool($proposal) + { + $proposal->addTool('microsoftProposalTool'); + } +} +?> \ No newline at end of file diff --git a/_define.php b/_define.php new file mode 100644 index 0000000..6a23ca1 --- /dev/null +++ b/_define.php @@ -0,0 +1,24 @@ +registerModule( + /* Name */ "translater", + /* Description*/ "Translate your Dotclear plugins and themes", + /* Author */ "JC Denis", + /* Version */ '2013.05.11', + /* Permissions */ null +); +?> \ No newline at end of file diff --git a/_install.php b/_install.php new file mode 100644 index 0000000..35cac0d --- /dev/null +++ b/_install.php @@ -0,0 +1,59 @@ +plugins->moduleInfo('translater','version'); +$old_version = $core->getVersion('translater'); + +if (version_compare($old_version,$new_version,'>=')) return; + +try +{ + if (version_compare(str_replace("-r","-p",DC_VERSION),$rdc_version,'<')) { + throw new Exception(sprintf('%s requires Dotclear %s','translater',$rdc_version)); + } + + $core->blog->settings->addNamespace('translater'); + + $core->blog->settings->translater->put('translater_plugin_menu',0,'boolean','Put a link in plugins page',false,true); + $core->blog->settings->translater->put('translater_theme_menu',0,'boolean','Put a link in themes page',false,true); + $core->blog->settings->translater->put('translater_backup_auto',1,'boolean','Make a backup of languages old files when there are modified',false,true); + $core->blog->settings->translater->put('translater_backup_limit',20,'string','Maximum backups per module',false,true); + $core->blog->settings->translater->put('translater_backup_folder','module','string','In which folder to store backups',false,true); + $core->blog->settings->translater->put('translater_start_page','setting,','string','Page to start on',false,true); + $core->blog->settings->translater->put('translater_write_po',1,'boolean','Write .po languages files',false,true); + $core->blog->settings->translater->put('translater_write_langphp',1,'boolean','Write .lang.php languages files',false,true); + $core->blog->settings->translater->put('translater_scan_tpl',0,'boolean','Translate strings of templates files',false,true); + $core->blog->settings->translater->put('translater_parse_nodc',1,'boolean','Translate only untranslated strings of Dotclear',false,true); + $core->blog->settings->translater->put('translater_hide_default',1,'boolean','Hide default modules of Dotclear',false,true); + $core->blog->settings->translater->put('translater_parse_comment',1,'boolean','Write comments and strings informations in lang files',false,true); + $core->blog->settings->translater->put('translater_parse_user',1,'boolean','Write inforamtions about author in lang files',false,true); + $core->blog->settings->translater->put('translater_parse_userinfo','displayname, email','string','Type of informations about user to write',false,true); + $core->blog->settings->translater->put('translater_import_overwrite',0,'boolean','Overwrite existing languages when import packages',false,true); + $core->blog->settings->translater->put('translater_export_filename','type-module-l10n-timestamp','string','Name of files of exported package',false,true); + $core->blog->settings->translater->put('translater_proposal_tool','google','string','Id of default tool for proposed translation',false,true); + $core->blog->settings->translater->put('translater_proposal_lang','en','string','Default source language for proposed translation',false,true); + + $core->setVersion('translater',$new_version); + + return true; +} +catch (Exception $e) +{ + $core->error->add($e->getMessage()); +} +return false; +?> \ No newline at end of file diff --git a/_prepend.php b/_prepend.php new file mode 100644 index 0000000..6e177f1 --- /dev/null +++ b/_prepend.php @@ -0,0 +1,26 @@ + \ No newline at end of file diff --git a/_uninstall.php b/_uninstall.php new file mode 100644 index 0000000..0e83c16 --- /dev/null +++ b/_uninstall.php @@ -0,0 +1,59 @@ +addUserAction( + /* type */ 'settings', + /* action */ 'delete_all', + /* ns */ 'translater', + /* description */ __('delete all settings') +); + +$this->addUserAction( + /* type */ 'plugins', + /* action */ 'delete', + /* ns */ 'translater', + /* description */ __('delete plugin files') +); + +$this->addUserAction( + /* type */ 'versions', + /* action */ 'delete', + /* ns */ 'translater', + /* description */ __('delete the version number') +); + + +$this->addDirectAction( + /* type */ 'settings', + /* action */ 'delete_all', + /* ns */ 'translater', + /* description */ sprintf(__('delete all %s settings'),'translater') +); + +$this->addDirectAction( + /* type */ 'plugins', + /* action */ 'delete', + /* ns */ 'translater', + /* description */ sprintf(__('delete %s plugin files'),'translater') +); + +$this->addDirectAction( + /* type */ 'versions', + /* action */ 'delete', + /* ns */ 'translater', + /* description */ sprintf(__('delete %s version number'),'translater') +); +?> \ No newline at end of file diff --git a/icon.png b/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..877f82feae4e68deb2af0b8a8100a141e4f9668c GIT binary patch literal 691 zcmV;k0!;mhP)%W!&uf*aR7m=b*g|(kUJU9 zp=n%1olDSG6tD^6Pm#c6n|B4fFQ*MV1`x;`99zMDR*0#E+F^m|oe836+P7h!>#yE1 zfI!Pp|0)RN)PgnUO$R@72rPJU`eo0n-Y3TD?vS-`EG)AfK{?SrAt%}ba;()aIZ6Qp z@}w&6x|k9*7g8`rL*Kdi8;ED#;QEIV91l(YIPowK+e{Jd0T~0&6kK`z6@7djhdf!w zwp#S^9d~>P!TC5WQwj!Wi!>p*WY}^u%h{eQ=zmnefl-ky`!{2#b7T1JN1S<4gx>S% z7mb%u>(n#eMi%C6?_t0)6t-JzDG!NaVz^b*qE`c@Om0%1= zpr*5B14!3kn2^D}|Au;ZM%_q;bhY%VI5ex^Fx52YQ--nur0X!-_-*q03322ghy(&cNNX?2}d-6ZV@ORut;!KHNq*K6#He9hTK Z<~I`7qRv array( + 'id' => 'translater_plugin_menu', + 'value' => 0, + 'type' => 'boolean', + 'label' => 'Put an link in plugins page' + ), + 'theme_menu' => array( + 'id' => 'translater_theme_menu', + 'value' => 0, + 'type' => 'boolean', + 'label' => 'Put a link in themes page' + ), + 'backup_auto' => array( + 'id' => 'translater_backup_auto', + 'value' => 1, + 'type' => 'boolean', + 'label' => 'Make a backup of languages old files when there are modified' + ), + 'backup_limit' => array( + 'id' => 'translater_backup_limit', + 'value' => 20, + 'type' => 'string', + 'label' => 'Maximum backups per module' + ), + 'backup_folder' => array( + 'id' => 'translater_backup_folder', + 'value' => 'module', + 'type' => 'string', + 'label' => 'In which folder to store backups' + ), + 'start_page' => array( + 'id' => 'translater_start_page', + 'value' => 'setting', + 'type' => 'string', + 'label' => 'Page to start on' + ), + 'write_po' => array( + 'id' => 'translater_write_po', + 'value' => 1, + 'type' => 'boolean', + 'label' => 'Write .po languages files' + ), + 'write_langphp' => array( + 'id' => 'translater_write_langphp', + 'value' => 1, + 'type' => 'boolean', + 'label' => 'Write .lang.php languages files' + ), + 'scan_tpl' => array( + 'id' => 'translater_scan_tpl', + 'value' => 0, + 'type' => 'boolean', + 'label' => 'Translate strings of templates files' + ), + 'parse_nodc' => array( + 'id' => 'translater_parse_nodc', + 'value' => 1, + 'type' => 'boolean', + 'label' => 'Translate only untranslated strings of Dotclear', + ), + 'hide_default' => array( + 'id' => 'translater_hide_default', + 'value' => 1, + 'type' => 'boolean', + 'label' => 'Hide default modules of Dotclear', + ), + 'parse_comment' => array( + 'id' => 'translater_parse_comment', + 'value' => 1, + 'type' => 'boolean', + 'label' => 'Write comments and strings informations in lang files' + ), + 'parse_user' => array( + 'id' => 'translater_parse_user', + 'value' => 1, + 'type' => 'boolean', + 'label' => 'Write inforamtions about author in lang files' + ), + 'parse_userinfo' => array( + 'id' => 'translater_parse_userinfo', + 'value' => 'displayname, email', + 'type' => 'string', + 'label' => 'Type of informations about user to write' + ), + 'import_overwrite' => array( + 'id' => 'translater_import_overwrite', + 'value' => 0, + 'type' => 'boolean', + 'label' => 'Overwrite existing languages when import packages' + ), + 'export_filename' => array( + 'id' => 'translater_export_filename', + 'value' => 'type-module-l10n-timestamp', + 'type' => 'string', + 'label' => 'Name of files of exported package' + ), + 'proposal_tool' => array( + 'id' => 'translater_proposal_tool', + 'value' => 'google', + 'type' => 'string', + 'label' => 'Id of default tool for proposed translation' + ), + 'proposal_lang' => array( + 'id' => 'translater_proposal_lang', + 'value' => 'en', + 'type' => 'string', + 'label' => 'Default source language for proposed translation' + ) + ); + # List of default modules of Dotclear + public static $default_dotclear_modules = array( + 'plugin' => array( + 'aboutConfig', + 'akismet', + 'antispam', + 'blogroll', + 'blowupConfig', + 'daInstaller', + 'externalMedia', + 'fairTrackbacks', + 'importExport', + 'maintenance', + 'pages', + 'pings', + 'simpleMenu', + 'tags', + 'themeEditor', + 'userPref', + 'widgets' + ), + 'theme' => array( + 'default', + 'blueSilence', + 'customCSS', + 'ductile' + ) + ); + + # List of modules (from plugins,thems, by dcModule::getModules) + private $modules = array(); + # Particular module + private $module = array(); + + # Construtor + function __construct($core) + { + $this->core =& $core; + $core->blog->settings->addNamespace('translater'); + $this->loadModules(); + $this->proposal = new translaterProposals($core); + } + + # Return array of default settings + public function getDefaultSettings() + { + return $this->default_settings; + } + + # Get settings for current blog + public function getSettings($id=null) + { + $res = array(); + foreach($this->default_settings AS $k => $v) + { + if ($k == $id) + { + return $this->core->blog->settings->translater->get( + $this->default_settings[$k]['id'] + ); + } + $res[$k] = $this->core->blog->settings->translater->get( + $this->default_settings[$k]['id'] + ); + } + return $res; + } + + # Get a setting according to default settings list + public function get($id) + { + if (isset($this->default_settings[$id])) + { + return $this->core->blog->settings->translater->get( + $this->default_settings[$id]['id'] + ); + } + return null; + } + + # See get() + public function __get($id) + { + return $this->get($id); + } + + # Set a setting according to default settings list + public function set($k,$v) + { + if (!isset($this->default_settings[$k])) return false; + + $this->dropOldSettings($this->default_settings[$k]['id']); + $this->core->blog->settings->translater->put( + $this->default_settings[$k]['id'], + $v, + $this->default_settings[$k]['type'], + $this->default_settings[$k]['label'], + true,true + ); + return true; + } + + # See set() + public function __set($k,$v) + { + return $this->set($k,$v); + } + + # Drop old "per blog" settings (as of version 2013.05.11) + private function dropOldSettings($id) + { + $this->core->blog->settings->translater->drop($id); + } + + # Retrieve a particular info for a given module + public function moduleInfo($id,$info) + { + if (isset($this->modules['plugin'][$id])) + { + $type = 'plugin'; + } + elseif (isset($this->modules['theme'][$id])) + { + $type = 'theme'; + } + else + { + return null; + } + + if ($info == 'type') + { + return $type; + } + + return isset($this->modules[$type][$id][$info]) ? $this->modules[$type][$id][$info] : null; + } + + # Load array of modules infos by type of modules + private function loadModules() + { + $themes = new dcThemes($this->core); + $themes->loadModules($this->core->blog->themes_path,null); + $this->modules['theme'] = $this->modules['plugin'] = array(); + + $m = $themes->getModules(); + foreach($m AS $k => $v) + { + if (!$v['root_writable']) continue; + $this->modules['theme'][$k] = $v; + $this->modules['theme'][$k]['id'] = $k; + $this->modules['theme'][$k]['type'] = 'theme'; + } + + $m = $this->core->plugins->getModules(); + foreach($m AS $k => $v) + { + if (!$v['root_writable']) continue; + $this->modules['plugin'][$k] = $v; + $this->modules['plugin'][$k]['id'] = $k; + $this->modules['plugin'][$k]['type'] = 'plugin'; + } + } + + # Return array of modules infos by type of modules + public function listModules($type='') + { + return in_array($type,array('plugin','theme')) ? + $this->modules[$type] : + array_merge($this->modules['theme'],$this->modules['plugin']); + } + + # Return array object of a particular module for a given type of module + public function getModule($module='',$type='') + { + $o = new ArrayObject(); + + # Load nothing? + if (empty($module)) { + return false; + } + + # Unknow type? + if (!in_array($type,array('plugin','theme'))) { + $modules = array_merge($this->modules['theme'],$this->modules['plugin']); + } + else { + $modules = $this->modules[$type]; + } + + # Unknow module? + if (!isset($modules[$module])) { + throw new Exception(sprintf( + __('Cannot find module %s'),$module) + ); + return false; + } + + # Module info + foreach($modules[$module] as $a => $b) { + $o->{$a} = $b; + } + $o->root = path::real($o->root); + # Locales path + $o->locales = $o->root.'/locales'; + # Module exists + $o->exists = true; + # Module Basename + $i = path::info($o->root); + $o->basename = $i['basename']; + + return $o; + } + + public function getBackupFolder($module,$throw=false) + { + $dir = false; + switch($this->backup_folder) + { + case 'module': + # plugin + if (isset($this->modules['plugin'][$module]) + && $this->modules['plugin'][$module]['root_writable']) + { + $dir = path::real($this->modules['plugin'][$module]['root']).'/locales'; + } + #theme + elseif (isset($this->modules['theme'][$module]) + && $this->modules['theme'][$module]['root_writable']) + { + $dir = path::real($this->modules['theme'][$module]['root']).'/locales'; + } + break; + + case 'plugin': + $tmp = path::real(array_pop(explode(PATH_SEPARATOR, DC_PLUGINS_ROOT))); + if ($tmp && is_writable($tmp)) + { + $dir = $tmp; + } + break; + + case 'public': + $tmp = path::real($this->core->blog->public_path); + if ($tmp && is_writable($tmp)) + { + $dir = $tmp; + } + break; + + case 'cache': + $tmp = path::real(DC_TPL_CACHE); + if ($tmp && is_writable($tmp)) + { + @mkDir($tmp.'/l10n'); + $dir = $tmp.'/l10n'; + } + break; + + case 'translater': + $tmp = path::real($this->modules['plugin']['translater']['root']); + if ($tmp && is_writable($tmp)) + { + @mkDir($tmp.'/locales'); + $dir = $tmp.'/locales'; + } + break; + } + if (!$dir && $throw) + { + throw new Exception(sprintf( + __('Cannot find backups folder for module %s'),$module) + ); + } + + return $dir; + } + + public function getLangsFolder($module='',$throw=false) + { + $dir = $module == 'dotclear' ? + DC_ROOT : + self::getModuleFolder($module,false); + + if (!$dir && $throw) + { + throw new Exception(sprintf( + __('Cannot find languages folder for module %s'),$module) + ); + } + + return !$dir ? false : $dir.'/locales'; + } + + public function getModuleFolder($module='',$throw=false) + { + $dir = false; + if ((isset($this->modules['plugin'][$module]['root']) + && ($tmp = path::real($this->modules['plugin'][$module]['root']))) || + (isset($this->modules['theme'][$module]['root']) + && ($tmp = path::real($this->modules['theme'][$module]['root'])))) + { + $dir = $tmp; + } + if (!$dir && $throw) + { + throw new Exception(sprintf( + __('Cannot find root folder for module %s'),$module) + ); + } + + return $dir; + } + + public function isBackupLimit($module,$throw=false) + { + # Find folder of backups + $backup = self::getBackupFolder($module,true); + + # Count backup files + $count = 0; + foreach(self::scandir($backup) AS $file) + { + if (!is_dir($backup.'/'.$file) + && preg_match('/^(l10n-'.$module.'(.*?).bck.zip)$/',$backup)) + { + $count++; + } + } + + # Limite exceed + if ($count >= $this->backup_limit) + { + if ($throw) + { + throw new Exception(sprintf( + __('Limit of %s backups for module %s exceed'), + $this->backup_limit,$module) + ); + } + return true; + } + else + { + return false; + } + } + + public function listBackups($module,$return_filename=false) + { + # Not a module installed + self::getLangsFolder($module,true); + + # No backup folder for this module + $backup = self::getBackupFolder($module,false); + if (!$backup) return array(); + + # Scan files for backups + $res = $sort = array(); + $files = self::scandir($backup); + foreach($files AS $file) + { + # Not a bakcup file + $is_backup = preg_match( + '/^(l10n-('.$module.')-(.*?)-([0-9]*?).bck.zip)$/',$file,$m); + + if (is_dir($backup.'/'.$file) + || !$is_backup + || !self::isIsoCode($m[3])) continue; + + # Backup infos + if ($return_filename) + { + $res[] = $file; + } + else + { + $res[$m[3]][$file] = path::info($backup.'/'.$file); + $res[$m[3]][$file]['time']= filemtime($backup.'/'.$file); + $res[$m[3]][$file]['size'] = filesize($backup.'/'.$file); + $res[$m[3]][$file]['module'] = $module; + } + } + return $res; + } + + public function createBackup($module,$lang) + { + # Not a module installed + $locales = self::getLangsFolder($module,true); + + # No backup folder for this module + $backup = self::getBackupFolder($module,true); + + # Not an existing lang + if (!is_dir($locales.'/'.$lang)) + { + throw new Exception(sprintf( + __('Cannot find language folder %s for module %s'),$lang,$module) + ); + } + + # Scan files for a lang + $res = array(); + $files = self::scandir($locales.'/'.$lang); + foreach($files as $file) + { + # Only lang file + if (!is_dir($locales.'/'.$lang.'/'.$file) + && (self::isLangphpFile($file) + || self::isPoFile($file))) + { + $res[$locales.'/'.$lang.'/'.$file] = + $module.'/locales/'.$lang.'/'.$file; + } + } + + # Do Zip + if (!empty($res)) + { + self::isBackupLimit($module,true); + + @set_time_limit(300); + $fp = fopen($backup.'/l10n-'.$module.'-'.$lang.'-'.time().'.bck.zip','wb'); + $zip = new fileZip($fp); + foreach($res AS $src => $dest) + { + $zip->addFile($src,$dest); + } + $zip->write(); + $zip->close(); + unset($zip); + } + } + + public function deleteBackup($module,$file) + { + # Not a module installed + self::getLangsFolder($module,true); + + # No backup folder for this module + $backup = self::getBackupFolder($module,true); + + # Not a bakcup file + $is_backup = preg_match('/^(l10n-('.$module.')-(.*?)-([0-9]*?).bck.zip)$/',$file,$m); + + if (is_dir($backup.'/'.$file) + || !$is_backup + || !self::isIsoCode($m[3])) continue; + + if (!files::isDeletable($backup.'/'.$file)) + { + throw new Exception(sprintf( + __('Cannot delete backup file %s'),$file) + ); + } + + unlink($backup.'/'.$file); + } + + public function restoreBackup($module,$file) + { + # Not a module installed + $locales = self::getModuleFolder($module,true); + + # No backup folder for this module + $backup = self::getBackupFolder($module,true); + + if (!file_exists($backup.'/'.$file)) + { + throw new Exception(sprintf( + __('Cannot find backup file %s'),$file) + ); + } + + $zip = new fileUnzip($backup.'/'.$file); + $zip_files = $zip->getFilesList(); + + foreach($zip_files AS $zip_file) + { + $f = self::explodeZipFilename($zip_file,true); + if ($module != $f['module']) continue; + + $zip->unzip($zip_file,$locales.'/locales/'.$f['lang'].'/'.$f['group'].$f['ext']); + $done = true; + } + $zip->close(); + unset($zip); + } + + public function exportPack($modules,$langs) + { + # Not a query good formed + if (!is_array($modules) || 1 > count($modules) + || !is_array($langs) || 1 > count($langs)) + { + throw new Exception( + __('Wrong export query') + ); + } + + # Filter default filename + $filename = files::tidyFileName($this->export_filename); + + # Not a filename good formed + if (empty($filename)) + { + throw new Exception(sprintf( + __('Cannot use export mask %s'),$this->export_filename) + ); + } + + # Modules folders + $res = array(); + $count = array(); + foreach($modules AS $module) + { + # Not a module installed + $locales = self::getLangsFolder($module,false); + if (!$locales) continue; + + # Langs folders + foreach($langs AS $lang) + { + # Not a lang folder + if (!is_dir($locales.'/'.$lang)) continue; + + # Scan files for a lang + $files = self::scandir($locales.'/'.$lang); + foreach($files as $file) + { + # Not a lang file + if (is_dir($locales.'/'.$lang.'/'.$file) + || !self::isLangphpFile($file) + && !self::isPoFile($file)) continue; + + # Add file to zip in format "module/locales/lang/filename" + $res[$locales.'/'.$lang.'/'.$file] = + $module.'/locales/'.$lang.'/'.$file; + + $count[$module] = 1; + } + } + } + + # Nothing to do + if (empty($res)) + { + throw new Exception('Nothing to export'); + } + + # Prepare files to zip + @set_time_limit(300); + $fp = fopen('php://output','wb'); + $zip = new fileZip($fp); + foreach($res as $from => $to) + { + $zip->addFile($from,$to); + } + + # Set filename + $file_infos = 1 < count($count) ? + array(time(),'modules','multi',self::$dcTranslaterVersion) : + array( + time(), + $modules[0], + self::moduleInfo($modules[0],'type'), + self::moduleInfo($modules[0],'version') + ); + $filename = + files::tidyFileName( + dt::str( + str_replace( + array('timestamp','module','type','version'), + $file_infos, + $this->export_filename + ) + ) + ); + + # Send Zip + header('Content-Disposition: attachment;filename='.$filename.'.zip'); + header('Content-Type: application/x-zip'); + $zip->write(); + unset($zip); + exit; + } + + public function importPack($modules,$zip_file) + { + # Not a file uploaded + files::uploadStatus($zip_file); + + # No modules to update + if (!is_array($modules) || 1 > count($modules)) + { + throw new Exception(__('Wrong import query')); + } + + $done = false; + $res = array(); + + # Load Unzip object + $zip = new fileUnzip($zip_file['tmp_name']); + $files = $zip->getFilesList(); + + # Scan zip + foreach($files AS $file) + { + $f = self::explodeZipFilename($file,false); + + # Not a requested module + if (!is_array($f) + || !in_array($f['module'],$modules)) continue; + + # Get locales folder (even if "locales" is not set) + if (!$dir = self::getModuleFolder($f['module'],false)) continue; + $locales = $dir.'/locales'; + + # Not allow overwrite + if (!$this->import_overwrite + && file_exists($locales.'/'.$f['lang'].'/'.$f['group'].$f['ext'])) continue; + + $res[] = array( + 'from' => $file, + 'root' => $locales.'/'.$f['lang'], + 'to' => $locales.'/'.$f['lang'].'/'.$f['group'].$f['ext'] + ); + } + # Unzip files + foreach ($res AS $rs) + { + if (!is_dir($rs['root'])) + files::makeDir($rs['root'],true); + + $zip->unzip($rs['from'],$rs['to']); + $done = true; + } + $zip->close(); + unlink($zip_file['tmp_name']); + + # No file unzip + if (!$done) + { + throw new Exception(sprintf( + __('Nothing to import for these modules in pack %s'), + $zip_file['name']) + ); + } + } + + public function explodeZipFilename($file='',$throw=false) + { + # module/locales/lang/group.ext + $is_file = preg_match( + '/^(.*?)\/locales\/(.*?)\/(.*?)(.po|.lang.php)$/',$file,$f); + + # Explode file to infos + if ($is_file) { + $module = null !== self::moduleInfo($f[1],'name') ? + $f[1] : false; + $lang = self::isIsoCode($f[2]) ? + $f[2] : false; + $group = in_array($f[3],self::$allowed_l10n_groups) ? + $f[3] : false; + $ext = self::isLangphpFile($f[4]) || self::isPoFile($f[4]) ? + $f[4] : false; + } + # Not good formed + if (!$is_file || !$module || !$lang || !$group || !$ext) + { + if ($throw) + { + throw new Exception(sprintf( + __('Zip file %s is not in translater format'),$file) + ); + } + return false; + } + return array( + 'module' => $module, + 'lang' => $lang, + 'group' => $group, + 'ext' => $ext + ); + } + + public function listLangs($module,$return_path=false) + { + $res = array(); + + # Not a module installed + $locales = self::getLangsFolder($module,true); + + # Add prefix "locales" as scandir remove it + $prefix = preg_match('/(locales(.*))$/',$locales) ? 'locales' : ''; + + # Retrieve langs folders + $files = self::scandir($locales); + foreach($files as $file) + { + if (!preg_match( + '/(.*?(locales\/)([^\/]*?)\/([^\/]*?)(.lang.php|.po))$/', + $prefix.$file,$m)) continue; + + if (!self::isIsoCode($m[3])) continue; + + if ($return_path) + { + $res[$m[3]][] = $file; # Path + } + else + { + $res[$m[3]] = self::$iso[$m[3]]; # Lang name + } + } + return $res; + } + + public function addLang($module,$lang,$from_lang='') + { + # Not a module installed + $locales = self::getLangsFolder($module,true); + + # Path is right formed + self::isIsoCode($lang,true); + + # Retrieve langs folders + $langs = self::listLangs($module); + + # Lang folder is not present + if (isset($langs[$lang])) + { + throw new Exception(sprintf( + __('Language %s already exists for module %s'),$lang,$module) + ); + } + + # Create new lang directory + files::makeDir($locales.'/'.$lang,true); + + # Verify folder of other lang + if (!empty($from_lang) && !isset($langs[$from_lang])) + { + throw new Exception(sprintf( + __('Cannot copy file from language %s for module %s'), + $from_lang,$module) + ); + } + + # Copy files from other lang + if (!empty($from_lang) + && isset($langs[$from_lang])) + { + $files = self::scandir($locales.'/'.$from_lang); + foreach($files as $file) + { + if (is_dir($locales.'/'.$from_lang.'/'.$file) + || !self::isLangphpFile($file) + && !self::isPoFile($file)) continue; + + files::putContent($locales.'/'.$lang.'/'.$file, + file_get_contents($locales.'/'.$from_lang.'/'.$file)); + } + } + else + { + # Create basic empty lang file as translater need these files to be present + self::setLangphpFile($module,$lang,'main',array()); + self::setPoFile($module,$lang,'main',array()); + } + } + + public function updLang($module,$lang,$msgs) + { + # Not a module installed + $locales = self::getLangsFolder($module,true); + + # Path is right formed + self::isIsoCode($lang,true); + + # Retrieve langs folders + $langs = self::listLangs($module); + + # Lang folder is not present + if (!isset($langs[$lang])) + { + throw new Exception(sprintf( + __('Cannot find language folder %s for module %s'),$lang,$module) + ); + } + + # Sort msgids by groups + $rs = array(); + foreach($msgs as $msg) + { + $msg['group'] = isset($msg['group']) ? $msg['group'] : ''; + $msg['msgid'] = isset($msg['msgid']) ? $msg['msgid'] : ''; + $msg['msgstr'] = isset($msg['msgstr']) ? trim($msg['msgstr']) : ''; +/* + if (get_magic_quotes_gpc()) { + $msg['msgid'] = stripcslashes($msg['msgid']); + $msg['msgstr'] = stripcslashes($msg['msgstr']); + } +*/ if ($msg['msgstr'] == '') continue; + + $rs[$msg['group']][$msg['msgid']] = $msg['msgstr']; + } + + # Backup files if auto-backup is on + if ($this->backup_auto) + { + self::createBackup($module,$lang); + } + + # Delete empty files (files with no group) + foreach(self::$allowed_l10n_groups AS $group) + { + if (isset($rs[$group])) continue; + + $po_file = $locales.'/'.$lang.'/'.$group.'.po'; + $langphp_file = $locales.'/'.$lang.'/'.$group.'.lang.php'; + + if (file_exists($po_file)) + { + unlink($po_file); + } + if (file_exists($langphp_file)) + { + unlink($langphp_file); + } + } + + # No msgstr to write + if (empty($rs)) + { + throw new Exception(sprintf( + __('No string to write, language %s deleted for module %s'), + $lang,$module) + ); + } + + # Write .po and .lang.php files + foreach($rs AS $group => $msgs) + { + self::setLangphpFile($module,$lang,$group,$msgs); + self::setPoFile($module,$lang,$group,$msgs); + } + } + + public function delLang($module,$lang,$del_empty_dir=true) + { + # Not a module installed + $locales = self::getLangsFolder($module,true); + + # Path is right formed + self::isIsoCode($lang,true); + + # Retrieve langs folders + $files = self::listLangs($module,true); + + # Lang folder is not present + if (!isset($files[$lang])) + { + throw new Exception(sprintf( + __('Cannot find language folder %s for module %s'),$lang,$module) + ); + } + + # Delete .po and .lang.php files + foreach($files[$lang] as $file) + { + unlink($locales.'/'.$file); + } + + # Delete lang folder if empty + $dir = self::scandir($locales.'/'.$lang); + if (empty($dir)) + { + rmdir($locales.'/'.$lang); + } + + # Delete locales folder if empty + $loc = self::scandir($locales); + if (empty($loc)) + { + rmdir($locales); + } + } + + public static function encodeMsg($str) + { + return text::toUTF8(stripslashes(trim($str))); + } + + /* Scan a module folder to find all l10n strings in .php files */ + public function getMsgIds($module) + { + $res = array(); + + # Not a module installed + $dir = self::getModuleFolder($module,true); + + $files = self::scandir($dir); + + $scan_ext = array('php'); + if ($this->scan_tpl) + { + $scan_ext[] = 'html'; + } + + foreach($files AS $file) + { + if (is_dir($dir.'/'.$file) + || !in_array(files::getExtension($file),$scan_ext)) continue; + + $contents = file($dir.'/'.$file); + foreach($contents AS $line => $content) + { + # php files + //if (preg_match_all("|__\((['\"]{1})(.*)([\"']{1})\)|U",$content,$matches)) + if (preg_match_all("|__\((['\"]{1})(.*?)([\"']{1})\)|",$content,$matches)) + { + foreach($matches[2] as $id) + { + $res[] = array( + 'msgid' => self::encodeMsg($id), + 'file' => $file, + 'line' => $line + 1 + ); + } + } + # tpl files + if ($this->scan_tpl + && preg_match_all('/\{\{tpl:lang\s([^}]+)\}\}/',$content,$matches)) + { + foreach($matches[1] as $id) + { + $res[] = array( + 'msgid' => self::encodeMsg($id), + 'file' => $file, + 'line' => $line + 1 + ); + } + } + } + unset($contents); + } + return $res; + } + + /* Scan a lang folder to find l10n translations in files */ + public function getMsgStrs($module,$requested_lang='') + { + $res = array(); + + # Not a module installed + $locales = self::getLangsFolder($module,true); + + $langs = self::listLangs($module,true); + + # Not an existing lang + if (!isset($langs[$requested_lang])) return $res; + + # Lang files + $exists = array(); + foreach($langs[$requested_lang] as $file) + { + if (in_array($file,$exists)) continue; + $exists[] = $file; + $path = path::clean($locales.'/'.$file); + + # .po files + if (self::isPoFile($file)) + { + $po = self::getPoFile($path); + if (!is_array($po)) continue; + + foreach($po as $id => $str) + { + $is_po[$requested_lang][$id] = 1; + + $res[] = array( + 'msgid' => self::encodeMsg($id), + 'msgstr' => self::encodeMsg($str), + 'lang' => $requested_lang, + 'type' => 'po', + 'path' => $path, + 'file' => basename($file), + 'group'=> str_replace('.po','',basename($file)) + ); + } + } + # .lang.php files + elseif (self::isLangphpFile($file)) + { + $php = self::getLangphpFile($path); + foreach($php AS $id => $str) + { + # Don't overwrite .po + if (isset($is_po[$requested_lang][$id])) continue; + $res[] = array( + 'msgid' => self::encodeMsg($id), + 'msgstr' => self::encodeMsg($str), + 'lang' => $requested_lang, + 'type' => 'php', + 'path' => $path, + 'file' => basename($file), + 'group'=> str_replace('.lang.php','',basename($file)) + ); + } + } + } + return $res; + } + + public function getMsgs($module,$requested_lang='') + { + # Get messages ids of a module + $m_msgids = self::getMsgIds($module); + + # Get messages translations for a module + $m_msgstrs = self::getMsgStrs($module,$requested_lang); + + # Get messages translations for others modules + foreach(self::listModules() AS $o_module => $o_infos) + { + if ($o_module == $module) continue; + $m_o_msgstrs[$o_module] = self::getMsgStrs($o_module,$requested_lang); + } + $m_o_msgstrs['dotclear'] = self::getMsgStrs('dotclear',$requested_lang); + + # Only one lang or all + $langs = '' == $requested_lang ? + self::listLangs($module) : + array($requested_lang => self::isIsoCode($requested_lang)); + + # Let's go reorder the mixture + $res = array(); + foreach($langs AS $lang => $iso) + { + $res[$lang] = array(); + + # From id list + foreach($m_msgids AS $rs) + { + $res[$lang][$rs['msgid']]['files'][] = array(trim($rs['file'],'/'),$rs['line']); + $res[$lang][$rs['msgid']]['group'] = 'main'; + $res[$lang][$rs['msgid']]['msgstr'] = ''; + $res[$lang][$rs['msgid']]['in_dc'] = false; + $res[$lang][$rs['msgid']]['o_msgstrs'] = array(); + } + + # From str list + foreach($m_msgstrs AS $rs) + { + if ($rs['lang'] != $lang) continue; + + if (!isset($res[$lang][$rs['msgid']])) + { + $res[$lang][$rs['msgid']]['files'][] = array(); + $res[$lang][$rs['msgid']]['in_dc'] = false; + $res[$lang][$rs['msgid']]['o_msgstrs'] = array(); + } + $res[$lang][$rs['msgid']]['group'] = $rs['group']; + $res[$lang][$rs['msgid']]['msgstr'] = $rs['msgstr']; + $res[$lang][$rs['msgid']]['in_dc'] = false; + } + + # From others str list + foreach($m_o_msgstrs AS $o_module => $o_msgstrs) + { + foreach($o_msgstrs AS $rs) + { + if ($rs['lang'] != $lang) continue; + + if (!isset($res[$lang][$rs['msgid']])) continue; + + $res[$lang][$rs['msgid']]['o_msgstrs'][] = array( + 'msgstr' => $rs['msgstr'], + 'module' => $o_module, + 'file' => $rs['file'] + ); + if ($o_module == 'dotclear') + { + $res[$lang][$rs['msgid']]['in_dc'] = true; + } + } + } + } + return '' == $requested_lang ? $res : $res[$requested_lang]; + } + + /* Write a lang file */ + private function writeLangFile($dir,$content,$throw) + { + $path = path::info($dir); + if (is_dir($path['dirname']) && !is_writable($path['dirname']) + || file_exists($dir) && !is_writable($dir)) + { + throw new Exception(sprintf( + __('Cannot grant write acces on lang file %s'),$dir) + ); + } + + # -- BEHAVIOR -- dcTranslaterBeforeWriteLangFile + $this->core->callBehavior('dcTranslaterBeforeWriteLangFile',$dir,$content,$throw); + + $f = @files::putContent($dir,$content); + if (!$f && $throw) + { + throw new Exception(sprintf( + __('Cannot write lang file %s'),$dir) + ); + } + + # -- BEHAVIOR -- dcTranslaterAfterWriteLangFile + $this->core->callBehavior('dcTranslaterAfterWriteLangFile',$f,$dir,$content,$throw); + + return $f; + } + + /* Try if a file is a .lang.php file */ + public static function isLangphpFile($file) + { + return files::getExtension($file) == 'php' && stristr($file,'.lang.php'); + } + + /* Get and parse a .lang.php file */ + public static function getLangphpFile($file) + { + if (!file_exists($file)) return array(); + + $res = array(); + $content = implode('',file($file)); + $count = preg_match_all('/(\$GLOBALS\[\'__l10n\'\]\[\'(.*?)\'\]\s*\x3D\s*\'(.*?)\';)/',$content, $m); + + if (!$count) return array(); + + for ($i=0; $i<$count; $i++) + { + $id = $m[2][$i]; + $str = self::langphpString($m[3][$i]); + + if ($str) + { + $res[self::langphpString($id)] = $str; + } + } + if (!empty($res[''])) + { + $res = array_diff_key($res,array(''=>1)); + } + return $res; + } + + /* Construct and write a .lang.php file */ + private function setLangphpFile($module,$lang,$group,$fields) + { + if (!$this->write_langphp) return; + + # Not a module installed + $locales = self::getLangsFolder($module,true); + + # Path is right formed + $lang_name = self::isIsoCode($lang,true); + + $l = "parse_comment) + { + $l .= + '// Language: '.$lang_name." \n". + '// Module: '.$module." - ".self::moduleInfo($module,'version')."\n". + '// Date: '.dt::str('%Y-%m-%d %H:%M:%S')." \n"; + + if ($this->parse_user && !empty($this->parse_userinfo)) + { + $search = self::$allowed_user_informations; + foreach($search AS $n) + { + $replace[] = $this->core->auth->getInfo('user_'.$n); + } + $info = trim(str_replace($search,$replace,$this->parse_userinfo)); + if (!empty($info)) + { + $l .= '// Author: '.html::escapeHTML($info)."\n"; + } + } + $l .= + '// Translated with dcTranslater - '.$this->core->plugins->moduleInfo('translater','version')." \n\n"; + } + if ($this->parse_comment) + { + $infos = self::getMsgids($module); + foreach($infos AS $info) + { + if (isset($fields[$info['msgid']])) + { + $comments[$info['msgid']] = (isset($comments[$info['msgid']]) ? + $comments[$info['msgid']] : ''). + '#'.trim($info['file'],'/').':'.$info['line']."\n"; + } + } + } + + foreach($fields as $id => $str) + { + if ($this->parse_comment && isset($comments[$id])) + { + $l .= $comments[$id]; + } + + $l .= + '$GLOBALS[\'__l10n\'][\''.addcslashes($id,"'").'\'] = '. + '\''.self::langphpString($str,true)."';\n"; + + if ($this->parse_comment) + { + $l .= "\n"; + } + } + $l .= "?>"; + + self::writeLangFile($locales.'/'.$lang.'/'.$group.'.lang.php',$l,true); + } + + /* Parse a .lang.php string */ + private static function langphpString($string,$reverse=false) + { + if ($reverse) + { + $smap = array('\'', "\n", "\t", "\r"); + $rmap = array('\\\'', '\\n"' . "\n" . '"', '\\t', '\\r'); + return trim((string) str_replace($smap, $rmap, $string)); + } + else + { + $smap = array('/\\\\n/', '/\\\\r/', '/\\\\t/', "/\\\'/"); + $rmap = array("\n", "\r", "\t", "'"); + return trim((string) preg_replace($smap, $rmap, $string)); + } + } + + /* Try if a file is a .po file */ + public static function isPoFile($file) + { + return files::getExtension($file) == 'po'; + } + + /* Get and parse a .po file */ + public static function getPoFile($file) + { + if (!file_exists($file)) return false; + + $res = array(); + $content = implode('',file($file)); + + $count = preg_match_all('/msgid\s(.*(?:\n".*")*)\nmsgstr\s(.*(?:\n".*")*)/',$content,$m); + + if (!$count) return false; + + for ($i=0; $i<$count; $i++) + { + $id = preg_replace('/"(.*)"/s','\\1',$m[1][$i]); + $str= preg_replace('/"(.*)"/s','\\1',$m[2][$i]); + + $str = self::poString($str); + + if ($str) + { + $res[self::poString($id)] = $str; + } + } + + if (!empty($res[''])) + { + $res = array_diff_key($res,array(''=>1)); + } + return $res; + } + /* Construct and parse a .po file */ + private function setPoFile($module,$lang,$group,$fields) + { + if (!$this->write_po) return; + + # Not a module installed + $locales = self::getLangsFolder($module,true); + + # Path is right formed + self::isIsoCode($lang,true); + + $l = ''; + if ($this->parse_comment) + { + $l .= + '# Language: '.self::$iso[$lang]."\n". + '# Module: '.$module." - ".self::moduleInfo($module,'version')."\n". + '# Date: '.dt::str('%Y-%m-%d %H:%M:%S')."\n"; + + if ($this->parse_user && !empty($this->parse_userinfo)) + { + $search = self::$allowed_user_informations; + foreach($search AS $n) + { + $replace[] = $this->core->auth->getInfo('user_'.$n); + } + $info = trim(str_replace($search,$replace,$this->parse_userinfo)); + if (!empty($info)) + { + $l .= '# Author: '.html::escapeHTML($info)."\n"; + } + } + $l .= + '# Translated with translater '.$this->core->plugins->moduleInfo('translater','version')."\n"; + } + $l .= + "\n". + "msgid \"\"\n". + "msgstr \"\"\n". + '"Content-Type: text/plain; charset=UTF-8\n"'."\n". + '"Project-Id-Version: '.$module.' '.self::moduleInfo($module,'version').'\n"'."\n". + '"POT-Creation-Date: \n"'."\n". + '"PO-Revision-Date: '.date('c').'\n"'."\n". + '"Last-Translator: '.$this->core->auth->getInfo('user_cn').'\n"'."\n". + '"Language-Team: \n"'."\n". + '"MIME-Version: 1.0\n"'."\n". + '"Content-Transfer-Encoding: 8bit\n"'."\n\n"; + + if ($this->parse_comment) + { + $infos = self::getMsgids($module); + foreach($infos AS $info) + { + if (isset($fields[$info['msgid']])) + { + $comments[$info['msgid']] = (isset($comments[$info['msgid']]) ? + $comments[$info['msgid']] : ''). + '#: '.trim($info['file'],'/').':'.$info['line']."\n"; + } + } + } + + foreach($fields as $id => $str) + { + if ($this->parse_comment && isset($comments[$id])) + { + $l .= $comments[$id]; + } + $l .= + 'msgid "'.self::poString($id,true) .'"'."\n". + 'msgstr "'.self::poString($str,true).'"'."\n\n"; + } + + self::writeLangFile($locales.'/'.$lang.'/'.$group.'.po',$l,true); + } + + /* Parse .po string */ + private static function poString($string,$reverse=false) + { + if ($reverse) + { + $smap = array('"', "\n", "\t", "\r"); + $rmap = array('\\"', '\\n"' . "\n" . '"', '\\t', '\\r'); + return trim((string) str_replace($smap, $rmap, $string)); + } + else + { + $smap = array('/"\s+"/', '/\\\\n/', '/\\\\r/', '/\\\\t/', '/\\\"/'); + $rmap = array('', "\n", "\r", "\t", '"'); + return trim((string) preg_replace($smap, $rmap, $string)); + } + } + + /* Scan recursively a folder and return files and folders names */ + public static function scandir($path,$dir='',$res=array()) + { + $path = path::real($path); + if (!is_dir($path) || !is_readable($path)) return array(); + + $files = files::scandir($path); + + foreach($files AS $file) + { + if ($file == '.' || $file == '..') continue; + + if (is_dir($path.'/'.$file)) + { + $res[] = $file; + $res = self::scanDir($path.'/'.$file,$dir.'/'.$file,$res); + } + else + { + $res[] = empty($dir) ? $file : $dir.'/'.$file; + } + } + return $res; + } + + /* Return array of langs like in clearbreaks l10n */ + public static function getIsoCodes($flip=false,$name_with_code=false) + { + if (empty(self::$iso)) + { + self::$iso = l10n::getISOcodes($flip,$name_with_code); + } + return self::$iso; + } + + /* Find if lang code exists or lang name */ + public static function isIsoCode($iso,$throw=false) + { + $codes = self::getIsoCodes(); + $code = isset($codes[$iso]) ? $codes[$iso] : false; + if (!$code && $throw) + { + throw new Exception(sprintf( + __('Cannot find language for code %s'),$iso) + ); + } + return $code; + } +} +?> \ No newline at end of file diff --git a/inc/class.translater.proposals.php b/inc/class.translater.proposals.php new file mode 100644 index 0000000..441d051 --- /dev/null +++ b/inc/class.translater.proposals.php @@ -0,0 +1,64 @@ +core = $core; + + # --BEHAVIOR-- addTranslaterProposalTool + $core->callBehavior('addTranslaterProposalTool',$this); + } + + public function addTool($id) + { + if (!class_exists($id)) { + return; + } + + $r = new ReflectionClass($id); + $p = $r->getParentClass(); + + if (!$p || $p->name != 'translaterProposalTool') { + return; + } + + $this->stack[$id] = new $id($this->core); + } + + public function getTools() + { + return $this->stack; + } + + public function getTool($id) + { + return array_key_exists($id,$this->stack) ? $this->stack[$id] : null; + } + + public function hasTool($id) + { + return array_key_exists($id,$this->stack); + } +} +?> \ No newline at end of file diff --git a/inc/class.translater.rest.php b/inc/class.translater.rest.php new file mode 100644 index 0000000..490e51f --- /dev/null +++ b/inc/class.translater.rest.php @@ -0,0 +1,76 @@ +proposal->hasTool($tool)) + { + throw new Exception(__('Failed to get translation tool')); + } + if (!$translater->proposal->getTool($tool)->isActive()) + { + throw new Exception(__('Translation tool is not configured')); + } + + $str_out = (string) $translater->proposal->getTool($tool)->translate($str_in,$from,$to); + } + + $x = new xmlTag('proposal'); + $x->lang_from = $from; + $x->lang_to = $to; + $x->tool = $tool; + $x->str_from = $str_in; + $x->str_to = text::toUTF8(html::decodeEntities($str_out)); + $rsp->insertNode($x); + } + catch (Exception $e) + { + $core->error->add($e->getMessage()); + } + return $rsp; + } +} +?> \ No newline at end of file diff --git a/inc/img/field.png b/inc/img/field.png new file mode 100644 index 0000000000000000000000000000000000000000..204de72316ac13eb8456bd8208f881939885d6d4 GIT binary patch literal 321 zcmV-H0lxl;P)4KV|@pIrODbY{Z;v_Aj;iCtd* zm+qN?Y5+dG>FBcmjjI#?Z@Dt{|Al9}{`c?B`XAn}`@em0*?$I*DTHuhm*@YDmnZz+ zcx}>u5a`*H`oDiq`hVMedEyd_PqpFyZCB>}M+TcNP5p14ElHdKmO0YjyLTo1Z{L*w z*4(}`0qg>x0pE!-0I0AfxJToE@2(WE%^(`Y2a5L)o#ugxJAn8T$k|Z310)Ur_h~;h TM?3Hj00000NkvXXu0mjfa9597 literal 0 HcmV?d00001 diff --git a/inc/img/toggle.png b/inc/img/toggle.png new file mode 100644 index 0000000000000000000000000000000000000000..9ab4a89664eee1aa81a51ca127f64cb3ac6fa918 GIT binary patch literal 209 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!60wlNoGJgf6SkfJR9T^zbpD<_bdI{u9mbgZg z1m~xflqVLYGB~E>C#5QQ<|d}62BjvZR2H60wE-$h_H=O!(Kw&{<9vg>(S^W+6Zii9 z|Nhthr~iNb*Z!}6uiN$Dz5neG3a-`baBX8yz4q@v|B?28{s)#N@CGn3@%_y|zAV9T z66e<&B4?b6oF&azg|C(V&1ZbI_D}pL`}(^FT2yXwG1Ph~$Q@h8mJYOz!PC{xWt~$( F699+YQR)By literal 0 HcmV?d00001 diff --git a/inc/lib.translater.google.php b/inc/lib.translater.google.php new file mode 100644 index 0000000..ebe613c --- /dev/null +++ b/inc/lib.translater.google.php @@ -0,0 +1,97 @@ +key = $this->core->blog->settings->translater->translater_google_proposal_key; + + $this->setName(__('Google')); + $this->setDesc(__('Google Translation Tool API')); + $this->setActive(!empty($this->key)); + } + + public function form() + { + return + '

'. + '

'.__('You must have on Google API console:').'

'. + ''; + } + + public function save() + { + $key = empty($_POST['translater_google_proposal_key']) ? + '' : $_POST['translater_google_proposal_key']; + + $this->core->blog->settings->translater->put('translater_google_proposal_key',$key,'string','',true,true); + } + + public function translate($str,$from,$to) + { + try + { + $data = array( + 'key' => $this->key, + 'q' => $str, + 'source' => $from, + 'target' => $to + ); + + $path = ''; + $client = netHttp::initClient($this->api,$path); + $client->setUserAgent($this->agent); + $client->useGzip(false); + $client->setPersistReferers(false); + $client->get($path,$data); + + $rs = $client->getContent(); + + if ($client->getStatus() != 200) { + throw new Exception(__('Failed to query service.')); + } + + if (null === ($dec = json_decode($rs))) { + throw new Exception('Failed to decode result'); + } + + if ('' == @$dec->data->translations[0]->translatedText) { + throw new Exception('No data response'); + } + + return $dec->data->translations[0]->translatedText; + } + catch (Exception $e) {} + return ''; + } +} +?> \ No newline at end of file diff --git a/inc/lib.translater.microsoft.php b/inc/lib.translater.microsoft.php new file mode 100644 index 0000000..e11d6c3 --- /dev/null +++ b/inc/lib.translater.microsoft.php @@ -0,0 +1,225 @@ +setActive(false); + $this->client = $this->core->blog->settings->translater->translater_microsoft_proposal_client; + $this->secret = $this->core->blog->settings->translater->translater_microsoft_proposal_secret; + + $this->setName(__('Bing')); + $this->setDesc(__('Microsoft Bing translation tool')); + $this->setActive(!empty($this->client) && !empty($this->secret)); + } + + public function form() + { + return + '

'. + '

'. + '

'.__('You must have:').'

'. + ''; + } + + public function save() + { + $client = empty($_POST['translater_microsoft_proposal_client']) ? + '' : $_POST['translater_microsoft_proposal_client']; + $secret = empty($_POST['translater_microsoft_proposal_secret']) ? + '' : $_POST['translater_microsoft_proposal_secret']; + + $this->core->blog->settings->translater->put('translater_microsoft_proposal_client',$client,'string','',true,true); + $this->core->blog->settings->translater->put('translater_microsoft_proposal_secret',$secret,'string','',true,true); + } + + public function translate($str,$from,$to) + { + try { + return $this->doYourFuckingJob($this->client,$this->secret,$str,$from,$to); + } + catch (Exception $e) {} + return ''; + } + + // + // Microsoft fucking oAuth + // + + private function doYourFuckingJob($client,$secret,$str,$from,$to) + { + try { + //Client ID of the application. + $clientID = $client; + //Client Secret key of the application. + $clientSecret = $secret; + //OAuth Url. + $authUrl = "https://datamarket.accesscontrol.windows.net/v2/OAuth2-13/"; + //Application Scope Url + $scopeUrl = "http://api.microsofttranslator.com"; + //Application grant type + $grantType = "client_credentials"; + + //Get the Access token. + $accessToken = $this->getTokens($grantType, $scopeUrl, $clientID, $clientSecret, $authUrl); + //Create the authorization Header string. + $authHeader = "Authorization: Bearer ". $accessToken; + + //Set the params.// + $fromLanguage = $from; + $toLanguage = $to; + $inputStr = $str; + $contentType = 'text/plain'; + $category = 'general'; + + $params = "text=".urlencode($inputStr)."&to=".$toLanguage."&from=".$fromLanguage; + $translateUrl = "http://api.microsofttranslator.com/v2/Http.svc/Translate?$params"; + + //Get the curlResponse. + $curlResponse = $this->curlRequest($translateUrl, $authHeader); + + //Interprets a string of XML into an object. + $xmlObj = simplexml_load_string($curlResponse); + foreach((array)$xmlObj[0] as $val){ + $translatedStr = $val; + } + + return (string) $translatedStr; + /* + echo ""; + echo ""; + echo ""; + echo ""; + echo ""; + echo "
From $fromLanguageTo $toLanguage
".$inputStr."".$translatedStr."
"; + */ + } catch (Exception $e) { + throw $e; + } + } + + /* + * Get the access token. + * + * @param string $grantType Grant type. + * @param string $scopeUrl Application Scope URL. + * @param string $clientID Application client ID. + * @param string $clientSecret Application client ID. + * @param string $authUrl Oauth Url. + * + * @return string. + */ + private function getTokens($grantType, $scopeUrl, $clientID, $clientSecret, $authUrl) + { + try { + //Initialize the Curl Session. + $ch = curl_init(); + //Create the request Array. + $paramArr = array ( + 'grant_type' => $grantType, + 'scope' => $scopeUrl, + 'client_id' => $clientID, + 'client_secret' => $clientSecret + ); + //Create an Http Query.// + $paramArr = http_build_query($paramArr); + //Set the Curl URL. + curl_setopt($ch, CURLOPT_URL, $authUrl); + //Set HTTP POST Request. + curl_setopt($ch, CURLOPT_POST, TRUE); + //Set data to POST in HTTP "POST" Operation. + curl_setopt($ch, CURLOPT_POSTFIELDS, $paramArr); + //CURLOPT_RETURNTRANSFER- TRUE to return the transfer as a string of the return value of curl_exec(). + curl_setopt ($ch, CURLOPT_RETURNTRANSFER, TRUE); + //CURLOPT_SSL_VERIFYPEER- Set FALSE to stop cURL from verifying the peer's certificate. + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); + //Execute the cURL session. + $strResponse = curl_exec($ch); + //Get the Error Code returned by Curl. + $curlErrno = curl_errno($ch); + if($curlErrno){ + $curlError = curl_error($ch); + curl_close($ch); + throw new Exception($curlError); + } + //Close the Curl Session. + curl_close($ch); + //Decode the returned JSON string. + $objResponse = json_decode($strResponse); + if (@$objResponse->error){ + throw new Exception($objResponse->error_description); + } + return $objResponse->access_token; + } catch (Exception $e) { + throw $e; + } + } + + /* + * Create and execute the HTTP CURL request. + * + * @param string $url HTTP Url. + * @param string $authHeader Authorization Header string. + * @param string $postData Data to post. + * + * @return string. + * + */ + private function curlRequest($url, $authHeader) { + //Initialize the Curl Session. + $ch = curl_init(); + //Set the Curl url. + curl_setopt ($ch, CURLOPT_URL, $url); + //Set the HTTP HEADER Fields. + curl_setopt ($ch, CURLOPT_HTTPHEADER, array($authHeader,"Content-Type: text/xml")); + //CURLOPT_RETURNTRANSFER- TRUE to return the transfer as a string of the return value of curl_exec(). + curl_setopt ($ch, CURLOPT_RETURNTRANSFER, TRUE); + //CURLOPT_SSL_VERIFYPEER- Set FALSE to stop cURL from verifying the peer's certificate. + curl_setopt ($ch, CURLOPT_SSL_VERIFYPEER, False); + //Execute the cURL session. + $curlResponse = curl_exec($ch); + //Get the Error Code returned by Curl. + $curlErrno = curl_errno($ch); + if ($curlErrno) { + $curlError = curl_error($ch); + curl_close($ch); + throw new Exception($curlError); + } + //Close a cURL session. + curl_close($ch); + return $curlResponse; + } +} +?> \ No newline at end of file diff --git a/inc/lib.translater.proposal.php b/inc/lib.translater.proposal.php new file mode 100644 index 0000000..0daa7c5 --- /dev/null +++ b/inc/lib.translater.proposal.php @@ -0,0 +1,125 @@ +core = $core; + $this->setup(); + } + + /** + Set name of this tool + + @param string Tool's name + */ + final protected function setName($name) + { + $this->name = (string) $name; + } + + /** + Get name of this tool + + @return string Tool's name + */ + final public function getName() + { + return $this->name; + } + + /** + Set description of this tool + + @param string Tool's description + */ + final protected function setDesc($desc) + { + $this->desc = (string) $desc; + } + + /** + Get description of this tool + + @return string Tool's description + */ + final public function getDesc() + { + return $this->desc; + } + + /** + Set tool as (un)active + + @param boolean $active True to set it as active + */ + final protected function setActive($active) + { + $this->active = (boolean) $active; + } + + /** + Check if this tool is active + + @return boolean True if it is active + */ + final public function isActive() + { + return $this->active; + } + + /** + Set tool's info - using setName(),setDesc(),setActive() + */ + abstract protected function setup(); + + /** + Get configuration interface + + @return Form field + */ + abstract public function form(); + + /** + Save configuration + */ + abstract public function save(); + + /** + Translate a string from a language to another + + @param string $str Trimed UTF-8 string to translate + @param string $from Source language code + @param string to Destination language code + @return Translated string + */ + abstract public function translate($str,$from,$to); +} +?> \ No newline at end of file diff --git a/index.php b/index.php new file mode 100644 index 0000000..ee30555 --- /dev/null +++ b/index.php @@ -0,0 +1,1386 @@ + __('Settings'), + 'modules_plugin' => __('Plugins'), + 'modules_theme' => __('Themes'), + 'pack' => __('Import/Export') +); + +$combo_backup_limit = array( + 5 => 5, + 10 => 10, + 15 => 15, + 20 => 20, + 40 => 40, + 60 => 60 +); + +$combo_backup_folder = array( + 'module' => __('locales folders of each module'), + 'plugin' => __('plugins folder root'), + 'public' => __('public folder root'), + 'cache' => __('cache folder of Dotclear'), + 'translater' =>__('locales folder of translater') +); + +$succes = array( + 'save_setting' => __('Configuration successfully updated'), + 'update_lang' => __('Translation successfully updated'), + 'add_lang' => __('Translation successfully created'), + 'delete_lang' => __('Translation successfully deleted'), + 'create_backup' => __('Backups successfully create'), + 'restore_backup' => __('Backups successfully restored'), + 'delete_backup' => __('Backups successfully deleted'), + 'import_pack' => __('Package successfully imported'), + 'export_pack' => __('Package successfully exported') +); + +$errors = array( + 'save_setting' => __('Failed to update settings: %s'), + 'update_lang' => __('Failed to update translation: %s'), + 'add_lang' => __('Failed to create translation: %s'), + 'delete_lang' => __('Failed to delete translation: %s'), + 'create_backup' => __('Failed to create backups: %s'), + 'restore_backup' => __('Failed to restore backups: %s'), + 'delete_backup' => __('Failed to delete backups: %s'), + 'import_pack' => __('Failed to import package: %s'), + 'export_pack' => __('Failed to export package: %s') +); + +$p_url = 'plugin.php?p=translater'; +$start_page = @explode('_',$translater->start_page); +if (count($start_page) < 2) $start_page[1] = ''; + +# +# Parse request +# + +$msg = isset($_REQUEST['msg']) ? $_REQUEST['msg'] : ''; +$part = isset($_REQUEST['part']) ? $_REQUEST['part'] : $start_page[0]; +$tab = isset($_REQUEST['tab']) ? $_REQUEST['tab'] : ''; +$action = isset($_REQUEST['action']) ? $_REQUEST['action'] : ''; +$type = isset($_REQUEST['type']) ? $_REQUEST['type'] : $start_page[1]; +$module = isset($_REQUEST['module']) ? $_REQUEST['module'] : ''; +$from = isset($_POST['from']) && $_POST['from'] != '-' ? $_POST['from'] : ''; +$lang = isset($_REQUEST['lang']) && $_REQUEST['lang'] != '-' ? $_REQUEST['lang'] : ''; +if ($type == '-' || $module == '-') +{ + $type = $module = ''; +} + +# +# Manage action +# + +switch ($action) +{ + case '': + break; + + /** + * Create lang for a module + */ + case 'module_add_lang': + + try + { + if (empty($lang)) + { + throw new Exception(__('No lang to create')); + } + $translater->addLang($module,$lang,$from); + + http::redirect($p_url.'&part=lang&module='.$module.'&type='.$type.'&lang='.$lang.'&msg=add_lang'); + } + catch (Exception $e) + { + $core->error->add(sprintf($errors[$action],$e->getMessage())); + } + + break; + + /** + * Delete lang for a module + */ + case 'module_delete_lang': + + try + { + if (empty($lang)) + { + throw new Exception(__('No lang to delete')); + } + $translater->delLang($module,$lang); + + http::redirect($p_url.'&part=module&module='.$module.'&type='.$type.'&tab=module-lang&msg=delete_lang'); + } + catch (Exception $e) + { + $core->error->add(sprintf($errors[$action],$e->getMessage())); + } + + break; + + /** + * Create backup for a module + */ + case 'module_create_backup': + + try + { + if (empty($_POST['modules']) || empty($_POST['langs'])) + { + throw new Exception(__('No lang to backup')); + } + + foreach($_POST['modules'] as $b_module) + { + $b_list = $translater->listLangs($b_module); + foreach($_POST['langs'] as $b_lang) + { + if (isset($b_list[$b_lang])) + { + $translater->createBackup($b_module,$b_lang); + } + } + } + + http::redirect($p_url.'&part=module&module='.$module.'&type='.$type.'&tab=module-backup&msg=creat_backup'); + } + catch (Exception $e) + { + $core->error->add(sprintf($errors[$action],$e->getMessage())); + } + + break; + + /** + * Restore backup for a module + */ + case 'module_restore_backup': + + try + { + if (empty($_POST['modules']) || empty($_POST['files'])) + { + throw New Exception(__('No blackup to restore')); + } + + sort($_POST['files']); + $done = false; + foreach($_POST['modules'] as $b_module) + { + $b_list = $translater->listBackups($b_module,true); + foreach($_POST['files'] as $b_file) + { + if (in_array($b_file,$b_list)) + { + $translater->restoreBackup($b_module,$b_file); + $done = true; + } + } + } + if (!$done) + { + throw new Exception(__('No bakcup to to restore')); + } + + http::redirect($p_url.'&part=module&module='.$module.'&type='.$type.'&tab=module-backup&msg=restore_backup'); + } + catch (Exception $e) + { + $core->error->add(sprintf($errors[$action],$e->getMessage())); + } + + break; + + /** + * Delete backup for a module + */ + case 'module_delete_backup': + + try + { + if (empty($_POST['modules']) || empty($_POST['files'])) + { + throw New Exception(__('No backup to delete')); + } + + $done = false; + foreach($_POST['modules'] as $b_module) + { + $b_list = $translater->listBackups($b_module,true); + foreach($_POST['files'] as $b_file) + { + if (in_array($b_file,$b_list)) + { + $translater->deleteBackup($b_module,$b_file); + $done = true; + } + } + } + if (!$done) + { + throw new Exception(__('No backup to delete')); + } + + http::redirect($p_url.'&part=module&module='.$module.'&type='.$type.'&tab=module-backup&msg=delete_backup'); + } + catch (Exception $e) + { + $core->error->add(sprintf($errors[$action],$e->getMessage())); + } + + break; + + /** + * Import language package for a module + */ + case 'module_import_pack': + + try + { + if (empty($_FILES['packfile']['name'])) + { + throw new Exception(__('Nothing to import')); + } + $translater->importPack($_POST['modules'],$_FILES['packfile']); + + http::redirect($p_url.'&part=module&module='.$module.'&type='.$type.'&tab=module-pack&msg=import_pack'); + } + catch (Exception $e) + { + $core->error->add(sprintf($errors[$action],$e->getMessage())); + } + + break; + + /** + * Export language package for a module + */ + case 'module_export_pack': + + try + { + if (empty($_POST['modules']) || empty($_POST['entries'])) + { + throw new Exception(__('Nothing to export')); + } + $translater->exportPack($_POST['modules'],$_POST['entries']); + + http::redirect($p_url.'&part=module&module='.$module.'&type='.$type.'&tab=module-pack&msg=export_pack'); + } + catch (Exception $e) + { + $core->error->add(sprintf($errors[$action],$e->getMessage())); + } + + break; + + /** + * Update language + */ + case 'update_lang': + + try + { + if (empty($_POST['entries']) || empty($lang) || empty($module)) + { + throw new Exception(__('No language to update')); + } + foreach($_POST['entries'] as $i => $entry) + { + if (isset($entry['check']) && isset($_POST['multigroup'])) + { + $_POST['entries'][$i]['group'] = $_POST['multigroup']; + unset($_POST['entries'][$i]['check']); + } + } + $translater->updLang($module,$lang,$_POST['entries']); + + http::redirect($p_url.'&part=lang&module='.$module.'&type='.$type.'&lang='.$lang.'&msg='.$action); + } + catch (Exception $e) + { + $core->error->add(sprintf($errors[$action],$e->getMessage())); + } + + break; + + /** + * Import language packages + */ + case 'import_pack': + + try + { + if (empty($_FILES['packfile']['name'])) + { + throw new Exception(__('Nothing to import')); + } + $translater->importPack($_POST['modules'],$_FILES['packfile']); + + http::redirect($p_url.'&part=pack&msg='.$action.'&tab=pack-import'); + } + catch (Exception $e) + { + $core->error->add(sprintf($errors[$action],$e->getMessage())); + } + + break; + + /** + * Export language packages + */ + case 'export_pack': + + try + { + if (empty($_POST['modules']) || empty($_POST['entries'])) + { + throw new Exception(__('Nothing to export')); + } + $translater->exportPack($_POST['modules'],$_POST['entries']); + + http::redirect($p_url.'&part=pack&msg='.$action.'&tab=pack-export'); + } + catch (Exception $e) + { + $core->error->add(sprintf($errors[$action],$e->getMessage())); + } + + break; + + /** + * Save settings + */ + case 'save_setting': + + try + { + if (empty($_POST['translater_write_po']) + && empty($_POST['translater_write_langphp'])) + { + throw new Exception('You must choose one file format at least'); + } + + foreach($translater->getDefaultSettings() as $k => $v) + { + $translater->set($k,(isset($_POST['translater_'.$k]) ? $_POST['translater_'.$k] : '')); + } + + foreach($translater->proposal->getTools() AS $k => $v) + { + $v->save(); + } + + http::redirect($p_url.'&part=setting&msg='.$action); + } + catch (Exception $e) + { + $core->error->add(sprintf($errors[$action],$e->getMessage())); + } + + break; + + /** + * Modules / Unknow / None + */ + default: + break; +} + +# +# Fill in title and prepare display +# + +switch ($part) +{ + /** + * Modules + */ + case 'modules': + + $title = ''.($type == 'theme' ? __('Themes') : __('Plugins')).''; + + break; + + /** + * Module + */ + case 'module': + + # Get info about requested module + try + { + $M = $translater->getModule($module,$type); + } + catch(Exception $e) + { + $core->error->add(sprintf(__('Failed to launch translater: %s'),$e->getMessage())); + $action = $module = $type = ''; + $M = false; + } + if (!empty($module) && !empty($type) && !$M) + { + $action = $module = $type = ''; + $M = false; + } + + $M->langs = $translater->listLangs($module); + $M->backups = $translater->listBackups($module); + $M->unused_langs = array_flip(array_diff($translater->getIsoCodes(),$M->langs)); + $M->used_langs = array_flip(array_diff($M->langs,array_flip($translater->getIsoCodes()))); + $allowed_groups = array_combine( + dcTranslater::$allowed_l10n_groups, + dcTranslater::$allowed_l10n_groups + ); + + $title = + ''.($type == 'theme' ? __('Themes') : __('Plugins')).''. + ' › '.$module.''; + + break; + + /** + * Lang + */ + case 'lang': + + # Get infos on module wanted + try + { + $M = $translater->getModule($module,$type); + + # Retrieve some infos + $M->langs = $translater->listLangs($module); + $M->backups = $translater->listBackups($module); + $M->unused_langs = array_flip(array_diff($translater->getIsoCodes(),$M->langs)); + $M->used_langs = array_flip(array_diff($M->langs,array_flip($translater->getIsoCodes()))); + $allowed_groups = array_combine( + dcTranslater::$allowed_l10n_groups, + dcTranslater::$allowed_l10n_groups + ); + } + catch(Exception $e) + { + $core->error->add(sprintf(__('Failed to launch translater: %s'),$e->getMessage())); + $action = $module = $type = ''; + $M = false; + } + if (!empty($module) && !empty($type) && !$M) + { + $action = $module = $type = ''; + $M = false; + } + + $title = + ''.($type == 'theme' ? __('Themes') : __('Plugins')).''. + ' › '. + ''.$module.''; + if (!empty($M->langs) && isset($M->langs[$lang])) + { + $title .= ' › '.$M->langs[$lang].''; + } + + break; + + /** + * Import/Export (pack) + */ + case 'pack': + + $title = ''.__('Import/Export').''; + + break; + + /** + * Settings + */ + case 'setting': + default: + + $title = ''.__('Settings').''; + + break; +} + +# +# Display page +# + +echo ' + +'.__('Translater').''. +dcPage::jsPageTabs($tab). +dcPage::jsLoad('js/_posts_list.js'). +dcPage::jsLoad('index.php?pf=translater/js/jquery.translater.js'); + +if ('' != $translater->proposal_tool) +{ + echo + '\n". + "\n"; +} + +# --BEHAVIOR-- translaterAdminHeaders +$core->callBehavior('translaterAdminHeaders'); + +echo +' +

'.__('Translater').' › '.$title. +' - '.__('Plugins').''. +' - '.__('Themes').''. +' - '.__('Import/Export').''. +'

'; + +if (isset($succes[$msg])) +{ + dcPage::message($succes[$msg]); +} + +switch ($part) +{ + /** + * Modules + */ + case 'modules': + + echo '
'; + + $res = ''; + foreach ($translater->listModules($type) as $k => $v) + { + if ($translater->hide_default && in_array($k,dcTranslater::$default_dotclear_modules[$type])) + { + continue; + } + + if ($v['root_writable']) + { + $res .= + ''. + ''. + ''.$k.''; + } + else + { + $res .= + ''. + ''.$k.''; + } + $res .= + ''.$v['version'].''. + ''; + + $array_langs = array(); + foreach ($translater->listLangs($k) as $lang_name => $lang_infos) + { + $array_langs[$lang_name] = + ''. + $lang_name.''; + } + $res .= implode(', ',$array_langs). + ''. + ''; + } + if ($res) + { + echo ' + + + + + + '. + $res. + '
'.__('Id').''.__('Version').''.__('Languages').'
'; + + } + else + { + echo ''.__('There is no editable modules').''; + } + echo ' +

 

+ +
'; + + break; + + /** + * Module + */ + case 'module': + + # Summary + echo ' +
+

'.__('Module').'

+ + + + + + + + + + + + + + + +
'.__('About').'
'.__('Name').' '.$M->name.'
'.__('Version').' '.$M->version.'
'.__('Author').' '.$M->author.'
'.__('Type').' '.$M->type.'
'.__('Root').' '.$M->root.'
'.__('Backups').' '. + $translater->getBackupFolder($module).'
+

 

'; + + if (count($M->langs)) + { + echo + '

'.__('l10n').'

'. + ''. + ''. + ''. + ''. + ''. + ''. + ''; + + foreach($M->langs AS $lang => $name) + { + echo + ''. + ''. + ''; + + if (isset($M->backups[$lang])) + { + foreach($M->backups[$lang] AS $file => $info) + { + $time[$lang] = isset($time[$lang]) && $time[$lang] > $info['time'] ? + $time[$lang] : $info['time']; + } + echo + ''. + ''; + } + else + { + echo ''; + } + echo ''; + } + echo '
'.__('Languages').''.__('Code').''.__('Backups').''.__('Last backup').'
'. + ''.$name.''. + ' '.$lang.''.count($M->backups[$lang]).' '. + dt::str('%Y-%m-%d %H:%M',$time[$lang],$core->blog->settings->system->blog_timezone). + ''.__('no backup').'
'; + } + echo '
'; + + # Add/Remove/Edit lang + echo '
'; + + # Edit lang + if (!empty($M->langs)) + { + echo ' +

'.__('Edit language').'

+
+

'.__('Select language:').' '. + form::combo(array('lang'),$M->used_langs,$lang).'

+

'. + $core->formNonce(). + form::hidden(array('type'),$type). + form::hidden(array('module'),$module). + form::hidden(array('action'),''). + form::hidden(array('part'),'lang'). + form::hidden(array('p'),'translater').' +

+
+

 

'; + } + + # New lang + if (!empty($M->unused_langs)) + { + echo ' +

'.__('Add language').'

+
+

'.__('Select language:').' '. + form::combo(array('lang'),array_merge(array('-'=>'-'),$M->unused_langs),$core->auth->getInfo('user_lang')).'

'; + if (!empty($M->used_langs)) + { + echo + '

'.__('Copy from language:').' '. + form::combo(array('from'),array_merge(array('-'=>'-'),$M->used_langs)). + ' ('.__('Optionnal').')

'; + } + else + { + echo '

'.form::hidden(array('from'),'').'

'; + } + echo ' +

'. + $core->formNonce(). + form::hidden(array('type'),$type). + form::hidden(array('module'),$module). + form::hidden(array('action'),'module_add_lang'). + form::hidden(array('part'),'module'). + form::hidden(array('tab'),'module-lang'). + form::hidden(array('p'),'translater').' +

+
+

 

'; + } + + # Delete lang + if (!empty($M->used_langs)) + { + echo ' +

'.__('Delete language').'

+
+

'.__('Select language:').' '. + form::combo(array('lang'),array_merge(array('-'=>'-'),$M->used_langs)).'

+

'. + $core->formNonce(). + form::hidden(array('type'),$type). + form::hidden(array('module'),$module). + form::hidden(array('action'),'module_delete_lang'). + form::hidden(array('part'),'module'). + form::hidden(array('tab'),'module-lang'). + form::hidden(array('p'),'translater').' +

+
+

 

'; + } + echo '
'; + + # Create/delete/restore backups + if (!empty($M->used_langs) || !empty($M->backups)) { + + echo '
'; + + if (!empty($M->used_langs)) + { + echo ' +

'.__('Create backups').'

+
+

'.__('Choose languages to backup').'

+ + '; + $i=0; + foreach($M->used_langs AS $name => $lang) + { + $i++; + echo ' + + + + + '; + } + echo ' +
'.form::checkbox(array('langs[]'),$lang,'','','',false).''.$name.''.$lang.'
+
+

 

+

 

+
+

+ '. + form::hidden(array('modules[]'),$module). + $core->formNonce(). + form::hidden(array('type'),$type). + form::hidden(array('module'),$module). + form::hidden(array('action'),'module_create_backup'). + form::hidden(array('part'),'module'). + form::hidden(array('tab'),'module-backup'). + form::hidden(array('p'),'translater').' +

+
+

 

'; + } + + if (!empty($M->backups)) + { + echo + '

'.__('List of backups').'

'. + '
'. + ''. + ''. + ''. + ''. + ''. + ''. + ''; + $i=0; + foreach($M->backups as $lang => $langs) + { + foreach($langs as $file => $infos) + { + $i++; + echo + ''. + ''. + ''. + ''. + ''. + ''. + ''; + } + } + echo ' +
'.__('File').''.__('Date').''.__('Language').''.__('Size').'
'.form::checkbox(array('files[]'),$file,'','','',false).''.$file.''. + dt::str(__('%Y-%m-%d %H:%M:%S'),$infos['time'],$core->blog->settings->system->blog_timezone). + ''.$translater->isIsoCode($lang).''.files::size($infos['size']).'
+
+

 

+

'.__('Selected backups action:').' '. + form::combo('action',array( + __('Restore backups') => 'module_restore_backup', + __('Delete backups') => 'module_delete_backup') + ).' + '. + form::hidden(array('modules[]'),$module). + $core->formNonce(). + form::hidden(array('type'),$type). + form::hidden(array('module'),$module). + form::hidden(array('part'),'module'). + form::hidden(array('tab'),'module-backup'). + form::hidden(array('p'),'translater').' +

+
+
+

 

'; + } + + echo '
'; + } // end if (!empty($M->used_langs) || !empty($M->backups)) { + + # Import/Export pack + echo '
'; + + # Import + echo ' +

'.__('Import').'

+
+

'.__('Choose language package to import').'
+

+

+ '. + form::hidden(array('modules[]'),$module). + $core->formNonce(). + form::hidden(array('type'),$type). + form::hidden(array('module'),$module). + form::hidden(array('action'),'module_import_pack'). + form::hidden(array('part'),'module'). + form::hidden(array('tab'),'module-pack'). + form::hidden(array('p'),'translater').' +

+
+

 

'; + + # Export + if (!empty($M->used_langs)) + { + echo + '

'.__('Export').'

'. + '
'. + '

'.__('Choose languages to export').'

'. + ''. + ''; + $i=0; + foreach($M->used_langs AS $name => $lang) + { + $i++; + echo + ''. + ''. + ''. + ''. + ''; + } + echo + '
'. + form::checkbox(array('entries[]'),$lang,'','','',false). + ''.$name.''.$lang.'
'. + '
'. + '

 

'. + '

 

'. + '
'. + '

'. + ''. + form::hidden(array('modules[]'),$module). + $core->formNonce(). + form::hidden(array('type'),$type). + form::hidden(array('module'),$module). + form::hidden(array('action'),'module_export_pack'). + form::hidden(array('part'),'module'). + form::hidden(array('tab'),'module-pack'). + form::hidden(array('p'),'translater'). + '

'. + '
'. + '

 

'; + } + echo '
'; + + break; + + /** + * Lang + */ + case 'lang': + + # Existing langs + if (empty($M->langs) || !isset($M->langs[$lang])) + { + break; + } + + $iso = $M->langs[$lang]; + + $i = 0; + $sort_order = 'asc'; + $lines = $translater->getMsgs($module,$lang); + + # Sort array + if (isset($_GET['sort']) && !empty($lines)) + { + $sort = explode(',',$_GET['sort']); + $sort_by = $sort[0]; + $sort_order = isset($sort[1]) && $sort[1] == 'desc' ? 'asc' : 'desc'; + + switch($sort_by) + { + case 'group': + foreach($lines AS $k => $v) + { + $sort_list[] = $v['group']; + } + break; + + case 'msgid': + foreach($lines AS $k => $v) + { + $sort_list[] = strtolower($k); + } + break; + + case 'file': + foreach($lines AS $k => $v) + { + $file = array(); + foreach($v['files'] as $fv) + { + $file[] = empty($fv[0]) || empty($fv[1]) ? '' : $fv[0].($fv[1] /1000); + } + sort($file); + $sort_list[] = $file[0]; + } + break; + + case 'msgstr': + foreach($lines AS $k => $v) + { + $sort_list[] = strtolower($v['msgstr']); + } + break; + + default: + $sort_list = false; + break; + } + if ($sort_list) + { + array_multisort( + $sort_list, + ($sort_order == 'asc' ? SORT_DESC : SORT_ASC), + SORT_STRING, + $lines + ); + } + } + + echo + '
'. + '
'. + ''. + ''. + ''. + ''. + ''. + ''. + ''. + ''; + + foreach ($lines AS $msgid => $rs) + { + $i++; + $in_dc = ($rs['in_dc'] && $translater->parse_nodc); + + echo + ''. + + ''. + + 'proposal_tool ? ' class="translatermsgid"' : '' ).'>'. + html::escapeHTML($msgid).''. + + ''. + + ''. + + ''. + ''; + } + + $i++; + echo + ''. + ''. + ''. + ''. + ''. + ''. + ''. + '
'.__('Group').''.__('String').''.__('Translation').''.__('Existing').''.__('File').'
'. + form::checkbox(array('entries['.$i.'][check]'),1).' '. + form::combo(array('entries['.$i.'][group]'), + $allowed_groups,$rs['group'],'','',$in_dc + ). + ''. + form::hidden(array('entries['.$i.'][msgid]'),html::escapeHTML($msgid)). + form::field(array('entries['.$i.'][msgstr]'), + 75,255,html::escapeHTML($rs['msgstr']),'','',$in_dc). + ''; + $strin = array(); + foreach($rs['o_msgstrs'] AS $o_msgstr) + { + if (!isset($strin[$o_msgstr['msgstr']])) + { + $strin[$o_msgstr['msgstr']] = ''; + } + $strin[$o_msgstr['msgstr']][] = array('module'=>$o_msgstr['module'],'file'=>$o_msgstr['file']); + } + foreach($strin as $k => $v) + { + echo '
'.html::escapeHTML($k).'
'; + foreach($v as $str) + { + echo ''.html::escapeHTML($str['module'].' => '.$str['file']).'
'; + } + echo '

'; + } + echo + '
'; + if (empty($rs['files'][0])) + { + echo ' '; + } + elseif (count($rs['files']) == 1) + { + echo $rs['files'][0][0].' : '.$rs['files'][0][1]; + } + else + { + echo + ''.sprintf(__('%s occurrences'),count($rs['files'])).''. + '
'; + foreach($rs['files'] as $location) + { + echo ''.implode(' : ',$location).'
'; + } + echo '
'; + } + echo + '
'. + form::checkbox(array('entries['.$i.'][check]'),1).' '. + form::combo(array('entries['.$i.'][group]'),$allowed_groups,'main'). + ''.form::field(array('entries['.$i.'][msgid]'),75,255,'').''.form::field(array('entries['.$i.'][msgstr]'),75,255,'').'  
'. + '

'.sprintf(__('Total of %s strings.'),$i-1).'

'. + '

'. + '

'.__('Change the group of the selected entries to:').' '. + form::combo(array('multigroup'),$allowed_groups). + '

'. + '

'. + ''. + $core->formNonce(). + form::hidden(array('lang'),$lang). + form::hidden(array('type'),$type). + form::hidden(array('module'),$module). + form::hidden(array('action'),'update_lang'). + form::hidden(array('part'),'lang'). + form::hidden(array('p'),'translater'). + '

'. + '
'. + '

 

'. + '
'; + + break; + + /** + * Import/Export (Pack) + */ + case 'pack': + + # Import + echo ' +
+
+

'.__('Choose language package to import').'
+

+

+ '; + $i=0; + foreach($translater->listModules() AS $k => $v) + { + if ($translater->hide_default && ( + in_array($k,dcTranslater::$default_dotclear_modules['plugin']) || + in_array($k,dcTranslater::$default_dotclear_modules['theme']))) continue; + + echo form::hidden(array('modules[]'),$k);$i++; + } + echo + $core->formNonce(). + form::hidden(array('type'),$type). + form::hidden(array('module'),$module). + form::hidden(array('action'),'import_pack'). + form::hidden(array('part'),'pack'). + form::hidden(array('tab'),'pack-import'). + form::hidden(array('p'),'translater').' +

+
+
'; + + # Export + echo ' +
+
+

'.__('Choose modules to export').'

+ + '; + $i=0; + $langs_list = array(); + + foreach($translater->listModules() AS $k => $v) + { + if ($translater->hide_default && ( + in_array($k,dcTranslater::$default_dotclear_modules['plugin']) || + in_array($k,dcTranslater::$default_dotclear_modules['theme']))) continue; + + $info_lang = $translater->listLangs($k); + if (!is_array($info_lang) || 1 > count($info_lang)) continue; + + $i++; + $langs_list = array_merge($langs_list,$info_lang); + + echo ' + + + + + '; + } + + echo ' +
'.__('Modules').''.__('Languages').'
'.form::checkbox(array('modules[]'),$k,'','','',false).''.$v['name'].''.implode(', ',$info_lang).'
+

'.__('Choose languages to export').'

+ + '; + $i=0; + foreach($langs_list AS $k => $v) + { + $i++; + echo ' + + + + + '; + } + echo ' +
'.__('Languages').''.__('Code').'
'.form::checkbox(array('entries[]'),$k,'','','',false).''.$v.''.$k.'
+
+

+

 

+
+

+ '. + $core->formNonce(). + form::hidden(array('type'),$type). + form::hidden(array('module'),$module). + form::hidden(array('action'),'export_pack'). + form::hidden(array('part'),'pack'). + form::hidden(array('tab'),'pack-export'). + form::hidden(array('p'),'translater').' +

+
+
'; + + break; + + /** + * Settings + */ + case 'setting': + default: + + echo ' +
+ +
+

+

+

+

+

+

+


+ '.form::field('translater_parse_userinfo',65,255,$translater->parse_userinfo).'

+
+ +
+

+ +

'.__('Select and configure the tool to use to translate strings:').'

'; + + foreach($translater->proposal->getTools() AS $k => $v) + { + $form = $v->form(); + + echo ' +
+
'. + (empty($form) ? + '

'.sprintf(__('Nothing to configure for %s tool.'),$v->getName()).'

' : + $form + ).'
'; + } + + echo ' +
+ +
+

+

+
+ +
+

+

+

+
+ +
+

+

+

+
+ +
+

'. + $core->formNonce(). + form::hidden(array('p'),'translater'). + form::hidden(array('part'),'setting'). + form::hidden(array('action'),'save_setting').' +

+
'; + + break; +} + +dcPage::helpBlock('translater'); + +echo +'

'. +''.__('Settings').' - '. +'translater - '.$core->plugins->moduleInfo('translater','version').'  +'.__('Translater').'

+'; +?> \ No newline at end of file diff --git a/js/jquery.translater.js b/js/jquery.translater.js new file mode 100644 index 0000000..a0766a8 --- /dev/null +++ b/js/jquery.translater.js @@ -0,0 +1,91 @@ +/* -- BEGIN LICENSE BLOCK ---------------------------------- + * + * This file is part of translater, a plugin for Dotclear 2. + * + * Copyright (c) 2009-2013 Jean-Christian Denis and contributors + * contact@jcdenis.fr + * + * Licensed under the GPL version 2.0 license. + * A copy of this license is available in LICENSE file or at + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * + * -- END LICENSE BLOCK ------------------------------------*/ + +;if(window.jQuery) (function($){ + $.fn.translater = function(options){ + var opts = $.extend({}, $.fn.translater.defaults, options); + return this.each(function(){ + + var img = ''; + var tog = ''; + var line = this; + var msgid = $(line).children('.translatermsgid'); + var msgfile = $(line).children('.translatermsgfile'); + var msgstr = $(line).children('.translatermsgstr'); + var target = $(line).children('.translatertarget'); + + $('.strlist').hide(); + + var img_go = $(''+img+'').css('cursor','pointer'); + $(msgid).prepend(' ').prepend(img_go); + $(img_go).click(function(){ + var txt = $(msgid).text(); + $(img_go).css('cursor','wait'); + $.get(opts.url,{f:opts.func,langFrom:opts.from,langTo:opts.to,langTool:opts.tool,langStr:txt},function(data){ + data=$(data); + if(data.find('rsp').attr('status')=='ok' && $(data).find('proposal').attr('str_to')){ + var resp = $(data).find('proposal').attr('str_to'); + if (confirm(opts.title+'\n'+resp)){ + addText(target,resp); + $(img_go).css('cursor','pointer'); + } + else{ + $(img_go).css('cursor','pointer'); + } + }else{ + alert(opts.failed); + $(img_go).css('cursor','pointer'); + } + }); + }); + + $(msgstr).children('.subtranslatermsgstr').each(function(){ + var img_str = $(''+tog+'').css('cursor','pointer'); + $(this).children('strong').each(function(){ + var txt = $(this).text(); + var img_add = $(''+img+'').css('cursor','pointer'); + $(this).prepend(' ').prepend(img_add); + $(img_add).click(function(){addText(target,txt);}); + + $(this).append(' ').append(img_str); + var strlist=$(this).siblings('.strlist'); + $(strlist).click(function(){$(strlist).toggle();}); + $(img_str).click(function(){$(strlist).toggle();}); + }); + }); + + var img_file = $(''+tog+'').css('cursor','pointer'); + $(msgfile).children('strong').each(function(){ + $(this).append(' ').append(img_file); + var strlist=$(this).siblings('.strlist'); + $(strlist).click(function(){$(strlist).toggle();}); + $(img_file).click(function(){$(strlist).toggle();}); + }); + }); + }; + function addText(target,text){ + $(target).children(':text').val(text); + } + + $.fn.translater.defaults = { + url: '', + func: '', + from: 'en', + to: 'fr', + tool: 'google', + failed: 'Failed to translate this', + title: 'Copy translation to field:', + title_go: 'Find translation', + title_add: 'Add this translation' + }; +})(jQuery); \ No newline at end of file diff --git a/locales/fr/help/help.html b/locales/fr/help/help.html new file mode 100644 index 0000000..643a622 --- /dev/null +++ b/locales/fr/help/help.html @@ -0,0 +1,174 @@ + + + Translater + + + + +

Paramètres

+
+
Activer l'interface simple
+
Permet de n'avoir que l'essentiel sous les yeux.
+ +
Utiliser deux colonnes sur la page d'administrations
+
Permet d'avoir un affichage sur deux colonnes.
+ +
Activer le menu de la page des extensions
+
Ajoute un onglet sur la page de gestion des extensions.
+ +
Activer le menu de la page des thèmes
+
Ajoute un lien sur les details du thème sélectionné sur le pages de gestion des thèmes.
+ +
Ecrire les fichier .po
+
C'est le format par défaut des fichiers de langue de Dotclear.
+ +
Ecrire les fichiers .lang.php
+
C'est le format rapide des fichiers de langues. + Peu importe le choix de fichier, vous devez au moins en selectionner un.
+ +
Traduire également les chaines des fichiers de template
+
Cette option permet de traduire les balises de langage des fichiers .html. Ex: {{tpl:lang Lorem}}.
+ +
Traduire uniquement les chaines inconnues
+
Cette option est recommander est permet de griser les chaines déjà traduite dans Dotclear.
+ +
Ecrire les commentaires dans les fichiers
+
Cela est pratique lors du developpement d'une extensions, le nom et la ligne ou se trouve la chaine sont alors écrit dans le fichier de langue. + Il est préfèrable de désactiver cette option lors de la réalisation de version finale, afin d'avoir des fichiers plus légés.
+ +
Ecrire les informations à propos de l'auteur
+
Ajoute aux fichiers de langue (si les commentaires sont actifs) des informations à propos de l'auteur de la traduction. + Ces informations peuvent ne pas être présentes pour un auteur elles seront alors ignorées. Il accepte les masques suivants: +
    +
  • displayname : pseudo de l'auteur
  • +
  • firstname : prénom de l'auteur
  • +
  • name : nom de l'auteur
  • +
  • email : email de l'auteur
  • +
  • url : site de l'auteur
  • +
+
+ +
Utiliser un outil d'aide à la traduction
+
Une fois qu'un outil est choisi, lors d'un click sur une texte à traduire dans l'edition d'une traduction, + une alerte javascript avec la traduction apparait.
+ +
Langue par default des sources l10n
+
Langue des textes présents dans les fonctions:
__('...') et {{tpl:lang ...}.
+ +
Ecraser les langages existants
+
Lors de l'import d'un paquetage de langue, si cette option est activé, une langue présente dans l'extension sera remplacé par celle venant du paquetage.
+ +
Nom du paquetage exporté
+
C'est le nom par défaut du fichier zip lorsque vous exportez des sauvegardes.
+
Il accepte les masques suivants: +
    +
  • type : plugin ou thème
  • +
  • module : nom du module
  • +
  • version : la version du module
  • +
  • timestamp : le temps unix
  • +
  • %Y%m%D.... : les masques de date Dotclear
  • +
+
+ +
Faire des sauvegardes lors de modifications
+
Permet de faire automatiquement des sauvegardes des fichiers de langue à chaque changement enregistré.
+ +
Limiter les sauvegardes
+
Permet de limiter le nombre de sauvegarde par module afin de ne pas prendre trop de place sur la plateforme. + Les sauvegardes ne sont pas effacées automatiquement il faut le faire dans le gesionnaire de sauvegarde.
+ +
Stoquer les sauvegardes dans un dossier particulier
+
Plusieurs choix de lieu de sauvegarde s'offrent: +
    +
  • Repertoires locales de chaque module
  • +
  • Repertoire racine des extensions
  • +
  • Repertoire publique (du blog en cours)
  • +
  • Repertoire cache de Dotclear
  • +
  • Repertoire unique du module translater
  • +
+
+
Pour ne pas tous mélanger il est préférable de stoquer les sauvegardes dans chaque module associé.
+
+ +

Ajout/Supression/Modification

+

Cet onglet permet d'ajouter, de supprimer ou d'étider des langues d'une extension. +Il est possible de copier la traduction d'une langue à une autre lors de sa création. +La supression d'une langue ne supprimera pas les éventuelles sauvegardes associées à cette langue.

+ +

Edition d'une langue

+
+
Groupe
+
Cette option est uniquement présente en mode avancé. + C'est le nom du fichier de langue. + D'autres choix sont possibles mais Dotclear ne recherche que le groupe main pour un plugin et public pour un thème. + Pour de gros plugin ou tout simplement pour des raisons de commodité (!) vous pouvez vouloir séparer certaines traductions. + Il faut alors demander explicitement à Dotclear de les charger. + Exemple: l10n::set('mon_repertoire/error');
+ +
Chaine
+
C'est la chaine à traduire trouvée dans l'extension. elle est apellé avec __('ma_chaine') .
+ +
Fichier
+
Cette option est uniquement présente en mode avancé. + C'est la liste des fichiers ainsi que la ligne ou se trouve la chaine.
+ +
Traduction
+
Traduction donnée à cette chaine pour la langue choisie. + Cette case est grisée (Inaccessible) si cette chaine est déjà traduite et + que l'option "Traduire uniquement les chaines inconnues" est selectionnée dans les paramètres.
+ +
Existant
+
Indique la traduction et l'extension qui utilise cette chaine le cas échéant.
+
+ +

Sauvegardes

+
+
Créer une sauvegarde
+
Si des traductions existent, une partie "Créer une sauvegarde" apparait. + Il est possible de faire une sauvegarde (au format zip) d'une langue d'une extension. + Elle sera stoquée dans le repertoire choisi dans les paramètres.
+ +
Listes des sauvegardes
+
Si des sauvegardes existent, il est possible de restaurer ou de supprimer ces sauvegardes. + Il suffit de selectionner les sauvegardes puis de choisir "Restaurer les sauvegardes". Elles seront restaurées par ordre chronologique. + C'est à dire que la plus ancienne sauvegarde sera restaurée en premier puis les plus récentes. + Dans cette même liste on peut supprimer des sauvegardes. La date, la langue et la taille de la sauvegarde sont affichées dans cette liste.
+ +
+ +

Import/Export

+
+
Importer
+
Il est possible d'importer des paquetages de langue. + Le format de ces paquetages est le zip et la structure pour chaque fichier doit obligatoirement avoir: +
    +
  • le nom du module
  • +
  • le repertoire locales
  • +
  • le code de la langue
  • +
  • le nom du fichier de langue
  • +
+ Ce qui donne par exemple pour l'extension translater: +
    +
  • translater/locales/fr/main.po
  • +
  • translater/locales/fr/main.lang.php
  • +
  • translater/locales/fr/error.po
  • +
  • ...
  • +
+ Seuls les fichiers .po et .lang.php sont reconnus. + Un paquetage peut contenir plusieurs langues et plusieurs extensions. + Si l'import se fait depuis la page d'une extension, seule cette extension sera importée. + Si l'import se fait depuis la page général, toutes les extensions seront importées. + Si l'option Ecraser les fichier lors de l'import est activée dans les paramètres alors les fichiers exsitant seront remplacés.
+ +
+ +

Support

+

Si vous souhaitez plus d'aide ou apporter votre contribution à cette extension, voici quelques liens utiles.

+ + + + diff --git a/locales/fr/main.lang.php b/locales/fr/main.lang.php new file mode 100644 index 0000000..5f75960 --- /dev/null +++ b/locales/fr/main.lang.php @@ -0,0 +1,402 @@ + \ No newline at end of file diff --git a/locales/fr/main.po b/locales/fr/main.po new file mode 100644 index 0000000..5f6e1fa --- /dev/null +++ b/locales/fr/main.po @@ -0,0 +1,537 @@ +# Language: Français +# Module: translater - 2013.05.11 +# Date: 2013-05-12 23:35:00 +# Translated with translater 2013.05.11 + +msgid "" +msgstr "" +"Content-Type: text/plain; charset=UTF-8\n" +"Project-Id-Version: translater 2013.05.11\n" +"POT-Creation-Date: \n" +"PO-Revision-Date: 2013-05-12T23:35:00+00:00\n" +"Last-Translator: Jean-Christian Denis\n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Transfer-Encoding: 8bit\n" + +#: _admin.php:26 +#: index.php:537 +#: index.php:570 +#: index.php:1384 +msgid "Translater" +msgstr "Traducteur" + +#: _admin.php:44 +msgid "Translate extensions" +msgstr "Traduire les extensions" + +#: _admin.php:48 +#: index.php:670 +msgid "Name" +msgstr "Nom" + +#: _admin.php:62 +msgid "Translate this plugin" +msgstr "Traduire cette extension" + +#: _admin.php:85 +msgid "Translate this theme" +msgstr "Traduire ce thème" + +#: inc/class.dc.translater.php:434 +msgid "Cannot find backups folder for module %s" +msgstr "Impossible de trouver le dossier de sauvegarde du module %s" + +#: inc/class.dc.translater.php:450 +msgid "Cannot find languages folder for module %s" +msgstr "Impossible de trouver le dossier de langue du module %s" + +#: inc/class.dc.translater.php:470 +msgid "Cannot find root folder for module %s" +msgstr "Impossible de trouver le dossier racine du module %s" + +#: inc/class.dc.translater.php:499 +msgid "Limit of %s backups for module %s exceed" +msgstr "La limite de %s sauvegardes pour le module % est dépassée" + +#: inc/class.dc.translater.php:561 +#: inc/class.dc.translater.php:951 +#: inc/class.dc.translater.php:1028 +msgid "Cannot find language folder %s for module %s" +msgstr "Impossible de trouver le dossier de langue %s pour le module %s" + +#: inc/class.dc.translater.php:616 +msgid "Cannot delete backup file %s" +msgstr "Impossible d'effacer le fichier de sauvegarde %s" + +#: inc/class.dc.translater.php:634 +msgid "Cannot find backup file %s" +msgstr "Impossible de trouver le fichier de sauvegarde %s" + +#: inc/class.dc.translater.php:660 +msgid "Wrong export query" +msgstr "Mauvaise requète d'exportation" + +#: inc/class.dc.translater.php:671 +msgid "Cannot use export mask %s" +msgstr "Impossible d'utiliser le masque d'exportation %s" + +#: inc/class.dc.translater.php:759 +msgid "Wrong import query" +msgstr "Mauvaise requète d'importation" + +#: inc/class.dc.translater.php:808 +msgid "Nothing to import for these modules in pack %s" +msgstr "Rien à importer pour ces modules dans le paquetage %s" + +#: inc/class.dc.translater.php:837 +msgid "Zip file %s is not in translater format" +msgstr "Le fichier zip n'est pas au format de translater" + +#: inc/class.dc.translater.php:897 +msgid "Language %s already exists for module %s" +msgstr "La langue %s existe dèjà pour le module %s" + +#: inc/class.dc.translater.php:908 +msgid "Cannot copy file from language %s for module %s" +msgstr "Impossible de copier le fichier depuis le langage %s pour le module %s" + +#: inc/class.dc.translater.php:1000 +msgid "No string to write, language %s deleted for module %s" +msgstr "Aucune chaine à écrire, la langue %s a été effacée pour le module %s" + +#: inc/class.dc.translater.php:1264 +msgid "Cannot grant write acces on lang file %s" +msgstr "Impossible d'avoir les droits en écriture sur le fichier de langue %s" + +#: inc/class.dc.translater.php:1275 +msgid "Cannot write lang file %s" +msgstr "Impossible d'écrire le fichier de langue %s" + +#: inc/class.dc.translater.php:1579 +msgid "Cannot find language for code %s" +msgstr "Impossible de trouver de langue pour le code %s" + +#: inc/class.translater.rest.php:42 +msgid "Missing params" +msgstr "Paramètres manquants" + +#: inc/class.translater.rest.php:51 +msgid "Failed to get translation tool" +msgstr "Impossible de trouver l'outil de traduction" + +#: inc/class.translater.rest.php:55 +msgid "Translation tool is not configured" +msgstr "L'outil de traduction n'est pas configuré" + +#: inc/lib.translater.google.php:44 +msgid "You must have on Google API console:" +msgstr "Vous devez avoir sur la console des API Google :" + +#: inc/lib.translater.google.php:80 +msgid "Failed to query service." +msgstr "Impossible d'interoger le service." + +#: inc/lib.translater.microsoft.php:49 +msgid "You must have:" +msgstr "Vous devez avoir :" + +#: index.php:26 +#: index.php:526 +#: index.php:1382 +msgid "Settings" +msgstr "Paramètres" + +#: index.php:42 +msgid "locales folders of each module" +msgstr "le répertoire locales de chaque module" + +#: index.php:43 +msgid "plugins folder root" +msgstr "le répertoire racine des extensions" + +#: index.php:44 +msgid "public folder root" +msgstr "le répertoire public du blog" + +#: index.php:45 +msgid "cache folder of Dotclear" +msgstr "le répertoire cache de Dotclear" + +#: index.php:46 +msgid "locales folder of translater" +msgstr "le répertoire locales de l'extension translater" + +#: index.php:51 +msgid "Translation successfully updated" +msgstr "Traduction mise à jour avec succès" + +#: index.php:52 +msgid "Translation successfully created" +msgstr "Traduction crée avec succès" + +#: index.php:53 +msgid "Translation successfully deleted" +msgstr "Traduction effacée avec succès" + +#: index.php:54 +msgid "Backups successfully create" +msgstr "Sauvegardes crées avec succès" + +#: index.php:55 +msgid "Backups successfully restored" +msgstr "Sauvegardes effectuées avec succès" + +#: index.php:56 +msgid "Backups successfully deleted" +msgstr "Sauvegardes effacées avec succès" + +#: index.php:57 +msgid "Package successfully imported" +msgstr "Paquetage importé avec succès" + +#: index.php:58 +msgid "Package successfully exported" +msgstr "Paquetage exporté avec succès" + +#: index.php:62 +msgid "Failed to update settings: %s" +msgstr "Impossible de mettre à jour les paramètres : %s" + +#: index.php:63 +msgid "Failed to update translation: %s" +msgstr "Impossible de mettre à jour la traduction : %s" + +#: index.php:64 +msgid "Failed to create translation: %s" +msgstr "Impossible de créer la traduction : %s" + +#: index.php:65 +msgid "Failed to delete translation: %s" +msgstr "Impossible d'effacer la traduction : %s" + +#: index.php:66 +msgid "Failed to create backups: %s" +msgstr "Impossible de créer les sauvegardes : %s" + +#: index.php:67 +msgid "Failed to restore backups: %s" +msgstr "Impossible de restaurer les sauvegardes : %s" + +#: index.php:68 +msgid "Failed to delete backups: %s" +msgstr "Impossible d'effacer les sauvegardes : %s" + +#: index.php:69 +msgid "Failed to import package: %s" +msgstr "Impossible d'importer le paquetage : %s" + +#: index.php:70 +msgid "Failed to export package: %s" +msgstr "Impossible d'exporter le paquetage : %s" + +#: index.php:112 +msgid "No lang to create" +msgstr "Pas de langue à créer" + +#: index.php:134 +msgid "No lang to delete" +msgstr "Pas de langue à effacer" + +#: index.php:156 +msgid "No lang to backup" +msgstr "Pas de langue à sauvegarder" + +#: index.php:189 +msgid "No blackup to restore" +msgstr "Pas de langue à restaurer" + +#: index.php:208 +msgid "No bakcup to to restore" +msgstr "Pas de sauvegarde à restaurer" + +#: index.php:229 +#: index.php:247 +msgid "No backup to delete" +msgstr "Pas de sauvegarde à effacer" + +#: index.php:290 +#: index.php:364 +msgid "Nothing to export" +msgstr "Rien à exporter" + +#: index.php:312 +msgid "No language to update" +msgstr "Pas de langue à mettre à jour" + +#: index.php:443 +#: index.php:490 +msgid "Failed to launch translater: %s" +msgstr "Impossible de démarrer translater : %s" + +#: index.php:556 +msgid "Use this %s translation:" +msgstr "Utiliser la traduction de %s :" + +#: index.php:557 +msgid "Translate this text with %s" +msgstr "Traduire ce texte avec %s" + +#: index.php:558 +msgid "Use this text" +msgstr "Utiliser ce texte" + +#: index.php:606 +msgid "Translate theme \"%s\" (by %s)" +msgstr "Traduire le thème \"%s\" (de %s)" + +#: index.php:607 +msgid "Translate plugin \"%s\" (by %s)" +msgstr "Traduire l'extension \"%s\" (de %s)" + +#: index.php:627 +#: index.php:741 +msgid "Edit translation" +msgstr "Modifier la traduction" + +#: index.php:639 +msgid "Id" +msgstr "Id" + +#: index.php:649 +msgid "There is no editable modules" +msgstr "Il n'y a pas de modules modifiables." + +#: index.php:665 +msgid "Summary" +msgstr "Résumé" + +#: index.php:666 +msgid "Module" +msgstr "Module" + +#: index.php:668 +msgid "About" +msgstr "À propos" + +#: index.php:678 +msgid "Root" +msgstr "Racine" + +#: index.php:680 +#: index.php:694 +#: index.php:811 +#: index.php:1342 +msgid "Backups" +msgstr "Sauvegardes" + +#: index.php:695 +msgid "Last backup" +msgstr "Dernière sauvegarde" + +#: index.php:722 +msgid "no backup" +msgstr "Aucune sauvegarde" + +#: index.php:731 +msgid "Translations" +msgstr "Traductions" + +#: index.php:737 +msgid "Edit language" +msgstr "Modifier une langue" + +#: index.php:757 +msgid "Add language" +msgstr "Ajouter une langue" + +#: index.php:764 +msgid "Copy from language:" +msgstr "Copier depuis la langue :" + +#: index.php:766 +msgid "Optionnal" +msgstr "Optionnel" + +#: index.php:773 +msgid "Add translation" +msgstr "Ajouter la traduction" + +#: index.php:790 +msgid "Delete language" +msgstr "Effacer une langue" + +#: index.php:794 +msgid "Delete translation" +msgstr "Effacer la traduction" + +#: index.php:816 +msgid "Create backups" +msgstr "Créer une sauvegarde" + +#: index.php:818 +msgid "Choose languages to backup" +msgstr "Choisir la langue à sauvegarder" + +#: index.php:839 +msgid "Backup" +msgstr "Sauvegarde" + +#: index.php:856 +msgid "List of backups" +msgstr "Liste des sauvegardes" + +#: index.php:860 +#: index.php:1066 +msgid "File" +msgstr "Fichier" + +#: index.php:863 +msgid "Size" +msgstr "Taille" + +#: index.php:887 +msgid "Selected backups action:" +msgstr "Action sur les sauvegardes sélectionnées :" + +#: index.php:889 +msgid "Restore backups" +msgstr "Restaurer les sauvegardes" + +#: index.php:890 +msgid "Delete backups" +msgstr "Effacer les sauvegardes" + +#: index.php:916 +#: index.php:1183 +msgid "Choose language package to import" +msgstr "Choisir le paquetage de langue à importer" + +#: index.php:938 +#: index.php:1240 +msgid "Choose languages to export" +msgstr "Choisir les langues à exporter" + +#: index.php:1059 +msgid "Group" +msgstr "Groupe" + +#: index.php:1061 +msgid "String" +msgstr "Chaîne" + +#: index.php:1063 +#: index.php:1284 +msgid "Translation" +msgstr "Traduction" + +#: index.php:1064 +msgid "Existing" +msgstr "Existant" + +#: index.php:1127 +msgid "%s occurrences" +msgstr "%s occurrences" + +#: index.php:1153 +msgid "Total of %s strings." +msgstr "Total de %s chaînes." + +#: index.php:1155 +msgid "Change the group of the selected entries to:" +msgstr "Changer le groupe des entrées sélectionnées vers :" + +#: index.php:1212 +msgid "Choose modules to export" +msgstr "Choisir les modules à exporter" + +#: index.php:1214 +msgid "Modules" +msgstr "Modules" + +#: index.php:1287 +msgid "Write .po files" +msgstr "Écrire les fichier .po" + +#: index.php:1290 +msgid "Write .lang.php files" +msgstr "Écrire les fichier .lang.php" + +#: index.php:1293 +msgid "Translate also strings of template files" +msgstr "Traduire également les chaînes des fichiers de template" + +#: index.php:1296 +msgid "Translate only unknow strings" +msgstr "Traduire uniquement les chaînes inconnues" + +#: index.php:1299 +msgid "Hide default modules of Dotclear" +msgstr "Cacher les modules d'origine de Dotclear" + +#: index.php:1302 +msgid "Write comments in files" +msgstr "Écrire les commentaires dans les fichiers" + +#: index.php:1305 +msgid "Write informations about author in files" +msgstr "Écrire les informations à propos de l'auteur dans les fichiers" + +#: index.php:1309 +msgid "Tools" +msgstr "Outils" + +#: index.php:1310 +msgid "Default language of l10n source:" +msgstr "Langue par défaut des sources l10n :" + +#: index.php:1314 +msgid "Select and configure the tool to use to translate strings:" +msgstr "Sélectionner et configurer un outil de traduction :" + +#: index.php:1326 +msgid "Nothing to configure for %s tool." +msgstr "Rien à configurer pour l'outil %s." + +#: index.php:1337 +msgid "Overwrite existing languages" +msgstr "Écraser les langages existants" + +#: index.php:1338 +msgid "Name of exported package" +msgstr "Nom du paquetage exporté" + +#: index.php:1345 +msgid "Make backups when changes are made" +msgstr "Faire des sauvegardes lors des modifications" + +#: index.php:1346 +msgid "Limit backups to %s files per module" +msgstr "Limiter les sauvegardes à %s fichiers par module" + +#: index.php:1349 +msgid "Store backups in %s" +msgstr "Stocker les sauvegardes dans %s" + +#: index.php:1354 +msgid "Behaviors" +msgstr "Comportements" + +#: index.php:1355 +msgid "Default start menu:" +msgstr "Menu de départ par défaut :" + +#: index.php:1360 +msgid "Enable menu on extensions page" +msgstr "Activer le menu sur la page des extensions" + +#: index.php:1363 +msgid "Enable menu on themes page" +msgstr "Activer le menu sur la page des thèmes" + +msgid "translater" +msgstr "Traducteur" + +msgid "Translate your Dotclear plugins and themes" +msgstr "Traduire vos extensions et thèmes pour Dotclear" + diff --git a/locales/fr/resources.php b/locales/fr/resources.php new file mode 100644 index 0000000..b0da71f --- /dev/null +++ b/locales/fr/resources.php @@ -0,0 +1,16 @@ + \ No newline at end of file diff --git a/release.txt b/release.txt new file mode 100644 index 0000000..590d033 --- /dev/null +++ b/release.txt @@ -0,0 +1,86 @@ +x.x xxxxxxxx + - help translation + - source translation + +2013.05.11 + * Rewrited proposal tools + * Added Microsoft translation tool + * Updated Google translation tool + * Removed permissions, now required superadmin + * Fixed page title and messages and contents + * Moved all sub-pages into one page + +1.5 20100901 + * Added option to set defaut tab (closes #552) + * Fixed occurrences count (closes #551) + * Fixed regxep (closes #550) + * Cleaned design (thanks to osku) + +1.4.2 20100901 + * Tried to fix crash with regexp on parsing .po file + +1.4.1 20100626 + * Fixed crash on .po files + * Fixed toggle function + * Fixed admin crash on non 2.2 + * Fixed minor bugs + * Added option to hide default modules of Dotclear + +1.4 20100605 + * Switched to DC 2.2 + * Added toogle list of existing translation + * Fixed google translate (now uses Google ajax API) + * Removed "simple mode" + * Changed admin interface (easy, light, fast) + +1.3 20091025 + * Added babelfish help + * Added behaviors on files writing + * Fixed regexp again + * Changed priority to .po files instead of .lang.php files + +1.2 20091010 + * Added direct text copy and paste + * Added grouping file change + * Fixed some typo + +1.1 20091002 + * Added the proposed translation + * Rewrited settings system + * Speed up expert mode + +1.0 20090928 + * Added translation of template files. closes #250 + +0.9 20090923 + * Fixed bug on translate escape string + * Added _uninstall.php support + +0.8 20090816 + * Fixed php 5.3 compatibility + +0.7 + * Fixed some l10n + * Fixed ''xhtml strict'' validation + +0.6 + * Added ''author'' to langs files + * Added ''two-cols'' option + * Added ''sort option'' on array of translations + * Fixed ''bugs'' with no theme + * Fixed ''bugs'' with folder perms + * Fixed ''nothing to update" in simple mode + * Fixed ''bugs'' in Import/export + * Fixed ''html >'' like DC changset 2385 + * Fixed ''xhtml strict'' validation + +0.5 + * Fixed ''admin url'' + * Added user perm check + * Fixed ''bugs'' with bad strings (close #166) + +0.4 + * Changed default tab to plugin + * Replaced list of modules in select box rather than in help + * Fixed wrong message when nothing to export + * Added help in helpBlock \ No newline at end of file