commit db7e22c83b8c1d6da2399170fbf6259abfbd29c0 Author: imacat Date: Mon Sep 9 19:50:18 2019 +0800 Initialized from version 1.28 (2009/6/27). diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7c00a62 --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +Makefile +Makefile.old +blib +Build +_build +MYMETA.json +MYMETA.yml +pm_to_blib diff --git a/Artistic b/Artistic new file mode 100644 index 0000000..5f22124 --- /dev/null +++ b/Artistic @@ -0,0 +1,131 @@ + + + + + The "Artistic License" + + Preamble + +The intent of this document is to state the conditions under which a +Package may be copied, such that the Copyright Holder maintains some +semblance of artistic control over the development of the package, +while giving the users of the package the right to use and distribute +the Package in a more-or-less customary fashion, plus the right to make +reasonable modifications. + +Definitions: + + "Package" refers to the collection of files distributed by the + Copyright Holder, and derivatives of that collection of files + created through textual modification. + + "Standard Version" refers to such a Package if it has not been + modified, or has been modified in accordance with the wishes + of the Copyright Holder as specified below. + + "Copyright Holder" is whoever is named in the copyright or + copyrights for the package. + + "You" is you, if you're thinking about copying or distributing + this Package. + + "Reasonable copying fee" is whatever you can justify on the + basis of media cost, duplication charges, time of people involved, + and so on. (You will not be required to justify it to the + Copyright Holder, but only to the computing community at large + as a market that must bear the fee.) + + "Freely Available" means that no fee is charged for the item + itself, though there may be fees involved in handling the item. + It also means that recipients of the item may redistribute it + under the same conditions they received it. + +1. You may make and give away verbatim copies of the source form of the +Standard Version of this Package without restriction, provided that you +duplicate all of the original copyright notices and associated disclaimers. + +2. You may apply bug fixes, portability fixes and other modifications +derived from the Public Domain or from the Copyright Holder. A Package +modified in such a way shall still be considered the Standard Version. + +3. You may otherwise modify your copy of this Package in any way, provided +that you insert a prominent notice in each changed file stating how and +when you changed that file, and provided that you do at least ONE of the +following: + + a) place your modifications in the Public Domain or otherwise make them + Freely Available, such as by posting said modifications to Usenet or + an equivalent medium, or placing the modifications on a major archive + site such as uunet.uu.net, or by allowing the Copyright Holder to include + your modifications in the Standard Version of the Package. + + b) use the modified Package only within your corporation or organization. + + c) rename any non-standard executables so the names do not conflict + with standard executables, which must also be provided, and provide + a separate manual page for each non-standard executable that clearly + documents how it differs from the Standard Version. + + d) make other distribution arrangements with the Copyright Holder. + +4. You may distribute the programs of this Package in object code or +executable form, provided that you do at least ONE of the following: + + a) distribute a Standard Version of the executables and library files, + together with instructions (in the manual page or equivalent) on where + to get the Standard Version. + + b) accompany the distribution with the machine-readable source of + the Package with your modifications. + + c) give non-standard executables non-standard names, and clearly + document the differences in manual pages (or equivalent), together + with instructions on where to get the Standard Version. + + d) make other distribution arrangements with the Copyright Holder. + +5. You may charge a reasonable copying fee for any distribution of this +Package. You may charge any fee you choose for support of this +Package. You may not charge a fee for this Package itself. However, +you may distribute this Package in aggregate with other (possibly +commercial) programs as part of a larger (possibly commercial) software +distribution provided that you do not advertise this Package as a +product of your own. You may embed this Package's interpreter within +an executable of yours (by linking); this shall be construed as a mere +form of aggregation, provided that the complete Standard Version of the +interpreter is so embedded. + +6. The scripts and library files supplied as input to or produced as +output from the programs of this Package do not automatically fall +under the copyright of this Package, but belong to whoever generated +them, and may be sold commercially, and may be aggregated with this +Package. If such scripts or library files are aggregated with this +Package via the so-called "undump" or "unexec" methods of producing a +binary executable image, then distribution of such an image shall +neither be construed as a distribution of this Package nor shall it +fall under the restrictions of Paragraphs 3 and 4, provided that you do +not represent such an executable image as a Standard Version of this +Package. + +7. C subroutines (or comparably compiled subroutines in other +languages) supplied by you and linked into this Package in order to +emulate subroutines and variables of the language defined by this +Package shall not be considered part of this Package, but are the +equivalent of input as in Paragraph 6, provided these subroutines do +not change the language in any way that would cause it to fail the +regression tests for the language. + +8. Aggregation of this Package with a commercial distribution is always +permitted provided that the use of this Package is embedded; that is, +when no overt attempt is made to make this Package's interfaces visible +to the end user of the commercial distribution. Such use shall not be +construed as a distribution of this Package. + +9. The name of the Copyright Holder may not be used to endorse or promote +products derived from this software without specific prior written permission. + +10. THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED +WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + + The End diff --git a/BUGS b/BUGS new file mode 100644 index 0000000..3b986ba --- /dev/null +++ b/BUGS @@ -0,0 +1,4 @@ +Please send your bug reports to me directly, at +imacat@mail.imacat.idv.tw. + +Sorry that mailing list is not available yet till this time. diff --git a/Build.PL b/Build.PL new file mode 100755 index 0000000..33fe42e --- /dev/null +++ b/Build.PL @@ -0,0 +1,22 @@ +#! /usr/bin/perl -w +use strict; +use Module::Build; + +my $build = Module::Build->new( + dist_name => "Locale-Maketext-Gettext", + dist_version => "1.28", + dist_abstract => "Joins gettext and Maketext frameworks", + dist_author => "imacat ", + license => "perl", + sign => 1, + + script_files => [ "script/maketext" ], + requires => { + "perl" => "5.8.0", + }, + add_to_cleanup => [ "t/test_native.po", "t/locale/en/LC_MESSAGES/test_native.mo" ], +); + +$build->create_build_script; + +__END__ diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..94a9ed0 --- /dev/null +++ b/COPYING @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If 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 convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/Changes b/Changes new file mode 100644 index 0000000..b58f006 --- /dev/null +++ b/Changes @@ -0,0 +1,581 @@ +Locale-Maketext-Gettext change log + +2009-06-27 version 1.28 + Build script fix. No code changes. + 1. Build.PL: Removed the build_requires, as they are not + required by the build process, but by the test process. + The build process should not be blocked by them. + Suggested by Ryan Niebur and + Adam Kennedy + (and Audrey Tang). + +2009-04-28 version 1.27 + Test suite fix. No code changes. + 1. t/03-errors.t: Fixed the find_system_mo() subroutine to + exclude MO files with special characters that might be + considered as code by Locale::Maketext. + 2. t/02-big-endian.t: Fixed the native-built MO file test + and moved the $POfile and $MOfile file name assignment + to the beginning, to ensure that their values are + assigned even if GNU gettext is not installed and + the test is skipped on the target test system. + +2008-11-11 + 1. INSTALL: Fixed the grammer, changed "none" to "None.". + +2008-04-22 version 1.26 + Documentation fix. No code changes. + 1. bin/maketext: Moved to script/maketext. + +2008-04-21 + 1. TODO: Added. + 2. Artistic and COPYING: Added. + 3. INSTALL: Added. Installation instructions moved from README + to INSTALL. + +2008-04-11 version 1.25 + Test suite fix. No code changes. + 1. t/03-errors.t and t/08-f-errors.t: Added find_system_mo(), to + select a system MO file that we can use. + +2008-02-26 version 1.24 + Test and build suite fix. No code changes. + 1. t/02-big-endian.t: Added creating PO at run time, in order + to work with GNU gettext < 0.15 whose msgfmt does not support + msgctxt yet. This should work with both older and newer + GNU gettext. + 2. t/test_native.po: Removed. It is now generated by + t/02-big-endian.t at run time. + 3. Makefile.PL: Added "clean" to clean-up possible existing + test_native.po and test_native.mo. + 4. Build.PL: Added "add_to_cleanup" to clean-up possible existing + test_native.po and test_native.mo. + 5. Build.PL: Added "build_requires". + 6. t/08-f-errors.t: Fixed so that it finds the newest MO file + found on the system, in order to avoid lagacy MO files. + (gettext 0.10 in 1995?) + 7. t/08-f-errors.t: Fixed test 38 so that it skips in the eval() + block, and dumps the error on failure. + 8. Changes: Added several missing change notes in version 1.23. + +2008-02-19 version 1.23 + Added support for GNU gettext pgettext() as pmaketext(), to + translate text messages in a particular context. + 1. Gettext.pm: Added the pmaketext() method as an equivalent of + the pgettext() function in GNU gettext, to look up the text + message in a particular context, according to the suggestion + by Chris Travers. + 2. Functions.pm: Added the pmaketext() function as an equivalent + of the pgettext() function in GNU gettext, and the dpmaketext() + function as an equivalent of the dpgettext() function in GNU + gettext, to look up the text message in a particular context, + according to the suggestion by Chris Travers. + 3. Gettext.pm and Functions.pm: Fixed documentation and comments. + Changed "perl" as "Perl". Removed contractions and replaced + them with full sayings, in order to work with syntax + highlighting. Removed emotional marks for the same reason. + 4. Makefile.PL: Added conditional checks for whether LICENSE is + available in ExtUtils::MakeMaker. + 5. Gettext.pm and Functions.pm: Added xgettext keyword notation + for dmaketext(), pmaketext() and dpmaketext(). + 6. t/locale/*/LC_MESSAGES/*.mo, t/test_native.po: Added messages + with contexts for pmaketext() testings. + 7. t/*.t: Added tests for pmaketext() and dpmaketext(). + 8. Gettext.pm: Fixed the failure_handler_auto() method to remove + the message context from the message key before sending to + the failure lexicon. The failure_handler_auto() method + differs from its Locale::Maketext parent from now on. + +2007-03-28 version 1.22 + 1. t/*-f-*.t: Moved Locale::Maketext::Gettext::Functions::_reset() + from the end of each set of tests, to the front of each set of + tests. If at the end of the test set, it would not be run if + the eval{} block fails. + 2. Gettext.pm, Functions.pm and maketext: Updated the URL of the + GNU gettext manual in the their POD documents. + 3. Functions.pm: The example in its POD document is updated to show + more of the nature of Locale::Maketext. + +2007-03-28 version 1.21 + 1. 04-encodings.t and 09-f-encodings.t: Inserted test#26 to test the + $@ error message. It is an encode() error that is not affected + by the Locale::Maketext $@ change with "local $@". + 2. 06-racing.t: Added $LOOKUP_FAILURE as look-up failure flag. Added + a simple subroutine to handle lookup failure. Test that to see if + we are really die from lookup failure. + 3. Gettext.pm, Functions.pm and 09-f-encodings.t: Renamed + ENCODING_SET to USERSET_ENCODING. USERSET_ENCODING is set to + the encoding user specified. Future test should use "exists", + but not "defined", as user may supply undef on this. + 4. Gettext.pm: Renaming _lmg_failure_handler_auto() to + failure_handler_auto(). It should be OK to override it, and this + help subclasses to employ our version. + 5. Functions.pm: Note why we don't use $@ but @ as the prototypes of + __(), N_(), maketext(). + 6. Functions.pm: Removed unused global variable $ENCODING. + 7. Functions.pm: Renamed %VARS to %PARAMS. Changed $KEY_ENCODING + to $PARAMS{"KEY_ENCODING"}. Changed $DIE_FOR_LOOKUP_FAILURES to + $PARAMS{"DIE_FOR_LOOKUP_FAILURES"}. Changed $ENCODE_FAILURE to + $PARAMS{"ENCODE_FAILURE"}. + 8. Gettext.pm and Functions.pm: Changed the returning line of + encoding(), key_encoding() and die_for_lookup_failures() to one + conditional line. This is more neat. + 9. Functions.pm: Updated _reset(), resetting $PARAMS{"KEY_ENCODING"} to + "US-ASCII" instead of undef, add resetting + $PARAMS{"DIE_FOR_LOOKUP_FAILURES"} to 0; + 10. Gettext.pm: Updated failure_handler_auto(), check if this method + is revoked statically. + 11. t/*-f-*.t: Added Locale::Maketext::Gettext::Functions::_reset() to + the end of each set of tests, to reset the environment. + 12. Gettext.pm: Added fail_with() as a wrapper to record the user set + lookup failure handler, so that we can safely switch with + die_for_lookup_failures(). A new attribute {"USERSET_FAIL"} is + added to record it. All references to fail_with() in other places + of the code are changed to SUPER::fail_with(). fail_with() is + only for the user since it will update the registered user + preference. + 13. Gettext.pm and Functions.pm: In maketext(), check if the key is + Perl utf8 text with Encode::is_utf8() first before decode() the + key, so that user can safely run a multibyte-awared script. + 14. Gettext.pm and Functions.pm: In maketext(), check if the key is + Perl utf8 text with Encode::is_utf8() first before encode(), for + empty/invalid lexicon with key_encoding() set but no output + encoding, so that we can have a real pass through. In the case + of an empty/invalid lexicon, the user should be responsible for + the output encoding. Added $keyd as the decoded() $key, so + that we can check the original $key later. + 15. Functions.pm: In __(), removed unused variables $encoding, + $lh_encoding and $key_encoding. + 16. maketext: In parse_args(), check the return value of the eval { } + block instead of $@, and remove the extra new line when outputing + the error in $@. + 17. maketext: Added an example at the DESCRIPTION chapter of the + POD documentation. + 18. Gettext.pm: Added a note on the relation of fail_with() + die_for_lookup_failures() at the description of the + die_for_lookup_failures() method at the METHODS section + of the POD documentation. + 19. Makefile.PL: Added LICENSE attribute as "perl". + +2007-03-28 version 1.20 + Rewrite to follow the error handler design of Locale::Maketext, i.e. + the fail_with() method, instead of our own. + Clean up encoding mess. You may need to check if your application + was depending on the previous wrong, hard-to-handle behavior on look + up failurs. The new behavior should be easier to deal with. + 1. Gettext.pm: In the textdomain() function, make sure {"ENCODING"} + is not set to undef when MO file does not exists. + 2. Functions.pm: In the encoding() function, removed a piece of long + gone, commented code. + 3. Gettext.pm and Functions.pm: In the encoding(), textdomain() and + the _get_handle() function, add a flag {"ENCODING_SET"} to tag if + we should respect the encoding() setting of the user. If there is + no user preferred encoding(), when obtaining language handle with + _get_handle() or textdomain(), encoding() will change to the + output encoding() to that of the corresponding MO file. + 4. 09-f-encoding.t: Added "delete $VARS{"ENCODING_SET"}" in the end + of each test to clean up the environment. + 5. Gettext.pm: Update the version number to 1.20. + 6. Functions.pm: Update the version number to 0.10. + 7. t/99-pod.t: Added. + 8. Makefile.PL: Stylish clean-up and update. Added "SIGN". + 9. Build.PL: Added. + +2007-03-27 + 1. Gettext.pm: Added _lmg_failure_handler_auto(), as a replacement + of Locale::Maketext's failure_handler_auto(). Locale::Maketext's + failure_handler_auto() until version 1.10 has problem with + message keys without any brackets. See bug#33938 on rt.cpan.org. + It's content was simply copied from failure_handler_auto(). + 2. Gettext.pm: Removed classes Locale::Maketext::Gettext::_AUTO, + Locale::Maketext::Gettext::_AUTO::i_default, + Locale::Maketext::Gettext::_EMPTY and + Locale::Maketext::Gettext::_EMPTY::i_default. We are using + Locale::Maketext's fail_with() method to deal with look up errors + rather than making dummy language handles and deal with them by + ourselves. This simplify things, making the code more compatible + with Locale::Maketext. As a result, Locale::Maketext's + fail_with() method and {"fail"} attribute are working now. + 3. Gettext.pm: In the maketext() method, removed the code that deal + with look up errors by ourselves. Now we handle this with the + {"fail"} attribute and the _lmg_failure_handler_auto() method, + following the original Locale::Maketext's design. + 4. Gettext.pm: In the textdomain() method, added clear_isa_scan() in + the end to clear the ISA handler look up cache table. This way we + are not affected by the cache of _lmg_failure_handler_auto() + when switching domain with textdomain(). + 5. Gettext.pm and Functions.pm: In encoding() and key_encoding(), + feeding undef delete the property. This is to unify the practice + of "exists" and "defined" tests. All tests should use "exists" + instead of "defined" now. + 6. Functions.pm: Added classes + Locale::Maketext::Gettext::Functions::_EMPTY and + Locale::Maketext::Gettext::Functions::_EMPTY::i_default as a + dummy locale. + 7. Functions.pm: Add _get_empty_handle() to return the dummy locale, + with die_for_lookup_failures() properly set. This replaces the + $_EMPTY setting in the beginning of the file. + 8. Functions.pm: In the _get_handle() method added unset the + key_encoding() before returning the language handle. + key_encoding() is handled by Locale::Maketext::Gettext::Functions. + Locale::Maketext::Gettext does not need to handle key_encoding(). + This is to follow the nature of functional API that + key_encoding() should apply to all the following session in + functional API. + 9. Gettext.pm and Functions.pm: In maketext(), stop playing magic + with encoding() and key_encoding() that automatically encode() + text back to key_encoding() when lookup fails. The result is + too unpredictable. The application should specify which encoding + they want themselves. + +2007-03-22 version 1.18 + Fixes for Perl 5.9. Locale::Maketext in Perl 5.9 does not set $@ + as its error message. Tests replying on $@ may fail for this. But + I should check the return value of the eval{} block rather than $@ + anyway. + 1. t/*.t: Adition of $r as the return value of the eval{} blocks. + Addition of "return 1;" to the eval{} blocks to specify the return + values. Changing tests "ok($@, "");" to "ok($r, 1);". Changing + tests "ok($@, qr/maketext doesn't know how to say/);" and + "ok($@, qr/does not map to/);" to "ok($r, undef)". This is to + be refined. It is necessary to test if it fails by our expected + reason rather than only knowing that it fails. + 2. Update the version number of Locale::Maketext::Gettext to match + the distribution version. + +2005-04-27 version 1.17 + 1. Changes: Typo fixed. + +2005-04-26 version 1.16 + Test suite fixes. + 1. 02-big-endian.t: In test 7, 8, $MOfile and $POfile are quoted + when sending to the shell (backticks), to be safe to spaces and + other special shell characters in their paths. + 2. 11-command-line.t: In all tests $maketext is quoted when sending + to the shell (backticks), to be safe to spaces and other special + shell characters in its path. This should solve the CPAN tester + failures 200029, 200332 and 200331. Thanks to Max Maischein + for testing and reporting this. + 3. Changes: File edited to widen line limit from 60 columns to 79 + columns, to ease the reading. + 4. THANKS: Updated. + +2005-04-26 version 1.15 + 1. SIGNATURE: Included Modules::Signature gpg signature file added. + 2. 00-signature.t: Added. + +2005-04-21 version 1.14 + 1. maketext: Prototypes of the following subroutines are + declared first now: main() and parse_args(). + +2005-04-20 version 1.13 + 1. Changes: Addition of new unencoded UTF-8 tests is appended to the + change log. + 2. Changes: Addition of warning of key_encoding() is appended to the + change log. + 3. Changes: Typo fixed: "Vesion" should be "Version". + 4. Changes: Typo fixed: 2005-04-10 should be 2005-04-20. + +2005-04-20 version 1.12 + Documentation fixes. + 1. Update the copyright year on the updated files. + 2. Changes: Version number added. + +2005-04-19 version 1.11 + 1. Gettext.pm: Using undef as encoding or key_encoding. Methods + encoding() and key_encoding() now check the number of arguments + instead of the argument value. When undef, the unencoded UTF-8 + text is returned. + 2. Gettext.pm: Prototype of subroutine read_mo() is declared first + now. + 3. Gettext.pm: Deprecated subroutine readmo() is removed. + 4. Functions.pm: Prototypes of the following subroutines are declared + first now: bindtextdomain(), textdomain(), get_handle(), + maketext(), __(), N_(), dmaketext(), reload_text(), encoding(), + key_encoding(), encode_failure(), die_for_lookup_failures(), + _declare_class(), _catclass(), _init_textdomain(), _get_langs(), + _get_handle(), _reset(), _new_rid(), _k() and _lang(). + 5. 07-f-basic.t: Locale environment variables are cleared before + running tests, to avoid confusion from the user's current locale. + 6. 10-f-switching.t: Locale environment variables are cleared before + running tests, to avoid confusion from the user's current locale. + 7. Gettext.pm: Default KEY_ENCODING to US-ASCII. It is always + decoded to UTF-8 first when looked up in the _AUTO lexicon, to + ensure that the resulted text is always in UTF-8. + 8. Gettext.pm: Avoid caching undefined MO file encoding when MO file + is not available. + 9. Gettext.pm: New attribute "MO_ENCODING" is added to to record the + encoding of the MO file. + 10. Functions.pm: Default KEY_ENCODING to US-ASCII. It is always + decoded to UTF-8 first when looked up in the _AUTO lexicon, to + ensure that the resulted text is always in UTF-8. + 11. 04-encodings.t: Tests 28-33 added to test returning unencoded + UTF-8 text. + 12. 09-f-encodings.t: Tests 28-33 added to test returning unencoded + UTF-8 text. + 13. Gettext.pm: Warning is added to the use of key_encoding(). + 14. Functions.pm: Warning is added to the use of key_encoding(). + +2005-04-05 + 1. Gettext.pm: Subroutine attribute "method" is taged on the + following methods: encoding(), key_encoding(), new(), + subclass_init(), bindtextdomain(), textdomain(), maketext(), + reload_text(), die_for_lookup_failures() and encode_failure(). + +2003-05-12 + 1. Gettext.pm: key_encoding applied to "not found" keys only, so that + it can be looked up in their original encoding in the %Lexicon. + +2003-05-09 version 1.10 + 1. Functions.pm: _get_handle: Fallback language handler changed from + the failed language handler $FLH to the empty lexicon $_EMPTY. + This has no real effect, since lookup failures are always handled + by $FLH, but not the current language handler $LH. But this + matches the reality. + +2003-05-08 + 1. Changes: typo (version 1.07 should be version 1.09) + +2003-05-07 + 1. Changes: An excess blank line is removed. + +2003-05-07 version 1.09 + Test suite fix. + 1. 02-big-endian.t: Fix on $MSGFMT so that it works on more + platforms. + +2003-05-07 version 1.08 + Fixes mistakes from version 1.07 + 1. Return the interpreter line to the maketext script. It was a + misunderstanding at 1.07 + 2. Content-Transfer-Encoding is fixed from 8bit to 7bit in the + English MO files. + 3. t/test_native.po: Added. + 4. 02-big-endian.t: Test suite for native-built MO files are added. + Please report your failures. + +2003-05-05 version 1.07 + Bug fixes. + 1. Fix to the interpreter line in the maketext script so that it can + be replaced with correct interpreter at make time. + 2. 11-command-line.t: script location changed from lib/maketext to + blib/script/maketext. + 3. C locale is added to the test suite. + 4. 11-command-line.t: Locales en, zh_TW, zh_CN are remove from the + test suite, to avoid perl warnings from systems that did not + install these locales. + 5. 11-command-line.t: $ENV{"LANGUAGE"} controls added. + +2003-05-04 version 1.06 + 1. Language function override is not available for L::M::G::F and the + maketext script. I almost forgot this problem. Notice is added + to the documentation. Suggestions are welcome. + +2003-05-03 version 1.06 + Introducing "maketext", a command line interface to + Locale::Maketext::Gettext (and Locale::Maketext). + 1. The maketext script is added, a command line interface to + Locale::Maketext::Gettext (and Locale::Maketext). + +2003-05-03 version 1.05 + Test suites fixes. This fixes the failures reported from CPAN + testers. Failures reported from CPAN testers are caused by + Archive-Tar called by CPANPLUS, but not by Locale::Maketext::Gettext + itself. Bug report concerning this problem is already submitted to + Archive-Tar. + 1. Test suite temporarily files test_dyn.mo are removed from + MANIFEST. + 2. File::Copy is used instead of link in the test suites to enable + tests on platforms that does not support hard links (Win32, for + example). + 3. Temporarily files are cleaned up at the end of 05-switching.t and + 10-f-switching.t. + Upgrade is not required if you are using version 1.04. + +2003-05-02 version 1.04 + 1. Support for MO files without encoding specified was added. I + don't know there are MO files born without its encoding. ^^; + 2. L::M::G::F: textdomain() now works for default system locale + directories, too. For domains that are not binded with + bindtextdomain(), it searches the system locale directories to + find the domain MO files. Unlike textdomain() in L::M::G, it + remembers the search result in order to build the index key. + 3. Tests for default system locale directory search are added. It + may be skipped, though. + +2003-05-02 version 1.03 + 1. L::M::G: A algorism bug about key_encoding with auto-lexicon was + fixed. + 2. L::M::G::F: I decide to give up mod_perl safety for encoding, + key_encoding, encode_failure and die_for_lookup_failures(). + POSIX::setlocale() didn't solve this problem, either. Use L::M::G + if you need that. Suggestions and solutions are welcome. + 3. L::M::G: FLH (Failure language handler) is added to + help switching the die_for_lookup_failures() setting. + Maketext can be slightly faster. + +2003-05-01 + Labor's Day! + A Major rewrite to Locale::Maketext::Gettext::Functions. + 1. Packages are declared by a random class ID, instead of by its text + domain previously, in order for a text domain to be redeclared + with different sets of languages infinitely. This solves the + problem where languages cannot to be removed at run-time. You can + add/remove languages/MO files at run time now. You can also + bindtextdomain() to another directory after setting textdomain(). + This is not possible to the object interface of + Locale::Maketext::Gettext (and Locale::Maketext). + 2. fork_and_test is removed from the test suite. It does not seem to + be required by the current random class ID system anymore. + 3. The used of ($t1..$t6) is replaced by ($_[0]..$_[5]) in to + 05-switching.t. + 4. Garbage collection is added. Language handles associated with + abandoned localization classes are dropped in time, in order to + reduce memory used. + +2003-04-30 + 1. L::M::G::F: I forgot to document dmaketext, too. ^^; + But, no, I will not put a third release today! + 2. L::M::G::F: Fixes to documentation typos. + 3. maketext() and __() are exchanged. maketext() is a wrapper to + __() now. The speed of __() should be slightly faster. + +2003-04-30 version 1.02 + Documentation fixes. + 1. L::M::G::F: Documentation fixes. get_handle was not included in + the documentation. ^^; I forgot to put it in. + 2. README was updated. L::M::G::F is included in the README. Also, + the installation procedure is updated, too. I forgot to update it + last time. + +2003-04-30 version 1.01 + Improvements to Locale::Maketext::Gettext::Functions. + 1. L::M::G::F: Documentation fixes. Lots of errors sit there in the + previous documentation. It's impossible to work if you follow the + previous documentation. ^^; Ha ha... + 2. L::M::G: Map the language i-default to the locale C. + 3. L::M::G::F: Map the locale C to the language i-default. + 4. fork_and_test is added to the test suite to test without polluting + the package space. It is slow, though. ^^; + 5. L::M::G::F: Several test suites are added. + 6. L::M::G::F: Error tolerance is largely improved. + 7. L::M::G: New method subclass_init() is added. Object + initialization is moved from the new method to the subclass_init() + method, so that another subclass may inherit it further. + 8. L::M::G::F: The "experimental" warning is removed. Large amount of + errors can be handled gracefully now. + +2003-04-28 + 1. TestPkg/L10N.pm is renamed as T_L10N.pm. It is neater. A + TestPkg/ subdirectory is not necessary. + 2. Change log is fixed. I forgot to put the version number 0.07. + 3. Also, the version number should become 1.00, for the joining of + Locale::Maketext::Gettext::Functions. ^_*' + +2003-04-28 version 0.07 (1.00) + Introducing Locale::Maketext::Gettext::Functions, a functional + interface to Locale::Maketext::Gettext. + 1. The first Locale::Maketext::Gettext::Functions is out. It works! + ^_*' But it is still experimental. It cannot deal with real + world problems still. ^^; + 2. Documentation was fixed so that it is neater. + +2003-04-27 + 1. The name of the _AUTO lexicon package is shorten to + Locale::Maketext::Gettext::_AUTO. + 2. Documentation rearrange. + +2003-04-27 version 0.06 + Improvements. + 1. textdomain() works for default system locale directories now. For + domains that are not binded with bindtextdomain(), it searches the + system locale directories to find the MO file. No test suite is + available for this functionality. I cannot predict what MO files + are available in your system locale directories. ^^; Please + report bugs if it does not work. + 2. Slave package Locale::Maketext::Gettext::_AUTO::L10N is added, in + order to process the _AUTO Lexicon seperately. This saves + resources when user change the die_for_lookup_failures() setting. + Changing die_for_lookup_failures() setting won't trigger copying + and replacing your whole %Lexicon anymore. As an effect, the + cached compiled result of the _AUTO lexicon is preserved and the + compilation overhead from Locale::Maketext is greatly reduced. + 3. read_mo() is added to retire the readmo(). Use of readmo() is + deprecated. This idea is inspired by the implementation of + readmo() as "parse_mo" in Locale::Maketext::Lexicon by Autrijus. + There is far too much meta infomation to be returned other than + its encoding. It's not possible to change the API for each new + requirement. To enable sharing of the algorithm used in read_mo() + with whoever need it, it's necessary to limit its function to read + and return the raw data, leaving all the other jobs to its caller. + 4. For the same reason, caching control is removed from read_mo(), + too. read_mo() read the MO file and return the %Lexicon only. It + really reads. Nothing more. ^_*' + +2003-04-27 version 0.05 + Bug and documentation fixes. + 1. New method key_encoding was added. The _AUTO lexicon is itself + not multibyte-safe. You can specify the encoding of your keys if + you are using non-ASCII keys. This is not a solution, but a + workaround. + 2. New method encode_failure was added. The default action when + encode fails changed from FB_CROAK to FB_DEFAULT. I have changed + my mind. GNU gettext never fails. + 3. The paragraph about Locale::Maketext::Lexicon at the NOTES section + in the documentation is updated. The paragraph about msgunfmt is + removed. + 4. The README file was updated. + 5. The strange line "exists ${"$class\::Lexicon"}{$key};" is removed. + That problem seems to be solved. It is not required anymore. + +2003-04-25 version 0.04 + Documentation fixes. Upgrade is not required if you are using version + 0.03. + +2003-04-25 version 0.03 + Bug fixes. + 1. Fixed clashing of the die_for_lookup_failures() setting from + multiple instances. + 2. Change log is rewritten, to my own format. + 3. A new racing test suite is added. The old racing test suite was + renamed as t-switching. + 4. Redundant initialization of $LH->{"LOCALEDIRS"} in + bindtextdomain() is removed. + 5. An old line at die_for_lookup_failures() which initialize a wrong + $LH->{"LOCALE"} is removed. + 6. Removed 2 incorrect notice in the documentation. There will not + be infinite loops for bindtextdomain() and textdomain(), whatever + value it takes. Apparently I had made a mistake. ^^; + 7. Several typos in the comments are fixed. + 8. Sanity checks to the MO file is moved into readmo(). Cache now + has a higher precedence than the sanity checks, which conforms + with the global design. + 9. More documentation was added to the SYNOPSIS. + 10. Sanity checks for whether a method is invoked as a static method + are added. Maketext use static variables. We should not clash + them. + 11. As a result of the above, the maketext method is no more static. + It is an instance method, meaning that MyPkg::L10N::en->maketext + does not work anymore. + 12. Instance lexicon is initialized in the new method. I almost + forgot it. Thanks to the test suite. :p + +2003-04-25 version 0.02 + Class/object design fixes. + 1. I did tell the difference from class variables to instance + variables. Forgive me, I had no experience with object-oriented + programming at all, not even OO for perl. :p Just a few Java + books. Anyway, the problem with clashing class variables is + fixed. Most class variables are moved into instance variables. + 2. Solved the default output encoding problem by using the encoding + of the MO file as the default encoding. + 3. reload_text method is added to purge the MO file cache, so that + applications do not need to be restarted when their MO file + updates. + 4. MO files of different byte orders are supported now. Big-endian + MO files support is added. + 5. die_for_lookup_failures() method was added. The default behavior + changed to "never fails", as GNU gettext does. + 6. A test suite is added. + +2003-04-24 version 0.01 diff --git a/INSTALL b/INSTALL new file mode 100644 index 0000000..c64d3aa --- /dev/null +++ b/INSTALL @@ -0,0 +1,146 @@ +Locale-Maketext-Gettext Installation Guide + +* Table of Contents + + * System Requirements + * Installation Instruction + ** Install with ExtUtils::MakeMaker + ** Install with Module::Build + ** Install with the CPAN Shell + ** Install with the CPANPLUS Shell + + +* System Requirements + + 1. Perl, version 5.8.0 or above. Locale::Maketext::Gettext uses +the utf8 text internally that is only available since 5.8.0. You can +run perl -v to see your current Perl version. If you don't have +Perl, or if you have an older version of Perl, you can download and +install/upgrade it from Perl website. + +http://www.perl.com/ + + If you are using MS-Windows, you can download and install +ActiveState ActivePerl. + +http://www.activestate.com/ + + 2. Required Perl modules: None. + + 3. Optional Perl modules: None. + + +* Installation Instruction + +** Install with ExtUtils::MakeMaker + + arclog uses standard Perl installation with ExtUtils::MakeMaker. +Follow these steps: + + % perl Makefile.PL + % make + % make test + % make install + + When running make install, make sure you have the priviledge to +write to the installation location. This usually requires the root +priviledge. + + If you are using ActivePerl under MS-Windows, you should use +nmake instead of make. nmake can be obtained from the Microsoft FTP +site. + +ftp://ftp.microsoft.com/Softlib/MSLFILES/nmake15.exe + + If you want to install into another location, you can set the +PREFIX. For example, to install into your home when you are not +root: + + % perl Makefile.PL PREFIX=/home/jessica + + Refer to the docuemntation of ExtUtils::MakeMaker for more +installation options (by running perldoc ExtUtils::MakeMaker). + + +** Install with Module::Build + + You can install with Module::Build instead, if you prefer. +Follow these steps: + + % perl Build.PL + % ./Build + % ./Build test + % ./Build install + + When running ./Build install, make sure you have the priviledge to +write to the installation location. This usually requires the root +priviledge. + + If you want to install into another location, you can set the +--prefix. For example, to install into your home when you are not +root: + + % perl Build.PL --prefix=/home/jessica + + Refer to the docuemntation of Module::Build for more +installation options (by running perldoc Module::Build). + + +** Install with the CPAN Shell + + You can install with the CPAN shell, if you prefer. CPAN shell +takes care of ExtUtils::MakeMaker and Module::Build for you: + + % cpan Locale::Maketext::Gettext + + Make sure you have the priviledge to write to the installation +location. This usually requires the root priviledge. Since CPAN +shell 1.81 you can set "make_install_make_command" and +"mbuild_install_build_command" in your CPAN configuration to switch +to root just before install: + + % cpan + cpan> o conf make_install_make_command "sudo make" + cpan> o conf mbuild_install_build_command "sudo ./Build" + cpan> install Locale::Maketext::Gettext + + If you want to install into another location, you can set the +"makepl_arg" and "mbuild_arg" in your CPAN configuration. For +example, to install into your home when you are not root: + + % cpan + cpan> o conf makepl_arg "PREFIX=/home/jessica" + cpan> o conf mbuild_arg "--prefix=/home/jessica" + cpan> install Locale::Maketext::Gettext + + Refer to the docuemntation of cpan for more CPAN shell commands +(by running perldoc cpan). + + +** Install with the CPANPLUS Shell + + You can install with the CPANPLUS shell, if you prefer. CPANPLUS +shell takes care of ExtUtils::MakeMaker and Module::Build for you: + + % cpanp -i Locale::Maketext::Gettext + + Make sure you have the priviledge to write to the installation +location. This usually requires the root priviledge. + + If you want to install into another location, you can set the +"makemakerflags" and "buildflags" in your CPANPLUS configuration. +For example, to install into your home when you are not root: + + % cpanp + CPAN Terminal> s conf makemakerflags "PREFIX=/home/jessica" + CPAN Terminal> s conf buildflags "--prefix=/home/jessica" + CPAN Terminal> install Locale::Maketext::Gettext + + Refer to the docuemntation of cpanp for more CPANPLUS shell +commands (by running perldoc cpanp). + + +imacat +2008-11-11 +imacat@mail.imacat.idv.tw +http://www.imacat.idv.tw/ diff --git a/MANIFEST b/MANIFEST new file mode 100644 index 0000000..fec98ae --- /dev/null +++ b/MANIFEST @@ -0,0 +1,43 @@ +Artistic +BUGS +Build.PL +Changes +COPYING +INSTALL +lib/Locale/Maketext/Gettext.pm +lib/Locale/Maketext/Gettext/Functions.pm +Makefile.PL +MANIFEST This list of files +META.yml +README +script/maketext +SIGNATURE +t/00-signature.t +t/01-basic.t +t/02-big-endian.t +t/03-errors.t +t/04-encodings.t +t/05-switching.t +t/06-racing.t +t/07-f-basic.t +t/08-f-errors.t +t/09-f-encodings.t +t/10-f-switching.t +t/11-command-line.t +t/99-pod.t +t/locale/C/LC_MESSAGES/test.mo +t/locale/en/LC_MESSAGES/bad.mo +t/locale/en/LC_MESSAGES/test.mo +t/locale/en/LC_MESSAGES/test2.mo +t/locale/en/LC_MESSAGES/test_be.mo +t/locale/en/LC_MESSAGES/test_utf8.mo +t/locale/zh_CN/LC_MESSAGES/test.mo +t/locale/zh_CN/LC_MESSAGES/test_be.mo +t/locale/zh_CN/LC_MESSAGES/test_utf8.mo +t/locale/zh_TW/LC_MESSAGES/test.mo +t/locale/zh_TW/LC_MESSAGES/test2.mo +t/locale/zh_TW/LC_MESSAGES/test_be.mo +t/locale/zh_TW/LC_MESSAGES/test_utf8.mo +t/T_L10N.pm +THANKS +TODO diff --git a/META.yml b/META.yml new file mode 100644 index 0000000..7bf5be4 --- /dev/null +++ b/META.yml @@ -0,0 +1,21 @@ +--- #YAML:1.0 +name: Locale-Maketext-Gettext +version: 1.28 +abstract: Joins gettext and Maketext frameworks +author: + - imacat +license: perl +distribution_type: module +configure_requires: + ExtUtils::MakeMaker: 0 +build_requires: + ExtUtils::MakeMaker: 0 +requires: {} +no_index: + directory: + - t + - inc +generated_by: ExtUtils::MakeMaker version 6.52 +meta-spec: + url: http://module-build.sourceforge.net/META-spec-v1.4.html + version: 1.4 diff --git a/Makefile.PL b/Makefile.PL new file mode 100755 index 0000000..686e348 --- /dev/null +++ b/Makefile.PL @@ -0,0 +1,32 @@ +#! /usr/bin/perl -w +require 5.008; +use strict; +use ExtUtils::MakeMaker; + +# LICENSE is only availabe since ExtUtils::MakeMaker 6.30_01 +use vars qw(%license $eummver); +%license = qw(); +$eummver = $ExtUtils::MakeMaker::VERSION; +$eummver =~ s/_//; +%license = (LICENSE => "perl") if $eummver > 6.30; + +WriteMakefile( + NAME => "Locale-Maketext-Gettext", + VERSION => "1.28", + ABSTRACT => "Joins gettext and Maketext frameworks", + AUTHOR => "imacat ", + %license, + PREREQ_PM => { }, + SIGN => 1, + + EXE_FILES => [ "script/maketext" ], + dist => { + COMPRESS => "gzip -9", + SUFFIX => ".gz", + }, + clean => { + FILES => "t/test_native.po t/locale/en/LC_MESSAGES/test_native.mo", + }, +); + +__END__ diff --git a/README b/README new file mode 100644 index 0000000..b4eb9cf --- /dev/null +++ b/README @@ -0,0 +1,54 @@ +Locale::Maketext::Gettext - Joins the gettext and Maketext frameworks + +Locale::Maketext::Gettext joins the GNU gettext and Maketext +frameworks. It is a subclass of Locale::Maketext that follows the +way GNU gettext works. It works seamlessly, both in the sense of +GNU gettext and Maketext. As a result, you enjoy both their +advantages, and get rid of both their problems, too. + +You start as an usual GNU gettext localization project: Work on +PO files with the help of translators, reviewers and Emacs. Turn +them into MO files with msgfmt. Copy them into the appropriate +locale directory, such as +/usr/share/locale/de/LC_MESSAGES/myapp.mo. + +Then, build your Maketext localization class, with your base class +changed from Locale::Maketext to Locale::Maketext::Gettext. That's +all. ^_*' + + +* Locale::Maketext::Gettext::Functions + +Locale::Maketext::Gettext::Functions is a functional +interface to Locale::Maketext::Gettext (and Locale::Maketext). +It works completely the GNU gettext way. It plays magic to +Locale::Maketext. No more localization class/subclasses and language +handles are required. + + +* The maketext script + +The maketext script is a command-line interface to +Locale::Maketext::Gettext (and Locale::Maketext). It can be used in +shell scripts, etc, to translate, maketext and return the +result. It enables Maketext to be integrated into other programming +languages/systems, like bash/csh, python, PHP, C, etc. It works +like the command-line program gettext. + + +* Installation + + Read INSTALL for instructions on how to install +Locale::Maketext::Gettext. + + +* News, Changes and Updates + + Refer to the Changes for changes, bug fixes, updates, new functions, etc. + + +* Copyright + +Copyright (c) 2003-2007 imacat. All rights reserved. This program is free +software; you can redistribute it and/or modify it under the same terms +as Perl itself. diff --git a/SIGNATURE b/SIGNATURE new file mode 100644 index 0000000..4bcd23b --- /dev/null +++ b/SIGNATURE @@ -0,0 +1,65 @@ +This file contains message digests of all files listed in MANIFEST, +signed via the Module::Signature module, version 0.55. + +To verify the content in this distribution, first make sure you have +Module::Signature installed, then type: + + % cpansign -v + +It will check each file's integrity, as well as the signature's +validity. If "==> Signature verified OK! <==" is not displayed, +the distribution may already have been compromised, and you should +not run its Makefile.PL or Build.PL. + +-----BEGIN PGP SIGNED MESSAGE----- +Hash: SHA1 + +SHA1 be0627fff2e8aef3d2a14d5d7486babc8a4873ba Artistic +SHA1 f044bfe71c57b6f25c659bc0a6067aa854bc8343 BUGS +SHA1 7d6278e57e33472b167335a5435081f307f0dc70 Build.PL +SHA1 8624bcdae55baeef00cd11d5dfcfa60f68710a02 COPYING +SHA1 5863e86e8a20bbb63b3fd295520b861fa87db0c7 Changes +SHA1 e9345adfeb32db567cd1be52f3f27307943a36bc INSTALL +SHA1 c7d9c9b334b816e6f06473647cd9a054152a44c9 MANIFEST +SHA1 39589ded15e1d09a79c41391392d249de3708f4a META.yml +SHA1 ec92e11b76afaa05f67d83a6e32056505a45c99e Makefile.PL +SHA1 b699f73f5fa33123b2f4f1b210665e295900c74d README +SHA1 3db402b52e04cf5a6e60291c23aac8c16f2db810 THANKS +SHA1 85dd5ac895cc0a5b95827060999578d8ce8d41dd TODO +SHA1 f9deac7b12cc6a3cb18b7f5282f9e6a1e1a624b9 lib/Locale/Maketext/Gettext.pm +SHA1 b88b6bad1e8cb0ba825594c233c006c1b297646f lib/Locale/Maketext/Gettext/Functions.pm +SHA1 f89afd70fdcbaeeab44fada7ed3087d5c7f6f018 script/maketext +SHA1 525f414c7a0aadc61240a4b0959ca2cda7514c75 t/00-signature.t +SHA1 7bc2bf2d3ef2befd48a457fb3d90c7c7bdcc4854 t/01-basic.t +SHA1 73ce1e3ba168373318655b3f4fbca1847e49fd75 t/02-big-endian.t +SHA1 4b07656e4c35bfce793c96b59bb9ae2917be335e t/03-errors.t +SHA1 8a28c721b5eefe607c300192c8cb8e8107f419be t/04-encodings.t +SHA1 c16af7bbe1d9bd7ce8644c19f968231b94156c12 t/05-switching.t +SHA1 0212e5b50d6886601b0a4ad82d3c656975009a73 t/06-racing.t +SHA1 2ddb628b67ae35033fad4331c5323928d7dd7395 t/07-f-basic.t +SHA1 a4491ef77b899c4988b8ab34fb8f945a8d06c285 t/08-f-errors.t +SHA1 8fb7ecbce36daaf81ac9c197dab6834b25233cc5 t/09-f-encodings.t +SHA1 2c1641cc6fff792fb83826486050acab23a1db51 t/10-f-switching.t +SHA1 4afe4980e7ed4d326009b8257e01320abfb33d14 t/11-command-line.t +SHA1 b85626024d610808bd0909989dcfb9fb20a40485 t/99-pod.t +SHA1 97dad77dee7f751d09efdb6900b077d68c853d46 t/T_L10N.pm +SHA1 676b87d7a8edc735ba676d2901827eb52532b418 t/locale/C/LC_MESSAGES/test.mo +SHA1 80ca74a96bd6f83bd903ebfd021b87c9184582eb t/locale/en/LC_MESSAGES/bad.mo +SHA1 676b87d7a8edc735ba676d2901827eb52532b418 t/locale/en/LC_MESSAGES/test.mo +SHA1 fbd67646ebb39a99c1ab5aa985addd7f7ab27561 t/locale/en/LC_MESSAGES/test2.mo +SHA1 99b05048a1243bb75622a3c6e8a7c8e9f59a9151 t/locale/en/LC_MESSAGES/test_be.mo +SHA1 5d3e312b86526ebe164589b1f7fbf96f01823b0c t/locale/en/LC_MESSAGES/test_utf8.mo +SHA1 42eece65e0ceae8267000b42692212c260a49b36 t/locale/zh_CN/LC_MESSAGES/test.mo +SHA1 746c06a73e4eb641eb006c90ed938c4b98cfc2ec t/locale/zh_CN/LC_MESSAGES/test_be.mo +SHA1 8db66e4035d1f55cd27186e02c0386709c2fc3d0 t/locale/zh_CN/LC_MESSAGES/test_utf8.mo +SHA1 6aa98d6675c91555309d58a93ff59cb205cbdd7b t/locale/zh_TW/LC_MESSAGES/test.mo +SHA1 bcc89cdc393507d51b5a76e40954db72914b60d8 t/locale/zh_TW/LC_MESSAGES/test2.mo +SHA1 20f810c6229d0bd4064b27f9b3d86b61a20f0821 t/locale/zh_TW/LC_MESSAGES/test_be.mo +SHA1 eb13887b005e3c3e9fab774dfea22de5c01ad1ef t/locale/zh_TW/LC_MESSAGES/test_utf8.mo +-----BEGIN PGP SIGNATURE----- +Version: GnuPG v1.4.9 (GNU/Linux) + +iEYEARECAAYFAkpFc8MACgkQi9gubzC5S1w3kQCfY4bdMI9M6+sTT+y//sHibPDP +LUUAnRvtrXrg1Tov/anLHx0Xur3TmAQQ +=Au1F +-----END PGP SIGNATURE----- diff --git a/THANKS b/THANKS new file mode 100644 index 0000000..06f6a95 --- /dev/null +++ b/THANKS @@ -0,0 +1,24 @@ +Thanks to Sean M. Burke, for writing such a great localization framework as +Locale::Maketext, especially for his ideas about the plural forms. + +Thanks to the GNU group, for writing such a great localization framework as +GNU gettext, especially for the completeness and simplicity of its design. + +Thanks for Autrijus Tang for writing +Locale::Maketext::Lexicon. It inspires me to import %Lexicon from other +sources. Also thanks for approving such a project that is a competition to +his own Locale::Maketext::Lexicon. :p + +Thanks to Max Maischein for reporting CPAN tester failures +200029, 200332 and 200331, that helps me finding the shell character escaping +problem on my test suite. + +Thanks to Andreas Koenig for reporting CPAN tester failures +387357 and submitting rt bug 23956, informing me the base class +Locale::Maketext has updated its error handling behavior in the Perl 5.9. + +Thanks to Chris Travers for suggestion on +implementing pgettext() in GNU gettext as pmaketext(). + +Finally, well, :p thanks to Larry Wall, for writing such a +great programming language, as Perl. diff --git a/TODO b/TODO new file mode 100644 index 0000000..edd1bbd --- /dev/null +++ b/TODO @@ -0,0 +1,4 @@ +Locale-Maketext-Gettext TODO list + +- Design a way for MO file installation through ExtUtils::MakeMaker + and Module::Build. diff --git a/lib/Locale/Maketext/Gettext.pm b/lib/Locale/Maketext/Gettext.pm new file mode 100644 index 0000000..0f37a3e --- /dev/null +++ b/lib/Locale/Maketext/Gettext.pm @@ -0,0 +1,814 @@ +# Locale::Maketext::Gettext - Joins the gettext and Maketext frameworks + +# Copyright (c) 2003-2009 imacat. All rights reserved. This program is free +# software; you can redistribute it and/or modify it under the same terms +# as Perl itself. +# First written: 2003-04-23 + +package Locale::Maketext::Gettext; +use 5.008; +use strict; +use warnings; +use base qw(Locale::Maketext Exporter); +use vars qw($VERSION @ISA %Lexicon @EXPORT @EXPORT_OK); +$VERSION = 1.28; +@EXPORT = qw(read_mo); +@EXPORT_OK = @EXPORT; +# Prototype declaration +sub read_mo($); + +use Encode qw(encode decode FB_DEFAULT); +use File::Spec::Functions qw(catfile); +no strict qw(refs); + +use vars qw(%Lexicons %ENCODINGS $REREAD_MO $MOFILE); +$REREAD_MO = 0; +$MOFILE = ""; +use vars qw(@SYSTEM_LOCALEDIRS); +@SYSTEM_LOCALEDIRS = qw(/usr/share/locale /usr/lib/locale + /usr/local/share/locale /usr/local/lib/locale); + +# encoding: Set or retrieve the output encoding +sub encoding : method { + local ($_, %_); + my $self; + ($self, $_) = @_; + + # This is not a static method + return if ref($self) eq ""; + + # Set the output encoding + if (@_ > 1) { + if (defined $_) { + $self->{"ENCODING"} = $_; + } else { + delete $self->{"ENCODING"}; + } + $self->{"USERSET_ENCODING"} = $_; + } + + # Return the encoding + return exists $self->{"ENCODING"}? $self->{"ENCODING"}: undef; +} + +# key_encoding: Specify the encoding used in the keys +sub key_encoding : method { + local ($_, %_); + my $self; + ($self, $_) = @_; + + # This is not a static method + return if ref($self) eq ""; + + # Set the encoding used in the keys + if (@_ > 1) { + if (defined $_) { + $self->{"KEY_ENCODING"} = $_; + } else { + delete $self->{"KEY_ENCODING"}; + } + } + + # Return the encoding + return exists $self->{"KEY_ENCODING"}? $self->{"KEY_ENCODING"}: undef; +} + +# new: Initialize the language handler +sub new : method { + local ($_, %_); + my ($self, $class); + $class = ref($_[0]) || $_[0]; + $self = bless {}, $class; + $self->subclass_init; + $self->init; + return $self; +} + +# subclass_init: Initialize at the subclass level, so that it can be +# inherited by calling $self->SUPER:subclass_init +sub subclass_init : method { + local ($_, %_); + my ($self, $class); + $self = $_[0]; + $class = ref($self); + # Initialize the instance lexicon + $self->{"Lexicon"} = {}; + # Initialize the LOCALEDIRS registry + $self->{"LOCALEDIRS"} = {}; + # Initialize the MO timestamp + $self->{"REREAD_MO"} = $REREAD_MO; + # Initialize the DIE_FOR_LOOKUP_FAILURES setting + $self->{"DIE_FOR_LOOKUP_FAILURES"} = 0; + $self->SUPER::fail_with($self->can("failure_handler_auto")); + # Initialize the ENCODE_FAILURE setting + $self->{"ENCODE_FAILURE"} = FB_DEFAULT; + # Initialize the MOFILE value of this instance + $self->{"MOFILE"} = ""; + ${"$class\::MOFILE"} = "" if !defined ${"$class\::MOFILE"}; + # Find the locale name, for this subclass + $self->{"LOCALE"} = $class; + $self->{"LOCALE"} =~ s/^.*:://; + $self->{"LOCALE"} =~ s/(_)(.*)$/$1 . uc $2/e; + # Map i_default to C + $self->{"LOCALE"} = "C" if $self->{"LOCALE"} eq "i_default"; + # Set the category. Currently this is always LC_MESSAGES + $self->{"CATEGORY"} = "LC_MESSAGES"; + # Default key encoding is US-ASCII + $self->{"KEY_ENCODING"} = "US-ASCII"; + return; +} + +# bindtextdomain: Bind a text domain to a locale directory +sub bindtextdomain : method { + local ($_, %_); + my ($self, $DOMAIN, $LOCALEDIR); + ($self, $DOMAIN, $LOCALEDIR) = @_; + + # This is not a static method + return if ref($self) eq ""; + + # Return null for this rare case + return if !defined $LOCALEDIR + && !exists ${$self->{"LOCALEDIRS"}}{$DOMAIN}; + + # Register the DOMAIN and its LOCALEDIR + ${$self->{"LOCALEDIRS"}}{$DOMAIN} = $LOCALEDIR if defined $LOCALEDIR; + + # Return the registry + return ${$self->{"LOCALEDIRS"}}{$DOMAIN}; +} + +# textdomain: Set the current text domain +sub textdomain : method { + local ($_, %_); + my ($self, $class, $DOMAIN, $LOCALEDIR, $MOfile); + ($self, $DOMAIN) = @_; + + # This is not a static method + return if ref($self) eq ""; + # Find the class name + $class = ref($self); + + # Return the current domain + return $self->{"DOMAIN"} if !defined $DOMAIN; + + # Set the timestamp of this read in this instance + $self->{"REREAD_MO"} = $REREAD_MO; + # Set the current domain + $self->{"DOMAIN"} = $DOMAIN; + + # Clear it + $self->{"Lexicon"} = {}; + %{"$class\::Lexicon"} = qw(); + $self->{"MOFILE"} = ""; + ${"$class\::MOFILE"} = ""; + + # The format is "{LOCALEDIR}/{LOCALE}/{CATEGORY}/{DOMAIN}.mo" + # Search the system locale directories if the domain was not + # registered yet + if (!exists ${$self->{"LOCALEDIRS"}}{$DOMAIN}) { + undef $MOfile; + foreach $LOCALEDIR (@SYSTEM_LOCALEDIRS) { + $_ = catfile($LOCALEDIR, $self->{"LOCALE"}, + $self->{"CATEGORY"}, "$DOMAIN.mo"); + if (-f $_ && -r $_) { + $MOfile = $_; + last; + } + } + # Not found at last + return $DOMAIN if !defined $MOfile; + + # This domain was registered + } else { + $MOfile = catfile(${$self->{"LOCALEDIRS"}}{$DOMAIN}, + $self->{"LOCALE"}, $self->{"CATEGORY"}, "$DOMAIN.mo"); + } + + # Record it + ${"$class\::MOFILE"} = $MOfile; + $self->{"MOFILE"} = $MOfile; + + # Read the MO file + # Cached + if (!exists $ENCODINGS{$MOfile} || !exists $Lexicons{$MOfile}) { + my $enc; + # Read it + %_ = read_mo($MOfile); + + # Successfully read + if (scalar(keys %_) > 0) { + # Decode it + # Find the encoding of that MO file + if ($_{""} =~ /^Content-Type: text\/plain; charset=(.*)$/im) { + $enc = $1; + # Default to US-ASCII + } else { + $enc = "US-ASCII"; + } + # Set the current encoding to the encoding of the MO file + $_{$_} = decode($enc, $_{$_}) foreach keys %_; + } + + # Cache them + $Lexicons{$MOfile} = \%_; + $ENCODINGS{$MOfile} = $enc; + } + + # Respect the existing output encoding + if (defined $ENCODINGS{$MOfile}) { + $self->{"MO_ENCODING"} = $ENCODINGS{$MOfile}; + } else { + delete $self->{"MO_ENCODING"}; + } + # Respect the MO file encoding unless there is a user preferrence + if (!exists $self->{"USERSET_ENCODING"}) { + if (exists $self->{"MO_ENCODING"}) { + $self->{"ENCODING"} = $self->{"MO_ENCODING"}; + } else { + delete $self->{"ENCODING"}; + } + } + $self->{"Lexicon"} = $Lexicons{$MOfile}; + %{"$class\::Lexicon"} = %{$Lexicons{$MOfile}}; + $self->clear_isa_scan; + + return $DOMAIN; +} + +# maketext: Encode after maketext +sub maketext : method { + local ($_, %_); + my ($self, $key, @param, $class, $keyd); + ($self, $key, @param) = @_; + + # This is not a static method - NOW + return if ref($self) eq ""; + # Find the class name + $class = ref($self); + + # MO file should be re-read + if ($self->{"REREAD_MO"} < $REREAD_MO) { + $self->{"REREAD_MO"} = $REREAD_MO; + defined($_ = $self->textdomain) and $self->textdomain($_); + } + + # If the instance lexicon is changed. + # Maketext uses a class lexicon. We have to copy the instance + # lexicon into the class lexicon. This is slow. Mass memory + # copy sucks. Avoid create several language handles for a + # single localization subclass whenever possible. + # Maketext uses class lexicon in order to track the inheritance. + # It is hard to change it. + if (${"$class\::MOFILE"} ne $self->{"MOFILE"}) { + ${"$class\::MOFILE"} = $self->{"MOFILE"}; + %{"$class\::Lexicon"} = %{$self->{"Lexicon"}}; + } + + # Decode the source text + $keyd = $key; + $keyd = decode($self->{"KEY_ENCODING"}, $keyd, $self->{"ENCODE_FAILURE"}) + if exists $self->{"KEY_ENCODING"} && !Encode::is_utf8($key); + # Maketext + $_ = $self->SUPER::maketext($keyd, @param); + # Output to the requested encoding + if (exists $self->{"ENCODING"}) { + $_ = encode($self->{"ENCODING"}, $_, $self->{"ENCODE_FAILURE"}); + # Pass through the empty/invalid lexicon + } elsif ( scalar(keys %{$self->{"Lexicon"}}) == 0 + && exists $self->{"KEY_ENCODING"} + && !Encode::is_utf8($key)) { + $_ = encode($self->{"KEY_ENCODING"}, $_, $self->{"ENCODE_FAILURE"}); + } + + return $_; +} + +# pmaketext: Maketext with context +sub pmaketext : method { + local ($_, %_); + my ($self, $ctxt, $key, @param); + ($self, $ctxt, $key, @param) = @_; + # This is not a static method - NOW + return if ref($self) eq ""; + # This is actually a wrapper to the maketext() method + return $self->maketext("$ctxt\x04$key", @param); +} + +# read_mo: Subroutine to read and parse the MO file +# Refer to gettext documentation section 8.3 +sub read_mo($) { + local ($_, %_); + my ($MOfile, $len, $FH, $content, $tmpl); + $MOfile = $_[0]; + + # Avild being stupid + return unless -f $MOfile && -r $MOfile; + # Read the MO file + $len = (stat $MOfile)[7]; + open $FH, $MOfile or return; # GNU gettext never fails! + binmode $FH; + defined($_ = read $FH, $content, $len) + or return; + close $FH or return; + + # Find the byte order of the MO file creator + $_ = substr($content, 0, 4); + # Little endian + if ($_ eq "\xde\x12\x04\x95") { + $tmpl = "V"; + # Big endian + } elsif ($_ eq "\x95\x04\x12\xde") { + $tmpl = "N"; + # Wrong magic number. Not a valid MO file. + } else { + return; + } + + # Check the MO format revision number + $_ = unpack $tmpl, substr($content, 4, 4); + # There is only one revision now: revision 0. + return if $_ > 0; + + my ($num, $offo, $offt); + # Number of messages + $num = unpack $tmpl, substr($content, 8, 4); + # Offset to the beginning of the original messages + $offo = unpack $tmpl, substr($content, 12, 4); + # Offset to the beginning of the translated messages + $offt = unpack $tmpl, substr($content, 16, 4); + %_ = qw(); + for ($_ = 0; $_ < $num; $_++) { + my ($len, $off, $stro, $strt); + # The first word is the length of the message + $len = unpack $tmpl, substr($content, $offo+$_*8, 4); + # The second word is the offset of the message + $off = unpack $tmpl, substr($content, $offo+$_*8+4, 4); + # Original message + $stro = substr($content, $off, $len); + + # The first word is the length of the message + $len = unpack $tmpl, substr($content, $offt+$_*8, 4); + # The second word is the offset of the message + $off = unpack $tmpl, substr($content, $offt+$_*8+4, 4); + # Translated message + $strt = substr($content, $off, $len); + + # Hash it + $_{$stro} = $strt; + } + + return %_; +} + +# reload_text: Method to purge the lexicon cache +sub reload_text : method { + local ($_, %_); + + # Purge the text cache + %Lexicons = qw(); + %ENCODINGS = qw(); + $REREAD_MO = time; + + return; +} + +# fail_with: A wrapper to the fail_with() of Locale::Maketext, in order +# to record the preferred failure handler of the user, so that +# die_for_lookup_failures() knows where to return to. +sub fail_with : method { + local ($_, %_); + my $self; + ($self, $_) = @_; + + # This is not a static method + return if ref($self) eq ""; + + # Set the current setting + if (@_ > 1) { + if (defined $_) { + $self->{"USERSET_FAIL"} = $_; + $self->SUPER::fail_with($_) if $self->{"DIE_FOR_LOOKUP_FAILURES"}; + } else { + delete $self->{"USERSET_FAIL"}; + delete $self->{"fail"} if $self->{"DIE_FOR_LOOKUP_FAILURES"}; + } + } + + # Return the current setting + return exists $self->{"USERSET_FAIL"}? $self->{"USERSET_FAIL"}: undef; +} + +# die_for_lookup_failures: Whether we should die for lookup failure +# The default is no. GNU gettext never fails. +sub die_for_lookup_failures : method { + local ($_, %_); + my $self; + ($self, $_) = @_; + + # This is not a static method + return if ref($self) eq ""; + + # Set the current setting + if (@_ > 1) { + if ($_) { + $self->{"DIE_FOR_LOOKUP_FAILURES"} = 1; + if (exists $self->{"USERSET_FAIL"}) { + $self->{"fail"} = $self->{"USERSET_FAIL"}; + } else { + delete $self->{"fail"}; + } + } else { + $self->SUPER::fail_with($self->can("failure_handler_auto")); + $self->{"DIE_FOR_LOOKUP_FAILURES"} = 0; + } + } + + # Return the current setting + return exists $self->{"DIE_FOR_LOOKUP_FAILURES"}? + $self->{"DIE_FOR_LOOKUP_FAILURES"}: undef; +} + +# encode_failure: What to do if the text is out of your output encoding +# Refer to Encode on possible values of this check +sub encode_failure : method { + local ($_, %_); + my $self; + ($self, $_) = @_; + + # This is not a static method + return if ref($self) eq ""; + + # Specify the action used in the keys + $self->{"ENCODE_FAILURE"} = $_ if @_ > 1; + + # Return the encoding + return $self->{"ENCODE_FAILURE"} if exists $self->{"ENCODE_FAILURE"}; + return undef; +} + +# failure_handler_auto: Our local version of failure_handler_auto(), +# Copied and rewritten from Locale::Maketext, with bug#33938 patch applied. +# See http://rt.perl.org/rt3//Public/Bug/Display.html?id=33938 +sub failure_handler_auto : method { + local ($_, %_); + my ($self, $key, @param, $r); + ($self, $key, @param) = @_; + + # This is not a static method + return if ref($self) eq ""; + + # Remove the context + # We assume there is no one using EOF either in the context or message. + # That does not work in GNU gettext, anyway. + $key =~ s/^[^\x04]*\x04//; + + $self->{"failure_lex"} = {} if !exists $self->{"failure_lex"}; + ${$self->{"failure_lex"}}{$key} = $self->_compile($key) + if !exists ${$self->{"failure_lex"}}{$key}; + $_ = ${$self->{"failure_lex"}}{$key}; + + # A scalar result + return $$_ if ref($_) eq "SCALAR"; + return $_ unless ref($_) eq "CODE"; + # A compiled subroutine + { + local $SIG{"__DIE__"}; + $r = eval { + $_ = &$_($self, @param); + return 1; + }; + } + + # If we make it here, there was an exception thrown in the + # call to $value, and so scream: + if (!defined $r) { + $_ = $@; + # pretty up the error message + s<\s+at\s+\(eval\s+\d+\)\s+line\s+(\d+)\.?\n?> + <\n in bracket code [compiled line $1],>s; + Carp::croak "Error in maketexting \"$key\":\n$_ as used"; + return; + } + + # OK + return $_; +} + +return 1; + +__END__ + +=head1 NAME + +Locale::Maketext::Gettext - Joins the gettext and Maketext frameworks + +=head1 SYNOPSIS + +In your localization class: + + package MyPackage::L10N; + use base qw(Locale::Maketext::Gettext); + return 1; + +In your application: + + use MyPackage::L10N; + $LH = MyPackage::L10N->get_handle or die "What language?"; + $LH->bindtextdomain("mypackage", "/home/user/locale"); + $LH->textdomain("mypackage"); + $LH->maketext("Hello, world!!"); + +If you want to have more control to the detail: + + # Change the output encoding + $LH->encoding("UTF-8"); + # Stick with the Maketext behavior on lookup failures + $LH->die_for_lookup_failures(1); + # Flush the MO file cache and re-read your updated MO files + $LH->reload_text; + # Set the encoding of your maketext keys, if not in English + $LH->key_encoding("Big5"); + # Set the action when encode fails + $LH->encode_failure(Encode::FB_HTMLCREF); + +Use Locale::Maketext::Gettext to read and parse the MO file: + + use Locale::Maketext::Gettext; + %Lexicon = read_mo($MOfile); + +=head1 DESCRIPTION + +Locale::Maketext::Gettext joins the GNU gettext and Maketext +frameworks. It is a subclass of L +that follows the way GNU gettext works. It works seamlessly, I. As a result, you I + +You start as an usual GNU gettext localization project: Work on +PO files with the help of translators, reviewers and Emacs. Turn +them into MO files with F. Copy them into the appropriate +locale directory, such as +F. + +Then, build your Maketext localization class, with your base class +changed from L to +Locale::Maketext::Gettext. That is all. + +=head1 METHODS + +=over + +=item $LH->bindtextdomain(DOMAIN, LOCALEDIR) + +Register a text domain with a locale directory. Returns C +itself. If C is omitted, the registered locale directory +of C is returned. This method always success. + +=item $LH->textdomain(DOMAIN) + +Set the current text domain. Returns the C itself. If +C is omitted, the current text domain is returned. This +method always success. + +=item $text = $LH->maketext($key, @param...) + +Lookup the $key in the current lexicon and return a translated +message in the language of the user. This is the same method in +L, with a wrapper that +returns the text message Cd according to the current +C. Refer to L for +the maketext plural notation. + +=item $text = $LH->pmaketext($ctxt, $key, @param...) + +Lookup the $key in a particular context in the current lexicon and +return a translated message in the language of the user. Use +"--keyword=pmaketext:1c,2" for the xgettext utility. + +=item $LH->language_tag + +Retrieve the language tag. This is the same method in +L. It is readonly. + +=item $LH->encoding(ENCODING) + +Set or retrieve the output encoding. The default is the same +encoding as the gettext MO file. You can specify C, to return +the result in unencoded UTF-8. + +=item $LH->key_encoding(ENCODING) + +Specify the encoding used in your original text. The C +method itself is not multibyte-safe to the _AUTO lexicon. If you are +using your native non-English language as your original text and you +are having troubles like: + +Unterminated bracket group, in: + +Then, specify the C to the encoding of your original +text. Returns the current setting. + +B You should always use US-ASCII text keys. Using +non-US-ASCII keys is always discouraged and is not guaranteed to +be working. + +=item $LH->encode_failure(CHECK) + +Set the action when encode fails. This happens when the output text +is out of the scope of your output encoding. For exmaple, output +Chinese into US-ASCII. Refer to L for the +possible values of this C. The default is C, +which is a safe choice that never fails. But part of your text may +be lost, since that is what C does. Returns the current +setting. + +=item $LH->die_for_lookup_failures(SHOULD_I_DIE) + +Maketext dies for lookup failures, but GNU gettext never fails. +By default Lexicon::Maketext::Gettext follows the GNU gettext +behavior. But if you are Maketext-styled, or if you need a better +control over the failures (like me :p), set this to 1. Returns the +current setting. + +Note that lookup failure handler you registered with fail_with() only +work when die_for_lookup_failures() is enabled. if you disable +die_for_lookup_failures(), maketext() never fails and lookup failure +handler will be ignored. + +=item $LH->reload_text + +Purge the MO text cache. It purges the MO text cache from the base +class Locale::Maketext::Gettext. The next time C is +called, the MO file will be read and parse from the disk again. This +is used when your MO file is updated, but you cannot shutdown and +restart the application. For example, when you are a co-hoster on a +mod_perl-enabled Apache, or when your mod_perl-enabled Apache is too +vital to be restarted for every update of your MO file, or if you +are running a vital daemon, such as an X display server. + +=back + +=head1 FUNCTIONS + +=over + +=item %Lexicon = read_mo($MOfile); + +Read and parse the MO file. Returns the read %Lexicon. The returned +lexicon is in its original encoding. + +If you need the meta infomation of your MO file, parse the entry +C<$Lexicon{""}>. For example: + + /^Content-Type: text\/plain; charset=(.*)$/im; + $encoding = $1; + +C is exported by default, but you need to C in order to use it. It is not exported +from your localization class, but from the Locale::Maketext::Gettext +package. + +=back + +=head1 NOTES + +B do not try to put any lexicon in your language subclass. +When the C method is called, the current lexicon will be +B, but not appended. This is to accommodate the way +C works. Messages from the previous text domain should +not stay in the current text domain. + +An essential benefit of this Locale::Maketext::Gettext over the +original L is that: +I but Perl source is not. GNU gettext +is safe to Big5 characters like \xa5\x5c (Gong1). But if you follow +the current L document and +put your lexicon as a hash in the source of a localization subclass, +you have to escape bytes like \x5c, \x40, \x5b, etc., in the middle +of some natural multibyte characters. This breaks these characters +in halves. Your non-technical translators and reviewers will be +presented with unreadable mess, "Luan4Ma3". Sorry to say this, but +it is weird for a localization framework to be not multibyte-safe. +But, well, here comes Locale::Maketext::Gettext to rescue. With +Locale::Maketext::Gettext, you can sit back and relax now, leaving +all this mess to the excellent GNU gettext framework. + +The idea of Locale::Maketext::Getttext came from +L, a great +work by Autrijus. But it has several problems at that time (version +0.16). I was first trying to write a wrapper to fix it, but finally +I dropped it and decided to make a solution towards +L itself. +L should be +fine now if you obtain a version newer than 0.16. + +Locale::Maketext::Gettext also solved the problem of lack of the +ability to handle the encoding in +L. I implement this since +this is what GNU gettext does. When %Lexicon is read from MO files +by C, the encoding tagged in gettext MO files is used to +C the text into the internal encoding of Perl. Then, when +extracted by C, it is Cd by the current +C value. The C can be set at run time, so +that you can run a daemon and output to different encoding +according to the language settings of individual users, without +having to restart the application. This is an improvement to the +L, and is essential to +daemons and C applications. + +You should trust the encoding of your gettext MO file. GNU gettext +C checks the illegal characters for you when you compile your +MO file from your PO file. The encoding form your MO files are +always good. If you try to output to a wrong encoding, part of your +text may be lost, as C does. If you do not like this +C, change the failure behavior with the method +C. + +If you need the behavior of auto Traditional Chinese/Simplfied +Chinese conversion, as GNU gettext smartly does, do it yourself with +L, too. There may be a +solution for this in the future, but not now. + +If you set C to a domain that is not C to +specific a locale directory yet, it will try search system locale +directories. The current system locale directory search order is: +/usr/share/locale, /usr/lib/locale, /usr/local/share/locale, +/usr/local/lib/locale. Suggestions for this search order are +welcome. + +B Imaketext(...) is not available +anymore,> as the C method is no more static. That is a +sure result, as %Lexicon is imported from foreign sources +dynamically, but not statically hardcoded in Perl sources. But the +documentation of L does not +say that you can use it as a static method anyway. Maybe you were +practicing this before. You had better check your existing code for +this. If you try to invoke it statically, it returns C. + +C and C in GNU gettext are not implemented. +It is not possible to temporarily change the current text domain in +the current design of Locale::Maketext::Gettext. Besides, it is +meaningless. Locale::Maketext is object-oriented. You can always +raise a new language handle for another text domain. This is +different from the situation of GNU gettext. Also, the category +is always C. Of course it is. We are gettext and +Maketext. + +Avoid creating different language handles with different +textdomain on the same localization subclass. This currently +works, but it violates the basic design of +L. In +L, %Lexicon is saved as a +class variable, in order for the lexicon inheritance system to work. +So, multiple language handles to a same localization subclass shares +a same lexicon space. Their lexicon space clash. I tried to avoid +this problem by saving a copy of the current lexicon as an instance +variable, and replacing the class lexicon with the current instance +lexicon whenever it is changed by another language handle instance. +But this involves large scaled memory copy, which affects the +proformance seriously. This is discouraged. You are adviced to use +a single textdomain for a single localization class. + +The C is a workaround, not a solution. There is no +solution to this problem yet. You should avoid using non-English +language as your original text. You will get yourself into trouble +if you mix several original text encodings, for example, joining +several pieces of code from programmers all around the world, with +their messages written in their own language and encodings. Solution +suggestions are welcome. + +C in GNU gettext is implemented as C, in order +to look up the text message translation in a particular context. +Thanks to the suggestion from Chris Travers. + +=head1 BUGS + +GNU gettext never fails. I tries to achieve it as long as possible. +The only reason that maketext may die unexpectedly now is +"Unterminated bracket group". I cannot get a better solution to it +currently. Suggestions are welcome. + +You are welcome to fix my English. I have done my best to this +documentation, but I am not a native English speaker after all. ^^; + +=head1 SEE ALSO + +L, +L, +L, +L, L, +L. Also, please refer to the official GNU +gettext manual at L. + +=head1 AUTHOR + +imacat + +=head1 COPYRIGHT + +Copyright (c) 2003-2008 imacat. All rights reserved. This program is free +software; you can redistribute it and/or modify it under the same terms +as Perl itself. + +=cut diff --git a/lib/Locale/Maketext/Gettext/Functions.pm b/lib/Locale/Maketext/Gettext/Functions.pm new file mode 100644 index 0000000..658b6c8 --- /dev/null +++ b/lib/Locale/Maketext/Gettext/Functions.pm @@ -0,0 +1,781 @@ +# Locale::Maketext::Gettext::Functions - Functional interface to Locale::Maketext::Gettext + +# Copyright (c) 2003-2008 imacat. All rights reserved. This program is free +# software; you can redistribute it and/or modify it under the same terms +# as Perl itself. +# First written: 2003-04-28 + +package Locale::Maketext::Gettext::Functions; +use 5.008; +use strict; +use warnings; +use base qw(Exporter); +use vars qw($VERSION @EXPORT @EXPORT_OK); +$VERSION = 0.13; +@EXPORT = qw(); +push @EXPORT, qw(bindtextdomain textdomain get_handle maketext __ N_); +push @EXPORT, qw(dmaketext pmaketext dpmaketext); +push @EXPORT, qw(reload_text read_mo encoding key_encoding encode_failure); +push @EXPORT, qw(die_for_lookup_failures); +@EXPORT_OK = @EXPORT; +# Prototype declaration +sub bindtextdomain($;$); +sub textdomain(;$); +sub get_handle(@); +sub maketext(@); +sub __(@); +sub N_(@); +sub dmaketext($$@); +sub pmaketext($$@); +sub dpmaketext($$$@); +sub reload_text(); +sub encoding(;$); +sub key_encoding(;$); +sub encode_failure(;$); +sub die_for_lookup_failures(;$); +sub _declare_class($); +sub _catclass(@); +sub _init_textdomain($); +sub _get_langs($$); +sub _get_handle(); +sub _get_empty_handle(); +sub _reset(); +sub _new_rid(); +sub _k($); +sub _lang($); + +use Encode qw(encode decode from_to FB_DEFAULT); +use File::Spec::Functions qw(catdir catfile); +use Locale::Maketext::Gettext qw(read_mo); +use vars qw(%LOCALEDIRS %RIDS %CLASSES %LANGS); +use vars qw(%LHS $_EMPTY $LH $DOMAIN $CATEGORY $CLASSBASE @LANGS %PARAMS); +use vars qw(@SYSTEM_LOCALEDIRS); +%LHS = qw(); +# The category is always LC_MESSAGES +$CATEGORY = "LC_MESSAGES"; +$CLASSBASE = "Locale::Maketext::Gettext::_runtime"; +# Current language parameters +@LANGS = qw(); +@SYSTEM_LOCALEDIRS = @Locale::Maketext::Gettext::SYSTEM_LOCALEDIRS; +%PARAMS = qw(); +$PARAMS{"KEY_ENCODING"} = "US-ASCII"; +$PARAMS{"ENCODE_FAILURE"} = FB_DEFAULT; +$PARAMS{"DIE_FOR_LOOKUP_FAILURES"} = 0; +# Parameters for random class IDs +use vars qw($RID_LEN @RID_CHARS); +$RID_LEN = 8; +@RID_CHARS = split //, + "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + +# bindtextdomain: Bind a text domain to a locale directory +sub bindtextdomain($;$) { + local ($_, %_); + my ($domain, $LOCALEDIR); + ($domain, $LOCALEDIR) = @_; + # Return the current registry + return (exists $LOCALEDIRS{$domain}? $LOCALEDIRS{$domain}: undef) + if !defined $LOCALEDIR; + # Register the locale directory + $LOCALEDIRS{$domain} = $LOCALEDIR; + # Reinitialize the text domain + _init_textdomain($domain); + # Reset the current language handle + _get_handle() if defined $DOMAIN && $domain eq $DOMAIN; + # Return the locale directory + return $LOCALEDIR; +} + +# textdomain: Set the current text domain +sub textdomain(;$) { + local ($_, %_); + my ($new_domain); + $new_domain = $_[0]; + # Return the current text domain + return $DOMAIN if !defined $new_domain; + # Set the current text domain + $DOMAIN = $new_domain; + # Reinitialize the text domain + _init_textdomain($DOMAIN); + # Reset the current language handle + _get_handle(); + return $DOMAIN; +} + +# get_handle: Get a language handle +sub get_handle(@) { + local ($_, %_); + # Register the current get_handle arguments + @LANGS = @_; + # Reset and return the current language handle + return _get_handle(); +} + +# maketext: Maketext, in its long name +# Use @ instead of $@ in prototype, so that we can pass @_ to it. +sub maketext(@) { + return __($_[0], @_[1..$#_]); +} + +# __: Maketext, in its shortcut name +# Use @ instead of $@ in prototype, so that we can pass @_ to it. +sub __(@) { + local ($_, %_); + my ($key, @param, $keyd); + ($key, @param) = @_; + # Reset the current language handle if it is not set yet + _get_handle() if !defined $LH; + + # Decode the source text + $keyd = $key; + $keyd = decode($PARAMS{"KEY_ENCODING"}, $keyd, $PARAMS{"ENCODE_FAILURE"}) + if exists $PARAMS{"KEY_ENCODING"} && !Encode::is_utf8($key); + # Maketext + $_ = $LH->maketext($keyd, @param); + # Output to the requested encoding + if (exists $PARAMS{"ENCODING"}) { + $_ = encode($PARAMS{"ENCODING"}, $_, $PARAMS{"ENCODE_FAILURE"}); + # Pass through the empty/invalid lexicon + } elsif ( scalar(keys %{$LH->{"Lexicon"}}) == 0 + && exists $PARAMS{"KEY_ENCODING"} + && !Encode::is_utf8($key)) { + $_ = encode($PARAMS{"KEY_ENCODING"}, $_, $PARAMS{"ENCODE_FAILURE"}); + } + + return $_; +} + +# N_: Return the original text untouched, so that it can be catched +# with xgettext +# Use @ instead of $@ in prototype, so that we can pass @_ to it. +sub N_(@) { + # Watch out for this Perl magic! :p + return $_[0] unless wantarray; + return @_; +} + +# dmaketext: Maketext in another text domain temporarily, +# an equivalent to dgettext(). +sub dmaketext($$@) { + local ($_, %_); + my ($domain, $key, @param, $lh0, $domain0, $text); + ($domain, $key, @param) = @_; + # Preserve the current status + ($lh0, $domain0) = ($LH, $DOMAIN); + # Reinitialize the text domain + textdomain($domain); + # Maketext + $text = maketext($key, @param); + # Return the current status + ($LH, $DOMAIN) = ($lh0, $domain0); + # Return the "made text" + return $text; +} + +# pmaketext: Maketext with context, +# an equivalent to pgettext(). +sub pmaketext($$@) { + local ($_, %_); + my ($ctxt, $key, @param); + ($ctxt, $key, @param) = @_; + # This is actually a wrapper to the maketext() function + return maketext("$ctxt\x04$key", @param); +} + +# dpmaketext: Maketext with context in another text domain temporarily, +# an equivalent to dpgettext(). +sub dpmaketext($$$@) { + local ($_, %_); + my ($domain, $ctxt, $key, @param); + ($domain, $ctxt, $key, @param) = @_; + # This is actually a wrapper to the dmaketext() function + return dmaketext($domain, "$ctxt\x04$key", @param); +} + +# reload_text: Purge the lexicon cache +sub reload_text() { + # reload_text is static. + Locale::Maketext::Gettext->reload_text; +} + +# encoding: Set the output encoding +sub encoding(;$) { + local ($_, %_); + $_ = $_[0]; + + # Set the output encoding + if (@_ > 0) { + if (defined $_) { + $PARAMS{"ENCODING"} = $_; + } else { + delete $PARAMS{"ENCODING"}; + } + $PARAMS{"USERSET_ENCODING"} = $_; + } + + # Return the encoding + return exists $PARAMS{"ENCODING"}? $PARAMS{"ENCODING"}: undef; +} + +# key_encoding: Set the encoding of the original text +sub key_encoding(;$) { + local ($_, %_); + $_ = $_[0]; + + # Set the encoding used in the keys + if (@_ > 0) { + if (defined $_) { + $PARAMS{"KEY_ENCODING"} = $_; + } else { + delete $PARAMS{"KEY_ENCODING"}; + } + } + + # Return the encoding + return exists $PARAMS{"KEY_ENCODING"}? $PARAMS{"KEY_ENCODING"}: undef; +} + +# encode_failure: What to do if the text is out of your output encoding +# Refer to Encode on possible values of this check +sub encode_failure(;$) { + local ($_, %_); + $_ = $_[0]; + # Set and return the current setting + $PARAMS{"ENCODE_FAILURE"} = $_ if @_ > 0; + # Return the current setting + return $PARAMS{"ENCODE_FAILURE"}; +} + +# die_for_lookup_failures: Whether we should die for lookup failure +# The default is no. GNU gettext never fails. +sub die_for_lookup_failures(;$) { + local ($_, %_); + $_ = $_[0]; + # Set the current setting + if (@_ > 0) { + $PARAMS{"DIE_FOR_LOOKUP_FAILURES"} = $_? 1: 0; + $LH->die_for_lookup_failures($PARAMS{"DIE_FOR_LOOKUP_FAILURES"}); + } + # Return the current setting + # Resetting the current language handle is not required + # Lookup failures are handled by the fail handler directly + return $PARAMS{"DIE_FOR_LOOKUP_FAILURES"}; +} + +# _declare_class: Declare a class +sub _declare_class($) { + local ($_, %_); + $_ = $_[0]; + eval << "EOT"; +package $_[0]; +use base qw(Locale::Maketext::Gettext); +use vars qw(\@ISA %Lexicon); +EOT +} + +# _catclass: Catenate the class name +sub _catclass(@) { + return join("::", @_);; +} + +# _init_textdomain: Initialize a text domain +sub _init_textdomain($) { + local ($_, %_); + my ($domain, $k, @langs, $langs); + $domain = $_[0]; + + # Return if text domain not specified yet + return if !defined $domain; + + # Obtain the available locales + # A binded domain + if (exists $LOCALEDIRS{$domain}) { + @langs = _get_langs($LOCALEDIRS{$domain}, $domain); + # Not binded + } else { + @langs = qw(); + # Search the system locale directories + foreach (@SYSTEM_LOCALEDIRS) { + @langs = _get_langs($_, $domain); + # Domain not found in this directory + next if @langs == 0; + $LOCALEDIRS{$domain} = $_; + last; + } + # Not found at last + return if !exists $LOCALEDIRS{$domain}; + } + $langs = join ",", sort @langs; + + # Obtain the registry key + $k = _k($domain); + + # Available language list remains for this domain + return if exists $LANGS{$k} && $LANGS{$k} eq $langs; + # Register this new language list + $LANGS{$k} = $langs; + + my ($rid, $class); + # Garbage collection - drop abandoned language handles + if (exists $CLASSES{$k}) { + delete $LHS{$_} foreach grep /^$CLASSES{$k}/, keys %LHS; + } + # Get a new class ID + $rid = _new_rid(); + # Obtain the class name + $class = _catclass($CLASSBASE, $rid); + # Register the domain with this class + $CLASSES{$k} = $class; + # Declare this class + _declare_class($class); + # Declare its language subclasses + _declare_class(_catclass($class, $_)) + foreach @langs; + + return; +} + +# _get_langs: Search a locale directory and return the available languages +sub _get_langs($$) { + local ($_, %_); + my ($dir, $domain, $DH, $entry, $MOfile); + ($dir, $domain) = @_; + + @_ = qw(); + { + opendir $DH, $dir or last; + while (defined($entry = readdir $DH)) { + # Skip hidden entries + next if $entry =~ /^\./; + # Skip non-directories + next unless -d catdir($dir, $entry); + # Skip locales with dot "." (trailing encoding) + next if $entry =~ /\./; + # Get the MO file name + $MOfile = catfile($dir, $entry, $CATEGORY, "$domain.mo"); + # Skip if MO file is not available for this locale + next if ! -f $MOfile && ! -r $MOfile; + # Map C to i_default + $entry = "i_default" if $entry eq "C"; + # Add this language + push @_, lc $entry; + } + close $DH or last; + } + return @_; +} + +# _get_handle: Set the language handle with the current DOMAIN and @LANGS +sub _get_handle() { + local ($_, %_); + my ($k, $class, $subclass); + + # Lexicon empty if text domain not specified, or not binded yet + return _get_empty_handle if !defined $DOMAIN || !exists $LOCALEDIRS{$DOMAIN}; + # Obtain the registry key + $k = _k($DOMAIN); + # Lexicon empty if text domain was not properly set yet + return _get_empty_handle if !exists $CLASSES{$k}; + + # Get the localization class name + $class = $CLASSES{$k}; + # Get the language handle + $LH = $class->get_handle(@LANGS); + # Lexicon empty if failed get_handle() + return _get_empty_handle if !defined $LH; + + # Obtain the subclass name of the got language handle + $subclass = ref($LH); + # Use the existing language handle whenever possible, to reduce + # the initialization overhead + if (exists $LHS{$subclass}) { + $LH = $LHS{$subclass}; + if (!exists $PARAMS{"USERSET_ENCODING"}) { + if (exists $LH->{"MO_ENCODING"}) { + $PARAMS{"ENCODING"} = $LH->{"MO_ENCODING"}; + } else { + delete $PARAMS{"ENCODING"}; + } + } + return _lang($LH) + } + + # Initialize it + $LH->bindtextdomain($DOMAIN, $LOCALEDIRS{$DOMAIN}); + $LH->textdomain($DOMAIN); + # Respect the MO file encoding unless there is a user preferrence + if (!exists $PARAMS{"USERSET_ENCODING"}) { + if (exists $LH->{"MO_ENCODING"}) { + $PARAMS{"ENCODING"} = $LH->{"MO_ENCODING"}; + } else { + delete $PARAMS{"ENCODING"}; + } + } + # We handle the encoding() and key_encoding() ourselves. + $LH->key_encoding(undef); + $LH->encoding(undef); + # Register it + $LHS{$subclass} = $LH; + + return _lang($LH); +} + +# _get_empty_handle: Obtain the empty language handle +sub _get_empty_handle() { + local ($_, %_); + if (!defined $_EMPTY) { + $_EMPTY = Locale::Maketext::Gettext::Functions::_EMPTY->get_handle; + $_EMPTY->key_encoding(undef); + $_EMPTY->encoding(undef); + } + $LH = $_EMPTY; + $LH->die_for_lookup_failures($PARAMS{"DIE_FOR_LOOKUP_FAILURES"}); + return _lang($LH); +} + +# _reset: Initialize everything +sub _reset() { + local ($_, %_); + + %LOCALEDIRS = qw(); + undef $LH; + undef $DOMAIN; + @LANGS = qw(); + %PARAMS = qw(); + $PARAMS{"KEY_ENCODING"} = "US-ASCII"; + $PARAMS{"ENCODE_FAILURE"} = FB_DEFAULT; + $PARAMS{"DIE_FOR_LOOKUP_FAILURES"} = 0; + + return; +} + +# _new_rid: Generate a new random ID +sub _new_rid() { + local ($_, %_); + my ($id); + + do { + for ($id = "", $_ = 0; $_ < $RID_LEN; $_++) { + $id .= $RID_CHARS[int rand scalar @RID_CHARS]; + } + } while exists $RIDS{$id}; + $RIDS{$id} = 1; + + return $id; +} + +# _k: Build the key for the domain registry +sub _k($) { + return join "\n", $LOCALEDIRS{$_[0]}, $CATEGORY, $_[0]; +} + +# _lang: The langage from a language handle. language_tag is not quite sane. +sub _lang($) { + local ($_, %_); + $_ = $_[0]; + $_ = ref($_); + s/^.+:://; + s/_/-/g; + return $_; +} + +# Public empty lexicon +package Locale::Maketext::Gettext::Functions::_EMPTY; +use 5.008; +use strict; +use warnings; +use base qw(Locale::Maketext::Gettext); +use vars qw($VERSION @ISA %Lexicon); +$VERSION = 0.01; + +package Locale::Maketext::Gettext::Functions::_EMPTY::i_default; +use 5.008; +use strict; +use warnings; +use base qw(Locale::Maketext::Gettext); +use vars qw($VERSION @ISA %Lexicon); +$VERSION = 0.01; + +return 1; + +__END__ + +=head1 NAME + +Locale::Maketext::Gettext::Functions - Functional interface to Locale::Maketext::Gettext + +=head1 SYNOPSIS + + use Locale::Maketext::Gettext::Functions; + bindtextdomain(DOMAIN, LOCALEDIR); + textdomain(DOMAIN); + get_handle("de"); + print __("Hello, world!\n"); + +=head1 DESCRIPTION + +Locale::Maketext::Gettext::Functions is a functional +interface to +L (and +L). It works exactly the GNU +gettext way. It plays magic to +L for you. No more +localization class/subclasses and language handles are required at +all. + +The C, C, C and C +functions attempt to translate a text message into the native +language of the user, by looking up the translation in an MO lexicon +file. + +=head1 FUNCTIONS + +=over + +=item bindtextdomain(DOMAIN, LOCALEDIR) + +Register a text domain with a locale directory. Returns C +itself. If C is omitted, the registered locale directory +of C is returned. This method always success. + +=item textdomain(DOMAIN) + +Set the current text domain. Returns the C itself. if +C is omitted, the current text domain is returned. This +method always success. + +=item get_handle(@languages) + +Set the language of the user. It searches for an available language +in the provided @languages list. If @languages was not provided, it +looks checks environment variable LANG, and HTTP_ACCEPT_LANGUAGE +when running as CGI. Refer to +L for the magic of the +C. + +=item $message = maketext($key, @param...) + +Attempts to translate a text message into the native language of the +user, by looking up the translation in an MO lexicon file. Refer to +L for the C plural +grammer. + +=item $message = __($key, @param...) + +A synonym to C. This is a shortcut to C so +that it is cleaner when you employ maketext to your existing project. + +=item ($key, @param...) = N_($key, @param...) + +Returns the original text untouched. This is to enable the text be +catched with xgettext. + +=item $message = dmaketext($domain, $key, @param...) + +Temporarily switch to another text domain and attempts to translate +a text message into the native language of the user in that text +domain. Use "--keyword=dmaketext:2" for the xgettext utility. + +=item $message = pmaketext($ctxt, $key, @param...) + +Attempts to translate a text message in a particular context into the +native language of the user. Use "--keyword=pmaketext:1c,2" for +the xgettext utility. + +=item $message = dpmaketext($domain, $ctxt, $key, @param...) + +Temporarily switch to another text domain and attempts to translate +a text message in a particular context into the native language of +the user in that text domain. Use "--keyword=dpmaketext:2c,3" for +the xgettext utility. + +=item encoding(ENCODING) + +Set or retrieve the output encoding. The default is the same +encoding as the gettext MO file. You can specify C, to return +the result in unencoded UTF-8. + +=item key_encoding(ENCODING) + +Specify the encoding used in your original text. The C +method itself is not multibyte-safe to the _AUTO lexicon. If you are +using your native non-English language as your original text and you +are having troubles like: + +Unterminated bracket group, in: + +Then, specify the C to the encoding of your original +text. Returns the current setting. + +B You should always use US-ASCII text keys. Using +non-US-ASCII keys is always discouraged and is not guaranteed to +be working. + +=item encode_failure(CHECK) + +Set the action when encode fails. This happens when the output text +is out of the scope of your output encoding. For exmaple, output +Chinese into US-ASCII. Refer to L for the +possible values of this C. The default is C, +which is a safe choice that never fails. But part of your text may +be lost, since that is what C does. Returns the current +setting. + +=item die_for_lookup_failures(SHOULD_I_DIE) + +Maketext dies for lookup failures, but GNU gettext never fails. +By default Lexicon::Maketext::Gettext follows the GNU gettext +behavior. But if you are Maketext-styled, or if you need a better +control over the failures (like me :p), set this to 1. Returns the +current setting. + +=item reload_text() + +Purges the MO text cache. By default MO files are cached after they +are read and parsed from the disk, to reduce I/O and parsing overhead +on busy sites. reload_text() purges this cache, so that updated MO +files can take effect at run-time. This is used when your MO file is +updated, but you cannot shutdown and restart the application. for +example, when you are a co-hoster on a mod_perl-enabled Apache, or +when your mod_perl-enabled Apache is too vital to be restarted for +every update of your MO file, or if you are running a vital daemon, +such as an X display server. + +=item %Lexicon = read_mo($MOfile) + +Read and parse the MO file. Returns the read %Lexicon. The returned +lexicon is in its original encoding. + +If you need the meta infomation of your MO file, parse the entry +C<$Lexicon{""}>. For example: + + /^Content-Type: text\/plain; charset=(.*)$/im; + $encoding = $1; + +=back + +=head1 NOTES + +B Since localization classes are generated at run-time, it is +not possible to override the Maketext language functions, like +C or C. If that is your concern, use +L instead. +Suggestions are welcome. + +You can now add/remove languages/MO files at run-time. This is a +major improvement over the original +L (and +L). This is done by +registering localization classes with random IDs, so that the same +text domain can be re-declared infinitely, whenever needed (language +list changes, LOCALEDIR changes, etc.) This is not possible to the +object-interface of +L (and +L). + +Language addition/removal takes effect only after C +or C is called. It has no effect on C calls. +This keeps a basic sanity in the lifetime of a running script. + +If you set C to a domain that is not C to +specific a locale directory yet, it will try search system locale +directories. The current system locale directory search order is: +/usr/share/locale, /usr/lib/locale, /usr/local/share/locale, +/usr/local/lib/locale. Suggestions are welcome. + +=head1 STORY + +The idea is that: I finally realized that, no matter how hard I try, +I.> A common wrapper +like: + + sub __ { return $LH->maketext(@_) }; + +always fails if $LH is not initialized yet. For this reason, +C can hardly be employed in error handlers to output +graceful error messages in the natural language of the user. So, +I have to write something like this: + + sub __ { + $LH = MyPkg::L10N->get_handle if !defined $LH; + return $LH->maketext(@_); + } + +But what if C itself fails? So, this becomes: + + sub __ { + $LH = MyPkg::L10N->get_handle if !defined $LH; + $LH = _AUTO->get_handle if !defined $LH; + return $LH->maketext(@_); + } + package _AUTO; + use base qw(Locale::Maketext); + package _AUTO::i_default; + use base qw(Locale::Maketext); + %Lexicon = ( "_AUTO" => 1 ); + +Ya, this works. But, if I always have to do this in my every +application, why should I not make a solution to the localization +framework itself? This is a common problem to every localization +projects. It should be solved at the localization framework level, +but not at the application level. + +Another reason is that: I without the knowledge of object-oriented programming.> +A localization framework should be neat and simple. It should lower +down its barrier, be friendly to the beginners, in order to +encourage the use of localization and globalization. Apparently +the current practice of L +does not satisfy this request. + +The third reason is: Since +L imports +the lexicon from foreign sources, the class source file is left +empty. It exists only to help the C method looking for +a proper language handle. Then, why not make it disappear, and be +generated whenever needed? Why bother the programmers to put +an empty class source file there? + +How neat can we be? + +imacat, 2003-04-29 + +=head1 BUGS + +Since maketext localization classes are generated at run time, +Maketext language function override, like C or C, is +not available here. Suggestions are welcome. + +C, C, C and +C are not mod_perl-safe. These settings +affect the whole process, including the following scripts it is +going to run. This is the same as C in +L. Always set them at the very beginning of your +script if you are running under mod_perl. If you do not like it, +use the object-oriented +L instead. +Suggestions are welcome. + +Smart translation between Traditional Chinese/Simplified Chinese, +like what GNU gettext does, is not available yet. Suggestions are +welcome. + +=head1 SEE ALSO + +L, +L, +L, +L, L. +Also, please refer to the official GNU gettext manual at +L. + +=head1 AUTHOR + +imacat + +=head1 COPYRIGHT + +Copyright (c) 2003-2008 imacat. All rights reserved. This program is free +software; you can redistribute it and/or modify it under the same terms +as Perl itself. + +=cut diff --git a/script/maketext b/script/maketext new file mode 100755 index 0000000..5015898 --- /dev/null +++ b/script/maketext @@ -0,0 +1,215 @@ +#! /usr/bin/perl -w +# Command-line interface to Locale::Maketext::Gettext (and Locale::Maketext) + +# Copyright (c) 2003-2007 imacat. All rights reserved. This program is free +# software; you can redistribute it and/or modify it under the same terms +# as Perl itself. +# First written: 2003-05-03 + +use 5.008; +use strict; +use warnings; +use Getopt::Long qw(GetOptions); +use Locale::Maketext::Gettext::Functions; +use vars qw($VERSION); +$VERSION = 0.05; +# Prototype declaration +sub main(); +sub parse_args(); + +use vars qw($THIS_FILE $SHORTHELP $VERSTR $SEARCH $HELP); +$THIS_FILE = $0; +$THIS_FILE =~ s/^.*\///; +$SHORTHELP = "Try `$THIS_FILE --help' for more information."; +$VERSTR = "$THIS_FILE v$VERSION by imacat "; +$SEARCH = join " ", @Locale::Maketext::Gettext::Functions::SYSTEM_LOCALEDIRS; +$HELP = << "EOT"; +Usage: maketext [OPTION] [--domain=TEXTDOMAIN] MSGKEY [PARAM...] +or: maketext [OPTION] -s MSGID [PARAM...] + +Maketext and display native language translation of a textual message. + + -d, --domain=TEXTDOMAIN retrieve translated messages from TEXTDOMAIN + -h, --help display this help and exit + -V, --version display version information and exit + MSGKEY [PARAM...] retrieve translated message corresponding + to MSGKEY from TEXTDOMAIN + +If the TEXTDOMAIN parameter is not given, the domain is determined from the +environment variable TEXTDOMAIN. If the message catalog is not found in the +regular directory, another location can be specified with the environment +variable TEXTDOMAINDIR. +When used with the -s option the program adds a new line to the end of the +output so that it behaves like the `echo' or the `gettext' command. +Standard search directories: $SEARCH + +Report bugs to . +EOT + +use vars qw($DOMAIN $LOCALEDIR $ECHO $KEY @PARAM); +$ECHO = 0; + +# Main program +main(); +exit 0; + +# main: Main program +sub main() { + local ($_, %_); + + # Parse the arguments + parse_args(); + + bindtextdomain($DOMAIN, $LOCALEDIR) + if defined $DOMAIN && defined $LOCALEDIR; + textdomain($DOMAIN) if defined $DOMAIN; + print maketext($KEY, @PARAM); + print "\n" if $ECHO; + + return; +} + +# parse_args: Parse the arguments +sub parse_args() { + local ($_, %_); + + # Get the arguments 取得參數 + $_ = eval { + local $SIG{__WARN__} = sub { die $_[0]; }; + Getopt::Long::Configure("no_auto_abbrev"); + GetOptions( "domain|d=s"=>\$DOMAIN, + "s"=>sub { $ECHO = 1; }, + "help|h"=>sub { print $HELP; exit 0; }, + "version|V"=>sub { print "$VERSTR\n"; exit 0; }); + return 1; + }; + die "$THIS_FILE: $@" if !defined $_; + + # The MSGKEY + die "$THIS_FILE: missing arguments\n" if @ARGV == 0; + $KEY = shift @ARGV; + @PARAM = @ARGV; + + # Set the locale directory + $LOCALEDIR = $ENV{"TEXTDOMAINDIR"} if exists $ENV{"TEXTDOMAINDIR"}; + # Set the text domain + $DOMAIN = $ENV{"TEXTDOMAIN"} + if !defined $DOMAIN && exists $ENV{"TEXTDOMAIN"}; + + return; +} + +__END__ + +=head1 NAME + +maketext - translate and make messages + +=head1 SYNOPSIS + + maketext [OPTION] [--domain=TEXTDOMAIN] MSGKEY [PARAM...] + maketext [OPTION] -s MSGID [PARAM...] + +=head1 DESCRIPTION + +The C script translates a natural language message into +the user's language, by looking up the translation in a message MO +file, and process the plural transformation with Maketext. + +The C script is a command-line interface to +L (and +L). It can be used in shell +scripts, etc, to translate, maketext and return the result. By this +way, it enables Maketext to be integrated into other programming +languages/systems, like bash/csh, python, PHP, C, etc. It works +like the command-line program gettext. + +For example: + + % maketext -s "[*,_1,virus was,viruses were] found in [*,_2,file,files]." 0 1 + 0 viruses were found in 1 file. + % maketext -s "[*,_1,virus was,viruses were] found in [*,_2,file,files]." 1 3 + 1 virus was found in 3 files. + % + +=head1 OPTIONS + +=over + +=item -d,--domain=TEXTDOMAIN + +Retrieve translated messages from TEXTDOMAIN. + +=item -s + +Adds a new line to the end of the output so that it behaves like the +`echo' or the `gettext' command. + +=item -h,--help + +Display the help messages. + +=item -V,--version + +Display version information and exit. + +=item MSGKEY + +The original text used to look up translated text. + +=item PARAM... + +Parameters to Maketext for the plural and other text functions. + +=back + +=head1 ENVIRONMENT + +=over + +=item TEXTDOMAIN + +TEXTDOMAIN is used to determine the text domain when the -d +parameter is not given. + +=item TEXTDOMAINDIR + +TEXTDOMAINDIR is used to search the message catelog/MO file if it +does not reside in the system locale directories. + +=back + +=head1 NOTES + +Maketext language function override, like C or C, is +not available here. Suggestions are welcome. + +The current system locale directory search order is: +/usr/share/locale, /usr/lib/locale, /usr/local/share/locale, +/usr/local/lib/locale. Suggestions are welcome. + +=head1 BUGS + +Report bugs to imacat + +=head1 SEE ALSO + +L, +L, +L, +L, +L, L. +Also, please refer to the official GNU gettext manual at +L. + +=head1 AUTHOR + +imacat + +=head1 COPYRIGHT + +Copyright (c) 2003-2007 imacat. All rights reserved. This program is free +software; you can redistribute it and/or modify it under the same terms +as Perl itself. + +=cut diff --git a/t/00-signature.t b/t/00-signature.t new file mode 100755 index 0000000..1cf917c --- /dev/null +++ b/t/00-signature.t @@ -0,0 +1,22 @@ +#!/usr/bin/perl +use strict; +print "1..1\n"; + +if (!-s 'SIGNATURE') { + print "ok 1 # skip No signature file found\n"; +} +elsif (!eval { require Module::Signature; 1 }) { + print "ok 1 # skip ", + "Next time around, consider install Module::Signature, ", + "so you can verify the integrity of this distribution.\n"; +} +elsif (!eval { require Socket; Socket::inet_aton('pgp.mit.edu') }) { + print "ok 1 # skip Cannot connect to the keyserver\n"; +} +else { + (Module::Signature::verify() == Module::Signature::SIGNATURE_OK()) + or print "not "; + print "ok 1 # Valid signature\n"; +} + +__END__ diff --git a/t/01-basic.t b/t/01-basic.t new file mode 100755 index 0000000..c63a4e6 --- /dev/null +++ b/t/01-basic.t @@ -0,0 +1,150 @@ +#! /usr/bin/perl -w +# Basic test suite +# Copyright (c) 2003-2008 imacat. All rights reserved. This program is free +# software; you can redistribute it and/or modify it under the same terms +# as Perl itself. + +use 5.008; +use strict; +use warnings; +use Test; + +BEGIN { plan tests => 22 } + +use FindBin; +use File::Spec::Functions qw(catdir catfile); +use lib $FindBin::Bin; +use vars qw($LOCALEDIR $r); +$LOCALEDIR = catdir($FindBin::Bin, "locale"); + +# Basic test suite +use Encode qw(decode); +use vars qw($META $n $k1 $k2 $s1 $s2); + +# bindtextdomain +$r = eval { + require T_L10N; + $_ = T_L10N->get_handle("en"); + $_->bindtextdomain("test", $LOCALEDIR); + $_ = $_->bindtextdomain("test"); + return 1; +}; +# 1 +ok($r, 1); +# 2 +ok($_, "$LOCALEDIR"); + +# textdomain +$r = eval { + require T_L10N; + $_ = T_L10N->get_handle("en"); + $_->bindtextdomain("test", $LOCALEDIR); + $_->textdomain("test"); + $_ = $_->textdomain; + return 1; +}; +# 3 +ok($r, 1); +# 4 +ok($_, "test"); + +# read_mo +$META = << "EOT"; +Project-Id-Version: test 1.1 +Report-Msgid-Bugs-To: +POT-Creation-Date: 2008-02-19 12:31+0800 +PO-Revision-Date: 2008-02-19 12:31+0800 +Last-Translator: imacat +Language-Team: English +MIME-Version: 1.0 +Content-Type: text/plain; charset=US-ASCII +Content-Transfer-Encoding: 7bit +Plural-Forms: nplurals=2; plural=n != 1; +EOT +$r = eval { + use Locale::Maketext::Gettext; + $_ = catfile($LOCALEDIR, "en", "LC_MESSAGES", "test.mo"); + %_ = read_mo($_); + @_ = sort keys %_; + $n = scalar(@_); + $k1 = $_[0]; + $k2 = $_[1]; + $s1 = $_{$k1}; + $s2 = $_{$k2}; + return 1; +}; +# 5 +ok($r, 1); +# 6 +ok($n, 4); +# 7 +ok($k1, ""); +# 8 +ok($k2, "Hello, world!"); +# 9 +ok($s1, $META); +# 10 +ok($s2, "Hiya :)"); + +# English +$r = eval { + require T_L10N; + @_ = qw(); + $_ = T_L10N->get_handle("en"); + $_->bindtextdomain("test", $LOCALEDIR); + $_->textdomain("test"); + $_[0] = $_->maketext("Hello, world!"); + $_[1] = $_->pmaketext("Menu|File|", "Hello, world!"); + $_[2] = $_->pmaketext("Menu|View|", "Hello, world!"); + return 1; +}; +# 11 +ok($r, 1); +# 12 +ok($_[0], "Hiya :)"); +# 13 +ok($_[1], "Hiya :) under the File menu"); +# 14 +ok($_[2], "Hiya :) under the View menu"); + +# Traditional Chinese +$r = eval { + require T_L10N; + @_ = qw(); + $_ = T_L10N->get_handle("zh-tw"); + $_->bindtextdomain("test", $LOCALEDIR); + $_->textdomain("test"); + $_[0] = $_->maketext("Hello, world!"); + $_[1] = $_->pmaketext("Menu|File|", "Hello, world!"); + $_[2] = $_->pmaketext("Menu|View|", "Hello, world!"); + return 1; +}; +# 15 +ok($r, 1); +# 16 +ok($_[0], "大家好。"); +# 17 +ok($_[1], "檔案選單下的大家好。"); +# 18 +ok($_[2], "瀏覽選單下的大家好。"); + +# Simplified Chinese +$r = eval { + require T_L10N; + @_ = qw(); + $_ = T_L10N->get_handle("zh-cn"); + $_->bindtextdomain("test", $LOCALEDIR); + $_->textdomain("test"); + $_[0] = $_->maketext("Hello, world!"); + $_[1] = $_->pmaketext("Menu|File|", "Hello, world!"); + $_[2] = $_->pmaketext("Menu|View|", "Hello, world!"); + return 1; +}; +# 19 +ok($r, 1); +# 20 +ok($_[0], "湮模疑﹝"); +# 21 +ok($_[1], "紫偶粕等狟腔湮模疑﹝"); +# 22 +ok($_[2], "銡擬粕等狟腔湮模疑﹝"); diff --git a/t/02-big-endian.t b/t/02-big-endian.t new file mode 100755 index 0000000..23d1dee --- /dev/null +++ b/t/02-big-endian.t @@ -0,0 +1,143 @@ +#! /usr/bin/perl -w +# Test the big endian MO files +# Copyright (c) 2003-2009 imacat. All rights reserved. This program is free +# software; you can redistribute it and/or modify it under the same terms +# as Perl itself. + +use 5.008; +use strict; +use warnings; +use Test; + +BEGIN { plan tests => 10 } + +use FindBin; +use File::Spec::Functions qw(catdir catfile); +use lib $FindBin::Bin; +use vars qw($LOCALEDIR $r); +$LOCALEDIR = catdir($FindBin::Bin, "locale"); + +# Check reading big-endian PO files +use vars qw($skip $POfile $MOfile $hasctxt); +# English +$r = eval { + require T_L10N; + $_ = T_L10N->get_handle("en"); + $_->bindtextdomain("test_be", $LOCALEDIR); + $_->textdomain("test_be"); + $_ = $_->maketext("Hello, world!"); + return 1; +}; +# 1 +ok($r, 1); +# 2 +ok($_, "Hiya :)"); + +# Traditional Chinese +$r = eval { + require T_L10N; + $_ = T_L10N->get_handle("zh-tw"); + $_->bindtextdomain("test_be", $LOCALEDIR); + $_->textdomain("test_be"); + $_ = $_->maketext("Hello, world!"); + return 1; +}; +# 3 +ok($r, 1); +# 4 +ok($_, "大家好。"); + +# Simplified Chinese +$r = eval { + require T_L10N; + $_ = T_L10N->get_handle("zh-cn"); + $_->bindtextdomain("test_be", $LOCALEDIR); + $_->textdomain("test_be"); + $_ = $_->maketext("Hello, world!"); + return 1; +}; +# 5 +ok($r, 1); +# 6 +ok($_, "湮模疑﹝"); + +# Native-built MO file +{ +my $FH; +$skip = 1; +$POfile = catfile($FindBin::Bin, "test_native.po"); +$MOfile = catfile($LOCALEDIR, "en", "LC_MESSAGES", "test_native.mo"); +$_ = join "", `msgfmt --version 2>&1`; +last unless $? == 0; +last unless /GNU gettext/; +last unless /GNU gettext.* (\d+)\.(\d+)/; +# Gettext from 0.15 has msgctxt +$hasctxt = $1 > 0 || ($1 == 0 && $2 >= 15); +$_ = << "EOT"; +# English PO file for the test_native project. +# Copyright (C) 2003-2009 imacat +# This file is distributed under the same license as the commonlib package. +# imacat , 2003-%1\$04d. +# +msgid "" +msgstr "" +"Project-Id-Version: test_native 1.1\\n" +"Report-Msgid-Bugs-To: \\n" +"POT-Creation-Date: %1\$04d-%2\$02d-%3\$02d %4\$02d:%5\$02d+0800\\n" +"PO-Revision-Date: %1\$04d-%2\$02d-%3\$02d %4\$02d:%5\$02d+0800\\n" +"Last-Translator: imacat \\n" +"Language-Team: English \\n" +"MIME-Version: 1.0\\n" +"Content-Type: text/plain; charset=US-ASCII\\n" +"Content-Transfer-Encoding: 7bit\\n" +"Plural-Forms: nplurals=2; plural=n != 1;\\n" + +#: test_native.pl:100 +msgid "Hello, world!" +msgstr "Hiya :)" +EOT +$_ .= << "EOT" if $hasctxt; + +#: test_native.pl:103 +msgctxt "Menu|File|" +msgid "Hello, world!" +msgstr "Hiya :) under the File menu" + +#: test_native.pl:106 +msgctxt "Menu|View|" +msgid "Hello, world!" +msgstr "Hiya :) under the View menu" +EOT +@_ = localtime; +$_[5] += 1900; +$_[4]++; +$_ = sprintf $_, @_[5,4,3,2,1,0]; +open $FH, ">$POfile"; +print $FH $_; +close $FH; +`msgfmt -o "$MOfile" "$POfile"`; +last unless $? == 0; +$skip = 0; +$r = eval { + require T_L10N; + $_ = T_L10N->get_handle("en"); + $_->bindtextdomain("test_native", $LOCALEDIR); + $_->textdomain("test_native"); + $_[0] = $_->maketext("Hello, world!"); + $_[1] = $_->pmaketext("Menu|File|", "Hello, world!") if $hasctxt; + $_[2] = $_->pmaketext("Menu|View|", "Hello, world!") if $hasctxt; + return 1; +}; +} +# 7 +skip($skip, $r, 1); +# 8 +skip($skip, $_[0], "Hiya :)"); +# 9 +skip($skip || !$hasctxt, $_[1], "Hiya :) under the File menu"); +# 10 +skip($skip || !$hasctxt, $_[2], "Hiya :) under the View menu"); + +# Garbage collection +unlink $POfile; +unlink $MOfile; diff --git a/t/03-errors.t b/t/03-errors.t new file mode 100755 index 0000000..611121b --- /dev/null +++ b/t/03-errors.t @@ -0,0 +1,283 @@ +#! /usr/bin/perl -w +# Test suite for the behavior when something goes wrong +# Copyright (c) 2003-2009 imacat. All rights reserved. This program is free +# software; you can redistribute it and/or modify it under the same terms +# as Perl itself. + +use 5.008; +use strict; +use warnings; +use Test; + +BEGIN { plan tests => 29 } + +use Encode qw(); +use FindBin; +use File::Basename qw(basename); +use File::Spec::Functions qw(catdir catfile); +use lib $FindBin::Bin; +use vars qw($THIS_FILE $LOCALEDIR $r); +$THIS_FILE = basename($0); +$LOCALEDIR = catdir($FindBin::Bin, "locale"); +sub find_system_mo(); + +# find_system_mo: Find a safe system MO to be tested +sub find_system_mo() { + local ($_, %_); + my %cands; + use Locale::Maketext::Gettext::Functions; + # Find all the system MO files + %cands = qw(); + foreach my $dir (@Locale::Maketext::Gettext::Functions::SYSTEM_LOCALEDIRS) { + my ($DH, @locales); + next unless -d $dir; + + @locales = qw(); + opendir $DH, $dir or die "$THIS_FILE: $dir: $!"; + while (defined($_ = readdir $DH)) { + my $dir1; + $dir1 = catfile($dir, $_, "LC_MESSAGES"); + push @locales, $_ if -d $dir1 && -r $dir1 + && /^(?:en|zh_tw|zh_cn)$/i; + } + closedir $DH or die "$THIS_FILE: $dir: $!"; + + foreach my $loc (sort @locales) { + my $dir1; + $dir1 = catfile($dir, $loc, "LC_MESSAGES"); + opendir $DH, $dir1 or die "$THIS_FILE: $dir1: $!"; + while (defined($_ = readdir $DH)) { + my ($file, $domain); + $file = catfile($dir1, $_); + next unless -f $file && -r $file && /^(.+)\.mo$/; + $domain = $1; + $cands{$file} = [$loc, $domain]; + } + closedir $DH or die "$THIS_FILE: $dir1: $!"; + } + } + # Check each MO file, from the newest + foreach my $file (sort { (stat $b)[9] <=> (stat $a)[9] } keys %cands) { + my ($FH, $size, $content, $charset, $lang, $domain); + $size = (stat $file)[7]; + open $FH, $file or die "$THIS_FILE: $file: $!"; + read $FH, $content, $size or die "$THIS_FILE: $file: $!"; + close $FH or die "$THIS_FILE: $file: $!"; + # Only take files whose meta information does not have special characters + # that might be considered as code by Locale::Maketext + next unless $content =~ /Project-Id-Version:([^\n\0\[\]~]+\n)+\0/; + # Only take files that resolve to a valid character set + next unless $content =~ /\s+charset=([^\n]+)/; + $charset = $1; + next unless defined Encode::resolve_alias($charset); + # OK. We take this one + ($lang, $domain) = @{$cands{$file}}; + $lang = lc $lang; + $lang =~ s/_/-/g; + $lang = "i-default" if $lang eq "c"; + return ($lang, $domain); + } + # Not found + return (undef, undef); +} + +# When something goes wrong +use vars qw($dir $domain $lang $skip); +# GNU gettext never fails! +# bindtextdomain +$r = eval { + require T_L10N; + $_ = T_L10N->get_handle("en"); + $_ = $_->bindtextdomain("test"); + return 1; +}; +# 1 +ok($r, 1); +# 2 +ok($_, undef); + +# textdomain +$r = eval { + require T_L10N; + $_ = T_L10N->get_handle("en"); + $_->bindtextdomain("test", $LOCALEDIR); + $_ = $_->textdomain; + return 1; +}; +# 3 +ok($r, 1); +# 4 +ok($_, undef); + +# No text domain claimed yet +$r = eval { + require T_L10N; + $_ = T_L10N->get_handle("en"); + $_ = $_->maketext("Hello, world!"); + return 1; +}; +# 5 +ok($r, 1); +# 6 +ok($_, "Hello, world!"); + +# Non-existing LOCALEDIR +$r = eval { + require T_L10N; + $_ = T_L10N->get_handle("en"); + $_->bindtextdomain("test", "/dev/null"); + $_->textdomain("test"); + $_ = $_->maketext("Hello, world!"); + return 1; +}; +# 7 +ok($r, 1); +# 8 +ok($_, "Hello, world!"); + +# Not-registered DOMAIN +$r = eval { + require T_L10N; + $_ = T_L10N->get_handle("en"); + $_->textdomain("not_registered"); + $_ = $_->maketext("Hello, world!"); + return 1; +}; +# 9 +ok($r, 1); +# 10 +ok($_, "Hello, world!"); + +# PO file not exists +$r = eval { + require T_L10N; + $_ = T_L10N->get_handle("en"); + $_->bindtextdomain("no_such_domain", $LOCALEDIR); + $_->textdomain("no_such_domain"); + $_ = $_->maketext("Hello, world!"); + return 1; +}; +# 11 +ok($r, 1); +# 12 +ok($_, "Hello, world!"); + +# PO file invalid +$r = eval { + require T_L10N; + $_ = T_L10N->get_handle("en"); + $_->bindtextdomain("bad", $LOCALEDIR); + $_->textdomain("bad"); + $_ = $_->maketext("Hello, world!"); + return 1; +}; +# 13 +ok($r, 1); +# 14 +ok($_, "Hello, world!"); + +# No such message +$r = eval { + require T_L10N; + @_ = qw(); + $_ = T_L10N->get_handle("en"); + $_->bindtextdomain("test", $LOCALEDIR); + $_->textdomain("test"); + $_[0] = $_->maketext("[*,_1,non-existing message,non-existing messages]", 1); + $_[1] = $_->maketext("[*,_1,non-existing message,non-existing messages]", 3); + $_[2] = $_->pmaketext("Menu|View|", "[*,_1,non-existing message,non-existing messages]", 1); + $_[3] = $_->pmaketext("Menu|View|", "[*,_1,non-existing message,non-existing messages]", 3); + $_[4] = $_->pmaketext("Menu|None|", "Hello, world!"); + return 1; +}; +# 15 +ok($r, 1); +# 16 +ok($_[0], "1 non-existing message"); +# 17 +ok($_[1], "3 non-existing messages"); +# 18 +ok($_[2], "1 non-existing message"); +# 19 +ok($_[3], "3 non-existing messages"); +# 20 +ok($_[4], "Hello, world!"); + +# die_for_lookup_failures +$r = eval { + require T_L10N; + $_ = T_L10N->get_handle("en"); + $_->bindtextdomain("test", $LOCALEDIR); + $_->textdomain("test"); + $_->die_for_lookup_failures(1); + $_ = $_->maketext("non-existing message"); + return 1; +}; +# To be refined - to know that we failed at maketext() +# was ok($@, qr/maketext doesn't know how to say/); +# 21 +ok($r, undef); + +# multibyte keys +$r = eval { + require T_L10N; + $_ = T_L10N->get_handle("zh-tw"); + $_->bindtextdomain("test", $LOCALEDIR); + $_->textdomain("test"); + $_->key_encoding("Big5"); + $_ = $_->maketext("(未設定)"); + return 1; +}; +# 22 +ok($r, 1); +# 23 +ok($_, "(未設定)"); + +$r = eval { + require T_L10N; + $_ = T_L10N->get_handle("zh-tw"); + $_->bindtextdomain("test", "/dev/null"); + $_->textdomain("test"); + $_->key_encoding("Big5"); + $_ = $_->maketext("(未設定)"); + return 1; +}; +# 24 +ok($r, 1); +# 25 +ok($_, "(未設定)"); + +# Call maketext before and after binding text domain +$r = eval { + require T_L10N; + $_ = T_L10N->get_handle("en"); + $_->maketext("Hello, world!"); + $_->bindtextdomain("test", $LOCALEDIR); + $_->textdomain("test"); + $_ = $_->maketext("Hello, world!"); + return 1; +}; +# 26 +ok($r, 1); +# 27 +ok($_, "Hiya :)"); + +# Search system locale directories +($lang, $domain) = find_system_mo; +$skip = defined $domain? 0: 1; +print "($lang, $domain)\n"; +$r = eval { + return if $skip; + require T_L10N; + $_ = T_L10N->get_handle($lang); + $_->textdomain($domain); + print "OK 1111\n"; + $_ = $_->maketext(""); + # Skip if $Lexicon{""} does not exists + $skip = 1 if $_ eq ""; + return 1; +}; +# 28 +skip($skip, $r, 1); +# 29 +skip($skip, $_, qr/Project-Id-Version:/); diff --git a/t/04-encodings.t b/t/04-encodings.t new file mode 100755 index 0000000..cc09765 --- /dev/null +++ b/t/04-encodings.t @@ -0,0 +1,266 @@ +#! /usr/bin/perl -w +# Test suite for different encodings +# Copyright (c) 2003-2007 imacat. All rights reserved. This program is free +# software; you can redistribute it and/or modify it under the same terms +# as Perl itself. + +use 5.008; +use strict; +use warnings; +use Test; + +BEGIN { plan tests => 34 } + +use Encode qw(); +use FindBin qw(); +use File::Spec::Functions qw(catdir); +use lib $FindBin::Bin; +use vars qw($LOCALEDIR $r); +$LOCALEDIR = catdir($FindBin::Bin, "locale"); + +# Different encodings +# English +# Find the default encoding +$r = eval { + require T_L10N; + $_ = T_L10N->get_handle("en"); + $_->bindtextdomain("test", $LOCALEDIR); + $_->textdomain("test"); + $_ = $_->encoding; + return 1; +}; +# 1 +ok($r, 1); +# 2 +ok($_, "US-ASCII"); + +# Traditional Chinese +# Find the default encoding +$r = eval { + require T_L10N; + $_ = T_L10N->get_handle("zh-tw"); + $_->bindtextdomain("test", $LOCALEDIR); + $_->textdomain("test"); + $_ = $_->encoding; + return 1; +}; +# 3 +ok($r, 1); +# 4 +ok($_, "Big5"); + +# Turn to Big5 +$r = eval { + require T_L10N; + $_ = T_L10N->get_handle("zh-tw"); + $_->bindtextdomain("test", $LOCALEDIR); + $_->textdomain("test"); + $_->encoding("Big5"); + $_ = $_->maketext("Hello, world!"); + return 1; +}; +# 5 +ok($r, 1); +# 6 +ok($_, "大家好。"); + +# Turn to UTF-8 +$r = eval { + require T_L10N; + $_ = T_L10N->get_handle("zh-tw"); + $_->bindtextdomain("test", $LOCALEDIR); + $_->textdomain("test"); + $_->encoding("UTF-8"); + $_ = $_->maketext("Hello, world!"); + return 1; +}; +# 7 +ok($r, 1); +# 8 +ok($_, "憭批振憟賬"); + +# Turn to UTF-16LE +$r = eval { + require T_L10N; + $_ = T_L10N->get_handle("zh-tw"); + $_->bindtextdomain("test", $LOCALEDIR); + $_->textdomain("test"); + $_->encoding("UTF-16LE"); + $_ = $_->maketext("Hello, world!"); + return 1; +}; +# 9 +ok($r, 1); +# 10 +ok($_, "'Y跋}Y0"); + +# Find the default encoding, in UTF-8 +$r = eval { + require T_L10N; + $_ = T_L10N->get_handle("zh-tw"); + $_->bindtextdomain("test_utf8", $LOCALEDIR); + $_->textdomain("test_utf8"); + $_ = $_->encoding; + return 1; +}; +# 11 +ok($r, 1); +# 12 +ok($_, "UTF-8"); + +# Turn to UTF-8 +$r = eval { + require T_L10N; + $_ = T_L10N->get_handle("zh-tw"); + $_->bindtextdomain("test_utf8", $LOCALEDIR); + $_->textdomain("test_utf8"); + $_->encoding("UTF-8"); + $_ = $_->maketext("Hello, world!"); + return 1; +}; +# 13 +ok($r, 1); +# 14 +ok($_, "憭批振憟賬"); + +# Turn to Big5 +$r = eval { + require T_L10N; + $_ = T_L10N->get_handle("zh-tw"); + $_->bindtextdomain("test_utf8", $LOCALEDIR); + $_->textdomain("test_utf8"); + $_->encoding("Big5"); + $_ = $_->maketext("Hello, world!"); + return 1; +}; +# 15 +ok($r, 1); +# 16 +ok($_, "大家好。"); + +# Turn to UTF-16LE +$r = eval { + require T_L10N; + $_ = T_L10N->get_handle("zh-tw"); + $_->bindtextdomain("test_utf8", $LOCALEDIR); + $_->textdomain("test_utf8"); + $_->encoding("UTF-16LE"); + $_ = $_->maketext("Hello, world!"); + return 1; +}; +# 17 +ok($r, 1); +# 18 +ok($_, "'Y跋}Y0"); + +# Find the default encoding +# Simplified Chinese +$r = eval { + require T_L10N; + $_ = T_L10N->get_handle("zh-cn"); + $_->bindtextdomain("test_utf8", $LOCALEDIR); + $_->textdomain("test_utf8"); + $_ = $_->encoding; + return 1; +}; +# 19 +ok($r, 1); +# 20 +ok($_, "UTF-8"); + +# Turn to GB2312 +$r = eval { + require T_L10N; + $_ = T_L10N->get_handle("zh-cn"); + $_->bindtextdomain("test_utf8", $LOCALEDIR); + $_->textdomain("test_utf8"); + $_->encoding("GB2312"); + $_ = $_->maketext("Hello, world!"); + return 1; +}; +# 21 +ok($r, 1); +# 22 +ok($_, "湮模疑﹝"); + +# Encode failure +# FB_DEFAULT +$r = eval { + require T_L10N; + $_ = T_L10N->get_handle("zh-tw"); + $_->bindtextdomain("test2", $LOCALEDIR); + $_->textdomain("test2"); + $_->encoding("GB2312"); + $_ = $_->maketext("Every story has a happy ending."); + return 1; +}; +# 23 +ok($r, 1); +# 24 +ok($_, "嘟岈飲衄藝?腔?擁﹝"); + +# FB_CROAK +$r = eval { + require T_L10N; + $_ = T_L10N->get_handle("zh-tw"); + $_->bindtextdomain("test2", $LOCALEDIR); + $_->textdomain("test2"); + $_->encoding("GB2312"); + $_->encode_failure(Encode::FB_CROAK); + $_ = $_->maketext("Every story has a happy ending."); + return 1; +}; +# 25 +ok($r, undef); +# 26 +ok($@, qr/does not map to/); + +# FB_HTMLCREF +$r = eval { + require T_L10N; + $_ = T_L10N->get_handle("zh-tw"); + $_->bindtextdomain("test2", $LOCALEDIR); + $_->textdomain("test2"); + $_->encoding("GB2312"); + $_->encode_failure(Encode::FB_HTMLCREF); + $_ = $_->maketext("Every story has a happy ending."); + return 1; +}; +# 27 +ok($r, 1); +# 28 +ok($_, "嘟岈飲衄藝麗腔結擁﹝"); + +# Return the unencoded UTF-8 text +$r = eval { + require T_L10N; + $_ = T_L10N->get_handle("zh-tw"); + $_->bindtextdomain("test", $LOCALEDIR); + $_->textdomain("test"); + $_->encoding(undef); + $_ = $_->maketext("Hello, world!"); + return 1; +}; +# 29 +ok($r, 1); +# 30 +ok($_, "\x{5927}\x{5BB6}\x{597D}\x{3002}"); +# 31 +ok((Encode::is_utf8($_)? "utf8": "non-utf8"), "utf8"); + +# Return the unencoded UTF-8 text with auto lexicon +$r = eval { + require T_L10N; + $_ = T_L10N->get_handle("zh-tw"); + $_->bindtextdomain("test", $LOCALEDIR); + $_->textdomain("test"); + $_->encoding(undef); + $_ = $_->maketext("Big watermelon"); + return 1; +}; +# 32 +ok($r, 1); +# 33 +ok($_, "Big watermelon"); +# 34 +ok((Encode::is_utf8($_)? "utf8": "non-utf8"), "utf8"); diff --git a/t/05-switching.t b/t/05-switching.t new file mode 100755 index 0000000..c3e17aa --- /dev/null +++ b/t/05-switching.t @@ -0,0 +1,157 @@ +#! /usr/bin/perl -w +# Test suite for switching between different settings +# Copyright (c) 2003-2008 imacat. All rights reserved. This program is free +# software; you can redistribute it and/or modify it under the same terms +# as Perl itself. + +use 5.008; +use strict; +use warnings; +use Test; + +BEGIN { plan tests => 25 } + +use FindBin; +use File::Spec::Functions qw(catdir catfile); +use lib $FindBin::Bin; +use vars qw($LOCALEDIR $r); +$LOCALEDIR = catdir($FindBin::Bin, "locale"); + +# Switching between different settings +use File::Copy qw(copy); +use vars qw($lh1 $lh2 $dir $f $f1 $f2); + +# 2 language handles of the same localization subclass +$r = eval { + require T_L10N; + @_ = qw(); + $lh1 = T_L10N->get_handle("en"); + $lh1->bindtextdomain("test", $LOCALEDIR); + $lh1->textdomain("test"); + $lh2 = T_L10N->get_handle("en"); + $lh2->bindtextdomain("test2", $LOCALEDIR); + $lh2->textdomain("test2"); + $_[0] = $lh1->maketext("Hello, world!"); + $_[1] = $lh1->maketext("Every story has a happy ending."); + $_[2] = $lh2->maketext("Hello, world!"); + $_[3] = $lh2->maketext("Every story has a happy ending."); + $_[4] = $lh1->maketext("Hello, world!"); + $_[5] = $lh1->maketext("Every story has a happy ending."); + return 1; +}; +# 1 +ok($r, 1); +# 2 +ok($_[0], "Hiya :)"); +# 3 +ok($_[1], "Every story has a happy ending."); +# 4 +ok($_[2], "Hello, world!"); +# 5 +ok($_[3], "Pray it."); +# 6 +ok($_[4], "Hiya :)"); +# 7 +ok($_[5], "Every story has a happy ending."); + +# Switch between domains +$r = eval { + require T_L10N; + @_ = qw(); + $_ = T_L10N->get_handle("en"); + $_->bindtextdomain("test", $LOCALEDIR); + $_->bindtextdomain("test2", $LOCALEDIR); + $_->textdomain("test"); + $_[0] = $_->maketext("Hello, world!"); + $_[1] = $_->maketext("Every story has a happy ending."); + $_->textdomain("test2"); + $_[2] = $_->maketext("Hello, world!"); + $_[3] = $_->maketext("Every story has a happy ending."); + $_->textdomain("test"); + $_[4] = $_->maketext("Hello, world!"); + $_[5] = $_->maketext("Every story has a happy ending."); + return 1; +}; +# 8 +ok($r, 1); +# 9 +ok($_[0], "Hiya :)"); +# 10 +ok($_[1], "Every story has a happy ending."); +# 11 +ok($_[2], "Hello, world!"); +# 12 +ok($_[3], "Pray it."); +# 13 +ok($_[4], "Hiya :)"); +# 14 +ok($_[5], "Every story has a happy ending."); + +# Switch between encodings +$r = eval { + require T_L10N; + @_ = qw(); + $_ = T_L10N->get_handle("zh-tw"); + $_->bindtextdomain("test", $LOCALEDIR); + $_->textdomain("test"); + $_->encoding("Big5"); + $_[0] = $_->maketext("Hello, world!"); + $_->encoding("UTF-8"); + $_[1] = $_->maketext("Hello, world!"); + $_->encoding("Big5"); + $_[2] = $_->maketext("Hello, world!"); + return 1; +}; +# 15 +ok($r, 1); +# 16 +ok($_[0], "大家好。"); +# 17 +ok($_[1], "憭批振憟賬"); +# 18 +ok($_[2], "大家好。"); + +# Reload the text +$r = eval { + $dir = catdir($LOCALEDIR, "en", "LC_MESSAGES"); + $f = catfile($dir, "test_reload.mo"); + $f1 = catfile($dir, "test.mo"); + $f2 = catfile($dir, "test2.mo"); + unlink $f; + copy $f1, $f or die "ERROR: $f1 $f: $!"; + + require T_L10N; + @_ = qw(); + $_ = T_L10N->get_handle("en"); + $_->bindtextdomain("test_reload", $LOCALEDIR); + $_->textdomain("test_reload"); + $_[0] = $_->maketext("Hello, world!"); + $_[1] = $_->maketext("Every story has a happy ending."); + unlink $f; + copy $f2, $f or die "ERROR: $f2 $f: $!"; + $_[2] = $_->maketext("Hello, world!"); + $_[3] = $_->maketext("Every story has a happy ending."); + $_->reload_text; + $_[4] = $_->maketext("Hello, world!"); + $_[5] = $_->maketext("Every story has a happy ending."); + + unlink $f; + return 1; +}; +# 19 +ok($r, 1); +# 20 +ok($_[0], "Hiya :)"); +# 21 +ok($_[1], "Every story has a happy ending."); +# 22 +ok($_[2], "Hiya :)"); +# 23 +ok($_[3], "Every story has a happy ending."); +# 24 +ok($_[4], "Hello, world!"); +# 25 +ok($_[5], "Pray it."); + +# Garbage collection +unlink catfile($LOCALEDIR, "en", "LC_MESSAGES", "test_reload.mo"); diff --git a/t/06-racing.t b/t/06-racing.t new file mode 100755 index 0000000..f6c9c87 --- /dev/null +++ b/t/06-racing.t @@ -0,0 +1,317 @@ +#! /usr/bin/perl -w +# Test suite for the hybrid racing condition +# Copyright (c) 2003-2007 imacat. All rights reserved. This program is free +# software; you can redistribute it and/or modify it under the same terms +# as Perl itself. + +use 5.008; +use strict; +use warnings; +use Test; + +BEGIN { plan tests => 42 } + +use FindBin; +use File::Spec::Functions qw(catdir catfile); +use lib $FindBin::Bin; +use vars qw($LOCALEDIR $r $LOOKUP_FAILURE); +$LOCALEDIR = catdir($FindBin::Bin, "locale"); + +# Hybrid racing conditionr +use vars qw($lh1 $lh2); +$r = eval { + undef $LOOKUP_FAILURE; + require T_L10N; + + $lh1 = T_L10N->get_handle("zh-tw"); + $lh1->fail_with(sub { $LOOKUP_FAILURE = 1; die; }); + $lh1->bindtextdomain("test", $LOCALEDIR); + $lh1->textdomain("test"); + $lh1->encoding("Big5"); + $lh1->die_for_lookup_failures(0); + + $lh2 = T_L10N->get_handle("zh-tw"); + $lh2->fail_with(sub { $LOOKUP_FAILURE = 1; die; }); + $lh2->bindtextdomain("test2", $LOCALEDIR); + $lh2->textdomain("test2"); + $lh2->encoding("UTF-8"); + $lh2->die_for_lookup_failures(1); + return 1; +}; +# 1 +ok($r, 1); + +# Once +$r = eval { + undef $LOOKUP_FAILURE; + $_ = $lh1->maketext("Hello, world!"); + return 1; +}; +# 2 +ok($_, "大家好。"); +$r = eval { + undef $LOOKUP_FAILURE; + $_ = $lh1->maketext("Every story has a happy ending."); + return 1; +}; +# 3 +ok($_, "Every story has a happy ending."); +$r = eval { + undef $LOOKUP_FAILURE; + $_ = $lh2->maketext("Hello, world!"); + return 1; +}; +# 4 +ok($r, undef); +# 5 +ok($LOOKUP_FAILURE, 1); +$r = eval { + undef $LOOKUP_FAILURE; + $_ = $lh2->maketext("Every story has a happy ending."); + return 1; +}; +# 6 +ok($_, "鈭賣蝢暻蝯撅"); + +# Again +$r = eval { + undef $LOOKUP_FAILURE; + $_ = $lh1->maketext("Hello, world!"); + return 1; +}; +# 7 +ok($_, "大家好。"); +$r = eval { + undef $LOOKUP_FAILURE; + $_ = $lh1->maketext("Every story has a happy ending."); + return 1; +}; +# 8 +ok($_, "Every story has a happy ending."); +$r = eval { + undef $LOOKUP_FAILURE; + $_ = $lh2->maketext("Hello, world!"); + return 1; +}; +# 9 +ok($r, undef); +# 10 +ok($LOOKUP_FAILURE, 1); +$r = eval { + undef $LOOKUP_FAILURE; + $_ = $lh2->maketext("Every story has a happy ending."); + return 1; +}; +# 11 +ok($_, "鈭賣蝢暻蝯撅"); + +# Exchange everything! +$r = eval { + undef $LOOKUP_FAILURE; + $lh1->bindtextdomain("test2", $LOCALEDIR); + $lh1->textdomain("test2"); + $lh1->encoding("UTF-8"); + $lh1->die_for_lookup_failures(1); + + $lh2->bindtextdomain("test", $LOCALEDIR); + $lh2->textdomain("test"); + $lh2->encoding("Big5"); + $lh2->die_for_lookup_failures(0); + return 1; +}; +# 12 +ok($r, 1); + +$r = eval { + undef $LOOKUP_FAILURE; + $_ = $lh1->maketext("Hello, world!"); + return 1; +}; +# 13 +ok($r, undef); +# 14 +ok($LOOKUP_FAILURE, 1); +$r = eval { + undef $LOOKUP_FAILURE; + $_ = $lh1->maketext("Every story has a happy ending."); + return 1; +}; +# 15 +ok($_, "鈭賣蝢暻蝯撅"); +$r = eval { + undef $LOOKUP_FAILURE; + $_ = $lh2->maketext("Hello, world!"); + return 1; +}; +# 16 +ok($_, "大家好。"); +$r = eval { + undef $LOOKUP_FAILURE; + $_ = $lh2->maketext("Every story has a happy ending."); + return 1; +}; +# 17 +ok($_, "Every story has a happy ending."); + +# Exchange the text domains +$r = eval { + undef $LOOKUP_FAILURE; + $lh1->textdomain("test"); + $lh2->textdomain("test2"); + return 1; +}; +# 18 +ok($r, 1); +$r = eval { + undef $LOOKUP_FAILURE; + $_ = $lh1->maketext("Hello, world!"); + return 1; +}; +# 19 +ok($_, "憭批振憟賬"); +$r = eval { + undef $LOOKUP_FAILURE; + $_ = $lh1->maketext("Every story has a happy ending."); + return 1; +}; +# 20 +ok($r, undef); +# 21 +ok($LOOKUP_FAILURE, 1); +$r = eval { + undef $LOOKUP_FAILURE; + $_ = $lh2->maketext("Hello, world!"); + return 1; +}; +# 22 +ok($_, "Hello, world!"); +$r = eval { + undef $LOOKUP_FAILURE; + $_ = $lh2->maketext("Every story has a happy ending."); + return 1; +}; +# 23 +ok($_, "故事都有美麗的結局。"); + +# Exchange encodings +$r = eval { + undef $LOOKUP_FAILURE; + $lh1->encoding("Big5"); + $lh2->encoding("UTF-8"); + return 1; +}; +# 24 +ok($r, 1); +$r = eval { + undef $LOOKUP_FAILURE; + $_ = $lh1->maketext("Hello, world!"); + return 1; +}; +# 25 +ok($_, "大家好。"); +$r = eval { + undef $LOOKUP_FAILURE; + $_ = $lh1->maketext("Every story has a happy ending."); + return 1; +}; +# 26 +ok($r, undef); +# 27 +ok($LOOKUP_FAILURE, 1); +$r = eval { + undef $LOOKUP_FAILURE; + $_ = $lh2->maketext("Hello, world!"); + return 1; +}; +# 28 +ok($_, "Hello, world!"); +$r = eval { + undef $LOOKUP_FAILURE; + $_ = $lh2->maketext("Every story has a happy ending."); + return 1; +}; +# 29 +ok($_, "鈭賣蝢暻蝯撅"); + +# Exchange lookup-failure behaviors +$r = eval { + undef $LOOKUP_FAILURE; + $lh1->die_for_lookup_failures(0); + $lh2->die_for_lookup_failures(1); + return 1; +}; +# 30 +ok($r, 1); +$r = eval { + undef $LOOKUP_FAILURE; + $_ = $lh1->maketext("Hello, world!"); + return 1; +}; +# 31 +ok($_, "大家好。"); +$r = eval { + undef $LOOKUP_FAILURE; + $_ = $lh1->maketext("Every story has a happy ending."); + return 1; +}; +# 32 +ok($_, "Every story has a happy ending."); +$r = eval { + undef $LOOKUP_FAILURE; + $_ = $lh2->maketext("Hello, world!"); + return 1; +}; + +# 33 +ok($r, undef); +# 34 +ok($LOOKUP_FAILURE, 1); +$r = eval { + undef $LOOKUP_FAILURE; + $_ = $lh2->maketext("Every story has a happy ending."); + return 1; +}; +# 35 +ok($_, "鈭賣蝢暻蝯撅"); + +# Switch to an non-existing domain +$r = eval { + undef $LOOKUP_FAILURE; + $lh1->textdomain("Big5"); + $lh2->textdomain("GB2312"); + return 1; +}; +# 36 +ok($r, 1); +$r = eval { + undef $LOOKUP_FAILURE; + $_ = $lh1->maketext("Hello, world!"); + return 1; +}; +# 37 +ok($_, "Hello, world!"); +$r = eval { + undef $LOOKUP_FAILURE; + $_ = $lh1->maketext("Every story has a happy ending."); + return 1; +}; +# 38 +ok($_, "Every story has a happy ending."); +$r = eval { + undef $LOOKUP_FAILURE; + $_ = $lh2->maketext("Hello, world!"); + return 1; +}; +# 39 +ok($r, undef); +# 40 +ok($LOOKUP_FAILURE, 1); +$r = eval { + undef $LOOKUP_FAILURE; + $_ = $lh2->maketext("Every story has a happy ending."); + return 1; +}; +# 41 +ok($r, undef); +# 42 +ok($LOOKUP_FAILURE, 1); diff --git a/t/07-f-basic.t b/t/07-f-basic.t new file mode 100755 index 0000000..aa1ce81 --- /dev/null +++ b/t/07-f-basic.t @@ -0,0 +1,275 @@ +#! /usr/bin/perl -w +# Basic test suite for the functional interface +# Copyright (c) 2003-2008 imacat. All rights reserved. This program is free +# software; you can redistribute it and/or modify it under the same terms +# as Perl itself. + +use 5.008; +use strict; +use warnings; +use Test; + +BEGIN { plan tests => 41 } + +use FindBin; +use File::Spec::Functions qw(catdir catfile); +use lib $FindBin::Bin; +use vars qw($LOCALEDIR $r); +$LOCALEDIR = catdir($FindBin::Bin, "locale"); +delete $ENV{$_} + foreach qw(LANGUAGE LC_ALL LC_CTYPE LC_COLLATE LC_MESSAGES LC_NUMERIC + LC_MONETARY LC_TIME LANG); + +# Basic test suite +# bindtextdomain +$r = eval { + use Locale::Maketext::Gettext::Functions; + Locale::Maketext::Gettext::Functions::_reset(); + $_ = bindtextdomain("test", $LOCALEDIR); + return 1; +}; +# 1 +ok($r, 1); +# 2 +ok($_, $LOCALEDIR); + +# textdomain +$r = eval { + use Locale::Maketext::Gettext::Functions; + Locale::Maketext::Gettext::Functions::_reset(); + bindtextdomain("test", $LOCALEDIR); + $_ = textdomain("test"); + return 1; +}; +# 3 +ok($r, 1); +# 4 +ok($_, "test"); + +# get_handle +$r = eval { + use Locale::Maketext::Gettext::Functions; + Locale::Maketext::Gettext::Functions::_reset(); + bindtextdomain("test", $LOCALEDIR); + textdomain("test"); + get_handle("en"); + return 1; +}; +# 5 +ok($r, 1); + +# maketext +$r = eval { + use Locale::Maketext::Gettext::Functions; + Locale::Maketext::Gettext::Functions::_reset(); + bindtextdomain("test", $LOCALEDIR); + textdomain("test"); + get_handle("en"); + $_ = maketext("Hello, world!"); + return 1; +}; +# 6 +ok($r, 1); +# 7 +ok($_, "Hiya :)"); + +# __ (shortcut to maketext) +$r = eval { + use Locale::Maketext::Gettext::Functions; + Locale::Maketext::Gettext::Functions::_reset(); + bindtextdomain("test", $LOCALEDIR); + textdomain("test"); + get_handle("en"); + $_ = __("Hello, world!"); + return 1; +}; +# 8 +ok($r, 1); +# 9 +ok($_, "Hiya :)"); + +# N_ (do nothing) +$r = eval { + use Locale::Maketext::Gettext::Functions; + Locale::Maketext::Gettext::Functions::_reset(); + bindtextdomain("test", $LOCALEDIR); + textdomain("test"); + get_handle("en"); + $_ = N_("Hello, world!"); + return 1; +}; +# 10 +ok($r, 1); +# 11 +ok($_, "Hello, world!"); + +# N_ (do nothing) +$r = eval { + use Locale::Maketext::Gettext::Functions; + Locale::Maketext::Gettext::Functions::_reset(); + bindtextdomain("test", $LOCALEDIR); + textdomain("test"); + get_handle("en"); + # 李子文你這粒大西瓜! :p (From: 台灣霹靂火) + @_ = N_("Hello, world!", "Cool!", "Big watermelon"); + return 1; +}; +# 12 +ok($r, 1); +# 13 +ok($_[0], "Hello, world!"); +# 14 +ok($_[1], "Cool!"); +# 15 +ok($_[2], "Big watermelon"); + +$r = eval { + use Locale::Maketext::Gettext::Functions; + Locale::Maketext::Gettext::Functions::_reset(); + bindtextdomain("test", $LOCALEDIR); + textdomain("test"); + get_handle("en"); + $_ = N_("Hello, world!"); + return 1; +}; +# 16 +ok($r, 1); +# 17 +ok($_, "Hello, world!"); + +# maketext +# English +$r = eval { + use Locale::Maketext::Gettext::Functions; + Locale::Maketext::Gettext::Functions::_reset(); + @_ = qw(); + bindtextdomain("test", $LOCALEDIR); + textdomain("test"); + get_handle("en"); + $_[0] = __("Hello, world!"); + $_[1] = pmaketext("Menu|File|", "Hello, world!"); + $_[2] = pmaketext("Menu|View|", "Hello, world!"); + return 1; +}; +# 18 +ok($r, 1); +# 19 +ok($_[0], "Hiya :)"); +# 20 +ok($_[1], "Hiya :) under the File menu"); +# 21 +ok($_[2], "Hiya :) under the View menu"); + +# Traditional Chinese +$r = eval { + use Locale::Maketext::Gettext::Functions; + Locale::Maketext::Gettext::Functions::_reset(); + @_ = qw(); + bindtextdomain("test", $LOCALEDIR); + textdomain("test"); + get_handle("zh-tw"); + $_[0] = __("Hello, world!"); + $_[1] = pmaketext("Menu|File|", "Hello, world!"); + $_[2] = pmaketext("Menu|View|", "Hello, world!"); + return 1; +}; +# 22 +ok($r, 1); +# 23 +ok($_[0], "大家好。"); +# 24 +ok($_[1], "檔案選單下的大家好。"); +# 25 +ok($_[2], "瀏覽選單下的大家好。"); + +# Simplified Chinese +$r = eval { + use Locale::Maketext::Gettext::Functions; + Locale::Maketext::Gettext::Functions::_reset(); + @_ = qw(); + bindtextdomain("test", $LOCALEDIR); + textdomain("test"); + get_handle("zh-cn"); + $_[0] = __("Hello, world!"); + $_[1] = pmaketext("Menu|File|", "Hello, world!"); + $_[2] = pmaketext("Menu|View|", "Hello, world!"); + return 1; +}; +# 26 +ok($r, 1); +# 27 +ok($_[0], "湮模疑﹝"); +# 28 +ok($_[1], "紫偶粕等狟腔湮模疑﹝"); +# 29 +ok($_[2], "銡擬粕等狟腔湮模疑﹝"); + +# maketext - by environment +# English +$r = eval { + use Locale::Maketext::Gettext::Functions; + Locale::Maketext::Gettext::Functions::_reset(); + @_ = qw(); + bindtextdomain("test", $LOCALEDIR); + textdomain("test"); + $ENV{"LANG"} = "en"; + get_handle(); + $_[0] = __("Hello, world!"); + $_[1] = pmaketext("Menu|File|", "Hello, world!"); + $_[2] = pmaketext("Menu|View|", "Hello, world!"); + return 1; +}; +# 30 +ok($r, 1); +# 31 +ok($_[0], "Hiya :)"); +# 32 +ok($_[1], "Hiya :) under the File menu"); +# 33 +ok($_[2], "Hiya :) under the View menu"); + +# Traditional Chinese +$r = eval { + use Locale::Maketext::Gettext::Functions; + Locale::Maketext::Gettext::Functions::_reset(); + @_ = qw(); + bindtextdomain("test", $LOCALEDIR); + textdomain("test"); + $ENV{"LANG"} = "zh-tw"; + get_handle(); + $_[0] = __("Hello, world!"); + $_[1] = pmaketext("Menu|File|", "Hello, world!"); + $_[2] = pmaketext("Menu|View|", "Hello, world!"); + return 1; +}; +# 34 +ok($r, 1); +# 35 +ok($_[0], "大家好。"); +# 36 +ok($_[1], "檔案選單下的大家好。"); +# 37 +ok($_[2], "瀏覽選單下的大家好。"); + +# Simplified Chinese +$r = eval { + use Locale::Maketext::Gettext::Functions; + Locale::Maketext::Gettext::Functions::_reset(); + @_ = qw(); + bindtextdomain("test", $LOCALEDIR); + textdomain("test"); + $ENV{"LANG"} = "zh-cn"; + get_handle(); + $_[0] = __("Hello, world!"); + $_[1] = pmaketext("Menu|File|", "Hello, world!"); + $_[2] = pmaketext("Menu|View|", "Hello, world!"); + return 1; +}; +# 38 +ok($r, 1); +# 39 +ok($_[0], "湮模疑﹝"); +# 40 +ok($_[1], "紫偶粕等狟腔湮模疑﹝"); +# 41 +ok($_[2], "銡擬粕等狟腔湮模疑﹝"); diff --git a/t/08-f-errors.t b/t/08-f-errors.t new file mode 100755 index 0000000..2378435 --- /dev/null +++ b/t/08-f-errors.t @@ -0,0 +1,344 @@ +#! /usr/bin/perl -w +# Test suite on the functional interface for the behavior when something goes wrong +# Copyright (c) 2003-2008 imacat. All rights reserved. This program is free +# software; you can redistribute it and/or modify it under the same terms +# as Perl itself. + +use 5.008; +use strict; +use warnings; +use Test; + +BEGIN { plan tests => 39 } + +use Encode qw(); +use FindBin; +use File::Basename qw(basename); +use File::Spec::Functions qw(catdir catfile); +use lib $FindBin::Bin; +use vars qw($THIS_FILE $LOCALEDIR $r); +$THIS_FILE = basename($0); +$LOCALEDIR = catdir($FindBin::Bin, "locale"); +sub find_system_mo(); + +# find_system_mo: Find a safe system MO to be tested +sub find_system_mo() { + local ($_, %_); + my %cands; + use Locale::Maketext::Gettext::Functions; + # Find all the system MO files + %cands = qw(); + foreach my $dir (@Locale::Maketext::Gettext::Functions::SYSTEM_LOCALEDIRS) { + my ($DH, @langs); + next unless -d $dir; + + @langs = qw(); + opendir $DH, $dir or die "$THIS_FILE: $dir: $!"; + while (defined($_ = readdir $DH)) { + my $dir1; + $dir1 = catfile($dir, $_, "LC_MESSAGES"); + push @langs, $_ if -d $dir1 && -r $dir1; + } + closedir $DH or die "$THIS_FILE: $dir: $!"; + + foreach my $lang (sort @langs) { + my $dir1; + $dir1 = catfile($dir, $lang, "LC_MESSAGES"); + opendir $DH, $dir1 or die "$THIS_FILE: $dir1: $!"; + while (defined($_ = readdir $DH)) { + my ($file, $domain); + $file = catfile($dir1, $_); + next unless -f $file && -r $file && /^(.+)\.mo$/; + $domain = $1; + $cands{$file} = [$lang, $domain]; + } + closedir $DH or die "$THIS_FILE: $dir1: $!"; + } + } + # Check each MO file, from the newest + foreach my $file (sort { (stat $b)[9] <=> (stat $a)[9] } keys %cands) { + my ($FH, $size, $content, $charset, $lang, $domain); + $size = (stat $file)[7]; + open $FH, $file or die "$THIS_FILE: $file: $!"; + read $FH, $content, $size or die "$THIS_FILE: $file: $!"; + close $FH or die "$THIS_FILE: $file: $!"; + next unless $content =~ /Project-Id-Version:/; + next unless $content =~ /\s+charset=([^\n]+)/; + $charset = $1; + next unless defined Encode::resolve_alias($charset); + # OK. We take this one + ($lang, $domain) = @{$cands{$file}}; + $lang = lc $lang; + $lang =~ s/_/-/g; + $lang = "i-default" if $lang eq "c"; + return ($lang, $domain); + } + # Not found + return (undef, undef); +} + +# When something goes wrong +use vars qw($dir $domain $lang $skip); +# GNU gettext never fails! +# bindtextdomain +$r = eval { + use Locale::Maketext::Gettext::Functions; + Locale::Maketext::Gettext::Functions::_reset(); + $_ = bindtextdomain("test"); + return 1; +}; +# 1 +ok($r, 1); +# 2 +ok($_, undef); + +# textdomain +$r = eval { + use Locale::Maketext::Gettext::Functions; + Locale::Maketext::Gettext::Functions::_reset(); + bindtextdomain("test", $LOCALEDIR); + $_ = textdomain; + return 1; +}; +# 3 +ok($r, 1); +# 4 +ok($_, undef); + +# No text domain claimed yet +$r = eval { + use Locale::Maketext::Gettext::Functions; + Locale::Maketext::Gettext::Functions::_reset(); + $_ = __("Hello, world!"); + return 1; +}; +# 5 +ok($r, 1); +# 6 +ok($_, "Hello, world!"); + +# Non-existing LOCALEDIR +$r = eval { + use Locale::Maketext::Gettext::Functions; + Locale::Maketext::Gettext::Functions::_reset(); + bindtextdomain("test", "/dev/null"); + textdomain("test"); + $_ = __("Hello, world!"); + return 1; +}; +# 7 +ok($r, 1); +# 8 +ok($_, "Hello, world!"); + +# Not-registered DOMAIN +$r = eval { + use Locale::Maketext::Gettext::Functions; + Locale::Maketext::Gettext::Functions::_reset(); + textdomain("not_registered"); + $_ = __("Hello, world!"); + return 1; +}; +# 9 +ok($r, 1); +# 10 +ok($_, "Hello, world!"); + +# PO file not exists +$r = eval { + use Locale::Maketext::Gettext::Functions; + Locale::Maketext::Gettext::Functions::_reset(); + bindtextdomain("no_such_domain", $LOCALEDIR); + textdomain("no_such_domain"); + $_ = __("Hello, world!"); + return 1; +}; +# 11 +ok($r, 1); +# 12 +ok($_, "Hello, world!"); + +# PO file invalid +$r = eval { + use Locale::Maketext::Gettext::Functions; + Locale::Maketext::Gettext::Functions::_reset(); + bindtextdomain("bad", $LOCALEDIR); + textdomain("bad"); + $_ = __("Hello, world!"); + return 1; +}; +# 13 +ok($r, 1); +# 14 +ok($_, "Hello, world!"); + +# No such message +$r = eval { + use Locale::Maketext::Gettext::Functions; + Locale::Maketext::Gettext::Functions::_reset(); + @_ = qw(); + bindtextdomain("test", $LOCALEDIR); + textdomain("test"); + get_handle("en"); + $_[0] = __("[*,_1,non-existing message,non-existing messages]", 1); + $_[1] = __("[*,_1,non-existing message,non-existing messages]", 3); + $_[2] = pmaketext("Menu|View|", "[*,_1,non-existing message,non-existing messages]", 1); + $_[3] = pmaketext("Menu|View|", "[*,_1,non-existing message,non-existing messages]", 3); + $_[4] = pmaketext("Menu|None|", "Hello, world!"); + return 1; +}; +# 15 +ok($r, 1); +# 16 +ok($_[0], "1 non-existing message"); +# 17 +ok($_[1], "3 non-existing messages"); +# 18 +ok($_[2], "1 non-existing message"); +# 19 +ok($_[3], "3 non-existing messages"); +# 20 +ok($_[4], "Hello, world!"); + +# get_handle before textdomain +$r = eval { + use Locale::Maketext::Gettext::Functions; + Locale::Maketext::Gettext::Functions::_reset(); + get_handle("en"); + bindtextdomain("test", $LOCALEDIR); + textdomain("test"); + $_ = __("Hello, world!"); + return 1; +}; +# 21 +ok($r, 1); +# 22 +ok($_, "Hiya :)"); + +# bindtextdomain after textdomain +$r = eval { + use Locale::Maketext::Gettext::Functions; + Locale::Maketext::Gettext::Functions::_reset(); + get_handle("en"); + textdomain("test2"); + bindtextdomain("test2", $LOCALEDIR); + $_ = __("Every story has a happy ending."); + return 1; +}; +# 23 +ok($r, 1); +# 24 +ok($_, "Pray it."); + +# multibyte keys +$r = eval { + use Locale::Maketext::Gettext::Functions; + Locale::Maketext::Gettext::Functions::_reset(); + bindtextdomain("test", $LOCALEDIR); + textdomain("test"); + get_handle("zh-tw"); + key_encoding("Big5"); + $_ = maketext("(未設定)"); + return 1; +}; +# 25 +ok($r, 1); +# 26 +ok($_, "(未設定)"); + +$r = eval { + use Locale::Maketext::Gettext::Functions; + Locale::Maketext::Gettext::Functions::_reset(); + bindtextdomain("test", "/dev/null"); + textdomain("test"); + get_handle("zh-tw"); + key_encoding("Big5"); + $_ = maketext("(未設定)"); + return 1; +}; +# 27 +ok($r, 1); +# 28 +ok($_, "(未設定)"); + +# Maketext before and after binding text domain +$r = eval { + use Locale::Maketext::Gettext::Functions; + Locale::Maketext::Gettext::Functions::_reset(); + __("Hello, world!"); + bindtextdomain("test", $LOCALEDIR); + textdomain("test"); + get_handle("en"); + $_ = __("Hello, world!"); + return 1; +}; +# 29 +ok($r, 1); +# 30 +ok($_, "Hiya :)"); + +# Switch to a domain that is not binded yet +$r = eval { + use Locale::Maketext::Gettext::Functions; + Locale::Maketext::Gettext::Functions::_reset(); + get_handle("en"); + bindtextdomain("test", $LOCALEDIR); + textdomain("test"); + textdomain("test2"); + $_ = __("Hello, world!"); + return 1; +}; +# 31 +ok($r, 1); +# 32 +ok($_, "Hello, world!"); + +# N_: different context - string to array +$r = eval { + use Locale::Maketext::Gettext::Functions; + Locale::Maketext::Gettext::Functions::_reset(); + bindtextdomain("test", $LOCALEDIR); + textdomain("test"); + get_handle("en"); + @_ = N_("Hello, world!"); + return 1; +}; +# 33 +ok($r, 1); +# 34 +ok($_[0], "Hello, world!"); +# 35 +ok($_[1], undef); + +# N_: different context - array to string +$r = eval { + use Locale::Maketext::Gettext::Functions; + Locale::Maketext::Gettext::Functions::_reset(); + bindtextdomain("test", $LOCALEDIR); + textdomain("test"); + get_handle("en"); + $_ = N_("Hello, world!", "Cool!", "Big watermelon"); + return 1; +}; +# 36 +ok($r, 1); +# 37 +ok($_, "Hello, world!"); + +# Search system locale directories +($lang, $domain) = find_system_mo; +$skip = defined $domain? 0: 1; +$r = eval { + return if $skip; + use Locale::Maketext::Gettext::Functions; + textdomain($domain); + get_handle($lang); + $_ = maketext(""); + # Skip if $Lexicon{""} does not exists + $skip = 1 if $_ eq ""; + return 1; +}; +# 38 +skip($skip, $r, 1, $@); +# 39 +skip($skip, $_, qr/Project-Id-Version:/); diff --git a/t/09-f-encodings.t b/t/09-f-encodings.t new file mode 100755 index 0000000..14f711a --- /dev/null +++ b/t/09-f-encodings.t @@ -0,0 +1,282 @@ +#! /usr/bin/perl -w +# Test suite on the functional interface for different encodings +# Copyright (c) 2003-2007 imacat. All rights reserved. This program is free +# software; you can redistribute it and/or modify it under the same terms +# as Perl itself. + +use 5.008; +use strict; +use warnings; +use Test; + +BEGIN { plan tests => 34 } + +use Encode qw(); +use FindBin; +use File::Spec::Functions qw(catdir); +use lib $FindBin::Bin; +use vars qw($LOCALEDIR $r); +$LOCALEDIR = catdir($FindBin::Bin, "locale"); + +# Different encodings +# English +# Find the default encoding +$r = eval { + use Locale::Maketext::Gettext::Functions; + Locale::Maketext::Gettext::Functions::_reset(); + bindtextdomain("test", $LOCALEDIR); + textdomain("test"); + get_handle("en"); + $_ = encoding(); + return 1; +}; +# 1 +ok($r, 1); +# 2 +ok($_, "US-ASCII"); + +# Traditional Chinese +# Find the default encoding +$r = eval { + use Locale::Maketext::Gettext::Functions; + Locale::Maketext::Gettext::Functions::_reset(); + bindtextdomain("test", $LOCALEDIR); + textdomain("test"); + get_handle("zh-tw"); + $_ = encoding(); + return 1; +}; +# 3 +ok($r, 1); +# 4 +ok($_, "Big5"); + +# Turn to Big5 +$r = eval { + use Locale::Maketext::Gettext::Functions; + Locale::Maketext::Gettext::Functions::_reset(); + bindtextdomain("test", $LOCALEDIR); + textdomain("test"); + get_handle("zh-tw"); + encoding("Big5"); + $_ = maketext("Hello, world!"); + return 1; +}; +# 5 +ok($r, 1); +# 6 +ok($_, "大家好。"); + +# Turn to UTF-8 +$r = eval { + use Locale::Maketext::Gettext::Functions; + Locale::Maketext::Gettext::Functions::_reset(); + bindtextdomain("test", $LOCALEDIR); + textdomain("test"); + get_handle("zh-tw"); + encoding("UTF-8"); + $_ = maketext("Hello, world!"); + return 1; +}; +# 7 +ok($r, 1); +# 8 +ok($_, "憭批振憟賬"); + +# Turn to UTF-16LE +$r = eval { + use Locale::Maketext::Gettext::Functions; + Locale::Maketext::Gettext::Functions::_reset(); + bindtextdomain("test", $LOCALEDIR); + textdomain("test"); + get_handle("zh-tw"); + encoding("UTF-16LE"); + $_ = maketext("Hello, world!"); + return 1; +}; +# 9 +ok($r, 1); +# 10 +ok($_, "'Y跋}Y0"); + +# Find the default encoding, in UTF-8 +$r = eval { + use Locale::Maketext::Gettext::Functions; + Locale::Maketext::Gettext::Functions::_reset(); + bindtextdomain("test_utf8", $LOCALEDIR); + textdomain("test_utf8"); + get_handle("zh-tw"); + $_ = encoding(); + return 1; +}; +# 11 +ok($r, 1); +# 12 +ok($_, "UTF-8"); + +# Turn to UTF-8 +$r = eval { + use Locale::Maketext::Gettext::Functions; + Locale::Maketext::Gettext::Functions::_reset(); + bindtextdomain("test_utf8", $LOCALEDIR); + textdomain("test_utf8"); + get_handle("zh-tw"); + encoding("UTF-8"); + $_ = maketext("Hello, world!"); + return 1; +}; +# 13 +ok($r, 1); +# 14 +ok($_, "憭批振憟賬"); + +# Turn to Big5 +$r = eval { + use Locale::Maketext::Gettext::Functions; + Locale::Maketext::Gettext::Functions::_reset(); + bindtextdomain("test_utf8", $LOCALEDIR); + textdomain("test_utf8"); + get_handle("zh-tw"); + encoding("Big5"); + $_ = maketext("Hello, world!"); + return 1; +}; +# 15 +ok($r, 1); +# 16 +ok($_, "大家好。"); + +# Turn to UTF-16LE +$r = eval { + use Locale::Maketext::Gettext::Functions; + Locale::Maketext::Gettext::Functions::_reset(); + bindtextdomain("test_utf8", $LOCALEDIR); + textdomain("test_utf8"); + get_handle("zh-tw"); + encoding("UTF-16LE"); + $_ = maketext("Hello, world!"); + return 1; +}; +# 17 +ok($r, 1); +# 18 +ok($_, "'Y跋}Y0"); + +# Find the default encoding +# Simplified Chinese +$r = eval { + use Locale::Maketext::Gettext::Functions; + Locale::Maketext::Gettext::Functions::_reset(); + bindtextdomain("test_utf8", $LOCALEDIR); + textdomain("test_utf8"); + get_handle("zh-cn"); + $_ = encoding(); + return 1; +}; +# 19 +ok($r, 1); +# 20 +ok($_, "UTF-8"); + +# Turn to GB2312 +$r = eval { + use Locale::Maketext::Gettext::Functions; + Locale::Maketext::Gettext::Functions::_reset(); + bindtextdomain("test_utf8", $LOCALEDIR); + textdomain("test_utf8"); + get_handle("zh-cn"); + encoding("GB2312"); + $_ = maketext("Hello, world!"); + return 1; +}; +# 21 +ok($r, 1); +# 22 +ok($_, "湮模疑﹝"); + +# Encode failure +# FB_DEFAULT +$r = eval { + use Locale::Maketext::Gettext::Functions; + Locale::Maketext::Gettext::Functions::_reset(); + bindtextdomain("test2", $LOCALEDIR); + textdomain("test2"); + get_handle("zh-tw"); + encoding("GB2312"); + $_ = maketext("Every story has a happy ending."); + return 1; +}; +# 23 +ok($r, 1); +# 24 +ok($_, "嘟岈飲衄藝?腔?擁﹝"); + +# FB_CROAK +$r = eval { + use Locale::Maketext::Gettext::Functions; + Locale::Maketext::Gettext::Functions::_reset(); + bindtextdomain("test2", $LOCALEDIR); + textdomain("test2"); + get_handle("zh-tw"); + encoding("GB2312"); + encode_failure(Encode::FB_CROAK); + $_ = maketext("Every story has a happy ending."); + return 1; +}; +# 25 +ok($r, undef); +# 26 +ok($@, qr/does not map to/); + +# FB_HTMLCREF +$r = eval { + use Locale::Maketext::Gettext::Functions; + Locale::Maketext::Gettext::Functions::_reset(); + bindtextdomain("test2", $LOCALEDIR); + textdomain("test2"); + get_handle("zh-tw"); + encoding("GB2312"); + encode_failure(Encode::FB_HTMLCREF); + $_ = maketext("Every story has a happy ending."); + return 1; +}; +# 27 +ok($r, 1); +# 28 +ok($_, "嘟岈飲衄藝麗腔結擁﹝"); + +# Return the unencoded UTF-8 text +$r = eval { + use Locale::Maketext::Gettext::Functions; + Locale::Maketext::Gettext::Functions::_reset(); + bindtextdomain("test", $LOCALEDIR); + textdomain("test"); + get_handle("zh-tw"); + encoding(undef); + $_ = maketext("Hello, world!"); + return 1; +}; +# 29 +ok($r, 1); +# 30 +ok($_, "\x{5927}\x{5BB6}\x{597D}\x{3002}"); +# 31 +ok((Encode::is_utf8($_)? "utf8": "non-utf8"), "utf8"); + +# Return the unencoded UTF-8 text with auto lexicon +$r = eval { + use Locale::Maketext::Gettext::Functions; + Locale::Maketext::Gettext::Functions::_reset(); + bindtextdomain("test", $LOCALEDIR); + textdomain("test"); + get_handle("zh-tw"); + encoding(undef); + $_ = maketext("Big watermelon"); + return 1; +}; +# 32 +ok($r, 1); +# 33 +ok($_, "Big watermelon"); +# 34 +ok((Encode::is_utf8($_)? "utf8": "non-utf8"), "utf8"); diff --git a/t/10-f-switching.t b/t/10-f-switching.t new file mode 100755 index 0000000..ec750b4 --- /dev/null +++ b/t/10-f-switching.t @@ -0,0 +1,416 @@ +#! /usr/bin/perl -w +# Test suite on the functional interface for switching between different settings +# Copyright (c) 2003-2008 imacat. All rights reserved. This program is free +# software; you can redistribute it and/or modify it under the same terms +# as Perl itself. + +use 5.008; +use strict; +use warnings; +use Test; + +BEGIN { plan tests => 63 } + +use FindBin; +use File::Spec::Functions qw(catdir catfile); +use lib $FindBin::Bin; +use vars qw($LOCALEDIR $r); +$LOCALEDIR = catdir($FindBin::Bin, "locale"); +delete $ENV{$_} + foreach qw(LANGUAGE LC_ALL LC_CTYPE LC_COLLATE LC_MESSAGES LC_NUMERIC + LC_MONETARY LC_TIME LANG); + +# Switching between different settings +use File::Copy qw(copy); +use vars qw($dir1 $dir2 $dir3 $f1 $f11 $f12 $f2 $f21 $f3 $f31 $class); + +# dmaketext in the middle +$r = eval { + use Locale::Maketext::Gettext::Functions; + Locale::Maketext::Gettext::Functions::_reset(); + @_ = qw(); + get_handle("en"); + bindtextdomain("test", $LOCALEDIR); + bindtextdomain("test2", $LOCALEDIR); + textdomain("test"); + $_[0] = __("Hello, world!"); + $_[1] = pmaketext("Menu|File|", "Hello, world!"); + $_[2] = __("Every story has a happy ending."); + $_[3] = pmaketext("Menu|File|", "Every story has a happy ending."); + $_[4] = dmaketext("test2", "Hello, world!"); + $_[5] = dpmaketext("test2", "Menu|File|", "Hello, world!"); + $_[6] = dmaketext("test2", "Every story has a happy ending."); + $_[7] = dpmaketext("test2", "Menu|File|", "Every story has a happy ending."); + $_[8] = __("Hello, world!"); + $_[9] = pmaketext("Menu|File|", "Hello, world!"); + $_[10] = __("Every story has a happy ending."); + $_[11] = pmaketext("Menu|File|", "Every story has a happy ending."); + return 1; +}; +# 1 +ok($r, 1); +# 2 +ok($_[0], "Hiya :)"); +# 3 +ok($_[1], "Hiya :) under the File menu"); +# 4 +ok($_[2], "Every story has a happy ending."); +# 5 +ok($_[3], "Every story has a happy ending."); +# 6 +ok($_[4], "Hello, world!"); +# 6 +ok($_[5], "Hello, world!"); +# 8 +ok($_[6], "Pray it."); +# 9 +ok($_[7], "Pray it under the File menu"); +# 10 +ok($_[8], "Hiya :)"); +# 11 +ok($_[9], "Hiya :) under the File menu"); +# 12 +ok($_[10], "Every story has a happy ending."); +# 13 +ok($_[11], "Every story has a happy ending."); + +# Switch between domains +$r = eval { + use Locale::Maketext::Gettext::Functions; + Locale::Maketext::Gettext::Functions::_reset(); + @_ = qw(); + bindtextdomain("test", $LOCALEDIR); + bindtextdomain("test2", $LOCALEDIR); + get_handle("en"); + textdomain("test"); + $_[0] = __("Hello, world!"); + $_[1] = __("Every story has a happy ending."); + textdomain("test2"); + $_[2] = __("Hello, world!"); + $_[3] = __("Every story has a happy ending."); + textdomain("test"); + $_[4] = __("Hello, world!"); + $_[5] = __("Every story has a happy ending."); + return 1; +}; +# 14 +ok($r, 1); +# 15 +ok($_[0], "Hiya :)"); +# 16 +ok($_[1], "Every story has a happy ending."); +# 17 +ok($_[2], "Hello, world!"); +# 18 +ok($_[3], "Pray it."); +# 19 +ok($_[4], "Hiya :)"); +# 20 +ok($_[5], "Every story has a happy ending."); + +# Switch between languages +$r = eval { + use Locale::Maketext::Gettext::Functions; + Locale::Maketext::Gettext::Functions::_reset(); + @_ = qw(); + bindtextdomain("test", $LOCALEDIR); + textdomain("test"); + get_handle("en"); + $_[0] = __("Hello, world!"); + get_handle("zh-tw"); + $_[1] = __("Hello, world!"); + get_handle("zh-cn"); + $_[2] = __("Hello, world!"); + return 1; +}; +# 21 +ok($r, 1); +# 22 +ok($_[0], "Hiya :)"); +# 23 +ok($_[1], "大家好。"); +# 24 +ok($_[2], "湮模疑﹝"); + +# Switch between languages - by environment +$r = eval { + use Locale::Maketext::Gettext::Functions; + Locale::Maketext::Gettext::Functions::_reset(); + @_ = qw(); + bindtextdomain("test", $LOCALEDIR); + textdomain("test"); + $ENV{"LANG"} = "en"; + get_handle(); + $_[0] = __("Hello, world!"); + $ENV{"LANG"} = "zh-tw"; + get_handle(); + $_[1] = __("Hello, world!"); + $ENV{"LANG"} = "zh-cn"; + get_handle(); + $_[2] = __("Hello, world!"); + return 1; +}; +# 25 +ok($r, 1); +# 26 +ok($_[0], "Hiya :)"); +# 27 +ok($_[1], "大家好。"); +# 28 +ok($_[2], "湮模疑﹝"); + +# Switch between different language methods +$r = eval { + use Locale::Maketext::Gettext::Functions; + Locale::Maketext::Gettext::Functions::_reset(); + @_ = qw(); + bindtextdomain("test", $LOCALEDIR); + textdomain("test"); + get_handle("en"); + $_[0] = __("Hello, world!"); + $ENV{"LANG"} = "zh-tw"; + get_handle(); + $_[1] = __("Hello, world!"); + get_handle("zh-cn"); + $_[2] = __("Hello, world!"); + $ENV{"LANG"} = "en"; + get_handle(); + $_[3] = __("Hello, world!"); + return 1; +}; +# 29 +ok($r, 1); +# 30 +ok($_[0], "Hiya :)"); +# 31 +ok($_[1], "大家好。"); +# 32 +ok($_[2], "湮模疑﹝"); +# 33 +ok($_[3], "Hiya :)"); + +# Reuse of a same text domain class +$r = eval { + use Locale::Maketext::Gettext::Functions; + Locale::Maketext::Gettext::Functions::_reset(); + @_ = qw(); + $ENV{"LANG"} = "en"; + bindtextdomain("test", $LOCALEDIR); + textdomain("test"); + get_handle(); + $_[0] = __("Hello, world!"); + $_[1] = __("Every story has a happy ending."); + $_[2] = ref($Locale::Maketext::Gettext::Functions::LH); + $_[2] =~ s/^(.+)::.*?$/$1/; + + bindtextdomain("test2", $LOCALEDIR); + textdomain("test2"); + get_handle("zh-tw"); + $_[3] = __("Hello, world!"); + $_[4] = __("Every story has a happy ending."); + + bindtextdomain("test", "/dev/null"); + textdomain("test"); + get_handle("en"); + $_[5] = __("Hello, world!"); + $_[6] = __("Every story has a happy ending."); + + bindtextdomain("test", $LOCALEDIR); + textdomain("test"); + get_handle("zh-cn"); + $_[7] = __("Hello, world!"); + $_[8] = __("Every story has a happy ending."); + $_[9] = ref($Locale::Maketext::Gettext::Functions::LH); + $_[9] =~ s/^(.+)::.*?$/$1/; + return 1; +}; +# 34 +ok($r, 1); +# 35 +ok($_[0], "Hiya :)"); +# 36 +ok($_[1], "Every story has a happy ending."); +# 37 +ok($_[3], "Hello, world!"); +# 38 +ok($_[4], "故事都有美麗的結局。"); +# 39 +ok($_[5], "Hello, world!"); +# 40 +ok($_[6], "Every story has a happy ending."); +# 41 +ok($_[7], "湮模疑﹝"); +# 42 +ok($_[8], "Every story has a happy ending."); +# 43 +ok($_[2], $_[9]); + +# Language addition/removal +$r = eval { + use Locale::Maketext::Gettext::Functions; + Locale::Maketext::Gettext::Functions::_reset(); + @_ = qw(); + $dir1 = catdir($LOCALEDIR, "en", "LC_MESSAGES"); + $dir2 = catdir($LOCALEDIR, "zh_TW", "LC_MESSAGES"); + $dir3 = catdir($LOCALEDIR, "zh_CN", "LC_MESSAGES"); + $f1 = catfile($dir1, "test_dyn.mo"); + $f11 = catfile($dir1, "test.mo"); + $f2 = catfile($dir2, "test_dyn.mo"); + $f21 = catfile($dir2, "test.mo"); + $f3 = catfile($dir3, "test_dyn.mo"); + $f31 = catfile($dir3, "test.mo"); + unlink $f1; + unlink $f2; + unlink $f3; + + bindtextdomain("test_dyn", $LOCALEDIR); + textdomain("test_dyn"); + get_handle("zh-tw"); + $_[0] = __("Hello, world!"); + get_handle("zh-cn"); + $_[1] = __("Hello, world!"); + + copy $f21, $f2 or die "ERROR: $f21 $f2: $!"; + textdomain("test_dyn"); + get_handle("zh-tw"); + $_[2] = __("Hello, world!"); + get_handle("zh-cn"); + $_[3] = __("Hello, world!"); + + unlink $f2; + copy $f31, $f3 or die "ERROR: $f31 $f3: $!"; + textdomain("test_dyn"); + get_handle("zh-tw"); + $_[4] = __("Hello, world!"); + get_handle("zh-cn"); + $_[5] = __("Hello, world!"); + + copy $f21, $f2 or die "ERROR: $f21 $f2: $!"; + textdomain("test_dyn"); + get_handle("zh-tw"); + $_[6] = __("Hello, world!"); + get_handle("zh-cn"); + $_[7] = __("Hello, world!"); + + unlink $f2; + unlink $f3; + textdomain("test_dyn"); + get_handle("zh-tw"); + $_[8] = __("Hello, world!"); + get_handle("zh-cn"); + $_[9] = __("Hello, world!"); + + unlink $f1; + unlink $f2; + unlink $f3; + return 1; +}; +# 44 +ok($r, 1); +# 45 +ok($_[0], "Hello, world!"); +# 46 +ok($_[1], "Hello, world!"); +# 47 +ok($_[2], "大家好。"); +# 48 +ok($_[3], "Hello, world!"); +# 49 +ok($_[4], "Hello, world!"); +# 50 +ok($_[5], "湮模疑﹝"); +# 51 +ok($_[6], "大家好。"); +# 52 +ok($_[7], "湮模疑﹝"); +# 53 +ok($_[8], "Hello, world!"); +# 54 +ok($_[9], "Hello, world!"); + +# Garbage collection - drop abandoned language handles +$r = eval { + use Locale::Maketext::Gettext::Functions; + Locale::Maketext::Gettext::Functions::_reset(); + $dir1 = catdir($LOCALEDIR, "en", "LC_MESSAGES"); + $dir2 = catdir($LOCALEDIR, "zh_TW", "LC_MESSAGES"); + $dir3 = catdir($LOCALEDIR, "zh_CN", "LC_MESSAGES"); + $f1 = catfile($dir1, "test_dyn.mo"); + $f11 = catfile($dir1, "test.mo"); + $f2 = catfile($dir2, "test_dyn.mo"); + $f21 = catfile($dir2, "test.mo"); + $f3 = catfile($dir3, "test_dyn.mo"); + $f31 = catfile($dir3, "test.mo"); + unlink $f1; + unlink $f2; + unlink $f3; + + copy $f11, $f1 or die "ERROR: $f11 $f1: $!"; + copy $f21, $f2 or die "ERROR: $f21 $f2: $!"; + textdomain("test_dyn"); + get_handle("en"); + get_handle("zh-tw"); + get_handle("zh-cn"); + $class = ref($Locale::Maketext::Gettext::Functions::LH); + $class =~ s/^(.+)::.*?$/$1/; + + unlink $f2; + copy $f31, $f3 or die "ERROR: $f31 $f3: $!"; + textdomain("test_dyn"); + get_handle("en"); + get_handle("zh-tw"); + get_handle("zh-cn"); + @_ = grep /^$class/, keys %Locale::Maketext::Gettext::Functions::LHS; + return 1; +}; +# 55 +ok($r, 1); +# 56 +ok(scalar(@_), 0); + +# Reload the text +$r = eval { + $dir1 = catdir($LOCALEDIR, "en", "LC_MESSAGES"); + $f1 = catfile($dir1, "test_reload.mo"); + $f11 = catfile($dir1, "test.mo"); + $f12 = catfile($dir1, "test2.mo"); + unlink $f1; + copy $f11, $f1 or die "ERROR: $f11 $f1: $!"; + use Locale::Maketext::Gettext::Functions; + Locale::Maketext::Gettext::Functions::_reset(); + @_ = qw(); + bindtextdomain("test_reload", $LOCALEDIR); + textdomain("test_reload"); + get_handle("en"); + $_[0] = __("Hello, world!"); + $_[1] = __("Every story has a happy ending."); + unlink $f1; + copy $f12, $f1 or die "ERROR: $f12 $f1: $!"; + $_[2] = __("Hello, world!"); + $_[3] = __("Every story has a happy ending."); + reload_text; + $_[4] = __("Hello, world!"); + $_[5] = __("Every story has a happy ending."); + unlink $f1; + return 1; +}; +# 57 +ok($r, 1); +# 58 +ok($_[0], "Hiya :)"); +# 59 +ok($_[1], "Every story has a happy ending."); +# 60 +ok($_[2], "Hiya :)"); +# 61 +ok($_[3], "Every story has a happy ending."); +# 62 +ok($_[4], "Hello, world!"); +# 63 +ok($_[5], "Pray it."); + +# Garbage collection +unlink catfile($LOCALEDIR, "en", "LC_MESSAGES", "test_dyn.mo"); +unlink catfile($LOCALEDIR, "zh_TW", "LC_MESSAGES", "test_dyn.mo"); +unlink catfile($LOCALEDIR, "zh_CN", "LC_MESSAGES", "test_dyn.mo"); +unlink catfile($LOCALEDIR, "en", "LC_MESSAGES", "test_reload.mo"); diff --git a/t/11-command-line.t b/t/11-command-line.t new file mode 100755 index 0000000..735ce61 --- /dev/null +++ b/t/11-command-line.t @@ -0,0 +1,92 @@ +#! /usr/bin/perl -w +# Test suite on the maketext script +# Copyright (c) 2003-2007 imacat. All rights reserved. This program is free +# software; you can redistribute it and/or modify it under the same terms +# as Perl itself. + +use 5.008; +use strict; +use warnings; +use Test; + +BEGIN { plan tests => 10 } + +use FindBin; +use File::Spec::Functions qw(catdir catfile updir); +use lib $FindBin::Bin; +use vars qw($LOCALEDIR $r $maketext); +$LOCALEDIR = catdir($FindBin::Bin, "locale"); +$maketext = catdir($FindBin::Bin, updir, "blib", "script", "maketext"); + +# The maketext script +# Ordinary text unchanged +$r = eval { + delete $ENV{"LANG"}; + delete $ENV{"LANGUAGE"}; + delete $ENV{"TEXTDOMAINDIR"}; + delete $ENV{"TEXTDOMAIN"}; + @_ = `"$maketext" "Hello, world!"`; + return 1; +}; +# 1 +ok($r, 1); +# 2 +ok($_[0], "Hello, world!"); + +# Specify the text domain by the -d argument +# English +$r = eval { + $ENV{"LANG"} = "C"; + $ENV{"LANGUAGE"} = "C"; + $ENV{"TEXTDOMAINDIR"} = $LOCALEDIR; + delete $ENV{"TEXTDOMAIN"}; + @_ = `"$maketext" -d test "Hello, world!"`; + return 1; +}; +# 3 +ok($r, 1); +# 4 +ok($_[0], "Hiya :)"); + +# Specify the text domain by the environment variable +# English +$r = eval { + $ENV{"LANG"} = "C"; + $ENV{"LANGUAGE"} = "C"; + $ENV{"TEXTDOMAINDIR"} = $LOCALEDIR; + $ENV{"TEXTDOMAIN"} = "test"; + @_ = `"$maketext" "Hello, world!"`; + return 1; +}; +# 5 +ok($r, 1); +# 6 +ok($_[0], "Hiya :)"); + +# The -s argument +$r = eval { + $ENV{"LANG"} = "C"; + $ENV{"LANGUAGE"} = "C"; + $ENV{"TEXTDOMAINDIR"} = $LOCALEDIR; + $ENV{"TEXTDOMAIN"} = "test"; + @_ = `"$maketext" -s "Hello, world!"`; + return 1; +}; +# 7 +ok($r, 1); +# 8 +ok($_[0], "Hiya :)\n"); + +# Maketext +$r = eval { + $ENV{"LANG"} = "C"; + $ENV{"LANGUAGE"} = "C"; + $ENV{"TEXTDOMAINDIR"} = $LOCALEDIR; + $ENV{"TEXTDOMAIN"} = "test"; + @_ = `"$maketext" -s "[*,_1,directory,directories]" 5`; + return 1; +}; +# 9 +ok($r, 1); +# 10 +ok($_[0], "5 directories\n"); diff --git a/t/99-pod.t b/t/99-pod.t new file mode 100755 index 0000000..9622b2f --- /dev/null +++ b/t/99-pod.t @@ -0,0 +1,5 @@ +#!/usr/bin/perl +use Test::More; +eval "use Test::Pod 1.00"; +plan skip_all => "Test::Pod 1.00 required for testing POD" if $@; +all_pod_files_ok(); diff --git a/t/T_L10N.pm b/t/T_L10N.pm new file mode 100644 index 0000000..2aa1fdf --- /dev/null +++ b/t/T_L10N.pm @@ -0,0 +1,24 @@ +# Test localization class and its subclasses +# Copyright (c) 2003 imacat. All rights reserved. This program is free +# software; you can redistribute it and/or modify it under the same terms +# as Perl itself. + +package T_L10N; +use base qw(Locale::Maketext::Gettext); + +return 1; + +package T_L10N::en; +use base qw(Locale::Maketext::Gettext); + +return 1; + +package T_L10N::zh_tw; +use base qw(Locale::Maketext::Gettext); + +return 1; + +package T_L10N::zh_cn; +use base qw(Locale::Maketext::Gettext); + +return 1; diff --git a/t/locale/C/LC_MESSAGES/test.mo b/t/locale/C/LC_MESSAGES/test.mo new file mode 100644 index 0000000..6803e36 Binary files /dev/null and b/t/locale/C/LC_MESSAGES/test.mo differ diff --git a/t/locale/en/LC_MESSAGES/bad.mo b/t/locale/en/LC_MESSAGES/bad.mo new file mode 100644 index 0000000..e70fee9 --- /dev/null +++ b/t/locale/en/LC_MESSAGES/bad.mo @@ -0,0 +1 @@ +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 \ No newline at end of file diff --git a/t/locale/en/LC_MESSAGES/test.mo b/t/locale/en/LC_MESSAGES/test.mo new file mode 100644 index 0000000..6803e36 Binary files /dev/null and b/t/locale/en/LC_MESSAGES/test.mo differ diff --git a/t/locale/en/LC_MESSAGES/test2.mo b/t/locale/en/LC_MESSAGES/test2.mo new file mode 100644 index 0000000..0ca4111 Binary files /dev/null and b/t/locale/en/LC_MESSAGES/test2.mo differ diff --git a/t/locale/en/LC_MESSAGES/test_be.mo b/t/locale/en/LC_MESSAGES/test_be.mo new file mode 100644 index 0000000..629aade Binary files /dev/null and b/t/locale/en/LC_MESSAGES/test_be.mo differ diff --git a/t/locale/en/LC_MESSAGES/test_utf8.mo b/t/locale/en/LC_MESSAGES/test_utf8.mo new file mode 100644 index 0000000..9e6d563 Binary files /dev/null and b/t/locale/en/LC_MESSAGES/test_utf8.mo differ diff --git a/t/locale/zh_CN/LC_MESSAGES/test.mo b/t/locale/zh_CN/LC_MESSAGES/test.mo new file mode 100644 index 0000000..9d9975a Binary files /dev/null and b/t/locale/zh_CN/LC_MESSAGES/test.mo differ diff --git a/t/locale/zh_CN/LC_MESSAGES/test_be.mo b/t/locale/zh_CN/LC_MESSAGES/test_be.mo new file mode 100644 index 0000000..3eeb488 Binary files /dev/null and b/t/locale/zh_CN/LC_MESSAGES/test_be.mo differ diff --git a/t/locale/zh_CN/LC_MESSAGES/test_utf8.mo b/t/locale/zh_CN/LC_MESSAGES/test_utf8.mo new file mode 100644 index 0000000..b1e5344 Binary files /dev/null and b/t/locale/zh_CN/LC_MESSAGES/test_utf8.mo differ diff --git a/t/locale/zh_TW/LC_MESSAGES/test.mo b/t/locale/zh_TW/LC_MESSAGES/test.mo new file mode 100644 index 0000000..49e3465 Binary files /dev/null and b/t/locale/zh_TW/LC_MESSAGES/test.mo differ diff --git a/t/locale/zh_TW/LC_MESSAGES/test2.mo b/t/locale/zh_TW/LC_MESSAGES/test2.mo new file mode 100644 index 0000000..d9cc647 Binary files /dev/null and b/t/locale/zh_TW/LC_MESSAGES/test2.mo differ diff --git a/t/locale/zh_TW/LC_MESSAGES/test_be.mo b/t/locale/zh_TW/LC_MESSAGES/test_be.mo new file mode 100644 index 0000000..b5f74fc Binary files /dev/null and b/t/locale/zh_TW/LC_MESSAGES/test_be.mo differ diff --git a/t/locale/zh_TW/LC_MESSAGES/test_utf8.mo b/t/locale/zh_TW/LC_MESSAGES/test_utf8.mo new file mode 100644 index 0000000..670ead7 Binary files /dev/null and b/t/locale/zh_TW/LC_MESSAGES/test_utf8.mo differ