From 802e35d79c7ee5e11d824e6606d5adcf2cc2ad81 Mon Sep 17 00:00:00 2001 From: Packit Date: Sep 24 2020 08:27:49 +0000 Subject: ocaml-gettext-0.3.7 base --- diff --git a/.announce b/.announce new file mode 100644 index 0000000..622dc0e --- /dev/null +++ b/.announce @@ -0,0 +1,25 @@ +From: Sylvain Le Gall +To: caml-list@inria.fr +Subject: [ANN] ocaml-gettext v0.3.3 + +Hello, + +General: +ocaml-gettext is a library that enables string translation in OCaml. The API +is based on GNU gettext. It comes with a tool to extract strings which need to +be translated from OCaml source files. + +This enables OCaml program to output string in the native language of the user, +if a corresponding translation file of the English strings is provided. + +Changes: +v 0.3.3 is a bug fix release: +* compatible with ocaml-fileutils 0.4.0 +* more static type-check of format string + +Links: +http://le-gall.net/sylvain+violaine/ocaml-gettext.html +http://le-gall.net/sylvain+violaine/download/ocaml-gettext-0.3.3.tar.gz + +Regards +Sylvain Le Gall diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d853d40 --- /dev/null +++ b/.gitignore @@ -0,0 +1,12 @@ +/_build/ +/setup.data +/setup.log +/dist/ +/test.byte +/ConfMakefile +/configure +/libgettext-ocaml/META +/libgettext-ocaml/gettextConfig.ml +/config.log +/config.status +/po/fr.po.bak diff --git a/.headache.config b/.headache.config new file mode 100644 index 0000000..51c8188 --- /dev/null +++ b/.headache.config @@ -0,0 +1,52 @@ +########################################################################## +# ocaml-gettext : a library to translate messages # +# # +# Copyright (C) 2003-2007 Sylvain Le Gall # +# # +# This library is free software; you can redistribute it and/or # +# modify it under the terms of the GNU Lesser General Public # +# License as published by the Free Software Foundation; either # +# version 2.1 of the License, or (at your option) any later version; # +# with the OCaml static compilation exception. # +# # +# This library 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 # +# Lesser General Public License for more details. # +# # +# You should have received a copy of the GNU Lesser General Public # +# License along with this library; if not, write to the Free Software # +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 # +# USA # +########################################################################## + +| ".*README.*" -> no +| ".*COPYING.*" -> no +| ".*\.doap" -> skip match:"<\?xml.*>" +| ".*\.doap" -> lines open:"" +| ".*\.xml" -> skip match:"<\?xml.*>" +| ".*\.xml" -> lines open:"" +| ".*\.patch" -> no +| "CHANGELOG" -> no +| "THANKS" -> no +| "TODO" -> no +| "install-sh" -> no +| "missing" -> no +| "configure" -> no +| "INSTALL" -> no +| "configure.in" -> frame open:"dnl *" line:"*" close:"*" +| "aclocal.m4" -> frame open:"#" line:"#" close:"#" +| ".*\\.ml\\.in" -> frame open:"(*" line:"*" close:"*)" +| ".*\\.ml" -> skip match:"(\\*pp .* \\*)" +| "\\.headache\\.config" -> frame open:"#" line:"#" close:"#" +| ".*\\.swp" -> no +| ".*\\.po" -> no +| ".*\\.mo" -> no +| "META" -> frame open:"#" line:"#" close:"#" +| "META.in" -> frame open:"#" line:"#" close:"#" +| "\\.announce" -> no +| "POTFILES" -> no +| "LINGUAS" -> no +| ".*\\.pot" -> no +| ".*\\.png" -> no +| "\\.header" -> no diff --git a/.header b/.header new file mode 100644 index 0000000..a7205f5 --- /dev/null +++ b/.header @@ -0,0 +1,19 @@ +ocaml-gettext: a library to translate messages + +Copyright (C) 2003-2007 Sylvain Le Gall + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version; +with the OCaml static compilation exception. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 +USA diff --git a/CHANGELOG b/CHANGELOG new file mode 100644 index 0000000..3ee9325 --- /dev/null +++ b/CHANGELOG @@ -0,0 +1,65 @@ +v 0.3.7 (Wed, 01 Mar 2017 22:55:57 +0100): + * Add an extra step to generate configure. + +v 0.3.6 (Sun, 26 Feb 2017 12:34:34 +0100): + * Remove unix.cma linking argument for 4.0{3,4} compatibility + +v 0.3.5 (Mon, 04 Aug 2014 23:31:41 +0200): + * Always use format_of_string to not segfault with OCaml 4.02. + +v 0.3.4 (Tue, 09 Aug 2011 10:12:55 +0000): + * Use camomile 0.8.3 + +v 0.3.3 (Sun, 01 Nov 2009 23:59:13 +0100): + * Upgrade ocaml-fileutils 0.4.0 + * Improve gettext-stub C parts (memory management) + * Correctly handle OCaml string, wrt of escape char (Closes: FS#70) + * Remove the right file when uninstalling (Closes: FS#61) + * Enforce type safety for format string and plural (Closes: FS#72) + * Only output warning for install check that can be handle afterwards i + (Closes: FS#74) + * Add a --strict flag to ocaml-gettext to make an error for every install check + failed. + * Take into account Windows eol '\r' when parsing comments + (Closes: FS#75) + * Don't include empty translation (Closes: FS#90) + * Check returned format string for fdgettext (Closes: FS#91) + +v 0.3.2 (Thu, 05 Jun 2008 22:12:51 +0200): + * Fix bug when buggy LANG/LC_ALL is set, using gettext stub (Richard W.M. Jones) + +v 0.3.1 (Fri, 09 May 2008 23:10:59 +0200): + * Fix bug when no LANG or LC_ALL is set + * Better autoconf use of @VERSION@ + * Only remove build dir in top Makefile + +v 0.3.0 (Tue, 29 Apr 2008 22:58:22 +0200): + * Get rid of ocaml-ast-analyze (Richard W.M. Jones) + * Port of camlp4 extension to OCaml 3.10.1 (Richard W.M. Jones) + * Compile with camomile 0.7.1 (Richar W.M. Jones) + * Check dependency on ocaml-fileutils in configure + * Add --disable-doc-pdf to configure, to allow building doc without PDF (and fop) + * Get rid of camlidl + * Distribute .mli and .cmx file + * Handle multiline comment when merging, especially location "#:" and special + comment "#," + * Partially handle UTF-8, by not escaping string using "Printf.fprintf + "%S"" but by using the same rule as the one used for reading escaped string + * Rework build system to: + * use "install" programm to create dir and copy files + * use --docdir and --prefix from configure + * Don't build PDF document by default + * Install documentation in $docdir/html/{api|api-ref|reference-manual} + * Fix typo in documentation + +v 0.2.0 : + * Full rewrite of ocaml-gettext. + * Contains two version of the gettext engine : one is based on a purely OCaml approach (gettext-camomile), + one is base on the former one (gettext-stub). + * The two engines shared a common interface based on gettext.base module. + * Most of the encoding/settings choice are accessible through command line option. + * Simplify the functions to accesse to gettext : 4 functions to translate + * Rebuild the string extractor with as a camlp4 analyzer + * Write test and benchmark program for maintaing the quality + * Write an example + * Write a manual diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..207af23 --- /dev/null +++ b/COPYING @@ -0,0 +1,539 @@ +The Library is distributed under the terms of the GNU Library General +Public License version 2 (found in /usr/share/common-licenses/LGPL-2 +on debian systems). + +As a special exception to the GNU Library General Public License, you +may link, statically or dynamically, a "work that uses the Library" +with a publicly distributed version of the Library to produce an +executable file containing portions of the Library, and distribute +that executable file under terms of your choice, without any of the +additional requirements listed in clause 6 of the GNU Library General +Public License. By "a publicly distributed version of the Library", +we mean either the unmodified Library as distributed by INRIA, or a +modified version of the Library that is distributed under the +conditions defined in clause 3 of the GNU Library General Public +License. This exception does not however invalidate any other reasons +why the executable file might be covered by the GNU Library General +Public License. + +----------------------------------------------------------------------- + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations +below. + + When we speak of free software, we are referring to freedom of use, +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 and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. +^L + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it +becomes a de-facto standard. To achieve this, non-free programs must +be allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. +^L + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, 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 library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete 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 distribute a copy of this License along with the +Library. + + 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 Library or any portion +of it, thus forming a work based on the Library, 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) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +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 Library, 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 Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. +^L + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you 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. + + If distribution of 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 satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. +^L + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at least + three years, to give the same user the materials specified in + Subsection 6a, above, for a charge no more than the cost of + performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be 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. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. +^L + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library 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. + + 9. 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 Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +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 with +this License. +^L + 11. 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 Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library 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 Library. + +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. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library 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. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser 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 Library +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 Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. +^L + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +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 + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "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 +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. 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 LIBRARY 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 +LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS +^L + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms +of the ordinary General Public License). + + To apply these terms, attach the following notices to the library. +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 library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or +your school, if any, to sign a "copyright disclaimer" for the library, +if necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James + Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + + +######################## +# File test/utf8-ja.po # +######################## + +This file has been extracted for testing purpose from virt-p2v. Here is the +copyright notice: + +# Japanese translations for virt-p2v package. +# Copyright (C) 2008 THE virt-p2v'S COPYRIGHT HOLDER +# This file is distributed under the same license as the virt-p2v package. +# Richard Jones , 2008. diff --git a/ConfMakefile.in b/ConfMakefile.in new file mode 100644 index 0000000..5a1e9ea --- /dev/null +++ b/ConfMakefile.in @@ -0,0 +1,82 @@ +########################################################################## +# ocaml-gettext: a library to translate messages # +# # +# Copyright (C) 2001 Jean-Christophe FILLIATRE # +# Copyright (C) 2003-2008 Sylvain Le Gall # +# # +# This library is free software; you can redistribute it and/or # +# modify it under the terms of the GNU Lesser General Public # +# License as published by the Free Software Foundation; either # +# version 2.1 of the License, or (at your option) any later version; # +# with the OCaml static compilation exception. # +# # +# This library 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 # +# Lesser General Public License for more details. # +# # +# You should have received a copy of the GNU Lesser General Public # +# License along with this library; if not, write to the Free Software # +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 # +# USA # +########################################################################## + +# where to install the binaries +prefix=@prefix@ +exec_prefix=@exec_prefix@ +datarootdir=@datarootdir@ +PACKAGE_TARNAME=@PACKAGE_TARNAME@ +BINDIR =@bindir@ +LIBDIR =@libdir@ +MANDIR =@mandir@ +DOCDIR =@docdir@ +PODIR =@localedir@ +OCAMLLIB=@OCAMLLIB@ + +# programs to build an compile +OCAMLFIND_COMMANDS = "ocamlc=@OCAMLC@ \ + ocamlopt=@OCAMLOPT@ \ + ocamldep=@OCAMLDEP@" +OCAMLC = @OCAMLFIND@ ocamlc +OCAMLOPT = @OCAMLFIND@ ocamlopt +OCAMLDEP = @OCAMLFIND@ ocamldep +OCAMLBEST = @OCAMLBEST@ +OCAMLVERSION = @OCAMLVERSION@ +OCAMLFIND = @OCAMLFIND@ +OCAMLMKLIB = @OCAMLMKLIB@ +OCAMLYACC = @OCAMLYACC@ +OCAMLLEX = @OCAMLLEX@ +EXE = @EXE@ +XSLTPROC = @XSLTPROC@ +XMLLINT = @XMLLINT@ +OCAMLDOC = @OCAMLDOC@ +FOP = @FOP@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +VERSION = @PACKAGE_VERSION@ + +# stylesheets +HTMLXSL = @HTMLXSL@ +MANXSL = @MANXSL@ +FOXSL = @FOXSL@ + +# temporary build directory +abs_top_srcdir = @abs_top_srcdir@ +TMPBUILDDIR = @TMPBUILDDIR@ + +# build target +BUILD_CAMOMILE=@BUILD_CAMOMILE@ +BUILD_STUB=@BUILD_STUB@ +BUILD_TEST=@BUILD_TEST@ +BUILD_BENCH=@BUILD_BENCH@ +BUILD_DOC=@BUILD_DOC@ +BUILD_DOC_PDF=@BUILD_DOC_PDF@ + +# configuration +STUB_LDFLAGS=@STUB_LDFLAGS@ +STUB_CFLAGS=@STUB_CFLAGS@ + +# ocamlfind install/remove flags +OCAMLFIND_INSTALL_FLAGS=@OCAMLFIND_INSTALL_FLAGS@ +OCAMLFIND_REMOVE_FLAGS=@OCAMLFIND_REMOVE_FLAGS@ diff --git a/INSTALL b/INSTALL new file mode 100644 index 0000000..229f937 --- /dev/null +++ b/INSTALL @@ -0,0 +1,29 @@ +-------------- +ocaml-gettext +-------------- + +Prerequisite : + +In order to install you will need : +- ocaml ( >= 3.10.1 ) + http://caml.inria.fr/ +- ocamlfind ( >= 0.8 ) + http://www.ocaml-programming.de/packages/ +- make, a C compiler, libintl.h ( which is bundle in libc6 ) +- camomile (>= 0.8.3) +- ocaml-fileutils (>= 0.4.0) +- ounit +- benchmark +- docbook DTD and XSL +- xsltproc + +Installing : +1) Go in the untared source directory +2) ./configure +3) make +4) make install + +Uninstalling : +With the same value you use to install +1) Go in the untared source directory +2) make uninstall diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..ee4670f --- /dev/null +++ b/Makefile @@ -0,0 +1,86 @@ +########################################################################## +# ocaml-gettext: a library to translate messages # +# # +# Copyright (C) 2003-2008 Sylvain Le Gall # +# # +# This library is free software; you can redistribute it and/or # +# modify it under the terms of the GNU Lesser General Public # +# License as published by the Free Software Foundation; either # +# version 2.1 of the License, or (at your option) any later version; # +# with the OCaml static compilation exception. # +# # +# This library 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 # +# Lesser General Public License for more details. # +# # +# You should have received a copy of the GNU Lesser General Public # +# License along with this library; if not, write to the Free Software # +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 # +# USA # +########################################################################## + +all: + cd libgettext-ocaml && $(MAKE) all + cd libgettext-stub-ocaml && $(MAKE) all + cd libgettext-camomile-ocaml && $(MAKE) all + cd ocaml-gettext && $(MAKE) all + cd po && $(MAKE) all + cd doc && $(MAKE) all + cd test && $(MAKE) all + +install: + cd libgettext-ocaml && $(MAKE) install + cd libgettext-stub-ocaml && $(MAKE) install + cd libgettext-camomile-ocaml && $(MAKE) install + cd ocaml-gettext && $(MAKE) install + cd po && $(MAKE) install + cd doc && $(MAKE) install + +uninstall: + cd doc && $(MAKE) uninstall + cd po && $(MAKE) uninstall + cd ocaml-gettext && $(MAKE) uninstall + cd libgettext-camomile-ocaml && $(MAKE) uninstall + cd libgettext-stub-ocaml && $(MAKE) uninstall + cd libgettext-ocaml && $(MAKE) uninstall + +clean: + -cd test && $(MAKE) clean + -cd doc && $(MAKE) clean + -cd po && $(MAKE) clean + -cd examples && $(MAKE) clean + -cd ocaml-gettext && $(MAKE) clean + -cd libgettext-camomile-ocaml && $(MAKE) clean + -cd libgettext-stub-ocaml && $(MAKE) clean + -cd libgettext-ocaml && $(MAKE) clean + -$(RM) -r $(TMPBUILDDIR) + +distclean: clean + -cd test && $(MAKE) distclean + -cd doc && $(MAKE) distclean + -cd po && $(MAKE) distclean + -cd examples && $(MAKE) distclean + -cd ocaml-gettext && $(MAKE) distclean + -cd libgettext-camomile-ocaml && $(MAKE) distclean + -cd libgettext-stub-ocaml && $(MAKE) distclean + -cd libgettext-ocaml && $(MAKE) distclean + -$(RM) config.* ConfMakefile + -$(RM) -r autom4te.cache config.log config.cache config.status + +headache: distclean + headache -h .header -c .headache.config `find $(CURDIR)/ -type d -name .svn -prune -false -o -type f` + +deploy: + mkdir dist || true + admin-gallu-deploy \ + --package_name ocaml-gettext --package_version "$(VERSION)" \ + --verbose --forge_upload --forge_group ocaml-gettext \ + --autogen + +test: all + cd test && ./test + +-include ConfMakefile + +.PHONY: all install uninstall clean distclean deploy test diff --git a/README b/README new file mode 100644 index 0000000..ed90e94 --- /dev/null +++ b/README @@ -0,0 +1,17 @@ +-------------- +ocaml-gettext +-------------- + +This library is a wrapper around gettext +(http://www.gnu.org/software/gettext/gettext.html), it also provides a pure +Ocaml implementation based on camomile. + +The primary target of this work is to bring internationalization to +cameleon ( http://savannah.nongnu.org/projects/cameleon/ ). Before i +can do it, i need to test and test this library... ( so i package it +and try to use it with some program of my creation... ). + +Feel free to use it in your application. There is no real doc coming +with it, but i will prepare some in the next future. + +Sylvain LE GALL diff --git a/THANKS b/THANKS new file mode 100644 index 0000000..80ce4ad --- /dev/null +++ b/THANKS @@ -0,0 +1,5 @@ + +Special thanks to Richard W.M. Jones from Red Hat. He made possible to release +version 0.3.0 by porting the library to OCaml 3.10.1. A great part of the 0.3.0 +has been made by him. + diff --git a/TODO b/TODO new file mode 100644 index 0000000..ecdda97 --- /dev/null +++ b/TODO @@ -0,0 +1,11 @@ +v 0.4.0 : +- Better parse comments, to keep licence et al +- Try to be faster than gettext C library +- Try to get GettextStub.* and GettextDummy.* be typed GettextTypes.REALIZE_TYPE (trying to avoid + the use of a .mli file). +- Reread the code to improve general layout (naming scheme, exception name... ie better style) +- Correct the BUG: related to bug from ocaml-fileutils +- Intercept problem when recoding string (charset), errors should be wrap inside a failsafe or + at least raise a coherent exception. +- Create what is necessary for running into Threaded env +- Write a patch for xgettext et al diff --git a/TopMakefile b/TopMakefile new file mode 100644 index 0000000..4405879 --- /dev/null +++ b/TopMakefile @@ -0,0 +1,327 @@ +########################################################################## +# ocaml-gettext: a library to translate messages # +# # +# Copyright (C) 2001 Jean-Christophe FILLIATRE # +# Copyright (C) 2003-2008 Sylvain Le Gall # +# # +# This library is free software; you can redistribute it and/or # +# modify it under the terms of the GNU Lesser General Public # +# License as published by the Free Software Foundation; either # +# version 2.1 of the License, or (at your option) any later version; # +# with the OCaml static compilation exception. # +# # +# This library 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 # +# Lesser General Public License for more details. # +# # +# You should have received a copy of the GNU Lesser General Public # +# License along with this library; if not, write to the Free Software # +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 # +# USA # +########################################################################## + +# destination dir +DOCHTML=$(DOCDIR)/html +DOCODOC=$(DOCDIR)/html +DOCPDF =$(DOCDIR)/pdf + +# programs flags +FOPFLAGS = +XSLTPROCFLAGS = --xinclude +XMLLINTFLAGS = --xinclude --noent --noout --postvalid +OCAMLDOCFLAGS = + +ifdef REQUIRES +CLI_REQUIRES = -package "$(REQUIRES)" +endif + +ifdef PREDICATES +CLI_PREDICATES = -predicates "$(PREDICATES)" +endif + +ifdef SYNTAX +CLI_SYNTAX = -syntax $(SYNTAX) +endif + +CLI_OCAMLFIND = $(CLI_REQUIRES) $(CLI_PREDICATES) $(CLI_SYNTAX) + +BUILDLIB = $(TMPBUILDDIR)/lib +BUILDBIN = $(TMPBUILDDIR)/bin +BUILDDOC = $(TMPBUILDDIR)/share/doc +BUILDHTML = $(BUILDDOC)/html +BUILDMAN = $(TMPBUILDDIR)/share/man +BUILDODOC = $(BUILDHTML)/api +BUILDPDF = $(BUILDDOC) +BUILDPO = $(TMPBUILDDIR)/share/locale + +OCAMLPATH:=$(BUILDLIB)/:$(OCAMLPATH) +export OCAMLPATH + +all: $(STUBSLIB) $(if $(NAME),all-$(OCAMLBEST)) +all-byte: $(if $(LIBRARY), install-buildlib-byte, install-buildprog-byte) +all-opt: $(if $(LIBRARY), install-buildlib-opt, install-buildprog-opt) +all: install-builddoc-html +all: install-builddoc-man +all: install-builddoc-odoc +all: install-builddoc-pdf + +install: all $(if $(NAME),install-$(OCAMLBEST)) +install-byte: $(if $(LIBRARY), install-lib-byte, install-prog-byte) +install-opt: $(if $(LIBRARY), install-lib-opt, install-prog-byte) +install: install-doc-html +install: install-doc-man +install: install-doc-odoc +install: install-doc-pdf + +uninstall: $(if $(LIBRARY), uninstall-lib, uninstall-prog) +uninstall: uninstall-doc-html +uninstall: uninstall-doc-man +uninstall: uninstall-doc-odoc +uninstall: uninstall-doc-pdf + + +clean:: $(if $(LIBRARY), clean-lib, clean-prog) +clean:: clean-doc-odoc +clean:: clean-doc-pdf +clean:: clean-doc-html +clean:: clean-doc-man + +distclean:: + +CMX = $(patsubst dll%.so, lib%.a, $(CMO:.cmo=.cmx)) + +INSTALL_DIR_AUX=$(INSTALL) -d $(2); $(INSTALL_DATA) -t $(2) $(wildcard $(dir $(1))*) +INSTALL_DIR=$(call INSTALL_DIR_AUX,$(1),$(2)/$(notdir $(patsubst %/,%,$(dir $(1))))) + +# Build the lib +ONLY_BYTE=$(filter-out %.o,$(filter-out %.a,$(filter-out %.cmx, $(filter-out %.cmxa,$(1))))) $(filter lib%.a,$(1)) +ML_OR_MLI=$(if $(wildcard $(1).mli),$(1).mli,$(1).ml) +GET_MLI=$(foreach interf,$(patsubst %.mli,%,$(filter %.mli,$(patsubst %.cmi,%.mli,$(1)))),$(call ML_OR_MLI,$(interf))) + +%.cmxa %.a %.so: + $(if $(STUBSOBJS), \ + $(OCAMLMKLIB) -o $* $^ $(OCAMLMKLIB_FLAGS), \ + $(OCAMLOPT) -a -o $*.cmxa $^ \ + ) + +%.cma %.a %.so: + $(if $(STUBSOBJS), \ + $(OCAMLMKLIB) -o $* $^ $(OCAMLMKLIB_FLAGS), \ + $(OCAMLC) -a -o $*.cma $^ \ + ) + +install-buildlib-byte: REAL_INSTALLIB = $(call ONLY_BYTE,$(INSTALLIB)) +install-buildlib-byte: $(call ONLY_BYTE,$(INSTALLIB)) +install-buildlib-byte: install-buildlib-common + +install-buildlib-opt: REAL_INSTALLIB = $(INSTALLIB) +install-buildlib-opt: $(INSTALLIB) +install-buildlib-opt: install-buildlib-common + +install-buildlib-common: $(call GET_MLI,$(INSTALLIB)) + $(INSTALL) -d $(BUILDLIB) + -$(OCAMLFIND) remove -destdir $(BUILDLIB) $(NAME) + $(OCAMLFIND) install -ldconf ignore -destdir $(BUILDLIB) -patch-version $(VERSION) $(NAME) \ + $(REAL_INSTALLIB) $(call GET_MLI, $(REAL_INSTALLIB)) + +install-lib-byte: REAL_INSTALLIB = $(call ONLY_BYTE,$(INSTALLIB)) +install-lib-byte: $(call ONLY_BYTE,$(INSTALLIB)) +install-lib-byte: install-lib-common + +install-lib-opt: REAL_INSTALLIB = $(INSTALLIB) +install-lib-opt: $(INSTALLIB) +install-lib-opt: install-lib-common + +install-lib-common: $(call GET_MLI,$(INSTALLIB)) + -$(OCAMLFIND) remove $(OCAMLFIND_REMOVE_FLAGS) $(NAME) + $(OCAMLFIND) install $(OCAMLFIND_INSTALL_FLAGS) -patch-version $(VERSION) $(NAME) \ + $(REAL_INSTALLIB) $(call GET_MLI,$(INSTALLIB)) + +uninstall-lib: + -$(OCAMLFIND) remove $(OCAMLFIND_REMOVE_FLAGS) $(NAME) + +clean-lib: + -$(OCAMLFIND) remove -destdir $(BUILDLIB) $(NAME) + +# Build the executable + +BLIBS = $(addsuffix .cma,$(LIBS)) + +install-buildprog-byte: $(CMO) + $(OCAMLC) -o $(NAME) $(INCLUDES) -package "$(REQUIRES)" -linkpkg \ + -predicates "$(PREDICATES)" $(BLIBS) $(CMO) + $(INSTALL) -d $(BUILDBIN) + $(INSTALL_SCRIPT) -t $(BUILDBIN) $(NAME) + +install-prog-byte: install-buildprog-byte + $(INSTALL) -d $(BINDIR) + $(INSTALL_SCRIPT) -t $(BINDIR) $(NAME) + +OLIBS = $(addsuffix .cmxa,$(LIBS)) + +install-buildprog-opt: $(CMX) + $(OCAMLOPT) -o $(NAME) $(INCLUDES) -package "$(REQUIRES)" -linkpkg \ + -predicates "$(PREDICATES)" $(OLIBS) $(CMX) + $(INSTALL) -d $(BUILDBIN) + $(INSTALL_SCRIPT) -t $(BUILDBIN) $(NAME) + +install-prog-opt: install-buildprog-opt + $(INSTALL) -d $(BINDIR) + $(INSTALL_SCRIPT) -t $(BINDIR)/ $(NAME) + +uninstall-prog: + $(if $(NAME),-$(RM) $(BINDIR)/$(NAME)) + +clean-prog: + -$(RM) $(NAME) + +# Build PDF files + +PDF_TARGET=$(patsubst %.xml,$(BUILDPDF)/%.pdf,$(1)) +$(BUILDPDF)/%.pdf: %.fo + $(INSTALL) -d $(dir $@) + $(FOP) $(FOPFLAGS) -fo $^ -pdf $@ + +%.fo: %.xml + $(XMLLINT) $(XMLLINTFLAGS) $^ + $(XSLTPROC) $(XSLTPROCFLAGS) -o $@ $(FOXSL) $^ + +install-builddoc-pdf: $(PDF_TARGETS) + +clean-doc-pdf: + +install-doc-pdf: + $(if $(PDF_TARGETS),$(INSTALL) -d $(DOCPDF)) + $(if $(PDF_TARGETS),$(INSTALL_DATA) -t $(DOCPDF) $(PDF_TARGETS)) + +uninstall-doc-pdf: + -$(if $(PDF_TARGETS),$(RM) $(foreach i,$(PDF_TARGETS),$(DOCPDF)/$(notdir $(i)))) + + +# Build OCaml documentaiton + +ODOC_TARGET=$(BUILDODOC)/$(1)/index.html +TARGET_ODOC=$(notdir $(patsubst %/index.html,%,$(1))) +$(BUILDODOC)/%/index.html: + $(INSTALL) -d $(dir $@) + EXTRAFLAGS=`$(OCAMLFIND) query -i-format -r $(REQUIRES)`; \ + $(OCAMLDOC) $(OCAMLDOCFLAGS) $$EXTRAFLAGS -d $(dir $@) -html $^ + $(if $(EXTRAFILES),$(INSTALL_DATA) -t $(dir $@) $(EXTRAFILES)) + +install-builddoc-odoc: $(ODOC_TARGETS) + +clean-doc-odoc: + +install-doc-odoc: + $(if $(ODOC_TARGETS),$(foreach i,$(ODOC_TARGETS), $(INSTALL) -d $(DOCODOC)/$(call TARGET_ODOC,$(i));)) + $(if $(ODOC_TARGETS),$(foreach i,$(ODOC_TARGETS), $(INSTALL_DATA) -t $(DOCODOC)/$(call TARGET_ODOC,$(i)) $(wildcard $(dir $(i))/*);)) + +uninstall-doc-odoc: clean-doc-odoc + -$(if $(ODOC_TARGETS),$(RM) -r $(foreach i,$(ODOC_TARGETS),$(DOCODOC)/$(call TARGET_ODOC,$(i)))) + +# Build HTML files + +HTML_TARGET=$(patsubst %.xml,$(BUILDHTML)/%/index.html,$(1)) +TARGET_HTML=$(notdir $(patsubst %/index.html,%,$(1))) +$(BUILDHTML)/%/index.html: %.xml + $(XMLLINT) $(XMLLINTFLAGS) $^ + $(INSTALL) -d $(dir $@) + $(XSLTPROC) $(XSLTPROCFLAGS) --stringparam base.dir "$(dir $@)" $(HTMLXSL) $^ + $(if $(IMAGES),$(INSTALL_DATA) -t $(dir $@) $(IMAGES)) + +install-builddoc-html: $(HTML_TARGETS) + +clean-doc-html: + +install-doc-html: install-builddoc-html + $(if $(HTML_TARGETS),$(foreach i,$(HTML_TARGETS),$(call INSTALL_DIR,$(i),$(DOCHTML));)) + +uninstall-doc-html: clean-doc-html + -$(if $(HTML_TARGETS),$(RM) -r $(foreach i,$(HTML_TARGETS),$(DOCHTML)/$(call TARGET_HTML,$(i)))) + +# Build MAN files + +MAN_SECTION=man$(patsubst .%,%,$(suffix $(1))) +MAN_BUILD_TARGETS=$(BUILDMAN)/$(call MAN_SECTION,$(1))/$(1) +MAN_INSTALL_TARGETS=$(MANDIR)/$(call MAN_SECTION,$(1))/$(1) + +define PROCESS_MAN + $(XMLLINT) $(XMLLINTFLAGS) $^ + $(XSLTPROC) $(XSLTPROCFLAGS) $(MANXSL) $^ + $(INSTALL) -d $(dir $(call MAN_BUILD_TARGETS,$@)) + $(INSTALL_DATA) $@ $(call MAN_BUILD_TARGETS,$@) +endef + +%.1: + $(PROCESS_MAN) +%.2: + $(PROCESS_MAN) +%.3: + $(PROCESS_MAN) +%.4: + $(PROCESS_MAN) +%.5: + $(PROCESS_MAN) +%.6: + $(PROCESS_MAN) +%.7: + $(PROCESS_MAN) +%.8: + $(PROCESS_MAN) +%.9: + $(PROCESS_MAN) + +install-builddoc-man: $(MAN_TARGETS) + +clean-doc-man: + +install-doc-man: install-builddoc-man + $(if $(MAN_TARGETS),$(foreach i,$(MAN_TARGETS),$(INSTALL) -d $(dir $(call MAN_INSTALL_TARGETS,$(i)));)) + $(if $(MAN_TARGETS),$(foreach i,$(MAN_TARGETS),$(INSTALL_DATA) $(call MAN_BUILD_TARGETS,$(i)) $(call MAN_INSTALL_TARGETS,$(i));)) + +uninstall-doc-man: clean-doc-man + -$(if $(MAN_TARGETS),$(RM) $(foreach i,$(MAN_TARGETS),$(call MAN_INSTALL_TARGETS,$(i)))) + +# Extra files + +.SUFFIXES: .mli .ml .cmi .cmo .cmx .mll .mly .zog .idl .h .o + +%.ml %.mli %.h %_stubs.c: %.idl + $(CAMLIDL) -nocpp -no-include $< + +.c.o: + $(OCAMLC) $(INCLUDES) -c $< + +.mli.cmi: + $(OCAMLC) $(INCLUDES) $(CLI_OCAMLFIND) -c $< + +.ml.cmo: + $(OCAMLC) $(INCLUDES) $(CLI_OCAMLFIND) -c $< + +.ml.o: + $(OCAMLOPT) $(INCLUDES) $(CLI_OCAMLFIND) -c $< + +.ml.cmx: + $(OCAMLOPT) $(INCLUDES) $(CLI_OCAMLFIND) -c $< + +.mll.ml: + $(OCAMLLEX) $< + +.mly.ml: + $(OCAMLYACC) -v $< + +.mly.mli: + $(OCAMLYACC) -v $< + +#.ml.mli: +# $(OCAMLC) $(INCLUDES) $(CLI_OCAMLFIND) -i $< > $@ || $(RM) $@ +# # Set date of the target to the date of the source +# touch -r $< $@ + + +clean:: $(if $(LIBRARY),clean-lib,clean-prog) + -$(RM) *.cm[ioxa] *.o *.cmxa *.a *.so + -$(RM) $(GENERATED) *.output + +distclean:: clean diff --git a/aclocal.m4 b/aclocal.m4 new file mode 100644 index 0000000..72a27da --- /dev/null +++ b/aclocal.m4 @@ -0,0 +1,307 @@ +########################################################################## +# ocaml-gettext: a library to translate messages # +# # +# Copyright (C) 2003-2008 Sylvain Le Gall # +# # +# This library is free software; you can redistribute it and/or # +# modify it under the terms of the GNU Lesser General Public # +# License as published by the Free Software Foundation; either # +# version 2.1 of the License, or (at your option) any later version; # +# with the OCaml static compilation exception. # +# # +# This library 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 # +# Lesser General Public License for more details. # +# # +# You should have received a copy of the GNU Lesser General Public # +# License along with this library; if not, write to the Free Software # +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 # +# USA # +########################################################################## + +# AC_PROG_OCAML_VERSION(prog,prog.opt,[ACTION-IF-FOUND],[ACTION-IF-NOT-FOUND]) +#--------------------------------------------------------- +# Check for the existence of an ocaml tool and parse its version +# Version in $versionval, tool in $toolval + +AC_DEFUN([AC_PROG_OCAML_VERSION], +[ +AC_CHECK_PROG(ac_ocaml_$1,$1,$1) +AC_CHECK_PROG(ac_ocaml_$1_opt,$2,$2) +if test "x$ac_ocaml_$1" = "x" && test "x$ac_ocaml_$1_opt" = "x"; then + : + $4 +else + if ! test "x$ac_ocaml_$1" = "x"; then + ac_ocaml_$1_version=`$ac_ocaml_$1 -v | sed -n -e "s|.*version *\(.*\)$|\1|p" ` + versionval=$ac_ocaml_$1_version + toolval=$ac_ocaml_$1 + fi + + if ! test "x$ac_ocaml_$1_opt" = "x"; then + ac_ocaml_$1_opt_version=`$ac_ocaml_$1 -v | sed -n -e "s|.*version *\(.*\)$|\1|p" ` + if ! test "x$ac_ocaml_$1" = "x" && test "$ac_ocaml_$1_opt_version" = "$ac_ocaml_$1_version"; then + versionval=$ac_ocaml_$1_opt_version + toolval=$ac_ocaml_$1_opt + elif test "x$ac_ocaml_$1" = "x"; then + versionval=$ac_ocaml_$1_opt_version + toolval=$ac_ocaml_$1_opt + else + AC_MSG_WARN("$1 and $2 version differs") + fi + fi + $3 +fi +]) + + +# AC_CHECK_OCAMLC([ACTION-IF-FOUND],[ACTION-IF-NOT-FOUND]) +#--------------------------------------------------------- +# Check for the existence of ocamlc (or ocamlc.opt) +# Subst OCAMLC, OCAMLVERSION, OCAMLBEST variable. +AC_DEFUN([AC_CHECK_OCAMLC], +[ +dnl Check the presence of ocamlc/ocamlc.opt and their version +AC_PROG_OCAML_VERSION(ocamlc,ocamlc.opt,[ + OCAMLC=$toolval + if test "x$OCAMLBEST" = "x"; then + OCAMLBEST=byte + fi + if ! test "x$OCAMLVERSION" = "x" && ! test "$versionval" = "$OCAMLVERSION"; then + AC_MSG_WARN($versionval doesn't match ocaml v. $OCAMLVERSION) + else + CAMLVERSION=$versionval + fi +],[$2]) +AC_SUBST(OCAMLC) +AC_SUBST(OCAMLVERSION) +AC_SUBST(OCAMLBEST) +]) + +# AC_CHECK_OCAMLOPT([ACTION-IF-FOUND],[ACTION-IF-NOT-FOUND]) +#--------------------------------------------------------- +# Check for the existence of ocamlopt (or ocamlopt.opt) +# Subst OCAMLOPT, OCAMLVERSION, OCAMLBEST variable. +AC_DEFUN([AC_CHECK_OCAMLOPT], +[ +dnl Check the presence of ocamlc/ocamlc.opt and their version +AC_PROG_OCAML_VERSION(ocamlopt,ocamlopt.opt,[ + OCAMLOPT=$toolval + if test "x$OCAMLBEST" = "x" || test "$OCAMLBEST" = "byte"; then + OCAMLBEST=opt + fi + if ! test "x$OCAMLVERSION" = "x" && ! test "$versionval" = "$OCAMLVERSION"; then + AC_MSG_WARN($versionval doesn't match ocaml v. $OCAMLVERSION) + else + OCAMLVERSION=$versionval + fi +],[$2]) +AC_SUBST(OCAMLOPT) +AC_SUBST(OCAMLVERSION) +AC_SUBST(OCAMLBEST) +]) + +# AC_CHECK_OCAMLLEX([ACTION-IF-FOUND],[ACTION-IF-NOT-FOUND]) +#--------------------------------------------------------- +# Check for the existence of ocamllex (or ocamllex.opt) +# Subst OCAMLLEX, OCAMLVERSION variable. +AC_DEFUN([AC_CHECK_OCAMLLEX], +[ +dnl Check the presence of ocamlc/ocamlc.opt and their version +AC_PROG_OCAML_VERSION(ocamllex,ocamllex.opt,[ + OCAMLLEX=$toolval + if ! test "x$OCAMLVERSION" = "x" && ! test "$versionval" = "$OCAMLVERSION"; then + AC_MSG_WARN($versionval doesn't match ocaml v. $OCAMLVERSION) + else + OCAMLVERSION=$versionval + fi +],[$2]) +AC_SUBST(OCAMLLEX) +AC_SUBST(OCAMLVERSION) +]) + +# AC_CHECK_OCAMLYACC([ACTION-IF-FOUND],[ACTION-IF-NOT-FOUND]) +#--------------------------------------------------------- +# Check for the existence of ocamlyacc (or ocamlyacc.opt) +# Subst OCAMLYACC variable. +AC_DEFUN([AC_CHECK_OCAMLYACC], +[ +dnl Check the presence of ocamlyacc/ocamlyacc.opt and their version +AC_CHECK_PROG(ac_ocaml_ocamlyacc,ocamlyacc,ocamlyacc) +AC_CHECK_PROG(ac_ocaml_ocamlyacc_opt,ocamlyacc.opt,ocamlyacc.opt) +if test "x$ac_ocaml_ocamlyacc" = "x" && test "x$ac_ocaml_ocamlyacc_opt" = "x"; then + : + $2 +elif ! test "x$ac_ocaml_ocamlyacc_opt" = "x"; then + OCAMLYACC=$ac_ocaml_ocamlyacc_opt + $1 +else + OCAMLYACC=$ac_ocaml_ocamlyacc + $1 +fi +AC_SUBST(OCAMLYACC) +]) + +# AC_CHECK_OCAMLBUILD([ACTION-IF-FOUND],[ACTION-IF-NOT-FOUND]) +#--------------------------------------------------------- +# Check for the existence of ocamlbuild. +# Subst OCAMLBUIKD variable. +AC_DEFUN([AC_CHECK_OCAMLBUILD], +[ +dnl Check the presence of ocamlbuild/ocamlbuild.opt and their version +AC_CHECK_PROG(ac_ocaml_ocamlbuild,ocamlbuild,ocamlbuild) +if test "x$ac_ocaml_ocamlbuild" = "x"; then + : + $2 +else + OCAMLBUILD=$ac_ocaml_ocamlbuild + $1 +fi +AC_SUBST(OCAMLBUILD) +]) + + +# AC_CHECK_OCAMLFIND ([ACTION-IF-FOUND],[ACTION-IF-NOT-FOUND]) +#--------------------------------------------------------- +# Check for the existence of ocamlfind. +# Subst OCAMLFIND variable. +AC_DEFUN([AC_CHECK_OCAMLFIND], +[ +dnl Check the presence of ocamlfind +AC_CHECK_PROG(ac_ocaml_ocamlfind,ocamlfind,ocamlfind) +if test "x$ac_ocaml_ocamlfind" = "x"; then + : + $2 +else + : + OCAMLFIND=$ac_ocaml_ocamlfind + $1 +fi +AC_SUBST(OCAMLFIND) +]) + +# OCAMLFIND_CHECK_MODULE (MODULE,[ACTION-IF-FOUND],[ACTION-IF-NOT-FOUND]) +#--------------------------------------------------------- +# Check for the existence of the module using OCAMLFIND +AC_DEFUN([OCAMLFIND_CHECK_MODULE], +[ +AC_REQUIRE([AC_CHECK_OCAMLFIND]) +dnl Check the presence of module $1 +AC_MSG_CHECKING(for module $1) +if ! test "x$OCAMLFIND" = "x" ; then + ac_ocaml_pkg_$1=`$OCAMLFIND query $1 2> /dev/null` + if ! test "x$ac_ocaml_pkg_$1" = "x"; then + AC_MSG_RESULT($ac_ocaml_pkg_$1) + $2 + else + AC_MSG_RESULT(no) + $3 + fi +else + AC_MSG_RESULT(no) + $3 +fi +]) + +# AC_CHECK_[CAMLP4,CAMLIDL,OCAMLMKLIB,MKCAMLP4] ([ACTION-IF-FOUND],[ACTION-IF-NOT-FOUND]) +#--------------------------------------------------------- +# Subst the corresponding var +AC_DEFUN([AC_CHECK_STAR], +[ +AC_CHECK_PROG($1,$2,$2) +if test "x$$1" = "x"; then + : + $4 +else + : + $3 +fi +AC_SUBST($1) +]) +AC_DEFUN([AC_CHECK_CAMLP4], [AC_CHECK_STAR(CAMLP4,camlp4,$1,$2)]) +AC_DEFUN([AC_CHECK_CAMLP4OF], [AC_CHECK_STAR(CAMLP4OF,camlp4of,$1,$2)]) +AC_DEFUN([AC_CHECK_CAMLP4O], [AC_CHECK_STAR(CAMLP4O,camlp4o,$1,$2)]) +AC_DEFUN([AC_CHECK_CAMLIDL], [AC_CHECK_STAR(CAMLIDL,camlidl,$1,$2)]) +AC_DEFUN([AC_CHECK_OCAMLMKLIB], [AC_CHECK_STAR(OCAMLMKLIB,ocamlmklib,$1,$2)]) +AC_DEFUN([AC_CHECK_MKCAMLP4], [AC_CHECK_STAR(MKCAMLP4,mkcamlp4,$1,$2)]) +AC_DEFUN([AC_CHECK_OCAMLDOC], [AC_CHECK_STAR(OCAMLDOC,ocamldoc,$1,$2)]) +AC_DEFUN([AC_CHECK_XSLTPROC], [AC_CHECK_STAR(XSLTPROC,xsltproc,$1,$2)]) +AC_DEFUN([AC_CHECK_XMLLINT], [AC_CHECK_STAR(XMLLINT,xmllint,$1,$2)]) + +# AC_LIB_OCAML () +#--------------------------------------------------------- +# Get the library path +# Subst OCAMLLIB +AC_DEFUN([AC_LIB_OCAML], +[ +AC_REQUIRE([AC_CHECK_OCAMLC]) +AC_MSG_CHECKING(for ocaml libdir) +OCAMLLIB=`$OCAMLC -where` +AC_MSG_RESULT($OCAMLLIB) +AC_SUBST(OCAMLLIB) +]) + +# AC_CHECK_XSL (VAR,config,DEFAULT_STYLESHEET,[ACTION-IF-OK],[ACTION-IF-NOT-OK]) +#--------------------------------------------------------- +# Check the presence and the possibility to validate +# and apply the given stylesheet +# Subst XSLTPROC, XMLLINT, DOCBOOK_STYLESHEET_VAR, +AC_DEFUN([AC_CHECK_XSL], +[ +AC_REQUIRE([AC_CHECK_XSLTPROC]) +AC_REQUIRE([AC_CHECK_XMLLINT]) + +AC_ARG_WITH(docbook-stylesheet-$2, + AC_HELP_STRING([--with-docbook-stylesheet-$2=file], [Where to find the docbook stylesheet for $2 generation]), + $1=$withval, $1="$3") + +AC_MSG_CHECKING(for $2 XSL) +if ! test -e "$$1"; then + AC_MSG_RESULT(no) + $1= +else + AC_MSG_RESULT($$1) +fi + +if ! test "x$$1" = "x" && ! test "x$XSLTPROC" = "x"; then + : + $4 +else + : + $5 +fi + +AC_SUBST($1) +]) + + +# AC_CHECK_HTMLXSL (DEFAULT_STYLESHEET,[ACTION-IF-OK],[ACTION-IF-NOT-OK]) +#--------------------------------------------------------- +# Check the possibility to generate HTML out of Docbook XML +AC_DEFUN([AC_CHECK_HTMLXSL], +[ +AC_CHECK_XSL(HTMLXSL,html,$1,$2,$3) +]); + +# AC_CHECK_MANXSL (STYLESHEET,DEFAULT_STYLESHEET,[ACTION-IF-OK],[ACTION-IF-NOT-OK]) +#--------------------------------------------------------- +# Check the possibility to generate manpages out of Docbook XML +AC_DEFUN([AC_CHECK_MANXSL], +[ +AC_CHECK_XSL(MANXSL,manpages,$1,$2,$3) +]); + +# AC_CHECK_PDFXSL (STYLESHEET,DEFAULT_STYLESHEET,[ACTION-IF-OK],[ACTION-IF-NOT-OK]) +#--------------------------------------------------------- +# Check the possibility to generate PDF out of Docbook XML +AC_DEFUN([AC_CHECK_PDFXSL], +[ +AC_CHECK_XSL(FOXSL,pdf,$1,$2,$3) +AC_CHECK_PROG(FOP,fop,fop) +if test "x$FOP" = "x"; then + AC_MSG_WARN(Cannot find fop.) + $3 +fi +]); + diff --git a/autogen.sh b/autogen.sh new file mode 100755 index 0000000..d6a3aa0 --- /dev/null +++ b/autogen.sh @@ -0,0 +1,4 @@ +#!/bin/sh +set -e +autoconf +rm -rf autom4te.cache || true diff --git a/bootstrap.lua b/bootstrap.lua new file mode 100644 index 0000000..c3130f7 --- /dev/null +++ b/bootstrap.lua @@ -0,0 +1,38 @@ + +local os = os +local error = error +local lfs = require("lfs") +local package = package + +module("bootstrap") + +function init () + local ci_url = os.getenv("CI_URL") + if not ci_url then + ci_url = "http://mini:8080/job/continuous-integration/label=debian-squeeze64/lastSuccessfulBuild/artifact/dist/ci.zip" + end + local bootstrapdir = "build/bootstrap" + if lfs.attributes(bootstrapdir) then + for fn in lfs.dir(bootstrapdir) do + if fn ~= "." and fn ~= ".." then + local realfn = bootstrapdir .. "/" .. fn + if not os.remove(realfn) then + error("Cannot remove " .. realfn) + end + end + end + lfs.rmdir("build/bootstrap") + end + lfs.mkdir("build") + lfs.mkdir("build/bootstrap") + local topdir = lfs.currentdir() + lfs.chdir("build/bootstrap") + if os.execute("curl -o ci.zip " .. ci_url) ~= 0 then + error "Cannot download ci.zip" + end + if os.execute("unzip ci.zip") ~= 0 then + error "Cannot unzip ci.zip" + end + lfs.chdir(topdir) + package.path = "./build/bootstrap/?.lua;" .. package.path +end diff --git a/ci-main.lua b/ci-main.lua new file mode 100644 index 0000000..ef4a351 --- /dev/null +++ b/ci-main.lua @@ -0,0 +1,27 @@ + +bootstrap = require("bootstrap") + +bootstrap.init() + +oasis = require("oasis") +darcs = require("darcs") +ci = require("ci") +godi = require("godi") + +ci.init() +godi.init() +oasis.init() +darcs.init() + +godi.bootstrap("3.12") +godi.update() +godi.upgrade() +godi.build("godi-findlib") +godi.build("godi-camomile") +godi.build("godi-ocaml-fileutils") +godi.build("godi-ounit") + +ci.exec("autoconf") +ci.exec("./configure", "--disable-doc", "--enable-test") +ci.exec("make", "all") +ci.exec("make", "test") diff --git a/configure.in b/configure.in new file mode 100644 index 0000000..9840d2b --- /dev/null +++ b/configure.in @@ -0,0 +1,153 @@ +dnl ************************************************************************** +dnl * ocaml-gettext: a library to translate messages * +dnl * * +dnl * Copyright (C) 2003-2008 Sylvain Le Gall * +dnl * * +dnl * This library is free software; you can redistribute it and/or * +dnl * modify it under the terms of the GNU Lesser General Public * +dnl * License as published by the Free Software Foundation; either * +dnl * version 2.1 of the License, or (at your option) any later version; * +dnl * with the OCaml static compilation exception. * +dnl * * +dnl * This library is distributed in the hope that it will be useful, * +dnl * but WITHOUT ANY WARRANTY; without even the implied warranty of * +dnl * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * +dnl * Lesser General Public License for more details. * +dnl * * +dnl * You should have received a copy of the GNU Lesser General Public * +dnl * License along with this library; if not, write to the Free Software * +dnl * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * +dnl * USA * +dnl ************************************************************************** + +AC_INIT(ocaml-gettext, 0.3.7) + +AC_PROG_INSTALL + +AC_ARG_WITH(tmpbuilddir, + AC_HELP_STRING([--with-tmpbuilddir=dir], [Location of the temporary build dir. Default: ${topdir}/build]), + TMPBUILDDIR=$withval, [TMPBUILDDIR='${abs_top_srcdir}/_build']) + +AC_ARG_WITH(defaultlocaledir, + AC_HELP_STRING([--with-defaultlocaledir=dir], [Location of the default locale dir. Default: /usr/share/locale]), + DEFAULTLOCALEDIR="\"$withval\"", DEFAULTLOCALEDIR="\"/usr/share/locale\"") + +AC_ARG_WITH(localedir, + AC_HELP_STRING([--with-localedir=dir], [Additional location of the locale dir. Default: "/usr/local/share/locale"]), + LOCALEDIR=$LOCALEDIR '"$withval";', LOCALEDIR='"/usr/local/share/locale"') + +AC_ARG_WITH(stub-cflags, + AC_HELP_STRING([--with-stub-cflags=flags], [Flags to use when compiling gettext-stub]), + STUB_CFLAGS=$withval, STUB_CFLAGS="") + +AC_ARG_WITH(stub-ldflags, + AC_HELP_STRING([--with-stub-ldflags=flags], [Flags to use when linking gettext-stub. Default: -L(OCAMLLIB)]), + STUB_LDFLAGS=$withval, STUB_LDFLAGS="") + +AC_ARG_WITH(docbook-stylesheet, + AC_HELP_STRING([--with-docbook-stylesheet=dir], [Where to find the docbook stylesheets]), + DOCBOOK_STYLESHEET=$withval, DOCBOOK_STYLESHEET=/usr/share/xml/docbook/stylesheet/nwalsh) + +AC_ARG_ENABLE(camomile, + AC_HELP_STRING([--enable-camomile], [Build camomile extension. Default: yes]), + BUILD_CAMOMILE=$enableval, BUILD_CAMOMILE="yes") + +AC_ARG_ENABLE(stub, + AC_HELP_STRING([--enable-stub], [Build stub extension. Default: yes]), + BUILD_STUB=$enableval, BUILD_STUB="yes") + +AC_ARG_ENABLE(test, + AC_HELP_STRING([--enable-test], [Build program test. Default: no]), + BUILD_TEST=$enableval, BUILD_TEST="no") + +AC_ARG_ENABLE(bench, + AC_HELP_STRING([--enable-bench], [Build program benchmark. Default: no]), + BUILD_BENCH=$enableval, BUILD_BENCH="no") + +AC_ARG_ENABLE(doc, + AC_HELP_STRING([--enable-doc], [Build the documentation. Default: yes]), + BUILD_DOC=$enableval, BUILD_DOC="yes") + +AC_ARG_ENABLE(doc-pdf, + AC_HELP_STRING([--enable-doc-pdf], [Build the PDF documentation. Default: no]), + BUILD_DOC_PDF=$enableval, BUILD_DOC_PDF="no") + +AC_ARG_WITH(ocamlfind-install-flags, + AC_HELP_STRING([--with-ocamlfind-install-flags], [Flags to use when installing with ocamlfind (e.g. -destdir, -ldconf). Default:]), + OCAMLFIND_INSTALL_FLAGS=$withval, OCAMLFIND_INSTALL_FLAGS="") + +AC_ARG_WITH(ocamlfind-remove-flags, + AC_HELP_STRING([--with-ocamlfind-remove-flags], [Flags to use when uninstalling with ocamlfind (e.g. -destdir, -ldconf). Default: (OCAMLFIND_INSTALL_FLAGS)]), + OCAMLFIND_REMOVE_FLAGS=$withval, OCAMLFIND_REMOVE_FLAGS='${OCAMLFIND_INSTALL_FLAGS}') + +AC_CHECK_OCAMLC([],[AC_MSG_ERROR(Cannot find ocamlc.)]) +AC_CHECK_OCAMLOPT([],[AC_MSG_WARN(Cannot find ocamlopt, byte compilation only)]) +AC_CHECK_OCAMLLEX([],[AC_MSG_ERROR(Cannot find ocamllex.)]) +AC_CHECK_OCAMLYACC([],[AC_MSG_ERROR(Cannot find ocamlyacc.)]) +AC_CHECK_OCAMLFIND([],[AC_MSG_ERROR(Cannot find ocamlfind.)]) +AC_CHECK_CAMLP4([],[AC_MSG_ERROR(Cannot find camlp4.)]) +AC_CHECK_CAMLP4O([],[AC_MSG_ERROR(Cannot find camlp4o.)]) +AC_CHECK_CAMLP4OF([],[AC_MSG_ERROR(Cannot find camlp4of.)]) +AC_CHECK_OCAMLMKLIB([],[AC_MSG_ERROR(Cannot find ocamlmklib.)]) + +if test "x$BUILD_DOC" = "xyes"; then + AC_CHECK_OCAMLDOC([],[AC_MSG_ERROR(Cannot find ocamldoc.)]) + AC_CHECK_HTMLXSL($DOCBOOK_STYLESHEET/xhtml/chunk.xsl,[],[AC_MSG_ERROR(Cannot generate HTML pages.)]) + AC_CHECK_MANXSL($DOCBOOK_STYLESHEET/manpages/docbook.xsl,[],[AC_MSG_ERROR(Cannot generate manpages.)]) + if test "x$BUILD_DOC_PDF" = "xyes"; then + AC_CHECK_PDFXSL($DOCBOOK_STYLESHEET/fo/docbook.xsl,[],[AC_MSG_ERROR(Cannot generate PDF files.)]) + fi +fi + +OCAMLFIND_CHECK_MODULE(fileutils,[],[AC_MSG_ERROR(Cannot find ocaml-fileutils)]) + +OCAMLFIND_CHECK_MODULE(camomile,[], +[ + if test "x$BUILD_CAMOMILE" = "xyes"; then + AC_MSG_ERROR(Cannot build libgettext-camomile-ocaml.) + fi +]) + +if test "x$BUILD_TEST" = "xyes"; then + OCAMLFIND_CHECK_MODULE(oUnit,[],[AC_MSG_ERROR(Cannot build test.)]) +fi + +if test "x$BUILD_BENCH" = "xyes"; then + OCAMLFIND_CHECK_MODULE(benchmark,[],[AC_MSG_ERROR(Cannot build benchmark)]) +fi + +AC_LIB_OCAML() + +# check if we build at least one of stub | camomile for building ocaml-gettext + +if test "$BUILD_STUB" = "no" || test "$BUILD_CAMOMILE" = "no"; then + if test "$BUILD_TEST" = "yes"; then + AC_MSG_ERROR(Cannot build test since it depends on both gettext-stub and gettext-camomile) + fi + if test "$BUILD_BENCH" = "yes"; then + AC_MSG_ERROR(Cannot build bench since it depends on both gettext-stub and gettext-camomile) + fi +fi + +# substitutions to perform +AC_SUBST(TMPBUILDDIR) +AC_SUBST(DOCDIR) +AC_SUBST(EXE) +AC_SUBST(DEFAULTLOCALEDIR) +AC_SUBST(LOCALEDIR) +AC_SUBST(CODESET) +AC_SUBST(STUB_CFLAGS) +AC_SUBST(STUB_LDFLAGS) +AC_SUBST(BUILD_CAMOMILE) +AC_SUBST(BUILD_STUB) +AC_SUBST(BUILD_TEST) +AC_SUBST(BUILD_BENCH) +AC_SUBST(BUILD_DOC) +AC_SUBST(BUILD_DOC_PDF) +AC_SUBST(OCAMLFIND_INSTALL_FLAGS) +AC_SUBST(OCAMLFIND_REMOVE_FLAGS) + +# Finally create the Makefile from Makefile.in +OUTPUT="ConfMakefile libgettext-ocaml/gettextConfig.ml libgettext-ocaml/META" + +AC_OUTPUT([$OUTPUT]) diff --git a/doc/Makefile b/doc/Makefile new file mode 100644 index 0000000..03b6c35 --- /dev/null +++ b/doc/Makefile @@ -0,0 +1,84 @@ +########################################################################## +# ocaml-gettext: a library to translate messages # +# # +# Copyright (C) 2003-2008 Sylvain Le Gall # +# # +# This library is free software; you can redistribute it and/or # +# modify it under the terms of the GNU Lesser General Public # +# License as published by the Free Software Foundation; either # +# version 2.1 of the License, or (at your option) any later version; # +# with the OCaml static compilation exception. # +# # +# This library 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 # +# Lesser General Public License for more details. # +# # +# You should have received a copy of the GNU Lesser General Public # +# License along with this library; if not, write to the Free Software # +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 # +# USA # +########################################################################## + +include ../ConfMakefile + +all: + +ifeq ($(BUILD_DOC),yes) +# HTML documentation +HTML_TARGETS=$(call HTML_TARGET,reference-manual.xml) +$(call HTML_TARGET,reference-manual.xml): IMAGES=gui.png gui-fr.png + +# PDF documentation +ifeq ($(BUILD_DOC_PDF),yes) +PDF_TARGETS=$(call PDF_TARGET,reference-manual.xml) +endif + +ODOC_TARGETS=$(call ODOC_TARGET,api) $(call ODOC_TARGET,api-ext) + +# Manpage documentation +MAN_TARGETS=ocaml-gettext.1 ocaml-xgettext.1 ocaml-gettext.5 +ocaml-gettext.1: ocaml-gettext.xml +ocaml-xgettext.1: ocaml-xgettext.xml +ocaml-gettext.5: ocaml-gettext-options.xml +endif + +include ../TopMakefile + +ifeq ($(BUILD_DOC),yes) +ALL_PACKAGES=gettext.base gettext.extension gettext.extract + +ifeq ($(BUILD_CAMOMILE),yes) +ALL_PACKAGES+=gettext-camomile +endif + +ifeq ($(BUILD_STUB),yes) +ALL_PACKAGES+=gettext-stub +endif + +$(call ODOC_TARGET,api):REQUIRES=$(ALL_PACKAGES) +$(call ODOC_TARGET,api):OCAMLDOCFLAGS+=-I ../libgettext-ocaml/ +$(call ODOC_TARGET,api):../libgettext-ocaml/gettext.mli +$(call ODOC_TARGET,api):../libgettext-ocaml/gettextTypes.ml +$(call ODOC_TARGET,api):../libgettext-ocaml/gettextDummy.ml + +ifeq ($(BUILD_CAMOMILE),yes) +$(call ODOC_TARGET,api):../libgettext-camomile-ocaml/gettextCamomile.mli +$(call ODOC_TARGET,api):OCAMLDOCFLAGS+=-I ../libgettext-camomile-ocaml/ +endif + +ifeq ($(BUILD_STUB),yes) +$(call ODOC_TARGET,api):../libgettext-stub-ocaml/gettextStub.ml +$(call ODOC_TARGET,api):OCAMLDOCFLAGS+=-I ../libgettext-stub-ocaml/ +endif + +$(call ODOC_TARGET,api-ext):OCAMLDOCFLAGS+=-I ../libgettext-ocaml +$(call ODOC_TARGET,api-ext):REQUIRES=$(ALL_PACKAGES) +$(call ODOC_TARGET,api-ext):$(filter-out ../libgettext-ocaml/pr_gettext.ml,$(wildcard ../libgettext-ocaml/*.ml)) +$(call ODOC_TARGET,api-ext):$(wildcard ../libgettext-ocaml/*.mli) +endif + +clean:: + -$(RM) '$(CURDIR)/ocaml-gettext.5' + -$(RM) '$(CURDIR)/ocaml-xgettext.1' + -$(RM) '$(CURDIR)/ocaml-gettext.1' diff --git a/doc/gui-fr.png b/doc/gui-fr.png new file mode 100644 index 0000000..5369cbf Binary files /dev/null and b/doc/gui-fr.png differ diff --git a/doc/gui.png b/doc/gui.png new file mode 100644 index 0000000..3a4fd90 Binary files /dev/null and b/doc/gui.png differ diff --git a/doc/ocaml-gettext-options.xml b/doc/ocaml-gettext-options.xml new file mode 100644 index 0000000..c76c6ad --- /dev/null +++ b/doc/ocaml-gettext-options.xml @@ -0,0 +1,189 @@ + + + + + + + + ocaml-gettext + + 2008-04-29 + + + + OCAML-GETTEXT + 5 + + + + ocaml-gettext + + common options to manage internationalisation in OCaml program through + ocaml-gettext library. + + + + + + --gettext-failsafe + + + ignore + inform-stderr + raise-exception + + + + --gettext-disable + --gettext-domain-dir + textdomain + dir + + + --gettext-dir dir + + --gettext-language language + + --gettext-codeset codeset + + + + + + OCAML-GETTEXT OPTIONS + + + This section describes briefly the common options provided by programs using ocaml-gettext library. + + + + + + + + + + Defines the behaviour of ocaml-gettext regarding any error that could be encountered during the + processing of string translation. is the default behaviour. + The string returned is the original string untranslated. This behaviour is consistent and + allows to have a usable output, even if it is not perfect. + + + + + + + + + + Same behaviour as , except that a message is printed on stderr, + + + + + + + + + + Stops the program by raising an exception when an error is encountered. + + + + + + + + + + Disables any translation made by ocaml-gettext. All translations return the original string + untranslated. + + + + + + + + + + Defines a dir to search for a specific domain. This could be useful if MO files are stored in + a non standard directory. + + + + + + + + + + Adds a directory to search for MO files. + + + + + + + + + + Sets the language to use in ocaml-gettext library. The language should be POSIX compliant. The language should + follow the following convention: lang[_territory][.charset][@modifier]. The lang and territory should be + two letters ISO code. Charset should be a valid ISO character set (at least recognised by the underlying + charset recoding routine). For example, valid languages are: fr_FR.ISO-8859-1@euro, de_DE.UTF-8. + + + + + + + + + + Sets the codeset for output. + + + + + + + Users should be aware that these command line options, apply only for strings after the initialisation of the library. + This means that if the options initially guessed by ocaml-gettext don't match the command line provided, there should + be some untranslated string, because these strings are translated before parsing options. This is particularly true + for the usage message itself (): even if the strings are translated, they are translated + before setting the correct option. + + + + Some options ( for example) are overrided internally for particular use. It + should be required to always translate strings to UTF-8 in graphical user interface (because GTK2 requires it). + + + + diff --git a/doc/ocaml-gettext.xml b/doc/ocaml-gettext.xml new file mode 100644 index 0000000..663dbff --- /dev/null +++ b/doc/ocaml-gettext.xml @@ -0,0 +1,417 @@ + + + + + + + + ocaml-gettext + + 2008-04-29 + + + + OCAML-GETTEXT + 1 + + + + ocaml-gettext + program to manage PO and MO files for OCaml source files. + + + + + ocaml-gettext + --action + + + extract + compile + install + uninstall + merge + + + + --extract-command cmd + + --extract-default-option options + + --extract-filename-option filename + options + + --extract-pot filename + + --compile-output filename + + --install-language language + + --install-category category + + --install-textdomain textdomain + + --install-destdir dirname + + --uninstall-language language + + --uninstall-category category + + --uninstall-textdomain textdomain + + --uninstall-orgdir dirname + + --merge-pot filename + + --merge-backup-extension extension + + + + + --version + --short-version + -help + --help + + + file + + + + + DESCRIPTION + + + This manual page documents briefly the ocaml-gettext command. + + + + + + + + + + Files provided are considered to be OCaml source files and ocaml-gettext + tries to extract translatable strings of it. The output of the command is a POT file. As a + special case, if a file named POTFILES is in the list of the file + provided, every line of it is considered as a file to be searched. + + + + + + + + + + Files provided are considered to be PO file. These files are compiled in binary + MO files, + + + + + + + + + + Files provided are considered to be MO files. They are installed in their + respective directories considering language, textdomain and category, + + + + + + + + + + This is the symmetric command to , but it uninstalls files provided for the considered + language, textdomain and category, + + + + + + + + + + Merges a POT file with the provided PO file. + + + + + + + + + + Command to extract translatable strings from an OCaml source file. This command should output + the same marshalled structure as ocaml-xgettext. The best to do is to use the same + build version of ocaml-gettext. + + + + + + + + + + Default options used when extracting translatable strings. These options are camlp4 options and + will be passed to ocaml-xgettext when processing files that don't already have specific + camlp4 options. + + + + + + + + + + Specific filename camlp4 options. It is used when extracting strings from the specified filename. It + overrides default camlp4 options. + + + + + + + + + + POT file to write when extracting translatable strings. + + + + + + + + + + MO file to write when compiling a PO file. If not provided, the output will be the name of the PO file with + ".mo" extension. + + + + + + + + + + Language to use when installing a MO file. + + + + + + + + + + Category to use when installing a MO file. + + + + + + + + + + Textdomain to use when installing a MO file. + + + + + + + + + + Base directory to use when installing a MO file. + + + + + + + + + + Language to use when uninstalling a MO file. + + + + + + + + + + Category to use when uninstalling a MO file. + + + + + + + + + + Textdomain to use when uninstalling a MO file. + + + + + + + + + + Base directory used when uninstalling a MO file. + + + + + + + + + + POT file to use as a master for merging PO file. + + + + + + + + + + Backup extension to use when moving PO file which have been merged. + + + + + + + + + + Return version information on ocaml-gettext. + + + + + + + + + + Returns only the version of ocaml-gettext. The return is made to be easily parseable by configure + script. The output of this command will always be the shortest version string, made of numeric characters (0-9) and + ".". The version strings should be compared considering that a version A is greater than a version B if there is a + number between two "." of A that is greater than B the corresponding number, beginning at the right of the string. + For example: 0.14 is greater than 0.13.1. + + + + + + + + + + + + + Displays the help about the ocaml-gettext command. + + + + + + + + + + + + NOTES + + Options , , + and could be + guessed from the filename provided. You must be aware that these options can conflict with the fact that you + provide several files to install. For example, if you provide a textdomain, you should either install several + MO files which filenames only reflect the language or only one MO file if you also provide a language. For + example, you can execute + ocaml-gettext + + fr.po + de.po + + without problem, but you cannot execute + ocaml-gettext + + + fr.po + de.po + . This restriction is due to the fact that you should not over specified file installation. + + + Rules for guessing the language/textdomain are: language[.textdomain].mo. For a full automated install + without giving any hints, through command line options, you should name your file + fr.mytextdomain.mo or de.mytextdomain.mo. + + + + SEE ALSO + + + + ocaml-xgettext + 1 + + + + + ocaml-gettext + 5 + + + + diff --git a/doc/ocaml-xgettext.xml b/doc/ocaml-xgettext.xml new file mode 100644 index 0000000..28670a8 --- /dev/null +++ b/doc/ocaml-xgettext.xml @@ -0,0 +1,84 @@ + + + + + + + + ocaml-gettext + + 2008-04-29 + + + + OCAML-XGETTEXT + 1 + + + + ocaml-xgettext + program to extract translatable strings from OCaml source file. + + + + + ocaml-xgettext + camlp4 arguments + filename + + + + + DESCRIPTION + + + This manual page documents briefly the ocaml-xgettext + + + + ocaml-xgettext is a camlp4 top level. This top level is compiled with the + printer module pr_gettext.cmo. It outputs an OCaml marshalled data structure that can only + be understood by ocaml-gettext. The purpose of this program is to be a back end for OCaml source + code string extraction. You should not use it directly. + + + + + SEE ALSO + + + + ocaml-gettext + 1 + + + + + camlp4 + 1 + + + + diff --git a/doc/reference-manual.xml b/doc/reference-manual.xml new file mode 100644 index 0000000..c83ae7c --- /dev/null +++ b/doc/reference-manual.xml @@ -0,0 +1,1131 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + +TO DO'> +&fbk;' > +&fbk;' > +&fbk;' > +&fbk;' > +&fbk;' > +&fbk;' > +&fbk;' > +&fbk;' > +&fbk;' > +&fbk;' > +&fbk;' > +&fbk;' > +&fbk;' > +' > +' > +' > + + + + + + + + + + + + + + + + + + + + + + + ]> + + ocaml-gettext reference manual + + + + This user manual is considered as a source code and is licensed under the GNU Lesser General Public + License v2.1 with OCaml static compilation exception. + + + + Sylvain + Le Gall + + + 2005 + Sylvain Le Gall + + + + + svn:revision + svn:date + + + + + Overview +
+ What is ocaml-gettext? + + ocaml-gettext is a library to support string translation in OCaml. It provides a simple interface + to help programmers and translators to create programs that can support different languages. This + allows the internationalisation of programs. + + + ocaml-gettext provides two main functionalities: + + + + translate English strings into localised strings (depending on which gettext data are installed), + + + + + convert charset from the one used by the translator into the charset of the user. + + + + + The library is build according three points of view: + + + + for the programmer: this library provides functions that can be used in OCaml, + + + + + for the translator: this library provides a standard file format (PO file) to help the translator, + + + + + for the user: this library provides a set of command line options to set the language + and the charset. + + + + + + ocaml-gettext was initially only a wrapper of gettext. It comes with a patch against the source of + xgettext to add support of the OCaml language. As i already used this approach, + i was convinced that this library should be better integrated by using more advanced features of + OCaml (such as camlp4). + + + As a result of porting gettext to ocaml-gettext, we have : + + + + a library that can understood the native format of gettext file (MO file), + + + + + two implementations: a wrapper around gettext and a pure OCaml implementation using + camomile, + + + + + ocaml-gettext: a command line tool to help you to extract, to merge and to install + gettext data. + + + + +
+
+ How is ocaml-gettext related to gettext? + + ocaml-gettext is a close cousin of gettext. In fact, the API is based on gettext. Almost everything + in ocaml-gettext is compatible with gettext: + + + functions provided in the API are very close to the gettext one, + + + the library contains a binding of the gettext library, + + + + all the file used are gettext compatible: the library uses PO file for translator + and MO file for translation, + + + + + the library tries to use the same initialisation sequence as gettext : + it uses the same environment variable, tries to find MO files in the + same places... + + + + + + This documentation will not covered the point that are better explained in the + gettext documentation. It is highly recommended + to read this documentation, before reading this manual. Most of the point that are explained there + are not explained again here. However, we have tried to be as precise as possible to enable + programming with ocaml-gettext without having the need to be a gettext expert. + +
+
+ + How to install ocaml-gettext +
+ Using source + To compile ocaml-gettext, you need to install the following prerequisites : + + + + OCaml v3.10.1 or later, + + + + + findlib v1.0.4 or later, + + + + + ocaml-fileutils v0.3.0 or later, + + + + + camomile v0.7.1 or later, + + + + + gettext v0.14.3 or later, + + + + + OUnit v1.0.1 or later + Only if you want to build unitary test tool, + + + + + ocaml-benchmark v0.6 or later + Only if you want to build benchmarking tool, + + + + + Docbook DTD and stylesheets v4.3 + Only if you want to build the documentation, + + + + + xsltproc v1.1.12 or later, + , + + + + + fop v0.20.5 or later. + , + + + + + + Camomile and gettext are optional but you need at least one of them in order to be able to build + the command line tool ocaml-gettext. + + + After having build and install all prerequisites, extract the source + code of ocaml-gettext to a directory. Go to the source directory and type : + + + + ./configure + + + If needed, you can use ./configure + to have a complete help on every option you can use to tweak the + installation of ocaml-gettext. To enable documentation generation, use + . To enable benchmark executable, use + . To enable test executable, use + . + + + + + + + make + + + + + make install + + + + + cd test + + + + + ./test + + + + + + ./benchmark + + + + + + + Since ocaml-gettext is not yet a stable release, building the benchmark and the unitary test tools + is important, especially for debugging purpose. If you encounter problems, you should first + try to run this two commands. They will give you good hints on what causes the problem. + + + + There is a patches directory in the source tree. This directory contains patches + against different programs that should be compatible with ocaml-gettext. Please have a look to the + README.patches of this directory, to know how to handle patching these programs. + + +
+
+ Debian distribution + + + A debian package is available in the official Debian archive (release "unstable", + "main" section). + + + To install it, use the command : + apt-get + libgettext-ocaml-dev + . + +
+
+ Other distributions + + There is no plan to release packages for other distributions. If you have any skill + concerning the packaging of ocaml-gettext for any other distribution, please contact me. + +
+
+ + Programming with ocaml-gettext +
+ Overview + + The API of ocaml-gettext is really reduced. It is made on purpose. The design is heavily + based on modules and functors. There is no real reason for this design, it was just + really useful at the time the code was written. + + + The library supposes that all the textdomain that will be used during + translation, are declared before using it. It is a real constraint, but it enables more + optimisation. Moreover, it allows having a more "functional" use. + + + + First of all, a parameter t should be defined. This parameter holds + all the required value to initialise ocaml-gettext. In particular, it contains information + about : + + + + which textdomain will be used, + + + + + which language will be used, + + + + + how the error should be handle, + + + + + which directory to search. + + + + This parameter is build and updated through internal functions. You don't have direct access + to it. + + + + The parameter t is not directly used for translation. It must be converted into + a parameter t' which is a real function to access translation. The transformation + from t to t' is handled through a function + realize. The parameter t' is used in low level translation. + + + + All the work of the library is done in the function realize. This function is + not provided in the base package. It is build out of real implementation of ocaml-gettext (such as + gettext-camomile or gettext-stub). This function could handle + all the parameters in different ways. Concerning gettext-camomile, it builds a + translation table for all the files found which correspond to a declared textdomain. + + + + Since it should be very difficult to pass a parameter t or t' in + all functions that should use translation, we provide a more simple way to use the library. The + top level functions use a global reference to store the parameters t and + t'. This helps to integrate ocaml-gettext more easily into existing application. + +
+
+ Makefile and source layout + + + The source layout should conform to the one described in + gettext documentation. In particular, it should + contain a po directory. There should be in this directory : + + + + a file LINGUAS describing available translations, + + + + + a file POTFILES containing the listing of all files that + contain translatable strings, + + + + + a Makefile to build the whole thing, + + + + + a set of PO file which contains translated strings for different languages. + + + + During the build, the Makefile should generate a file + your-domain.pot that contains a template PO file that can be used + by translator. + + + <filename>Makefile</filename> for <filename>po</filename> + &po-makefile; + + + <filename>LINGUAS</filename> + &LINGUAS; + + + <filename>POTFILES</filename> + &POTFILES; + + + + To build programs and libraries with ocaml-gettext, the preferred way is to use ocamlfind. + There are five findlib packages: + + + + gettext.base: the base package of ocaml-gettext. It contains the top level type + required to compile any library, + + + + + + + gettext.extension + + + This feature is described here for your information only. Since it belongs to + low level implementation of ocaml-gettext, it should not be used. + + + : a package used to extend ocaml-gettext. It is reserved for very + particular functions (such as creating a new realize function), + + + + + + + gettext.extract: a package that enables + to create special camlp4 program (such as + ocaml-xgettext), + + + + + + + gettext-stub: an implementation of ocaml-gettext using gettext, + + + + + + + gettext-camomile: an implementation of ocaml-gettext using camomile. It is a pure + ocaml implementation. + + + + + + In order to link an application or a library using ocaml-gettext, you should link with one of : gettext.base, + gettext-camomile or gettext-stub. + + + <filename>Makefile</filename> + &makefile; + +
+
+ Library + + Library should use the module Gettext.Library. It doesn't need any real implementation + of ocaml-gettext. By this way, you can let the library user choose the most appropriate ocaml-gettext + implementation. This point is essential : a library could be used as well in a GUI program or in short run + command line program. These two examples don't require the same kind of implementation at all: GUI program + loads most of their translated strings, command line program only use one among them. So by using the module + Gettext.Library framework, you don't restrict programs to use one particular implementation + of ocaml-gettext. + + + The library should define, using the functor Init provided: + + + + his textdomain through the textdomain value, + + + + + his dependencies through the dependencies value, + + + + + if needed his charset and directory binding (but it is not recommended to do so). + + + + + + After having instantiated the module Gettext.Library with the appropriate Init, + you should use the function provided : + + + + s_ : for translating singular strings, + + + + + f_ : for translating singular strings which will be used with Printf + function + + + Strings which should be used by Printf function are checked to be sure + that the returned strings are equivalent to the provided English string. In particular, every + "%"-symbol should be the same in the provided string and the returned string. If not, + it is the untranslated string which is returned. + + , + + + + + sn_ : for translating plural strings, + + + + + fn_ : for translating plural strings which will be used with Printf + function, + + + + + + + You must keep the function name s_, f_, sn_ and fn_. + The extraction of translatable strings matches these names. If you don't keep it, the extraction of translatable strings + will fail. + + + + <filename>library.ml</filename> + &library-ml; + + + <filename>libraryGettext.ml</filename> + &library-gettext-ml; + + + All the calls to translation functions, use the textdomain provided in Init. + + + The only constraint when using ocaml-gettext in your library is to provide an access to the value + Gettext.Library.init. This value is used as dependencies for other libraries and + programs that depend on it. For example, since you use the library ocaml-gettext, your primary dependency + is the function init provided in the top level (the function Gettext.string_of_exception + is localised). + + + + If you distribute your library, don't forget to mention that ocaml-gettext will only be able to translate + the string defined in your library, if and only if the MO file build with is also installed. If not installed + ocaml-gettext is useless. + + +
+
+ Program + + Program should use ocaml-gettext just as libraries do. The only difference lies in the fact that you should provide + a realize function in the InitProgram. The other difference is that the + init value is not a dependency that should be used by other program. It is a Arg + usable value. It allows user to define some important parameters. + + + <filename>program.ml</filename> + &program-ml; + + + <filename>programGettext.ml</filename> + &program-gettext-ml; + + + Output of <command>program <option>--help</option></command> + + $>./program + + --my-name name Your name. Default : "" + --gettext-failsafe {ignore|inform-stderr|raise-exception} + Choose how to handle failure in ocaml-gettext. Default: ignore. + --gettext-disable Disable the translation perform by ocaml-gettext. Default: enable. + --gettext-domain-dir textdomain dir Set a dir to search ocaml-gettext files for the specified domain. Default: [ ]. + --gettext-dir dir Add a search dir for ocaml-gettext files. Default: [ "/usr/share/locale"; + "/usr/local/share/locale" ]. + --gettext-language language Set the default language for ocaml-gettext. Default: (none). + --gettext-codeset codeset Set the default codeset for outputting string with ocaml-gettext. Default: ISO-8859-1. + -help Display this list of options + --help Display this list of options + + + + + If you want to include a manpage (or info file), that describes the command line option of ocaml-gettext, you should use the Docbook + XML fragment distributed with this application. Docbook should be enough generic to allow you to link it into your documentation. + If you don't want to link it with your documentation, you can refer to + ocaml-gettext-options manpage. + + + <filename>program.xml</filename> : Docbook manpage + &program-xml; + + + + <filename>Makefile</filename> : Makefile for docbook manpage + &makefile-doc; + + + + You should take care of what implementation of ocaml-gettext you are using. In order to choose the right implementation + you should consider your program and every characteristic of it (how many strings does it need to fetch? Does it use already + a C library that link with gettext?). + + Characteristics of ocaml-gettext implementation. + + + + Implementation + Characteristics + Use + + + + + GettextCamomile.Map + + + + + Pure OCaml implementation, + + + + + Full load of all MO files before any translation, + + + + + Use OCaml standard Map. + + + + + + + + + Pure OCaml program, + + + + + Program that requires to translate a lot of strings, + + + + + Threaded program (since it use OCaml Map, it should be thread safe without + problem). + + + + + + + GettextCamomile.Hashtbl + + + + + Pure OCaml implementation, + + + + + Full load of all MO file before any translation, + + + + + Use OCaml standard Hashtbl. + + + + + + + + + Pure OCaml program, + + + + + Program that requires to translate a lot of strings, + + + + + Should work with threaded program, provided that the Hashtbl + works in threaded environment. + + + + + + + GettextCamomile.Open + + + + + Pure OCaml implementation, + + + + + Load strings from MO if needed, + + + + + Use OCaml standard Hashtbl, + + + + + Use a dichotomic search for the strings, + + + + + Compute MO file to open at initialisation, + + + + + Open a file when fetching string, + + + + + Doesn't memorise already translated strings, + + + + + Implementation design copied from gettext. + + + + + + + + + Pure OCaml program, + + + + + Program that require to translate very few strings, + + + + + Should work with threaded program, provided that open_in function call + work. + + + + + + + GettextStub.Native + + + + + Native gettext library, + + + + + Partial load of all MO file before any translation, use + mmap. + + + + + + + + + OCaml program that uses library compiled with gettext, + + + + + Should work with threaded program, provided that the + gettext work in threaded + environment. To support a language, the corresponding + locales need to be generated. + + + + + + + GettextStub.Preload + + + + + Native gettext library, + + + + + Forced load of all MO file before any translation, the preload is realized by trying to load + the string "" for all the textdomain defined. + + + + + + + + + OCaml program that uses library compiled with gettext, + + + + + Program that needs to translate a lot of strings, + + + + + Should work with threaded program, provided that the + gettext work in threaded + environment. To support a language, the corresponding + locales need to be generated. + + + + + + + +
+
+
+
+ Graphical user interface + + Graphical user interface works just as a program or a library. The only difference is that the file which contains + the graphical user interface should not be written in OCaml. You have two cases : + + + + You use glade file, so you should extract the strings from this file using xgettext + + + + + You use a hand written interface written in OCaml, the extraction of the strings follow the same way + as a library. + + + + + + You should use the first alternative : it is easier for a translator to extract strings, without having to compile + your application (it enables translators that don't know OCaml to help you). In order to do so, you should use the native + xgettext binary (provided with gettext). It should support the format of the translatable strings found + in your GUI file (for example, xgettext supports glade file). + + + But for now you can only use the second alternative, because OCaml is not yet supported in xgettext. This + should be fixed, once ocaml-gettext will be enough stable to become a back-end for xgettext + + + For now, extracting strings from OCaml source file and glade file, requires to patch gettext. + You can find this path in patches. This patch will be sent to upstream author once it will be considered enough stable. + + . + + + In the two cases, you just have to add your GUI file (in OCaml or native form) to POTFILES. + + + Graphical user interfaces are good candidates for settings a fixed Init.codeset. Typically, GTK2 interfaces require + to have these parameters set to UTF-8. + + + + <filename>gui.ml</filename> + &gui-ml; + + + + <command>./program --gettext-dir ../build/share/locale --gettext-lang C --my-name Sylvain</command> + + + + + + + <command>./program --gettext-dir ../build/share/locale --gettext-lang fr --my-name Sylvain</command> + + + + + + + + If you build lablgtk application, you must keep in mind that some locale settings are made + in the function GMain.Init. Those settings could override those done through the command line + options. In order to correctly use ocaml-gettext, you must be sure to call GMain.Init before + using Arg.parse with the ocaml-gettext args. + + + +
+
+ + Translating ocaml-gettext programs and libraries + + ocaml-gettext has been built around gettext. This allows translators to use exactly the same technics as they should + use with gettext. All the documentation required for translating can be found in + gettext documentation. + + + It is recommended to use GUI which allows easier translation such as : + + + + gtranslator, + + + + + kbabel. + + + + + + + Using ocaml-gettext programs + + ocaml-gettext program can be used just as any OCaml program. The only difference with standard OCaml program is that + they come with a bunch of command line options which are specific to OCaml program. In most cases, you just have to define + a well suited LC_ALL or LANG environment variable. Since ocaml-gettext is compatible + with gettext, if your environment variable works with gettext, it should also works with ocaml-gettext. + + + You can find more details in the gettext documentation or in the ocaml-gettext-options manpage. + + + + Tips and tricks + +
+ Adding gettext support without depending on ocaml-gettext + + + You want to 'gettextify' your program, adding 's_' and 'f_' + annotations throughout. However now your program has an additional + dependency, ocaml-gettext. You can make ocaml-gettext optional by + creating a set of dummy functions which do nothing: + + + + <filename>prog_gettext.ml</filename> + + (* This file is generated automatically by ./configure. *) + module Gettext = + struct + external s_ : string -> string = "%identity" + external f_ : ('a -> 'b, 'c, 'd) format -> ('a -> 'b, 'c, 'd) format = "%identity" + + let sn_ : string -> string -> int -> string = + fun s p n -> + if n = 1 then s else p + + let fn_ : ('a -> 'b, 'c, 'd) format -> ('a -> 'b, 'c, 'd) format -> int -> ('a -> 'b, 'c, 'd) format = + fun s p n -> + if n = 1 then s else p + end + + + + + You have to arrange for your configure script to place the above + content or the real ProgramGettext module into 'prog_gettext.ml', + depending on whether it detects that ocaml-gettext is installed + (eg. using 'ocamlfind query gettext' or some other method). + +
+
+ + Links + + + + OCaml website + + + + + Gettext website + + + + + Gettext documentation + + + + + Camomile website + + + + + ocaml-fileutils website + + + + + OUnit website + + + + + ocaml-benchmark website + + + + + Docbook website + + + + + + Manpages + + &ocaml-gettext-manual; + + + &ocaml-xgettext-manual; + + + &ocaml-gettext-options-manual; + + +
diff --git a/examples/Makefile b/examples/Makefile new file mode 100644 index 0000000..3273260 --- /dev/null +++ b/examples/Makefile @@ -0,0 +1,37 @@ +########################################################################## +# ocaml-gettext: a library to translate messages # +# # +# Copyright (C) 2003-2008 Sylvain Le Gall # +# # +# This library is free software; you can redistribute it and/or # +# modify it under the terms of the GNU Lesser General Public # +# License as published by the Free Software Foundation; either # +# version 2.1 of the License, or (at your option) any later version; # +# with the OCaml static compilation exception. # +# # +# This library 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 # +# Lesser General Public License for more details. # +# # +# You should have received a copy of the GNU Lesser General Public # +# License along with this library; if not, write to the Free Software # +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 # +# USA # +########################################################################## + +all: + mkdir build + cd library && $(MAKE) all + cd gui && $(MAKE) all + cd program && $(MAKE) all + cd po && $(MAKE) all + cd doc && $(MAKE) all + +clean: + cd doc && $(MAKE) clean + cd po && $(MAKE) clean + cd program && $(MAKE) clean + cd gui && $(MAKE) clean + cd library && $(MAKE) clean + $(RM) -r build diff --git a/examples/doc/Makefile b/examples/doc/Makefile new file mode 100644 index 0000000..3fbbe32 --- /dev/null +++ b/examples/doc/Makefile @@ -0,0 +1,30 @@ +########################################################################## +# ocaml-gettext: a library to translate messages # +# # +# Copyright (C) 2003-2008 Sylvain Le Gall # +# # +# This library is free software; you can redistribute it and/or # +# modify it under the terms of the GNU Lesser General Public # +# License as published by the Free Software Foundation; either # +# version 2.1 of the License, or (at your option) any later version; # +# with the OCaml static compilation exception. # +# # +# This library 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 # +# Lesser General Public License for more details. # +# # +# You should have received a copy of the GNU Lesser General Public # +# License along with this library; if not, write to the Free Software # +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 # +# USA # +########################################################################## + +all: program.1 + +clean: + $(RM) program.1 + +program.1: program.xml + xmllint --xinclude --noent --noout --postvalid $^ + xsltproc --xinclude /usr/share/xml/docbook/stylesheet/nwalsh/manpages/docbook.xsl $^ diff --git a/examples/doc/program.xml b/examples/doc/program.xml new file mode 100644 index 0000000..7509f7b --- /dev/null +++ b/examples/doc/program.xml @@ -0,0 +1,109 @@ + + + + + + +]> + + + PROGRAM + 1 + + + + program + programs to say hello. + + + + + program + + --my-name name + -help + --help + + + + + + + + + DESCRIPTION + + + This manual page documents briefly the program command. + + + + + + + + + + Your name. This could be any name, with which you should be greeted. + + + + + + + + + + + + + Display the help about the program command. + + + + + + + + + + + + SEE ALSO + + + + ocaml-gettext + 5 + + + + diff --git a/examples/gui/Makefile b/examples/gui/Makefile new file mode 100644 index 0000000..4b08b15 --- /dev/null +++ b/examples/gui/Makefile @@ -0,0 +1,29 @@ +########################################################################## +# ocaml-gettext: a library to translate messages # +# # +# Copyright (C) 2003-2008 Sylvain Le Gall # +# # +# This library is free software; you can redistribute it and/or # +# modify it under the terms of the GNU Lesser General Public # +# License as published by the Free Software Foundation; either # +# version 2.1 of the License, or (at your option) any later version; # +# with the OCaml static compilation exception. # +# # +# This library 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 # +# Lesser General Public License for more details. # +# # +# You should have received a copy of the GNU Lesser General Public # +# License along with this library; if not, write to the Free Software # +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 # +# USA # +########################################################################## + +all: gui.cma + +clean: + $(RM) *.cma *.cmi *.cmo + +gui.cma: guiGettext.ml gui.ml + ocamlfind ocamlc -package "gettext.base lablgtk2" -a -o $@ $^ diff --git a/examples/gui/gui.ml b/examples/gui/gui.ml new file mode 100644 index 0000000..58efdef --- /dev/null +++ b/examples/gui/gui.ml @@ -0,0 +1,42 @@ +(**************************************************************************) +(* ocaml-gettext: a library to translate messages *) +(* *) +(* Copyright (C) 2003-2008 Sylvain Le Gall *) +(* *) +(* This library is free software; you can redistribute it and/or *) +(* modify it under the terms of the GNU Lesser General Public *) +(* License as published by the Free Software Foundation; either *) +(* version 2.1 of the License, or (at your option) any later version; *) +(* with the OCaml static compilation exception. *) +(* *) +(* This library 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 *) +(* Lesser General Public License for more details. *) +(* *) +(* You should have received a copy of the GNU Lesser General Public *) +(* License along with this library; if not, write to the Free Software *) +(* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *) +(* USA *) +(**************************************************************************) + +open GuiGettext.Gettext;; + +(* Give access to the init of GuiGettext *) +let init = + Gettext.init +;; + +(* Build a simple window that display your name *) +let hello_you name = + let spf x = Printf.sprintf x + in + let window = GWindow.window ~title:(s_ "Hello world !") ~border_width:12 () + in + let label = GMisc.label ~text:(spf (f_ "Hello %s") name) ~packing:window#add () + in + ignore (window#event#connect#delete ~callback:(fun _ -> false)); + ignore (window#connect#destroy ~callback:(fun _ -> GMain.Main.quit ())); + window#show (); + GMain.Main.main () +;; diff --git a/examples/gui/guiGettext.ml b/examples/gui/guiGettext.ml new file mode 100644 index 0000000..4658175 --- /dev/null +++ b/examples/gui/guiGettext.ml @@ -0,0 +1,30 @@ +(**************************************************************************) +(* ocaml-gettext: a library to translate messages *) +(* *) +(* Copyright (C) 2003-2008 Sylvain Le Gall *) +(* *) +(* This library is free software; you can redistribute it and/or *) +(* modify it under the terms of the GNU Lesser General Public *) +(* License as published by the Free Software Foundation; either *) +(* version 2.1 of the License, or (at your option) any later version; *) +(* with the OCaml static compilation exception. *) +(* *) +(* This library 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 *) +(* Lesser General Public License for more details. *) +(* *) +(* You should have received a copy of the GNU Lesser General Public *) +(* License along with this library; if not, write to the Free Software *) +(* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *) +(* USA *) +(**************************************************************************) + +(* Create the module Gettext, using the textdomain "mydomain" *) +module Gettext = Gettext.Library(struct + let textdomain = "mydomain" + let codeset = Some "UTF-8" + let dir = None + let dependencies = Gettext.init +end) +;; diff --git a/examples/library/Makefile b/examples/library/Makefile new file mode 100644 index 0000000..26a1900 --- /dev/null +++ b/examples/library/Makefile @@ -0,0 +1,30 @@ +########################################################################## +# ocaml-gettext: a library to translate messages # +# # +# Copyright (C) 2003-2008 Sylvain Le Gall # +# # +# This library is free software; you can redistribute it and/or # +# modify it under the terms of the GNU Lesser General Public # +# License as published by the Free Software Foundation; either # +# version 2.1 of the License, or (at your option) any later version; # +# with the OCaml static compilation exception. # +# # +# This library 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 # +# Lesser General Public License for more details. # +# # +# You should have received a copy of the GNU Lesser General Public # +# License along with this library; if not, write to the Free Software # +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 # +# USA # +########################################################################## + +all: library.cma + +clean: + $(RM) *.cma *.cmi *.cmo + +library.cma: libraryGettext.ml library.ml + ocamlfind ocamlc -package gettext.base -a -o $@ $^ + diff --git a/examples/library/library.ml b/examples/library/library.ml new file mode 100644 index 0000000..c645e7e --- /dev/null +++ b/examples/library/library.ml @@ -0,0 +1,51 @@ +(**************************************************************************) +(* ocaml-gettext: a library to translate messages *) +(* *) +(* Copyright (C) 2003-2008 Sylvain Le Gall *) +(* *) +(* This library is free software; you can redistribute it and/or *) +(* modify it under the terms of the GNU Lesser General Public *) +(* License as published by the Free Software Foundation; either *) +(* version 2.1 of the License, or (at your option) any later version; *) +(* with the OCaml static compilation exception. *) +(* *) +(* This library 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 *) +(* Lesser General Public License for more details. *) +(* *) +(* You should have received a copy of the GNU Lesser General Public *) +(* License along with this library; if not, write to the Free Software *) +(* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *) +(* USA *) +(**************************************************************************) + +open LibraryGettext.Gettext;; + +(* Give access to the init of LibraryGettext *) +let init = + Gettext.init +;; + +(* Example function *) +let library_only_function () = + + (* Two simple examples : singular translation *) + print_endline (s_ "Hello world !"); + Printf.printf (f_ "Hello %s !\n") "world"; + + (* More complicated : plural translation, using strings *) + print_endline ( + (sn_ "There is " "There are " 2) + ^(string_of_int 2) + ^(sn_ "plate." "plates." 2) + ); + + (* More simple forms of plural translation, using printf *) + Printf.printf (fn_ "There is %d plate.\n" "There are %d plates.\n" 2) 2 +;; + +(* Another example function : used by program.ml *) +let hello_you name = + Printf.printf (f_ "Hello %s\n") name +;; diff --git a/examples/library/libraryGettext.ml b/examples/library/libraryGettext.ml new file mode 100644 index 0000000..18e86be --- /dev/null +++ b/examples/library/libraryGettext.ml @@ -0,0 +1,30 @@ +(**************************************************************************) +(* ocaml-gettext: a library to translate messages *) +(* *) +(* Copyright (C) 2003-2008 Sylvain Le Gall *) +(* *) +(* This library is free software; you can redistribute it and/or *) +(* modify it under the terms of the GNU Lesser General Public *) +(* License as published by the Free Software Foundation; either *) +(* version 2.1 of the License, or (at your option) any later version; *) +(* with the OCaml static compilation exception. *) +(* *) +(* This library 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 *) +(* Lesser General Public License for more details. *) +(* *) +(* You should have received a copy of the GNU Lesser General Public *) +(* License along with this library; if not, write to the Free Software *) +(* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *) +(* USA *) +(**************************************************************************) + +(* Create the module Gettext, using the textdomain "mydomain" *) +module Gettext = Gettext.Library(struct + let textdomain = "mydomain" + let codeset = None + let dir = None + let dependencies = Gettext.init +end) +;; diff --git a/examples/po/LINGUAS b/examples/po/LINGUAS new file mode 100644 index 0000000..527e861 --- /dev/null +++ b/examples/po/LINGUAS @@ -0,0 +1 @@ +fr diff --git a/examples/po/Makefile b/examples/po/Makefile new file mode 100644 index 0000000..e70ff1b --- /dev/null +++ b/examples/po/Makefile @@ -0,0 +1,83 @@ +########################################################################## +# ocaml-gettext: a library to translate messages # +# # +# Copyright (C) 2003-2008 Sylvain Le Gall # +# # +# This library is free software; you can redistribute it and/or # +# modify it under the terms of the GNU Lesser General Public # +# License as published by the Free Software Foundation; either # +# version 2.1 of the License, or (at your option) any later version; # +# with the OCaml static compilation exception. # +# # +# This library 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 # +# Lesser General Public License for more details. # +# # +# You should have received a copy of the GNU Lesser General Public # +# License along with this library; if not, write to the Free Software # +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 # +# USA # +########################################################################## + +OCAML_GETTEXT_PACKAGE = mydomain +LINGUAS=$(shell cat LINGUAS) +SOURCES=POTFILES + +OCAML_GETTEXT=ocaml-gettext +OCAML_GETTEXT_EXTRACT_OPTIONS= +OCAML_GETTEXT_COMPILE_OPTIONS= +OCAML_GETTEXT_INSTALL_OPTIONS= +OCAML_GETTEXT_MERGE_OPTIONS= + +BUILDPO=../build/share/locale/ + +POFILES=$(addsuffix .po,$(LINGUAS)) +MOFILES=$(addsuffix .mo,$(LINGUAS)) +POTFILE=$(OCAML_GETTEXT_PACKAGE).pot + +all: install-buildpo + +install: install-po + +uninstall: uninstall-po + +clean:: clean-po + +%.mo: %.po + $(OCAML_GETTEXT) --action compile $(OCAML_GETTEXT_COMPILE_OPTIONS) \ + --compile-output $@ $^ + +%.pot: $(SOURCES) + $(OCAML_GETTEXT) --action extract $(OCAML_GETTEXT_EXTRACT_OPTIONS) \ + --extract-pot $@ $^ + +%.po: $(POTFILE) + $(OCAML_GETTEXT) --action merge $(OCAML_GETTEXT_MERGE_OPTIONS) \ + --merge-pot $(POTFILE) $@ + +$(BUILDPO): + mkdir -p $(BUILDPO) + +.PRECIOUS: $(POTFILE) + +install-buildpo: $(MOFILES) $(BUILDPO) + $(OCAML_GETTEXT) --action install $(OCAML_GETTEXT_INSTALL_OPTIONS) \ + --install-textdomain $(OCAML_GETTEXT_PACKAGE) \ + --install-destdir $(BUILDPO) $(MOFILES) + +install-po: $(MOFILES) + $(OCAML_GETTEXT) --action install $(OCAML_GETTEXT_INSTALL_OPTIONS) \ + --install-textdomain $(OCAML_GETTEXT_PACKAGE) \ + --install-destdir $(PODIR) $(MOFILES) + +uninstall-po: + $(OCAML_GETTEXT) --action uninstall $(OCAML_GETTEXT_INSTALL_OPTIONS) \ + --uninstall-textdomain $(OCAML_GETTEXT_PACKAGE) \ + --uninstall-orgdir $(PODIR) $(MOFILES) + +clean-po: + -$(OCAML_GETTEXT) --action uninstall $(OCAML_GETTEXT_INSTALL_OPTIONS) \ + --uninstall-textdomain $(OCAML_GETTEXT_PACKAGE) \ + --uninstall-orgdir $(BUILDPO) $(MOFILES) + -$(RM) $(MOFILES) diff --git a/examples/po/POTFILES b/examples/po/POTFILES new file mode 100644 index 0000000..9d777a1 --- /dev/null +++ b/examples/po/POTFILES @@ -0,0 +1,3 @@ +../library/library.ml +../gui/gui.ml +../program/program.ml diff --git a/examples/po/fr.po b/examples/po/fr.po new file mode 100644 index 0000000..4e779d6 --- /dev/null +++ b/examples/po/fr.po @@ -0,0 +1,59 @@ +# EXAMPLE MYDOMAIN PROGRAM. +# Copyright (C) 2005-2008 Sylvain Le Gall +# This file is distributed under the same license as the ocaml-gettext package. +# , fuzzy +msgid "" +msgstr "" +"Project-Id-Version: ocaml-gettext 0.2\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2005-04-05 23:20+0000\n" +"PO-Revision-Date: 2005-04-06 01:29+0200\n" +"Last-Translator: Sylvain Le Gall \n" +"Language-Team: French \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n>1;" + +#: ../gui/gui.ml:15 +msgid "Hello %s" +msgstr "Bonjour %s" + +#: ../library/library.ml:14 +msgid "Hello %s !\\n" +msgstr "Bonjour %s !\\n" + +#: ../library/library.ml:29 +msgid "Hello %s\\n" +msgstr "Bonjour %s\\n" + +#: ../library/library.ml:13 ../gui/gui.ml:13 +msgid "Hello world !" +msgstr "Bonjour le monde !" + +#: ../library/library.ml:18 +msgid "There is " +msgid_plural "There are " +msgstr[0] "Il y a " +msgstr[1] "Il y a" + +#: ../library/library.ml:24 +msgid "There is %d plate.\\n" +msgid_plural "There are %d plates.\\n" +msgstr[0] "Il y a %d assiettes.\\n" +msgstr[1] "Il y a %d assiettes.\\n" + +#: ../program/program.ml:35 +msgid "\\\"Hello you\\\" program by Sylvain Le Gall\n\n%s\n\nCommand: program [options]\n\nOptions:" +msgstr "\\\"Bonjour à toi\\\" application par Sylvain Le Gall\n\n%s\n\nCommande: program [options]\n\nOptions :" + +#: ../program/program.ml:19 +msgid "name Your name. Default : %S" +msgstr "nom Votre nom. Par défaut : %S" + +#: ../library/library.ml:20 +msgid "plate." +msgid_plural "plates." +msgstr[0] "assiette." +msgstr[1] "assiettes." + diff --git a/examples/po/mydomain.pot b/examples/po/mydomain.pot new file mode 100644 index 0000000..3378d3f --- /dev/null +++ b/examples/po/mydomain.pot @@ -0,0 +1,61 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2005-04-05 23:31+0000\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=CHARSET\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n" + +#: ../gui/gui.ml:15 +msgid "Hello %s" +msgstr "" + +#: ../library/library.ml:14 +msgid "Hello %s !\\n" +msgstr "" + +#: ../library/library.ml:29 +msgid "Hello %s\\n" +msgstr "" + +#: ../library/library.ml:13 ../gui/gui.ml:13 +msgid "Hello world !" +msgstr "" + +#: ../library/library.ml:18 +msgid "There is " +msgid_plural "There are " +msgstr[0] "" +msgstr[1] "" + +#: ../library/library.ml:24 +msgid "There is %d plate.\\n" +msgid_plural "There are %d plates.\\n" +msgstr[0] "" +msgstr[1] "" + +#: ../program/program.ml:35 +msgid "\\\"Hello you\\\" program by Sylvain Le Gall\n\n%s\n\nCommand: program [options]\n\nOptions:" +msgstr "" + +#: ../program/program.ml:19 +msgid "name Your name. Default : %S" +msgstr "" + +#: ../library/library.ml:20 +msgid "plate." +msgid_plural "plates." +msgstr[0] "" +msgstr[1] "" + diff --git a/examples/program/Makefile b/examples/program/Makefile new file mode 100644 index 0000000..6f51449 --- /dev/null +++ b/examples/program/Makefile @@ -0,0 +1,31 @@ +########################################################################## +# ocaml-gettext: a library to translate messages # +# # +# Copyright (C) 2003-2008 Sylvain Le Gall # +# # +# This library is free software; you can redistribute it and/or # +# modify it under the terms of the GNU Lesser General Public # +# License as published by the Free Software Foundation; either # +# version 2.1 of the License, or (at your option) any later version; # +# with the OCaml static compilation exception. # +# # +# This library 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 # +# Lesser General Public License for more details. # +# # +# You should have received a copy of the GNU Lesser General Public # +# License along with this library; if not, write to the Free Software # +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 # +# USA # +########################################################################## + +all: program + +clean: + $(RM) *.cmi *.cmo program + +program: programGettext.ml program.ml + ocamlfind ocamlc -package "gettext-camomile lablgtk2.init" -linkpkg \ + -I ../library -I ../gui -o $@ library.cma gui.cma $^ + diff --git a/examples/program/program.ml b/examples/program/program.ml new file mode 100644 index 0000000..24f117d --- /dev/null +++ b/examples/program/program.ml @@ -0,0 +1,61 @@ +(**************************************************************************) +(* ocaml-gettext: a library to translate messages *) +(* *) +(* Copyright (C) 2003-2008 Sylvain Le Gall *) +(* *) +(* This library is free software; you can redistribute it and/or *) +(* modify it under the terms of the GNU Lesser General Public *) +(* License as published by the Free Software Foundation; either *) +(* version 2.1 of the License, or (at your option) any later version; *) +(* with the OCaml static compilation exception. *) +(* *) +(* This library 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 *) +(* Lesser General Public License for more details. *) +(* *) +(* You should have received a copy of the GNU Lesser General Public *) +(* License along with this library; if not, write to the Free Software *) +(* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *) +(* USA *) +(**************************************************************************) + +open ProgramGettext.Gettext;; + +let () = + let my_name = ref "" + in + let spf x = Printf.sprintf x + in + let (gettext_args,gettext_copyright) = + ProgramGettext.Gettext.init + in + let args = + Arg.align ( + [ + "--my-name", + Arg.String ( fun s -> + my_name := s + ), + ( spf (f_ "name Your name. Default : %S") !my_name ) + ] @ gettext_args + ) + in + let () = + Arg.parse + args + ( fun str -> () ) + ( + spf (f_ +"\"Hello you\" program by Sylvain Le Gall + +%s + +Command: program [options] + +Options:") gettext_copyright + ) + in + Library.hello_you !my_name; + Gui.hello_you !my_name +;; diff --git a/examples/program/programGettext.ml b/examples/program/programGettext.ml new file mode 100644 index 0000000..79bdc0b --- /dev/null +++ b/examples/program/programGettext.ml @@ -0,0 +1,36 @@ +(**************************************************************************) +(* ocaml-gettext: a library to translate messages *) +(* *) +(* Copyright (C) 2003-2008 Sylvain Le Gall *) +(* *) +(* This library is free software; you can redistribute it and/or *) +(* modify it under the terms of the GNU Lesser General Public *) +(* License as published by the Free Software Foundation; either *) +(* version 2.1 of the License, or (at your option) any later version; *) +(* with the OCaml static compilation exception. *) +(* *) +(* This library 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 *) +(* Lesser General Public License for more details. *) +(* *) +(* You should have received a copy of the GNU Lesser General Public *) +(* License along with this library; if not, write to the Free Software *) +(* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *) +(* USA *) +(**************************************************************************) + +(* Create the module Gettext, using the textdomain "mydomain" *) +module Gettext = Gettext.Program +( + struct + let textdomain = "mydomain" + let codeset = None + let dir = None + let dependencies = Library.init @ Gui.init + end +) +(* I do prefer fully ocaml implementation, so choose the + GettextCamomile module *) +(GettextCamomile.Map) +;; diff --git a/install-sh b/install-sh new file mode 100755 index 0000000..4d4a951 --- /dev/null +++ b/install-sh @@ -0,0 +1,323 @@ +#!/bin/sh +# install - install a program, script, or datafile + +scriptversion=2005-05-14.22 + +# This originates from X11R5 (mit/util/scripts/install.sh), which was +# later released in X11R6 (xc/config/util/install.sh) with the +# following copyright and license. +# +# Copyright (C) 1994 X Consortium +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +# AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC- +# TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# +# Except as contained in this notice, the name of the X Consortium shall not +# be used in advertising or otherwise to promote the sale, use or other deal- +# ings in this Software without prior written authorization from the X Consor- +# tium. +# +# +# FSF changes to this file are in the public domain. +# +# Calling this script install-sh is preferred over install.sh, to prevent +# `make' implicit rules from creating a file called install from it +# when there is no Makefile. +# +# This script is compatible with the BSD install script, but was written +# from scratch. It can only install one file at a time, a restriction +# shared with many OS's install programs. + +# set DOITPROG to echo to test this script + +# Don't use :- since 4.3BSD and earlier shells don't like it. +doit="${DOITPROG-}" + +# put in absolute paths if you don't have them in your path; or use env. vars. + +mvprog="${MVPROG-mv}" +cpprog="${CPPROG-cp}" +chmodprog="${CHMODPROG-chmod}" +chownprog="${CHOWNPROG-chown}" +chgrpprog="${CHGRPPROG-chgrp}" +stripprog="${STRIPPROG-strip}" +rmprog="${RMPROG-rm}" +mkdirprog="${MKDIRPROG-mkdir}" + +chmodcmd="$chmodprog 0755" +chowncmd= +chgrpcmd= +stripcmd= +rmcmd="$rmprog -f" +mvcmd="$mvprog" +src= +dst= +dir_arg= +dstarg= +no_target_directory= + +usage="Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE + or: $0 [OPTION]... SRCFILES... DIRECTORY + or: $0 [OPTION]... -t DIRECTORY SRCFILES... + or: $0 [OPTION]... -d DIRECTORIES... + +In the 1st form, copy SRCFILE to DSTFILE. +In the 2nd and 3rd, copy all SRCFILES to DIRECTORY. +In the 4th, create DIRECTORIES. + +Options: +-c (ignored) +-d create directories instead of installing files. +-g GROUP $chgrpprog installed files to GROUP. +-m MODE $chmodprog installed files to MODE. +-o USER $chownprog installed files to USER. +-s $stripprog installed files. +-t DIRECTORY install into DIRECTORY. +-T report an error if DSTFILE is a directory. +--help display this help and exit. +--version display version info and exit. + +Environment variables override the default commands: + CHGRPPROG CHMODPROG CHOWNPROG CPPROG MKDIRPROG MVPROG RMPROG STRIPPROG +" + +while test -n "$1"; do + case $1 in + -c) shift + continue;; + + -d) dir_arg=true + shift + continue;; + + -g) chgrpcmd="$chgrpprog $2" + shift + shift + continue;; + + --help) echo "$usage"; exit $?;; + + -m) chmodcmd="$chmodprog $2" + shift + shift + continue;; + + -o) chowncmd="$chownprog $2" + shift + shift + continue;; + + -s) stripcmd=$stripprog + shift + continue;; + + -t) dstarg=$2 + shift + shift + continue;; + + -T) no_target_directory=true + shift + continue;; + + --version) echo "$0 $scriptversion"; exit $?;; + + *) # When -d is used, all remaining arguments are directories to create. + # When -t is used, the destination is already specified. + test -n "$dir_arg$dstarg" && break + # Otherwise, the last argument is the destination. Remove it from $@. + for arg + do + if test -n "$dstarg"; then + # $@ is not empty: it contains at least $arg. + set fnord "$@" "$dstarg" + shift # fnord + fi + shift # arg + dstarg=$arg + done + break;; + esac +done + +if test -z "$1"; then + if test -z "$dir_arg"; then + echo "$0: no input file specified." >&2 + exit 1 + fi + # It's OK to call `install-sh -d' without argument. + # This can happen when creating conditional directories. + exit 0 +fi + +for src +do + # Protect names starting with `-'. + case $src in + -*) src=./$src ;; + esac + + if test -n "$dir_arg"; then + dst=$src + src= + + if test -d "$dst"; then + mkdircmd=: + chmodcmd= + else + mkdircmd=$mkdirprog + fi + else + # Waiting for this to be detected by the "$cpprog $src $dsttmp" command + # might cause directories to be created, which would be especially bad + # if $src (and thus $dsttmp) contains '*'. + if test ! -f "$src" && test ! -d "$src"; then + echo "$0: $src does not exist." >&2 + exit 1 + fi + + if test -z "$dstarg"; then + echo "$0: no destination specified." >&2 + exit 1 + fi + + dst=$dstarg + # Protect names starting with `-'. + case $dst in + -*) dst=./$dst ;; + esac + + # If destination is a directory, append the input filename; won't work + # if double slashes aren't ignored. + if test -d "$dst"; then + if test -n "$no_target_directory"; then + echo "$0: $dstarg: Is a directory" >&2 + exit 1 + fi + dst=$dst/`basename "$src"` + fi + fi + + # This sed command emulates the dirname command. + dstdir=`echo "$dst" | sed -e 's,/*$,,;s,[^/]*$,,;s,/*$,,;s,^$,.,'` + + # Make sure that the destination directory exists. + + # Skip lots of stat calls in the usual case. + if test ! -d "$dstdir"; then + defaultIFS=' + ' + IFS="${IFS-$defaultIFS}" + + oIFS=$IFS + # Some sh's can't handle IFS=/ for some reason. + IFS='%' + set x `echo "$dstdir" | sed -e 's@/@%@g' -e 's@^%@/@'` + shift + IFS=$oIFS + + pathcomp= + + while test $# -ne 0 ; do + pathcomp=$pathcomp$1 + shift + if test ! -d "$pathcomp"; then + $mkdirprog "$pathcomp" + # mkdir can fail with a `File exist' error in case several + # install-sh are creating the directory concurrently. This + # is OK. + test -d "$pathcomp" || exit + fi + pathcomp=$pathcomp/ + done + fi + + if test -n "$dir_arg"; then + $doit $mkdircmd "$dst" \ + && { test -z "$chowncmd" || $doit $chowncmd "$dst"; } \ + && { test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } \ + && { test -z "$stripcmd" || $doit $stripcmd "$dst"; } \ + && { test -z "$chmodcmd" || $doit $chmodcmd "$dst"; } + + else + dstfile=`basename "$dst"` + + # Make a couple of temp file names in the proper directory. + dsttmp=$dstdir/_inst.$$_ + rmtmp=$dstdir/_rm.$$_ + + # Trap to clean up those temp files at exit. + trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0 + trap '(exit $?); exit' 1 2 13 15 + + # Copy the file name to the temp name. + $doit $cpprog "$src" "$dsttmp" && + + # and set any options; do chmod last to preserve setuid bits. + # + # If any of these fail, we abort the whole thing. If we want to + # ignore errors from any of these, just make sure not to ignore + # errors from the above "$doit $cpprog $src $dsttmp" command. + # + { test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } \ + && { test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } \ + && { test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } \ + && { test -z "$chmodcmd" || $doit $chmodcmd "$dsttmp"; } && + + # Now rename the file to the real destination. + { $doit $mvcmd -f "$dsttmp" "$dstdir/$dstfile" 2>/dev/null \ + || { + # The rename failed, perhaps because mv can't rename something else + # to itself, or perhaps because mv is so ancient that it does not + # support -f. + + # Now remove or move aside any old file at destination location. + # We try this two ways since rm can't unlink itself on some + # systems and the destination file might be busy for other + # reasons. In this case, the final cleanup might fail but the new + # file should still install successfully. + { + if test -f "$dstdir/$dstfile"; then + $doit $rmcmd -f "$dstdir/$dstfile" 2>/dev/null \ + || $doit $mvcmd -f "$dstdir/$dstfile" "$rmtmp" 2>/dev/null \ + || { + echo "$0: cannot unlink or rename $dstdir/$dstfile" >&2 + (exit 1); exit 1 + } + else + : + fi + } && + + # Now rename the file to the real destination. + $doit $mvcmd "$dsttmp" "$dstdir/$dstfile" + } + } + fi || { (exit 1); exit 1; } +done + +# The final little trick to "correctly" pass the exit status to the exit trap. +{ + (exit 0); exit 0 +} + +# Local variables: +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "scriptversion=" +# time-stamp-format: "%:y-%02m-%02d.%02H" +# time-stamp-end: "$" +# End: diff --git a/libgettext-camomile-ocaml/META b/libgettext-camomile-ocaml/META new file mode 100644 index 0000000..a9de8a9 --- /dev/null +++ b/libgettext-camomile-ocaml/META @@ -0,0 +1,27 @@ +########################################################################## +# ocaml-gettext: a library to translate messages # +# # +# Copyright (C) 2003-2008 Sylvain Le Gall # +# # +# This library is free software; you can redistribute it and/or # +# modify it under the terms of the GNU Lesser General Public # +# License as published by the Free Software Foundation; either # +# version 2.1 of the License, or (at your option) any later version; # +# with the OCaml static compilation exception. # +# # +# This library 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 # +# Lesser General Public License for more details. # +# # +# You should have received a copy of the GNU Lesser General Public # +# License along with this library; if not, write to the Free Software # +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 # +# USA # +########################################################################## + +version = "@PACKAGE_VERSION@" +predicates = "" +requires = "gettext.extension camomile" +archive(byte) = "gettextCamomile.cma" +archive(native) = "gettextCamomile.cmxa" diff --git a/libgettext-camomile-ocaml/Makefile b/libgettext-camomile-ocaml/Makefile new file mode 100644 index 0000000..7f4c71f --- /dev/null +++ b/libgettext-camomile-ocaml/Makefile @@ -0,0 +1,60 @@ +########################################################################## +# ocaml-gettext: a library to translate messages # +# # +# Copyright (C) 2003-2008 Sylvain Le Gall # +# # +# This library is free software; you can redistribute it and/or # +# modify it under the terms of the GNU Lesser General Public # +# License as published by the Free Software Foundation; either # +# version 2.1 of the License, or (at your option) any later version; # +# with the OCaml static compilation exception. # +# # +# This library 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 # +# Lesser General Public License for more details. # +# # +# You should have received a copy of the GNU Lesser General Public # +# License along with this library; if not, write to the Free Software # +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 # +# USA # +########################################################################## + +include ../ConfMakefile + +ifeq ($(BUILD_CAMOMILE),yes) + +NAME = gettext-camomile +CMO = +LIBRARY = true +REQUIRES = gettext.extension camomile +PREDICATES = +INSTALLIB = META + +all: + +########################## +# Ocaml Gettext Camomile # +########################## + +REQUIRES += camomile + +CMO_CAMOMILE= \ + gettextCamomile.cmo + +INSTALLIB += \ + gettextCamomile.cma \ + gettextCamomile.cmxa \ + gettextCamomile.a \ + gettextCamomile.cmi + +INSTALLIB += $(CMO_CAMOMILE:.cmo=.cmx) + +gettextCamomile.cmo: gettextCamomile.cmi +gettextCamomile.cmx: gettextCamomile.cmi +gettextCamomile.cma: $(CMO_CAMOMILE) +gettextCamomile.cmxa: $(CMO_CAMOMILE:.cmo=.cmx) + +endif + +include ../TopMakefile diff --git a/libgettext-camomile-ocaml/gettextCamomile.ml b/libgettext-camomile-ocaml/gettextCamomile.ml new file mode 100644 index 0000000..949c4e3 --- /dev/null +++ b/libgettext-camomile-ocaml/gettextCamomile.ml @@ -0,0 +1,111 @@ +(**************************************************************************) +(* ocaml-gettext: a library to translate messages *) +(* *) +(* Copyright (C) 2003-2008 Sylvain Le Gall *) +(* *) +(* This library is free software; you can redistribute it and/or *) +(* modify it under the terms of the GNU Lesser General Public *) +(* License as published by the Free Software Foundation; either *) +(* version 2.1 of the License, or (at your option) any later version; *) +(* with the OCaml static compilation exception. *) +(* *) +(* This library 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 *) +(* Lesser General Public License for more details. *) +(* *) +(* You should have received a copy of the GNU Lesser General Public *) +(* License along with this library; if not, write to the Free Software *) +(* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *) +(* USA *) +(**************************************************************************) + + +open CamomileLibraryDefault.Camomile;; +open GettextTypes;; + +(** Error reported when something goes wrong during Camomile initialization. + *) +exception GettextCamomileCreate of string * exn +;; + +(** Error reported when something goes wrong during Camomile operation. + *) +exception GettextCamomileRecode of string * exn +;; + +(** Charset module, that is derived directly from the camomile library. *) +module Charset : GettextCharset.CHARSET_TYPE = + struct + (**/**) + type encoding = string + type u = { + failsafe : failsafe; + in_enc : CharEncoding.t; + out_enc : CharEncoding.t; + } + + let create t in_enc out_enc = + try + { + failsafe = t.GettextTypes.failsafe; + in_enc = CharEncoding.of_name in_enc; + out_enc = CharEncoding.of_name out_enc; + } + with e -> + GettextUtils.fail_or_continue + t.GettextTypes.failsafe + (GettextCamomileCreate( + Printf.sprintf + "Cannot create conversion from %s to %s" + in_enc + out_enc, + e)) + { + failsafe = t.GettextTypes.failsafe; + in_enc = CharEncoding.of_name "ISO-8859-1"; + out_enc = CharEncoding.of_name "ISO-8859-1"; + } + + let recode u str = + try + CharEncoding.recode_string u.in_enc u.out_enc str + with e -> + GettextUtils.fail_or_continue + u.failsafe + (GettextCamomileCreate( + Printf.sprintf + "Cannot create convert string %s from %s to %s" + str + (CharEncoding.name_of u.in_enc) + (CharEncoding.name_of u.out_enc), + e)) + str + end +;; + +(** Implementation based on a Map storage for string. *) +module Map : GettextTypes.REALIZE_TYPE = + GettextRealize.Generic + (GettextTranslate.Map) (* Map translation *) + (Charset) (* Camomile charset *) + (GettextLocale.Posix) (* POSIX locale *) +;; + +(** Implementation based on a Hashtbl storage for string. *) +module Hashtbl : GettextTypes.REALIZE_TYPE = + GettextRealize.Generic + (GettextTranslate.Hashtbl) (* Hashtbl translation *) + (Charset) (* Camomile charset *) + (GettextLocale.Posix) (* POSIX locale *) +;; + +(** Low memory and fast initialization implementation, files are opened only when needed. + *) +module Open : GettextTypes.REALIZE_TYPE = + GettextRealize.Generic + (GettextTranslate.Open) (* Open translation *) + (Charset) (* Camomile charset *) + (GettextLocale.Posix) (* POSIX locale *) +;; + diff --git a/libgettext-camomile-ocaml/gettextCamomile.mli b/libgettext-camomile-ocaml/gettextCamomile.mli new file mode 100644 index 0000000..25220a3 --- /dev/null +++ b/libgettext-camomile-ocaml/gettextCamomile.mli @@ -0,0 +1,6 @@ +exception GettextCamomileCreate of string * exn +exception GettextCamomileRecode of string * exn +module Charset : GettextCharset.CHARSET_TYPE +module Map : GettextTypes.REALIZE_TYPE +module Hashtbl : GettextTypes.REALIZE_TYPE +module Open : GettextTypes.REALIZE_TYPE diff --git a/libgettext-ocaml/META.in b/libgettext-ocaml/META.in new file mode 100644 index 0000000..7822aa8 --- /dev/null +++ b/libgettext-ocaml/META.in @@ -0,0 +1,44 @@ +########################################################################## +# ocaml-gettext: a library to translate messages # +# # +# Copyright (C) 2003-2008 Sylvain Le Gall # +# # +# This library is free software; you can redistribute it and/or # +# modify it under the terms of the GNU Lesser General Public # +# License as published by the Free Software Foundation; either # +# version 2.1 of the License, or (at your option) any later version; # +# with the OCaml static compilation exception. # +# # +# This library 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 # +# Lesser General Public License for more details. # +# # +# You should have received a copy of the GNU Lesser General Public # +# License along with this library; if not, write to the Free Software # +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 # +# USA # +########################################################################## + +version = "@PACKAGE_VERSION@" +predicates = "" + +package "base" ( + version = "@PACKAGE_VERSION@" + requires = "fileutils" + archive(byte) = "gettextBase.cma" + archive(native) = "gettextBase.cmxa" +) + +package "extension" ( + version = "@PACKAGE_VERSION@" + requires = "gettext.base" + archive(byte) = "gettextExtension.cma" + archive(native) = "gettextExtension.cmxa" +) + +package "extract" ( + version = "@PACKAGE_VERSION@" + requires = "gettext.extension" + archive(byte) = "pr_gettext.cmo" +) diff --git a/libgettext-ocaml/Makefile b/libgettext-ocaml/Makefile new file mode 100644 index 0000000..1a155c1 --- /dev/null +++ b/libgettext-ocaml/Makefile @@ -0,0 +1,182 @@ +########################################################################## +# ocaml-gettext: a library to translate messages # +# # +# Copyright (C) 2003-2008 Sylvain Le Gall # +# # +# This library is free software; you can redistribute it and/or # +# modify it under the terms of the GNU Lesser General Public # +# License as published by the Free Software Foundation; either # +# version 2.1 of the License, or (at your option) any later version; # +# with the OCaml static compilation exception. # +# # +# This library 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 # +# Lesser General Public License for more details. # +# # +# You should have received a copy of the GNU Lesser General Public # +# License along with this library; if not, write to the Free Software # +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 # +# USA # +########################################################################## + +NAME = gettext +CMO = +LIBRARY = true +REQUIRES = fileutils +PREDICATES = +INSTALLIB = META + +all: + +distclean:: + -$(RM) gettextConfig.ml + -$(RM) META + +############################## +# Ocaml Gettext base library # +############################## + +CMO_BASE = \ + gettextConfig.cmo \ + gettextCategory.cmo \ + gettextTypes.cmo \ + gettextUtils.cmo \ + gettextModules.cmo \ + gettextCompat.cmo \ + gettext.cmo \ + gettextFormat_parser.cmo \ + gettextFormat_lexer.cmo \ + gettextFormat.cmo \ + gettextMo_int32.cmo \ + gettextMo_parser.cmo \ + gettextMo_lexer.cmo \ + gettextMo.cmo \ + gettextDummy.cmo + +INSTALLIB += \ + gettextBase.cma \ + gettextBase.cmxa \ + gettextBase.a \ + gettextTypes.cmi \ + gettextDummy.cmi \ + gettext.cmi + +INSTALLIB += $(CMO_BASE:.cmo=.cmx) + +gettextCompat.cmo: gettextCompat.cmi +gettextCompat.cmx: gettextCompat.cmi +gettext.cmo: gettext.cmi +gettext.cmx: gettext.cmi +gettextMo_parser.cmo: gettextMo_parser.cmi +gettextMo_parser.cmx: gettextMo_parser.cmi +gettextFormat_parser.cmo: gettextFormat_parser.cmi +gettextFormat_parser.cmx: gettextFormat_parser.cmi +gettextBase.cma: $(CMO_BASE) +gettextBase.cmxa: $(CMO_BASE:.cmo=.cmx) + +clean:: + -$(RM) gettextMo_parser.output + -$(RM) gettextMo_parser.ml + -$(RM) gettextMo_parser.mli + -$(RM) gettextDummy.mli + -$(RM) gettextTypes.mli + +################################### +# Ocaml Gettext extension library # +################################### + +CMO_EXTENSION= \ + gettextLocale_types.cmo \ + gettextLocale_parser.cmo \ + gettextLocale_lexer.cmo \ + gettextLocale.cmo \ + gettextDomain.cmo \ + gettextCharset.cmo \ + gettextTranslate.cmo \ + gettextRealize.cmo \ + gettextPo_utils.cmo \ + gettextPoComment_parser.cmo \ + gettextPo_lexer.cmo \ + gettextPo_parser.cmo \ + gettextPo.cmo \ + gettextCompile.cmo + +INSTALLIB += \ + gettextExtension.cma \ + gettextExtension.cmxa \ + gettextExtension.a \ + gettextConfig.cmi \ + gettextModules.cmi \ + gettextFormat.cmi \ + gettextMo.cmi \ + gettextCategory.cmi \ + gettextLocale.cmi \ + gettextDomain.cmi \ + gettextCharset.cmi \ + gettextTranslate.cmi \ + gettextRealize.cmi \ + gettextPo.cmi \ + gettextUtils.cmi \ + gettextCompat.cmi \ + gettextCompile.cmi + +INSTALLIB += $(CMO_EXTENSION:.cmo=.cmx) + +gettextPo_lexer.cmo: gettextPo_parser.cmi gettextPoComment_parser.cmi +gettextPo_lexer.cmx: gettextPo_parser.cmi gettextPoComment_parser.cmi +gettextPoComment_parser.cmo: gettextPoComment_parser.cmi +gettextPoComment_parser.cmx: gettextPoComment_parser.cmi +gettextPo_parser.cmo: gettextPo_parser.cmi gettextPo_lexer.cmi +gettextPo_parser.cmx: gettextPo_parser.cmi gettextPo_lexer.cmi +gettextLocale_parser.cmo: gettextLocale_parser.cmi +gettextLocale_parser.cmx: gettextLocale_parser.cmi +gettextExtension.cma: $(CMO_EXTENSION) +gettextExtension.cmxa: $(CMO_EXTENSION:.cmo=.cmx) + +clean:: + -$(RM) gettextPoComment_parser.output + -$(RM) gettextPoComment_parser.ml + -$(RM) gettextPoComment_parser.mli + -$(RM) gettextPo_parser.output + -$(RM) gettextPo_parser.ml + -$(RM) gettextPo_parser.mli + -$(RM) gettextFormat_parser.output + -$(RM) gettextFormat_parser.ml + -$(RM) gettextFormat_parser.mli + -$(RM) gettextLocale_parser.output + -$(RM) gettextLocale_parser.ml + -$(RM) gettextLocale_parser.mli + -$(RM) gettextLocale.mli + -$(RM) gettextFormat.mli + -$(RM) gettextCharset.mli + -$(RM) gettextCategory.mli + -$(RM) gettextRealize.mli + -$(RM) gettextTranslate.mli + -$(RM) gettextConfig.mli + -$(RM) gettextDomain.mli + -$(RM) gettextModules.mli + -$(RM) gettextUtils.mli + -$(RM) gettextPo.mli + -$(RM) gettextCompile.mli + -$(RM) gettextMo.mli + +######################## +# Pa_gettext extension # +######################## + +INSTALLIB += \ + pr_gettext.cmo + +pr_gettext.cmo: pr_gettext.ml + ocamlc \ + -I +camlp4 \ + -I $(shell ocamlc -where)/camlp4/Camlp4Parsers \ + -pp camlp4of \ + camlp4lib.cma \ + gettextBase.cma \ + gettextExtension.cma \ + -c $< -o $@ + +include ../ConfMakefile +include ../TopMakefile diff --git a/libgettext-ocaml/gettext.ml b/libgettext-ocaml/gettext.ml new file mode 100644 index 0000000..e334b12 --- /dev/null +++ b/libgettext-ocaml/gettext.ml @@ -0,0 +1,393 @@ +(**************************************************************************) +(* ocaml-gettext: a library to translate messages *) +(* *) +(* Copyright (C) 2003-2008 Sylvain Le Gall *) +(* *) +(* This library is free software; you can redistribute it and/or *) +(* modify it under the terms of the GNU Lesser General Public *) +(* License as published by the Free Software Foundation; either *) +(* version 2.1 of the License, or (at your option) any later version; *) +(* with the OCaml static compilation exception. *) +(* *) +(* This library 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 *) +(* Lesser General Public License for more details. *) +(* *) +(* You should have received a copy of the GNU Lesser General Public *) +(* License along with this library; if not, write to the Free Software *) +(* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *) +(* USA *) +(**************************************************************************) + +open GettextTypes;; +open GettextCompat;; +open GettextUtils;; +open GettextModules;; +open Lexing;; + +(* Function the main global variable of gettext with/without thread *) + +type global_type = + { + t : t option; + realize : t -> t'; + t' : t' option; + } +;; + +(* Default value *) + +let dummy_realize = + (fun t printf_format textdomain str str_plural category -> str) +;; + +let default_realize = + dummy_realize +;; + +(* Referenced function used to manage access to global variable, + in other word to be fullfiled with mutex locking/unlocking if + needed + *) + +let global_lock = ref ( fun () -> () ) +;; + +let global_unlock = ref ( fun () -> () ) +;; + +let global = ref { + t = None; + realize = default_realize; + t' = None; +} +;; + +let get_global_t () = + let t = + !global_lock (); + !global.t + in + !global_unlock (); + match t with + Some t -> t + | None -> raise GettextUninitialized +;; + +let set_global_t t = + let () = + !global_lock (); + global := { + !global with + t = Some t; + t' = None; + } + in + !global_unlock () +;; + +let set_global_realize realize = + let () = + !global_lock (); + global := { + !global with + realize = realize; + t' = None + } + in + !global_unlock () +;; + +let get_global_t' () = + let t' = + !global_lock (); + match !global.t' with + None -> + (* Try to build it out of the other value provided *) + let t = + match !global.t with + Some t -> t + | None -> raise GettextUninitialized + in + let t' = + !global.realize t + in + global := { + !global with + t' = Some t' + }; + t' + | Some t' -> + t' + in + !global_unlock (); + t' +;; + +(* High level functions *) + +module Library = + functor (Init : INIT_TYPE) -> + struct + let init = (Init.textdomain, Init.codeset, Init.dir) :: Init.dependencies + + let s_ str = dgettext (get_global_t' ()) Init.textdomain str + let f_ str = fdgettext (get_global_t' ()) Init.textdomain str + let sn_ str = dngettext (get_global_t' ()) Init.textdomain str + let fn_ str = fdngettext (get_global_t' ()) Init.textdomain str + end +;; + +(* i18n/l10n of gettext it self *) +module GettextGettext = Library(struct + let textdomain = "ocaml-gettext" + let codeset = None + let dir = None + let dependencies = [] + (* Off course, we don't depend on anything because + we are the root of translation *) + end) +;; + +(* Initialization of gettext library *) + +let init = GettextGettext.init +;; + +(* Exception *) + +let string_of_exception exc = + (* It is important to keep the name f_ and s_, in order to allow ocaml-gettext + program to extract the string *) + let f_ x = + GettextGettext.f_ x + in + let s_ x = + GettextGettext.s_ x + in + let spf x = + Printf.sprintf x + in + let string_of_pos lexbuf = + let char_pos = lexbuf.lex_curr_p.pos_cnum - lexbuf.lex_curr_p.pos_bol + in + let line_pos = lexbuf.lex_curr_p.pos_lnum + in + spf (f_ "line %d character %d") + line_pos char_pos + in + match exc with + CompileProblemReadingFile(fln,error) -> + spf (f_ "Problem reading file %s: %s.") fln error + | CompileExtractionFailed(fln,cmd,status) -> + spf (f_ "Problem while extracting %s: command %S exits with code %d.") + fln cmd status + | CompileExtractionInterrupted(fln,cmd,signal) -> + spf (f_ "Problem while extracting %s: command %S killed by signal %d.") + fln cmd signal + | DomainFileDoesntExist(lst) -> + spf (f_ "Cannot find an approriate ocaml-gettext compiled file ( %s ).") + (string_of_list lst) + | GettextUninitialized -> + (s_ "Ocaml-gettext library is not initialized") + | MoInvalidOptions (lexbuf,text) -> + spf (f_ "Error while processing parsing of options at %s: %S.") + (string_of_pos lexbuf) + text + | MoInvalidPlurals (lexbuf,text) -> + spf (f_ "Error while processing parsing of plural at %s: %S.") + (string_of_pos lexbuf) + text + | MoInvalidContentType (lexbuf,text) -> + spf (f_ "Error while processing parsing of content-type at %s: %S.") + (string_of_pos lexbuf) + text + | MoInvalidFile -> + (s_ "MO file provided is not encoded following ocaml-gettext convention.") + | MoInvalidTranslationSingular (str,x) -> + spf (f_ "Trying to fetch the plural form %d of a singular form %S.") + x str + | MoInvalidTranslationPlural (lst,x) -> + spf (f_ "Trying to fetch the plural form %d of plural form %s.") + x (string_of_list lst) + | MoJunk (id,lst) -> + spf (f_ "Junk at the end of the plural form id %S: %s.") + id (string_of_list lst) + | MoEmptyEntry -> + (s_ "An empty entry has been encounter.") + | MoInvalidHeaderNegativeStrings -> + (s_ "Number of strings is negative.") + | MoInvalidHeaderTableStringOutOfBound((b1,e1),(b2,e2)) -> + spf (f_ "Offset of string table is out of bound ([%ld,%ld] should be in [%ld,%ld]).") + b1 e1 b2 e2 + | MoInvalidHeaderTableTranslationOutOfBound((b1,e1),(b2,e2)) -> + spf (f_ "Offset of translation table is out of bound ([%ld,%ld] should be in [%ld,%ld]).") + b1 e1 b2 e2 + | MoInvalidHeaderTableTranslationStringOverlap((b1,e1),(b2,e2)) -> + spf (f_ "Translation table and string table overlap ([%ld,%ld] and [%ld,%ld] have a non empty intersection).") + b1 e1 b2 e2 + | MoInvalidStringOutOfBound(max,cur) -> + spf (f_ "Out of bound access when trying to find a string (%d < %d).") + max cur + | MoInvalidTranslationOutOfBound(max,cur) -> + spf (f_ "Out of bound access when trying to find a translation (%d < %d).") + max cur + | MoCannotOpenFile fln -> + spf (f_ "Could not open file %s.") + fln + | PoInvalidFile (s,lexbuf,chn) -> + spf (f_ "Error while processing parsing of PO file: %S at %s.") + s (string_of_pos lexbuf) + | PoFileInvalidIndex (id,i) -> + spf (f_ "Error while processing parsing of PO file, in msgid %S, %d index is out of bound.") + id i + | PoFileDoesntExist fl -> + spf (f_ "Error while trying to load PO file %s, file doesn't exist.") + fl + | PoInconsistentMerge (str1,str2) -> + spf (f_ "Error while merging two PO files: %S and %S cannot be merged.") + str1 str2 + | TranslateStringNotFound str -> + spf (f_ "Cannot find string %S.") + str + | LocalePosixUnparseable str -> + spf (f_ "Unable to parse the POSIX language environment variable %s") + str + | _ -> + Printexc.to_string exc +;; + + +module Program = + functor (Init : INIT_TYPE) -> + functor (Realize : REALIZE_TYPE) -> + struct + let textdomain = Init.textdomain + + let dependencies = (Init.textdomain, Init.codeset, Init.dir) :: Init.dependencies + + let init = + (* Initialization from all the known textdomain/codeset/dir provided + by library linked with the program *) + (* It is important to keep f_ and s_, for the same reason as in + string_of_exception *) + let f_ x = + GettextGettext.f_ x + in + let s_ x = + GettextGettext.s_ x + in + let spf x = + Printf.sprintf x + in + let () = + set_global_t (GettextModules.create textdomain) + in + let () = + set_global_t ( + List.fold_left ( + fun t (textdomain,codeset_opt,dir_opt) -> + upgrade_textdomain t textdomain (codeset_opt,dir_opt) + ) (get_global_t ()) dependencies + ) + in + let () = + set_global_realize (Realize.realize) + in + [ + "--gettext-failsafe", + (Arg.Symbol + (["ignore"; "inform-stderr"; "raise-exception"], + (fun x -> + match x with + | "ignore" -> + set_global_t + {(get_global_t ()) with + failsafe = Ignore} + | "inform-stderr" -> + set_global_t + {(get_global_t ()) with + failsafe = InformStderr string_of_exception} + | "raise-exception" -> + set_global_t {(get_global_t ()) with + failsafe = RaiseException} + | _ -> + ()))), + spf + (f_ " Choose how to handle failure in ocaml-gettext. Default: %s.") + (match (get_global_t ()).failsafe with + | Ignore -> "ignore" + | InformStderr _ -> "inform-stderr" + | RaiseException -> "raise-exception"); + + "--gettext-disable", + (Arg.Unit (fun () -> set_global_realize dummy_realize)), + s_ " Disable the translation perform by ocaml-gettext. \ + Default: enable."; + + + "--gettext-domain-dir", + (let current_textdomain = + ref textdomain + in + Arg.Tuple + [ + Arg.String (fun textdomain -> + current_textdomain := textdomain); + Arg.String (fun dir -> + set_global_t + (bindtextdomain + !current_textdomain + dir + (get_global_t ()))); + ]), + spf + (f_ "textdomain dir Set a dir to search ocaml-gettext files \ + for the specified domain. Default: %s.") + (string_of_list + (MapTextdomain.fold + (fun textdomain (_,dir_opt) lst -> + match dir_opt with + | Some dir -> (spf ("%s: %s") textdomain dir) :: lst + | None -> lst) + (get_global_t ()).textdomains [])); + + + "--gettext-dir", + (Arg.String + (fun s -> set_global_t + {(get_global_t ()) with + path = s :: (get_global_t ()).path})), + spf + (f_ "dir Add a search dir for ocaml-gettext files. Default: %s.") + (string_of_list (get_global_t ()).path); + + "--gettext-language", + (Arg.String (fun s -> + set_global_t + {(get_global_t ()) with + language = Some s})), + spf + (f_ "language Set the default language for ocaml-gettext. \ + Default: %s.") + (match (get_global_t ()).language with + | Some s -> s + | None -> "(none)"); + + "--gettext-codeset", + (Arg.String (fun s -> set_global_t + {(get_global_t ()) with codeset = s})), + spf (f_ "codeset Set the default codeset for outputting string with \ + ocaml-gettext. Default: %s.") + (get_global_t ()).codeset; + ], + GettextConfig.copyright + + let s_ str = dgettext (get_global_t' ()) textdomain str + let f_ str = fdgettext (get_global_t' ()) textdomain str + let sn_ str = dngettext (get_global_t' ()) textdomain str + let fn_ str = fdngettext (get_global_t' ()) textdomain str + + end +;; diff --git a/libgettext-ocaml/gettext.mli b/libgettext-ocaml/gettext.mli new file mode 100644 index 0000000..29a4d68 --- /dev/null +++ b/libgettext-ocaml/gettext.mli @@ -0,0 +1,123 @@ +(**************************************************************************) +(* ocaml-gettext: a library to translate messages *) +(* *) +(* Copyright (C) 2003-2008 Sylvain Le Gall *) +(* *) +(* This library is free software; you can redistribute it and/or *) +(* modify it under the terms of the GNU Lesser General Public *) +(* License as published by the Free Software Foundation; either *) +(* version 2.1 of the License, or (at your option) any later version; *) +(* with the OCaml static compilation exception. *) +(* *) +(* This library 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 *) +(* Lesser General Public License for more details. *) +(* *) +(* You should have received a copy of the GNU Lesser General Public *) +(* License along with this library; if not, write to the Free Software *) +(* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *) +(* USA *) +(**************************************************************************) + +(** + Modules to use in libraries and programs. + @author Sylvain Le Gall +*) + +(** + This module defines all the function required to use gettext. The primary + design is to use applicative function. The "side effect" of such a choice is + that you must defines, before using any function, all the text domains, + codeset et al. When building a library, you should give access to + [Library.init] (by defining a [gettext_init = YouLibrary.init]). This is + required to enable string translation in the library and programs that uses + the library. The only function missing here is the [realize] function. This + function is defined in a real implementation library : + {ul + {- {!GettextDummy}} + {- {!GettextCamomile}} + {- {!GettextStub}} + } + *) + +(** {1 Exception} *) + +(** Return the string representation of a ocaml-gettext exception. + *) +val string_of_exception: exn -> string +;; + +(** {1 High level interfaces} *) + +(** Value of the dependencies for the initialization of the library + Gettext (for translating exception and help message) +*) +val init: GettextTypes.dependencies +;; + +(** Module to handle typical library requirement *) +module Library : + functor (Init: GettextTypes.INIT_TYPE) -> + sig + + (** Definition of all variables required by ocaml-gettext to use this module + (includes all the dependencies of the library, as defined in + {!GettextTypes.Init}). + *) + val init: GettextTypes.dependencies + + (** Translate a singular string. + *) + val s_: string -> string + + (** Translate a [Printf] singular argument. + *) + val f_: ('a, 'b, 'c, 'c, 'c, 'd) format6 -> + ('a, 'b, 'c, 'c, 'c, 'd) format6 + + (** Translate a plural string. + *) + val sn_: string -> string -> int -> string + + (** Translate a [Printf] plural argument. + *) + val fn_: ('a, 'b, 'c, 'c, 'c, 'd) format6 -> + ('a, 'b, 'c, 'c, 'c, 'd) format6 -> + int -> + ('a, 'b, 'c, 'c, 'c, 'd) format6 + end +;; + +(** Module to handle typical program requirement *) +module Program : + functor (Init: GettextTypes.INIT_TYPE) -> + functor (Realize: GettextTypes.REALIZE_TYPE) -> + sig + (** The first element is a [Arg] argument list. The second element + contains some information about the gettext library (version, + build date and author). + *) + val init: (Arg.key * Arg.spec * Arg.doc) list * string + + (** Seed {Library.s_} + *) + val s_: string -> string + + (** Seed {Library.f_} + *) + val f_: ('a, 'b, 'c, 'c, 'c, 'd) format6 -> + ('a, 'b, 'c, 'c, 'c, 'd) format6 + + (** Seed {Library.sn_} + *) + val sn_: string -> string -> int -> string + + (** Seed {Library.fn_} + *) + val fn_: ('a, 'b, 'c, 'c, 'c, 'd) format6 -> + ('a, 'b, 'c, 'c, 'c, 'd) format6 -> + int -> + ('a, 'b, 'c, 'c, 'c, 'd) format6 + end +;; diff --git a/libgettext-ocaml/gettextCategory.ml b/libgettext-ocaml/gettextCategory.ml new file mode 100644 index 0000000..9e69fbf --- /dev/null +++ b/libgettext-ocaml/gettextCategory.ml @@ -0,0 +1,89 @@ +(**************************************************************************) +(* ocaml-gettext: a library to translate messages *) +(* *) +(* Copyright (C) 2003-2008 Sylvain Le Gall *) +(* *) +(* This library is free software; you can redistribute it and/or *) +(* modify it under the terms of the GNU Lesser General Public *) +(* License as published by the Free Software Foundation; either *) +(* version 2.1 of the License, or (at your option) any later version; *) +(* with the OCaml static compilation exception. *) +(* *) +(* This library 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 *) +(* Lesser General Public License for more details. *) +(* *) +(* You should have received a copy of the GNU Lesser General Public *) +(* License along with this library; if not, write to the Free Software *) +(* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *) +(* USA *) +(**************************************************************************) + +(** Functions to manipulate category. + @author Sylvain Le Gall + *) + +type category = + LC_CTYPE + | LC_NUMERIC + | LC_TIME + | LC_COLLATE + | LC_MONETARY + | LC_MESSAGES + | LC_ALL +;; + +let string_of_category cat = + match cat with + LC_CTYPE -> "LC_CTYPE" + | LC_NUMERIC -> "LC_NUMERIC" + | LC_TIME -> "LC_TIME" + | LC_COLLATE -> "LC_COLLATE" + | LC_MONETARY -> "LC_MONETARY" + | LC_MESSAGES -> "LC_MESSAGES" + | LC_ALL -> "LC_ALL" +;; + +let category_of_string str = + match str with + "LC_CTYPE" -> LC_CTYPE + | "LC_NUMERIC" -> LC_NUMERIC + | "LC_TIME" -> LC_TIME + | "LC_COLLATE" -> LC_COLLATE + | "LC_MONETARY" -> LC_MONETARY + | "LC_MESSAGES" -> LC_MESSAGES + | "LC_ALL" -> LC_ALL + | _ -> raise (Invalid_argument "category_of_string") +;; + +let categories = [ + LC_CTYPE ; + LC_NUMERIC ; + LC_TIME ; + LC_COLLATE ; + LC_MONETARY ; + LC_MESSAGES ; + LC_ALL +] +;; + +let compare c1 c2 = + let val_category x = + match x with + LC_CTYPE -> 0 + | LC_NUMERIC -> 1 + | LC_TIME -> 2 + | LC_COLLATE -> 3 + | LC_MONETARY -> 4 + | LC_MESSAGES -> 5 + | LC_ALL -> 6 + in + compare (val_category c1) (val_category c2) +;; + +module MapCategory = Map.Make (struct + type t = category + let compare = compare +end) +;; diff --git a/libgettext-ocaml/gettextCharset.ml b/libgettext-ocaml/gettextCharset.ml new file mode 100644 index 0000000..54cd75d --- /dev/null +++ b/libgettext-ocaml/gettextCharset.ml @@ -0,0 +1,54 @@ +(**************************************************************************) +(* ocaml-gettext: a library to translate messages *) +(* *) +(* Copyright (C) 2003-2008 Sylvain Le Gall *) +(* *) +(* This library is free software; you can redistribute it and/or *) +(* modify it under the terms of the GNU Lesser General Public *) +(* License as published by the Free Software Foundation; either *) +(* version 2.1 of the License, or (at your option) any later version; *) +(* with the OCaml static compilation exception. *) +(* *) +(* This library 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 *) +(* Lesser General Public License for more details. *) +(* *) +(* You should have received a copy of the GNU Lesser General Public *) +(* License along with this library; if not, write to the Free Software *) +(* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *) +(* USA *) +(**************************************************************************) + +(** Signature of module for charset conversion + @author Sylvain Le Gall + *) + +open GettextTypes;; + +module type CHARSET_TYPE = + sig + type encoding = string + type u + + (** create in_enc out_enc : create a new charset converter from charset + in_enc to out_enc. + *) + val create : t -> encoding -> encoding -> u + + (** recode str enc : return a transcoded string according to enc. + *) + val recode : u -> string -> string + end +;; + +module Dummy : CHARSET_TYPE = + struct + type encoding = string + type u = () + + let create t in_enc out_enc = () + + let recode () str = str + end +;; diff --git a/libgettext-ocaml/gettextCompat.ml b/libgettext-ocaml/gettextCompat.ml new file mode 100644 index 0000000..08f00d5 --- /dev/null +++ b/libgettext-ocaml/gettextCompat.ml @@ -0,0 +1,132 @@ +(**************************************************************************) +(* ocaml-gettext: a library to translate messages *) +(* *) +(* Copyright (C) 2003-2008 Sylvain Le Gall *) +(* *) +(* This library is free software; you can redistribute it and/or *) +(* modify it under the terms of the GNU Lesser General Public *) +(* License as published by the Free Software Foundation; either *) +(* version 2.1 of the License, or (at your option) any later version; *) +(* with the OCaml static compilation exception. *) +(* *) +(* This library 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 *) +(* Lesser General Public License for more details. *) +(* *) +(* You should have received a copy of the GNU Lesser General Public *) +(* License along with this library; if not, write to the Free Software *) +(* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *) +(* USA *) +(**************************************************************************) + +open GettextTypes;; +open GettextCategory;; +open GettextModules;; + +let unsafe_format_of_string fmt str = + if false then + Obj.magic str + else + format_of_string fmt +;; + +let bindtextdomain textdomain dir t = + upgrade_textdomain t textdomain (None,Some dir) +;; + +let bind_textdomain_codeset textdomain codeset t = + upgrade_textdomain t textdomain (Some codeset,None) +;; + +let textdomain default t = + { t with default = default } +;; + +let get_textdomain t = + t.default +;; + +let gettext t' str = + t' false None str None LC_MESSAGES +;; + +let fgettext t' fmt = + unsafe_format_of_string + fmt + (t' true None (string_of_format fmt) None LC_MESSAGES) +;; + +let dgettext t' textdomain str = + t' false (Some textdomain) str None LC_MESSAGES +;; + +let fdgettext t' textdomain fmt = + unsafe_format_of_string + fmt + (t' true (Some textdomain) (string_of_format fmt) None LC_MESSAGES) +;; + +let dcgettext t' textdomain str category = + t' false (Some textdomain) str None category +;; + +let fdcgettext t' textdomain fmt category = + unsafe_format_of_string + fmt + (t' true (Some textdomain) (string_of_format fmt) None category) +;; + +let ngettext t' str str_plural n = + t' false None str (Some(str_plural,n)) LC_MESSAGES +;; + +let fngettext t' fmt fmt_plural n = + if true then + unsafe_format_of_string + fmt + (t' + true + None + (string_of_format fmt) + (Some (string_of_format fmt_plural, n) ) + LC_MESSAGES) + else + fmt_plural +;; + +let dngettext t' textdomain str str_plural n = + t' false (Some textdomain) str (Some (str_plural,n)) LC_MESSAGES +;; + +let fdngettext t' textdomain fmt fmt_plural n = + if true then + unsafe_format_of_string + fmt + (t' + true + (Some textdomain) + (string_of_format fmt) + (Some (string_of_format fmt_plural,n)) + LC_MESSAGES) + else + fmt_plural +;; + +let dcngettext t' textdomain str str_plural n category = + t' false (Some textdomain) str (Some(str_plural,n)) category +;; + +let fdcngettext t' textdomain fmt fmt_plural n category = + if true then + unsafe_format_of_string + fmt + (t' + true + (Some textdomain) + (string_of_format fmt) + (Some (string_of_format fmt_plural,n)) + category) + else + fmt_plural +;; diff --git a/libgettext-ocaml/gettextCompat.mli b/libgettext-ocaml/gettextCompat.mli new file mode 100644 index 0000000..17dbac5 --- /dev/null +++ b/libgettext-ocaml/gettextCompat.mli @@ -0,0 +1,128 @@ +(**************************************************************************) +(* ocaml-gettext: a library to translate messages *) +(* *) +(* Copyright (C) 2003-2008 Sylvain Le Gall *) +(* *) +(* This library is free software; you can redistribute it and/or *) +(* modify it under the terms of the GNU Lesser General Public *) +(* License as published by the Free Software Foundation; either *) +(* version 2.1 of the License, or (at your option) any later version; *) +(* with the OCaml static compilation exception. *) +(* *) +(* This library 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 *) +(* Lesser General Public License for more details. *) +(* *) +(* You should have received a copy of the GNU Lesser General Public *) +(* License along with this library; if not, write to the Free Software *) +(* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *) +(* USA *) +(**************************************************************************) + +(** Gettext compatibility with the native gettext API + @author Sylvain Le Gall + *) + +open GettextTypes +open GettextCategory + +(** [textdomain domain t] Set the current text domain. + *) +val textdomain: textdomain -> t -> t + +(** [get_textdomain t] Returns the current text domain. + *) +val get_textdomain: t -> textdomain + +(** [bindtextdomain textdomain dir] Set the default base directory for the + specified domain. + *) +val bindtextdomain: textdomain -> dir -> t -> t + +(** [bind_textdomain_codeset textdomain codeset] Set the codeset to use for the + specified domain. [codeset] must be a valid codeset for the underlying + character encoder/decoder (iconv, camomile, extlib...) + *) +val bind_textdomain_codeset: textdomain -> codeset -> t -> t + +(** [gettext t' str] Translate the string [str]. + *) +val gettext: t' -> string -> string + +(** [fgettext t' str] [gettext] returning format. + *) +val fgettext: t' -> + ('a, 'b, 'c, 'd, 'e, 'f) format6 -> + ('a, 'b, 'c, 'd, 'e, 'f) format6 + +(** [dgettext t' textdomain str] Translate the string [str] for the specified + domain. + *) +val dgettext: t' -> textdomain -> string -> string + +(** [fdgettext t' textdomain str] [dgettext] returning format. + *) +val fdgettext: t' -> + textdomain -> + ('a, 'b, 'c, 'd, 'e, 'f) format6 -> + ('a, 'b, 'c, 'd, 'e, 'f) format6 + +(** [dcgettext t' textdomain str category] Translate the string [str] for the + specified domain and category. + *) +val dcgettext: t' -> textdomain -> string -> category -> string + +(** [fdcgettext t' textdomain str category] [dcgettext] returning format. + *) +val fdcgettext: t' -> + textdomain -> + ('a, 'b, 'c, 'd, 'e, 'f) format6 -> + category -> + ('a, 'b, 'c, 'd, 'e, 'f) format6 + +(** [ngettext t' str str_plural n] Translate the string [str] using a plural form. + str_plural is the default english plural. n is the relevant number for plural + (i.e. the number of objects deals with the string). + *) +val ngettext: t' -> string -> string -> int -> string + +(** [fngettext t' str str_plural n] [ngettext] returning format. + *) +val fngettext: t' -> + ('a, 'b, 'c, 'd, 'e, 'f) format6 -> + ('a, 'b, 'c, 'd, 'e, 'f) format6 -> + int -> + ('a, 'b, 'c, 'd, 'e, 'f) format6 + +(** [dngettext t' textdomain str str_plural n] Translate the string [str] using + a plural form for the specified domain. + *) +val dngettext: t' -> textdomain -> string -> string -> int -> string + +(** [fdngettext t' textdomain str str_plural n] [dngettext] returning format. + *) +val fdngettext: t' -> + textdomain -> + ('a, 'b, 'c, 'd, 'e, 'f) format6 -> + ('a, 'b, 'c, 'd, 'e, 'f) format6 -> + int -> + ('a, 'b, 'c, 'd, 'e, 'f) format6 + +(** [dcngettext t' textdomain str str_plural n category] Translate the string + [str] using a plural form for the specified domain and category. + *) +val dcngettext: t' -> textdomain -> string -> string -> int -> category -> string + +(** [fdcngettext t' textdomain str str_plural n category] [dcngettext] returning + format. + *) +val fdcngettext: t' -> + textdomain -> + ('a, 'b, 'c, 'd, 'e, 'f) format6 -> + ('a, 'b, 'c, 'd, 'e, 'f) format6 -> + int -> + category -> + ('a, 'b, 'c, 'd, 'e, 'f) format6 + + diff --git a/libgettext-ocaml/gettextCompile.ml b/libgettext-ocaml/gettextCompile.ml new file mode 100644 index 0000000..15b2c44 --- /dev/null +++ b/libgettext-ocaml/gettextCompile.ml @@ -0,0 +1,243 @@ +(**************************************************************************) +(* ocaml-gettext: a library to translate messages *) +(* *) +(* Copyright (C) 2003-2008 Sylvain Le Gall *) +(* *) +(* This library is free software; you can redistribute it and/or *) +(* modify it under the terms of the GNU Lesser General Public *) +(* License as published by the Free Software Foundation; either *) +(* version 2.1 of the License, or (at your option) any later version; *) +(* with the OCaml static compilation exception. *) +(* *) +(* This library 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 *) +(* Lesser General Public License for more details. *) +(* *) +(* You should have received a copy of the GNU Lesser General Public *) +(* License along with this library; if not, write to the Free Software *) +(* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *) +(* USA *) +(**************************************************************************) + +(** Functions for extraction/compilation/installation of PO/MO file. + @author Sylvain Le Gall + *) + +open GettextTypes;; +open FileUtil;; +open FilePath;; + +let po_of_filename filename = + let chn = + try + open_in filename + with Sys_error(str) -> + raise (CompileProblemReadingFile(filename,str)) + in + let po = + GettextPo.input_po chn + in + close_in chn; + po +;; + +(** extract cmd default_option file_options src_files ppf : extract the + translatable strings from all the src_files provided. Each source file will + be extracted using the command cmd, which should be an executable that has + the same output as ocaml-xgettext. If cmd is not provided, it will be + searched in the current path. The command will be called with + default_option, or if the file being extracted is mapped in file_options, + with the option associated to the filename in file_options. The result will + be written using module Format to the formatter ppf. The result of the + extraction should be used as a po template file. + *) +let extract command default_options filename_options filename_lst filename_pot = + let make_command options filename = + Printf.sprintf "%s %s %s" command options filename + in + let extract_one po filename = + let options = + try + MapString.find filename filename_options + with Not_found -> + default_options + in + let real_command = + make_command options filename + in + let chn = + Unix.open_process_in real_command + in + let value = + (Marshal.from_channel chn : po_content) + in + match Unix.close_process_in chn with + | Unix.WEXITED 0 -> + GettextPo.merge_po po value + | Unix.WEXITED exit_code -> + raise (CompileExtractionFailed(filename,real_command,exit_code)) + | Unix.WSIGNALED signal + | Unix.WSTOPPED signal -> + raise (CompileExtractionInterrupted(filename,real_command,signal)) + in + let extraction = + List.fold_left extract_one GettextPo.empty_po filename_lst + in + let chn = + open_out filename_pot + in + let date = + let current_time = + Unix.time () + in + let gmt_time = + Unix.gmtime current_time + in + Printf.sprintf "%04d-%02d-%02d %02d:%02d+0000" + (gmt_time.Unix.tm_year + 1900) + (gmt_time.Unix.tm_mon + 1) + (gmt_time.Unix.tm_mday) + (gmt_time.Unix.tm_hour) + (gmt_time.Unix.tm_min) + in + Printf.fprintf chn +"# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid \"\" +msgstr \"\" +\"Project-Id-Version: PACKAGE VERSION\\n\" +\"Report-Msgid-Bugs-To: \\n\" +\"POT-Creation-Date: %s\\n\" +\"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\\n\" +\"Last-Translator: FULL NAME \\n\" +\"Language-Team: LANGUAGE \\n\" +\"MIME-Version: 1.0\\n\" +\"Content-Type: text/plain; charset=CHARSET\\n\" +\"Content-Transfer-Encoding: 8bit\\n\" +\"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\\n\" + +" date; + GettextPo.output_po chn extraction; + close_out chn +;; + +(** compile input_po output_mo : create a binary representation of the PO file + provided as input_pot. The output file is output_mo. +*) +let compile filename_po filename_mo = + let po = + po_of_filename filename_po + in + let output_one_map filename map = + let lst = + MapString.fold + ( + fun _ commented_po_translation lst -> + let po_translation = + commented_po_translation.po_comment_translation + in + (GettextPo.translation_of_po_translation po_translation) :: lst + ) + map + [] + in + let chn = + open_out_bin filename + in + GettextMo.output_mo chn lst; + close_out chn + in + let make_filename domain filename_mo = + let dirname = + dirname filename_mo + in + let basename = + basename filename_mo + in + (* BUG : should use add_extension *) + make_filename [ dirname ; domain^"."^basename ] + in + output_one_map filename_mo po.no_domain; + MapTextdomain.iter ( + fun domain map -> + output_one_map (make_filename domain filename_mo) map + ) po.domain +;; + +(** install destdir language category textdomain fln : copy the given + filename ( should be a MO file ) to the filename defined by all the + other parameters ( typically destdir/language/category/textdomain.mo ). +*) +let install strict destdir language category textdomain filename_mo_src = + let filename_mo_dst = + GettextDomain.make_filename destdir language category textdomain + in + let dirname_mo_dst = + dirname filename_mo_dst + in + (* Test of the mo file, it will raise an exception if there is any problem + in the MO structure *) + let ((),_) = + GettextMo.fold_mo + (if strict then + RaiseException + else + InformStderr + (function + | (MoInvalidPlurals _) as e -> + Gettext.string_of_exception e + | e -> + raise e)) + (fun x () -> ()) + () + filename_mo_src + in + mkdir ~parent:true dirname_mo_dst; + cp [filename_mo_src] filename_mo_dst +;; + +(** uninstall orgdir language category textdomain : remove the MO file + defined by all the other parameters + ( typically destdir/language/category/textdomain.mo ). +*) +let uninstall orgdir language category textdomain = + let filename_mo_org = + GettextDomain.make_filename orgdir language category textdomain + in + rm [filename_mo_org] +;; + +(** merge fln_pot fln_po_lst backup_ext : use fln_pot as a POT file and + merge the current content of the listed PO file ( fln_po_lst ) with it. + Backup all the PO file using the provided backup extension backup_ext and + produce a merged PO file in place. +*) +let merge filename_pot filename_po_lst backup_extension = + let pot = + po_of_filename filename_pot + in + let merge_one filename_po = + let po = + po_of_filename filename_po + in + let po_merged = + GettextPo.merge_pot pot po + in + let _ = + (* BUG: should use add_extension *) + (* BUG: should use mv *) + Sys.rename filename_po (filename_po^"."^backup_extension) + in + let chn = + open_out filename_po + in + GettextPo.output_po chn po_merged; + close_out chn + in + List.iter merge_one filename_po_lst +;; diff --git a/libgettext-ocaml/gettextConfig.ml.in b/libgettext-ocaml/gettextConfig.ml.in new file mode 100644 index 0000000..b7407c6 --- /dev/null +++ b/libgettext-ocaml/gettextConfig.ml.in @@ -0,0 +1,39 @@ +(**************************************************************************) +(* ocaml-gettext: a library to translate messages *) +(* *) +(* Copyright (C) 2003-2008 Sylvain Le Gall *) +(* *) +(* This library is free software; you can redistribute it and/or *) +(* modify it under the terms of the GNU Lesser General Public *) +(* License as published by the Free Software Foundation; either *) +(* version 2.1 of the License, or (at your option) any later version; *) +(* with the OCaml static compilation exception. *) +(* *) +(* This library 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 *) +(* Lesser General Public License for more details. *) +(* *) +(* You should have received a copy of the GNU Lesser General Public *) +(* License along with this library; if not, write to the Free Software *) +(* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *) +(* USA *) +(**************************************************************************) + +let default_dir = @DEFAULTLOCALEDIR@ +;; + +let default_path = default_dir :: [ @LOCALEDIR@ ] +;; + +let default_codeset = "@CODESET@" +;; + +let version = "@PACKAGE_VERSION@" +;; + +let copyright = + "ocaml-gettext v@PACKAGE_VERSION@\n" + ^"Copyright (C) 2003-2008 Sylvain Le Gall \n" + ^"Licenced under LGPL v2.1 with Ocaml exception" +;; diff --git a/libgettext-ocaml/gettextDomain.ml b/libgettext-ocaml/gettextDomain.ml new file mode 100644 index 0000000..c2336b3 --- /dev/null +++ b/libgettext-ocaml/gettextDomain.ml @@ -0,0 +1,106 @@ +(**************************************************************************) +(* ocaml-gettext: a library to translate messages *) +(* *) +(* Copyright (C) 2003-2008 Sylvain Le Gall *) +(* *) +(* This library is free software; you can redistribute it and/or *) +(* modify it under the terms of the GNU Lesser General Public *) +(* License as published by the Free Software Foundation; either *) +(* version 2.1 of the License, or (at your option) any later version; *) +(* with the OCaml static compilation exception. *) +(* *) +(* This library 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 *) +(* Lesser General Public License for more details. *) +(* *) +(* You should have received a copy of the GNU Lesser General Public *) +(* License along with this library; if not, write to the Free Software *) +(* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *) +(* USA *) +(**************************************************************************) + +(** Signature of module for domain management. + @author Sylvain Le Gall + *) + +open FilePath;; +open FileUtil;; +open GettextTypes;; +open GettextUtils;; +open GettextCategory;; + +(* BUG : a mettre � jour *) +(** compute_path textdomain category t : return the path to the + mo file corresponding to textdomain and category. Language is + guessed from category binding. If the textdomain is not found, + it tries to use the build default to find the file. The file + returned exists and is readable. If such a file doesn't exists + an exception DomainFileDoesntExist is thrown. If the function is + unable to guess the current language an exception + DomainLanguageNotSet is thrown. +*) + +let make_filename dir language category textdomain = + (* http://www.gnu.org/software/gettext/manual/html_mono/gettext.html#SEC148 + dir_name/locale/LC_category/domain_name.mo *) + make_filename [ + (* BUG : should use add_extension *) + dir; language; string_of_category category; textdomain ^ ".mo" + ] +;; + + +let find t languages category textdomain = + let search_path = + ( + try + match MapTextdomain.find textdomain t.textdomains with + (_,Some dir) -> [dir] + | (_,None) -> [] + with Not_found -> + [] + ) @ t.path + in + let ctest = test (And(Exists,Is_readable)) + in + let rec find_mo_file_aux dir languages = + match languages with + language :: tl -> + let current_filename = make_filename dir language category textdomain + in + if ctest current_filename then + current_filename + else + find_mo_file_aux dir tl + | [] -> + raise Not_found + in + let rec find_mo_file path languages = + match path with + dir :: tl -> + ( + try + find_mo_file_aux dir languages + with Not_found -> + find_mo_file tl languages + ) + | [] -> + raise Not_found + in + try + find_mo_file search_path languages + with Not_found -> + raise (DomainFileDoesntExist( + List.flatten ( + List.map ( + fun dir -> + List.map ( + fun language -> + make_filename dir language category textdomain + ) languages + ) search_path + ) + ) + ) +;; diff --git a/libgettext-ocaml/gettextDummy.ml b/libgettext-ocaml/gettextDummy.ml new file mode 100644 index 0000000..e58027c --- /dev/null +++ b/libgettext-ocaml/gettextDummy.ml @@ -0,0 +1,43 @@ +(**************************************************************************) +(* ocaml-gettext: a library to translate messages *) +(* *) +(* Copyright (C) 2003-2008 Sylvain Le Gall *) +(* *) +(* This library is free software; you can redistribute it and/or *) +(* modify it under the terms of the GNU Lesser General Public *) +(* License as published by the Free Software Foundation; either *) +(* version 2.1 of the License, or (at your option) any later version; *) +(* with the OCaml static compilation exception. *) +(* *) +(* This library 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 *) +(* Lesser General Public License for more details. *) +(* *) +(* You should have received a copy of the GNU Lesser General Public *) +(* License along with this library; if not, write to the Free Software *) +(* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *) +(* USA *) +(**************************************************************************) + +(** Concrete implementation based on nothing. + @author Sylvain Le Gall + *) + +open GettextTypes;; + +module Generic : REALIZE_TYPE = + struct + let realize t = + fun printf_format opt str plural_form category -> + match plural_form with + Some(str_plural,n) -> + if GettextMo.germanic_plural n = 0 then + str + else + str_plural + | None -> + str + end +;; + diff --git a/libgettext-ocaml/gettextFormat.ml b/libgettext-ocaml/gettextFormat.ml new file mode 100644 index 0000000..d29a24b --- /dev/null +++ b/libgettext-ocaml/gettextFormat.ml @@ -0,0 +1,90 @@ +(**************************************************************************) +(* ocaml-gettext: a library to translate messages *) +(* *) +(* Copyright (C) 2003-2008 Sylvain Le Gall *) +(* *) +(* This library is free software; you can redistribute it and/or *) +(* modify it under the terms of the GNU Lesser General Public *) +(* License as published by the Free Software Foundation; either *) +(* version 2.1 of the License, or (at your option) any later version; *) +(* with the OCaml static compilation exception. *) +(* *) +(* This library 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 *) +(* Lesser General Public License for more details. *) +(* *) +(* You should have received a copy of the GNU Lesser General Public *) +(* License along with this library; if not, write to the Free Software *) +(* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *) +(* USA *) +(**************************************************************************) + +(** Check string equivalence regarding printf use. + @author Sylvain Le Gall + *) + +open GettextTypes;; +open GettextUtils;; + +(** [check_format failsafe translation] Returns a translation structure + if all the string contained in the translation are equivalent of str_id, + regarding printf format. If not, replace each string which conflict by + str_id, in the result + *) +let check_format failsafe translation = + let format_lst_of_string str = + let lexbuf = + Lexing.from_string str + in + GettextFormat_parser.main + GettextFormat_lexer.token + lexbuf + in + (* return true in case of problem *) + let check_format_lst_lst lst1 lst2 = + let check_format_lst_lst_aux b s1 s2 = + b || (String.compare s1 s2 <> 0) + in + try + List.fold_left2 check_format_lst_lst_aux false lst1 lst2 + with Invalid_argument _-> + true + in + let check_format_lst_str lst str = + check_format_lst_lst + lst + (format_lst_of_string str) + in + let choose_format lst_ref str_ref str = + if check_format_lst_str lst_ref str then + fail_or_continue + failsafe + (FormatInconsistent(str,str_ref)) + str_ref + else + str + in + match translation with + | Singular (str_id,str) -> + let lst_id = + format_lst_of_string str_id + in + Singular(str_id,choose_format lst_id str_id str) + | Plural (str_id,str_plural,lst) -> + let lst_id = + format_lst_of_string str_id + in + let valid_str_plural = + choose_format lst_id str_id str_plural + in + let valid_lst = + match lst with + | trans_singular :: trans_plurals -> + (choose_format lst_id str_id trans_singular) :: + (List.map (choose_format lst_id valid_str_plural) trans_plurals) + | [] -> + [] + in + Plural(str_id, valid_str_plural, valid_lst) +;; diff --git a/libgettext-ocaml/gettextFormat_lexer.mll b/libgettext-ocaml/gettextFormat_lexer.mll new file mode 100644 index 0000000..b782a22 --- /dev/null +++ b/libgettext-ocaml/gettextFormat_lexer.mll @@ -0,0 +1,80 @@ +(**************************************************************************) +(* ocaml-gettext: a library to translate messages *) +(* *) +(* Copyright (C) 2003-2008 Sylvain Le Gall *) +(* *) +(* This library is free software; you can redistribute it and/or *) +(* modify it under the terms of the GNU Lesser General Public *) +(* License as published by the Free Software Foundation; either *) +(* version 2.1 of the License, or (at your option) any later version; *) +(* with the OCaml static compilation exception. *) +(* *) +(* This library 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 *) +(* Lesser General Public License for more details. *) +(* *) +(* You should have received a copy of the GNU Lesser General Public *) +(* License along with this library; if not, write to the Free Software *) +(* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *) +(* USA *) +(**************************************************************************) + +{ + + open GettextFormat_parser;; + +} + +let flags = ['-''0''+'' ']* ['0'-'9']* ('.'['0'-'9']*)? + +rule +token = parse + '%' flags { format_char lexbuf } +| eof { EOF } +| _ { token lexbuf } +and +format_char = parse + "d" as fc +| "i" as fc +| "n" as fc +| "N" as fc +| "u" as fc +| "x" as fc +| "X" as fc +| "o" as fc +| "s" as fc +| "S" as fc +| "c" as fc +| "C" as fc +| "f" as fc +| "F" as fc +| "e" as fc +| "E" as fc +| "g" as fc +| "G" as fc +| "B" as fc +| "b" as fc +| "ld" as fc +| "li" as fc +| "lu" as fc +| "lx" as fc +| "lX" as fc +| "lo" as fc +| "nd" as fc +| "ni" as fc +| "nu" as fc +| "nx" as fc +| "nX" as fc +| "no" as fc +| "Ld" as fc +| "Li" as fc +| "Lu" as fc +| "Lx" as fc +| "LX" as fc +| "Lo" as fc +| "a" as fc +| "t" as fc { FORMAT_CHAR fc } +| "!" +| "%" { token lexbuf } + diff --git a/libgettext-ocaml/gettextFormat_parser.mly b/libgettext-ocaml/gettextFormat_parser.mly new file mode 100644 index 0000000..a2e1635 --- /dev/null +++ b/libgettext-ocaml/gettextFormat_parser.mly @@ -0,0 +1,44 @@ +/**************************************************************************/ +/* ocaml-gettext: a library to translate messages */ +/* */ +/* Copyright (C) 2003-2008 Sylvain Le Gall */ +/* */ +/* This library is free software; you can redistribute it and/or */ +/* modify it under the terms of the GNU Lesser General Public */ +/* License as published by the Free Software Foundation; either */ +/* version 2.1 of the License, or (at your option) any later version; */ +/* with the OCaml static compilation exception. */ +/* */ +/* This library 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 */ +/* Lesser General Public License for more details. */ +/* */ +/* You should have received a copy of the GNU Lesser General Public */ +/* License along with this library; if not, write to the Free Software */ +/* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 */ +/* USA */ +/**************************************************************************/ + +%{ + +%} + +%token FORMAT_CHAR +%token EOF + +%type < string list > main +%start main + +%% + +main: + format_char EOF { List.rev $1 } +; + +format_char: + format_char FORMAT_CHAR { $2 :: $1 } +| { [] } +; + +%% diff --git a/libgettext-ocaml/gettextLocale.ml b/libgettext-ocaml/gettextLocale.ml new file mode 100644 index 0000000..8788f74 --- /dev/null +++ b/libgettext-ocaml/gettextLocale.ml @@ -0,0 +1,142 @@ +(**************************************************************************) +(* ocaml-gettext: a library to translate messages *) +(* *) +(* Copyright (C) 2003-2008 Sylvain Le Gall *) +(* *) +(* This library is free software; you can redistribute it and/or *) +(* modify it under the terms of the GNU Lesser General Public *) +(* License as published by the Free Software Foundation; either *) +(* version 2.1 of the License, or (at your option) any later version; *) +(* with the OCaml static compilation exception. *) +(* *) +(* This library 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 *) +(* Lesser General Public License for more details. *) +(* *) +(* You should have received a copy of the GNU Lesser General Public *) +(* License along with this library; if not, write to the Free Software *) +(* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *) +(* USA *) +(**************************************************************************) + +(** Implements different operation over locale/category. + @author Sylvain Le Gall + *) + +open GettextTypes;; +open GettextCategory;; +open GettextUtils;; + +module type LOCALE_TYPE = + sig + (** get_locale t cat : Return the value of locale and encoding for cat. + The value returned is in ASCII. Priority should be given to the + values language/codeset provided in variable t. + *) + val get_locale : t -> category -> (locale list * codeset) + end +;; + +(** Return the best value of environnement variable, that can be found according to the + priority defined in gettext. The choice take into account t and category, + but may ignore it, if a variable with a best priority is set. + This function can be used to get a value for a LOCALE_TYPE implementation. + Raise Not_found if nothing appropriate. +*) +let posix_getenv t category = + (* http://www.gnu.org/software/gettext/manual/html_mono/gettext.html#SEC155 + In the function dcgettext at every call the current setting of the + highest priority environment variable is determined and used. + Highest priority means here the following list with decreasing priority: + 1. LANGUAGE + 2. LC_ALL + 3. LC_xxx, according to selected locale + 4. LANG + *) + match t.language with + Some str -> + str + | None -> + try + let best_env = + List.find ( + fun s -> + try + ignore(Sys.getenv s); + true + with Not_found -> + false + ) [ + "LANGUAGE" ; + string_of_category LC_ALL ; + string_of_category category; + "LANG" ] + in + Sys.getenv best_env + with Not_found -> + "C" +;; + +module Posix : LOCALE_TYPE = + struct + + (* Extract from "man setlocale" + A locale name is typically of the form language[_territory][.codeset][@modi- + fier], where language is an ISO 639 language code, territory is an ISO 3166 + country code, and codeset is a character set or encoding identifier like + ISO-8859-1 or UTF-8. For a list of all supported locales, try "locale -a", cf. + locale(1). + *) + + let get_locale t category = + let posix_lang = posix_getenv t category + in + let locale = + try + let lexbuf = + Lexing.from_string posix_lang + in + GettextLocale_parser.main + GettextLocale_lexer.token + lexbuf + with x -> + fail_or_continue t.failsafe + (LocalePosixUnparseable (posix_lang^" "^(Printexc.to_string x))) + (GettextLocale_types.create_locale posix_lang) + in + let locales = + match + (locale.GettextLocale_types.territory,locale.GettextLocale_types.modifier) with + Some territory, Some modifier -> + [ + locale.GettextLocale_types.language^"_"^territory^"@"^modifier; + locale.GettextLocale_types.language^"_"^territory; + locale.GettextLocale_types.language + ] + | None, Some modifier -> + [ + locale.GettextLocale_types.language^"@"^modifier; + locale.GettextLocale_types.language + ] + | Some territory, None -> + [ + locale.GettextLocale_types.language^"_"^territory; + locale.GettextLocale_types.language + ] + | None, None -> + [ + locale.GettextLocale_types.language + ] + in + let codeset = + match locale.GettextLocale_types.codeset with + Some codeset -> + codeset + | None -> + t.codeset + in + (locales,codeset) + + end +;; diff --git a/libgettext-ocaml/gettextLocale_lexer.mll b/libgettext-ocaml/gettextLocale_lexer.mll new file mode 100644 index 0000000..48bf268 --- /dev/null +++ b/libgettext-ocaml/gettextLocale_lexer.mll @@ -0,0 +1,33 @@ +(**************************************************************************) +(* ocaml-gettext: a library to translate messages *) +(* *) +(* Copyright (C) 2003-2008 Sylvain Le Gall *) +(* *) +(* This library is free software; you can redistribute it and/or *) +(* modify it under the terms of the GNU Lesser General Public *) +(* License as published by the Free Software Foundation; either *) +(* version 2.1 of the License, or (at your option) any later version; *) +(* with the OCaml static compilation exception. *) +(* *) +(* This library 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 *) +(* Lesser General Public License for more details. *) +(* *) +(* You should have received a copy of the GNU Lesser General Public *) +(* License along with this library; if not, write to the Free Software *) +(* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *) +(* USA *) +(**************************************************************************) + +{ +open GettextLocale_parser;; +} + +rule token = parse + '_' { UNDERSCORE } + | '.' { DOT } + | '@' { AT } + | eof { EOF } + | [^'_''.''@']* as id { ID(id) } + diff --git a/libgettext-ocaml/gettextLocale_parser.mly b/libgettext-ocaml/gettextLocale_parser.mly new file mode 100644 index 0000000..2244189 --- /dev/null +++ b/libgettext-ocaml/gettextLocale_parser.mly @@ -0,0 +1,49 @@ +/**************************************************************************/ +/* ocaml-gettext: a library to translate messages */ +/* */ +/* Copyright (C) 2003-2008 Sylvain Le Gall */ +/* */ +/* This library is free software; you can redistribute it and/or */ +/* modify it under the terms of the GNU Lesser General Public */ +/* License as published by the Free Software Foundation; either */ +/* version 2.1 of the License, or (at your option) any later version; */ +/* with the OCaml static compilation exception. */ +/* */ +/* This library 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 */ +/* Lesser General Public License for more details. */ +/* */ +/* You should have received a copy of the GNU Lesser General Public */ +/* License along with this library; if not, write to the Free Software */ +/* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 */ +/* USA */ +/**************************************************************************/ + +%{ + +open GettextTypes;; +open GettextLocale_types;; + +%} + +%token UNDERSCORE +%token DOT +%token AT +%token EOF +%token ID + +%start main +%type main +%% + +main: + locale EOF { (*print_endline "eof";*) $1 } +; + +locale: +| locale UNDERSCORE ID { (*print_endline "underscore";*) { $1 with territory = Some $3 } } +| locale DOT ID { (*print_endline "dot";*) { $1 with codeset = Some $3 } } +| locale AT ID { (*print_endline "at";*) { $1 with modifier = Some $3 } } +| ID { (*print_endline "id";*) create_locale $1 } +; diff --git a/libgettext-ocaml/gettextLocale_types.ml b/libgettext-ocaml/gettextLocale_types.ml new file mode 100644 index 0000000..ce29c05 --- /dev/null +++ b/libgettext-ocaml/gettextLocale_types.ml @@ -0,0 +1,42 @@ +(**************************************************************************) +(* ocaml-gettext: a library to translate messages *) +(* *) +(* Copyright (C) 2003-2008 Sylvain Le Gall *) +(* *) +(* This library is free software; you can redistribute it and/or *) +(* modify it under the terms of the GNU Lesser General Public *) +(* License as published by the Free Software Foundation; either *) +(* version 2.1 of the License, or (at your option) any later version; *) +(* with the OCaml static compilation exception. *) +(* *) +(* This library 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 *) +(* Lesser General Public License for more details. *) +(* *) +(* You should have received a copy of the GNU Lesser General Public *) +(* License along with this library; if not, write to the Free Software *) +(* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *) +(* USA *) +(**************************************************************************) + +(** + @author Sylvain Le Gall + *) + +type locale = { + language : string; + territory : string option; + codeset : string option; + modifier : string option; +} +;; + +let create_locale language = { + language = language; + territory = None; + codeset = None; + modifier = None; +} +;; + diff --git a/libgettext-ocaml/gettextMo.ml b/libgettext-ocaml/gettextMo.ml new file mode 100644 index 0000000..fb395e8 --- /dev/null +++ b/libgettext-ocaml/gettextMo.ml @@ -0,0 +1,496 @@ +(**************************************************************************) +(* ocaml-gettext: a library to translate messages *) +(* *) +(* Copyright (C) 2003-2008 Sylvain Le Gall *) +(* *) +(* This library is free software; you can redistribute it and/or *) +(* modify it under the terms of the GNU Lesser General Public *) +(* License as published by the Free Software Foundation; either *) +(* version 2.1 of the License, or (at your option) any later version; *) +(* with the OCaml static compilation exception. *) +(* *) +(* This library 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 *) +(* Lesser General Public License for more details. *) +(* *) +(* You should have received a copy of the GNU Lesser General Public *) +(* License along with this library; if not, write to the Free Software *) +(* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *) +(* USA *) +(**************************************************************************) + +(** + @author Sylvain Le Gall + *) +open GettextUtils;; +open GettextTypes;; +open GettextMo_int32;; + +let mo_sig_be = int32_of_byte (0x95, 0x04, 0x12, 0xde) +;; + +let mo_sig_le = int32_of_byte (0xde, 0x12, 0x04, 0x95) +;; + +let check_mo_header chn hdr = + let offset_min = Int32.of_int 28 + in + let offset_max = Int32.of_int (in_channel_length chn) + in + let range_offset start_bound = + let end_bound = + Int32.add start_bound ( + Int32.mul (Int32.pred hdr.number_of_strings) (Int32.of_int 8) + ) + in + (offset_min,offset_max),(start_bound,end_bound) + in + let val_in_range (start_bound,end_bound) value = + Int32.compare start_bound value <= 0 + && Int32.compare value end_bound <= 0 + in + (* check_* function return true in case of problem *) + let check_overlap start_bound1 start_bound2 = + let (_,(_,end_bound1)) = + range_offset start_bound1 + in + let (_,(_,end_bound2)) = + range_offset start_bound2 + in + val_in_range (start_bound1,end_bound1) start_bound2 + || val_in_range (start_bound1,end_bound1) end_bound2 + || val_in_range (start_bound2,end_bound2) start_bound1 + || val_in_range (start_bound2,end_bound2) end_bound1 + in + let check_range_offset start_bound = + let (file,(start_bound,end_bound)) = + range_offset start_bound + in + not ( + val_in_range file start_bound + && val_in_range file end_bound + ) + in + if Int32.compare hdr.number_of_strings Int32.zero < 0 then + raise MoInvalidHeaderNegativeStrings + else if check_range_offset hdr.offset_table_strings then + raise ( + MoInvalidHeaderTableStringOutOfBound( + fst (range_offset hdr.offset_table_strings), + snd (range_offset hdr.offset_table_strings) + ) + ) + else if check_range_offset hdr.offset_table_translation then + raise ( + MoInvalidHeaderTableTranslationOutOfBound( + fst (range_offset hdr.offset_table_translation), + snd (range_offset hdr.offset_table_translation) + ) + ) + else if check_overlap hdr.offset_table_translation hdr.offset_table_strings then + raise ( + MoInvalidHeaderTableTranslationStringOverlap( + snd (range_offset hdr.offset_table_translation), + snd (range_offset hdr.offset_table_strings) + ) + ) + (* We don't care of hashing table, since we don't use it *) + else + hdr +;; + +let input_mo_header chn = + let endianess = + let magic_number = seek_in chn 0; input_int32 chn BigEndian + in + if magic_number = mo_sig_be then + BigEndian + else if magic_number = mo_sig_le then + LittleEndian + else + raise MoInvalidFile + in + let seek_and_input x = seek_in chn x; input_int32 chn endianess + in + check_mo_header chn + { + endianess = endianess; + file_format_revision = seek_and_input 4; + number_of_strings = seek_and_input 8; + offset_table_strings = seek_and_input 12; + offset_table_translation = seek_and_input 16; + size_of_hashing_table = seek_and_input 20; + offset_of_hashing_table = seek_and_input 24; + } +;; + +let output_mo_header chn hdr = + let output = output_int32 chn hdr.endianess + in + (* magic_number : be is the native way to + * specify it, it will be translated through + * the output_int32*) + output mo_sig_be; + output hdr.file_format_revision; + output hdr.number_of_strings; + output hdr.offset_table_strings; + output hdr.offset_table_translation; + output hdr.size_of_hashing_table; + output hdr.offset_of_hashing_table +;; + +let string_of_mo_header mo_header = + let buff = Buffer.create 256 + in + Printf.bprintf buff "File format revision : %ld\n" mo_header.file_format_revision; + Printf.bprintf buff "Number of string : %ld\n" mo_header.number_of_strings; + Printf.bprintf buff "Offset of table with original strings : %lx\n" mo_header.offset_table_strings; + Printf.bprintf buff "Offset of table with translation strings : %lx\n" mo_header.offset_table_translation; + Printf.bprintf buff "Size of hashing table : %lx\n" mo_header.size_of_hashing_table; + Printf.bprintf buff "Offset of hashing table : %lx\n" mo_header.offset_of_hashing_table; + Buffer.contents buff +;; + +let input_mo_untranslated failsafe chn mo_header number = + if number < (Int32.to_int mo_header.number_of_strings) then + let offset_pair = + (Int32.to_int mo_header.offset_table_strings) + number * 8 + in + let str = + try + seek_in chn offset_pair; + input_int32_pair_string chn mo_header.endianess + with End_of_file -> + raise (MoInvalidStringOutOfBound(in_channel_length chn,offset_pair)) + in + split_plural str + else + raise (MoInvalidStringOutOfBound(Int32.to_int mo_header.number_of_strings, number)) +;; + +let input_mo_translated failsafe chn mo_header number = + if number < (Int32.to_int mo_header.number_of_strings) then + ( + let offset_pair = + (Int32.to_int mo_header.offset_table_translation) + number * 8 + in + let str = + try + seek_in chn offset_pair; + input_int32_pair_string chn mo_header.endianess + with End_of_file -> + raise (MoInvalidTranslationOutOfBound + (in_channel_length chn,offset_pair)) + in + split_plural str + ) + else + ( + raise + (MoInvalidStringOutOfBound + (Int32.to_int mo_header.number_of_strings, + number)) + ) +;; + +let input_mo_translation failsafe chn mo_header number = + let untranslated = + input_mo_untranslated failsafe chn mo_header number + in + let translated = + input_mo_translated failsafe chn mo_header number + in + match untranslated with + | [id] -> + Singular (id, String.concat "\000" translated) + | id :: id_plural :: [] -> + Plural (id, id_plural, translated) + | id :: id_plural :: tl -> + fail_or_continue failsafe + (MoJunk (id, tl)) + (Plural (id, id_plural, translated)) + | [] -> + fail_or_continue failsafe + MoEmptyEntry + (Singular ("", "")) +;; + +let get_translated_value failsafe translation plural_number = + match (translation, plural_number) with + ((Singular (_,str)), 0) -> + str + | ((Singular (_,str)), x) -> + fail_or_continue failsafe + (MoInvalidTranslationSingular(str,x)) + str + | ((Plural (str,str_plural,[])),x) -> + if x = 0 then + str + else + str_plural + | ((Plural (_,_,lst)), x) when x < List.length lst -> + List.nth lst x + | ((Plural (_,_,lst)), x) -> + fail_or_continue failsafe + (MoInvalidTranslationPlural(lst,x)) + List.nth lst 0 +;; + +let germanic_plural = + (* The germanic default *) + fun n -> if n = 1 then 1 else 0 +;; + +let input_mo_informations failsafe chn mo_header = + (* La position de "" est forc�ment 0 *) + let empty_translation = + get_translated_value failsafe + (input_mo_translation failsafe chn mo_header 0) + 0 + in + let field_value = + let lexbuf = Lexing.from_string empty_translation + in + try + GettextMo_parser.main GettextMo_lexer.token_field_name lexbuf + with + Parsing.Parse_error + | Failure("lexing: empty token") -> + fail_or_continue failsafe + (MoInvalidOptions (lexbuf,empty_translation)) + [] + in + let (nplurals,fun_plural_forms) = + try + let field_plural_forms = List.assoc "Plural-Forms" field_value + in + let lexbuf = Lexing.from_string field_plural_forms + in + try + GettextMo_parser.plural_forms + GettextMo_lexer.token_field_plural_value lexbuf + with + Parsing.Parse_error + | Failure("lexing: empty token") -> + fail_or_continue + failsafe + (MoInvalidPlurals(lexbuf,field_plural_forms)) + (2,germanic_plural) + with Not_found -> + (2,germanic_plural) + in + let (content_type, content_type_charset) = + let gettext_content = ("text/plain", GettextConfig.default_codeset) + in + try + let field_content_type = List.assoc "Content-Type" field_value + in + let lexbuf = Lexing.from_string field_content_type + in + try + GettextMo_parser.content_type + GettextMo_lexer.token_field_content_type lexbuf + with + Parsing.Parse_error + | Failure("lexing: empty token") -> + fail_or_continue failsafe + (MoInvalidContentType(lexbuf,field_content_type)) + gettext_content + with Not_found -> + gettext_content + in + let extract_field_string name = + try + Some (List.assoc name field_value) + with Not_found -> + None + in + { + project_id_version = extract_field_string "Project-Id-Version"; + report_msgid_bugs_to = extract_field_string "Report-Msgid-Bugs-To"; + pot_creation_date = extract_field_string "POT-Creation-Date"; + po_revision_date = extract_field_string "PO-Revision-Date"; + last_translator = extract_field_string "Last-Translator"; + language_tream = extract_field_string "Language-Team"; + mime_version = extract_field_string "MIME-Version"; + content_type = extract_field_string "Content-Type"; + content_transfer_encoding = extract_field_string "Content-Transfer-Encoding"; + plural_forms = extract_field_string "Plural-Forms"; + content_type_charset = content_type_charset; + nplurals = nplurals; + fun_plural_forms = fun_plural_forms; + } +;; + +let string_of_mo_informations ?(compute_plurals=(0,3)) mo_translation = + let buff = Buffer.create 1024 + in + let p = Printf.bprintf + in + let extract_string x = + match x with + Some s -> s + | None -> "" + in + p buff "Project-Id-Version : %s\n" (extract_string mo_translation.project_id_version); + p buff "Report-Msgid-Bugs-To : %s\n" (extract_string mo_translation.report_msgid_bugs_to); + p buff "POT-Creation-Date : %s\n" (extract_string mo_translation.pot_creation_date); + p buff "PO-Revision-Date : %s\n" (extract_string mo_translation.po_revision_date); + p buff "Last-Translator : %s\n" (extract_string mo_translation.last_translator); + p buff "Language-Team : %s\n" (extract_string mo_translation.language_tream); + p buff "MIME-Version : %s\n" (extract_string mo_translation.mime_version); + p buff "Content-Type : %s\n" (extract_string mo_translation.content_type); + p buff "Plurals-Forms : %s\n" (extract_string mo_translation.plural_forms); + p buff "Content-Transfer-Encoding : %s\n" (extract_string mo_translation.content_transfer_encoding); + p buff "Content-Type-Charset : %s\n" mo_translation.content_type_charset; + p buff "NPlurals : %d\n" mo_translation.nplurals; + p buff "Fun plural : "; + ( + let (a,b) = compute_plurals + in + for i = a to b do + p buff "%d -> %d ; " i (mo_translation.fun_plural_forms i); + done; + ); + p buff "\n"; + Buffer.contents buff +;; + +let output_mo ?(endianess = LittleEndian) chn lst = + (* There could have potential issue with alignment, but it seems to be fixed + * at 1 in gettext-0.14.1/gettext-tools/configure.ac, so there is no probleme + * *) + let null_terminated lst = + List.map ( fun str -> str^"\000" ) lst + in + let compute_table start_pos lst = + let compute_length lst = + List.map String.length lst + in + let compute_offset (current_pos,lst_pos) length = + (* Remove 1 since we have NULL terminated strings *) + ( current_pos + length, (length - 1,current_pos) :: lst_pos ) + in + let (final_pos, lst_rev) = + List.fold_left compute_offset (start_pos, []) (compute_length lst) + in + (final_pos, List.rev lst_rev) + in + let no_empty_lst = + (* Avoid using empty translated string *) + List.filter + (function + | Singular (_, "") -> + false + | Plural (_, _, lst) when String.concat "" lst = "" -> + false + | _ -> + true) + lst + in + let sorted_lst = + let compare_entry entry1 entry2 = + let value_of_entry entry = + match entry with + | Singular (id, _) -> id + | Plural (id, _, _) -> id + in + String.compare + (value_of_entry entry1) + (value_of_entry entry2) + in + List.sort compare_entry no_empty_lst + in + let untranslated = + let to_string entry = + match entry with + Singular (id, _) -> id + | Plural (id, id_plural, _) -> id ^ "\000" ^ id_plural + in + null_terminated (List.map to_string sorted_lst) + in + let translated = + let to_string entry = + match entry with + Singular (_,str) -> str + | Plural (_, _, lst) -> String.concat "\000" lst + in + null_terminated (List.map to_string sorted_lst) + in + let gN = List.length sorted_lst + in + let gO = 28 (* Size of the header *) + in + let gT = gO + 8 * gN + in + let gS = 0 (* Hashtable is not implemented, since algorithm is not public -- documented *) + in + let gH = gT + 8 * gN + in + let (final_untranslated,untranslated_table) = + compute_table (gH + gS * 4) untranslated + in + let (_,translated_table) = + compute_table final_untranslated translated + in + let header = { + endianess = endianess; + file_format_revision = Int32.zero; + number_of_strings = Int32.of_int gN; + offset_table_strings = Int32.of_int gO; + offset_table_translation = Int32.of_int gT; + size_of_hashing_table = Int32.of_int gS; + offset_of_hashing_table = Int32.of_int gH; + } + in + output_mo_header chn header; + List.iter ( + List.iter ( + fun (a,b) -> + output_int32_pair chn endianess (Int32.of_int a,Int32.of_int b); + ) + ) [ untranslated_table ; translated_table ]; + List.iter (output_string chn) untranslated; + List.iter (output_string chn) translated +;; + +let fold_mo failsafe f init fl_mo = + let chn = + open_in_bin fl_mo + in + let res = + try + (* Processing of the file *) + let mo_header = input_mo_header chn + in + let informations = + input_mo_informations failsafe chn mo_header + in + let fun_plural_forms = + informations.GettextTypes.fun_plural_forms + in + let rec fold_mo_aux accu i = + if i < Int32.to_int mo_header.number_of_strings then + let new_translation = + input_mo_translation failsafe chn mo_header i + in + let new_accu = + f new_translation accu + in + fold_mo_aux new_accu (i+1) + else + accu + in + let translations = + fold_mo_aux init 0 + in + (translations,fun_plural_forms) + with (Sys_error _) -> + fail_or_continue failsafe + (MoCannotOpenFile fl_mo) + (init,germanic_plural) + in + close_in chn; + res +;; diff --git a/libgettext-ocaml/gettextMo_int32.ml b/libgettext-ocaml/gettextMo_int32.ml new file mode 100644 index 0000000..7490a3e --- /dev/null +++ b/libgettext-ocaml/gettextMo_int32.ml @@ -0,0 +1,116 @@ +(**************************************************************************) +(* ocaml-gettext: a library to translate messages *) +(* *) +(* Copyright (C) 2003-2008 Sylvain Le Gall *) +(* *) +(* This library is free software; you can redistribute it and/or *) +(* modify it under the terms of the GNU Lesser General Public *) +(* License as published by the Free Software Foundation; either *) +(* version 2.1 of the License, or (at your option) any later version; *) +(* with the OCaml static compilation exception. *) +(* *) +(* This library 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 *) +(* Lesser General Public License for more details. *) +(* *) +(* You should have received a copy of the GNU Lesser General Public *) +(* License along with this library; if not, write to the Free Software *) +(* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *) +(* USA *) +(**************************************************************************) + +(** + @author Sylvain Le Gall + *) + +open GettextTypes;; + +let int32_of_byte (a0,a1,a2,a3) = + Int32.add + (Int32.shift_left (Int32.of_int a0) 24) + (Int32.of_int + ( + (a1 lsl 16) + + (a2 lsl 8) + + a3 + ) + ) +;; + +let byte_of_int32 i = + let one_byte = Int32.of_int 0xFF + in + let extract_byte sb = + let mask = + Int32.shift_left one_byte ( sb * 8 ) + in + let i_masked = + Int32.logand i mask + in + Int32.to_int (Int32.shift_right i_masked ( sb * 8 )) + in + ( extract_byte 3, extract_byte 2, extract_byte 1, extract_byte 0 ) +;; + +let input_int32 chn endian = + let (a0,a1,a2,a3) = + ( + input_byte chn, + input_byte chn, + input_byte chn, + input_byte chn + ) + in + match endian with + BigEndian -> + int32_of_byte (a0,a1,a2,a3) + | LittleEndian -> + int32_of_byte (a3,a2,a1,a0) +;; + +let output_int32 chn endian vl = + let (a0,a1,a2,a3) = + byte_of_int32 vl + in + let order = + match endian with + BigEndian -> + [a0;a1;a2;a3] + | LittleEndian -> + [a3;a2;a1;a0] + in + List.iter (output_byte chn) order +;; + +let input_int32_pair chn endian = + let a = input_int32 chn endian + in + let b = input_int32 chn endian + in + (a, b) +;; + +let output_int32_pair chn endian (a,b) = + output_int32 chn endian a; + output_int32 chn endian b +;; + +let input_int32_pair_string chn endian = + let (length,offset) = + input_int32_pair chn endian + in + let (ilength,ioffset) = + (Int32.to_int length,Int32.to_int offset) + in + if 0 <= ioffset + ilength && ioffset + ilength < in_channel_length chn then + let str = String.make ilength 'X' + in + seek_in chn ioffset; + really_input chn str 0 ilength; + str + else + (* We use this exception, because that what should happen if we try to + read the string *) + raise End_of_file +;; diff --git a/libgettext-ocaml/gettextMo_lexer.mll b/libgettext-ocaml/gettextMo_lexer.mll new file mode 100644 index 0000000..d17306f --- /dev/null +++ b/libgettext-ocaml/gettextMo_lexer.mll @@ -0,0 +1,74 @@ +(**************************************************************************) +(* ocaml-gettext: a library to translate messages *) +(* *) +(* Copyright (C) 2003-2008 Sylvain Le Gall *) +(* *) +(* This library is free software; you can redistribute it and/or *) +(* modify it under the terms of the GNU Lesser General Public *) +(* License as published by the Free Software Foundation; either *) +(* version 2.1 of the License, or (at your option) any later version; *) +(* with the OCaml static compilation exception. *) +(* *) +(* This library 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 *) +(* Lesser General Public License for more details. *) +(* *) +(* You should have received a copy of the GNU Lesser General Public *) +(* License along with this library; if not, write to the Free Software *) +(* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *) +(* USA *) +(**************************************************************************) + +{ + +open GettextMo_parser;; + +} + +rule +token_field_name = parse + "Content-Type" [' ''\t']* ':' { CONTENT_TYPE(token_field_value lexbuf) } +| "Plural-Forms" [' ''\t']* ':' { PLURAL_FORMS(token_field_value lexbuf) } +| ([^'\n''\r''\t'' ']+ as id) [' ''\t']* ':' { FIELD_NAME(id, token_field_value lexbuf) } +| eof { EOF } +| _ { token_field_name lexbuf} +and +token_field_value = parse + [^'\n''\r']* as str { str } +and +token_field_plural_value = parse + "nplurals" { NPLURALS } +| ';' { SEMICOLON } +| "plural" { PLURAL } +| "?" { QUESTION_MARK } +| ":" { COLON } +| "||" { OR } +| "&&" { AND } +| "==" { EQ } +| '=' { EQUAL } +| "!=" { NEQ } +| "<=" { LE } +| "<" { L } +| ">=" { GE } +| ">" { G } +| "+" { PLUS } +| "-" { MINUS } +| "*" { MUL } +| "/" { DIV } +| "%" { MOD } +| "!" { NOT } +| '(' { LPAREN } +| ')' { RPAREN } +| "n" { ID } +| ['0'-'9']+ as nbr { (NUMBER (int_of_string nbr) ) } +| eof { EOF } +| [' ''\t'] { token_field_plural_value lexbuf } +and +token_field_content_type = parse + "charset" { CHARSET } +| ';' { SEMICOLON } +| '=' { EQUAL } +| [^' ''\t'';''=']+ as str { (STRING str) } +| [' ''\t'] { token_field_content_type lexbuf } +| eof { EOF } diff --git a/libgettext-ocaml/gettextMo_parser.mly b/libgettext-ocaml/gettextMo_parser.mly new file mode 100644 index 0000000..95d50e3 --- /dev/null +++ b/libgettext-ocaml/gettextMo_parser.mly @@ -0,0 +1,125 @@ +/**************************************************************************/ +/* ocaml-gettext: a library to translate messages */ +/* */ +/* Copyright (C) 2003-2008 Sylvain Le Gall */ +/* */ +/* This library is free software; you can redistribute it and/or */ +/* modify it under the terms of the GNU Lesser General Public */ +/* License as published by the Free Software Foundation; either */ +/* version 2.1 of the License, or (at your option) any later version; */ +/* with the OCaml static compilation exception. */ +/* */ +/* This library 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 */ +/* Lesser General Public License for more details. */ +/* */ +/* You should have received a copy of the GNU Lesser General Public */ +/* License along with this library; if not, write to the Free Software */ +/* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 */ +/* USA */ +/**************************************************************************/ + +%{ + +open GettextTypes;; + +%} + +%token EOF +%token NPLURALS +%token SEMICOLON +%token PLURAL +%token EQUAL +%token CHARSET +%token QUESTION_MARK +%token COLON +%token OR +%token AND +%token EQ +%token NEQ +%token LE +%token L +%token GE +%token G +%token PLUS +%token MINUS +%token MUL +%token DIV +%token MOD +%token NOT +%token ID +%token RPAREN +%token LPAREN + +%token FIELD_NAME +%token CONTENT_TYPE +%token PLURAL_FORMS +%token NUMBER +%token STRING + +%right QUESTION_MARK +%left OR +%left AND +%left EQ NEQ +%left G L GE LE +%left PLUS MINUS +%left MUL DIV MOD +%right NOT + +%type < (string * string) list > main +%start main + +%type < int * ( int -> int ) > plural_forms +%start plural_forms + +%type < string * string > content_type +%start content_type + +%% + +main: + lines EOF { $1 } +; + +lines: + lines line { $2 :: $1 } +| line { [$1] } +; + +line: + FIELD_NAME { let (a,b) = $1 in (a, b) } +| CONTENT_TYPE { ("Content-Type", $1) } +| PLURAL_FORMS { ("Plural-Forms", $1) } +; + +plural_forms: + NPLURALS EQUAL NUMBER SEMICOLON PLURAL EQUAL expr { ($3,$7) } +| NPLURALS EQUAL NUMBER SEMICOLON PLURAL EQUAL expr SEMICOLON { ($3,$7) } +; + +content_type: + STRING SEMICOLON CHARSET EQUAL STRING { ($1,String.uppercase $5) } +; + +expr: + ID { fun x -> x } +| NUMBER { fun x -> $1 } +| expr QUESTION_MARK expr COLON expr { fun x -> if ($1 x) != 0 then ($3 x) else ($5 x) } +| expr OR expr { fun x -> if ($1 x) != 0 then 1 else ($3 x) } +| expr AND expr { fun x -> if ($1 x) != 0 then 0 else ($3 x) } +| expr EQ expr { fun x -> if ($1 x) = ($3 x) then 1 else 0 } +| expr NEQ expr { fun x -> if ($1 x) != ($3 x) then 1 else 0 } +| expr LE expr { fun x -> if ($1 x) <= ($3 x) then 1 else 0 } +| expr L expr { fun x -> if ($1 x) < ($3 x) then 1 else 0 } +| expr GE expr { fun x -> if ($1 x) >= ($3 x) then 1 else 0 } +| expr G expr { fun x -> if ($1 x) > ($3 x) then 1 else 0 } +| expr PLUS expr { fun x -> ($1 x) + ($3 x) } +| expr MINUS expr { fun x -> ($1 x) - ($3 x) } +| expr MUL expr { fun x -> ($1 x) * ($3 x) } +| expr DIV expr { fun x -> ($1 x) / ($3 x) } +| expr MOD expr { fun x -> ($1 x) mod ($3 x) } +| NOT expr { fun x -> if ($2 x) = 0 then 1 else 0 } +| LPAREN expr RPAREN { fun x -> $2 x} +; +%% diff --git a/libgettext-ocaml/gettextModules.ml b/libgettext-ocaml/gettextModules.ml new file mode 100644 index 0000000..e2d6323 --- /dev/null +++ b/libgettext-ocaml/gettextModules.ml @@ -0,0 +1,107 @@ +(**************************************************************************) +(* ocaml-gettext: a library to translate messages *) +(* *) +(* Copyright (C) 2003-2008 Sylvain Le Gall *) +(* *) +(* This library is free software; you can redistribute it and/or *) +(* modify it under the terms of the GNU Lesser General Public *) +(* License as published by the Free Software Foundation; either *) +(* version 2.1 of the License, or (at your option) any later version; *) +(* with the OCaml static compilation exception. *) +(* *) +(* This library 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 *) +(* Lesser General Public License for more details. *) +(* *) +(* You should have received a copy of the GNU Lesser General Public *) +(* License along with this library; if not, write to the Free Software *) +(* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *) +(* USA *) +(**************************************************************************) + +(** + @author Sylvain Le Gall + *) + +open FileUtil;; +open GettextTypes;; +open GettextCategory;; + +(** Function for manipulation the type t *) + +let upgrade_textdomain t k value = + let (current_codeset,current_dir) = + try + MapTextdomain.find k t.textdomains + with Not_found -> + (None,None) + in + let new_value = + match value with + (None,None) -> (current_codeset,current_dir) + | (None,new_dir) -> (current_codeset,new_dir) + | (new_codeset,None) -> (new_codeset,current_dir) + | (new_codeset,new_dir) -> (new_codeset,new_dir) + in + { t with textdomains = MapTextdomain.add k new_value t.textdomains } +;; + +let create + ?(failsafe = Ignore) + ?(categories = []) + ?(codesets = []) + ?(dirs = []) + ?(textdomains = []) + ?(codeset = GettextConfig.default_codeset ) + ?(path = GettextConfig.default_path ) + ?(language) + textdomain = + let map_categories = + List.fold_left ( + fun map (category,locale) -> + MapCategory.add category locale map + ) + MapCategory.empty + categories + in + let result = + { + failsafe = failsafe; + textdomains = MapTextdomain.empty; + categories = map_categories; + language = language; + codeset = codeset; + path = path; + default = textdomain; + } + in + (* Apply any upgrade required by the different settings provided *) + let apply_upgrade t lst = + List.fold_left ( + fun t (textdomain,changes) -> + upgrade_textdomain t textdomain changes + ) t lst + in + (* All changes from the setting of textdomains *) + let textdomains_changes = + List.map + (fun textdomain -> (textdomain,(None,None))) + (textdomain :: textdomains) + in + (* All changes from the setting of codesets *) + let codesets_changes = + List.map + (fun (textdomain,codeset) -> (textdomain,(Some codeset,None))) + codesets + in + (* All changes from the setting of dirs *) + let dirs_changes = + List.map + (fun (textdomain,dir) -> (textdomain,(None,Some dir))) + dirs + in + apply_upgrade + result + ( textdomains_changes @ codesets_changes @ dirs_changes ) +;; diff --git a/libgettext-ocaml/gettextPo.ml b/libgettext-ocaml/gettextPo.ml new file mode 100644 index 0000000..a522dfa --- /dev/null +++ b/libgettext-ocaml/gettextPo.ml @@ -0,0 +1,343 @@ +(**************************************************************************) +(* ocaml-gettext: a library to translate messages *) +(* *) +(* Copyright (C) 2003-2008 Sylvain Le Gall *) +(* *) +(* This library is free software; you can redistribute it and/or *) +(* modify it under the terms of the GNU Lesser General Public *) +(* License as published by the Free Software Foundation; either *) +(* version 2.1 of the License, or (at your option) any later version; *) +(* with the OCaml static compilation exception. *) +(* *) +(* This library 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 *) +(* Lesser General Public License for more details. *) +(* *) +(* You should have received a copy of the GNU Lesser General Public *) +(* License along with this library; if not, write to the Free Software *) +(* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *) +(* USA *) +(**************************************************************************) + +(** + @author Sylvain Le Gall + *) + +open GettextUtils;; +open GettextTypes;; +open GettextMo;; + +open FileUtil;; +open FilePath;; + +(** empty_po : value representing an empty PO *) +let empty_po = + GettextPo_utils.empty_po +;; + +(** add_po_translation_no_domain po (comment_lst,location_lst,translation) : add a translation + to a corpus of already defined translation with no domain defined. If the + translation already exist, they are merged concerning location, and + follow these rules for the translation itself : + - singular and singular : if there is an empty string ( "" ) in one + of the translation, use the other translation, + - plural and plural : if there is an empty string list ( [ "" ; "" ] ) in + one of the translaiton, use the other translation, + - singular and plural : merge into a plural form. + There is checks during the merge that can raise PoInconsistentMerge : + - for one singular string if the two plural strings differs + - if there is some elements that differs ( considering the special case of + the empty string ) in the translation +*) +let add_po_translation_no_domain po po_translation = + try + GettextPo_utils.add_po_translation_no_domain po po_translation + with PoInconsistentMerge(str1,str2) -> + raise (PoInconsistentMerge(str1,str2)) +;; + +(** add_po_translation_domain po domain (comment_lst,location_lst,translation) : add a + translation to the already defined translation with the domain defined. + See add_translation_no_domain for details. +*) +let add_po_translation_domain po domain po_translation = + try + GettextPo_utils.add_po_translation_domain po domain po_translation + with PoInconsistentMerge(str1,str2) -> + raise (PoInconsistentMerge(str1,str2)) +;; + +(** merge_po po1 po2 : merge two PO. The rule for merging are the same as + defined in add_po_translation_no_domain. Can raise PoInconsistentMerge +*) +let merge_po po1 po2 = + (* We take po2 as the initial set, we merge po1 into po2 beginning with + po1.no_domain and then po1.domain *) + let merge_no_domain = + MapString.fold ( + fun _ translation po -> + add_po_translation_no_domain po translation + ) po1.no_domain po2 + in + let merge_one_domain domain map_domain po = + MapString.fold ( + fun _ translation po -> + add_po_translation_domain domain po translation + ) map_domain po + in + MapTextdomain.fold merge_one_domain po1.domain merge_no_domain +;; + +(** merge_pot po pot : merge a PO with a POT. Only consider strings that + exists in the pot. Always use location as defined in the POT. If a string + is not found, use the translation provided in the POT. If a plural is found + and a singular should be used, downgrade the plural to singular. If a + singular is found and a plural should be used, upgrade singular to plural, + using the strings provided in the POT for ending the translation. + *) +let merge_pot pot po = + let order_po_map ?(domain) () = + match domain with + None -> + po.no_domain :: ( + MapTextdomain.fold ( fun _ x lst -> x :: lst ) + po.domain [] + ) + | Some domain -> + let tl = + po.no_domain :: ( + MapTextdomain.fold ( + fun key x lst -> + if key = domain then + lst + else + x :: lst + ) po.domain [] + ) + in + try + (MapTextdomain.find domain po.domain) :: tl + with Not_found -> + tl + in + let merge_translation map_lst key commented_translation_pot = + let translation_pot = + commented_translation_pot.po_comment_translation + in + let translation_merged = + try + let (commented_translation_po) = + let map_po = + List.find (MapString.mem key) map_lst + in + MapString.find key map_po + in + let translation_po = + commented_translation_po.po_comment_translation + in + (* Implementation of the rule given above *) + match (translation_pot,translation_po) with + PoSingular(str_id,_), PoPlural(_, _, str :: _ ) -> + PoSingular(str_id, str) + | PoPlural(str_id, str_plural, _ :: tl ), PoSingular(_, str) -> + PoPlural(str_id, str_plural, str :: tl) + | PoPlural(str_id, str_plural, []), PoSingular(_, str) -> + PoPlural(str_id, str_plural, str :: []) + | _, translation -> + translation + with Not_found -> + (* Fallback to the translation provided in the POT *) + translation_pot + in + { + commented_translation_pot with + po_comment_translation = translation_merged + } + in + (* We begin with an empty po, and merge everything according to the rule + above. *) + let merge_no_domain = + MapString.fold ( + fun key pot_translation po -> + add_po_translation_no_domain po + (merge_translation (order_po_map ()) key pot_translation) + ) pot.no_domain empty_po + in + let merge_one_domain domain map_domain po = + MapString.fold ( + fun key pot_translation po -> + add_po_translation_domain domain po + (merge_translation (order_po_map ~domain:domain ()) key pot_translation) + ) map_domain po + in + MapTextdomain.fold merge_one_domain pot.domain merge_no_domain +;; + +let input_po chn = + let lexbuf = Lexing.from_channel chn + in + try + GettextPo_parser.msgfmt GettextPo_lexer.token lexbuf + with + Parsing.Parse_error -> + raise (PoInvalidFile ("parse error",lexbuf,chn)) + | Failure(s) -> + raise (PoInvalidFile (s,lexbuf,chn)) + | PoInconsistentMerge(str1,str2) -> + raise (PoInconsistentMerge(str1,str2)) +;; + +let output_po chn po = + let comment_max_length = + 80 + in + let fpf x = + Printf.fprintf chn x + in + let escape_string str = + let rec escape_string_aux buff i = + if i < String.length str then + let () = + match String.get str i with + | '\n' -> Buffer.add_string buff "\\n" + | '\t' -> Buffer.add_string buff "\\t" + | '\b' -> Buffer.add_string buff "\\b" + | '\r' -> Buffer.add_string buff "\\r" + | '\012' -> Buffer.add_string buff "\\f" + | '\011' -> Buffer.add_string buff "\\v" + | '\007' -> Buffer.add_string buff "\\a" + | '"' -> Buffer.add_string buff "\\\"" + | '\\' -> Buffer.add_string buff "\\\\" + | e -> + Buffer.add_char buff e + in + escape_string_aux buff (i+1) + else + ( + ) + in + let buff = + Buffer.create ((String.length str) + 2) + in + Buffer.add_char buff '"'; + escape_string_aux buff 0; + Buffer.add_char buff '"'; + Buffer.contents buff + in + + let hyphens chn lst = + match lst with + [] -> + () + | lst -> + Printf.fprintf chn + "%s" + (String.concat "\n" (List.map escape_string lst)) + in + + let comment_line str_hyphen str_sep line_max_length token_lst = + let str_len = + (List.fold_left (fun acc str -> acc + (String.length str)) 0 token_lst) + + + ((List.length token_lst) * (String.length str_sep)) + in + let buff = + Buffer.create + (str_len + (String.length str_hyphen) * (str_len / line_max_length)) + in + let rec comment_line_aux first_token line_length lst = + match lst with + | str :: tl -> + let sep_length = + if first_token then + 0 + else if (String.length str) + line_length > line_max_length then + ( + Buffer.add_char buff '\n'; + Buffer.add_string buff str_hyphen; + Buffer.add_string buff str_sep; + (String.length str_hyphen) + (String.length str_sep) + ) + else + ( + Buffer.add_string buff str_sep; + String.length str_sep + ) + in + Buffer.add_string buff str; + comment_line_aux false (sep_length + (String.length str) + line_length) tl + | [] -> + Buffer.contents buff + in + comment_line_aux true 0 token_lst + in + + + let rec output_po_translation_aux _ commented_translation = + ( + match commented_translation.po_comment_filepos with + | [] -> + () + | lst -> + fpf "%s\n" + (comment_line + "#." + " " + comment_max_length + ("#:" :: (List.map (fun (str,line) -> Printf.sprintf "%s:%d" str line) lst))) + ); + ( + match commented_translation.po_comment_special with + | [] -> + () + | lst -> + fpf "%s\n" + (comment_line + "#." + " " + comment_max_length + ("#," :: lst)) + ); + ( + match commented_translation.po_comment_translation with + PoSingular(id,str) -> + ( + fpf "msgid %a\n" hyphens id; + fpf "msgstr %a\n" hyphens str + ) + | PoPlural(id,id_plural,lst) -> + ( + fpf "msgid %a\n" hyphens id; + fpf "msgid_plural %a\n" hyphens id_plural; + let _ = List.fold_left + ( fun i s -> + fpf "msgstr[%i] %a\n" i hyphens s; + i + 1 + ) 0 lst + in + () + ) + ); + fpf "\n" + in + MapString.iter output_po_translation_aux po.no_domain; + MapTextdomain.iter ( + fun domain map -> + fpf "domain %S\n\n" domain; + MapString.iter output_po_translation_aux map + ) po.domain +;; + + +let translation_of_po_translation po_translation = + match po_translation with + PoSingular(id, str) -> + Singular(String.concat "" id, String.concat "" str) + | PoPlural(id, id_plural, lst) -> + Plural ( + String.concat "" id, + String.concat "" id_plural, + List.map (String.concat "") lst + ) +;; diff --git a/libgettext-ocaml/gettextPoComment_parser.mly b/libgettext-ocaml/gettextPoComment_parser.mly new file mode 100644 index 0000000..0678cfa --- /dev/null +++ b/libgettext-ocaml/gettextPoComment_parser.mly @@ -0,0 +1,67 @@ +/**************************************************************************/ +/* ocaml-gettext: a library to translate messages */ +/* */ +/* Copyright (C) 2003-2008 Sylvain Le Gall */ +/* */ +/* This library is free software; you can redistribute it and/or */ +/* modify it under the terms of the GNU Lesser General Public */ +/* License as published by the Free Software Foundation; either */ +/* version 2.1 of the License, or (at your option) any later version; */ +/* with the OCaml static compilation exception. */ +/* */ +/* This library 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 */ +/* Lesser General Public License for more details. */ +/* */ +/* You should have received a copy of the GNU Lesser General Public */ +/* License along with this library; if not, write to the Free Software */ +/* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 */ +/* USA */ +/**************************************************************************/ + + +%{ + +open GettextTypes;; +open GettextUtils;; +open GettextPo_utils;; + +%} + +%token COLON +%token LINE +%token KEYWORD +%token FILENAME +%token COMMENT_EOF + +%type comment_filepos +%type comment_special +%start comment_filepos comment_special + +%% + +comment_filepos: + filepos_list COMMENT_EOF { List.rev $1 } +| COMMENT_EOF { [] } +; + +filepos_list: + filepos_list filepos { $2 :: $1 } +| filepos { [$1] } +; + +filepos: + FILENAME COLON LINE { ($1,$3) } +; + +comment_special: + special_list COMMENT_EOF { List.rev $1 } +| COMMENT_EOF { [] } +; + +special_list: + special_list KEYWORD { $2 :: $1 } +| KEYWORD { [$1] } +; + diff --git a/libgettext-ocaml/gettextPo_lexer.mll b/libgettext-ocaml/gettextPo_lexer.mll new file mode 100644 index 0000000..2a58605 --- /dev/null +++ b/libgettext-ocaml/gettextPo_lexer.mll @@ -0,0 +1,116 @@ +(**************************************************************************) +(* ocaml-gettext: a library to translate messages *) +(* *) +(* Copyright (C) 2003-2008 Sylvain Le Gall *) +(* *) +(* This library is free software; you can redistribute it and/or *) +(* modify it under the terms of the GNU Lesser General Public *) +(* License as published by the Free Software Foundation; either *) +(* version 2.1 of the License, or (at your option) any later version; *) +(* with the OCaml static compilation exception. *) +(* *) +(* This library 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 *) +(* Lesser General Public License for more details. *) +(* *) +(* You should have received a copy of the GNU Lesser General Public *) +(* License along with this library; if not, write to the Free Software *) +(* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *) +(* USA *) +(**************************************************************************) + +{ + +open GettextPo_parser;; +open GettextPoComment_parser;; + +let next_line lexbuf = + lexbuf.Lexing.lex_curr_p <- + { + lexbuf.Lexing.lex_curr_p with + Lexing.pos_lnum = lexbuf.Lexing.lex_curr_p.Lexing.pos_lnum + 1; + Lexing.pos_bol = lexbuf.Lexing.lex_curr_p.Lexing.pos_cnum; + } +;; + +} + +rule +token = parse + "msgstr" { MSGSTR } +| "msgid" { MSGID } +| "msgid_plural" { MSGID_PLURAL } +| "domain" { DOMAIN } +| '[' { LBRACKET } +| ']' { RBRACKET } +| ['0'-'9']+ as nbr { NUMBER (int_of_string nbr) } +| '"' { STRING (string_val lexbuf) } +| "#:" { COMMENT_FILEPOS(comment_join (Buffer.create 80) lexbuf)} +| "#," { COMMENT_SPECIAL(comment_join (Buffer.create 80) lexbuf)} +| '#' { comment_skip lexbuf } +| [' ''\t'] { token lexbuf } +| ['\r''\n'] { next_line lexbuf; token lexbuf } +| eof { EOF } +and + +string_val = parse + "\\n" { "\n" ^ ( string_val lexbuf) } +| "\\t" { "\t" ^ ( string_val lexbuf) } +| "\\b" { "\b" ^ ( string_val lexbuf) } +| "\\r" { "\r" ^ ( string_val lexbuf) } +| "\\f" { "\012" ^ ( string_val lexbuf) } +| "\\v" { "\011" ^ ( string_val lexbuf) } +| "\\a" { "\007" ^ ( string_val lexbuf) } +| "\\\"" { "\"" ^ ( string_val lexbuf) } +| "\\\\" { "\\" ^ ( string_val lexbuf) } +| '\\' (['0'-'7'] ['0'-'7']? ['0'-'7']?) as oct + { + let chr = + try + char_of_int (int_of_string ( "0o" ^ oct )) + with _ -> + char_of_int 255 + in + ( String.make 1 chr ) ^ ( string_val lexbuf ) + } +| "\\x" (['0'-'9''A'-'F''a'-'f'] ['0'-'9''A'-'F''a'-'f']?) as hex + { + let chr = + try + char_of_int (int_of_string ("0x" ^ hex )) + with _ -> + char_of_int 255 + in + ( String.make 1 chr ) ^ ( string_val lexbuf ) + } +| [^'"''\\']+ as str { str ^ (string_val lexbuf) } +| '"' { "" } +and + +comment_skip = parse + ['\n'] { next_line lexbuf; token lexbuf } +| _ { comment_skip lexbuf } +and + +comment_join strbuf = parse +| "\n#." { next_line lexbuf; comment_join strbuf lexbuf } +| '\n' { next_line lexbuf; Buffer.contents strbuf } +| '\r' { comment_join strbuf lexbuf } +| [^'\n''\r']* as str { Buffer.add_string strbuf str; comment_join strbuf lexbuf } +and + +comment_filepos = parse +| [' ''\t''\r'] { comment_filepos lexbuf } +| ':' { COLON } +| ['0'-'9']+ as nbr { LINE (int_of_string nbr) } +| ([^' ''\t''\r''\n''"'':''0'-'9''['']''#'][^' ''\t''\r''\n''"'':''['']''#']*) as str + { FILENAME(str) } +| eof { COMMENT_EOF } +and + +comment_special = parse +| [' ''\t''\r'] { comment_special lexbuf } +| [^' ''\t''\r']+ as str { KEYWORD str } +| eof { COMMENT_EOF } + diff --git a/libgettext-ocaml/gettextPo_parser.mly b/libgettext-ocaml/gettextPo_parser.mly new file mode 100644 index 0000000..2fe1785 --- /dev/null +++ b/libgettext-ocaml/gettextPo_parser.mly @@ -0,0 +1,191 @@ +/**************************************************************************/ +/* ocaml-gettext: a library to translate messages */ +/* */ +/* Copyright (C) 2003-2008 Sylvain Le Gall */ +/* */ +/* This library is free software; you can redistribute it and/or */ +/* modify it under the terms of the GNU Lesser General Public */ +/* License as published by the Free Software Foundation; either */ +/* version 2.1 of the License, or (at your option) any later version; */ +/* with the OCaml static compilation exception. */ +/* */ +/* This library 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 */ +/* Lesser General Public License for more details. */ +/* */ +/* You should have received a copy of the GNU Lesser General Public */ +/* License along with this library; if not, write to the Free Software */ +/* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 */ +/* USA */ +/**************************************************************************/ + +%{ + +open GettextTypes;; +open GettextUtils;; +open GettextPo_utils;; + +type comment = + | CommentFilePos of po_filepos list + | CommentSpecial of string list +;; + +let check_string_format ref str = + str +;; + +let rec add_comment comments po_commented_translation = + match comments with + | CommentFilePos e :: comments_tl -> + add_comment + comments_tl + { + po_commented_translation with + po_comment_filepos = + List.append + e + po_commented_translation.po_comment_filepos + } + | CommentSpecial e :: comments_tl -> + add_comment + comments_tl + { + po_commented_translation with + po_comment_special = + List.append + e + po_commented_translation.po_comment_special + } + | [] -> + po_commented_translation +;; + +let check_plural id id_plural lst = + let check_plural_one index lst = + List.rev ( + snd ( + List.fold_left ( fun (index,lst) (cur_index,cur_elem) -> + if index + 1 = cur_index then + (cur_index, (check_string_format id cur_elem) :: lst) + else + raise (PoFileInvalidIndex(String.concat "" id,cur_index)) + ) (index,[]) lst + ) + ) + in + { + po_comment_special = []; + po_comment_filepos = []; + po_comment_translation = + PoPlural(id, (check_string_format id id_plural), (check_plural_one (-1) lst)); + } +;; + +let check_singular id str = + { + po_comment_special = []; + po_comment_filepos = []; + po_comment_translation = + PoSingular(id, check_string_format id str) + } +;; + +%} + +%token MSGSTR +%token MSGID +%token MSGID_PLURAL +%token DOMAIN +%token LBRACKET +%token RBRACKET +%token NUMBER +%token STRING +%token EOF +%token COMMENT_FILEPOS +%token COMMENT_SPECIAL + +%type msgfmt +%start msgfmt + +%% + +msgfmt: + msgfmt domain { let (d,l) = $2 in List.fold_left (add_po_translation_domain d) $1 l } +| domain { let (d,l) = $1 in List.fold_left (add_po_translation_domain d) empty_po l } +| msgfmt message_list { List.fold_left add_po_translation_no_domain $1 $2 } +| message_list { List.fold_left add_po_translation_no_domain empty_po $1 } +| EOF { empty_po } +; + +comment: +| COMMENT_FILEPOS + { + let lexbuf = + Lexing.from_string $1 + in + let lst = + GettextPoComment_parser.comment_filepos + GettextPo_lexer.comment_filepos + lexbuf + in + CommentFilePos lst + } +| COMMENT_SPECIAL + { + let lexbuf = + Lexing.from_string $1 + in + let lst = + GettextPoComment_parser.comment_special + GettextPo_lexer.comment_special + lexbuf + in + CommentSpecial lst + } +; + +comment_list: + comment_list comment { $2 :: $1 } +| comment { [$1] } +;; + +domain: + DOMAIN STRING message_list { ($2,$3) } +| DOMAIN STRING { ($2,[]) } +; + +message_list: + message_list message { $2 :: $1 } +| message { [$1] } +; + +message: + comment_list MSGID string_list MSGSTR string_list + { add_comment $1 (check_singular (List.rev $3) (List.rev $5)) } +| MSGID string_list MSGSTR string_list + { (check_singular (List.rev $2) (List.rev $4)) } +| comment_list MSGID string_list msgid_pluralform pluralform_list + { add_comment $1 (check_plural (List.rev $3) $4 (List.rev $5)) } +| MSGID string_list msgid_pluralform pluralform_list + { (check_plural (List.rev $2) $3 (List.rev $4)) } +; + +msgid_pluralform: + MSGID_PLURAL string_list { (List.rev $2) } +; + +pluralform_list: + pluralform_list pluralform { $2 :: $1 } +| pluralform { [$1] } +; + +pluralform: + MSGSTR LBRACKET NUMBER RBRACKET string_list { ($3,(List.rev $5)) } +; + +string_list: + string_list STRING { $2 :: $1 } +| STRING { [$1] } +; + diff --git a/libgettext-ocaml/gettextPo_utils.ml b/libgettext-ocaml/gettextPo_utils.ml new file mode 100644 index 0000000..eb2efcd --- /dev/null +++ b/libgettext-ocaml/gettextPo_utils.ml @@ -0,0 +1,144 @@ +(**************************************************************************) +(* ocaml-gettext: a library to translate messages *) +(* *) +(* Copyright (C) 2003-2008 Sylvain Le Gall *) +(* *) +(* This library is free software; you can redistribute it and/or *) +(* modify it under the terms of the GNU Lesser General Public *) +(* License as published by the Free Software Foundation; either *) +(* version 2.1 of the License, or (at your option) any later version; *) +(* with the OCaml static compilation exception. *) +(* *) +(* This library 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 *) +(* Lesser General Public License for more details. *) +(* *) +(* You should have received a copy of the GNU Lesser General Public *) +(* License along with this library; if not, write to the Free Software *) +(* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *) +(* USA *) +(**************************************************************************) + +(** + @author Sylvain Le Gall + *) + +open GettextTypes;; + +let empty_po = + { + no_domain = MapString.empty; + domain = MapTextdomain.empty; + } +;; + +(* See GettextPo for details concerning merge of the translation *) +let add_po_translation_aux map commented_translation = + let translation = + commented_translation.po_comment_translation + in + let is_lst_empty lst = + List.for_all ( fun lst -> (String.concat "" lst) = "") lst + in + let is_lst_same lst1 lst2 = + try + not (List.exists2 ( fun a b -> a <> b ) lst1 lst2) + with Invalid_argument _ -> + false + in + let string_of_list lst = + let lst_escaped = + List.map ( fun s -> Printf.sprintf "%S" (String.concat "" s) ) lst + in + Printf.sprintf "[ %a ]" ( fun () lst -> String.concat ";" lst) lst_escaped + in + let str_id = + match translation with + PoSingular(str_lst,_) + | PoPlural(str_lst,_,_) -> str_lst + in + let new_commented_translation = + try + let previous_commented_translation = + MapString.find (String.concat "" str_id) map + in + let previous_location_lst = + previous_commented_translation.po_comment_filepos + in + let previous_translation = + previous_commented_translation.po_comment_translation + in + let location_lst = + commented_translation.po_comment_filepos + in + let merged_translation = + match (previous_translation,translation) with + PoSingular(_,str1), PoSingular(_,str2) when is_lst_same str1 str2 -> + PoSingular(str_id,str1) + | PoSingular(_,[""]), PoSingular(_,str2) -> + PoSingular(str_id,str2) + | PoSingular(_,str1), PoSingular(_,[""]) -> + PoSingular(str_id,str1) + | PoSingular(_,str1), PoSingular(_,str2) -> + raise (PoInconsistentMerge(String.concat "" str1, String.concat "" str2)) + | PoPlural(_,str1,lst1), PoPlural(_,str2,lst2) + when is_lst_same str1 str2 && is_lst_empty lst1 -> + PoPlural(str_id,str2,lst2) + | PoPlural(_,str1,lst1), PoPlural(_,str2,lst2) + when is_lst_same str1 str2 && is_lst_empty lst2 -> + PoPlural(str_id,str1,lst1) + | PoPlural(_,str1,lst1), PoPlural(_,str2,lst2) + when is_lst_same str1 str2 && is_lst_same lst1 lst2 -> + PoPlural(str_id,str1,lst1) + | PoPlural(_,str1,lst1), PoPlural(_,str2,lst2) + when is_lst_same str1 str2 -> + raise (PoInconsistentMerge(string_of_list lst1,string_of_list lst2)) + | PoPlural(_,str1,_), PoPlural(_,str2,_) -> + raise (PoInconsistentMerge(String.concat "" str1, String.concat "" str2)) + | PoSingular(_,str), PoPlural(_,str_plural,lst) + | PoPlural(_,str_plural,lst), PoSingular(_,str) -> + ( + match lst with + x :: tl when (String.concat "" x) = "" -> + PoPlural(str_id, str_plural, str :: tl) + | [] -> + PoPlural(str_id, str_plural, [ str ]) + | _ -> + raise (PoInconsistentMerge(String.concat "" str, string_of_list lst)) + ) + in + (* TODO: merge po_comment_special and use fuzzy when merging *) + { + po_comment_special = previous_commented_translation.po_comment_special @ commented_translation.po_comment_special; + po_comment_filepos = location_lst @ previous_location_lst; + po_comment_translation = merged_translation; + } + with Not_found -> + commented_translation + in + MapString.add (String.concat "" str_id) new_commented_translation map +;; + +let add_po_translation_no_domain po po_translation = + { + po with no_domain = + add_po_translation_aux po.no_domain po_translation + } +;; + +let add_po_translation_domain domain po po_translation = + { + po with domain = + let map_domain = + try + MapTextdomain.find domain po.domain + with Not_found -> + MapString.empty + in + let map_domain = + add_po_translation_aux map_domain po_translation + in + MapTextdomain.add domain map_domain po.domain + } +;; diff --git a/libgettext-ocaml/gettextRealize.ml b/libgettext-ocaml/gettextRealize.ml new file mode 100644 index 0000000..e85f885 --- /dev/null +++ b/libgettext-ocaml/gettextRealize.ml @@ -0,0 +1,120 @@ +(**************************************************************************) +(* ocaml-gettext: a library to translate messages *) +(* *) +(* Copyright (C) 2003-2008 Sylvain Le Gall *) +(* *) +(* This library is free software; you can redistribute it and/or *) +(* modify it under the terms of the GNU Lesser General Public *) +(* License as published by the Free Software Foundation; either *) +(* version 2.1 of the License, or (at your option) any later version; *) +(* with the OCaml static compilation exception. *) +(* *) +(* This library 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 *) +(* Lesser General Public License for more details. *) +(* *) +(* You should have received a copy of the GNU Lesser General Public *) +(* License along with this library; if not, write to the Free Software *) +(* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *) +(* USA *) +(**************************************************************************) + +(** Module type for the function realize. + @author Sylvain Le Gall + *) + +open GettextTypes;; +open GettextUtils;; +open GettextCategory;; + +module Generic : ( + functor (Translate: GettextTranslate.TRANSLATE_TYPE) -> + functor (Charset: GettextCharset.CHARSET_TYPE) -> + functor (Locale: GettextLocale.LOCALE_TYPE) -> REALIZE_TYPE) = + functor (Translate: GettextTranslate.TRANSLATE_TYPE) -> + functor (Charset: GettextCharset.CHARSET_TYPE) -> + functor (Locale: GettextLocale.LOCALE_TYPE) -> + struct + + module MapTranslate = Map.Make(struct + type t = textdomain * category + let compare (t1,c1) (t2,c2) = + match String.compare t1 t2 with + 0 -> GettextCategory.compare c1 c2 + | x -> x + end) + + let add_textdomain_category t map_translate textdomain category = + try + let filename = + GettextDomain.find + t + (fst (Locale.get_locale t category)) + category + textdomain + in + let in_enc = + let chn = + open_in_bin filename + in + let mo_header = GettextMo.input_mo_header chn + in + let mo_informations = GettextMo.input_mo_informations t.failsafe chn mo_header + in + close_in chn; + mo_informations.content_type_charset + in + let out_enc = + try + match (MapTextdomain.find textdomain t.textdomains) with + (Some codeset, _) -> codeset + | (None, _) -> snd (Locale.get_locale t category) + with Not_found -> + snd (Locale.get_locale t category) + in + let recode = + Charset.recode (Charset.create t in_enc out_enc) + in + MapTranslate.add + (textdomain,category) + (Translate.create t filename recode) + map_translate + with DomainFileDoesntExist(filenames) -> + map_translate + + let add_textdomain t map_translate textdomain = + List.fold_left ( + fun m category -> + add_textdomain_category t m textdomain category + ) map_translate GettextCategory.categories + + let realize t = + let map_translate = + MapTextdomain.fold ( + fun textdomain _ m -> + add_textdomain t m textdomain + ) t.textdomains MapTranslate.empty + in + let dummy_translate = + GettextTranslate.Dummy.create t "(none)" ( fun s -> s ) + in + fun printf_format opt str plural_form category -> + ( + let textdomain = + match opt with + Some textdomain -> textdomain + | None -> t.default + in + try + Translate.translate ( + MapTranslate.find (textdomain,category) map_translate + ) printf_format str plural_form + with Not_found -> + GettextTranslate.Dummy.translate + dummy_translate + printf_format str plural_form + ) + end +;; + diff --git a/libgettext-ocaml/gettextTranslate.ml b/libgettext-ocaml/gettextTranslate.ml new file mode 100644 index 0000000..f624482 --- /dev/null +++ b/libgettext-ocaml/gettextTranslate.ml @@ -0,0 +1,298 @@ +(**************************************************************************) +(* ocaml-gettext: a library to translate messages *) +(* *) +(* Copyright (C) 2003-2008 Sylvain Le Gall *) +(* *) +(* This library is free software; you can redistribute it and/or *) +(* modify it under the terms of the GNU Lesser General Public *) +(* License as published by the Free Software Foundation; either *) +(* version 2.1 of the License, or (at your option) any later version; *) +(* with the OCaml static compilation exception. *) +(* *) +(* This library 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 *) +(* Lesser General Public License for more details. *) +(* *) +(* You should have received a copy of the GNU Lesser General Public *) +(* License along with this library; if not, write to the Free Software *) +(* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *) +(* USA *) +(**************************************************************************) + +(** Signature of module for translation storage / access. + @author Sylvain Le Gall + *) + +open GettextTypes;; +open GettextUtils;; +open GettextMo;; +open GettextFormat;; + +module type TRANSLATE_TYPE = + sig + type u + + (** create t filename recode : Create a translation + table using filename as the mo file and recode as the encoding + converter. + *) + val create : t -> filename -> (string -> string) -> u + + (* BUG : need update *) + (** translate str (plural_form,number) tbl : translate the string + str using tbl. It is possible that the operation modify tbl, + so it is returned also. It is also possible to get the plural + form of the translated string using plural_form and number. + *) + val translate : + u + -> bool + -> string + -> (string * int) option + -> string + end +;; + +module Dummy : TRANSLATE_TYPE = + struct + type u = string -> string + + let create t filename charset = charset + + let translate charset printf_format str plural_form = + match plural_form with + None -> + charset str + | Some(str_plural,x) -> + let check = + if printf_format then + check_format Ignore + else + fun x -> x + in + charset (get_translated_value Ignore (check (Plural(str,str_plural,[]))) + (germanic_plural x)) + end +;; + +module Map : TRANSLATE_TYPE = + struct + + type u = { + dummy : Dummy.u; + map : translation MapString.t; + failsafe : failsafe; + fun_plural_forms : int -> int; + } + + let create t filename charset = + let (map,fun_plural_forms) = + fold_mo + t.GettextTypes.failsafe + ( fun translation accu -> + match translation with + Singular(str_id, str) -> + MapString.add str_id + (Singular(str_id,charset str)) + accu + | Plural(str_id,str_plural,lst) -> + MapString.add str_id + (Plural(str_id,str_plural,List.map charset lst)) + accu + ) + MapString.empty + filename + in + { + dummy = Dummy.create t filename charset; + map = map; + failsafe = t.GettextTypes.failsafe; + fun_plural_forms = fun_plural_forms; + } + + let translate u printf_format str plural_form = + try + let plural_number = + u.fun_plural_forms ( + match plural_form with + Some(_,x) -> x + | None -> 0 + ) + in + let check = + if printf_format then + check_format u.failsafe + else + fun x -> x + in + get_translated_value u.failsafe (check (MapString.find str u.map)) plural_number + with Not_found -> + fail_or_continue u.failsafe + (TranslateStringNotFound str) + (Dummy.translate u.dummy printf_format str plural_form) + end +;; + +module Hashtbl : TRANSLATE_TYPE = + struct + + type u = { + dummy : Dummy.u; + hashtbl : (string,translation) Hashtbl.t; + failsafe : failsafe; + fun_plural_forms : int -> int; + } + + let create t filename charset = + let (hashtbl,fun_plural_forms) = + fold_mo + t.GettextTypes.failsafe + ( fun translation accu -> + match translation with + Singular(str_id, str) -> + ( + Hashtbl.add accu str_id + (Singular(str_id,charset str)); + accu + ) + | Plural(str_id,str_plural,lst) -> + ( + Hashtbl.add accu str_id + (Plural(str_id,str_plural,List.map charset lst)); + accu + ) + ) + (* 32 is only a guest on the number of string contains in the + future table *) + (Hashtbl.create 32) + filename + in + { + dummy = Dummy.create t filename charset; + hashtbl = hashtbl; + failsafe = t.GettextTypes.failsafe; + fun_plural_forms = fun_plural_forms; + } + + let translate u printf_format str plural_form = + try + let plural_number = + u.fun_plural_forms ( + match plural_form with + Some(_,x) -> x + | None -> 0 + ) + in + let check = + if printf_format then + check_format u.failsafe + else + fun x -> x + in + get_translated_value u.failsafe (check (Hashtbl.find u.hashtbl str)) plural_number + with Not_found -> + fail_or_continue u.failsafe + (TranslateStringNotFound str) + (Dummy.translate u.dummy printf_format str plural_form) + end +;; + +module Open : TRANSLATE_TYPE = + struct + + type u = { + dummy : Dummy.u; + filename : filename; + charset : string -> string; + failsafe : failsafe; + fun_plural_forms : int -> int; + number_of_strings : int; + } + + let create t filename charset = + (* Processing of the file *) + let chn = + open_in_bin filename + in + let header = + input_mo_header chn + in + let informations = + input_mo_informations t.GettextTypes.failsafe chn header + in + close_in chn; + { + dummy = Dummy.create t filename charset; + filename = filename; + charset = charset; + failsafe = t.GettextTypes.failsafe; + fun_plural_forms = informations.GettextTypes.fun_plural_forms; + number_of_strings = Int32.to_int header.GettextTypes.number_of_strings; + } + + let translate u printf_format str plural_form = + let chn = + open_in_bin u.filename + in + let res = + try + let plural_number = + u.fun_plural_forms ( + match plural_form with + Some(_,x) -> x + | None -> 0 + ) + in + let header = + input_mo_header chn + in + let rec find_str_id (start_index,end_index) = + let middle_index = + (start_index + end_index) / 2 + in + let str_id = + let lst_str_id = + input_mo_untranslated u.failsafe chn header middle_index + in + match lst_str_id with + str_id :: _ -> + str_id + | [] -> + (* BUG : should be a real exception *) + raise Not_found + in + match String.compare str str_id with + x when x < 0 && start_index <= middle_index - 1 -> + find_str_id (start_index,middle_index - 1) + | x when x > 0 && middle_index + 1 <= end_index-> + find_str_id (middle_index + 1,end_index) + | x when x = 0 -> + middle_index + | _ -> + raise Not_found + in + let translation = + let translation = + input_mo_translation + u.failsafe + chn + header + (find_str_id (0,u.number_of_strings-1)) + in + match translation with + Singular(str_id, str) -> + Singular(str_id, u.charset str) + | Plural(str_id,str_plural,lst) -> + Plural(str_id, str_plural, List.map u.charset lst) + in + get_translated_value u.failsafe translation plural_number + with Not_found -> + fail_or_continue u.failsafe + (TranslateStringNotFound str) + (Dummy.translate u.dummy printf_format str plural_form) + in + close_in chn; + res + end +;; diff --git a/libgettext-ocaml/gettextTypes.ml b/libgettext-ocaml/gettextTypes.ml new file mode 100644 index 0000000..4a629cd --- /dev/null +++ b/libgettext-ocaml/gettextTypes.ml @@ -0,0 +1,362 @@ +(**************************************************************************) +(* ocaml-gettext: a library to translate messages *) +(* *) +(* Copyright (C) 2003-2008 Sylvain Le Gall *) +(* *) +(* This library is free software; you can redistribute it and/or *) +(* modify it under the terms of the GNU Lesser General Public *) +(* License as published by the Free Software Foundation; either *) +(* version 2.1 of the License, or (at your option) any later version; *) +(* with the OCaml static compilation exception. *) +(* *) +(* This library 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 *) +(* Lesser General Public License for more details. *) +(* *) +(* You should have received a copy of the GNU Lesser General Public *) +(* License along with this library; if not, write to the Free Software *) +(* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *) +(* USA *) +(**************************************************************************) + +(** Types and exception of ocaml-gettext. + @author Sylvain Le Gall + *) + +open GettextCategory;; + +(** {1 Core types of ocaml-gettext library} *) + +type range = Int32.t * Int32.t +;; + +type textdomain = string +;; + +type locale = string +;; + +type dir = string +;; + +type filename = string +;; + +type codeset = string +;; + +(** {1 Exceptions} *) + +(** Filename wich generates the error message str + *) +exception CompileProblemReadingFile of filename * string;; +(** While extracting filename the command str returns exit code i. + *) +exception CompileExtractionFailed of filename * string * int;; +(** While extracting filename the command receive signal i. + *) +exception CompileExtractionInterrupted of filename * string * int;; +(** Cannot the filename corresponding to a textdomain among the specified files. + *) +exception DomainFileDoesntExist of filename list;; +(** The two strings returned doesn't have the same meaning regarding [Printf] + syntax. + *) +exception FormatInconsistent of string * string;; +(** A part of the code try to translate a string, but ocaml-gettext is not + initialized. + *) +exception GettextUninitialized;; +(** There is an invalid field in the content information of a MO file. + *) +exception MoInvalidOptions of Lexing.lexbuf * string;; +(** The plural-form field is not correct. + *) +exception MoInvalidPlurals of Lexing.lexbuf * string;; +(** The content-type field is not correct. + *) +exception MoInvalidContentType of Lexing.lexbuf * string;; +(** A plural translation of a singular string has occured. + *) +exception MoInvalidTranslationSingular of string * int;; +(** An out-of-bound plural translation has occured. + *) +exception MoInvalidTranslationPlural of (string list) * int;; +(** There is more plural translation than the number of plural forms. + *) +exception MoJunk of string * string list;; +(** + *) +exception MoEmptyEntry;; +(** A MO corrupted file has been read. + *) +exception MoInvalidFile;; +(** The MO file specified a negative number of strings. + *) +exception MoInvalidHeaderNegativeStrings;; +(** Offset of the string table is out of bound. + *) +exception MoInvalidHeaderTableStringOutOfBound of range * range;; +(** Offset of the translation table is out of bound. + *) +exception MoInvalidHeaderTableTranslationOutOfBound of range * range;; +(** String and translation table overlap. + *) +exception MoInvalidHeaderTableTranslationStringOverlap of range * range;; +(** The offset and length of a string entry leads to an access beyond the end + of the MO file. + *) +exception MoInvalidStringOutOfBound of int * int;; +(** The offset and length of a translation entry leads to an access beyond the end + of the MO file. + *) +exception MoInvalidTranslationOutOfBound of int * int;; +(** An error occured when trying to open a MO file. + *) +exception MoCannotOpenFile of string;; +(** A PO file cannot be parsed. + *) +exception PoInvalidFile of string * Lexing.lexbuf * in_channel;; +(** When parsing a PO file, found an out of order table indices in a plural + form. + *) +exception PoFileInvalidIndex of string * int;; +(** The PO file doesn't exist. + *) +exception PoFileDoesntExist of string;; +(** Cannot merge two PO files. + *) +exception PoInconsistentMerge of string * string;; +(** A string to translate cannot be found. + *) +exception TranslateStringNotFound of string;; +(** Cannot parse the POSIX representation of the locale. + *) +exception LocalePosixUnparseable of string;; + +(** {1 Modules signatures} *) + +type dependencies = (textdomain * (codeset option) * (dir option)) list +;; + +module type INIT_TYPE = + sig + val textdomain : textdomain + val codeset : codeset option + val dir : dir option + val dependencies : dependencies + end +;; + +(* We stop documentation here, for the gettext API reference, all those types + are internals : use at your own risk. + *) +(**/**) + +(** {1 Extended core types} *) + +module MapString = Map.Make(String);; +module SetString = Set.Make(String);; + +module MapTextdomain = Map.Make (struct + type t = textdomain + let compare = String.compare +end) +;; + +(** Defines behavior regarding exception in the ocaml-gettext library + *) +type failsafe = + Ignore + | InformStderr of (exn -> string) + | RaiseException +;; + +(** Data structure handling initialization variable of ocaml-gettext + *) +type t = { + failsafe : failsafe; + textdomains : ((codeset option) * (dir option)) MapTextdomain.t; + categories : locale MapCategory.t; + language : locale option; + codeset : codeset; + path : dir list; + default : textdomain; +} +;; + +(** Function to translate effectively a string + *) +type t' = bool -> textdomain option -> string -> (string * int) option -> category -> string +;; + +(** {1 Types for MO file processing} *) + +(** Endianess of a MO file + *) +type endianess = + BigEndian + | LittleEndian +;; + +(** Specification of .MO file *) +(** + @see GNU Gettext documentation. + + Format of MO file : + + byte + +------------------------------------------+ + 0 | magic number = 0x950412de | + | | + 4 | file format revision = 0 | + | | + 8 | number of strings | == N + | | + 12 | offset of table with original strings | == O + | | + 16 | offset of table with translation strings | == T + | | + 20 | size of hashing table | == S + | | + 24 | offset of hashing table | == H + | | + . . + . (possibly more entries later) . + . . + | | + O | length & offset 0th string ----------------. + O + 8 | length & offset 1st string ------------------. + ... ... | | +O + ((N-1)*8)| length & offset (N-1)th string | | | + | | | | + T | length & offset 0th translation ---------------. + T + 8 | length & offset 1st translation -----------------. + ... ... | | | | +T + ((N-1)*8)| length & offset (N-1)th translation | | | | | + | | | | | | + H | start hash table | | | | | + ... ... | | | | + H + S * 4 | end hash table | | | | | + | | | | | | + | NUL terminated 0th string <----------------' | | | + | | | | | + | NUL terminated 1st string <------------------' | | + | | | | + ... ... | | + | | | | + | NUL terminated 0th translation <---------------' | + | | | + | NUL terminated 1st translation <-----------------' + | | + ... ... + | | + +------------------------------------------+ + +*) +type mo_header = { + endianess : endianess; + file_format_revision : int32; + number_of_strings : int32; + offset_table_strings : int32; + offset_table_translation : int32; + size_of_hashing_table : int32; + offset_of_hashing_table : int32; +} +;; + +(** Details associated with "" + Project-Id-Version: PACKAGE VERSION\n + Report-Msgid-Bugs-To: \n + POT-Creation-Date: 2004-05-31 16:53+0200\n + PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n + Last-Translator: FULL NAME \n + Language-Team: LANGUAGE \n + MIME-Version: 1.0\n + Content-Type: text/plain; charset=CHARSET\n + Content-Transfer-Encoding: 8bit\n + Plural-Forms: specific ( 0 is false and 1 is + true + *) +type mo_translation = { + project_id_version : string option; + report_msgid_bugs_to : string option; + pot_creation_date : string option; + po_revision_date : string option; + last_translator : string option; + language_tream : string option; + mime_version : string option; + content_type : string option; + content_transfer_encoding : string option; + plural_forms : string option; + (* The only interesting fields *) + (* Those field are precomputed for regular use *) + content_type_charset : string; + nplurals : int; + fun_plural_forms : int -> int; +} +;; + +(** Base type of MO content : translation of string. The first string members are + the string identifier ( singular form ). +*) +type translation = + Singular of string * string +| Plural of string * string * string list +;; + +(** Types for the PO processing. The main difference with the type translation + comes from the necessity of keeping a maximum of comment. +*) +type po_translation = + PoSingular of (string list) * ( string list ) +| PoPlural of (string list) * ( string list ) * ( string list ) list +;; + +(** PO string localizator : represents in which file/lineno a string can be + found + *) +type po_filepos = filename * int +;; + +(** PO keyword: represents special keyword like fuzzy, wrap, c-format... + *) +type po_special = string +;; + +type po_commented_translation = + { + po_comment_special: po_special list; + po_comment_filepos: po_filepos list; + po_comment_translation: po_translation; + } +;; + +(** Mapping of PO content using the string identifier as the key. +*) +type po_translations = po_commented_translation MapString.t +;; + +(** Content of a PO file. Since comments should be saved, and that we only save + comments before and in message translation, we need to keep trace of the + last comments, which is not attached to any translation +*) +type po_content = { + no_domain : po_translations; + domain : po_translations MapTextdomain.t; +} +;; + + +(** {1 Modules signatures} *) + +(** Signature for module handling transformation of initialization parameters + to concrete translation function. + *) +module type REALIZE_TYPE = + sig + val realize : t -> t' + end +;; + diff --git a/libgettext-ocaml/gettextUtils.ml b/libgettext-ocaml/gettextUtils.ml new file mode 100644 index 0000000..f62c8c0 --- /dev/null +++ b/libgettext-ocaml/gettextUtils.ml @@ -0,0 +1,65 @@ +(**************************************************************************) +(* ocaml-gettext: a library to translate messages *) +(* *) +(* Copyright (C) 2003-2008 Sylvain Le Gall *) +(* *) +(* This library is free software; you can redistribute it and/or *) +(* modify it under the terms of the GNU Lesser General Public *) +(* License as published by the Free Software Foundation; either *) +(* version 2.1 of the License, or (at your option) any later version; *) +(* with the OCaml static compilation exception. *) +(* *) +(* This library 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 *) +(* Lesser General Public License for more details. *) +(* *) +(* You should have received a copy of the GNU Lesser General Public *) +(* License along with this library; if not, write to the Free Software *) +(* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *) +(* USA *) +(**************************************************************************) + +(** Misc. utilities. + @author Sylvain Le Gall + *) + +open GettextTypes;; + +let string_of_list lst = + "[ "^(String.concat "; " (List.map (fun str -> Printf.sprintf "%S" str) lst))^" ]" +;; + +let split_plural str = + let rec split_plural_one start = + let next_sep = + try + String.index_from str start '\000' + with Not_found -> + String.length str + in + let new_plural = + String.sub str start (next_sep - start) + in + if (next_sep + 1) >= String.length str then + [new_plural] + else + new_plural :: (split_plural_one (next_sep + 1)) + in + split_plural_one 0 +;; + +let fail_or_continue failsafe exc cont_value = + match failsafe with + Ignore -> + cont_value + | InformStderr exc_printer -> + ( + prerr_string (exc_printer exc); + prerr_newline (); + cont_value + ) + | RaiseException -> + raise exc +;; + diff --git a/libgettext-ocaml/pr_gettext.ml b/libgettext-ocaml/pr_gettext.ml new file mode 100644 index 0000000..c44933f --- /dev/null +++ b/libgettext-ocaml/pr_gettext.ml @@ -0,0 +1,263 @@ +(**************************************************************************) +(* ocaml-gettext: a library to translate messages *) +(* *) +(* Copyright (C) 2003-2008 Sylvain Le Gall *) +(* *) +(* This library is free software; you can redistribute it and/or *) +(* modify it under the terms of the GNU Lesser General Public *) +(* License as published by the Free Software Foundation; either *) +(* version 2.1 of the License, or (at your option) any later version; *) +(* with the OCaml static compilation exception. *) +(* *) +(* This library 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 *) +(* Lesser General Public License for more details. *) +(* *) +(* You should have received a copy of the GNU Lesser General Public *) +(* License along with this library; if not, write to the Free Software *) +(* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *) +(* USA *) +(**************************************************************************) + +(** Camlp4 dumper to extract strings. + @author Sylvain Le Gall + @author Richard W.M. Jones (translation to OCaml 3.10.X new camlp4) + *) + +(* Extract the string which should be used for a gettext translation. Output a + po_content list through the function Marshal.to_channel + Functions that are looked for : +Functions Arg 1 Arg 2 Arg 3 Arg 4 Arg 5 Arg 6 ... +s_ singular +f_ singular +sn_ singular plural _ +fn_ singular plural _ +gettext _ singular +fgettext _ singular +dgettext _ domain singular +fdgettext _ domain singular +dcgettext _ domain singular _ +fdcgettext _ domain singular _ +ngettext _ singular plural _ +fngettext _ singular plural _ +dngettext _ domain singular plural _ +fdngettext _ domain singular plural _ +dcngettext _ domain singular plural _ _ +fdcngettext _ domain singular plural _ _ + + +All this function name should also be matched when they are called using a +module. + +*) + +open Format +open GettextTypes +open GettextPo + +let default_textdomain = ref None + +module Id = struct + (* name is printed with the -loaded-modules switch *) + let name = "pr_gettext" + (* cvs id's seem to be the preferred version string *) + let version = "$Id$" +end + +module Make (Syntax : Camlp4.Sig.Camlp4Syntax) + : Camlp4.Sig.Printer(Syntax.Ast).S = +struct + module Loc = Syntax.Loc + module Ast = Syntax.Ast + + type untranslated_t = + { + str: string; (* Real string, not OCaml one *) + locations: (string * int) list; (* Location in the file *) + } + + type t = + { + po_content: po_content; + translated: SetString.t; + untranslated: untranslated_t MapString.t; + } + + let string_of_ocaml_string str = + prerr_endline str; + Scanf.sscanf + (Printf.sprintf "\"%s\"" str) + "%S" + (fun s -> s) + + let string_not_translated t ocaml_str = + not (SetString.mem ocaml_str t.translated) + + let add_untranslated t loc ocaml_str = + let cur = + try + MapString.find ocaml_str t.untranslated + with Not_found -> + { + str = string_of_ocaml_string ocaml_str; + locations = []; + } + in + let untranslated = + MapString.add + ocaml_str + {cur with + locations = + (Loc.file_name loc, Loc.start_line loc) :: cur.locations} + t.untranslated + in + {t with untranslated = untranslated} + + + let add_translation t loc ocaml_singular plural_opt domain = + let filepos = + Loc.file_name loc, Loc.start_line loc + in + let singular = + string_of_ocaml_string ocaml_singular + in + let translated = + SetString.add ocaml_singular t.translated + in + let translated, translation = + match plural_opt with + | Some ocaml_plural -> + let plural = + string_of_ocaml_string ocaml_plural + in + SetString.add ocaml_plural translated, + { + po_comment_special = []; + po_comment_filepos = [filepos]; + po_comment_translation = PoPlural([singular],[plural],[[""];[""]]); + } + | None -> + translated, + { + po_comment_special = []; + po_comment_filepos = [filepos]; + po_comment_translation = PoSingular([singular],[""]); + } + in + let po_content = + match domain, !default_textdomain with + | Some domain, _ -> + add_po_translation_domain domain t.po_content translation + | None, Some domain -> + add_po_translation_domain domain t.po_content translation + | None, None -> + add_po_translation_no_domain t.po_content translation + in + {t with + po_content = po_content; + translated = translated} + + let output_translations ?output_file t = + let fd = + match output_file with + | Some f -> open_out f + | None -> stdout + in + MapString.iter + (fun _ {str = str; locations = locs} -> + List.iter + (fun (fn, lineno) -> + Printf.eprintf + "%s:%d String %S not translated\n%!" + fn lineno str) + locs) + t.untranslated; + Marshal.to_channel fd t.po_content [] + + (* Check if the given node belong to the given functions *) + let is_like e functions = + let rec function_name e = + match e with + | <:ident<$_$.$id:e$>> -> + function_name e + | <:ident<$lid:s$>> -> + s + | _ -> + raise Not_found + in + try + List.mem (function_name e) functions + with Not_found -> + false + + class visitor = object + inherit Ast.fold as super + + val t = + { + po_content = empty_po; + untranslated = MapString.empty; + translated = SetString.empty; + } + + method t = t + + method expr = function + | <:expr@loc< $id:e$ $str:singular$ >> when + is_like e ["s_"; "f_"] -> + (* Add a singular / default domain string *) + {< t = add_translation t loc singular None None >} + + | <:expr@loc< $id:e$ $str:singular$ $str:plural$ >> when + is_like e ["sn_"; "fn_"] -> + (* Add a plural / default domain string *) + {< t = add_translation t loc singular (Some plural) None >} + + | <:expr@loc< $id:e$ $expr$ $str:singular$ >> when + is_like e ["gettext"; "fgettext"] -> + (* Add a singular / default domain string *) + {< t = add_translation t loc singular None None >} + + | <:expr@loc< $id:e$ $expr$ $str:domain$ $str:singular$ >> when + is_like e ["dgettext"; "fdgettext"; "dcgettext"; "fdcgettext"] -> + (* Add a singular / defined domain string *) + {< t = add_translation t loc singular None (Some domain) >} + + | <:expr@loc< $id:e$ $expr$ $str:singular$ $str:plural$ >> when + is_like e ["ngettext"; "fngettext"] -> + (* Add a plural / default domain string *) + {< t = add_translation t loc singular (Some plural) None >} + + | <:expr@loc< $id:e$ $expr$ $str:domain$ $str:singular$ $str:plural$ >> when + is_like e ["dngettext"; "fdngettext"; "dcngettext"; "fdcngettext"] -> + (* Add a plural / defined domain string *) + {< t = add_translation t loc singular (Some plural) (Some domain) >} + + | <:expr@loc<$str:str$>> when + string_not_translated t str -> + {< t = add_untranslated t loc str >} + + | e -> super#expr e + + end + + (* Called on *.mli files, but cannot contain translateable strings. *) + let print_interf ?input_file ?output_file _ = () + + (* Called on *.ml files. *) + let print_implem ?input_file ?output_file ast = + let visitor = (new visitor)#str_item in + let t = (visitor ast)#t in + output_translations ?output_file t +end + +(* Register the new printer. *) +module M = Camlp4.Register.OCamlPrinter(Id)(Make) ;; + +(* XXX How to do this? +Pcaml.add_option "-default-textdomain" + (Arg.String ( fun textdomain -> default_textdomain := Some textdomain ) ) + " Defines the default textdomain" +;; +*) diff --git a/libgettext-stub-ocaml/META b/libgettext-stub-ocaml/META new file mode 100644 index 0000000..cfb39f3 --- /dev/null +++ b/libgettext-stub-ocaml/META @@ -0,0 +1,27 @@ +########################################################################## +# ocaml-gettext: a library to translate messages # +# # +# Copyright (C) 2003-2008 Sylvain Le Gall # +# # +# This library is free software; you can redistribute it and/or # +# modify it under the terms of the GNU Lesser General Public # +# License as published by the Free Software Foundation; either # +# version 2.1 of the License, or (at your option) any later version; # +# with the OCaml static compilation exception. # +# # +# This library 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 # +# Lesser General Public License for more details. # +# # +# You should have received a copy of the GNU Lesser General Public # +# License along with this library; if not, write to the Free Software # +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 # +# USA # +########################################################################## + +version = "@PACKAGE_VERSION@" +requires = "gettext.extension" +archive(byte) = "gettextStub.cma" +archive(native) = "gettextStub.cmxa" + diff --git a/libgettext-stub-ocaml/Makefile b/libgettext-stub-ocaml/Makefile new file mode 100644 index 0000000..52bbdf2 --- /dev/null +++ b/libgettext-stub-ocaml/Makefile @@ -0,0 +1,68 @@ +########################################################################## +# ocaml-gettext: a library to translate messages # +# # +# Copyright (C) 2003-2008 Sylvain Le Gall # +# # +# This library is free software; you can redistribute it and/or # +# modify it under the terms of the GNU Lesser General Public # +# License as published by the Free Software Foundation; either # +# version 2.1 of the License, or (at your option) any later version; # +# with the OCaml static compilation exception. # +# # +# This library 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 # +# Lesser General Public License for more details. # +# # +# You should have received a copy of the GNU Lesser General Public # +# License along with this library; if not, write to the Free Software # +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 # +# USA # +########################################################################## + +include ../ConfMakefile + +ifeq ($(BUILD_STUB),yes) + +NAME = gettext-stub +CMO = +LIBRARY = true +REQUIRES = gettext.extension +PREDICATES = +INSTALLIB = META +INCLUDES = $(STUB_CFLAGS) + +all: + +############################## +# Stub library # +############################## + +CMO_STUB = \ + gettextStubCompat_stubs.o \ + gettextStubCompat.cmo \ + gettextStub.cmo + +INSTALLIB += \ + gettextStub.cmxa \ + gettextStub.cma \ + gettextStub.a \ + dllgettextStub.so \ + libgettextStub.a \ + gettextStub.cmi \ + gettextStubCompat.cmi + +INSTALLIB += $(CMO_STUB:.cmo=.cmx) + +gettextStub.cma: $(CMO_STUB) +gettextStub.cmxa: $(CMO_STUB:.cmo=.cmx) +gettextStub.cma gettextStub.cmxa: STUBSOBJS = yes +gettextStub.cma gettextStub.cmxa: OCAMLMKLIB_FLAGS += $(STUB_LDFLAGS) + +clean:: + -$(RM) gettextStub.mli + -$(RM) gettextStubCompat.mli + +endif + +include ../TopMakefile diff --git a/libgettext-stub-ocaml/gettextStub.ml b/libgettext-stub-ocaml/gettextStub.ml new file mode 100644 index 0000000..03ac2a5 --- /dev/null +++ b/libgettext-stub-ocaml/gettextStub.ml @@ -0,0 +1,177 @@ +(**************************************************************************) +(* ocaml-gettext: a library to translate messages *) +(* *) +(* Copyright (C) 2003-2008 Sylvain Le Gall *) +(* *) +(* This library is free software; you can redistribute it and/or *) +(* modify it under the terms of the GNU Lesser General Public *) +(* License as published by the Free Software Foundation; either *) +(* version 2.1 of the License, or (at your option) any later version; *) +(* with the OCaml static compilation exception. *) +(* *) +(* This library 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 *) +(* Lesser General Public License for more details. *) +(* *) +(* You should have received a copy of the GNU Lesser General Public *) +(* License along with this library; if not, write to the Free Software *) +(* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *) +(* USA *) +(**************************************************************************) + +(** Concrete implementation based on native gettext library. + @see Gettext library + @author Sylvain Le Gall +*) + +(** {1 Concrete implementations} *) + +open GettextTypes;; +open GettextUtils;; +open GettextCategory;; + +(** Native implementation of gettext. All translation are bound to C library + call. Still use check_format, to ensure that strings follow printf format. + *) +module Native : GettextTypes.REALIZE_TYPE = + struct + (**/**) + + let realize t = + (* Here we do the binding between C library call and the information we + have in parameter t. *) + let native_category_of_category cat = + match cat with + GettextCategory.LC_CTYPE -> GettextStubCompat.LC_CTYPE + | GettextCategory.LC_NUMERIC -> GettextStubCompat.LC_NUMERIC + | GettextCategory.LC_TIME -> GettextStubCompat.LC_TIME + | GettextCategory.LC_COLLATE -> GettextStubCompat.LC_COLLATE + | GettextCategory.LC_MONETARY -> GettextStubCompat.LC_MONETARY + | GettextCategory.LC_MESSAGES -> GettextStubCompat.LC_MESSAGES + | GettextCategory.LC_ALL -> GettextStubCompat.LC_ALL + in + let default_dir = + match t.path with + default_dir :: _ -> + Some default_dir + | [] -> + None + in + let bind_textdomain_one textdomain (codeset_opt,dir_opt) = + ( + let codeset = + match codeset_opt with + Some codeset -> + codeset + | None -> + t.codeset + in + ignore(GettextStubCompat.bind_textdomain_codeset textdomain codeset) + ); + ( + match dir_opt with + | Some dir -> + ignore(GettextStubCompat.bindtextdomain textdomain dir) + | None -> + ( + match default_dir with + | Some dir -> + ignore(GettextStubCompat.bindtextdomain textdomain dir) + | None -> + () + ) + ) + in + (* We only use the first path of t.path, since there is no notion of search path + in native gettext. So the MO file should be in : + - first component of t.path, + - directory pointed by bindtextdomain, + - default directory of gettext. + *) + let _ = + GettextStubCompat.textdomain t.default + in + let _ = + match t.language with + Some language -> + ( + try + GettextStubCompat.setlocale GettextStubCompat.LC_ALL language + with Failure("setlocale(invalid localization)") as exc -> + let () = + fail_or_continue t.failsafe exc () + in + GettextStubCompat.setlocale GettextStubCompat.LC_ALL "" + ) + | None -> + GettextStubCompat.setlocale GettextStubCompat.LC_ALL "" + in + let () = + MapCategory.iter + (fun cat locale -> + ignore(GettextStubCompat.setlocale + (native_category_of_category cat) locale)) + t.categories + in + let () = + MapTextdomain.iter bind_textdomain_one t.textdomains + in + fun printf_format textdomain_opt str_id str_plural_opt cat -> + let check x = + if printf_format then + match GettextFormat.check_format t.failsafe (Singular(str_id,x)) with + | Singular(_, str) -> str + | _ -> str_id + else + x + in + let ncat = + native_category_of_category cat + in + let textdomain = + match textdomain_opt with + Some textdomain -> + textdomain + | None -> + t.default + in + let translation = + match str_plural_opt with + Some(str_plural,n) -> + GettextStubCompat.dcngettext textdomain str_id str_plural n ncat + | None -> + GettextStubCompat.dcgettext textdomain str_id ncat + in + check translation + + end +;; + +(** Native implementation of gettext. Use the Native module, but use + informations provided to preload all textdomain translation. The preload + is made by trying to translate the string "", which is mandatory in MO file. + This is not the default behavior of gettext. Use this module if you know + that it is better to preload all string. Don't use this module if you think + you will only have a few strings to translate. + *) +module Preload : GettextTypes.REALIZE_TYPE = + struct + (**/**) + + let realize t = + let t' = Native.realize t + in + let () = + MapTextdomain.iter + (fun textdomain _ -> + (* We only load LC_MESSAGES, since it is what is mainly use with + * gettext. Anyway, this is just a local optimization... + *) + ignore(t' false (Some textdomain) "" None LC_MESSAGES)) + t.textdomains + in + t' + + end +;; diff --git a/libgettext-stub-ocaml/gettextStubCompat.ml b/libgettext-stub-ocaml/gettextStubCompat.ml new file mode 100644 index 0000000..1efddbd --- /dev/null +++ b/libgettext-stub-ocaml/gettextStubCompat.ml @@ -0,0 +1,112 @@ +(**************************************************************************) +(* ocaml-gettext: a library to translate messages *) +(* *) +(* Copyright (C) 2003-2008 Sylvain Le Gall *) +(* *) +(* This library is free software; you can redistribute it and/or *) +(* modify it under the terms of the GNU Lesser General Public *) +(* License as published by the Free Software Foundation; either *) +(* version 2.1 of the License, or (at your option) any later version; *) +(* with the OCaml static compilation exception. *) +(* *) +(* This library 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 *) +(* Lesser General Public License for more details. *) +(* *) +(* You should have received a copy of the GNU Lesser General Public *) +(* License along with this library; if not, write to the Free Software *) +(* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *) +(* USA *) +(**************************************************************************) + + +(** Low level interface to gettext C library. + * @author Sylvain Le Gall + *) + +type lc = + | LC_CTYPE + | LC_NUMERIC + | LC_TIME + | LC_COLLATE + | LC_MONETARY + | LC_MESSAGES + | LC_ALL +;; + +(** Set the current localization for the category + *) +external setlocale : lc -> string -> string option + = "gettextStubCompat_setlocale" +;; + +(** Look up MSGID in the current default message catalog for the current + * LC_MESSAGES locale. If not found, returns MSGID itself (the default text). + *) +external gettext : string -> string + = "gettextStubCompat_gettext" +;; + +(** Look up MSGID in the DOMAINNAME message catalog for the current LC_MESSAGES + * locale. + *) +external dgettext : string -> string -> string + = "gettextStubCompat_dgettext" +;; + +(** Look up MSGID in the DOMAINNAME message catalog for the current CATEGORY + * locale. + *) +external dcgettext : string -> string -> lc -> string + = "gettextStubCompat_dcgettext" +;; + +(** Similar to `gettext' but select the plural form corresponding to the number + * N. + *) +external ngettext : string -> string -> int -> string + = "gettextStubCompat_ngettext" +;; + +(** Similar to `dgettext' but select the plural form corresponding to the number + * N. + *) +external dngettext : string -> string -> string -> int -> string + = "gettextStubCompat_dngettext" +;; + +(** Similar to `dcgettext' but select the plural form corresponding to the + * number N. + *) +external dcngettext : string -> string -> string -> int -> lc -> string + = "gettextStubCompat_dcngettext" +;; + +(** Set the current default message catalog to DOMAINNAME.If DOMAINNAME is "", + * reset to the default of "messages". + *) +external textdomain : string -> string option + = "gettextStubCompat_textdomain" +;; + +(** Get the current default message catalog to DOMAINNAME. + *) +external get_textdomain : unit -> string option + = "gettextStubCompat_get_textdomain" +;; + +(** Specify that the DOMAINNAME message catalog will be foundin DIRNAME rather + * than in the system locale data base. + *) +external bindtextdomain : string -> string -> string option + = "gettextStubCompat_bindtextdomain" +;; + +(** Specify the character encoding in which the messages from theDOMAINNAME + * message catalog will be returned. + *) +external bind_textdomain_codeset : string -> string -> string option + = "gettextStubCompat_bind_textdomain_codeset" +;; + diff --git a/libgettext-stub-ocaml/gettextStubCompat_stubs.c b/libgettext-stub-ocaml/gettextStubCompat_stubs.c new file mode 100644 index 0000000..0bbac1b --- /dev/null +++ b/libgettext-stub-ocaml/gettextStubCompat_stubs.c @@ -0,0 +1,200 @@ +/**************************************************************************/ +/* ocaml-gettext: a library to translate messages */ +/* */ +/* Copyright (C) 2003-2008 Sylvain Le Gall */ +/* */ +/* This library is free software; you can redistribute it and/or */ +/* modify it under the terms of the GNU Lesser General Public */ +/* License as published by the Free Software Foundation; either */ +/* version 2.1 of the License, or (at your option) any later version; */ +/* with the OCaml static compilation exception. */ +/* */ +/* This library 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 */ +/* Lesser General Public License for more details. */ +/* */ +/* You should have received a copy of the GNU Lesser General Public */ +/* License along with this library; if not, write to the Free Software */ +/* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 */ +/* USA */ +/**************************************************************************/ + +#include +#include +#include +#include +#include + +#define STRINGIFY(x) #x + +/* Make a string option value from a const string which might be NULL. */ +static value return_string_option (const char *str) +{ + CAMLparam0 (); + CAMLlocal2 (rv, strv); + + if (str == NULL) + rv = Val_int (0); /* None */ + else { + strv = caml_copy_string (str); + rv = caml_alloc (1, 0); + Store_field (rv, 0, strv); + } + + CAMLreturn (rv); +} + +int ml2c_lc_tab[7] = { + LC_CTYPE, + LC_NUMERIC, + LC_TIME, + LC_COLLATE, + LC_MONETARY, + LC_MESSAGES, + LC_ALL +}; + +inline int ml2c_lc(value v) +{ + return ml2c_lc_tab[Int_val(v)]; +} + +CAMLprim value gettextStubCompat_setlocale( + value v_n, + value v_val) +{ + CAMLparam2(v_n, v_val); + CAMLreturn( + return_string_option ( + setlocale( + ml2c_lc(v_n), + String_val(v_val)))); +} + +CAMLprim value gettextStubCompat_gettext( + value v_msgid) +{ + CAMLparam1(v_msgid); + CAMLreturn(copy_string(gettext(String_val(v_msgid)))); +} + +CAMLprim value gettextStubCompat_dgettext( + value v_domainname, + value v_msgid) +{ + CAMLparam2(v_domainname, v_msgid); + CAMLreturn( + copy_string( + dgettext( + String_val(v_domainname), + String_val(v_msgid)))); +} + +CAMLprim value gettextStubCompat_dcgettext( + value v_domainname, + value v_msgid, + value v_category) +{ + CAMLparam3(v_domainname, v_msgid, v_category); + CAMLreturn( + copy_string( + dcgettext( + String_val(v_domainname), + String_val(v_msgid), + ml2c_lc(v_category)))); +} + +CAMLprim value gettextStubCompat_ngettext( + value v_msgid1, + value v_msgid2, + value v_n) +{ + CAMLparam3(v_msgid1, v_msgid2, v_n); + CAMLreturn( + copy_string( + ngettext( + String_val(v_msgid1), + String_val(v_msgid2), + Long_val(v_n)))); +} + +CAMLprim value gettextStubCompat_dngettext( + value v_domainname, + value v_msgid1, + value v_msgid2, + value v_n) +{ + CAMLparam4(v_domainname, v_msgid1, v_msgid2, v_n); + CAMLreturn( + copy_string( + dngettext( + String_val(v_domainname), + String_val(v_msgid1), + String_val(v_msgid2), + Long_val(v_n)))); +} + +CAMLprim value gettextStubCompat_dcngettext( + value v_domainname, + value v_msgid1, + value v_msgid2, + value v_n, + value v_category) +{ + + char *res = NULL; + + CAMLparam5(v_domainname, v_msgid1, v_msgid2, v_n, v_category); + res = dcngettext( + String_val(v_domainname), + String_val(v_msgid1), + String_val(v_msgid2), + Long_val(v_n), + ml2c_lc(v_category)); + + if (res == NULL) + { + caml_failwith("NULL string not expected at "STRINGIFY(__LINE__)" in "__FILE__); + }; + + CAMLreturn(copy_string(res)); +} + +CAMLprim value gettextStubCompat_textdomain( + value v_domainname) +{ + CAMLparam1(v_domainname); + CAMLreturn(return_string_option(textdomain(String_val(v_domainname)))); +} + +CAMLprim value gettextStubCompat_get_textdomain(value v_unit) +{ + CAMLparam1(v_unit); + CAMLreturn(return_string_option(textdomain(NULL))); +} + +CAMLprim value gettextStubCompat_bindtextdomain( + value v_domainname, + value v_dirname) +{ + CAMLparam2(v_domainname, v_dirname); + CAMLreturn( + return_string_option ( + bindtextdomain( + String_val(v_domainname), + String_val(v_dirname)))); +} + +CAMLprim value gettextStubCompat_bind_textdomain_codeset( + value v_domainname, + value v_codeset) +{ + CAMLparam2(v_domainname, v_codeset); + CAMLreturn( + return_string_option ( + bind_textdomain_codeset( + String_val(v_domainname), + String_val(v_codeset)))); +} + diff --git a/missing b/missing new file mode 100755 index 0000000..894e786 --- /dev/null +++ b/missing @@ -0,0 +1,360 @@ +#! /bin/sh +# Common stub for a few missing GNU programs while installing. + +scriptversion=2005-06-08.21 + +# Copyright (C) 1996, 1997, 1999, 2000, 2002, 2003, 2004, 2005 +# Free Software Foundation, Inc. +# Originally by Fran,cois Pinard , 1996. + +# 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, 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. + +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +if test $# -eq 0; then + echo 1>&2 "Try \`$0 --help' for more information" + exit 1 +fi + +run=: + +# In the cases where this matters, `missing' is being run in the +# srcdir already. +if test -f configure.ac; then + configure_ac=configure.ac +else + configure_ac=configure.in +fi + +msg="missing on your system" + +case "$1" in +--run) + # Try to run requested program, and just exit if it succeeds. + run= + shift + "$@" && exit 0 + # Exit code 63 means version mismatch. This often happens + # when the user try to use an ancient version of a tool on + # a file that requires a minimum version. In this case we + # we should proceed has if the program had been absent, or + # if --run hadn't been passed. + if test $? = 63; then + run=: + msg="probably too old" + fi + ;; + + -h|--h|--he|--hel|--help) + echo "\ +$0 [OPTION]... PROGRAM [ARGUMENT]... + +Handle \`PROGRAM [ARGUMENT]...' for when PROGRAM is missing, or return an +error status if there is no known handling for PROGRAM. + +Options: + -h, --help display this help and exit + -v, --version output version information and exit + --run try to run the given command, and emulate it if it fails + +Supported PROGRAM values: + aclocal touch file \`aclocal.m4' + autoconf touch file \`configure' + autoheader touch file \`config.h.in' + automake touch all \`Makefile.in' files + bison create \`y.tab.[ch]', if possible, from existing .[ch] + flex create \`lex.yy.c', if possible, from existing .c + help2man touch the output file + lex create \`lex.yy.c', if possible, from existing .c + makeinfo touch the output file + tar try tar, gnutar, gtar, then tar without non-portable flags + yacc create \`y.tab.[ch]', if possible, from existing .[ch] + +Send bug reports to ." + exit $? + ;; + + -v|--v|--ve|--ver|--vers|--versi|--versio|--version) + echo "missing $scriptversion (GNU Automake)" + exit $? + ;; + + -*) + echo 1>&2 "$0: Unknown \`$1' option" + echo 1>&2 "Try \`$0 --help' for more information" + exit 1 + ;; + +esac + +# Now exit if we have it, but it failed. Also exit now if we +# don't have it and --version was passed (most likely to detect +# the program). +case "$1" in + lex|yacc) + # Not GNU programs, they don't have --version. + ;; + + tar) + if test -n "$run"; then + echo 1>&2 "ERROR: \`tar' requires --run" + exit 1 + elif test "x$2" = "x--version" || test "x$2" = "x--help"; then + exit 1 + fi + ;; + + *) + if test -z "$run" && ($1 --version) > /dev/null 2>&1; then + # We have it, but it failed. + exit 1 + elif test "x$2" = "x--version" || test "x$2" = "x--help"; then + # Could not run --version or --help. This is probably someone + # running `$TOOL --version' or `$TOOL --help' to check whether + # $TOOL exists and not knowing $TOOL uses missing. + exit 1 + fi + ;; +esac + +# If it does not exist, or fails to run (possibly an outdated version), +# try to emulate it. +case "$1" in + aclocal*) + echo 1>&2 "\ +WARNING: \`$1' is $msg. You should only need it if + you modified \`acinclude.m4' or \`${configure_ac}'. You might want + to install the \`Automake' and \`Perl' packages. Grab them from + any GNU archive site." + touch aclocal.m4 + ;; + + autoconf) + echo 1>&2 "\ +WARNING: \`$1' is $msg. You should only need it if + you modified \`${configure_ac}'. You might want to install the + \`Autoconf' and \`GNU m4' packages. Grab them from any GNU + archive site." + touch configure + ;; + + autoheader) + echo 1>&2 "\ +WARNING: \`$1' is $msg. You should only need it if + you modified \`acconfig.h' or \`${configure_ac}'. You might want + to install the \`Autoconf' and \`GNU m4' packages. Grab them + from any GNU archive site." + files=`sed -n 's/^[ ]*A[CM]_CONFIG_HEADER(\([^)]*\)).*/\1/p' ${configure_ac}` + test -z "$files" && files="config.h" + touch_files= + for f in $files; do + case "$f" in + *:*) touch_files="$touch_files "`echo "$f" | + sed -e 's/^[^:]*://' -e 's/:.*//'`;; + *) touch_files="$touch_files $f.in";; + esac + done + touch $touch_files + ;; + + automake*) + echo 1>&2 "\ +WARNING: \`$1' is $msg. You should only need it if + you modified \`Makefile.am', \`acinclude.m4' or \`${configure_ac}'. + You might want to install the \`Automake' and \`Perl' packages. + Grab them from any GNU archive site." + find . -type f -name Makefile.am -print | + sed 's/\.am$/.in/' | + while read f; do touch "$f"; done + ;; + + autom4te) + echo 1>&2 "\ +WARNING: \`$1' is needed, but is $msg. + You might have modified some files without having the + proper tools for further handling them. + You can get \`$1' as part of \`Autoconf' from any GNU + archive site." + + file=`echo "$*" | sed -n 's/.*--output[ =]*\([^ ]*\).*/\1/p'` + test -z "$file" && file=`echo "$*" | sed -n 's/.*-o[ ]*\([^ ]*\).*/\1/p'` + if test -f "$file"; then + touch $file + else + test -z "$file" || exec >$file + echo "#! /bin/sh" + echo "# Created by GNU Automake missing as a replacement of" + echo "# $ $@" + echo "exit 0" + chmod +x $file + exit 1 + fi + ;; + + bison|yacc) + echo 1>&2 "\ +WARNING: \`$1' $msg. You should only need it if + you modified a \`.y' file. You may need the \`Bison' package + in order for those modifications to take effect. You can get + \`Bison' from any GNU archive site." + rm -f y.tab.c y.tab.h + if [ $# -ne 1 ]; then + eval LASTARG="\${$#}" + case "$LASTARG" in + *.y) + SRCFILE=`echo "$LASTARG" | sed 's/y$/c/'` + if [ -f "$SRCFILE" ]; then + cp "$SRCFILE" y.tab.c + fi + SRCFILE=`echo "$LASTARG" | sed 's/y$/h/'` + if [ -f "$SRCFILE" ]; then + cp "$SRCFILE" y.tab.h + fi + ;; + esac + fi + if [ ! -f y.tab.h ]; then + echo >y.tab.h + fi + if [ ! -f y.tab.c ]; then + echo 'main() { return 0; }' >y.tab.c + fi + ;; + + lex|flex) + echo 1>&2 "\ +WARNING: \`$1' is $msg. You should only need it if + you modified a \`.l' file. You may need the \`Flex' package + in order for those modifications to take effect. You can get + \`Flex' from any GNU archive site." + rm -f lex.yy.c + if [ $# -ne 1 ]; then + eval LASTARG="\${$#}" + case "$LASTARG" in + *.l) + SRCFILE=`echo "$LASTARG" | sed 's/l$/c/'` + if [ -f "$SRCFILE" ]; then + cp "$SRCFILE" lex.yy.c + fi + ;; + esac + fi + if [ ! -f lex.yy.c ]; then + echo 'main() { return 0; }' >lex.yy.c + fi + ;; + + help2man) + echo 1>&2 "\ +WARNING: \`$1' is $msg. You should only need it if + you modified a dependency of a manual page. You may need the + \`Help2man' package in order for those modifications to take + effect. You can get \`Help2man' from any GNU archive site." + + file=`echo "$*" | sed -n 's/.*-o \([^ ]*\).*/\1/p'` + if test -z "$file"; then + file=`echo "$*" | sed -n 's/.*--output=\([^ ]*\).*/\1/p'` + fi + if [ -f "$file" ]; then + touch $file + else + test -z "$file" || exec >$file + echo ".ab help2man is required to generate this page" + exit 1 + fi + ;; + + makeinfo) + echo 1>&2 "\ +WARNING: \`$1' is $msg. You should only need it if + you modified a \`.texi' or \`.texinfo' file, or any other file + indirectly affecting the aspect of the manual. The spurious + call might also be the consequence of using a buggy \`make' (AIX, + DU, IRIX). You might want to install the \`Texinfo' package or + the \`GNU make' package. Grab either from any GNU archive site." + # The file to touch is that specified with -o ... + file=`echo "$*" | sed -n 's/.*-o \([^ ]*\).*/\1/p'` + if test -z "$file"; then + # ... or it is the one specified with @setfilename ... + infile=`echo "$*" | sed 's/.* \([^ ]*\) *$/\1/'` + file=`sed -n '/^@setfilename/ { s/.* \([^ ]*\) *$/\1/; p; q; }' $infile` + # ... or it is derived from the source name (dir/f.texi becomes f.info) + test -z "$file" && file=`echo "$infile" | sed 's,.*/,,;s,.[^.]*$,,'`.info + fi + # If the file does not exist, the user really needs makeinfo; + # let's fail without touching anything. + test -f $file || exit 1 + touch $file + ;; + + tar) + shift + + # We have already tried tar in the generic part. + # Look for gnutar/gtar before invocation to avoid ugly error + # messages. + if (gnutar --version > /dev/null 2>&1); then + gnutar "$@" && exit 0 + fi + if (gtar --version > /dev/null 2>&1); then + gtar "$@" && exit 0 + fi + firstarg="$1" + if shift; then + case "$firstarg" in + *o*) + firstarg=`echo "$firstarg" | sed s/o//` + tar "$firstarg" "$@" && exit 0 + ;; + esac + case "$firstarg" in + *h*) + firstarg=`echo "$firstarg" | sed s/h//` + tar "$firstarg" "$@" && exit 0 + ;; + esac + fi + + echo 1>&2 "\ +WARNING: I can't seem to be able to run \`tar' with the given arguments. + You may want to install GNU tar or Free paxutils, or check the + command line arguments." + exit 1 + ;; + + *) + echo 1>&2 "\ +WARNING: \`$1' is needed, and is $msg. + You might have modified some files without having the + proper tools for further handling them. Check the \`README' file, + it often tells you about the needed prerequisites for installing + this package. You may also peek at any GNU archive site, in case + some other package would contain this missing \`$1' program." + exit 1 + ;; +esac + +exit 0 + +# Local variables: +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "scriptversion=" +# time-stamp-format: "%:y-%02m-%02d.%02H" +# time-stamp-end: "$" +# End: diff --git a/ocaml-gettext/Makefile b/ocaml-gettext/Makefile new file mode 100644 index 0000000..3858ea8 --- /dev/null +++ b/ocaml-gettext/Makefile @@ -0,0 +1,83 @@ +########################################################################## +# ocaml-gettext: a library to translate messages # +# # +# Copyright (C) 2003-2008 Sylvain Le Gall # +# # +# This library is free software; you can redistribute it and/or # +# modify it under the terms of the GNU Lesser General Public # +# License as published by the Free Software Foundation; either # +# version 2.1 of the License, or (at your option) any later version; # +# with the OCaml static compilation exception. # +# # +# This library 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 # +# Lesser General Public License for more details. # +# # +# You should have received a copy of the GNU Lesser General Public # +# License along with this library; if not, write to the Free Software # +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 # +# USA # +########################################################################## + +include ../ConfMakefile + +ifeq ($(BUILD_CAMOMILE),yes) +GETTEXT_MODULES=CAMOMILE +else +ifeq ($(BUILD_STUB),yes) +GETTEXT_MODULES=STUB +endif +endif + +ifdef GETTEXT_MODULES +################################# +# Ocaml Gettext utility program # +################################# + +NAME = ocaml-gettext +CMO = OCamlGettext.cmo +REQUIRES = gettext.extension fileutils + +ifeq ($(GETTEXT_MODULES),CAMOMILE) +REQUIRES += gettext-camomile +INCLUDES += -pp 'camlp4o -I +camlp4 pa_macro.cmo -DCAMOMILE' +endif + +ifeq ($(GETTEXT_MODULES),STUB) +REQUIRES += gettext-stub +INCLUDES += -pp 'camlp4o -I +camlp4 pa_macro.cmo -DSTUB' +endif + +include ../TopMakefile + +################################# +# Ocaml Gettext extract program # +################################# + +all: ocaml-xgettext + +install: ocaml-xgettext-install + +uninstall: ocaml-xgettext-uninstall + +ocaml-xgettext: $(BUILDBIN) + $(OCAMLC) \ + -I +camlp4 dynlink.cma camlp4lib.cma \ + `$(OCAMLFIND) query -r -predicates byte gettext.extract -i-format` \ + `$(OCAMLFIND) query -r -predicates byte gettext.extract -a-format` \ + `$(OCAMLFIND) query -r -predicates byte gettext.extract -o-format` \ + Camlp4Bin.cmo \ + -o $@ + $(INSTALL_SCRIPT) -t $(BUILDBIN) $@ + +ocaml-xgettext-install: + $(INSTALL_SCRIPT) -t $(BINDIR) ocaml-xgettext + +ocaml-xgettext-uninstall: + -$(RM) $(BINDIR)/ocaml-xgettext + +clean:: + -$(RM) ocaml-xgettext + -$(RM) $(BUILDBIN)/ocaml-xgettext +endif diff --git a/ocaml-gettext/OCamlGettext.ml b/ocaml-gettext/OCamlGettext.ml new file mode 100644 index 0000000..6e72971 --- /dev/null +++ b/ocaml-gettext/OCamlGettext.ml @@ -0,0 +1,550 @@ +(**************************************************************************) +(* ocaml-gettext: a library to translate messages *) +(* *) +(* Copyright (C) 2003-2008 Sylvain Le Gall *) +(* *) +(* This library is free software; you can redistribute it and/or *) +(* modify it under the terms of the GNU Lesser General Public *) +(* License as published by the Free Software Foundation; either *) +(* version 2.1 of the License, or (at your option) any later version; *) +(* with the OCaml static compilation exception. *) +(* *) +(* This library 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 *) +(* Lesser General Public License for more details. *) +(* *) +(* You should have received a copy of the GNU Lesser General Public *) +(* License along with this library; if not, write to the Free Software *) +(* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *) +(* USA *) +(**************************************************************************) + +(** Ocaml-gettext tools. + @author Sylvain Le Gall + *) + +(** Helper program to : + - extract translatable strings from OCaml source, + - compile PO file, + - install MO file, + - merge POT and PO file. +*) + +open GettextTypes;; +open GettextCategory;; +open GettextUtils;; +open FilePath.DefaultPath;; + +IFDEF CAMOMILE THEN +module OcamlGettextRealize = GettextCamomile.Open +ELSE IFDEF STUB THEN +module OcamlGettextRealize = GettextStub.Native +ELSE +module OcamlGettextRealize = GettextDummy.Dummy +ENDIF +ENDIF +;; + +module OcamlGettext = Gettext.Program +( + struct + let textdomain = "ocaml-gettext" + let codeset = None + let dir = None + let dependencies = Gettext.init + end +) +( OcamlGettextRealize ) +;; + +type action = + Extract + | Compile + | Install + | Uninstall + | Merge + | Version + | VersionShort +;; + +type t = + { + action_option : action option; + extract_command : string; + extract_default_option : string; + extract_filename_options : (string * string) list; + extract_pot : string; + compile_output_file_option : string option; + install_language_option : string option; + install_category : GettextCategory.category; + install_textdomain_option : string option; + install_destdir : string; + uninstall_language_option : string option; + uninstall_category : GettextCategory.category; + uninstall_textdomain_option : string option; + uninstall_orgdir : string; + merge_filename_pot : string; + merge_backup_extension : string; + input_files : string list; + strict : bool; + } +;; + +exception ActionRequired;; +exception InstallUninstallTooManyFilename;; +exception CompileTooManyFilename;; + +let string_of_exception exc = + let s_ x = + OcamlGettext.s_ x + in + match exc with + ActionRequired -> + (s_ "You must specify one action.") + | InstallUninstallTooManyFilename -> + (s_ +"You cannot specify at the same time a language, a textdomain +and provide more than one file to install/uninstall : all files +will have the same destination filename.") + | CompileTooManyFilename -> + (s_ +"You cannot specify a output filename and more than one +filename : all the compiled file will have the same output filename") + | _ -> + Gettext.string_of_exception exc +;; + +let do_extract t = + let real_lst = + let rec extract_potfiles accu lst = + match lst with + str :: lst when str = "POTFILES" -> + let chn = open_in str + in + let new_accu = + let rec extract_potfiles_aux accu = + try + let new_filename = + input_line chn + in + extract_potfiles_aux (new_filename :: accu) + with End_of_file -> + accu + in + extract_potfiles_aux accu + in + close_in chn; + extract_potfiles new_accu lst + | str :: lst -> + extract_potfiles (str :: accu) lst + | [] -> + List.rev accu + in + extract_potfiles [] t.input_files + in + let map_filename_options = + List.fold_left ( + fun map (fl,options) -> + MapString.add fl options map + ) MapString.empty t.extract_filename_options + in + GettextCompile.extract + t.extract_command + t.extract_default_option + map_filename_options + real_lst + t.extract_pot +;; + +let do_compile t = + match (t.compile_output_file_option,t.input_files) with + Some fl_mo, [fl_po] -> + GettextCompile.compile fl_po fl_mo + | None, lst -> + let fl_mo_of_fl_po fl_po = + (* BUG: should use add_extension *) + (chop_extension fl_po)^".mo" + in + List.iter ( fun fl_po -> + GettextCompile.compile fl_po (fl_mo_of_fl_po fl_po) + ) lst + | Some _, [] -> + () + | Some _, lst -> + raise CompileTooManyFilename +;; + +let guess_language_textdomain (language_option,textdomain_option) lst = + (* Rules for guessing language : language[.textdomain].mo *) + match (language_option,textdomain_option,lst) with + Some language, Some textdomain, [fl_mo] -> + [(language,textdomain,fl_mo)] + | Some _, Some _, [] -> + [] + | Some _, Some _, lst -> + raise InstallUninstallTooManyFilename + | Some language, None, lst -> + List.map (fun fl_mo -> (language,(chop_extension fl_mo),fl_mo)) lst + | None, Some textdomain, lst -> + List.map (fun fl_mo -> ((chop_extension fl_mo),textdomain,fl_mo)) lst + | None, None, lst -> + List.map (fun fl_mo -> + (* BUG: should be able to have get_extension working *) + (* + let str_reduce = + chop_extension fl_mo + in + * (((chop_extension str_reduce), (get_extension str_reduce)),fl_mo)*) + raise (Failure +"FilePath suffers from a default with the handling of +chop/get_extension. This bug should disappears with +newer version of ocaml-fileutils") + ) lst +;; + +let do_install t = + let install (language,textdomain,fl_mo) = + GettextCompile.install + t.strict + t.install_destdir + language + t.install_category + textdomain + fl_mo + in + List.iter install ( + guess_language_textdomain + (t.install_language_option,t.install_textdomain_option) + t.input_files + ) +;; + +let do_uninstall t = + let uninstall (language,textdomain,_) = + GettextCompile.uninstall + t.uninstall_orgdir + language + t.uninstall_category + textdomain + in + List.iter + uninstall + (guess_language_textdomain + (t.uninstall_language_option,t.uninstall_textdomain_option) + t.input_files) +;; + +let do_merge t = + GettextCompile.merge t.merge_filename_pot t.input_files t.merge_backup_extension +;; + +let do_action t = + match t.action_option with + Some Extract -> + do_extract t + | Some Compile -> + do_compile t + | Some Install -> + do_install t + | Some Uninstall -> + do_uninstall t + | Some Merge -> + do_merge t + | Some Version -> + ( + let (_,gettext_copyright) = + OcamlGettext.init + in + print_string gettext_copyright; + print_newline () + ) + | Some VersionShort -> + ( + print_string GettextConfig.version; + print_newline () + ) + | None -> + raise ActionRequired +;; + +let () = + let spf x = + Printf.sprintf x + in + let f_ x = + OcamlGettext.f_ x + in + let s_ x = + OcamlGettext.s_ x + in + let t = ref + { + action_option = None; + extract_command = "ocaml-xgettext"; + extract_default_option = "-I +camlp4 pa_o.cmo"; + extract_filename_options = []; + extract_pot = "messages.pot"; + compile_output_file_option = None; + install_language_option = None; + install_category = LC_MESSAGES; + install_textdomain_option = None; + install_destdir = GettextConfig.default_dir; + uninstall_language_option = None; + uninstall_category = LC_MESSAGES; + uninstall_textdomain_option = None; + uninstall_orgdir = GettextConfig.default_dir; + merge_filename_pot = "messages.pot"; + merge_backup_extension = "bak"; + input_files = []; + strict = false; + } + in + let actions = [ + "extract", Extract; + "compile", Compile; + "install", Install; + "uninstall", Uninstall; + "merge", Merge + ] + in + let (gettext_args,gettext_copyright) = + OcamlGettext.init + in + let args = + Arg.align + ( + [ + ( + "--action", + Arg.Symbol + ( + (List.map fst actions), + (fun symbol -> + try + t := { !t with action_option = Some (List.assoc symbol actions) } + with Not_found -> + raise (Arg.Bad (spf (f_ "Invalid action: %s.") symbol)) + ) + ), + ( + (s_ "Action to execute. Default: none.") + ) + ); + ( + "--extract-command", + Arg.String ( fun cmd -> + t := { !t with extract_command = cmd } + ), + ( + spf (f_ "cmd Command to extract translatable strings from an OCaml source file. Default: %s.") + !t.extract_command + ) + ); + ( + "--extract-default-option", + Arg.String ( fun default_option -> + t := { !t with extract_default_option = default_option } + ), + ( + spf (f_ "options Default option used when extracting translatable strings. Default: %S.") + !t.extract_default_option + ) + ); + ( + "--extract-filename-option", + Arg.Tuple ( + let filename = ref "" + in + [ + Arg.String ( fun str -> filename := str ); + Arg.String ( fun options -> + t := { !t with extract_filename_options = + (!filename,options) :: + !t.extract_filename_options + } + ) + ] + ), + ( + spf (f_ "filename options Per filename option used when extracting strings from the specified filename. Default: %s.") + (string_of_list ( + List.map ( fun (str1,str2) -> + spf "(%s,%s)" str1 str2 + ) !t.extract_filename_options + ) + ) + ) + ); + ( + "--extract-pot", + ( + Arg.String ( fun str -> + t := { !t with extract_pot = str } + ) + ), + spf (f_ "filename POT file to write when extracting translatable strings. Default: %s.") + !t.extract_pot + ); + ( + "--compile-output", + ( + Arg.String ( fun str -> + t := { !t with compile_output_file_option = Some str } + ) + ), + (s_ "filename MO file to write when compiling a PO file. Default: name of the PO file with \".mo\" extension.") + ); + ( + "--install-language", + ( + Arg.String ( fun str -> + t := { !t with install_language_option = Some str } + ) + ), + (s_ "language Language to use when installing a MO file. Default: try to guess it from the name of the MO file.") + ); + ( + "--install-category", + ( + Arg.String ( fun str -> + t := { !t with install_category = GettextCategory.category_of_string str } + ) + ), + spf (f_ "category Category to use when installing a MO file. Default: %s.") + (GettextCategory.string_of_category !t.install_category) + ); + ( + "--install-textdomain", + ( + Arg.String ( fun str -> + t := { !t with install_textdomain_option = Some str } + ) + ), + (s_ "textdomain Textdomain to use when installing a MO file. Default: try to guess it from the name of the MO file.") + ); + ( + "--install-destdir", + ( + Arg.String ( fun str -> + t := { !t with install_destdir = str } + ) + ), + spf (f_ "dirname Base dir used when installing a MO file. Default: %s.") + !t.install_destdir + ); + ( + "--strict", + Arg.Unit (fun () -> t := {!t with strict = true}), + spf (f_ " Additional check are errors during install. Default: %b.") + !t.strict + ); + ( + "--uninstall-language", + ( + Arg.String ( fun str -> + t := { !t with uninstall_language_option = Some str } + ) + ), + (s_ "language Language to use when uninstalling a MO file. Default: try to guess it from the name of the MO file.") + ); + ( + "--uninstall-category", + ( + Arg.String ( fun str -> + t := { !t with uninstall_category = GettextCategory.category_of_string str } + ) + ), + spf (f_ "category Category to use when uninstalling a MO file. Default: %s.") + (GettextCategory.string_of_category !t.uninstall_category) + ); + ( + "--uninstall-textdomain", + ( + Arg.String ( fun str -> + t := { !t with uninstall_textdomain_option = Some str } + ) + ), + (s_ "textdomain Textdomain to use when uninstalling a MO file. Default: try to guess it from the name of the MO file.") + ); + ( + "--uninstall-orgdir", + ( + Arg.String ( fun str -> + t := { !t with uninstall_orgdir = str } + ) + ), + spf (f_ "dirname Base dir used when uninstalling a MO file. Default: %s.") + !t.uninstall_orgdir + ); + ( + "--merge-pot", + ( + Arg.String ( fun str -> + t := { !t with merge_filename_pot = str } + ) + ), + spf (f_ "filename POT file to use as a master for merging PO file. Default: %s.") + !t.merge_filename_pot + ); + ( + "--merge-backup-extension", + ( + Arg.String ( fun str -> + t := { !t with merge_backup_extension = str } + ) + ), + spf (f_ "extension Backup extension to use when moving PO file which have been merged. Default: %s.") + !t.merge_backup_extension + ); + ( + "--version", + ( + Arg.Unit ( fun () -> + t := { !t with action_option = Some Version } + ) + ), + (s_ " Returns version information on ocaml-gettext.") + ); + ( + "--short-version", + ( + Arg.Unit ( fun () -> + t := { !t with action_option = Some VersionShort } + ) + ), + (s_ " Returns only the version string of ocaml-gettext.") + ); + ] @ gettext_args + ) + in + let () = + Arg.parse + args + ( + fun str -> + t := { !t with input_files = str :: !t.input_files } + ) + ( + spf (f_ "%s + +Command: ocaml-gettext -action (%s) [options] +When trying to guess language and textdomain from a +MO file, the rules applied are: language.textdomain.mo + +Options:") + gettext_copyright + (String.concat "|" (List.map fst actions)) + ) + in + try + do_action !t + with exc -> + ( + prerr_string (string_of_exception exc); + prerr_newline (); + prerr_string (s_ "An error occurs while processing."); + prerr_newline (); + exit 1 + ) +;; diff --git a/patches/README.patches b/patches/README.patches new file mode 100644 index 0000000..4b7f0ec --- /dev/null +++ b/patches/README.patches @@ -0,0 +1,3 @@ +gettext-ocaml.patch: +Used to work on previous version. No longer work, but will work again in version 0.3.0, be +patient. diff --git a/patches/gettext-ocaml.patch b/patches/gettext-ocaml.patch new file mode 100644 index 0000000..86c3607 --- /dev/null +++ b/patches/gettext-ocaml.patch @@ -0,0 +1,1365 @@ +diff -Nurd gettext-0.12.1/gettext-tools/src/FILES gettext-0.12.1.ocaml/gettext-tools/src/FILES +--- gettext-0.12.1/gettext-tools/src/FILES 2003-04-29 12:00:15.000000000 +0200 ++++ gettext-0.12.1.ocaml/gettext-tools/src/FILES 2003-07-03 18:52:19.000000000 +0200 +@@ -177,6 +177,7 @@ + format-ycp.c Format string handling for YCP. + format-tcl.c Format string handling for Tcl. + format-php.c Format string handling for PHP. ++format-ocaml.c Format string handling for Objective Caml. + format.c Table of the language dependent format string handlers. + + +-------------- The 'msgfmt' program +@@ -239,6 +240,9 @@ + | x-php.h + | x-php.c + | String extractor for PHP. ++| x-ocaml.h ++| x-ocaml.c ++| String extractor for Objective Caml. + | x-rst.h + | x-rst.c + | String extractor from .rst files, for Object Pascal. +diff -Nurd gettext-0.12.1/gettext-tools/src/Makefile.am gettext-0.12.1.ocaml/gettext-tools/src/Makefile.am +--- gettext-0.12.1/gettext-tools/src/Makefile.am 2003-04-29 12:05:37.000000000 +0200 ++++ gettext-0.12.1.ocaml/gettext-tools/src/Makefile.am 2003-07-16 22:00:23.000000000 +0200 +@@ -42,7 +42,7 @@ + write-mo.h read-java.h write-java.h read-tcl.h write-tcl.h po-time.h \ + plural-table.h format.h xgettext.h x-c.h x-po.h x-python.h x-lisp.h x-elisp.h \ + x-librep.h x-smalltalk.h x-java.h x-properties.h x-awk.h x-ycp.h x-tcl.h \ +-x-php.h x-rst.h x-glade.h ++x-php.h x-rst.h x-glade.h x-ocaml.h + + EXTRA_DIST += FILES project-id ChangeLog.0 + +@@ -93,7 +93,7 @@ + FORMAT_SOURCE = format.c format-invalid.h \ + format-c.c format-python.c format-lisp.c format-elisp.c format-librep.c \ + format-java.c format-awk.c format-pascal.c format-ycp.c format-tcl.c \ +-format-php.c ++format-php.c format-ocaml.c + + # libgettextsrc contains all code that is needed by at least two programs. + libgettextsrc_la_SOURCES = \ +@@ -119,7 +119,7 @@ + msgunfmt_SOURCES = msgunfmt.c read-mo.c read-java.c read-tcl.c + xgettext_SOURCES = xgettext.c \ + x-c.c x-po.c x-python.c x-lisp.c x-elisp.c x-librep.c x-smalltalk.c \ +- x-java.l x-awk.c x-ycp.c x-tcl.c x-php.c x-rst.c x-glade.c ++ x-java.l x-awk.c x-ycp.c x-tcl.c x-php.c x-rst.c x-glade.c x-ocaml.c + msgattrib_SOURCES = msgattrib.c + msgcat_SOURCES = msgcat.c + msgcomm_SOURCES = msgcomm.c +diff -Nurd gettext-0.12.1/gettext-tools/src/Makefile.in gettext-0.12.1.ocaml/gettext-tools/src/Makefile.in +--- gettext-0.12.1/gettext-tools/src/Makefile.in 2003-05-22 15:41:24.000000000 +0200 ++++ gettext-0.12.1.ocaml/gettext-tools/src/Makefile.in 2003-07-16 22:04:36.000000000 +0200 +@@ -250,7 +250,7 @@ + write-mo.h read-java.h write-java.h read-tcl.h write-tcl.h po-time.h \ + plural-table.h format.h xgettext.h x-c.h x-po.h x-python.h x-lisp.h x-elisp.h \ + x-librep.h x-smalltalk.h x-java.h x-properties.h x-awk.h x-ycp.h x-tcl.h \ +-x-php.h x-rst.h x-glade.h ++x-php.h x-rst.h x-glade.h x-ocaml.h + + + localedir = $(datadir)/locale +@@ -286,7 +286,7 @@ + FORMAT_SOURCE = format.c format-invalid.h \ + format-c.c format-python.c format-lisp.c format-elisp.c format-librep.c \ + format-java.c format-awk.c format-pascal.c format-ycp.c format-tcl.c \ +-format-php.c ++format-php.c format-ocaml.c + + + # libgettextsrc contains all code that is needed by at least two programs. +@@ -314,7 +314,7 @@ + msgunfmt_SOURCES = msgunfmt.c read-mo.c read-java.c read-tcl.c + xgettext_SOURCES = xgettext.c \ + x-c.c x-po.c x-python.c x-lisp.c x-elisp.c x-librep.c x-smalltalk.c \ +- x-java.l x-awk.c x-ycp.c x-tcl.c x-php.c x-rst.c x-glade.c ++ x-java.l x-awk.c x-ycp.c x-tcl.c x-php.c x-rst.c x-glade.c x-ocaml.c + + msgattrib_SOURCES = msgattrib.c + msgcat_SOURCES = msgcat.c +@@ -439,7 +439,7 @@ + dir-list.lo str-list.lo + am__objects_2 = format.lo format-c.lo format-python.lo format-lisp.lo \ + format-elisp.lo format-librep.lo format-java.lo format-awk.lo \ +- format-pascal.lo format-ycp.lo format-tcl.lo format-php.lo ++ format-pascal.lo format-ycp.lo format-tcl.lo format-php.lo format-ocaml.lo + am_libgettextsrc_la_OBJECTS = $(am__objects_1) read-po.lo \ + write-properties.lo write-po.lo msgl-ascii.lo msgl-iconv.lo \ + msgl-equal.lo msgl-cat.lo msgl-english.lo file-list.lo \ +@@ -517,7 +517,7 @@ + xgettext-x-java.$(OBJEXT) xgettext-x-awk.$(OBJEXT) \ + xgettext-x-ycp.$(OBJEXT) xgettext-x-tcl.$(OBJEXT) \ + xgettext-x-php.$(OBJEXT) xgettext-x-rst.$(OBJEXT) \ +- xgettext-x-glade.$(OBJEXT) ++ xgettext-x-glade.$(OBJEXT) xgettext-x-ocaml.$(OBJEXT) + xgettext_OBJECTS = $(am_xgettext_OBJECTS) + xgettext_DEPENDENCIES = ../libuniname/libuniname.a libgettextsrc.la + +@@ -1045,6 +1045,15 @@ + xgettext-x-php.lo: x-php.c + $(LIBTOOL) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(xgettext_CFLAGS) $(CFLAGS) -c -o xgettext-x-php.lo `test -f 'x-php.c' || echo '$(srcdir)/'`x-php.c + ++xgettext-x-ocaml.o: x-ocaml.c ++ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(xgettext_CFLAGS) $(CFLAGS) -c -o xgettext-x-ocaml.o `test -f 'x-ocaml.c' || echo '$(srcdir)/'`x-ocaml.c ++ ++xgettext-x-ocaml.obj: x-ocaml.c ++ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(xgettext_CFLAGS) $(CFLAGS) -c -o xgettext-x-ocaml.obj `if test -f 'x-ocaml.c'; then $(CYGPATH_W) 'x-ocaml.c'; else $(CYGPATH_W) '$(srcdir)/x-ocaml.c'; fi` ++ ++xgettext-x-ocaml.lo: x-ocaml.c ++ $(LIBTOOL) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(xgettext_CFLAGS) $(CFLAGS) -c -o xgettext-x-ocaml.lo `test -f 'x-ocaml.c' || echo '$(srcdir)/'`x-ocaml.c ++ + xgettext-x-rst.o: x-rst.c + $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(xgettext_CFLAGS) $(CFLAGS) -c -o xgettext-x-rst.o `test -f 'x-rst.c' || echo '$(srcdir)/'`x-rst.c + +diff -Nurd gettext-0.12.1/gettext-tools/src/Makefile.msvc gettext-0.12.1.ocaml/gettext-tools/src/Makefile.msvc +--- gettext-0.12.1/gettext-tools/src/Makefile.msvc 2003-05-18 13:21:29.000000000 +0200 ++++ gettext-0.12.1.ocaml/gettext-tools/src/Makefile.msvc 2003-07-16 22:06:25.000000000 +0200 +@@ -139,13 +139,14 @@ + format-pascal.obj \ + format-ycp.obj \ + format-tcl.obj \ +- format-php.obj ++ format-php.obj \ ++ format-ocaml.obj + + msgcmp_OBJECTS = msgcmp.obj + msgfmt_OBJECTS = msgfmt.obj write-mo.obj write-java.obj write-tcl.obj plural-eval.obj + msgmerge_OBJECTS = msgmerge.obj + msgunfmt_OBJECTS = msgunfmt.obj read-mo.obj read-java.obj read-tcl.obj +-xgettext_OBJECTS = xgettext.obj x-c.obj x-po.obj x-python.obj x-lisp.obj x-elisp.obj x-librep.obj x-smalltalk.obj x-java.obj x-awk.obj x-ycp.obj x-tcl.obj x-php.obj x-rst.obj x-glade.obj ++xgettext_OBJECTS = xgettext.obj x-c.obj x-po.obj x-python.obj x-lisp.obj x-elisp.obj x-librep.obj x-smalltalk.obj x-java.obj x-awk.obj x-ycp.obj x-tcl.obj x-php.obj x-rst.obj x-glade.obj x-ocaml.obj + msgattrib_OBJECTS = msgattrib.obj + msgcat_OBJECTS = msgcat.obj + msgcomm_OBJECTS = msgcomm.obj +@@ -269,6 +270,9 @@ + format-php.obj : format-php.c + $(CC) $(INCLUDES) $(CFLAGS) $(PICFLAGS) -c format-php.c + ++format-ocaml.obj : format-ocaml.c ++ $(CC) $(INCLUDES) $(CFLAGS) $(PICFLAGS) -c format-ocaml.c ++ + !if !$(DLL) + + gettextsrc.lib : $(OBJECTS) +@@ -360,6 +364,9 @@ + x-php.obj : x-php.c + $(CC) $(INCLUDES) $(CFLAGS) -c x-php.c + ++x-ocaml.obj : x-ocaml.c ++ $(CC) $(INCLUDES) $(CFLAGS) -c x-ocaml.c ++ + x-rst.obj : x-rst.c + $(CC) $(INCLUDES) $(CFLAGS) -c x-rst.c + +diff -Nurd gettext-0.12.1/gettext-tools/src/Makefile.vms gettext-0.12.1.ocaml/gettext-tools/src/Makefile.vms +--- gettext-0.12.1/gettext-tools/src/Makefile.vms 2003-04-29 12:00:16.000000000 +0200 ++++ gettext-0.12.1.ocaml/gettext-tools/src/Makefile.vms 2003-07-16 22:07:34.000000000 +0200 +@@ -85,13 +85,14 @@ + format-pascal.obj, \ + format-ycp.obj, \ + format-tcl.obj, \ +- format-php.obj ++ format-php.obj \ ++ format-ocaml.obj + + msgcmp_OBJECTS = msgcmp.obj + msgfmt_OBJECTS = msgfmt.obj, write-mo.obj, write-java.obj, write-tcl.obj, plural-eval.obj + msgmerge_OBJECTS = msgmerge.obj + msgunfmt_OBJECTS = msgunfmt.obj, read-mo.obj, read-java.obj, read-tcl.obj +-xgettext_OBJECTS = xgettext.obj, x-c.obj, x-po.obj, x-python.obj, x-lisp.obj, x-elisp.obj, x-librep.obj, x-smalltalk.obj, x-java.obj, x-awk.obj, x-ycp.obj, x-tcl.obj, x-php.obj, x-rst.obj, x-glade.obj ++xgettext_OBJECTS = xgettext.obj, x-c.obj, x-po.obj, x-python.obj, x-lisp.obj, x-elisp.obj, x-librep.obj, x-smalltalk.obj, x-java.obj, x-awk.obj, x-ycp.obj, x-tcl.obj, x-php.obj, x-rst.obj, x-glade.obj x-ocaml.obj + msgattrib_OBJECTS = msgattrib.obj + msgcat_OBJECTS = msgcat.obj + msgcomm_OBJECTS = msgcomm.obj +@@ -213,6 +214,9 @@ + format-php.obj : format-php.c + $(CC) $(INCLUDES) $(CFLAGS) /define=($(DEFS)) format-php.c + ++format-ocaml.obj : format-ocaml.c ++ $(CC) $(INCLUDES) $(CFLAGS) /define=($(DEFS)) format-ocaml.c ++ + gettextsrc.olb : $(OBJECTS) + $(AR) $(AR_FLAGS) gettextsrc.olb $(OBJECTS) + +@@ -290,6 +294,9 @@ + x-php.obj : x-php.c + $(CC) $(INCLUDES) $(CFLAGS) /define=($(DEFS)) x-php.c + ++x-ocaml.obj : x-ocaml.c ++ $(CC) $(INCLUDES) $(CFLAGS) /define=($(DEFS)) x-ocaml.c ++ + x-rst.obj : x-rst.c + $(CC) $(INCLUDES) $(CFLAGS) /define=($(DEFS)) x-rst.c + +diff -Nurd gettext-0.12.1/gettext-tools/src/format-ocaml.c gettext-0.12.1.ocaml/gettext-tools/src/format-ocaml.c +--- gettext-0.12.1/gettext-tools/src/format-ocaml.c 1970-01-01 01:00:00.000000000 +0100 ++++ gettext-0.12.1.ocaml/gettext-tools/src/format-ocaml.c 2003-07-16 22:19:35.000000000 +0200 +@@ -0,0 +1,108 @@ ++/* Ocaml format strings. ++ Copyright (C) 2001-2003 Free Software Foundation, Inc. ++ Written by Sylvain LE GALL , 2003. ++ ++ 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, or (at your option) ++ any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software Foundation, ++ Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ ++ ++#ifdef HAVE_CONFIG_H ++# include ++#endif ++ ++#include ++#include ++ ++#include "format.h" ++#include "xmalloc.h" ++#include "xerror.h" ++#include "format-invalid.h" ++#include "error.h" ++#include "progname.h" ++#include "gettext.h" ++ ++#define _(str) gettext (str) ++ ++/* Ocaml Printf format string ++ Unfortunately, there is no way to explicitely use args in string from ++ ocaml. One way is to use Printf.* but, it needs a plain string ( and not ++ the return of a function ). ++ ++ Still working on it ++*/ ++ ++struct spec ++{ ++ int directives; ++}; ++ ++static void * ++format_parse (const char *format, char **invalid_reason) ++{ ++ struct spec spec; ++ struct spec *result; ++ ++ spec.directives = 0; ++ ++ result = (struct spec *) xmalloc (sizeof (struct spec) ); ++ *result = spec; ++ return result; ++} ++ ++static void ++format_free (void *descr) ++{ ++ struct spec *spec = (struct spec *) descr; ++ ++ free (spec); ++} ++ ++static int ++format_get_number_of_directives (void *descr) ++{ ++ struct spec *spec = (struct spec *) descr; ++ ++ return spec->directives; ++} ++ ++static bool ++format_check (const lex_pos_ty *pos, void *msgid_descr, void *msgstr_descr, ++ bool equality, bool noisy, const char *pretty_msgstr) ++{ ++ struct spec *spec1 = (struct spec *) msgid_descr; ++ struct spec *spec2 = (struct spec *) msgstr_descr; ++ bool err = false; ++ unsigned int i; ++ ++ // We have always 0 directives ++ if ( equality ) ++ { ++ err = true; ++ } ++ else ++ { ++ err = false; ++ }; ++ ++ return err; ++} ++ ++ ++struct formatstring_parser formatstring_ocaml = ++{ ++ format_parse, ++ format_free, ++ format_get_number_of_directives, ++ format_check ++}; ++ +diff -Nurd gettext-0.12.1/gettext-tools/src/format.c gettext-0.12.1.ocaml/gettext-tools/src/format.c +--- gettext-0.12.1/gettext-tools/src/format.c 2002-08-19 13:00:09.000000000 +0200 ++++ gettext-0.12.1.ocaml/gettext-tools/src/format.c 2003-07-16 22:16:32.000000000 +0200 +@@ -37,5 +37,6 @@ + /* format_pascal */ &formatstring_pascal, + /* format_ycp */ &formatstring_ycp, + /* format_tcl */ &formatstring_tcl, +- /* format_php */ &formatstring_php ++ /* format_php */ &formatstring_php, ++ /* format_ocaml */ &formatstring_ocaml + }; +diff -Nurd gettext-0.12.1/gettext-tools/src/format.h gettext-0.12.1.ocaml/gettext-tools/src/format.h +--- gettext-0.12.1/gettext-tools/src/format.h 2003-02-24 11:50:20.000000000 +0100 ++++ gettext-0.12.1.ocaml/gettext-tools/src/format.h 2003-07-03 18:47:27.000000000 +0200 +@@ -67,6 +67,7 @@ + extern struct formatstring_parser formatstring_ycp; + extern struct formatstring_parser formatstring_tcl; + extern struct formatstring_parser formatstring_php; ++extern struct formatstring_parser formatstring_ocaml; + + /* Table of all format string parsers. */ + extern struct formatstring_parser *formatstring_parsers[NFORMATS]; +diff -Nurd gettext-0.12.1/gettext-tools/src/message.c gettext-0.12.1.ocaml/gettext-tools/src/message.c +--- gettext-0.12.1/gettext-tools/src/message.c 2003-04-29 12:04:04.000000000 +0200 ++++ gettext-0.12.1.ocaml/gettext-tools/src/message.c 2003-07-16 22:09:07.000000000 +0200 +@@ -45,7 +45,8 @@ + /* format_pascal */ "object-pascal", + /* format_ycp */ "ycp", + /* format_tcl */ "tcl", +- /* format_php */ "php" ++ /* format_php */ "php", ++ /* format_ocaml */ "ocaml" + }; + + const char *const format_language_pretty[NFORMATS] = +@@ -61,7 +62,8 @@ + /* format_pascal */ "Object Pascal", + /* format_ycp */ "YCP", + /* format_tcl */ "Tcl", +- /* format_php */ "PHP" ++ /* format_php */ "PHP", ++ /* format_ocaml */ "Objective Caml" + }; + + +diff -Nurd gettext-0.12.1/gettext-tools/src/message.h gettext-0.12.1.ocaml/gettext-tools/src/message.h +--- gettext-0.12.1/gettext-tools/src/message.h 2003-04-29 12:04:04.000000000 +0200 ++++ gettext-0.12.1.ocaml/gettext-tools/src/message.h 2003-07-03 18:48:21.000000000 +0200 +@@ -45,9 +45,10 @@ + format_pascal, + format_ycp, + format_tcl, +- format_php ++ format_php, ++ format_ocaml + }; +-#define NFORMATS 12 /* Number of format_type enum values. */ ++#define NFORMATS 13 /* Number of format_type enum values. */ + extern const char *const format_language[NFORMATS]; + extern const char *const format_language_pretty[NFORMATS]; + +diff -Nurd gettext-0.12.1/gettext-tools/src/test.c gettext-0.12.1.ocaml/gettext-tools/src/test.c +--- gettext-0.12.1/gettext-tools/src/test.c 1970-01-01 01:00:00.000000000 +0100 ++++ gettext-0.12.1.ocaml/gettext-tools/src/test.c 2003-07-17 00:02:24.000000000 +0200 +@@ -0,0 +1,25 @@ ++#include ++#include ++#include "xsetenv.h" ++#define _(string) gettext (string) ++ ++int main (argc, argv) ++ int argc; ++ char *argv[]; ++{ ++ int n = atoi (argv[2]); ++ ++ xsetenv ("LC_ALL", argv[1], 1); ++ if (setlocale (LC_ALL, "") == NULL) ++ { ++ fprintf (stderr, "Couldn't set locale.\n"); ++ exit (77); ++ } ++ ++ textdomain ("prog"); ++ bindtextdomain ("prog", "."); ++ ++ gettext("Bonjour"); ++ ++ exit (0); ++} +diff -Nurd gettext-0.12.1/gettext-tools/src/x-ocaml.c gettext-0.12.1.ocaml/gettext-tools/src/x-ocaml.c +--- gettext-0.12.1/gettext-tools/src/x-ocaml.c 1970-01-01 01:00:00.000000000 +0100 ++++ gettext-0.12.1.ocaml/gettext-tools/src/x-ocaml.c 2003-07-17 23:37:00.000000000 +0200 +@@ -0,0 +1,873 @@ ++/* xgettext Ocaml backend. ++ Copyright (C) 2003, Sylvain LE GALL ++ ++ 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, or (at your option) ++ any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software Foundation, ++ Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ ++ ++#ifdef HAVE_CONFIG_H ++# include "config.h" ++#endif ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include "message.h" ++#include "x-ocaml.h" ++#include "xgettext.h" ++#include "error.h" ++#include "progname.h" ++#include "xmalloc.h" ++#include "exit.h" ++#include "hash.h" ++#include "gettext.h" ++ ++#define _(s) gettext(s) ++ ++ ++/* Ocaml defines string format in ++http://localhost/cgi-bin/dwww?type=file&location=/usr/share/doc/ocaml/docs/ocaml.html/manual009.html ++ ++ 1. Use the text at the adress given to lexify token from input stream ++ ++ 2. When a keyword is found find the attached string ( if any ) ++ ++ This lexer implements the above, and presents the scanner (in xgettext.c) ++ with a stream of Ocaml tokens. The comments are accumulated in a buffer, ++ and given to xgettext when asked for. */ ++ ++enum xgettext_token_type_ty ++{ ++ xgettext_token_type_string_literal, ++ xgettext_token_type_comment, ++ xgettext_token_type_symbol, ++ xgettext_token_type_keyword, ++ xgettext_token_type_beg_parent, ++ xgettext_token_type_end_parent, ++ xgettext_token_type_eof ++}; ++ ++typedef enum xgettext_token_type_ty xgettext_token_type_ty; ++ ++typedef struct xgettext_token_ty xgettext_token_ty; ++struct xgettext_token_ty ++{ ++ xgettext_token_type_ty type; ++ ++ /* These fields are used only for xgettext_token_type_keyword. */ ++ int argnum1; ++ int argnum2; ++ ++ /* This field is used only for xgettext_token_type_string_literal. */ ++ char *string; ++ ++ /* These fields are only for ++ xgettext_token_type_keyword, ++ xgettext_token_type_string_literal. */ ++ lex_pos_ty pos; ++}; ++ ++ ++/* ========================= Lexer customization. ========================= */ ++ ++/* ====================== Keyword set customization. ====================== */ ++ ++/* If true extract all strings. */ ++static bool extract_all = false; ++ ++static hash_table keywords; ++static bool default_keywords = true; ++ ++ ++void ++x_ocaml_extract_all () ++{ ++ extract_all = true; ++} ++ ++ ++void ++x_ocaml_keyword (const char *name) ++{ ++ if (name == NULL) ++ default_keywords = false; ++ else ++ { ++ const char *end; ++ int argnum1; ++ int argnum2; ++ const char *colon; ++ ++ if (keywords.table == NULL) ++ init_hash (&keywords, 100); ++ ++ split_keywordspec (name, &end, &argnum1, &argnum2); ++ ++ /* The characters between name and end should form a valid Ocaml identifier. ++ A colon means an invalid parse in split_keywordspec(). */ ++ colon = strchr (name, ':'); ++ if (colon == NULL || colon >= end) ++ { ++ if (argnum1 == 0) ++ argnum1 = 1; ++ insert_entry (&keywords, name, end - name, ++ (void *) (long) (argnum1 + (argnum2 << 10))); ++ } ++ } ++} ++ ++bool ++x_ocaml_any_keywords () ++{ ++ return (keywords.filled > 0) || default_keywords; ++} ++ ++/* Finish initializing the keywords hash table. ++ Called after argument processing, before each file is processed. */ ++static void ++init_keywords () ++{ ++ if (default_keywords) ++ { ++ x_ocaml_keyword ("gettext"); ++ x_ocaml_keyword ("Camlgettext.gettext"); ++ x_ocaml_keyword ("dgettext:2"); ++ x_ocaml_keyword ("Camlgettext.dgettext:2"); ++ x_ocaml_keyword ("dcgettext:2"); ++ x_ocaml_keyword ("Camlgettext.dcgettext:2"); ++ x_ocaml_keyword ("ngettext:1,2"); ++ x_ocaml_keyword ("Camlgettext.ngettext:1,2"); ++ x_ocaml_keyword ("dngettext:2,3"); ++ x_ocaml_keyword ("Camlgettext.dngettext:2,3"); ++ x_ocaml_keyword ("dcngettext:2,3"); ++ x_ocaml_keyword ("Camlgettext.dcngettext:2,3"); ++ x_ocaml_keyword ("gettext_noop:2,3"); ++ x_ocaml_keyword ("Camlgettext.gettext_noop:2,3"); ++ default_keywords = false; ++ } ++} ++ ++ ++/* ================== Reading of characters and tokens. =================== */ ++ ++/* Real filename, used in error messages about the input file. */ ++static const char *real_file_name; ++ ++/* Logical filename and line number, used to label the extracted messages. */ ++static char *logical_file_name; ++static int line_number; ++ ++/* The input file stream. */ ++static FILE *fp; ++ ++ ++/* Maximum used guaranteed to be < 4. */ ++static unsigned char phase1_pushback[4]; ++static int phase1_pushback_length; ++static bool last_was_eof = false; ++ ++static int ++phase1_getc () ++{ ++ int c; ++ ++ if (phase1_pushback_length) ++ { ++ c = phase1_pushback[--phase1_pushback_length]; ++ if ( c == '\n' ) ++ ++line_number; ++ ++ if ( c == EOF ) ++ last_was_eof = true; ++ ++ return c; ++ } ++ for (;;) ++ { ++ c = getc (fp); ++ switch (c) ++ { ++ case EOF: ++ if (ferror (fp)) ++ { ++ error (EXIT_FAILURE, errno, ++ _("error while reading \"%s\""), real_file_name); ++ }; ++ ++ if ( last_was_eof ) ++ { ++ /* We ask two time for eof... We are looping */ ++ error (EXIT_FAILURE, errno, ++ _("try to extract repeatedly eof while reading \"%s\""), real_file_name); ++ }; ++ ++ last_was_eof = true; ++ return EOF; ++ ++ case '\n': ++ ++line_number; ++ last_was_eof = false; ++ return '\n'; ++ ++ default: ++ last_was_eof = false; ++ return c; ++ } ++ } ++} ++ ++ ++static void ++phase1_ungetc (int c) ++{ ++ switch (c) ++ { ++ case EOF: ++ break; ++ ++ case '\n': ++ --line_number; ++ /* FALLTHROUGH */ ++ ++ default: ++ phase1_pushback[phase1_pushback_length++] = c; ++ break; ++ } ++} ++ ++ ++/* ++ Taken from the lexer.mll find at the adress given ++ */ ++ ++static void ++eof_error ( const char * where, int c ) ++{ ++ if ( c == EOF ) ++ { ++ /* Error : we reach the end of the stream */ ++ ++ error_with_progname = false; ++ error (0, 0, _("%s:%d: warning: unterminated %s, we reach EOF"), ++ logical_file_name, line_number - 1, where); ++ error_with_progname = true; ++ } ++ ++} ++ ++#define IS_BLANKS(c) (c == ' ' || c == '\013' || c == '\t' || c == '\012' || c == '\010') ++ ++#define IS_BACKSLASHES_ESCAPES(c) (( c == '\\' ) \ ++ || (c == '"') \ ++ || (c == '\'') \ ++ || (c == 'n') \ ++ || (c == 't') \ ++ || (c == 'b') \ ++ || (c == 'r')) ++ ++#define VAL_BACKSLASHES_ESCAPES(c) \ ++ ( c == 'n' ? '\n' : \ ++ ( c == 't' ? '\t' : \ ++ ( c == 'b' ? '\b' : \ ++ ( c == 'r' ? '\r' : \ ++ c \ ++ )\ ++ )\ ++ )\ ++ )\ ++ ++#define LINE_SPLIT 1000 ++ ++static int ++get_regular_char ( bool *is_backslashed ) ++{ ++ int c, i, res; ++ ++ *is_backslashed = false; ++ c = phase1_getc(); ++ ++ if ( c == '\\' ) ++ { ++ c = phase1_getc(); ++ ++ if ( IS_BACKSLASHES_ESCAPES(c) ) ++ { ++ *is_backslashed = true; ++ res = VAL_BACKSLASHES_ESCAPES(c); ++ } ++ else if ( '0' <= c && c <= '9' ) ++ { ++ *is_backslashed = true; ++ for ( i = 0, res = 0 ; i < 3 ; i ++ ) ++ { ++ switch (c) ++ { ++ case '0' : case '1' : case '2' : case '3' : case '4' : ++ case '5' : case '6' : case '7' : case '8' : case '9' : ++ break; ++ default: ++ /* Error non number in the numeric definition*/ ++ error_with_progname = false; ++ error (0, 0, _( ++ "%s:%d: warning: numeric character constant contains non numeric char (%c)"), ++ logical_file_name, line_number - 1, c); ++ error_with_progname = true; ++ phase1_ungetc(c); ++ c = '0'; ++ break; ++ }; ++ res = res * 10 + (c - '0'); ++ c = phase1_getc(); ++ }; ++ ++ if ( res > 255 ) ++ { ++ /* Error number defined is too big */ ++ error_with_progname = false; ++ error (0, 0, _("%s:%d: warning: character constant too high"), ++ logical_file_name, line_number - 1); ++ error_with_progname = true; ++ res = 255; ++ }; ++ } ++ else ++ { ++ /* We have a backslash but nothing can be backslashed */ ++ *is_backslashed = false; ++ phase1_ungetc(c); ++ res = '\\'; ++ }; ++ } ++ else ++ { ++ *is_backslashed = false; ++ res = c; ++ }; ++ ++ return res; ++} ++ ++static void ++extract_char ( xgettext_token_ty *tp ) ++{ ++ int c, res; ++ bool is_backslashed; ++ char buffer[2]; ++ ++ ++ res = get_regular_char(&is_backslashed); ++ c = phase1_getc(); ++ ++ /* We verify that we reach the end of the char */ ++ ++ if ( c != '\'' ) ++ { ++ /* We have only the 'x which is a generic type */ ++ tp->type = xgettext_token_type_symbol; ++ tp->string = NULL; ++ tp->argnum1 = 0; ++ tp->argnum2 = 0; ++ tp->pos.file_name = logical_file_name; ++ tp->pos.line_number = line_number; ++ ++ phase1_ungetc(c); ++ } ++ else ++ { ++ buffer[0]=res; ++ buffer[1]='\0'; ++ tp->type = xgettext_token_type_string_literal; ++ tp->string = xstrdup(buffer); ++ tp->argnum1 = 0; ++ tp->argnum2 = 0; ++ tp->pos.file_name = logical_file_name; ++ tp->pos.line_number = line_number; ++ }; ++ ++} ++ ++static void ++extract_string ( xgettext_token_ty *tp ) ++{ ++ int bufmax = 0; ++ int bufpos = 0; ++ char *buffer = NULL; ++ int c, res; ++ bool is_backslashed; ++ ++ buffer = xrealloc (buffer, bufmax); ++ ++ c = get_regular_char(&is_backslashed); ++ ++ while ( !(c == '"' && !is_backslashed ) && c != EOF ) ++ { ++ ++ /* Possible start of a \\ (CR|LF|CRLF...) (' ' '\t')* ( line split ) ? */ ++ if ( c == '\\' && !is_backslashed ) ++ { ++ ++ c = phase1_getc(); ++ ++ while ( IS_BLANKS(c) ) ++ { ++ c = phase1_getc(); ++ }; ++ ++ phase1_ungetc(c); ++ } ++ else ++ { ++ if (bufpos >= bufmax) ++ { ++ bufmax += 100; ++ buffer = xrealloc (buffer, bufmax); ++ } ++ buffer[bufpos++] = c; ++ } ++ ++ c = get_regular_char(&is_backslashed); ++ }; ++ ++ buffer[bufpos] = 0; ++ ++ tp->type = xgettext_token_type_string_literal; ++ tp->string = xstrdup (buffer); ++ tp->argnum1 = 0; ++ tp->argnum2 = 0; ++ tp->pos.file_name = logical_file_name; ++ tp->pos.line_number = line_number; ++} ++ ++static void ++extract_comment ( xgettext_token_ty *tp ) ++{ ++ int bufmax = 0; ++ int bufpos = 0; ++ char *buffer = NULL; ++ /* If we are here, it means we have gone through a leading (* */ ++ int comment_depth = 1; ++ int c1, c2; ++ ++ buffer = xrealloc (buffer, bufmax); ++ ++ c2 = phase1_getc(); ++ ++ while ( comment_depth != 0 ) ++ { ++ c1 = c2; ++ c2 = phase1_getc(); ++ /* Is it a *) ? */ ++ if ( c1 == '*' && c2 == ')' ) ++ { ++ comment_depth--; ++ } ++ else if ( c1 == '(' && c2 == '*' ) ++ { ++ comment_depth++; ++ }; ++ ++ if (bufpos >= bufmax) ++ { ++ bufmax += 100; ++ buffer = xrealloc (buffer, bufmax); ++ }; ++ ++ /* We transform a multiline comment into a single line one */ ++ if ( c1 != '\r' && c1 != '\n' ) ++ { ++ buffer[bufpos++] = c1; ++ }; ++ }; ++ ++ bufpos--; /* We remove the trailing * */ ++ buffer[bufpos] = 0; ++ tp->type = xgettext_token_type_comment; ++ tp->string = xstrdup (buffer); ++ tp->argnum1 = 0; ++ tp->argnum2 = 0; ++ tp->pos.file_name = logical_file_name; ++ tp->pos.line_number = line_number; ++} ++ ++#define IN_RANGE(c, a, b) ( a <= c && c <= b ) ++#define SKIP(c, test) \ ++ c = phase1_getc();\ ++ while ( test ) { c = phase1_getc() ; }; \ ++ phase1_ungetc(c) ++ ++static void ++extract_numeric ( xgettext_token_ty *tp ) ++{ ++ int c1, c2, c; ++ /* We don't care about the numeric value... We just skip it */ ++ ++ c1 = phase1_getc(); ++ c2 = phase1_getc(); ++ ++ if ( c1 == '-' ) ++ { ++ c1 = c2; ++ c2 = phase1_getc(); ++ }; ++ ++ if ( c1 == '0' && (c2 == 'x' || c2 == 'X')) ++ { ++ SKIP(c, IN_RANGE(c, '0', '7') ++ || IN_RANGE(c, 'A', 'F') ++ || IN_RANGE(c, 'a', 'f')); ++ } ++ else if ( c1 == '0' && (c2 == 'o' || c2 == 'O')) ++ { ++ SKIP(c, IN_RANGE(c, '0', '7')); ++ } ++ else if ( c1 == '0' && (c2 == 'b' || c2 == 'B')) ++ { ++ SKIP(c, IN_RANGE(c, '0', '1')); ++ } ++ else ++ { ++ phase1_ungetc(c2); ++ phase1_ungetc(c1); ++ ++ SKIP(c, IN_RANGE(c, '0', '9')); ++ SKIP(c, (c == '.')); ++ SKIP(c, IN_RANGE(c, '0', '9')); ++ SKIP(c, (c == 'e' || c == 'E' || c == '+' || c == '-' ) ); ++ SKIP(c, IN_RANGE(c, '0', '9')); ++ } ++ ++ tp->type = xgettext_token_type_symbol; ++ tp->string = NULL; ++ tp->argnum1 = 0; ++ tp->argnum2 = 0; ++ tp->pos.file_name = logical_file_name; ++ tp->pos.line_number = line_number; ++} ++ ++ ++static void ++extract_ident ( xgettext_token_ty *tp ) ++{ ++ void *keyword_value; ++ int bufmax = 0; ++ int bufpos = 0; ++ char *buffer = NULL; ++ int c; ++ ++ buffer = xrealloc (buffer, bufmax); ++ ++ c = phase1_getc(); ++ ++ if (bufpos >= bufmax) ++ { ++ bufmax += 100; ++ buffer = xrealloc (buffer, bufmax); ++ }; ++ ++ buffer[bufpos++] = c; ++ ++ c = phase1_getc(); ++ ++ while ( c != '(' && c != ')' && c !='"' && c!='\'' && c != EOF && !IS_BLANKS(c) ) ++ { ++ if (bufpos >= bufmax) ++ { ++ bufmax += 100; ++ buffer = xrealloc (buffer, bufmax); ++ }; ++ ++ buffer[bufpos++] = c; ++ ++ c = phase1_getc(); ++ } ++ ++ phase1_ungetc(c); ++ ++ buffer[bufpos] = 0; ++ ++ if (find_entry (&keywords, buffer, strlen(buffer), &keyword_value) == 0) ++ { ++ ++ tp->type = xgettext_token_type_keyword; ++ tp->string = NULL; ++ tp->argnum1 = (int) (long) keyword_value & ((1 << 10) - 1); ++ tp->argnum2 = (int) (long) keyword_value >> 10; ++ tp->pos.file_name = logical_file_name; ++ tp->pos.line_number = line_number; ++ } ++ else ++ { ++ tp->type = xgettext_token_type_symbol; ++ tp->string = NULL; ++ tp->argnum1 = 0; ++ tp->argnum2 = 0; ++ tp->pos.file_name = logical_file_name; ++ tp->pos.line_number = line_number; ++ } ++} ++ ++static void ++ocaml_lex ( xgettext_token_ty *tp ) ++{ ++ int c; ++ ++ /* Eat white space and all this kind of stuff */ ++ ++ c = phase1_getc(); ++ ++ while ( IS_BLANKS(c) ) ++ { ++ c = phase1_getc(); ++ } ++ ++ ++ switch (c) ++ { ++ case '(' : ++ c = phase1_getc(); ++ if ( c == '*' ) ++ { ++ return extract_comment(tp); ++ } ++ else ++ { ++ phase1_ungetc(c); ++ tp->type = xgettext_token_type_beg_parent; ++ tp->string = NULL; ++ tp->argnum1 = 0; ++ tp->argnum2 = 0; ++ tp->pos.file_name = logical_file_name; ++ tp->pos.line_number = line_number; ++ } ++ break; ++ ++ case ')' : ++ tp->type = xgettext_token_type_end_parent; ++ tp->string = NULL; ++ tp->argnum1 = 0; ++ tp->argnum2 = 0; ++ tp->pos.file_name = logical_file_name; ++ tp->pos.line_number = line_number; ++ break; ++ ++ case '0' : case '1' : case '2' : case '3' : case '4' : ++ case '5' : case '6' : case '7' : case '8' : case '9' : ++ case '-' : case '.' : ++ phase1_ungetc(c); ++ return extract_numeric(tp); ++ break; ++ ++ case '\'' : ++ return extract_char(tp); ++ break; ++ ++ case '"' : ++ return extract_string(tp); ++ break; ++ ++ case EOF : ++ tp->type = xgettext_token_type_eof; ++ tp->string = NULL; ++ tp->argnum1 = 0; ++ tp->argnum2 = 0; ++ tp->pos.file_name = logical_file_name; ++ tp->pos.line_number = line_number; ++ break; ++ ++ default : ++ phase1_ungetc(c); ++ return extract_ident(tp); ++ break; ++ } ++} ++ ++ ++/* ========================= Extracting strings. ========================== */ ++ ++/* The file is broken into tokens. Scan the token stream, looking for ++ a keyword, followed by a left paren, followed by a string. When we ++ see this sequence, we have something to remember. We assume we are ++ looking at a valid C or C++ program, and leave the complaints about ++ the grammar to the compiler. ++ ++ Normal handling: Look for ++ keyword ( ... msgid ... ) ++ Plural handling: Look for ++ keyword ( ... msgid ... msgid_plural ... ) ++ ++ We use recursion because the arguments before msgid or between msgid ++ and msgid_plural can contain subexpressions of the same form. */ ++ ++ ++static bool ++ocaml_parse ( message_list_ty *mlp ) ++{ ++ xgettext_token_ty token; ++ message_ty *plural_mp = NULL; ++ int next_string = -1; ++ int next_plural = -1; ++ ++ for ( ; ; ) ++ { ++ ocaml_lex(&token); ++ ++ next_string--; ++ if ( next_string <= -1 ) ++ { ++ next_string = -1; ++ } ++ ++ next_plural--; ++ if ( next_plural <= -1 ) ++ { ++ next_plural = -1; ++ } ++ ++ if ( !extract_all ) ++ { ++ switch ( token.type ) ++ { ++ case xgettext_token_type_string_literal : ++ if ( next_string == 0 && next_plural > 0 ) ++ { ++ plural_mp=remember_a_message(mlp, token.string, &token.pos); ++ } ++ else if ( next_string == 0 && next_plural < 0 ) ++ { ++ remember_a_message(mlp, token.string, &token.pos); ++ plural_mp=NULL; ++ } ++ else if ( next_string < 0 && next_plural == 0 && plural_mp != NULL ) ++ { ++ remember_a_message_plural(plural_mp, token.string, &token.pos); ++ plural_mp = NULL; ++ } ++ else if ( next_string == 0 && next_plural == 0 ) ++ { ++ /* Error : we string && plural at the same time */ ++ error_with_progname = false; ++ error (0, 0, _("%s:%d: warning: plural and singular at the same position ( skipping )"), ++ logical_file_name, line_number - 1); ++ error_with_progname = true; ++ ++ next_string = -1; ++ next_plural = -1; ++ free(token.string); ++ } ++ else if ( next_string > 0 && next_plural == 0 ) ++ { ++ ++ /* Error : plural before singular*/ ++ error_with_progname = false; ++ error (0, 0, _("%s:%d: warning: plural before singular ( skipping )"), ++ logical_file_name, line_number - 1); ++ error_with_progname = true; ++ ++ next_string = -1; ++ next_plural = -1; ++ free(token.string); ++ ++ } ++ else if ( next_string < 0 && next_plural == 0 && plural_mp == NULL ) ++ { ++ /* Error : singular never found*/ ++ error_with_progname = false; ++ error (0, 0, _("%s:%d: warning: singular form never found ( skipping )"), ++ logical_file_name, line_number - 1); ++ error_with_progname = true; ++ ++ next_string = -1; ++ next_plural = -1; ++ free(token.string); ++ } ++ else ++ { ++ free(token.string); ++ } ++ break; ++ case xgettext_token_type_comment : ++ xgettext_comment_add(token.string); ++ free(token.string); ++ break; ++ case xgettext_token_type_symbol : ++ xgettext_comment_reset(); ++ break; ++ case xgettext_token_type_keyword : ++ next_string = token.argnum1; ++ next_plural = token.argnum2; ++ xgettext_comment_reset(); ++ break; ++ case xgettext_token_type_beg_parent : ++ xgettext_comment_reset(); ++ if ( !ocaml_parse(mlp) ) ++ { ++ /* Error : reach the end parenthized not balanced*/ ++ error_with_progname = false; ++ error (0, 0, _("%s:%d: warning: some parenthizes are not matched"), ++ logical_file_name, line_number - 1); ++ error_with_progname = true; ++ }; ++ break; ++ case xgettext_token_type_end_parent : ++ xgettext_comment_reset(); ++ return true; ++ break; ++ case xgettext_token_type_eof : ++ xgettext_comment_reset(); ++ return false; ++ break; ++ default : ++ abort(); ++ ++ } ++ } ++ else if ( token.type == xgettext_token_type_string_literal ) ++ { ++ if ( strlen(token.string) > 0 ) ++ remember_a_message(mlp, token.string, &token.pos); ++ } ++ else if ( token.type == xgettext_token_type_eof ) ++ { ++ return false; ++ } ++ } ++} ++ ++ ++void ++extract_ocaml (FILE *f, ++ const char *real_filename, const char *logical_filename, ++ msgdomain_list_ty *mdlp) ++{ ++ message_list_ty *mlp = mdlp->item[0]->messages; ++ ++ fp = f; ++ real_file_name = real_filename; ++ logical_file_name = xstrdup (logical_filename); ++ line_number = 1; ++ ++ init_keywords (); ++ ++ /* Eat token until eof */ ++ ocaml_parse(mlp); ++ ++ /* Close scanner. */ ++ fp = NULL; ++ real_file_name = NULL; ++ logical_file_name = NULL; ++ line_number = 0; ++} ++ +diff -Nurd gettext-0.12.1/gettext-tools/src/x-ocaml.h gettext-0.12.1.ocaml/gettext-tools/src/x-ocaml.h +--- gettext-0.12.1/gettext-tools/src/x-ocaml.h 1970-01-01 01:00:00.000000000 +0100 ++++ gettext-0.12.1.ocaml/gettext-tools/src/x-ocaml.h 2003-07-16 22:45:26.000000000 +0200 +@@ -0,0 +1,35 @@ ++/* xgettext Ocaml backend. ++ Copyright (C) 2003 Free Software Foundation, Inc. ++ Written by Sylvain LE GALL , 2003. ++ ++ 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, or (at your option) ++ any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software Foundation, ++ Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ ++ ++ ++#define EXTENSIONS_OCAML \ ++ { "ml", "Ocaml" }, \ ++ ++#define SCANNERS_OCAML \ ++ { "Ocaml", extract_ocaml, &formatstring_ocaml }, \ ++ ++/* Scan an Ocaml file and add its translatable strings to mdlp. */ ++extern void extract_ocaml (FILE *fp, const char *real_filename, ++ const char *logical_filename, ++ msgdomain_list_ty *mdlp); ++ ++ ++/* Handling of options specific to this language. */ ++ ++extern void x_ocaml_extract_all (void); ++extern void x_ocaml_keyword (const char *name); +diff -Nurd gettext-0.12.1/gettext-tools/src/xgettext.c gettext-0.12.1.ocaml/gettext-tools/src/xgettext.c +--- gettext-0.12.1/gettext-tools/src/xgettext.c 2003-05-21 12:56:46.000000000 +0200 ++++ gettext-0.12.1.ocaml/gettext-tools/src/xgettext.c 2003-07-16 22:44:07.000000000 +0200 +@@ -75,6 +75,7 @@ + #include "x-ycp.h" + #include "x-tcl.h" + #include "x-php.h" ++#include "x-ocaml.h" + #include "x-rst.h" + #include "x-glade.h" + +@@ -259,6 +260,7 @@ + x_awk_extract_all (); + x_tcl_extract_all (); + x_php_extract_all (); ++ x_ocaml_extract_all (); + x_glade_extract_all (); + break; + case 'c': +@@ -318,6 +320,7 @@ + x_awk_keyword (optarg); + x_tcl_keyword (optarg); + x_php_keyword (optarg); ++ x_ocaml_keyword (optarg); + x_glade_keyword (optarg); + } + break; +@@ -654,7 +657,7 @@ + -L, --language=NAME recognise the specified language\n\ + (C, C++, ObjectiveC, PO, Python, Lisp,\n\ + EmacsLisp, librep, Smalltalk, Java,\n\ +- JavaProperties, awk, YCP, Tcl, PHP, RST, Glade)\n")); ++ JavaProperties, awk, YCP, Tcl, PHP, RST, Glade, Ocaml)\n")); + printf (_("\ + -C, --c++ shorthand for --language=C++\n")); + printf (_("\ +@@ -1461,6 +1464,7 @@ + SCANNERS_PHP + SCANNERS_RST + SCANNERS_GLADE ++ SCANNERS_OCAML + /* Here will follow more languages and their scanners: perl, etc... + Make sure new scanners honor the --exclude-file option. */ + }; +@@ -1509,6 +1513,7 @@ + EXTENSIONS_PHP + EXTENSIONS_RST + EXTENSIONS_GLADE ++ EXTENSIONS_OCAML + /* Here will follow more file extensions: sh, pl ... */ + }; + diff --git a/po/LINGUAS b/po/LINGUAS new file mode 100644 index 0000000..527e861 --- /dev/null +++ b/po/LINGUAS @@ -0,0 +1 @@ +fr diff --git a/po/Makefile b/po/Makefile new file mode 100644 index 0000000..b2ee1a5 --- /dev/null +++ b/po/Makefile @@ -0,0 +1,85 @@ +########################################################################## +# ocaml-gettext: a library to translate messages # +# # +# Copyright (C) 2003-2008 Sylvain Le Gall # +# # +# This library is free software; you can redistribute it and/or # +# modify it under the terms of the GNU Lesser General Public # +# License as published by the Free Software Foundation; either # +# version 2.1 of the License, or (at your option) any later version; # +# with the OCaml static compilation exception. # +# # +# This library 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 # +# Lesser General Public License for more details. # +# # +# You should have received a copy of the GNU Lesser General Public # +# License along with this library; if not, write to the Free Software # +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 # +# USA # +########################################################################## + + +include ../ConfMakefile +include ../TopMakefile + +OCAML_GETTEXT_PACKAGE = ocaml-gettext +LINGUAS=$(shell cat LINGUAS) +SOURCES=POTFILES + +OCAML_GETTEXT=$(BUILDBIN)/ocaml-gettext +OCAML_GETTEXT_EXTRACT_OPTIONS=--extract-command $(BUILDBIN)/ocaml-xgettext +OCAML_GETTEXT_COMPILE_OPTIONS= +OCAML_GETTEXT_INSTALL_OPTIONS= +OCAML_GETTEXT_MERGE_OPTIONS= + +POFILES=$(addsuffix .po,$(LINGUAS)) +MOFILES=$(addsuffix .mo,$(LINGUAS)) +POTFILE=$(OCAML_GETTEXT_PACKAGE).pot + +all: install-buildpo + +install: install-po + +uninstall: uninstall-po + +clean:: clean-po + +%.mo: %.po + $(OCAML_GETTEXT) --action compile $(OCAML_GETTEXT_COMPILE_OPTIONS) \ + --compile-output $@ $^ + +%.pot: $(SOURCES) + $(OCAML_GETTEXT) --action extract $(OCAML_GETTEXT_EXTRACT_OPTIONS) \ + --extract-pot $@ $^ + +%.po: $(POTFILE) + $(OCAML_GETTEXT) --action merge $(OCAML_GETTEXT_MERGE_OPTIONS) \ + --merge-pot $(POTFILE) $@ + +$(BUILDPO): + mkdir -p $(BUILDPO) + +.PRECIOUS: $(POTFILE) + +install-buildpo: $(MOFILES) $(BUILDPO) $(OCAML_GETTEXT) + $(OCAML_GETTEXT) --action install $(OCAML_GETTEXT_INSTALL_OPTIONS) \ + --install-textdomain $(OCAML_GETTEXT_PACKAGE) \ + --install-destdir $(BUILDPO) $(MOFILES) + +install-po: $(MOFILES) $(OCAML_GETTEXT) + $(OCAML_GETTEXT) --action install $(OCAML_GETTEXT_INSTALL_OPTIONS) \ + --install-textdomain $(OCAML_GETTEXT_PACKAGE) \ + --install-destdir $(PODIR) $(MOFILES) + +uninstall-po: $(OCAML_GETTEXT) + $(OCAML_GETTEXT) --action uninstall $(OCAML_GETTEXT_INSTALL_OPTIONS) \ + --uninstall-textdomain $(OCAML_GETTEXT_PACKAGE) \ + --uninstall-orgdir $(PODIR) $(MOFILES) + +clean-po: + -$(OCAML_GETTEXT) --action uninstall $(OCAML_GETTEXT_INSTALL_OPTIONS) \ + --uninstall-textdomain $(OCAML_GETTEXT_PACKAGE) \ + --uninstall-orgdir $(BUILDPO) $(MOFILES) + -$(RM) $(MOFILES) diff --git a/po/POTFILES b/po/POTFILES new file mode 100644 index 0000000..5458bc1 --- /dev/null +++ b/po/POTFILES @@ -0,0 +1 @@ +../libgettext-ocaml/gettext.ml diff --git a/po/fr.po b/po/fr.po new file mode 100644 index 0000000..74bda5f --- /dev/null +++ b/po/fr.po @@ -0,0 +1,146 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: ocaml-gettext v0.1.1\n" +"Report-Msgid-Bugs-To: sylvain@le-gall.net\n" +"POT-Creation-Date: 2005-03-13 17:26+0000\n" +"PO-Revision-Date: 2005-03-15 01:04+0100\n" +"Last-Translator: Sylvain Le Gall \n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: nplurals=2; plural=n>1;" + +#: ../libgettext-ocaml/gettext.ml:312 +msgid " Choose how to handle failure in ocaml-gettext. Default: %s." +msgstr " Choisir la façon de traiter les erreurs dans ocaml-gettext. Défaut : %s." + +#: ../libgettext-ocaml/gettext.ml:327 +msgid " Disable the translation perform by ocaml-gettext. Default: enable." +msgstr " Désactive la traduction faite par ocaml-gettext. Défaut : active." + +#: ../libgettext-ocaml/gettext.ml:217 +msgid "An empty entry has been encounter." +msgstr "Une entrée vide a été trouvée." + +#: ../libgettext-ocaml/gettext.ml:189 +msgid "Cannot find an approriate ocaml-gettext compiled file ( %s )." +msgstr "Impossible de trouver un fichier ocaml-gettext compilé approprié ( %s )." + +#: ../libgettext-ocaml/gettext.ml:251 +msgid "Cannot find string %S." +msgstr "Impossible de trouver la chaîne %S." + +#: ../libgettext-ocaml/gettext.ml:236 +msgid "Could not open file %s." +msgstr "Impossible d'ouvrir le fichier %s." + +#: ../libgettext-ocaml/gettext.ml:248 +msgid "Error while merging two PO files: %S and %S cannot be merged." +msgstr "Erreur lors de la fusion de 2 fichiers PO : %S et %S ne peuvent être fusionnés." + +#: ../libgettext-ocaml/gettext.ml:242 +msgid "Error while processing parsing of PO file, in msgid %S, %d index is out of bound." +msgstr "Erreur lors du décodage du fichier PO, au niveau de msgid %S, l'indice %d est hors limite." + +#: ../libgettext-ocaml/gettext.ml:239 +msgid "Error while processing parsing of PO file: %S at %s." +msgstr "Erreur lors du décodage du fichier PO : %S à %s." + +#: ../libgettext-ocaml/gettext.ml:202 +msgid "Error while processing parsing of content-type at %s: %S." +msgstr "Erreur lors du décodage du champs content-type à %s : %S." + +#: ../libgettext-ocaml/gettext.ml:194 +msgid "Error while processing parsing of options at %s: %S." +msgstr "Erreur lors du décodage des options à %s : %S." + +#: ../libgettext-ocaml/gettext.ml:198 +msgid "Error while processing parsing of plural at %s: %S." +msgstr "Erreur lors du décodage de la forme plurielle à %s : %S." + +#: ../libgettext-ocaml/gettext.ml:245 +msgid "Error while trying to load PO file %s, file doesn't exist." +msgstr "Erreur lors du chargement du fichier PO %s, le fichier n'existe pas." + +#: ../libgettext-ocaml/gettext.ml:214 +msgid "Junk at the end of the plural form id %S: %s." +msgstr "Caractères non utilisé à la fin de la forme plurielle %S : %s." + +#: ../libgettext-ocaml/gettext.ml:206 +msgid "MO file provided is not encoded following ocaml-gettext convention." +msgstr "Le fichier MO fourni ne suit pas les règles de la librairie ocaml-gettext." + +#: ../libgettext-ocaml/gettext.ml:219 +msgid "Number of strings is negative." +msgstr "Le nombre de chaîne de caractères est négatif." + +#: ../libgettext-ocaml/gettext.ml:192 +msgid "Ocaml-gettext library is not initialized" +msgstr "La librairie ocaml-gettext n'est pas initialisée." + +#: ../libgettext-ocaml/gettext.ml:221 +msgid "Offset of string table is out of bound ([%ld,%ld] should be in [%ld,%ld])." +msgstr "Le décalage de la table de chaîne de caractère est hors limite ( [%ld,%ld] devrait être dans l'intervalle [%ld,%ld] )." + +#: ../libgettext-ocaml/gettext.ml:224 +msgid "Offset of translation table is out of bound ([%ld,%ld] should be in [%ld,%dl])." +msgstr "Le décalage de la table de chaîne de traduction est hors limite ( [%ld,%ld] devrait être dans l'intervalle [%ld,%ld] )." + +#: ../libgettext-ocaml/gettext.ml:230 +msgid "Out of bound access when trying to find a string (%d < %d)." +msgstr "Accès hors limite lors de la recherche d'une chaîne de caractère ( %d < %d )." + +#: ../libgettext-ocaml/gettext.ml:233 +msgid "Out of bound access when trying to find a translation (%d < %d)." +msgstr "Accès hors limite lors de la recherche d'une traduction ( %d < %d )." + +#: ../libgettext-ocaml/gettext.ml:181 +msgid "Problem reading file %s: %s." +msgstr "Problème lors de la lecture du fichier %s : %s." + +#: ../libgettext-ocaml/gettext.ml:183 +msgid "Problem while extracting %s: command %S exits with code %d." +msgstr "Problème lors de l'extraction %s : la commande %S s'est terminée avec le code de sortie %d." + +#: ../libgettext-ocaml/gettext.ml:186 +msgid "Problem while extracting %s: command %S killed by signal %d." +msgstr "Problème lors de l'extraction %s : la commande %S a été tuée avec le signal %d." + +#: ../libgettext-ocaml/gettext.ml:227 +msgid "Translation table and string table overlap ([%ld,%ld] and [%ld,%ld] have a non empty intersection)." +msgstr "Les tables de traduction et de chaîne de caractères se recouvrent ( [%ld,%ld] et [%ld,%ld] ont une intersection non vide )." + +#: ../libgettext-ocaml/gettext.ml:208 +msgid "Trying to fetch the plural form %d of a singular form %S." +msgstr "Tentative de récupération de la forme plurielle %d d'une forme singulière %S." + +#: ../libgettext-ocaml/gettext.ml:211 +msgid "Trying to fetch the plural form %d of plural form %s." +msgstr "Tentative de récupération de la forme plurielle %d de la forme plurielle %d." + +#: ../libgettext-ocaml/gettext.ml:254 +msgid "Unable to parse the POSIX language environment variable %s" +msgstr "" + +#: ../libgettext-ocaml/gettext.ml:387 +msgid "codeset Set the default codeset for outputting string with ocaml-gettext. Default: %s." +msgstr "codeset Défini le jeux de caractères à utiliser pour écrire les traductions. Défaut : %s." + +#: ../libgettext-ocaml/gettext.ml:364 +msgid "dir Add a search dir for ocaml-gettext files. Default: %s." +msgstr "dir Ajoute une répertoire de recherche pour les fichiers ocaml-gettext. Défaut : %s." + +#: ../libgettext-ocaml/gettext.ml:373 +msgid "language Set the default language for ocaml-gettext. Default: %s." +msgstr "language Défini le langage par défaut pour ocaml-gettext. Défaut : %s." + +#: ../libgettext-ocaml/gettext.ml:176 +msgid "line %d character %d" +msgstr "la ligne %d au caractère %d" + +#: ../libgettext-ocaml/gettext.ml:343 +msgid "textdomain dir Set a dir to search ocaml-gettext files for the specified domain. Default: %s." +msgstr "textdomain dir Défini un répertoire de recherche des fichiers ocaml-gettext pour le domaine de texte spécifié. Défaut : %s." + diff --git a/po/ocaml-gettext.pot b/po/ocaml-gettext.pot new file mode 100644 index 0000000..9fbcacc --- /dev/null +++ b/po/ocaml-gettext.pot @@ -0,0 +1,151 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2009-09-16 20:28+0000\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=CHARSET\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n" + +#: ../libgettext-ocaml/gettext.ml:312 +msgid " Choose how to handle failure in ocaml-gettext. Default: %s." +msgstr "" + +#: ../libgettext-ocaml/gettext.ml:327 +msgid " Disable the translation perform by ocaml-gettext. Default: enable." +msgstr "" + +#: ../libgettext-ocaml/gettext.ml:217 +msgid "An empty entry has been encounter." +msgstr "" + +#: ../libgettext-ocaml/gettext.ml:189 +msgid "Cannot find an approriate ocaml-gettext compiled file ( %s )." +msgstr "" + +#: ../libgettext-ocaml/gettext.ml:251 +msgid "Cannot find string %S." +msgstr "" + +#: ../libgettext-ocaml/gettext.ml:236 +msgid "Could not open file %s." +msgstr "" + +#: ../libgettext-ocaml/gettext.ml:248 +msgid "Error while merging two PO files: %S and %S cannot be merged." +msgstr "" + +#: ../libgettext-ocaml/gettext.ml:242 +msgid "Error while processing parsing of PO file, in msgid %S, %d index is out of bound." +msgstr "" + +#: ../libgettext-ocaml/gettext.ml:239 +msgid "Error while processing parsing of PO file: %S at %s." +msgstr "" + +#: ../libgettext-ocaml/gettext.ml:202 +msgid "Error while processing parsing of content-type at %s: %S." +msgstr "" + +#: ../libgettext-ocaml/gettext.ml:194 +msgid "Error while processing parsing of options at %s: %S." +msgstr "" + +#: ../libgettext-ocaml/gettext.ml:198 +msgid "Error while processing parsing of plural at %s: %S." +msgstr "" + +#: ../libgettext-ocaml/gettext.ml:245 +msgid "Error while trying to load PO file %s, file doesn't exist." +msgstr "" + +#: ../libgettext-ocaml/gettext.ml:214 +msgid "Junk at the end of the plural form id %S: %s." +msgstr "" + +#: ../libgettext-ocaml/gettext.ml:206 +msgid "MO file provided is not encoded following ocaml-gettext convention." +msgstr "" + +#: ../libgettext-ocaml/gettext.ml:219 +msgid "Number of strings is negative." +msgstr "" + +#: ../libgettext-ocaml/gettext.ml:192 +msgid "Ocaml-gettext library is not initialized" +msgstr "" + +#: ../libgettext-ocaml/gettext.ml:221 +msgid "Offset of string table is out of bound ([%ld,%ld] should be in [%ld,%ld])." +msgstr "" + +#: ../libgettext-ocaml/gettext.ml:224 +msgid "Offset of translation table is out of bound ([%ld,%ld] should be in [%ld,%dl])." +msgstr "" + +#: ../libgettext-ocaml/gettext.ml:230 +msgid "Out of bound access when trying to find a string (%d < %d)." +msgstr "" + +#: ../libgettext-ocaml/gettext.ml:233 +msgid "Out of bound access when trying to find a translation (%d < %d)." +msgstr "" + +#: ../libgettext-ocaml/gettext.ml:181 +msgid "Problem reading file %s: %s." +msgstr "" + +#: ../libgettext-ocaml/gettext.ml:183 +msgid "Problem while extracting %s: command %S exits with code %d." +msgstr "" + +#: ../libgettext-ocaml/gettext.ml:186 +msgid "Problem while extracting %s: command %S killed by signal %d." +msgstr "" + +#: ../libgettext-ocaml/gettext.ml:227 +msgid "Translation table and string table overlap ([%ld,%ld] and [%ld,%ld] have a non empty intersection)." +msgstr "" + +#: ../libgettext-ocaml/gettext.ml:208 +msgid "Trying to fetch the plural form %d of a singular form %S." +msgstr "" + +#: ../libgettext-ocaml/gettext.ml:211 +msgid "Trying to fetch the plural form %d of plural form %s." +msgstr "" + +#: ../libgettext-ocaml/gettext.ml:254 +msgid "Unable to parse the POSIX language environment variable %s" +msgstr "" + +#: ../libgettext-ocaml/gettext.ml:387 +msgid "codeset Set the default codeset for outputting string with ocaml-gettext. Default: %s." +msgstr "" + +#: ../libgettext-ocaml/gettext.ml:364 +msgid "dir Add a search dir for ocaml-gettext files. Default: %s." +msgstr "" + +#: ../libgettext-ocaml/gettext.ml:373 +msgid "language Set the default language for ocaml-gettext. Default: %s." +msgstr "" + +#: ../libgettext-ocaml/gettext.ml:176 +msgid "line %d character %d" +msgstr "" + +#: ../libgettext-ocaml/gettext.ml:343 +msgid "textdomain dir Set a dir to search ocaml-gettext files for the specified domain. Default: %s." +msgstr "" + diff --git a/test/Makefile b/test/Makefile new file mode 100644 index 0000000..97b6b6b --- /dev/null +++ b/test/Makefile @@ -0,0 +1,62 @@ +########################################################################## +# ocaml-gettext: a library to translate messages # +# # +# Copyright (C) 2003-2008 Sylvain Le Gall # +# # +# This library is free software; you can redistribute it and/or # +# modify it under the terms of the GNU Lesser General Public # +# License as published by the Free Software Foundation; either # +# version 2.1 of the License, or (at your option) any later version; # +# with the OCaml static compilation exception. # +# # +# This library 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 # +# Lesser General Public License for more details. # +# # +# You should have received a copy of the GNU Lesser General Public # +# License along with this library; if not, write to the Free Software # +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 # +# USA # +########################################################################## + +REQUIRES = fileutils gettext.extension gettext-camomile gettext-stub +TESTDIR = tests +CMO = common.cmo +ifeq ($(BUILD_BENCH),yes) +NAME = bench +CMO += bench.cmo +REQUIRES += benchmark +else +ifeq ($(BUILD_TEST),yes) +NAME = test +CMO += test.cmo +REQUIRES += oUnit +endif +endif + +include ../ConfMakefile + +ifneq ($(DONT_RECURSE),yes) +all: + $(MAKE) all BUILD_BENCH=$(BUILD_BENCH) DONT_RECURSE=yes + $(MAKE) all BUILD_TEST=$(BUILD_TEST) DONT_RECURSE=yes + +install: + $(MAKE) install BUILD_BENCH=$(BUILD_BENCH) DONT_RECURSE=yes + $(MAKE) install BUILD_TEST=$(BUILD_TEST) DONT_RECURSE=yes + +uninstall: + $(MAKE) uninstall BUILD_BENCH=$(BUILD_BENCH) DONT_RECURSE=yes + $(MAKE) uninstall BUILD_TEST=$(BUILD_TEST) DONT_RECURSE=yes + +clean:: + -$(MAKE) clean BUILD_BENCH=$(BUILD_BENCH) DONT_RECURSE=yes + -$(MAKE) clean BUILD_TEST=$(BUILD_TEST) DONT_RECURSE=yes + -$(RM) -r $(TESTDIR) + +distclean:: + +endif + +include ../TopMakefile diff --git a/test/TestGettext.ml b/test/TestGettext.ml new file mode 100644 index 0000000..908464f --- /dev/null +++ b/test/TestGettext.ml @@ -0,0 +1,7 @@ + +include Gettext.Library(struct + let textdomain = "ocaml-gettext" + let codeset = None + let dir = None + let dependencies = [] + end) diff --git a/test/bench.ml b/test/bench.ml new file mode 100644 index 0000000..8ced772 --- /dev/null +++ b/test/bench.ml @@ -0,0 +1,293 @@ +(**************************************************************************) +(* ocaml-gettext: a library to translate messages *) +(* *) +(* Copyright (C) 2003-2008 Sylvain Le Gall *) +(* *) +(* This library is free software; you can redistribute it and/or *) +(* modify it under the terms of the GNU Lesser General Public *) +(* License as published by the Free Software Foundation; either *) +(* version 2.1 of the License, or (at your option) any later version; *) +(* with the OCaml static compilation exception. *) +(* *) +(* This library 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 *) +(* Lesser General Public License for more details. *) +(* *) +(* You should have received a copy of the GNU Lesser General Public *) +(* License along with this library; if not, write to the Free Software *) +(* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *) +(* USA *) +(**************************************************************************) + +open Benchmark;; +open Common;; +open GettextTypes;; + + +type benchs = { + verbose : bool; + search_path : string list; + time : int; +} +;; + +let parse_arg () = + let benchs = ref { + verbose = false; + search_path = []; + time = 1; + } + in + Arg.parse (Arg.align [ + ( + "--search", Arg.String ( + fun dir -> + benchs := { !benchs with search_path = dir :: !benchs.search_path } + ) + ,"dir Search the specified directory for MO file." + ); + ( + "--verbose", Arg.Unit ( + fun () -> + benchs := { !benchs with verbose = true } + ) + ,"Processs with a lot of message." + ); + ( + "--time", Arg.Int ( + fun sec -> + benchs := { !benchs with time = sec } + ) + ,(Printf.sprintf "second Process each test during the specified number of second. Default : %d." + !benchs.time) + ); + ]) + ( fun str -> () ) + ("Benchmark utility for ocaml-gettext v"^(GettextConfig.version)^" by Sylvain Le Gall\n"^ + "Copyright (C) 2004-2008 Sylvain Le Gall \n"^ + "Licensed under LGPL v2.1 with Ocaml exception."); + !benchs +;; + +let print_debug benchs str = + if benchs.verbose then + (print_string str; print_newline ()) + else + () +;; + +let make_buffer lst = + (lst,[]) +;; + +let rec get_buffer (lst1,lst2) = + match (lst1,lst2) with + (hd :: tl,lst2) -> + (hd,(tl, hd :: lst2)) + | ([], hd :: tl) -> + (hd, (tl, [hd])) + | ([],[]) -> + failwith "Buffer is empty" +;; + +(* Generic function to benchmark gettextCompat function *) +let gettext_bench benchs str_gettext fun_gettext = + let f ref_translations = + let ((debug_str,t',textdomain,tr),buffer) = + get_buffer !ref_translations + in + print_debug benchs + (Printf.sprintf "Translation of %S from %s" (string_of_translation tr) debug_str); + ignore(fun_gettext t' textdomain tr); + ref_translations := buffer + in + let parameters_lst = + List.map parameters_of_filename mo_files_data + in + let create_translation (str_realize,realize) = + let rec create_one_translation accu lst = + match lst with + parameters :: tl -> + let t = + t_of_parameters parameters + in + let t' = + realize t + in + let new_accu = + List.fold_left ( fun lst tr -> + ( + (str_realize^" with textdomain "^parameters.textdomain), + t',parameters.textdomain,tr) + :: lst + ) accu parameters.translations + in + create_one_translation new_accu tl + | [] -> + make_buffer accu + in + ref (create_one_translation [] parameters_lst) + in + let bench_lst = + List.map ( fun (str_realize, realize) -> + str_realize, f, create_translation (str_realize,realize) + ) realize_data + in + print_debug benchs ("Benchmarking "^str_gettext^":"); + ( + str_gettext^" benchmark", + throughputN benchs.time bench_lst + ) +;; + +(*******************************) +(* Performance of check_format *) +(*******************************) + +let format_bench benchs = + let f ref_buffer = + let (elem,buffer) = + get_buffer !ref_buffer + in + let translation = + print_debug benchs ("Checking format of : "^(string_of_translation elem)); + GettextFormat.check_format Ignore elem + in + print_debug benchs ("Result of the check : "^(string_of_translation translation)); + ref_buffer := buffer + in + print_debug benchs "Benchmarking format :"; + ("Format benchmark", + throughputN benchs.time [ + ("Singular", f, ref (make_buffer format_translation_singular_data)); + ("Plural" , f, ref (make_buffer format_translation_plural_data)); + ("All" , f, ref (make_buffer format_translation_all_data)); + ] + ) +;; + +(***************************) +(* Performance of realize *) +(***************************) + +let realize_bench benchs = + let f (realize,parameters_lst) = + let f_one parameters = + let t = + print_debug benchs ("Creating t for "^parameters.fl_mo); + t_of_parameters parameters + in + print_debug benchs ("Realizing t for "^parameters.fl_mo); + ignore(realize t) + in + List.iter f_one parameters_lst + in + let parameters_lst = + List.map parameters_of_filename mo_files_data + in + let bench_lst = + List.map ( fun (str_implementation, realize) -> + str_implementation, f, (realize,parameters_lst) + ) realize_data + in + print_debug benchs "Benchmarking realize:"; + ( "Realize benchmark", + throughputN benchs.time bench_lst + ) +;; + +(**********************) +(* Performance of s_ *) +(**********************) + +let s_bench benchs = + let fun_gettext t' textdomain translation = + match translation with + | Singular(str,_) -> + ignore(GettextCompat.dgettext t' textdomain str) + | _ -> + () + in + gettext_bench benchs "s_" fun_gettext +;; + +(*********************) +(* Performance of f_ *) +(*********************) + +let f_bench benchs = + let fun_gettext t' textdomain translation = + match translation with + | Singular(str,_) -> + ignore(GettextCompat.fdgettext t' textdomain str) + | _ -> + () + in + gettext_bench benchs "f_" fun_gettext +;; + +(**********************) +(* Performance of sn_ *) +(**********************) + +let sn_bench benchs = + let fun_gettext t' textdomain translation = + match translation with + | Plural(str_id,str_plural,_) -> + ignore(GettextCompat.dngettext t' textdomain str_id str_plural 0); + ignore(GettextCompat.dngettext t' textdomain str_id str_plural 1); + ignore(GettextCompat.dngettext t' textdomain str_id str_plural 2); + ignore(GettextCompat.dngettext t' textdomain str_id str_plural 3) + | _ -> + () + in + gettext_bench benchs "sn_" fun_gettext +;; + +(**********************) +(* Performance of fn_ *) +(**********************) + +let fn_bench benchs = + let fun_gettext t' textdomain translation = + match translation with + | Plural(str_id,str_plural,_) -> + ignore(GettextCompat.fdngettext t' textdomain str_id str_plural 0); + ignore(GettextCompat.fdngettext t' textdomain str_id str_plural 1); + ignore(GettextCompat.fdngettext t' textdomain str_id str_plural 2); + ignore(GettextCompat.fdngettext t' textdomain str_id str_plural 3) + | _ -> + () + in + gettext_bench benchs ("fn_") fun_gettext +;; + +(**************************) +(* Main benchmark routine *) +(**************************) + +let benchs = parse_arg () +in +let all_bench = + [ + format_bench; + realize_bench; + s_bench; + f_bench; + sn_bench; + fn_bench; + ] +in +print_env "benchmarks"; +(* Running *) +let all_results = + List.map (fun x -> x benchs) all_bench +in +List.iter ( fun(str,results) -> + print_newline (); + print_newline (); + print_endline str; + tabulate results +) all_results + diff --git a/test/common.ml b/test/common.ml new file mode 100644 index 0000000..289ce22 --- /dev/null +++ b/test/common.ml @@ -0,0 +1,319 @@ +(**************************************************************************) +(* ocaml-gettext: a library to translate messages *) +(* *) +(* Copyright (C) 2003-2008 Sylvain Le Gall *) +(* *) +(* This library is free software; you can redistribute it and/or *) +(* modify it under the terms of the GNU Lesser General Public *) +(* License as published by the Free Software Foundation; either *) +(* version 2.1 of the License, or (at your option) any later version; *) +(* with the OCaml static compilation exception. *) +(* *) +(* This library 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 *) +(* Lesser General Public License for more details. *) +(* *) +(* You should have received a copy of the GNU Lesser General Public *) +(* License along with this library; if not, write to the Free Software *) +(* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *) +(* USA *) +(**************************************************************************) + +open GettextTypes;; +open GettextCategory;; +open FilePath.DefaultPath;; + +(* Version data *) +let print_env str = + print_endline ("Version : ocaml-gettext "^(GettextConfig.version)); + print_endline ("OS : "^(Sys.os_type)); + print_endline ("Running "^str^" ...") +;; + +(* Print a translation *) +let string_of_translation trans = + match trans with + Singular(str_id, str) -> + Printf.sprintf "Singular(%S, %S)" str_id str + | Plural(str_id, str_plural, lst) -> + Printf.sprintf "Plural(%S, %S, [ %s ])" str_id str_plural + (String.concat " ; " (List.map (fun x -> Printf.sprintf "%S" x) lst)) +;; + +(* Function for extracting all information of MO file *) +type parameters = { + fl_mo : filename; + base_dir : dir; + language : locale; + category : category; + textdomain : textdomain; + translations : translation list; +} +;; + +let parameters_of_filename fl_mo = + (* File scheme : + base_dir/lang/category/domain.mo + *) + let textdomain = + chop_extension (basename fl_mo) + in + let category = + GettextCategory.category_of_string (basename (dirname fl_mo)) + in + let language = + (basename (dirname (dirname (fl_mo)))) + in + let base_dir = + (dirname (dirname (dirname (fl_mo)))) + in + let (translations,_) = + GettextMo.fold_mo RaiseException + ( fun x lst -> x :: lst ) + [] + fl_mo + in + { + fl_mo = fl_mo; + base_dir = base_dir; + language = language; + category = category; + textdomain = textdomain; + translations = translations; + } +;; + +(* Build the parameter t out of parameters extracted above *) +let t_of_parameters parameters = + (* We use a UTF-8 binding, this is the most generic encoding + * for all strings *) + GettextModules.create + ~failsafe:RaiseException + ~codesets:[(parameters.textdomain,"UTF-8")] + ~path:[parameters.base_dir] + ~language:parameters.language + parameters.textdomain +;; + +(* Data for format test/bench *) +let format_translation_check_data = + [ + (* Identity *) + Singular("%d" ,"%d" ),Singular("%d" ,"%d" ); + Singular("%i" ,"%i" ),Singular("%i" ,"%i" ); + Singular("%n" ,"%n" ),Singular("%n" ,"%n" ); + Singular("%N" ,"%N" ),Singular("%N" ,"%N" ); + Singular("%u" ,"%u" ),Singular("%u" ,"%u" ); + Singular("%x" ,"%x" ),Singular("%x" ,"%x" ); + Singular("%X" ,"%X" ),Singular("%X" ,"%X" ); + Singular("%o" ,"%o" ),Singular("%o" ,"%o" ); + Singular("%s" ,"%s" ),Singular("%s" ,"%s" ); + Singular("%S" ,"%S" ),Singular("%S" ,"%S" ); + Singular("%c" ,"%c" ),Singular("%c" ,"%c" ); + Singular("%C" ,"%C" ),Singular("%C" ,"%C" ); + Singular("%f" ,"%f" ),Singular("%f" ,"%f" ); + Singular("%F" ,"%F" ),Singular("%F" ,"%F" ); + Singular("%e" ,"%e" ),Singular("%e" ,"%e" ); + Singular("%E" ,"%E" ),Singular("%E" ,"%E" ); + Singular("%g" ,"%g" ),Singular("%g" ,"%g" ); + Singular("%G" ,"%G" ),Singular("%G" ,"%G" ); + Singular("%B" ,"%B" ),Singular("%B" ,"%B" ); + Singular("%b" ,"%b" ),Singular("%b" ,"%b" ); + Singular("%ld","%ld"),Singular("%ld","%ld"); + Singular("%li","%li"),Singular("%li","%li"); + Singular("%lu","%lu"),Singular("%lu","%lu"); + Singular("%lx","%lx"),Singular("%lx","%lx"); + Singular("%lX","%lX"),Singular("%lX","%lX"); + Singular("%lo","%lo"),Singular("%lo","%lo"); + Singular("%nd","%nd"),Singular("%nd","%nd"); + Singular("%ni","%ni"),Singular("%ni","%ni"); + Singular("%nu","%nu"),Singular("%nu","%nu"); + Singular("%nx","%nx"),Singular("%nx","%nx"); + Singular("%nX","%nX"),Singular("%nX","%nX"); + Singular("%no","%no"),Singular("%no","%no"); + Singular("%Ld","%Ld"),Singular("%Ld","%Ld"); + Singular("%Li","%Li"),Singular("%Li","%Li"); + Singular("%Lu","%Lu"),Singular("%Lu","%Lu"); + Singular("%Lx","%Lx"),Singular("%Lx","%Lx"); + Singular("%LX","%LX"),Singular("%LX","%LX"); + Singular("%Lo","%Lo"),Singular("%Lo","%Lo"); + Singular("%a" ,"%a" ),Singular("%a" ,"%a" ); + Singular("%t" ,"%t" ),Singular("%t" ,"%t" ); + Singular("%!" ,"%!" ),Singular("%!" ,"%!" ); + Singular("%%" ,"%%" ),Singular("%%" ,"%%" ); + + (* Always fails *) + Singular("%d" ,"" ),Singular("%d" ,"%d" ); + Singular("%i" ,"" ),Singular("%i" ,"%i" ); + Singular("%n" ,"" ),Singular("%n" ,"%n" ); + Singular("%N" ,"" ),Singular("%N" ,"%N" ); + Singular("%u" ,"" ),Singular("%u" ,"%u" ); + Singular("%x" ,"" ),Singular("%x" ,"%x" ); + Singular("%X" ,"" ),Singular("%X" ,"%X" ); + Singular("%o" ,"" ),Singular("%o" ,"%o" ); + Singular("%s" ,"" ),Singular("%s" ,"%s" ); + Singular("%S" ,"" ),Singular("%S" ,"%S" ); + Singular("%c" ,"" ),Singular("%c" ,"%c" ); + Singular("%C" ,"" ),Singular("%C" ,"%C" ); + Singular("%f" ,"" ),Singular("%f" ,"%f" ); + Singular("%F" ,"" ),Singular("%F" ,"%F" ); + Singular("%e" ,"" ),Singular("%e" ,"%e" ); + Singular("%E" ,"" ),Singular("%E" ,"%E" ); + Singular("%g" ,"" ),Singular("%g" ,"%g" ); + Singular("%G" ,"" ),Singular("%G" ,"%G" ); + Singular("%B" ,"" ),Singular("%B" ,"%B" ); + Singular("%b" ,"" ),Singular("%b" ,"%b" ); + Singular("%ld","" ),Singular("%ld","%ld"); + Singular("%li","" ),Singular("%li","%li"); + Singular("%lu","" ),Singular("%lu","%lu"); + Singular("%lx","" ),Singular("%lx","%lx"); + Singular("%lX","" ),Singular("%lX","%lX"); + Singular("%lo","" ),Singular("%lo","%lo"); + Singular("%nd","" ),Singular("%nd","%nd"); + Singular("%ni","" ),Singular("%ni","%ni"); + Singular("%nu","" ),Singular("%nu","%nu"); + Singular("%nx","" ),Singular("%nx","%nx"); + Singular("%nX","" ),Singular("%nX","%nX"); + Singular("%no","" ),Singular("%no","%no"); + Singular("%Ld","" ),Singular("%Ld","%Ld"); + Singular("%Li","" ),Singular("%Li","%Li"); + Singular("%Lu","" ),Singular("%Lu","%Lu"); + Singular("%Lx","" ),Singular("%Lx","%Lx"); + Singular("%LX","" ),Singular("%LX","%LX"); + Singular("%Lo","" ),Singular("%Lo","%Lo"); + Singular("%a" ,"" ),Singular("%a" ,"%a" ); + Singular("%t" ,"" ),Singular("%t" ,"%t" ); + + (* Mismatch *) + Singular("%d" ,"%i" ),Singular("%d" ,"%d" ); + Singular("%i" ,"%d" ),Singular("%i" ,"%i" ); + Singular("%n" ,"%d" ),Singular("%n" ,"%n" ); + Singular("%N" ,"%d" ),Singular("%N" ,"%N" ); + Singular("%u" ,"%d" ),Singular("%u" ,"%u" ); + Singular("%x" ,"%d" ),Singular("%x" ,"%x" ); + Singular("%X" ,"%d" ),Singular("%X" ,"%X" ); + Singular("%o" ,"%d" ),Singular("%o" ,"%o" ); + Singular("%s" ,"%d" ),Singular("%s" ,"%s" ); + Singular("%S" ,"%d" ),Singular("%S" ,"%S" ); + Singular("%c" ,"%d" ),Singular("%c" ,"%c" ); + Singular("%C" ,"%d" ),Singular("%C" ,"%C" ); + Singular("%f" ,"%d" ),Singular("%f" ,"%f" ); + Singular("%F" ,"%d" ),Singular("%F" ,"%F" ); + Singular("%e" ,"%d" ),Singular("%e" ,"%e" ); + Singular("%E" ,"%d" ),Singular("%E" ,"%E" ); + Singular("%g" ,"%d" ),Singular("%g" ,"%g" ); + Singular("%G" ,"%d" ),Singular("%G" ,"%G" ); + Singular("%B" ,"%d" ),Singular("%B" ,"%B" ); + Singular("%b" ,"%d" ),Singular("%b" ,"%b" ); + Singular("%ld","%d" ),Singular("%ld","%ld"); + Singular("%li","%d" ),Singular("%li","%li"); + Singular("%lu","%d" ),Singular("%lu","%lu"); + Singular("%lx","%d" ),Singular("%lx","%lx"); + Singular("%lX","%d" ),Singular("%lX","%lX"); + Singular("%lo","%d" ),Singular("%lo","%lo"); + Singular("%nd","%d" ),Singular("%nd","%nd"); + Singular("%ni","%d" ),Singular("%ni","%ni"); + Singular("%nu","%d" ),Singular("%nu","%nu"); + Singular("%nx","%d" ),Singular("%nx","%nx"); + Singular("%nX","%d" ),Singular("%nX","%nX"); + Singular("%no","%d" ),Singular("%no","%no"); + Singular("%Ld","%d" ),Singular("%Ld","%Ld"); + Singular("%Li","%d" ),Singular("%Li","%Li"); + Singular("%Lu","%d" ),Singular("%Lu","%Lu"); + Singular("%Lx","%d" ),Singular("%Lx","%Lx"); + Singular("%LX","%d" ),Singular("%LX","%LX"); + Singular("%Lo","%d" ),Singular("%Lo","%Lo"); + Singular("%a" ,"%d" ),Singular("%a" ,"%a" ); + Singular("%t" ,"%d" ),Singular("%t" ,"%t" ); + Singular("%!" ,"%d" ),Singular("%!" ,"%!" ); + Singular("%%" ,"%d" ),Singular("%%" ,"%%" ); + + (* All in one *) + Singular("%d %i %n %N %u %x %X %o %s %S %c %C " + ^"%f %F %e %E %g %G %B %b %ld %li %lu %lx %lX " + ^"%lo %nd %ni %nu %nx %nX %no %Ld %Li %Lu %Lx " + ^"%LX %Lo %a %t %! %%", + "%d %i %n %N %u %x %X %o %s %S %c %C " + ^"%f %F %e %E %g %G %B %b %ld %li %lu %lx %lX " + ^"%lo %nd %ni %nu %nx %nX %no %Ld %Li %Lu %Lx " + ^"%LX %Lo %a %t %! %%"), + Singular("%d %i %n %N %u %x %X %o %s %S %c %C " + ^"%f %F %e %E %g %G %B %b %ld %li %lu %lx %lX " + ^"%lo %nd %ni %nu %nx %nX %no %Ld %Li %Lu %Lx " + ^"%LX %Lo %a %t %! %%", + "%d %i %n %N %u %x %X %o %s %S %c %C " + ^"%f %F %e %E %g %G %B %b %ld %li %lu %lx %lX " + ^"%lo %nd %ni %nu %nx %nX %no %Ld %Li %Lu %Lx " + ^"%LX %Lo %a %t %! %%"); + Singular("%d %i %n %N %u %x %X %o %s %S %c %C " + ^"%f %F %e %E %g %G %B %b %ld %li %lu %lx %lX " + ^"%lo %nd %ni %nu %nx %nX %no %Ld %Li %Lu %Lx " + ^"%LX %Lo %a %t %! %%", + "%d %i %n %N %u %x %X %o %s %S %c %C " + ^"%f %F %e %E g %G %B %b %ld %li %lu %lx %lX " + ^"lo nd ni nu nx %nX %no %Ld %Li %Lu %Lx " + ^"%LX %Lo %a %t %! %%"), + Singular("%d %i %n %N %u %x %X %o %s %S %c %C " + ^"%f %F %e %E %g %G %B %b %ld %li %lu %lx %lX " + ^"%lo %nd %ni %nu %nx %nX %no %Ld %Li %Lu %Lx " + ^"%LX %Lo %a %t %! %%", + "%d %i %n %N %u %x %X %o %s %S %c %C " + ^"%f %F %e %E %g %G %B %b %ld %li %lu %lx %lX " + ^"%lo %nd %ni %nu %nx %nX %no %Ld %Li %Lu %Lx " + ^"%LX %Lo %a %t %! %%"); + + (* Plural forms *) + Plural("singular %d","plural %i",["%d";"%d"]),Plural("singular %d","singular %d",["%d";"%d"]); + Plural("singular %d","plural %d",["%i";"%d"]),Plural("singular %d","plural %d",["singular %d";"%d"]); + Plural("singular %d","plural %d",["%d";"%i"]),Plural("singular %d","plural %d",["%d";"plural %d"]); + Plural("singular %d","plural %d",["%d";"%d"]),Plural("singular %d","plural %d",["%d";"%d"]); + + (* Idem potent *) + Singular("%%",""),Singular("%%",""); + Singular("%!",""),Singular("%!",""); + Singular("",""),Singular("",""); + Singular("a","b"),Singular("a","b"); + ] +;; + +let format_translation_all_data = + List.fold_left ( fun lst (a,b) -> a :: b :: lst ) + [] format_translation_check_data +;; + +let format_translation_plural_data = + List.filter ( function Plural(_,_,_) -> true | _ -> false ) + format_translation_all_data +;; + +let format_translation_singular_data = + List.filter ( function Singular(_,_) -> true | _ -> false ) + format_translation_all_data +;; + + +(* Files installed for testing purpose. "." refers to the directory + where common.ml is installed. *) +let mo_files_data = + [ + make_filename ["." ; "fr_FR" ; "LC_MESSAGES" ; "test1.mo" ]; + make_filename ["." ; "fr_FR" ; "LC_MESSAGES" ; "test2.mo" ]; + make_filename ["." ; "fr_FR" ; "LC_MESSAGES" ; "test3.mo" ]; + make_filename ["." ; "fr_FR" ; "LC_MESSAGES" ; "test4.mo" ]; + make_filename ["." ; "fr_FR" ; "LC_MESSAGES" ; "test10.mo" ]; + make_filename ["." ; "fr_FR" ; "LC_MESSAGES" ; "test11.mo" ]; + ] +;; + +(* Different implementation of realize. *) +let realize_data = + [ + ("Camomile.Map", GettextCamomile.Map.realize); + ("Camomile.Hashtbl", GettextCamomile.Hashtbl.realize); + ("Camomile.Open", GettextCamomile.Open.realize); + ("Stub.Native", GettextStub.Native.realize); + ("Stub.Preload", GettextStub.Preload.realize); + ] +;; + diff --git a/test/escape-char.ml b/test/escape-char.ml new file mode 100644 index 0000000..be50f12 --- /dev/null +++ b/test/escape-char.ml @@ -0,0 +1,13 @@ + +let _ = + [ + s_ "hello world!\n"; + s_ "goodbye world! +"; + s_ "goodby world 2!\ + "; + s_ "and then\tbye-bye"; + ] +;; + + diff --git a/test/fr_FR/LC_MESSAGES/test1.mo b/test/fr_FR/LC_MESSAGES/test1.mo new file mode 100644 index 0000000..77555c7 Binary files /dev/null and b/test/fr_FR/LC_MESSAGES/test1.mo differ diff --git a/test/fr_FR/LC_MESSAGES/test10.mo b/test/fr_FR/LC_MESSAGES/test10.mo new file mode 100644 index 0000000..77555c7 Binary files /dev/null and b/test/fr_FR/LC_MESSAGES/test10.mo differ diff --git a/test/fr_FR/LC_MESSAGES/test11.mo b/test/fr_FR/LC_MESSAGES/test11.mo new file mode 100644 index 0000000..cc4b53c Binary files /dev/null and b/test/fr_FR/LC_MESSAGES/test11.mo differ diff --git a/test/fr_FR/LC_MESSAGES/test2.mo b/test/fr_FR/LC_MESSAGES/test2.mo new file mode 100644 index 0000000..fa6091e Binary files /dev/null and b/test/fr_FR/LC_MESSAGES/test2.mo differ diff --git a/test/fr_FR/LC_MESSAGES/test3.mo b/test/fr_FR/LC_MESSAGES/test3.mo new file mode 100644 index 0000000..1dabcf7 Binary files /dev/null and b/test/fr_FR/LC_MESSAGES/test3.mo differ diff --git a/test/fr_FR/LC_MESSAGES/test4.mo b/test/fr_FR/LC_MESSAGES/test4.mo new file mode 100644 index 0000000..189189a Binary files /dev/null and b/test/fr_FR/LC_MESSAGES/test4.mo differ diff --git a/test/invalid_format1.ml b/test/invalid_format1.ml new file mode 100644 index 0000000..7d906da --- /dev/null +++ b/test/invalid_format1.ml @@ -0,0 +1,6 @@ +open TestGettext;; +open Printf;; + +let () = + eprintf (f_ "%ld") 2 +;; diff --git a/test/invalid_format2.ml b/test/invalid_format2.ml new file mode 100644 index 0000000..d8dc8ae --- /dev/null +++ b/test/invalid_format2.ml @@ -0,0 +1,6 @@ +open TestGettext;; +open Printf;; + +let () = + eprintf (f_ "%Ld") 2 +;; diff --git a/test/invalid_format3.ml b/test/invalid_format3.ml new file mode 100644 index 0000000..99c5688 --- /dev/null +++ b/test/invalid_format3.ml @@ -0,0 +1,6 @@ +open TestGettext;; +open Printf;; + +let () = + eprintf (f_ "%s") 2 +;; diff --git a/test/invalid_format4.ml b/test/invalid_format4.ml new file mode 100644 index 0000000..2c415bc --- /dev/null +++ b/test/invalid_format4.ml @@ -0,0 +1,6 @@ +open TestGettext;; +open Printf;; + +let () = + eprintf (f_ "%b") 2 +;; diff --git a/test/invalid_format5.ml b/test/invalid_format5.ml new file mode 100644 index 0000000..fa31a78 --- /dev/null +++ b/test/invalid_format5.ml @@ -0,0 +1,6 @@ +open TestGettext;; +open Printf;; + +let () = + eprintf (fn_ "%d category" "%Ld categories" 1) 1 +;; diff --git a/test/multiline-comment.po b/test/multiline-comment.po new file mode 100644 index 0000000..c2fcc3a --- /dev/null +++ b/test/multiline-comment.po @@ -0,0 +1,18 @@ +msgid "" +msgstr "" +"Project-Id-Version: oji 0.1\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2005-05-27 02:37+0200\n" +"PO-Revision-Date: 2005-05-27 02:39+0200\n" +"Last-Translator: French \n" +"Language-Team: French\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=ISO8859-1\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n>1;" + +#: ../dlg_test.ml:83 ../dlg_config.ml:126 ../dlg_browser.ml:76 +#. ../dlg_editor.ml:70 +msgid "vocabulary" +msgstr "vocabulaire" + diff --git a/test/test.ml b/test/test.ml new file mode 100644 index 0000000..bf59889 --- /dev/null +++ b/test/test.ml @@ -0,0 +1,907 @@ +(**************************************************************************) +(* ocaml-gettext: a library to translate messages *) +(* *) +(* Copyright (C) 2003-2008 Sylvain Le Gall *) +(* *) +(* This library is free software; you can redistribute it and/or *) +(* modify it under the terms of the GNU Lesser General Public *) +(* License as published by the Free Software Foundation; either *) +(* version 2.1 of the License, or (at your option) any later version; *) +(* with the OCaml static compilation exception. *) +(* *) +(* This library 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 *) +(* Lesser General Public License for more details. *) +(* *) +(* You should have received a copy of the GNU Lesser General Public *) +(* License along with this library; if not, write to the Free Software *) +(* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *) +(* USA *) +(**************************************************************************) + +open OUnit;; +open FileUtil;; +open FilePath;; +open GettextTypes;; +open GettextCategory;; +open Common;; + +type tests = { + verbose : bool; + search_path : string list; + ocaml_xgettext : string; + ocaml_gettext : string; + test_dir : string; +} +;; + +let parse_arg () = + let tests = ref { + verbose = false; + search_path = []; + ocaml_xgettext = make_filename [ parent_dir ; "_build" ; "bin" ; "ocaml-xgettext"]; + ocaml_gettext = make_filename [ parent_dir ; "_build" ; "bin" ; "ocaml-gettext"]; + test_dir = make_filename [ current_dir ; "tests" ]; + } + in + Arg.parse ( Arg.align [ + ( + "--search", Arg.String ( + fun dir -> + tests := { !tests with search_path = dir :: !tests.search_path } + ) + ,"dir Search the specified directory for MO file." + ); + ( + "--verbose", Arg.Unit ( + fun () -> + tests := { !tests with verbose = true } + ) + ,"Processs with a lot of message." + ); + ( + "--ocaml-xgettext", Arg.String ( + fun s -> + tests := { !tests with ocaml_xgettext = s } + ) + ,"cmd Specify the ocaml-xgettext executable." + ); + ( + "--ocaml-gettext", Arg.String ( + fun s -> + tests := { !tests with ocaml_gettext = s } + ) + ,"cmd Specify the ocaml-gettext executable." + ); + ( + "--test-dir", Arg.String ( + fun s -> + tests := { !tests with test_dir = s } + ) + ,"dir Specify the temporary dir for testing files." + ); + ]) + (fun str -> ()) + ("Test utility for ocaml-gettext v"^(GettextConfig.version)^" by Sylvain Le Gall\n"^ + "Copyright (C) 2004-2008 Sylvain Le Gall \n"^ + "Licensed under LGPL v2.1 with OCaml exception."); + !tests +;; + +let print_debug tests str = + if tests.verbose then + (print_string str; print_newline ()) + else + () +;; + +let run_and_read prog ?(env=[||]) cli = + + (* Temporary file to retain data from command *) + let fn_out, chn_out = + Filename.open_temp_file "ocaml-gettext-out" ".txt" + in + let fn_err, chn_err = + Filename.open_temp_file "ocaml-gettext-err" ".txt" + in + + (* Clean after run *) + let cleanup () = + let safe f a = + try + f a + with _ -> + () + in + safe close_out chn_out; safe close_out chn_err; + safe Sys.remove fn_out; safe Sys.remove fn_err; + in + + let input_fn fn = + let chn_in = + open_in fn + in + let buff = + Buffer.create 13 + in + Buffer.add_channel buff chn_in (in_channel_length chn_in); + close_in chn_in; + Buffer.contents buff + in + + try + let stdin_in, stdin_out = + Unix.pipe () + in + + let command_array = + (Array.of_list (prog :: cli)) + in + let command = + String.concat " " (Array.to_list command_array) + in + + let pid = + Unix.create_process_env + prog + command_array + env + stdin_in + (Unix.descr_of_out_channel chn_out) + (Unix.descr_of_out_channel chn_err) + in + let () = + Unix.close stdin_in; Unix.close stdin_out + in + let return_code = + match snd (Unix.waitpid [] pid) with + | Unix.WEXITED code + | Unix.WSIGNALED code + | Unix.WSTOPPED code -> + code + in + let err = + close_out chn_err; + input_fn fn_err + in + let out = + close_out chn_out; + input_fn fn_out + in + cleanup (); + command, return_code, out, err + with e -> + ( + cleanup (); + raise e + ) +;; + +let load_mo_file tests f_test_mo fl_mo = + let mo = + open_in_bin fl_mo + in + let mo_header = + GettextMo.input_mo_header mo + in + let mo_informations = + GettextMo.input_mo_informations + RaiseException + mo + mo_header + in + print_debug + tests + (GettextMo.string_of_mo_header mo_header); + print_debug + tests + (GettextMo.string_of_mo_informations mo_informations); + close_in mo; + f_test_mo fl_mo +;; + +(**********************************) +(* Test of Printf format checking *) +(**********************************) + +let format_test tests = + let format_test_one (trans_src,trans_dst) = + let lst_str_equal lst1 lst2 = + try + List.fold_left2 ( fun b str1 str2 -> b && str1 = str2 ) + true lst1 lst2 + with Invalid_argument _ -> + false + in + (Printf.sprintf "%s -> %s format checking" + (string_of_translation trans_src) + (string_of_translation trans_dst)) >:: + ( fun () -> + let trans_res = + GettextFormat.check_format Ignore trans_src + in + match (trans_res,trans_dst) with + Singular(str_id1,str1),Singular(str_id2,str2) when + str_id1 = str_id2 && str1 = str2 -> + () + | Plural(str_id1,str_plural1,lst1),Plural(str_id2,str_plural2,lst2) when + str_id1 = str_id2 && str_plural1 = str_plural2 + && lst_str_equal lst1 lst2 -> + () + | trans1, trans2 -> + assert_failure ((string_of_translation trans1) + ^" differs from " + ^(string_of_translation trans2)) + ) + in + "Printf format test" >::: + ( + List.map format_test_one format_translation_check_data + ) +;; + +(**************************) +(* Split plural functions *) +(**************************) + +let split_plural_test tests = + let split_plural_test_one (str,res_lst) = + (Printf.sprintf "Split plural test %S" str) >:: + ( fun () -> + let lst = GettextUtils.split_plural str + in + List.iter2 ( fun str1 str2 -> + print_debug tests (Printf.sprintf "Extracted : %S" str2); + print_debug tests (Printf.sprintf "Expected : %S" str1); + if str1 = str2 then + () + else + assert_failure (Printf.sprintf "%S should be %S" str2 str1) + ) res_lst lst + ) + in + "Split plural test" >::: + List.map split_plural_test_one [ + ("%d coffee\000more %d coffee",["%d coffee"; "more %d coffee"]) + ] +;; + +(*************************) +(* Test of PO processing *) +(*************************) + +let po_test tests = + let po_test_one f_test_mo fl_po = + let fl_mo = + concat + tests.test_dir + (replace_extension fl_po "mo") + in + (Printf.sprintf "Load and compile %s" fl_po) >:: + (fun () -> + let chn = + open_in fl_po + in + ignore (GettextPo.input_po chn); + close_in chn; + GettextCompile.compile fl_po fl_mo; + load_mo_file tests f_test_mo fl_mo) + in + "PO processing test" >::: + (List.map (po_test_one ignore) + [ + "test1.po"; + "test2.po"; + "test3.po"; + "test4.po"; + "test11.po"; + ]) + @ + [ + po_test_one + (fun fl_mo -> + let (), _ = + GettextMo.fold_mo + RaiseException + (fun trslt () -> + match trslt with + | Singular(id, "") + | Plural(id, _, []) + | Plural(id, _, [""]) -> + assert_failure + (Printf.sprintf + "%s contains an empty translation for %s" + fl_mo + id) + | _ -> + ()) + () + fl_mo + in + ()) + "test12.po" + ] +;; + + + + +(****************************************************) +(* Test compatibility with already produced mo file *) +(****************************************************) + +let compatibility_test tests = + let test_one_mo fl = + (Printf.sprintf "Loading %s" fl) >:: + (fun () -> load_mo_file tests ignore fl) + in + "Test compatibility" >::: + List.fold_left + (fun lst dir -> + find + (Has_extension "mo") + dir + (fun lst fln -> (test_one_mo fln) :: lst) + lst) + [] + tests.search_path +;; + +(*******************************************) +(* Test of OCaml source file PO extraction *) +(*******************************************) + +let extract_test tests = + let default_options = + "-I +camlp4 pa_o.cmo" + in + let filename_options = + MapString.empty + in + let extract_test_one (fl_ml, contents) = + let fl_pot = + concat tests.test_dir (replace_extension fl_ml "pot") + in + fl_ml >::: + [ + "Extracting" >:: + (fun () -> + ( + (* Extract data from files *) + try + GettextCompile.extract + tests.ocaml_xgettext + default_options + filename_options + [fl_ml] + fl_pot + with x -> + assert_failure + (fl_ml^" doesn't extract correctly: "^ + (Gettext.string_of_exception x)) + ); + (* Load POT file *) + let po = + let chn = + open_in fl_pot + in + let res = + GettextPo.input_po chn + in + close_in chn; + res + in + + (* Check result *) + List.iter + (fun str -> + if MapString.mem str po.no_domain then + () + else + assert_failure + (Printf.sprintf + "Cannot find %S in %s" + str + fl_pot)) + contents + ) + ] + in + "OCaml file extraction test" >::: + List.map extract_test_one + [ + "test4.ml", []; + "escape-char.ml", ["hello world!\n"; + "goodbye world!\n"; + "goodby world 2!"; + "and then\tbye-bye"]; + ] +;; + +(********************************) +(* Test of MO file installation *) +(********************************) + +let install_test tests = + let install_test_one (language, category, textdomain, fl_mo, fl_dsts) = + fl_mo >:: + (fun () -> + let fl_dst = + make_filename fl_dsts + in + GettextCompile.install + true + tests.test_dir + language + category + textdomain + fl_mo; + assert_bool + (Printf.sprintf "%s is not installed at %s" fl_mo fl_dst) + (test Exists fl_dst)) + in + + let install_fail_test_one (fl_mo, exc) = + let error = + Printexc.to_string exc + in + (Printf.sprintf "%s (%s)" fl_mo error) >:: + (fun () -> + assert_raises + ~msg:(Printf.sprintf + "Installation of %s should have failed with error %s" + fl_mo error) + exc + (fun () -> + GettextCompile.install + true + tests.test_dir + "fr" + LC_MESSAGES + "gettext-fail" + fl_mo)) + in + + let install_warning_test_one (language, category, textdomain, fl_mo, exp_err, fl_dsts) = + (Printf.sprintf "%s warning" fl_mo) >:: + (fun () -> + let command, return_code, out, err = + run_and_read + tests.ocaml_gettext + [ + "--action"; "install"; + "--install-language"; language; + "--install-category"; category; + "--install-textdomain"; textdomain; + "--install-destdir"; tests.test_dir; + fl_mo + ] + in + let fl_dst = + make_filename fl_dsts + in + assert_equal + ~msg:(command^" return code") + ~printer:string_of_int + 0 + return_code; + assert_equal + ~msg:(command^" standard output") + ~printer:(Printf.sprintf "%S") + "" + out; + assert_equal + ~msg:(command^" error output") + ~printer:(Printf.sprintf "%S") + exp_err + err; + assert_bool + (Printf.sprintf "File %s doesn't exist" fl_dst) + (test Exists fl_dst)) + in + + "MO file installation test" >::: + (List.map install_test_one + [ + "fr",LC_MESSAGES, "gettext-test1", concat tests.test_dir "test1.mo", + [tests.test_dir; "fr"; "LC_MESSAGES"; "gettext-test1.mo"]; + + "fr_FR",LC_MESSAGES, "gettext-test2", concat tests.test_dir "test2.mo", + [tests.test_dir; "fr_FR"; "LC_MESSAGES"; "gettext-test2.mo"]; + + "fr",LC_TIME, "gettext-test3", concat tests.test_dir "test3.mo", + [tests.test_dir; "fr"; "LC_TIME"; "gettext-test3.mo"]; + + "fr_FR@euro",LC_MESSAGES, "gettext-test4", concat tests.test_dir "test4.mo", + [tests.test_dir; "fr_FR@euro"; "LC_MESSAGES"; "gettext-test4.mo"]; + ]) @ + (List.map install_fail_test_one + [ + "test5.mo", + MoInvalidFile; + + "test6.mo", + MoInvalidHeaderTableStringOutOfBound((28l, 2626l),(-1l, 159l)); + + "test7.mo", + MoInvalidHeaderTableTranslationOutOfBound((28l, 2626l),(-49l, 111l)); + + "test8.mo", + MoInvalidStringOutOfBound(2626, 36); + + "test9.mo", + MoInvalidTranslationOutOfBound(2626, 196); + ]) @ + (List.map install_warning_test_one + [ + "fr", "LC_MESSAGES", "test10", "test10.mo", + "Error while processing parsing of plural at line 1 character 10: \" nplurals=INTEGER; plural=EXPRESSION;\".\n", + [tests.test_dir; "fr"; "LC_MESSAGES"; "test10.mo"]; + ]) +;; + +(************************) +(* Test of POT/PO merge *) +(************************) + +let merge_test tests = + let merge_one (fl_pot,fl_po,backup_ext) = + (fl_pot^"+"^fl_po) >::: + [ + "Merging" >:: + ( fun () -> + try + (* Copying the file to the good place *) + let fl_po_cp = + concat tests.test_dir fl_po + in + let () = + cp [fl_po] fl_po_cp + in + let fl_backup = + add_extension fl_po_cp backup_ext + in + GettextCompile.merge fl_pot [fl_po_cp] backup_ext; + ( + match cmp fl_po fl_po_cp with + Some -1 -> + assert_failure (fl_po^" or "^fl_po_cp^" doesn't exist") + | Some x -> + assert_failure (fl_po^" differs from "^fl_po_cp) + | None -> + () + ); + ( + match cmp fl_po fl_backup with + Some -1 -> + assert_failure (fl_po^" or "^fl_backup^" doesn't exist") + | Some x -> + assert_failure (fl_po^" differs from "^fl_backup) + | None -> + () + ) + with x -> + assert_failure ("Unexpected error while processing "^fl_po + ^" ( "^(Printexc.to_string x)^" )") + ); + ] + in + "POT/PO file merge test" >::: + List.map merge_one [ (concat tests.test_dir "test4.pot","test4.po","bak") ] +;; + +(**********************************) +(* Test of Gettext implementation *) +(**********************************) + +let implementation_test tests = + (* Generate a test case of simple load of a MO file using an implementation *) + let test_load parameters_lst (realize_str,realize) = + let test_load_one realize parameters = + (* Extract usefull information out of parameters *) + let fl_mo = + parameters.fl_mo + in + let test_translations = + parameters.translations + in + (* Build t *) + let t = + t_of_parameters parameters + in + (* Build t' *) + let t' = + realize t + in + let test_one_translation translation = + (* We cannot compare directly extracted values and t' extracted + value , since we have a charset translation *) + try + match translation with + Singular(str_id,_) -> + ignore(GettextCompat.gettext t' str_id) + | Plural(str_id,str_plural,_) -> + (* Using values from 0 to 2, we cover most of the plural cases *) + ignore(GettextCompat.ngettext t' str_id str_plural 0); + ignore(GettextCompat.ngettext t' str_id str_plural 1); + ignore(GettextCompat.ngettext t' str_id str_plural 2) + with exc -> + assert_failure ((Printexc.to_string exc)^" in " + ^(string_of_translation translation)) + in + fl_mo >:: + ( fun () -> + List.iter test_one_translation test_translations + ) + in + realize_str >::: + List.map (test_load_one realize) parameters_lst + in + (* Generate a cross test of string extracted, using different implementation *) + let test_cross implementation_lst parameters = + (* Extract usefull information out of parameters *) + let fl_mo = + parameters.fl_mo + in + let test_translations= + parameters.translations + in + (* Build t *) + let t = + t_of_parameters parameters + in + (* Build all t' *) + let t'_lst = + List.map + (fun (realize_str,realize) -> (realize_str,realize t)) + implementation_lst + in + let check_translation str lst = + let (_,same_str) = + List.fold_left ( + fun (prev_str_opt,res) (_,cur_str) -> + match prev_str_opt with + Some prev_str -> + (Some cur_str, res && prev_str = cur_str) + | None -> + (Some cur_str, res) + ) (None,true) lst + in + if same_str then + () + else + assert_failure + ( + Printf.sprintf + "All values should be identical in [ %s ] in function %s" + ( + String.concat " ; " + ( + List.map ( fun (realize_str,str) -> + Printf.sprintf "(%s,%S)" realize_str str + ) lst + ) + ) + str + ) + in + let test_cross_one translation = + match translation with + Singular(str_id,_) -> + check_translation + ( + Printf.sprintf "GettextCompat.gettext t' %S" str_id + ) + ( + List.map ( + fun (realize_str,t') -> + (realize_str,GettextCompat.gettext t' str_id) + ) t'_lst + ) + | Plural(str_id,str_plural,_) -> + List.iter ( + fun n -> + check_translation + ( + Printf.sprintf "GettextCompat.ngettext t' %S %S %d" + str_id str_plural n + ) + ( + List.map ( + fun (realize_str,t') -> + (realize_str,GettextCompat.ngettext t' str_id str_plural n) + ) t'_lst + ) + ) [ 0 ; 1 ; 2 ] + in + fl_mo >:: + ( fun () -> + List.iter test_cross_one test_translations + ) + in + (* Extract and test *) + let parameters_lst = + List.map parameters_of_filename mo_files_data + in + let implementation_lst = + realize_data + in + "Gettext implementation test" >::: + [ + "Load" >::: + List.map (test_load parameters_lst) implementation_lst; + "Cross check" >::: + List.map (test_cross implementation_lst) parameters_lst; + ] +;; + +(**********************************) +(* Test for PO processing comment *) +(**********************************) + +let po_process_test tests = + let copy_merge_compare fn_po = + let src_po = + make_filename [current_dir; fn_po] + in + let tgt_po = + make_filename [tests.test_dir; fn_po] + in + cp [src_po] tgt_po; + GettextCompile.merge tgt_po [tgt_po] "bak"; + match cmp tgt_po src_po with + | Some -1 -> + assert_failure (tgt_po^" or "^src_po^" doesn't exist") + | Some x -> + assert_failure (tgt_po^" differs from "^src_po) + | None -> + () + in + "Gettext po process test" >::: + [ + "multiline-comment.po" >:: + ( + fun () -> + copy_merge_compare "multiline-comment.po" + ); + "utf8-fr.po" >:: + ( + fun () -> + copy_merge_compare "utf8-fr.po" + ); + "utf8-ja.po" >:: + ( + fun () -> + copy_merge_compare "utf8-ja.po" + ); + ] +;; + +(********************************************) +(* Test for running ocaml-gettext program *) +(* (spot also problem with runtime behavior *) +(* of ocaml-gettext library) *) +(********************************************) + +let run_ocaml_gettext tests = + "Running ocaml-gettext" >::: + [ + "ocaml-gettext with LC_ALL and LANG unset" >:: + (fun () -> + let command, return_code, _, _ = + run_and_read + tests.ocaml_gettext + ["--help"] + in + assert_equal + ~msg:("return code of "^command) + ~printer:string_of_int + 0 + return_code) + ] +;; + + +(************************************************) +(* Set bad locale and spot error when setlocale *) +(* returns NULL *) +(************************************************) + +let bad_setlocale tests = + "Call setlocale with bad locale" >::: + [ + "setlocale with bad locale" >:: + ( + fun () -> + ignore (GettextStubCompat.setlocale GettextStubCompat.LC_ALL "xx"); + ) + ] +;; + +(******************) +(* Try to compile *) +(******************) + +let compile_ocaml _ = + "Compile OCaml code" >::: + (List.map + (fun (fn, exp_return_code, exp_out, exp_err) -> + fn >:: + (fun () -> + let command, return_code, out, err = + run_and_read + "ocamlc" + ["-c"; "-I"; "../libgettext-ocaml/"; "-I"; "../libgettext-stub-ocaml/"; "TestGettext.ml"; fn] + in + FileUtil.rm (List.map (FilePath.replace_extension fn) ["cmo"; "cmi"]); + FileUtil.rm (List.map (FilePath.add_extension "TestGettext") ["cmo"; "cmi"]); + assert_equal + ~msg:("error output of "^command) + ~printer:(Printf.sprintf "%S") + exp_err + err; + assert_equal + ~msg:("standard output of "^command) + ~printer:(Printf.sprintf "%S") + exp_out + out; + assert_equal + ~msg:("return code of "^command) + ~printer:string_of_int + exp_return_code + return_code)) + [ + "unsound_warning.ml", 0, "", ""; + "valid_format.ml", 0, "", ""; + "invalid_format1.ml", 2, "", + "File \"invalid_format1.ml\", line 5, characters 21-22:\n\ + Error: This expression has type int but an expression was expected of type\n\ + \ int32\n"; + "invalid_format2.ml", 2, "", + "File \"invalid_format2.ml\", line 5, characters 21-22:\n\ + Error: This expression has type int but an expression was expected of type\n\ + \ int64\n"; + "invalid_format3.ml", 2, "", + "File \"invalid_format3.ml\", line 5, characters 20-21:\n\ + Error: This expression has type int but an expression was expected of type\n\ + \ string\n"; + "invalid_format4.ml", 2, "", + "File \"invalid_format4.ml\", line 5, characters 20-21:\n\ + Error: This expression has type int but an expression was expected of type\n\ + \ bool\n"; + "invalid_format5.ml", 2, "", + "File \"invalid_format5.ml\", line 5, characters 29-45:\n\ + Error: This expression has type (int64 -> 'a, 'b, 'c, 'd, 'd, 'a) format6\n\ + \ but an expression was expected of type\n\ + \ (int -> 'e, 'f, 'g, 'g, 'g, 'e) format6\n"; + ]) +;; + +(*********************) +(* Main test routine *) +(*********************) + +let tests = parse_arg () +in +let all_test = + "Test ocaml-gettext" >::: + [ + format_test tests; + split_plural_test tests; + po_test tests; + compatibility_test tests; + extract_test tests; + install_test tests; + implementation_test tests; + po_process_test tests; + merge_test tests; + run_ocaml_gettext tests; + bad_setlocale tests; + compile_ocaml tests; + ] +in +print_env "tests"; +mkdir ~parent:true tests.test_dir; +run_test_tt all_test + diff --git a/test/test1.po b/test/test1.po new file mode 100644 index 0000000..129ae92 --- /dev/null +++ b/test/test1.po @@ -0,0 +1,15 @@ +msgid "" +msgstr "" +"Content-Type: text/plain; charset=ISO-8859-1\n" +"Plural-Forms: nplurals=2; plural=(n > 1);\n" + +msgid "'Your command, please?', asked the waiter." +msgstr "�Votre commande, s'il vous plait�, dit le gar�on." + +# Reverse the arguments. +#, c-format +msgid "%s is replaced by %s." +msgstr "%2$s remplace %1$s." + +msgid "hey" +msgstr "h�" diff --git a/test/test10.mo b/test/test10.mo new file mode 100644 index 0000000..eb4980f Binary files /dev/null and b/test/test10.mo differ diff --git a/test/test11.po b/test/test11.po new file mode 100644 index 0000000..bb4dd1a --- /dev/null +++ b/test/test11.po @@ -0,0 +1,1096 @@ +# translation of virt-top.tip.ja.po to Japanese +# Kiyoto Hashida , 2008. +msgid "" +msgstr "" +"Project-Id-Version: virt-top.tip.ja\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2008-03-28 17:30+0000\n" +"PO-Revision-Date: 2008-10-21 08:20+1000\n" +"Last-Translator: Kiyoto Hashida \n" +"Language-Team: Japanese \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=1; plural=0;\n" +"X-Generator: KBabel 1.11.4\n" + +#: ../virt-top/virt_top.ml:1490 +msgid "# .virt-toprc virt-top configuration file\\n" +msgstr "# .virt-toprc virt-top 設定ファイル\\n" + +#: ../virt-top/virt_top.ml:1508 +msgid "# Enable CSV output to the named file\\n" +msgstr "# 指定ファイルへの CSV 出力を有効にする\\n" + +#: ../virt-top/virt_top.ml:1511 +msgid "# To protect this file from being overwritten, uncomment next line\\n" +msgstr "# このファイルが上書きされるのを防止するには、次の行をアンコメントします\\n" + +#: ../virt-top/virt_top.ml:1505 +msgid "# To send debug and error messages to a file, uncomment next line\\n" +msgstr "# デバグとエラーのメッセージをファイルに送信するには、次の行をアンコメントします\\n" + +#: ../virt-top/virt_top.ml:1491 +msgid "# generated on %s by %s\\n" +msgstr "# %s によって %s 上で生成\\n" + +#: ../virt-top/virt_top.ml:63 +msgid "%CPU" +msgstr "%CPU" + +#: ../virt-top/virt_top.ml:64 +msgid "%MEM" +msgstr "%MEM" + +#: ../virt-top/virt_top.ml:1144 +msgid "" +"%d domains, %d active, %d running, %d sleeping, %d paused, %d inactive D:%d " +"O:%d X:%d" +msgstr "" +"%d domains, %d active, %d running, %d sleeping, %d paused, %d inactive D:%d " +"O:%d X:%d" + +#: ../mlvirsh/mlvirsh.ml:716 +msgid "%s: command not found" +msgstr "%s: コマンドが見つかりません" + +#: ../virt-top/virt_top.ml:105 +msgid "%s: display should be %s" +msgstr "%s: 表示は %s となるべきです" + +#: ../virt-top/virt_top.ml:82 +msgid "%s: sort order should be: %s" +msgstr "%s: 分類順は次のようになるべきです: %s" + +#: ../virt-df/virt_df.ml:362 ../virt-top/virt_top.ml:202 +msgid "%s: unknown parameter" +msgstr "%s: 不明なパラメータ" + +#: ../virt-top/virt_top.ml:233 +msgid "%s:%d: configuration item ``%s'' ignored\\n%!" +msgstr "%s:%d: 設定項目 ``%s'' 無視されました\\n%!" + +#: ../virt-df/virt_df.ml:514 +msgid "(device omitted)" +msgstr "(デバイス欠落)" + +#: ../virt-top/virt_top.ml:145 +msgid "-d: cannot set a negative delay" +msgstr "-d: ネガティブな遅延は設定できません" + +#: ../virt-df/virt_df.ml:498 +msgid "1K-blocks" +msgstr "1K-ブロック" + +#: ../virt-ctrl/vc_mainwindow.ml:97 +msgid "About ..." +msgstr "情報 ..." + +#: ../mlvirsh/mlvirsh.ml:399 +msgid "Attach device to domain." +msgstr "ドメインにデバイスを添付" + +#: ../virt-df/virt_df.ml:499 ../virt-df/virt_df.ml:498 +msgid "Available" +msgstr "利用可能" + +#: ../virt-top/virt_top.ml:167 +msgid "Batch mode" +msgstr "バッチモード" + +#: ../virt-top/virt_top.ml:70 +msgid "Block read reqs" +msgstr "読み込み要求を阻止する" + +#: ../virt-top/virt_top.ml:71 +msgid "Block write reqs" +msgstr "書き込み要求を阻止する" + +#: ../virt-ctrl/vc_connections.ml:408 +msgid "CPU" +msgstr "CPU" + +#: ../mlvirsh/mlvirsh.ml:365 +msgid "CPU affinity" +msgstr "CPU アフィニティ" + +#: ../virt-top/virt_top.ml:1151 +msgid "CPU: %2.1f%% Mem: %Ld MB (%Ld MB by guests)" +msgstr "CPU: %2.1f%% Mem: %Ld MB (ゲストによる %Ld MB)" + +#: ../virt-ctrl/vc_connection_dlg.ml:182 +msgid "Cancel" +msgstr "取消し" + +#: ../virt-top/virt_top.ml:1319 +msgid "Change delay from %.1f to: " +msgstr "遅延を %.1f から次へ変更する: " + +#: ../mlvirsh/mlvirsh.ml:409 +msgid "Close an existing hypervisor connection." +msgstr "既存のハイパーバイザー接続を閉じる" + +#: ../virt-ctrl/vc_mainwindow.ml:118 +msgid "Connect ..." +msgstr "接続 ..." + +#: ../virt-ctrl/vc_mainwindow.ml:160 +msgid "Connect to ..." +msgstr "次へ接続 ..." + +#: ../virt-df/virt_df.ml:348 ../virt-df/virt_df.ml:346 +#: ../virt-top/virt_top.ml:171 ../virt-top/virt_top.ml:169 +msgid "Connect to URI (default: Xen)" +msgstr "URI (デフォルト : Xen)へ接続" + +#: ../virt-top/virt_top.ml:1558 +msgid "Connect: %s; Hostname: %s" +msgstr "接続 : %s; ホスト名 : %s" + +#: ../mlvirsh/mlvirsh.ml:476 +msgid "Core dump a domain to a file for analysis." +msgstr "解析の為にドメインをファイルにコアダンプする。" + +#: ../mlvirsh/mlvirsh.ml:422 +msgid "Create a domain from an XML file." +msgstr "XML ファイルからドメインを作成する。" + +#: ../mlvirsh/mlvirsh.ml:534 +msgid "Create a network from an XML file." +msgstr "XML ファイルからネットワークを作成する" + +#: ../virt-top/virt_top.ml:1596 +msgid "DISPLAY MODES" +msgstr "表示モード" + +#: ../mlvirsh/mlvirsh.ml:426 +msgid "Define (but don't start) a domain from an XML file." +msgstr "XML ファイルからドメインを(起動せずに)定義する" + +#: ../mlvirsh/mlvirsh.ml:538 +msgid "Define (but don't start) a network from an XML file." +msgstr "XML ファイルからネットワークを(起動せずに)定義する" + +#: ../virt-top/virt_top.ml:1326 +msgid "Delay must be > 0" +msgstr "遅延はゼロよりも大きい値でなければなりません" + +#: ../virt-top/virt_top.ml:181 +msgid "Delay time interval (seconds)" +msgstr "遅延時間間隔(秒)" + +#: ../virt-top/virt_top.ml:1552 +msgid "Delay: %.1f secs; Batch: %s; Secure: %s; Sort: %s" +msgstr "遅延: %.1f 秒; バッチ: %s; セキュア: %s; 分類: %s" + +#: ../mlvirsh/mlvirsh.ml:433 +msgid "Destroy a domain." +msgstr "ドメインを破壊する" + +#: ../mlvirsh/mlvirsh.ml:541 +msgid "Destroy a network." +msgstr "ネットワークを破壊する" + +#: ../mlvirsh/mlvirsh.ml:430 +msgid "Detach device from domain." +msgstr "デバイスをドメインから分離する" + +#: ../virt-ctrl/vc_mainwindow.ml:123 +msgid "Details" +msgstr "詳細" + +#: ../virt-top/virt_top.ml:175 +msgid "Disable CPU stats in CSV" +msgstr "CSV 内の CPU 統計を無効にする" + +#: ../virt-top/virt_top.ml:177 +msgid "Disable block device stats in CSV" +msgstr "CSV 内のブロックデバイス統計を無効にする" + +#: ../virt-top/virt_top.ml:179 +msgid "Disable net stats in CSV" +msgstr "CSV 内のネット統計を無効にする" + +#: ../mlvirsh/mlvirsh.ml:493 +msgid "Display free memory for machine, NUMA cell or range of cells" +msgstr "マシン、NUMA セル、あるいはセルの範囲用の空きメモリーを表示する" + +#: ../mlvirsh/mlvirsh.ml:437 +msgid "Display the block device statistics for a domain." +msgstr "ドメインのブロックデバイス統計を表示する" + +#: ../mlvirsh/mlvirsh.ml:444 +msgid "Display the network interface statistics for a domain." +msgstr "ドメインのネットワークインターフェイス統計を表示する" + +#: ../virt-df/virt_df.ml:358 +msgid "Display version and exit" +msgstr "バージョンを表示して終了する" + +#: ../virt-top/virt_top.ml:191 +msgid "Do not read init file" +msgstr "init ファイルを読み込まない" + +#: ../virt-top/virt_top.ml:66 +msgid "Domain ID" +msgstr "ドメイン ID" + +#: ../virt-top/virt_top.ml:67 +msgid "Domain name" +msgstr "ドメイン名" + +#: ../virt-top/virt_top.ml:1610 +msgid "Domains display" +msgstr "ドメイン表示" + +#: ../virt-ctrl/vc_mainwindow.ml:61 ../virt-top/virt_top_main.ml:47 +#: ../virt-top/virt_top.ml:1528 +msgid "Error" +msgstr "エラー" + +#: ../virt-top/virt_top.ml:185 +msgid "Exit at given time" +msgstr "任意の時点で終了" + +#: ../virt-ctrl/vc_mainwindow.ml:79 +msgid "File" +msgstr "ファイル" + +#: ../virt-df/virt_df.ml:502 +msgid "Filesystem" +msgstr "ファイルシステム" + +#: ../mlvirsh/mlvirsh.ml:606 +msgid "Get the current scheduler parameters for a domain." +msgstr "ドメインの現在のスケジューラパラメータを取得する" + +#: ../mlvirsh/mlvirsh.ml:623 +msgid "Get the scheduler type." +msgstr "スケジューラタイプを取得する" + +#: ../mlvirsh/mlvirsh.ml:635 +msgid "Gracefully shutdown a domain." +msgstr "優しくドメインをシャットダウンする" + +#: ../virt-ctrl/vc_mainwindow.ml:96 ../virt-ctrl/vc_mainwindow.ml:80 +#: ../virt-top/virt_top.ml:1580 +msgid "Help" +msgstr "ヘルプ" + +#: ../virt-top/virt_top.ml:187 +msgid "Historical CPU delay" +msgstr "歴史的 CPU の遅延" + +#: ../mlvirsh/mlvirsh.ml:35 +msgid "Hypervisor connection URI" +msgstr "ハイパバイザー接続 URI" + +#: ../virt-ctrl/vc_connections.ml:405 +msgid "ID" +msgstr "ID" + +#: ../virt-df/virt_df.ml:500 +msgid "IFree" +msgstr "IFree" + +#: ../virt-df/virt_df.ml:500 +msgid "IUse" +msgstr "IUse" + +#: ../virt-df/virt_df.ml:500 +msgid "Inodes" +msgstr "Inodes" + +#: ../virt-df/virt_df_lvm2.ml:33 +msgid "LVM2 not supported yet" +msgstr "LVM2 はまだサポートされていません" + +#: ../virt-df/virt_df_ext2.ml:82 +msgid "Linux ext2/3" +msgstr "Linux ext2/3" + +#: ../virt-df/virt_df_linux_swap.ml:33 +msgid "Linux swap" +msgstr "Linux swap" + +#: ../mlvirsh/mlvirsh.ml:557 +msgid "List the active networks." +msgstr "アクティブなネットワークを一覧表示する" + +#: ../mlvirsh/mlvirsh.ml:565 +msgid "List the defined but inactive networks." +msgstr "定義すみでアクティブでないネットワークを一覧表示する" + +#: ../mlvirsh/mlvirsh.ml:516 +msgid "List the defined but not running domains." +msgstr "定義済で実行していないドメインを一覧表示する" + +#: ../mlvirsh/mlvirsh.ml:508 +msgid "List the running domains." +msgstr "実行中のドメインを一覧表示する" + +#: ../virt-ctrl/vc_mainwindow.ml:158 +msgid "Local QEMU/KVM" +msgstr "ローカル QEMU/KVM" + +#: ../virt-ctrl/vc_mainwindow.ml:157 +msgid "Local Xen" +msgstr "ローカル Xen" + +#: ../virt-ctrl/vc_connection_dlg.ml:93 +msgid "Local network" +msgstr "ローカルネットワーク" + +#: ../virt-top/virt_top.ml:173 +msgid "Log statistics to CSV file" +msgstr "CSV ファイルへのログ統計" + +#: ../virt-top/virt_top.ml:1563 +msgid "MAIN KEYS" +msgstr "主要キー" + +#: ../virt-ctrl/vc_connections.ml:409 +msgid "Memory" +msgstr "メモリー" + +#: ../virt-top/virt_top.ml:1617 +msgid "More help in virt-top(1) man page. Press any key to return." +msgstr "virt-top(1) man ページには他のヘルプがあります。どれかのキーを押すと戻ります" + +#: ../virt-df/virt_df.ml:382 ../virt-top/virt_top.ml:258 +msgid "" +"NB: If you want to monitor a local Xen hypervisor, you usually need to be " +"root" +msgstr "" +"NB: ローカル Xen ハイパバイザーをモニターしたい場合は、通常 root になる必要が " +"あります" + +#: ../virt-ctrl/vc_connections.ml:406 +msgid "Name" +msgstr "名前" + +#: ../virt-top/virt_top.ml:68 +msgid "Net RX bytes" +msgstr "ネット RX バイト" + +#: ../virt-top/virt_top.ml:69 +msgid "Net TX bytes" +msgstr "ネット TX バイト" + +#: ../virt-top/virt_top.ml:1332 +msgid "Not a valid number" +msgstr "有効な数ではありません" + +#: ../virt-top/virt_top.ml:193 +msgid "Number of iterations to run" +msgstr "繰り返し実行する回数" + +#: ../virt-ctrl/vc_connection_dlg.ml:170 ../virt-ctrl/vc_connection_dlg.ml:137 +msgid "Open" +msgstr "開く" + +#: ../mlvirsh/mlvirsh.ml:418 +msgid "Open a new hypervisor connection." +msgstr "新規ハイパバイザー接続を開く" + +#: ../virt-ctrl/vc_mainwindow.ml:86 +msgid "Open connection ..." +msgstr "接続を開く ..." + +#: ../virt-ctrl/vc_connection_dlg.ml:40 +msgid "Open connection to hypervisor" +msgstr "ハイパバイザーへの接続を開く" + +#: ../virt-ctrl/vc_mainwindow.ml:130 +msgid "Pause" +msgstr "一時停止" + +#: ../mlvirsh/mlvirsh.ml:670 ../mlvirsh/mlvirsh.ml:666 +msgid "Pin domain VCPU to a list of physical CPUs." +msgstr "ドメイン VCPU を物理 CPU の一覧に固定する" + +#: ../mlvirsh/mlvirsh.ml:706 +msgid "Print list of commands or full description of one command." +msgstr "コマンドの一覧、又は1つのコマンドの完全な説明を表示" + +#: ../mlvirsh/mlvirsh.ml:584 +msgid "Print node information." +msgstr "ノード情報を表示" + +#: ../virt-df/virt_df.ml:352 ../virt-df/virt_df.ml:350 +msgid "Print sizes in human-readable format" +msgstr "可読形式でサイズを表示" + +#: ../mlvirsh/mlvirsh.ml:440 +msgid "Print the ID of a domain." +msgstr "ドメインの ID を表示" + +#: ../mlvirsh/mlvirsh.ml:464 +msgid "Print the OS type of a domain." +msgstr "ドメインの OS タイプを表示" + +#: ../mlvirsh/mlvirsh.ml:472 +msgid "Print the UUID of a domain." +msgstr "ドメインの UUID を表示" + +#: ../mlvirsh/mlvirsh.ml:581 +msgid "Print the UUID of a network." +msgstr "ネットワークの UUID を表示" + +#: ../mlvirsh/mlvirsh.ml:480 +msgid "Print the XML description of a domain." +msgstr "ドメインの XML 記述を表示" + +#: ../mlvirsh/mlvirsh.ml:545 +msgid "Print the XML description of a network." +msgstr "ネットワークの XML 記述を表示" + +#: ../mlvirsh/mlvirsh.ml:530 +msgid "Print the bridge name of a network." +msgstr "ネットワークのブリッジ名を表示" + +#: ../mlvirsh/mlvirsh.ml:653 +msgid "Print the canonical URI." +msgstr "正規の URI を表示" + +#: ../mlvirsh/mlvirsh.ml:448 +msgid "Print the domain info." +msgstr "ドメイン情報を表示" + +#: ../mlvirsh/mlvirsh.ml:468 +msgid "Print the domain state." +msgstr "ドメインの状態を表示" + +#: ../mlvirsh/mlvirsh.ml:646 +msgid "Print the driver name" +msgstr "ドライバー名を表示" + +#: ../mlvirsh/mlvirsh.ml:677 +msgid "Print the driver version" +msgstr "ドライバーのバージョンを表示" + +#: ../mlvirsh/mlvirsh.ml:500 +msgid "Print the hostname." +msgstr "ホスト名を表示" + +#: ../mlvirsh/mlvirsh.ml:522 +msgid "Print the max VCPUs available." +msgstr "使用可能な最大 VCPU を表示" + +#: ../mlvirsh/mlvirsh.ml:456 +msgid "Print the max VCPUs of a domain." +msgstr "ドメインの最大 VCPU を表示" + +#: ../mlvirsh/mlvirsh.ml:452 +msgid "Print the max memory (in kilobytes) of a domain." +msgstr "ドメインの最大メモリーを(キロバイトで)表示" + +#: ../mlvirsh/mlvirsh.ml:460 +msgid "Print the name of a domain." +msgstr "ドメインの名前を表示" + +#: ../mlvirsh/mlvirsh.ml:569 +msgid "Print the name of a network." +msgstr "ネットワークの名前を表示" + +#: ../mlvirsh/mlvirsh.ml:497 +msgid "Print whether a domain autostarts at boot." +msgstr "起動時にドメインが自動開始するかどうか表示" + +#: ../mlvirsh/mlvirsh.ml:549 +msgid "Print whether a network autostarts at boot." +msgstr "起動時にネットワークが自動開始するかどうか表示" + +#: ../virt-ctrl/vc_connection_dlg.ml:83 +msgid "QEMU or KVM" +msgstr "QEMU 又は KVM" + +#: ../virt-ctrl/vc_mainwindow.ml:89 ../virt-top/virt_top.ml:1578 +msgid "Quit" +msgstr "終了" + +#: ../mlvirsh/mlvirsh.ml:519 +msgid "Quit the interactive terminal." +msgstr "対話式の端末で終了" + +#: ../mlvirsh/mlvirsh.ml:36 +msgid "Read-only connection" +msgstr "読み込み専用接続" + +#: ../mlvirsh/mlvirsh.ml:587 +msgid "Reboot a domain." +msgstr "ドメインを再起動" + +#: ../virt-ctrl/vc_connection_dlg.ml:134 +msgid "Refresh" +msgstr "リフレッシュ" + +#: ../mlvirsh/mlvirsh.ml:592 +msgid "Restore a domain from the named file." +msgstr "指定ファイルからドメインを復元" + +#: ../virt-ctrl/vc_mainwindow.ml:133 +msgid "Resume" +msgstr "復帰" + +#: ../mlvirsh/mlvirsh.ml:595 +msgid "Resume a domain." +msgstr "ドメインを復帰" + +#: ../mlvirsh/mlvirsh.ml:406 +msgid "Returns capabilities of hypervisor/driver." +msgstr "ハイパバイザーの戻り機能" + +#: ../virt-top/virt_top.ml:199 +msgid "Run from a script (no user interface)" +msgstr "スクリプトから実行(ユーザーインターフェイスなし)" + +#: ../virt-top/virt_top.ml:1584 +msgid "SORTING" +msgstr "分類" + +#: ../mlvirsh/mlvirsh.ml:599 +msgid "Save a domain to a file." +msgstr "ファイルにドメインを保存" + +#: ../virt-top/virt_top.ml:197 +msgid "Secure (\\\"kiosk\\\") mode" +msgstr "セキュア (\\\"kiosk\\\") モード" + +#: ../virt-top/virt_top.ml:1593 +msgid "Select sort field" +msgstr "分類フィールドを選択" + +#: ../virt-top/virt_top.ml:183 +msgid "Send debug messages to file" +msgstr "デバッグメッセージをファイルに送信" + +#: ../virt-top/virt_top.ml:189 +msgid "Set name of init file" +msgstr "init ファイルの名前を設定する" + +#: ../virt-top/virt_top.ml:195 +msgid "Set sort order (%s)" +msgstr "分類順 (%s) を設定する" + +#: ../virt-top/virt_top.ml:1340 +msgid "Set sort order for main display" +msgstr "ドメイン表示の分類順を設定する" + +#: ../mlvirsh/mlvirsh.ml:631 +msgid "Set the maximum memory used by the domain (in kilobytes)." +msgstr "ドメインにより使用される最大メモリー(キロバイト)を設定する" + +#: ../mlvirsh/mlvirsh.ml:627 +msgid "Set the memory used by the domain (in kilobytes)." +msgstr "ドメインにより使用されるメモリー(キロバイト)を設定する" + +#: ../mlvirsh/mlvirsh.ml:674 +msgid "Set the number of virtual CPUs assigned to a domain." +msgstr "ドメインに割り当てる CPU の数を設定する" + +#: ../mlvirsh/mlvirsh.ml:618 +msgid "Set the scheduler parameters for a domain." +msgstr "ドメイン用のスケジューラパラメータを設定する" + +#: ../virt-top/virt_top.ml:1579 +msgid "Set update interval" +msgstr "更新の間隔を設定する" + +#: ../mlvirsh/mlvirsh.ml:403 +msgid "Set whether a domain autostarts at boot." +msgstr "起動時にドメインが自動開始するかどうか設定する" + +#: ../mlvirsh/mlvirsh.ml:526 +msgid "Set whether a network autostarts at boot." +msgstr "起動時にネットワークが自動開始するかどうか設定する" + +#: ../virt-df/virt_df.ml:344 ../virt-df/virt_df.ml:342 +msgid "Show all domains (default: only active domains)" +msgstr "全てのドメインを表示(デフォルト: アクティブドメインのみ)" + +#: ../virt-df/virt_df.ml:356 ../virt-df/virt_df.ml:354 +msgid "Show inodes instead of blocks" +msgstr "ブロックではなくアイノードを表示" + +#: ../virt-ctrl/vc_mainwindow.ml:137 +msgid "Shutdown" +msgstr "シャットダウン" + +#: ../virt-df/virt_df.ml:499 +msgid "Size" +msgstr "サイズ" + +#: ../virt-top/virt_top.ml:1589 +msgid "Sort by %CPU" +msgstr "%CPU で分類" + +#: ../virt-top/virt_top.ml:1590 +msgid "Sort by %MEM" +msgstr "%MEM で分類" + +#: ../virt-top/virt_top.ml:1592 +msgid "Sort by ID" +msgstr "ID で分類" + +#: ../virt-top/virt_top.ml:1591 +msgid "Sort by TIME" +msgstr "時間で分類" + +#: ../virt-ctrl/vc_mainwindow.ml:127 +msgid "Start" +msgstr "開始" + +#: ../mlvirsh/mlvirsh.ml:639 +msgid "Start a previously defined inactive domain." +msgstr "以前に停止中と定義したドメインを開始" + +#: ../mlvirsh/mlvirsh.ml:573 +msgid "Start a previously defined inactive network." +msgstr "以前に停止中と定義したネットワークを開始" + +#: ../virt-top/virt_top.ml:165 +msgid "Start by displaying block devices" +msgstr "ブロックデバイスの表示で開始" + +#: ../virt-top/virt_top.ml:163 +msgid "Start by displaying network interfaces" +msgstr "ネットワークインターフェイスの表示で開始" + +#: ../virt-top/virt_top.ml:161 +msgid "Start by displaying pCPUs (default: tasks)" +msgstr "pCPU の表示で開始(デフォルト: タスク)" + +#: ../virt-ctrl/vc_connections.ml:407 +msgid "Status" +msgstr "ステータス" + +#: ../mlvirsh/mlvirsh.ml:643 +msgid "Suspend a domain." +msgstr "ドメインをサスペンド" + +#: ../mlvirsh/mlvirsh.ml:40 +msgid "" +"Synopsis:\n" +" %s [options] [command]\n" +"\n" +"List of all commands:\n" +" %s help\n" +"\n" +"Full description of a single command:\n" +" %s help command\n" +"\n" +"Options:" +msgstr "" +"シノプシス:\n" +" %s [options] [command]\n" +"\n" +"全コマンドの一覧:\n" +" %s help\n" +"\n" +"単独コマンドの完全説明:\n" +" %s help command\n" +"\n" +"オプション群:" + +#: ../virt-top/virt_top.ml:65 +msgid "TIME (CPU time)" +msgstr "時間(CPU 時間)" + +#: ../virt-ctrl/vc_connection_dlg.ml:62 +msgid "This machine" +msgstr "このマシン" + +#: ../virt-top/virt_top.ml:1613 +msgid "Toggle block devices" +msgstr "ブロックデバイスの切り替え" + +#: ../virt-top/virt_top.ml:1612 +msgid "Toggle network interfaces" +msgstr "ネットワークインターフェイスの切り替え" + +#: ../virt-top/virt_top.ml:1611 +msgid "Toggle physical CPUs" +msgstr "物理 CPU の切り替え" + +#: ../virt-df/virt_df.ml:502 +msgid "Type" +msgstr "タイプ" + +#: ../virt-top/virt_top.ml:1341 +msgid "Type key or use up and down cursor keys." +msgstr "キーをタイプするか、又は上下カーソルキーを使用" + +#: ../virt-ctrl/vc_connection_dlg.ml:160 +msgid "URI connection" +msgstr "URI 接続" + +#: ../mlvirsh/mlvirsh.ml:650 +msgid "Undefine an inactive domain." +msgstr "停止中のドメインを定義解除" + +#: ../mlvirsh/mlvirsh.ml:577 +msgid "Undefine an inactive network." +msgstr "停止中のネットワークを定義解除" + +#: ../virt-top/virt_top.ml:1622 +msgid "Unknown command - try 'h' for help" +msgstr "不明なコマンド - 'h' を使用してヘルプ参照 " + +#: ../virt-top/virt_top.ml:1577 +msgid "Update display" +msgstr "表示を更新" + +#: ../mlvirsh/mlvirsh.ml:690 +msgid "Use '%s help command' for help on a command." +msgstr "'%s help コマンドを使用してコマンドのヘルプを参照" + +#: ../virt-df/virt_df.ml:499 ../virt-df/virt_df.ml:498 +msgid "Used" +msgstr "使用中" + +#: ../virt-ctrl/vc_mainwindow.ml:23 +msgid "Virtual Control" +msgstr "仮想制御" + +#: ../virt-ctrl/vc_mainwindow.ml:53 +msgid "Virtualisation error" +msgstr "仮想化エラー" + +#: ../virt-ctrl/vc_mainwindow.ml:39 +msgid "" +"Virtualization control tool (virt-ctrl) by\n" +"Richard W.M. Jones (rjones@redhat.com).\n" +"\n" +"Copyright %s 2007-2008 Red Hat Inc.\n" +"\n" +"Libvirt version: %s\n" +"\n" +"Gtk toolkit version: %s" +msgstr "" +"仮想化制御ツール (virt-ctrl) 製作者\n" +"Richard W.M. Jones (rjones@redhat.com).\n" +"\n" +"Copyright %s 2007-2008 Red Hat Inc.\n" +"\n" +"Libvirt バージョン: %s\n" +"\n" +"Gtk toolkit バージョン: %s" + +#: ../virt-top/virt_top.ml:1523 +msgid "Wrote settings to %s" +msgstr "%s への設定を書き込み" + +#: ../virt-ctrl/vc_connection_dlg.ml:76 +msgid "Xen hypervisor" +msgstr "Xen ハイパバイザー" + +#: ../mlvirsh/mlvirsh.ml:364 +msgid "\\tCPU time: %Ld ns\\n" +msgstr "\\tCPU 時間: %Ld ns\\n" + +#: ../mlvirsh/mlvirsh.ml:362 +msgid "\\tcurrent state: %s\\n" +msgstr "\\tcurrent 状態: %s\\n" + +#: ../mlvirsh/mlvirsh.ml:361 +msgid "\\ton physical CPU: %d\\n" +msgstr "\\ton 物理 CPU: %d\\n" + +#: ../mlvirsh/mlvirsh.ml:298 ../mlvirsh/mlvirsh.ml:289 +#: ../virt-ctrl/vc_helpers.ml:54 +msgid "blocked" +msgstr "阻止" + +#: ../mlvirsh/mlvirsh.ml:330 +msgid "cores: %d\\n" +msgstr "コア: %d\\n" + +#: ../mlvirsh/mlvirsh.ml:342 +msgid "cpu_time: %Ld ns\\n" +msgstr "cpu_time: %Ld ns\\n" + +#: ../mlvirsh/mlvirsh.ml:326 +msgid "cpus: %d\\n" +msgstr "cpus: %d\\n" + +#: ../mlvirsh/mlvirsh.ml:293 ../virt-ctrl/vc_helpers.ml:58 +msgid "crashed" +msgstr "クラッシュ" + +#: ../virt-df/virt_df.ml:236 +msgid "detection of unpartitioned devices not yet supported" +msgstr "未パーティションの検出はまだサポートがありません" + +#: ../mlvirsh/mlvirsh.ml:242 +msgid "domain %s: not found. Additional info: %s" +msgstr "ドメイン%s: 見つかりません。追加情報: %s" + +#: ../virt-df/virt_df_ext2.ml:39 +msgid "error reading ext2/ext3 magic" +msgstr "ext2/ext3 magic の読み込みでエラー" + +#: ../virt-df/virt_df.ml:182 +msgid "error reading extended partition" +msgstr "拡張パーティションの読み込みでエラー" + +#: ../virt-df/virt_df.ml:149 +msgid "error reading partition table" +msgstr "パーティションテーブルの読み込みでエラー" + +#: ../virt-ctrl/vc_dbus.ml:239 +msgid "error set after getting System bus" +msgstr "システムバスの取得後にエラー設定" + +#: ../mlvirsh/mlvirsh.ml:379 +msgid "errors: %Ld\\n" +msgstr "エラー: %Ld\\n" + +#: ../mlvirsh/mlvirsh.ml:258 +msgid "expected field value pairs, but got an odd number of arguments" +msgstr "予期したフィールド値のペアですが、奇数の引数を受理しました" + +#: ../mlvirsh/mlvirsh.ml:610 +msgid "expecting domain followed by field value pairs" +msgstr "ドメインの後にフィールド値のペアを予期" + +#: ../mlvirsh/mlvirsh.ml:220 +msgid "flag should be '%s'" +msgstr "フラグは '%s' であるべきです" + +#: ../virt-df/virt_df.ml:419 ../virt-top/virt_top_xml.ml:46 +msgid "get_xml_desc didn't return " +msgstr "get_xml_desc は を返しませんでした" + +#: ../virt-df/virt_df.ml:427 +msgid "get_xml_desc returned no node in XML" +msgstr "get_xml_desc は XML で ノードを返しませんでした" + +#: ../virt-df/virt_df.ml:430 +msgid "get_xml_desc returned strange node" +msgstr "get_xml_desc は不思議な ノードを返しました" + +#: ../mlvirsh/mlvirsh.ml:700 +msgid "help: %s: command not found" +msgstr "ヘルプ: %s: コマンドが見つかりません" + +#: ../mlvirsh/mlvirsh.ml:188 ../mlvirsh/mlvirsh.ml:182 +#: ../mlvirsh/mlvirsh.ml:177 ../mlvirsh/mlvirsh.ml:172 +#: ../mlvirsh/mlvirsh.ml:168 ../mlvirsh/mlvirsh.ml:164 +#: ../mlvirsh/mlvirsh.ml:160 +msgid "incorrect number of arguments for function" +msgstr "機能するには引数の数量が違います" + +#: ../mlvirsh/mlvirsh.ml:339 +msgid "max_mem: %Ld K\\n" +msgstr "max_mem: %Ld K\\n" + +#: ../mlvirsh/mlvirsh.ml:340 ../mlvirsh/mlvirsh.ml:325 +msgid "memory: %Ld K\\n" +msgstr "メモリー: %Ld K\\n" + +#: ../mlvirsh/mlvirsh.ml:327 +msgid "mhz: %d\\n" +msgstr "mhz: %d\\n" + +#: ../mlvirsh/mlvirsh.ml:727 +msgid "mlvirsh" +msgstr "mlvirsh" + +#: ../mlvirsh/mlvirsh.ml:725 +msgid "mlvirsh(no connection)" +msgstr "mlvirsh (無接続)" + +#: ../mlvirsh/mlvirsh.ml:726 +msgid "mlvirsh(ro)" +msgstr "mlvirsh(ro)" + +#: ../mlvirsh/mlvirsh.ml:324 +msgid "model: %s\\n" +msgstr "モデル: %s\\n " + +#: ../mlvirsh/mlvirsh.ml:253 +msgid "network %s: not found. Additional info: %s" +msgstr "ネットワーク %s: 見つかりません。追加情報: %s" + +#: ../mlvirsh/mlvirsh.ml:328 +msgid "nodes: %d\\n" +msgstr "ノード: %d\\n" + +#: ../mlvirsh/mlvirsh.ml:202 ../mlvirsh/mlvirsh.ml:197 +msgid "not connected to the hypervisor" +msgstr "ハイパバイザーに接続されていません" + +#: ../mlvirsh/mlvirsh.ml:341 +msgid "nr_virt_cpu: %d\\n" +msgstr "nr_virt_cpu: %d\\n" + +#: ../mlvirsh/mlvirsh.ml:296 +msgid "offline" +msgstr "オフライン" + +#: ../virt-df/virt_df_ext2.ml:42 +msgid "partition marked EXT2/3 but no valid filesystem" +msgstr "パーティションは EXT2/3 になっていますが、有効なファイルシステムではありません" + +#: ../mlvirsh/mlvirsh.ml:290 ../virt-ctrl/vc_helpers.ml:55 +msgid "paused" +msgstr "一時停止" + +#: ../virt-df/virt_df.ml:188 +msgid "probe_extended_partition: internal error" +msgstr "probe_extended_partition: 内部エラー" + +#: ../mlvirsh/mlvirsh.ml:376 +msgid "read bytes: %Ld\\n" +msgstr "バイトの読み込み: %Ld\\n" + +#: ../mlvirsh/mlvirsh.ml:375 +msgid "read requests: %Ld\\n" +msgstr "要求の読み込み: %Ld\\n" + +#: ../mlvirsh/mlvirsh.ml:297 ../mlvirsh/mlvirsh.ml:288 +#: ../virt-ctrl/vc_helpers.ml:53 +msgid "running" +msgstr "実行中" + +#: ../mlvirsh/mlvirsh.ml:384 +msgid "rx bytes: %Ld\\n" +msgstr "rx バイト: %Ld\\n" + +#: ../mlvirsh/mlvirsh.ml:387 +msgid "rx dropped: %Ld\\n" +msgstr "rx 却下: %Ld\\n" + +#: ../mlvirsh/mlvirsh.ml:386 +msgid "rx errs: %Ld\\n" +msgstr "rx エラー: %Ld\\n" + +#: ../mlvirsh/mlvirsh.ml:385 +msgid "rx packets: %Ld\\n" +msgstr "rx パケット: %Ld\\n" + +#: ../mlvirsh/mlvirsh.ml:226 +msgid "setting should be '%s' or '%s'" +msgstr "設定は '%s'か '%s' になるべきです" + +#: ../mlvirsh/mlvirsh.ml:291 ../virt-ctrl/vc_helpers.ml:56 +msgid "shutdown" +msgstr "シャットダウン" + +#: ../mlvirsh/mlvirsh.ml:292 ../virt-ctrl/vc_helpers.ml:57 +msgid "shutoff" +msgstr "シャットオフ" + +#: ../mlvirsh/mlvirsh.ml:329 +msgid "sockets: %d\\n" +msgstr "ソケット: %d\\n" + +#: ../mlvirsh/mlvirsh.ml:338 +msgid "state: %s\\n" +msgstr "状態: %s\\n" + +#: ../mlvirsh/mlvirsh.ml:331 +msgid "threads: %d\\n" +msgstr "スレッド: %d\\n" + +#: ../mlvirsh/mlvirsh.ml:198 +msgid "tried to do read-write operation on read-only hypervisor connection" +msgstr "読み込み専用のハイパバイザー接続で読み込みと書き込みを試行しました" + +#: ../mlvirsh/mlvirsh.ml:388 +msgid "tx bytes: %Ld\\n" +msgstr "tx バイト: %Ld\\n" + +#: ../mlvirsh/mlvirsh.ml:391 +msgid "tx dropped: %Ld\\n" +msgstr "tx 却下: %Ld\\n" + +#: ../mlvirsh/mlvirsh.ml:390 +msgid "tx errs: %Ld\\n" +msgstr "tx エラー: %Ld\\n" + +#: ../mlvirsh/mlvirsh.ml:389 +msgid "tx packets: %Ld\\n" +msgstr "tx パケット: %Ld\\n" + +#: ../mlvirsh/mlvirsh.ml:287 ../virt-ctrl/vc_helpers.ml:52 +msgid "unknown" +msgstr "不明" + +#: ../virt-df/virt_df.ml:246 +msgid "unsupported partition type %02x" +msgstr "サポートされないパーティションタイプ %02x" + +#: ../virt-df/virt_df.ml:363 +msgid "" +"virt-df : like 'df', shows disk space used in guests\n" +"\n" +"SUMMARY\n" +" virt-df [-options]\n" +"\n" +"OPTIONS" +msgstr "" +"virt-df : 'df' の様に, ゲスト内で使用されているディスク量を示します\n" +"\n" +"要約\n" +" virt-df [-options]\n" +"\n" +"オプション" + +#: ../virt-top/virt_top.ml:1543 +msgid "virt-top %s (libvirt %d.%d.%d) by Red Hat" +msgstr "virt-top %s (libvirt %d.%d.%d) Red Hat 作" + +#: ../virt-top/virt_top.ml:203 +msgid "" +"virt-top : a 'top'-like utility for virtualization\n" +"\n" +"SUMMARY\n" +" virt-top [-options]\n" +"\n" +"OPTIONS" +msgstr "" +"virt-top : 'top'-のようなユーティリティで仮想化用です\n" +"\n" +"要約\n" +" virt-top [-options]\n" +"\n" +"オプション" + +#: ../virt-top/virt_top.ml:40 +msgid "virt-top was compiled without support for CSV files" +msgstr "virt-top は CSV ファイル用のサポートなしでコンパイルされています" + +#: ../virt-top/virt_top.ml:51 +msgid "virt-top was compiled without support for dates and times" +msgstr "virt-top は日付と時刻用のサポートなしでコンパイルされています" + +#: ../mlvirsh/mlvirsh.ml:360 +msgid "virtual CPU: %d\\n" +msgstr "仮想 CPU: %d\\n" + +#: ../virt-ctrl/vc_dbus.ml:219 +msgid "warning: ignored unknown message %s from %s\\n%!" +msgstr "警告: %s からの不明メッセージ %s を無視しました\\n%!" + +#: ../virt-ctrl/vc_dbus.ml:124 +msgid "warning: unexpected message contents of Found signal" +msgstr "警告: 予期しないメッセージが Found 信号を含んでいます" + +#: ../virt-ctrl/vc_dbus.ml:188 +msgid "warning: unexpected message contents of ItemNew signal" +msgstr "警告: 予期しないメッセージが ItemNew 信号を含んでいます" + +#: ../virt-ctrl/vc_dbus.ml:140 +msgid "warning: unexpected message contents of ItemRemove signal" +msgstr "警告: 予期しないメッセージが ItemRemove 信号を含んでいます" + +#: ../mlvirsh/mlvirsh.ml:378 +msgid "write bytes: %Ld\\n" +msgstr "バイトの書き込み: %Ld\\n" + +#: ../mlvirsh/mlvirsh.ml:377 +msgid "write requests: %Ld\\n" +msgstr "要求の書き込み: %Ld\\n" + diff --git a/test/test12.po b/test/test12.po new file mode 100644 index 0000000..7ee3f12 --- /dev/null +++ b/test/test12.po @@ -0,0 +1,8 @@ +msgid "" +msgstr "" +"Content-Type: text/plain; charset=ISO-8859-1\n" +"Plural-Forms: nplurals=2; plural=(n > 1);\n" + +msgid "'Your command, please?', asked the waiter." +msgstr "" + diff --git a/test/test2.po b/test/test2.po new file mode 100644 index 0000000..9ee5dc0 --- /dev/null +++ b/test/test2.po @@ -0,0 +1,20 @@ +msgid "" +msgstr "" +"Content-Type: text/plain; charset=ISO-8859-1\n" +"Plural-Forms: nplurals=2; plural=(n > 1);\n" + +msgid "'Your command, please?', asked the waiter." +msgstr "�Votre commande, s'il vous plait�, dit le gar�on." + +# Reverse the arguments. +#, c-format +msgid "%s is replaced by %s." +msgstr "%2$s remplace %1$s." + +msgid "hey" +msgstr "h�" + +msgid "%d coffee" +msgid_plural "more %d coffee" +msgstr[0] "%d caf�" +msgstr[1] "%d caf�s" diff --git a/test/test3.po b/test/test3.po new file mode 100644 index 0000000..66cce31 --- /dev/null +++ b/test/test3.po @@ -0,0 +1,24 @@ +msgid "" +msgstr "" +"Content-Type: text/plain; charset=ISO-8859-1\n" +"Plural-Forms: nplurals=2; plural=(n > 1);\n" + +domain "fr" + +msgid "'Your command, please?', asked the waiter." +msgstr "�Votre commande, s'il vous plait�, dit le gar�on." + +# Reverse the arguments. +#, c-format +msgid "%s is replaced by %s." +msgstr "%2$s remplace %1$s." + +msgid "hey" +msgstr "h�" + +domain "us" + +msgid "%d coffee" +msgid_plural "more %d coffee" +msgstr[0] "%d caf�" +msgstr[1] "%d caf�s" diff --git a/test/test4.ml b/test/test4.ml new file mode 100644 index 0000000..e8a03c8 --- /dev/null +++ b/test/test4.ml @@ -0,0 +1,145 @@ +(**************************************************************************) +(* ocaml-gettext: a library to translate messages *) +(* *) +(* Copyright (C) 2003-2008 Sylvain Le Gall *) +(* *) +(* This library is free software; you can redistribute it and/or *) +(* modify it under the terms of the GNU Lesser General Public *) +(* License as published by the Free Software Foundation; either *) +(* version 2.1 of the License, or (at your option) any later version; *) +(* with the OCaml static compilation exception. *) +(* *) +(* This library 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 *) +(* Lesser General Public License for more details. *) +(* *) +(* You should have received a copy of the GNU Lesser General Public *) +(* License along with this library; if not, write to the Free Software *) +(* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *) +(* USA *) +(**************************************************************************) + +let t' = () +;; + +let _ = s_ "s_" +;; + +let _ = f_ "f_" +;; + +let _ = sn_ "sn_ singular" "sn_ plural" 0 +;; + +let _ = fn_ "fn_ singular" "fn_ plural" 0 +;; + +let _ = gettext t' "gettext" +;; + +let _ = fgettext t' "fgettext" +;; + +let _ = dgettext t' "mydomain" "dgettext" +;; + +let _ = fdgettext t' "mydomain" "fdgettext" +;; + +let _ = dcgettext t' "mydomain" "dcgettext" LC_ALL +;; + +let _ = fdcgettext t' "mydomain" "fdcgettext" LC_ALL +;; + +let _ = ngettext t' "ngettext singular" "ngettext plural" 0 +;; + +let _ = fngettext t' "fngettext singular" "fngettext plural" 0 +;; + +let _ = dngettext t' "mydomain" "dngettext singular" "dngettext plural " 0 +;; + +let _ = fdngettext t' "mydomain" "fdngettext singular" "fdngettext plural" 0 +;; + +let _ = dcngettext t' "mydomain" "dcngettext singular" "dcngettext plural" 0 LC_ALL +;; + +let _ = fdcngettext t' "mydomain" "fdcngettext singular" "fdcngettext plural" 0 LC_ALL +;; + +let _ = TestGettext.s_ "TestGettext.s_" +;; + +let _ = TestGettext.f_ "TestGettext.f_" +;; + +let _ = TestGettext.sn_ "TestGettext.sn_ singular" "TestGettext.sn_ plural" 0 +;; + +let _ = TestGettext.fn_ "TestGettext.fn_ singular" "TestGettext.fn_ plural" 0 +;; + +let _ = GettextCompat.gettext t' "GettextCompat.gettext" +;; + +let _ = GettextCompat.fgettext t' "GettextCompat.fgettext" +;; + +let _ = GettextCompat.dgettext t' "mydomain" "GettextCompat.dgettext" +;; + +let _ = GettextCompat.fdgettext t' "mydomain" "GettextCompat.fdgettext" +;; + +let _ = GettextCompat.dcgettext t' "mydomain" "GettextCompat.dcgettext" LC_ALL +;; + +let _ = GettextCompat.fdcgettext t' "mydomain" "GettextCompat.fdcgettext" LC_ALL +;; + +let _ = GettextCompat.ngettext t' "GettextCompat.ngettext singular" "GettextCompat.ngettext plural" 0 +;; + +let _ = GettextCompat.fngettext t' "GettextCompat.fngettext singular" "GettextCompat.fngettext plural" 0 +;; + +let _ = GettextCompat.dngettext t' "mydomain" "GettextCompat.dngettext singular" "GettextCompat.dngettext plural " 0 +;; + +let _ = GettextCompat.fdngettext t' "mydomain" "GettextCompat.fdngettext singular" "GettextCompat.fdngettext plural" 0 +;; + +let _ = GettextCompat.dcngettext t' "mydomain" "GettextCompat.dcngettext singular" "GettextCompat.dcngettext plural" 0 LC_ALL +;; + +let _ = GettextCompat.fdcngettext t' "mydomain" "GettextCompat.fdcngettext singular" "GettextCompat.fdcngettext plural" 0 LC_ALL +;; + +let _ = TestGettext.Library.s_ "TestGettext.Library.s_" +;; + +let _ = TestGettext.Library.f_ "TestGettext.Library.f_" +;; + +let _ = TestGettext.Library.sn_ "TestGettext.Library.sn_ singular" "TestGettext.Library.sn_ plural" 0 +;; + +let _ = TestGettext.Library.fn_ "TestGettext.Library.fn_ singular" "TestGettext.Library.fn_ plural" 0 +;; + +let _ = TestGettext.Program.s_ "TestGettext.Program.s_" +;; + +let _ = TestGettext.Program.f_ "TestGettext.Program.f_" +;; + +let _ = TestGettext.Program.sn_ "TestGettext.Program.sn_ singular" "TestGettext.Program.sn_ plural" 0 +;; + +let _ = TestGettext.Gettext.Program.fn_ "TestGettext.Program.fn_ singular" "TestGettext.Program.fn_ plural" 0 +;; + diff --git a/test/test4.po b/test/test4.po new file mode 100644 index 0000000..bf4ad92 --- /dev/null +++ b/test/test4.po @@ -0,0 +1,216 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: ocaml-gettext 0.2.0\n" +"Report-Msgid-Bugs-To: submit@bugs.gallu.homelinux.org\n" +"POT-Creation-Date: 2005-02-08 23:35+0200\n" +"PO-Revision-Date: 2005-02-08 23:35+0200\n" +"Last-Translator: Sylvain Le Gall \n" +"Language-Team: French \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=ISO-8859-15\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n>1;\n" + +#: test4.ml:89 +msgid "GettextCompat.fgettext" +msgstr "Fonction de compatibilit� fgettext" + +#: test4.ml:107 +msgid "GettextCompat.fngettext singular" +msgid_plural "GettextCompat.fngettext plural" +msgstr[0] "Fonction de compatibilit� fngettext ( singulier )" +msgstr[1] "Fonction de compatibilit� fngettext ( pluriel )" + +#: test4.ml:86 +msgid "GettextCompat.gettext" +msgstr "Fonction de compatibilit� gettext" + +#: test4.ml:104 +msgid "GettextCompat.ngettext singular" +msgid_plural "GettextCompat.ngettext plural" +msgstr[0] "Fonction de compatibilit� ngettext ( singulier )" +msgstr[1] "Fonction de compatibilit� ngettext ( pluriel )" + +#: test4.ml:125 +msgid "TestGettext.Library.f_" +msgstr "Fonction de librairie f_" + +#: test4.ml:131 +msgid "TestGettext.Library.fn_ singular" +msgid_plural "TestGettext.Library.fn_ plural" +msgstr[0] "Fonction de librairie fn ( singulier )" +msgstr[1] "Fonction de librairie fn ( pluriel )" + +#: test4.ml:122 +msgid "TestGettext.Library.s_" +msgstr "Fonction de librairie s_" + +#: test4.ml:128 +msgid "TestGettext.Library.sn_ singular" +msgid_plural "TestGettext.Library.sn_ plural" +msgstr[0] "Fonction de librairie sn_ ( singulier )" +msgstr[1] "Fonction de librairie sn_ ( pluriel )" + +#: test4.ml:137 +msgid "TestGettext.Program.f_" +msgstr "Fonction de programme f_" + +#: test4.ml:143 +msgid "TestGettext.Program.fn_ singular" +msgid_plural "TestGettext.Program.fn_ plural" +msgstr[0] "Fonction de programme ( singulier )" +msgstr[1] "Fonction de programme ( pluriel )" + +#: test4.ml:134 +msgid "TestGettext.Program.s_" +msgstr "Fonction de programm s_" + +#: test4.ml:140 +msgid "TestGettext.Program.sn_ singular" +msgid_plural "TestGettext.Program.sn_ plural" +msgstr[0] "Fonction de programme sn_ ( singulier )" +msgstr[1] "Fonction de programme sn_ ( pluriel )" + +#: test4.ml:77 +msgid "TestGettext.f_" +msgstr "Fonction alias�e f_" + +#: test4.ml:83 +msgid "TestGettext.fn_ singular" +msgid_plural "TestGettext.fn_ plural" +msgstr[0] "Fonction alias�e fn_ ( singulier )" +msgstr[1] "Fonction alias�e fn_ ( pluriel )" + +#: test4.ml:74 +msgid "TestGettext.s_" +msgstr "Fonction simple alias�e s_" + +#: test4.ml:80 +msgid "TestGettext.sn_ singular" +msgid_plural "TestGettext.sn_ plural" +msgstr[0] "Fonction alias�e sn_ ( singulier )" +msgstr[1] "Fonction alias�e sn_ ( pluriel )" + +#: test4.ml:29 +msgid "f_" +msgstr "Fonction simple f_" + +#: test4.ml:41 +msgid "fgettext" +msgstr "Fonction simple fgettext" + +#: test4.ml:35 +msgid "fn_ singular" +msgid_plural "fn_ plural" +msgstr[0] "Fonction simple fn_ ( singulier )" +msgstr[1] "Fonction simple fn_ ( pluriel )" + +#: test4.ml:59 +msgid "fngettext singular" +msgid_plural "fngettext plural" +msgstr[0] "Fonction simple fngettext ( singulier )" +msgstr[1] "Fonction simple fngettext ( pluriel )" + +#: test4.ml:38 +msgid "gettext" +msgstr "Fonction simple gettext" + +#: test4.ml:56 +msgid "ngettext singular" +msgid_plural "ngettext plural" +msgstr[0] "Fonction ngettext ( singulier)" +msgstr[1] "Fonction ngettext ( pluriel )" + +#: test4.ml:26 +msgid "s_" +msgstr "Fonction simple s_" + +#: test4.ml:32 +msgid "sn_ singular" +msgid_plural "sn_ plural" +msgstr[0] "Fonction simple sn_ ( singulier )" +msgstr[1] "Fonction simple sn_ ( pluriel )" + +domain "mydomain" + +#: test4.ml:98 +msgid "GettextCompat.dcgettext" +msgstr "Fonction de compatibilit� dcgettext" + +#: test4.ml:116 +msgid "GettextCompat.dcngettext singular" +msgid_plural "GettextCompat.dcngettext plural" +msgstr[0] "Fonction de compatibilit� dcngettext ( singulier )" +msgstr[1] "Fonction de compatibilit� dcngettext ( pluriel )" + +#: test4.ml:92 +msgid "GettextCompat.dgettext" +msgstr "Fonction de compatibilit� dgettext" + +#: test4.ml:110 +msgid "GettextCompat.dngettext singular" +msgid_plural "GettextCompat.dngettext plural " +msgstr[0] "Fonction de compatibilit� dngettext ( singulier )" +msgstr[1] "Fonction de compatibilit� dngettext ( pluriel )" + +#: test4.ml:101 +msgid "GettextCompat.fdcgettext" +msgstr "Fonction de compatibilit� fdcgettext" + +#: test4.ml:119 +msgid "GettextCompat.fdcngettext singular" +msgid_plural "GettextCompat.fdcngettext plural" +msgstr[0] "Fonction de compatibilit� fdcngettext ( singulier )" +msgstr[1] "Fonction de compatibilit� fdcngettext ( pluriel )" + +#: test4.ml:95 +msgid "GettextCompat.fdgettext" +msgstr "Fonction de compatibilit� fdgettext" + +#: test4.ml:113 +msgid "GettextCompat.fdngettext singular" +msgid_plural "GettextCompat.fdngettext plural" +msgstr[0] "Fonction de compatibilit� fdngettext ( singulier )" +msgstr[1] "Fonction de compatibilit� fdngettext ( pluriel )" + +#: test4.ml:50 +msgid "dcgettext" +msgstr "Fonction simple dcgettext" + +#: test4.ml:68 +msgid "dcngettext singular" +msgid_plural "dcngettext plural" +msgstr[0] "Fonction simple dcngettext ( singulier )" +msgstr[1] "Fonction simple dcngettext ( pluriel )" + +#: test4.ml:44 +msgid "dgettext" +msgstr "Fonction simple dcgettext" + +#: test4.ml:62 +msgid "dngettext singular" +msgid_plural "dngettext plural " +msgstr[0] "Fonction simple dngettext ( singulier )" +msgstr[1] "Fonction simple dngettext ( pluriel )" + +#: test4.ml:53 +msgid "fdcgettext" +msgstr "Fonction simple fdcgettext" + +#: test4.ml:71 +msgid "fdcngettext singular" +msgid_plural "fdcngettext plural" +msgstr[0] "Fonction simple fdcngettext ( singulier )" +msgstr[1] "Fonction simple fdcngettext ( pluriel )" + +#: test4.ml:47 +msgid "fdgettext" +msgstr "Fonction simple dcgettext" + +#: test4.ml:65 +msgid "fdngettext singular" +msgid_plural "fdngettext plural" +msgstr[0] "Fonction simple dcgettext" +msgstr[1] "Fonction simple dcgettext" + diff --git a/test/test5.mo b/test/test5.mo new file mode 100644 index 0000000..eafe123 Binary files /dev/null and b/test/test5.mo differ diff --git a/test/test6.mo b/test/test6.mo new file mode 100644 index 0000000..e840b23 Binary files /dev/null and b/test/test6.mo differ diff --git a/test/test7.mo b/test/test7.mo new file mode 100644 index 0000000..aa7ba88 Binary files /dev/null and b/test/test7.mo differ diff --git a/test/test8.mo b/test/test8.mo new file mode 100644 index 0000000..4a9c982 Binary files /dev/null and b/test/test8.mo differ diff --git a/test/test9.mo b/test/test9.mo new file mode 100644 index 0000000..c535630 Binary files /dev/null and b/test/test9.mo differ diff --git a/test/unsound_warning.ml b/test/unsound_warning.ml new file mode 100644 index 0000000..3af19a6 --- /dev/null +++ b/test/unsound_warning.ml @@ -0,0 +1,6 @@ +open TestGettext;; + +let () = + Printf.eprintf (f_"%s\n") "abce"; + () +;; diff --git a/test/utf8-fr.po b/test/utf8-fr.po new file mode 100644 index 0000000..107699b --- /dev/null +++ b/test/utf8-fr.po @@ -0,0 +1,367 @@ +msgid "" +msgstr "" +"Project-Id-Version: oji 0.1\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2005-05-27 02:37+0200\n" +"PO-Revision-Date: 2005-05-27 02:39+0200\n" +"Last-Translator: French \n" +"Language-Team: French\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n>1;" + +#: ../dlg_browser.ml:93 +msgid " - browse kanji - " +msgstr " - parcourir les kanji - " + +#: ../dlg_config.ml:11 +msgid " - configuration" +msgstr "- configuration" + +#: ../dlg_editor.ml:84 +msgid " - edit kanji - " +msgstr " - édition de kanji" + +#: ../dlg_stacks.ml:18 +msgid " - stacks manager" +msgstr " - gestionnaire de tas" + +#: ../dlg_test.ml:170 +msgid " - statistics" +msgstr " - statistiques" + +#: ../dlg_test.ml:103 +msgid " - test - " +msgstr " - révision- " + +#: ../dlg_editor.ml:34 +msgid " book: " +msgstr "livre:" + +#: ../dlg_editor.ml:42 +msgid " keyword: " +msgstr "mot clef:" + +#: ../dlg_editor.ml:31 +msgid " lang: " +msgstr "langue:" + +#: ../dlg_editor.ml:46 +msgid " nicknames: " +msgstr "sésames:" + +#: ../dlg_editor.ml:50 +msgid " number (-1 if not in this book): " +msgstr "numéro (-1 si il n'est pas dans le livre)" + +#: ../dlg_test.ml:164 +msgid " bad answers were:" +msgstr "les mauvaises réponses étaient" + +#: ../dlg_search.ml:32 +msgid " because " +msgstr " car " + +#: ../dlg_stacks.ml:35 +msgid " in book" +msgstr " dans le livre " + +#: ../dlg_editor.ml:101 ../dlg_editor.ml:161 +msgid " kana " +msgstr " kana " + +#: ../dlg_editor.ml:103 ../dlg_editor.ml:163 +msgid " translation " +msgstr " signification " + +#: ../dlg_voclists.ml:41 +msgid " vocabulary files" +msgstr " fichiers de vocabulaire" + +#: ../dlg_editor.ml:36 +msgid "(just write it instead of selecting in the lists)" +msgstr "(écrivez le au lieu de le sélectionner)" + +#: ../dlg_search.ml:72 ../dlg_search.ml:81 ../dlg_search.ml:90 +msgid ") in lang " +msgstr ") en langue " + +#: ../dlg_config.ml:287 +msgid " select font for big kanji" +msgstr " chosissez la police pour le gros kanji" + +#: ../dlg_editor.ml:63 +msgid "ON readings " +msgstr "lectures ON" + +#: ../dlg_browser.ml:73 ../dlg_config.ml:68 ../dlg_test.ml:80 +msgid "ON readings" +msgstr "lectures ON" + +#: ../dlg_stacks.ml:186 +msgid "already known" +msgstr "déja connus" + +#: ../dlg_stacks.ml:26 +msgid "buffer zone" +msgstr "zone tampon" + +#: ../main.ml:39 +msgid "configuration" +msgstr "configuration" + +#: ../main.ml:47 +msgid "data" +msgstr "données" + +#: ../dlg_stacks.ml:240 +msgid "erroneous" +msgstr "érronés" + +#: ../dlg_editor.ml:39 +msgid "flashcard's datas" +msgstr "données de la fiche" + +#: ../dlg_stacks.ml:29 +msgid "import from number " +msgstr "importer depuis le numéro " + +#: ../dlg_editor.ml:25 +msgid "kanji: " +msgstr "kanji: " + +#: ../dlg_browser.ml:45 ../dlg_test.ml:52 +msgid "keywords in all langs" +msgstr "mots clefs dans toutes les langues" + +#: ../dlg_editor.ml:56 +msgid "kun readings " +msgstr "lectures kun" + +#: ../dlg_browser.ml:70 ../dlg_config.ml:97 ../dlg_test.ml:77 +msgid "kun readings" +msgstr "lectures kun" + +#: ../dlg_config.ml:27 +msgid "main data" +msgstr "données principales" + +#: ../dlg_browser.ml:57 ../dlg_test.ml:64 +msgid "nicknames in all langs" +msgstr "sésames dans toutes les langues" + +#: ../dlg_browser.ml:33 ../dlg_test.ml:40 +msgid "numbers in all books" +msgstr "numéros dans tous les livres" + +#: ../dlg_browser.ml:80 ../dlg_test.ml:87 +msgid "personal notes on this kanji" +msgstr "notes personnelles sur ce kanji" + +#: ../dlg_config.ml:18 +msgid "question type" +msgstr "type de question" + +#: ../dlg_editor.ml:22 +msgid "select flashcard" +msgstr "choisissez une fiche" + +#: ../dlg_stacks.ml:22 +msgid "stacks " +msgstr "tas " + +#: ../dlg_stacks.ml:57 +msgid "sub stacks" +msgstr "sous tas" + +#: ../dlg_editor.ml:70 ../dlg_browser.ml:76 ../dlg_config.ml:126 +#. ../dlg_test.ml:83 +msgid "vocabulary" +msgstr "vocabulaire" + +#: ../main.ml:28 +msgid "work" +msgstr "révision" + +#: ../dlg_test.ml:90 +msgid "See" +msgstr "Voir" + +#: ../main.ml:36 +msgid "add a day to kanji stacks" +msgstr "avancer d'un jour les tas de kanji" + +#: ../dlg_voclists.ml:15 +msgid "add all files you want, click on close when you are done" +msgstr "ajoutez les fichiers voulus, cliquez sur fermer une fois terminé" + +#: ../dlg_voclists.ml:44 +msgid "add and remove files" +msgstr "ajouter et supprimer des fichiers" + +#: ../dlg_config.ml:160 ../dlg_config.ml:215 +msgid "automagically detected in data file" +msgstr "détecté automagiquement dans le fichier de données" + +#: ../dlg_config.ml:160 +msgid "book to use" +msgstr "livre à utiliser" + +#: ../main.ml:49 +msgid "browse all kanji flashcards" +msgstr "parcourir les fiches de kanji" + +#: ../dlg_config.ml:18 +msgid "define what will be displayed" +msgstr "définissez ce qui sera affiché" + +#: ../main.ml:52 +msgid "edit the kanji flashcards" +msgstr "éditer les fiches de kanji" + +#: ../dlg_config.ml:82 ../dlg_config.ml:111 ../dlg_config.ml:140 +msgid "kana" +msgstr "kana" + +#: ../dlg_config.ml:37 ../dlg_config.ml:78 ../dlg_config.ml:107 +#. ../dlg_config.ml:136 +msgid "kanji" +msgstr "kanji" + +#: ../dlg_editor.ml:99 ../dlg_editor.ml:159 +msgid "kanji " +msgstr "kanji" + +#: ../dlg_test.ml:102 +msgid "kanji " +msgstr "kanji " + +#: ../dlg_voclists.ml:21 +msgid "kanjiscope file" +msgstr "fichier kanjiscope" + +#: ../dlg_config.ml:45 +msgid "keyword" +msgstr "mot-clef" + +#: ../dlg_search.ml:59 +msgid "keyword in lang " +msgstr "mot-clef en langue" + +#: ../dlg_search.ml:67 +msgid "kun reading = " +msgstr "lecture kun = " + +#: ../dlg_config.ml:215 +msgid "lang to use" +msgstr "langue à utiliser" + +#: ../dlg_voclists.ml:12 +msgid "load a vocabulary list" +msgstr "charger une liste de vocabulaire" + +#: ../main.ml:55 +msgid "manage vocabulary files" +msgstr "gérer les fichiers de vocabulaire" + +#: ../main.ml:41 +msgid "manage your kanji stacks" +msgstr "gérer les tas de kanji" + +#: ../dlg_voclists.ml:20 +msgid "native CSV file" +msgstr "fichier CSV natif" + +#: ../dlg_search.ml:63 +msgid "nickname in lang " +msgstr "sésame en langue" + +#: ../dlg_config.ml:49 +msgid "nicknames" +msgstr "sésames" + +#: ../dlg_config.ml:172 +msgid "no book" +msgstr "pas de livre" + +#: ../dlg_config.ml:228 +msgid "no lang" +msgstr "pas de langue" + +#: ../dlg_config.ml:53 +msgid "notes" +msgstr "notes" + +#: ../dlg_config.ml:41 +msgid "number" +msgstr "numéro" + +#: ../dlg_search.ml:47 +msgid "number in book " +msgstr "numéro dans le livre" + +#: ../dlg_search.ml:90 +msgid "on reading " +msgstr "lecture on" + +#: ../dlg_search.ml:85 +msgid "on reading = " +msgstr "lecture on = " + +#: ../dlg_config.ml:160 +msgid "only used to show numbers" +msgstr "utilisé seulement pour l'affichage des numéros" + +#: ../dlg_search.ml:16 +msgid "search a kanji" +msgstr "rechercher un kanji" + +#: ../dlg_stacks.ml:54 +msgid "stack " +msgstr "tas " + +#: ../main.ml:33 +msgid "stubborn work on kanji (1-0)" +msgstr "travail têtu sur les kanji (1-0)" + +#: ../dlg_stacks.ml:83 +msgid "sub stack " +msgstr "sous tas " + +#: ../dlg_test.ml:158 +msgid "success:" +msgstr "réussite:" + +#: ../dlg_test.ml:158 +msgid "test finished!" +msgstr "révisions terminées!" + +#: ../dlg_stacks.ml:32 +msgid "to" +msgstr "jusqu'au" + +#: ../dlg_config.ml:86 ../dlg_config.ml:115 ../dlg_config.ml:144 +msgid "translation" +msgstr "traduction" + +#: ../dlg_search.ml:81 +msgid "vocabulary " +msgstr "vocabulaire " + +#: ../dlg_search.ml:76 +msgid "vocabulary = " +msgstr "vocabulaire = " + +#: ../main.ml:30 +msgid "work today's kanji" +msgstr "réviser les kanji du jour" + +#: ../dlg_editor.ml:36 +msgid "you can add a lang, a book, or even a kanji" +msgstr "vous pouvez ajouter un livre, une langue ou même un kanji" + +#: ../dlg_search.ml:93 +msgid "your notes = " +msgstr "vos notes = " + diff --git a/test/utf8-ja.po b/test/utf8-ja.po new file mode 100644 index 0000000..5d6990d --- /dev/null +++ b/test/utf8-ja.po @@ -0,0 +1,395 @@ +msgid "" +msgstr "" +"Project-Id-Version: virt-p2v--devel\n" +"Report-Msgid-Bugs-To: rjones@redhat.com\n" +"POT-Creation-Date: 2008-03-22 15:53+0000\n" +"PO-Revision-Date: 2008-03-23 20:29+0000\n" +"Last-Translator: Naoko - \n" +"Language-Team: Japanese\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=1; plural=0;\n" + +#: ../virt-p2v.ml:1876 +msgid " (about %.0f minutes remaining)" +msgstr "残り 約%.0f分" + +#: ../virt-p2v.ml:1879 +msgid " (about %.0f seconds remaining)" +msgstr "残り 約%.0f秒" + +#: ../virt-p2v.ml:1902 +msgid "%s has finished" +msgstr "%s修了" + +#: ../virt-p2v.ml:813 +msgid "%s starting up ...\\n%!" +msgstr "%s起動中 ...\\n%!" + +#: ../virt-p2v.ml:1424 +msgid "(leave MAC blank for random)" +msgstr "" + +#: ../virt-p2v.ml:1391 +msgid "Architecture:" +msgstr "アーキテクチャ" + +#: ../virt-p2v.ml:1120 +msgid "Automatic from:" +msgstr "" + +#: ../virt-p2v.ml:1356 +msgid "Block devices" +msgstr "ブロックデバイス" + +#: ../virt-p2v.ml:1418 +msgid "CPUs:" +msgstr "" + +#: ../virt-p2v.ml:428 +msgid "Command failed:\\n\\n%s" +msgstr "コマンド失敗:\\n\\n%2" + +#: ../virt-p2v.ml:1090 +msgid "Configure network" +msgstr "ネットワークを設定" + +#: ../virt-p2v.ml:1382 +msgid "Configure target system" +msgstr "目標システムを設定" + +#: ../virt-p2v.ml:825 +msgid "Detecting hard drives (this may take some time) ..." +msgstr "ハードドライブ検出(要時間)" + +#: ../virt-p2v.ml:571 +msgid "" +"Disk snapshot failed: unable to read the size in sectors of block device %s" +msgstr "スナップショット失敗:%sのブロックデバイスのセクターのサイズ読み込み不可能" + +#: ../virt-p2v.ml:1136 +msgid "Don't configure the network" +msgstr "ネットワークを設定しないで下さい" + +#: ../virt-p2v.ml:323 +msgid "Error" +msgstr "エラー" + +#: ../virt-p2v.ml:295 +msgid "F12 for next screen | [ALT] [F2] root / no password for shell" +msgstr "" + +#: ../virt-p2v.ml:1021 +msgid "Finished detecting hard drives." +msgstr "ハードドライブ検出修了" + +#: ../virt-p2v.ml:1147 +msgid "Gateway" +msgstr "ゲートウエー" + +#: ../virt-p2v.ml:1384 +msgid "Hypervisor:" +msgstr "ハイパーバイザー" + +#: ../virt-p2v.ml:1143 +msgid "IP" +msgstr "" + +#: ../virt-p2v.ml:1141 +msgid "Interface" +msgstr "インタフェース" + +#: ../virt-p2v.ml:212 +msgid "Linux /boot" +msgstr "" + +#: ../virt-p2v.ml:208 +msgid "Linux swap" +msgstr "" + +#: ../virt-p2v.ml:1421 +msgid "MAC addr:" +msgstr "" + +#: ../virt-p2v.ml:1415 +msgid "Memory (MB):" +msgstr "メモリー (MB)" + +#: ../virt-p2v.ml:213 +msgid "Mountable non-root" +msgstr "" + +#: ../virt-p2v.ml:1149 +msgid "Nameserver" +msgstr "ネーム•サーバー" + +#: ../virt-p2v.ml:1145 +msgid "Netmask" +msgstr "ネットマスク" + +#: ../virt-p2v.ml:1089 +msgid "Network" +msgstr "" + +#: ../virt-p2v.ml:1237 +msgid "" +"Network configuration.\\n\\nPlease configure the network from this shell.\\n" +"\\nWhen you have finished, exit the shell with ^D or exit.\\n" +msgstr "" + +#: ../virt-p2v.ml:853 +msgid "" +"No non-removable block devices (hard disks, etc.) could be found on this " +"machine." +msgstr "" + +#: ../virt-p2v.ml:1557 +msgid "Note" +msgstr "" + +#: ../virt-p2v.ml:1599 +msgid "Performing LVM snapshots ...\\n" +msgstr "" + +#: ../virt-p2v.ml:1076 +msgid "Physical to Virtual (P2V)" +msgstr "" + +#: ../virt-p2v.ml:1133 +msgid "QEMU user network" +msgstr "QEMU ユーザーネットワーク" + +#: ../virt-p2v.ml:1287 +msgid "Remote directory" +msgstr "" + +#: ../virt-p2v.ml:1283 +msgid "Remote host" +msgstr "リモートホスト" + +#: ../virt-p2v.ml:1552 +msgid "" +"Remote hypervisor claims not to support fully virtualized %s guests.\\n" +"\\nContinuing anyway.\\n\\n%!" +msgstr "" + +#: ../virt-p2v.ml:1558 +msgid "" +"Remote hypervisor supports multiple types of fully virtualized %s guests.\\n" +"\\nPlease help further development of libvirt and virt-p2v by sending the " +"file /tmp/virt-p2v.log back to the developers. See the main virt-p2v " +"website for contact details." +msgstr "" + +#: ../virt-p2v.ml:1285 +msgid "Remote port" +msgstr "" + +#: ../virt-p2v.ml:1373 +msgid "Root filesystem" +msgstr "ルート ファイルシステム" + +#: ../virt-p2v.ml:1281 ../virt-p2v.ml:1280 +msgid "SSH configuration" +msgstr "SSH 設定" + +#: ../virt-p2v.ml:1338 +msgid "SSH configuration failed" +msgstr "SSH設定失敗" + +#: ../virt-p2v.ml:1289 +msgid "SSH username" +msgstr "SSHユーザーネーム" + +#: ../virt-p2v.ml:1358 +msgid "Select block devices to send" +msgstr "送信用のブロックデバイスを選択" + +#: ../virt-p2v.ml:1374 +msgid "Select root filesystem" +msgstr "" + +#: ../virt-p2v.ml:1123 +msgid "Start a shell" +msgstr "" + +#: ../virt-p2v.ml:1139 +msgid "Static configuration:" +msgstr "静的設定" + +#: ../virt-p2v.ml:1381 +msgid "Target system" +msgstr "" + +#: ../virt-p2v.ml:1302 +msgid "Test SSH connection" +msgstr "SSHコネクションをテスト" + +#: ../virt-p2v.ml:676 +msgid "Testing SSH connection by listing files in remote directory ...\\n" +msgstr "" + +#: ../virt-p2v.ml:1081 ../virt-p2v.ml:1080 +msgid "Transfer type" +msgstr "" + +#: ../virt-p2v.ml:1505 +msgid "Try to fetch remote hypervisor capabilities ...\\n" +msgstr "遠隔ハイパーバイザー機能をフェッチする" + +#: ../virt-p2v.ml:1266 +msgid "Trying QEMU network configuration.\\n" +msgstr "" + +#: ../virt-p2v.ml:1250 +msgid "Trying network auto-configuration from root filesystem ...\\n" +msgstr "" + +#: ../virt-p2v.ml:1241 +msgid "Trying static network configuration.\\n" +msgstr "" + +#: ../virt-p2v.ml:214 +msgid "Unknown partition type" +msgstr "不明のパーティションタイプ" + +#: ../virt-p2v.ml:1298 +msgid "Use SSH compression (not good for LANs)" +msgstr "" + +#: ../virt-p2v.ml:1427 +msgid "Use remote libvirtd" +msgstr "" + +#: ../virt-p2v.ml:1077 +msgid "Virtual to Virtual (V2V)" +msgstr "" + +#: ../virt-p2v.ml:1551 +msgid "Warning" +msgstr "警告" + +#: ../virt-p2v.ml:1068 +msgid "" +"Welcome to %s, a live CD for migrating a physical machine to a virtualized " +"host.\\n\\nTo continue press the Return key.\\n\\nTo get a shell you can use " +"[ALT] [F2] and log in as root with no password.\\n\\nExtra information is " +"logged in /tmp/virt-p2v.log but this file disappears when the machine " +"reboots." +msgstr "" + +#: ../virt-p2v.ml:211 +msgid "Windows root" +msgstr "" + +#: ../virt-p2v.ml:821 +msgid "You should only run this script from the live CD or a USB key." +msgstr "ライブCDもしくはUSBキーからこのスクリプトを実行すべきです" + +#: ../virt-p2v.ml:1801 +msgid "" +"\\\n" +"\\n\\n" +msgstr "" +"\\\n" +"\\n\\n" + +#: ../virt-p2v.ml:1256 ../virt-p2v.ml:1244 +msgid "" +"\\nAuto-configuration failed. Starting a shell.\\n\\nPlease configure the " +"network from this shell.\\n\\nWhen you have finished, exit the shell with ^D " +"or exit.\\n" +msgstr "" + +#: ../virt-p2v.ml:1840 +msgid "\\nSending /dev/%s (%.3f GB) to remote machine\\n\\n%!" +msgstr "" + +#: ../virt-p2v.ml:1903 +msgid "" +"\\nThe physical to virtual migration is complete.\\n\\nPlease verify the " +"disk image(s) and configuration file on the remote host, and then start up " +"the virtual machine by doing:\\n\\ncd %s\\nvirsh define %s\\n\\nWhen you " +"press [OK] this machine will reboot." +msgstr "" +"\\nThe physical to virtual migration is complete.\\n\\nPlease verify the " +"disk image(s) and configuration file on the remote host, and then start up " +"the virtual machine by doing:\\n\\ncd %s\\nvirsh define %s\\n\\nWhen you " +"OKを押して再起動する。" + +#: ../virt-p2v.ml:1819 +msgid "\\nWriting configuration file ...\\n" +msgstr "" + +#: ../virt-p2v.ml:683 +msgid "" +"\\n\\nDid SSH work?\\nHint: If not sure, there is a shell on console [ALT] " +"[F2]\\n" +msgstr "" + +#: ../virt-p2v.ml:599 +msgid "" +"\\n\\nDid automatic network configuration work?\\nHint: If not sure, there " +"is a shell on console [ALT] [F2]" +msgstr "" + +#: ../virt-p2v.ml:322 +msgid "" +"\\n\\nIf you want to report this error, there is a shell on [ALT] [F2], log " +"in as root with no password.\\n\\nPlease provide the contents of /tmp/virt-" +"p2v.log and output of the 'dmesg' command." +msgstr "" + +#: ../virt-p2v.ml:286 +msgid "open_centered_window: not in newt mode" +msgstr "" + +#: ../virt-p2v.ml:447 +msgid "shget: command killed by signal %d" +msgstr "" + +#: ../virt-p2v.ml:449 +msgid "shget: command stopped by signal %d" +msgstr "" + +#: ../virt-p2v.ml:661 +msgid "ssh: exited with error code %d" +msgstr "" + +#: ../virt-p2v.ml:662 +msgid "ssh: killed by signal %d" +msgstr "" + +#: ../virt-p2v.ml:663 +msgid "ssh: stopped by signal %d" +msgstr "" + +#: ../virt-p2v.ml:528 +msgid "unexpected output: " +msgstr "" + +#: ../virt-p2v.ml:1915 +msgid "usage: virt-p2v [--test] [ttyname]\\n%!" +msgstr "" + diff --git a/test/valid_format.ml b/test/valid_format.ml new file mode 100644 index 0000000..527674a --- /dev/null +++ b/test/valid_format.ml @@ -0,0 +1,12 @@ +open TestGettext;; + +let e = Printf.eprintf;; + +let () = + e (f_ "%ld") 1l; + e (f_ "%d") 1; + e (f_ "%s") "abcd"; + e (f_ "%a") (fun chn e -> Printf.fprintf chn "%s" e) "ancd"; + e (fn_ "%d category" "%d categories" 1) 1 +;; + diff --git a/website/Makefile b/website/Makefile new file mode 100644 index 0000000..116f685 --- /dev/null +++ b/website/Makefile @@ -0,0 +1,38 @@ +########################################################################## +# ocaml-gettext: a library to translate messages # +# # +# Copyright (C) 2003-2008 Sylvain Le Gall # +# # +# This library is free software; you can redistribute it and/or # +# modify it under the terms of the GNU Lesser General Public # +# License as published by the Free Software Foundation; either # +# version 2.1 of the License, or (at your option) any later version; # +# with the OCaml static compilation exception. # +# # +# This library 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 # +# Lesser General Public License for more details. # +# # +# You should have received a copy of the GNU Lesser General Public # +# License along with this library; if not, write to the Free Software # +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 # +# USA # +########################################################################## + +SGML_CATALOG_FILES=/etc/sgml/catalog +export SGML_CATALOG_FILES + +PROC = xsltproc --nonet --catalogs +WEBSITE = /usr/share/xml/docbook/custom/website/2.5.0/xsl + +all: ocaml-gettext.html + +%.html: autolayout.xml + $(PROC) $(WEBSITE)/chunk-tabular.xsl $< + +autolayout.xml: layout.xml + $(PROC) -o $@ $(WEBSITE)/autolayout.xsl $< + +clean: + -$(RM) *.html autolayout.xml diff --git a/website/layout.xml b/website/layout.xml new file mode 100644 index 0000000..61bd12e --- /dev/null +++ b/website/layout.xml @@ -0,0 +1,63 @@ + + + + + + + + 2003-2005 + Sylvain Le Gall + + + + + + Documentation + + Reference manual + + Reference manual (PDF) + + API + + Extended API + + Download + + Bugs + + + + diff --git a/website/ocaml-gettext-documentation.xml b/website/ocaml-gettext-documentation.xml new file mode 100644 index 0000000..58f649a --- /dev/null +++ b/website/ocaml-gettext-documentation.xml @@ -0,0 +1,44 @@ + + + + + + + + + + Documentation of ocaml-gettext + Documentation + Documentation of ocaml-gettext library + + + + +
+ Documentation + + Here you can find all the documentation of the ocaml-gettext project. +
+ +
diff --git a/website/ocaml-gettext.xml b/website/ocaml-gettext.xml new file mode 100644 index 0000000..fc53abd --- /dev/null +++ b/website/ocaml-gettext.xml @@ -0,0 +1,111 @@ + + + + + + + + Project ocaml-gettext + ocaml-gettext + OCaml library for string translation. + + + + +
+ Goals + ocaml-gettext should provide a support for internationalization of OCaml program. + Constraints: + + + provides a pure OCaml implementation, + + + the API should be as close as possible to GNU gettext, + + + provides a way to automatically extract translatable strings from OCaml source + code. + + +
+ +
+ Description + + + Internationalization of a program means that the program have the possibility to handle + different language. Handling different language means that it can output string which + depends on the language of the user, who will read it. Typically, if a program output + "bonjour" for a french user, and "hello" for an english user, this + program is internationalized. + + + + It is possible to make things very simple (at least i think so), for internationalizing + a program. GNU gettext seems a very good solution. You just need to use a special function + to translate strings, that need to be translated and they are treated automatically. After + having include this function (in the case of ocaml-gettext, functions are "s_", + "f_","sn_" and "fn_"). + + + For now, ocaml-gettext provides enough service to build a basic internationalized + program. It comes with: + + + a pure OCaml implementation, based on Camomile, + + + a binding to GNU gettext library, + + + ocaml-gettext a tool to extract strings from OCaml source. + + +
+ +
+ News + +
+ April, 30th 2008 + Version 0.3.0 + + + New version that works with OCaml 3.10.0. It also use less dependencies + (no more camlidl, nor ocaml-ast-analyze). Improve PO file merging. This + release fix a lot of bugs, making its usage easier. + +
+ +
+ April, 16th 2005 + Version 0.2.0 + + + This is the first official public release. + +
+
+
diff --git a/website/website.xml b/website/website.xml new file mode 100644 index 0000000..c17decb --- /dev/null +++ b/website/website.xml @@ -0,0 +1,32 @@ + + + + + + + + Project ocaml-gettext + + +