[Rt-commit] r4934 - in Locale-Maketext-Lexicon: . docs inc
inc/Module/Install lib lib/Locale lib/Locale/Maketext
lib/Locale/Maketext/Extract lib/Locale/Maketext/Lexicon
script t t/locale t/locale/en t/locale/en/LC_MESSAGES
t/locale/zh_CN t/locale/zh_CN/LC_MESSAGES t/locale/zh_TW
t/locale/zh_TW/LC_MESSAGES
autrijus at bestpractical.com
autrijus at bestpractical.com
Sun Apr 2 05:40:31 EDT 2006
Author: autrijus
Date: Sun Apr 2 05:40:20 2006
New Revision: 4934
Added:
Locale-Maketext-Lexicon/AUTHORS
Locale-Maketext-Lexicon/Changes
Locale-Maketext-Lexicon/Makefile.PL
Locale-Maketext-Lexicon/README
Locale-Maketext-Lexicon/docs/
Locale-Maketext-Lexicon/docs/webl10n.html (contents, props changed)
Locale-Maketext-Lexicon/docs/webl10n.zh-tw.html (contents, props changed)
Locale-Maketext-Lexicon/inc/
Locale-Maketext-Lexicon/inc/Module/
Locale-Maketext-Lexicon/inc/Module/Install/
Locale-Maketext-Lexicon/inc/Module/Install.pm
Locale-Maketext-Lexicon/inc/Module/Install/Base.pm
Locale-Maketext-Lexicon/inc/Module/Install/Can.pm
Locale-Maketext-Lexicon/inc/Module/Install/Fetch.pm
Locale-Maketext-Lexicon/inc/Module/Install/Makefile.pm
Locale-Maketext-Lexicon/inc/Module/Install/Metadata.pm
Locale-Maketext-Lexicon/inc/Module/Install/Scripts.pm
Locale-Maketext-Lexicon/inc/Module/Install/Win32.pm
Locale-Maketext-Lexicon/inc/Module/Install/WriteAll.pm
Locale-Maketext-Lexicon/lib/
Locale-Maketext-Lexicon/lib/Locale/
Locale-Maketext-Lexicon/lib/Locale/Maketext/
Locale-Maketext-Lexicon/lib/Locale/Maketext/Extract/
Locale-Maketext-Lexicon/lib/Locale/Maketext/Extract.pm
Locale-Maketext-Lexicon/lib/Locale/Maketext/Extract/Run.pm
Locale-Maketext-Lexicon/lib/Locale/Maketext/Lexicon/
Locale-Maketext-Lexicon/lib/Locale/Maketext/Lexicon.pm
Locale-Maketext-Lexicon/lib/Locale/Maketext/Lexicon/Auto.pm
Locale-Maketext-Lexicon/lib/Locale/Maketext/Lexicon/Gettext.pm
Locale-Maketext-Lexicon/lib/Locale/Maketext/Lexicon/Msgcat.pm
Locale-Maketext-Lexicon/lib/Locale/Maketext/Lexicon/Tie.pm
Locale-Maketext-Lexicon/script/
Locale-Maketext-Lexicon/script/xgettext.pl
Locale-Maketext-Lexicon/t/
Locale-Maketext-Lexicon/t/1-basic.t (contents, props changed)
Locale-Maketext-Lexicon/t/2-lmg.t (contents, props changed)
Locale-Maketext-Lexicon/t/3-big-endian.t (contents, props changed)
Locale-Maketext-Lexicon/t/4-encodings.t (contents, props changed)
Locale-Maketext-Lexicon/t/5-extract.t
Locale-Maketext-Lexicon/t/T_L10N.pm
Locale-Maketext-Lexicon/t/gencat.m
Locale-Maketext-Lexicon/t/locale/
Locale-Maketext-Lexicon/t/locale/en/
Locale-Maketext-Lexicon/t/locale/en/LC_MESSAGES/
Locale-Maketext-Lexicon/t/locale/en/LC_MESSAGES/test.mo (contents, props changed)
Locale-Maketext-Lexicon/t/locale/en/LC_MESSAGES/test_be.mo (contents, props changed)
Locale-Maketext-Lexicon/t/locale/en/LC_MESSAGES/test_utf8.mo (contents, props changed)
Locale-Maketext-Lexicon/t/locale/zh_CN/
Locale-Maketext-Lexicon/t/locale/zh_CN/LC_MESSAGES/
Locale-Maketext-Lexicon/t/locale/zh_CN/LC_MESSAGES/test.mo (contents, props changed)
Locale-Maketext-Lexicon/t/locale/zh_CN/LC_MESSAGES/test_be.mo (contents, props changed)
Locale-Maketext-Lexicon/t/locale/zh_CN/LC_MESSAGES/test_utf8.mo (contents, props changed)
Locale-Maketext-Lexicon/t/locale/zh_TW/
Locale-Maketext-Lexicon/t/locale/zh_TW/LC_MESSAGES/
Locale-Maketext-Lexicon/t/locale/zh_TW/LC_MESSAGES/test.mo (contents, props changed)
Locale-Maketext-Lexicon/t/locale/zh_TW/LC_MESSAGES/test_be.mo (contents, props changed)
Locale-Maketext-Lexicon/t/locale/zh_TW/LC_MESSAGES/test_utf8.mo (contents, props changed)
Locale-Maketext-Lexicon/t/messages.mo (contents, props changed)
Locale-Maketext-Lexicon/t/messages.po
Log:
* Locale::Maketext::Lexicon: Specifying the new "_auto => 1" option
now turns on _AUTO fallback handling for all language handles.
Requested by: Jesse Vincent
Added: Locale-Maketext-Lexicon/AUTHORS
==============================================================================
--- (empty file)
+++ Locale-Maketext-Lexicon/AUTHORS Sun Apr 2 05:40:20 2006
@@ -0,0 +1,29 @@
+[Credits]
+
+Here is a list of people and their CPAN id, extracted from the ChangeLog
+file and the mailing list archives. These people have either submitted
+patches or suggestions, or their bug reports or comments have inspired
+the appropriate patches. Corrections, additions, deletions welcome:
+
+Alain Barbet (ALIAN)
+Brian Cassidy (BRICAS)
+Christian Hansen
+Gaal Yahas (GAAL)
+Harmen
+Helmut Lichtenberg
+Hsin-Chan Chien (HCCHIEN)
+Jesse Vincent (JESSE)
+Jiing Deng
+Joe McCarty
+Macpaul Lin
+Mathieu Arnold (MAT)
+Mikael Sennerholm
+Noelle Christian
+Robert Spier (ROBRT)
+Sava Chankov (SAVA)
+Sean M. Burke (SBURKE)
+Stephen Quinney (SJQUINNEY)
+Tatsuhiko Miyagawa (MIYAGAWA)
+Thierry Vignaud
+Wei-Hon Chen
+Yi Ma Mao (IMACAT)
Added: Locale-Maketext-Lexicon/Changes
==============================================================================
--- (empty file)
+++ Locale-Maketext-Lexicon/Changes Sun Apr 2 05:40:20 2006
@@ -0,0 +1,327 @@
+[Changes for 0.56 - 2006-04-02]
+
+* Locale::Maketext::Lexicon: Specifying the new "_auto => 1" option
+ now turns on _AUTO fallback handling for all language handles.
+ Requested by: Jesse Vincent
+
+[Changes for 0.55 - 2006-03-22]
+
+* Locale::Maketext::Extract: Fix a long-standing bug that caused
+ double-escaping of backslashes and double quotes in loc strings.
+ Contributed by: Brian Cassidy
+
+[Changes for 0.54 - 2006-03-14]
+
+* When importing into an already defined lexicon and merging with its
+ entries, we need to clear the Locale::Maketext cache, otherwise
+ _AUTO entries generated by the old lexicon will linger around.
+ Contributed by: Helmut Lichtenberg
+
+[Changes for 0.53 - 2005-12-05]
+
+* Make xgettext.pl work with poEdit by not trying to read from empty
+ .po files. Patch from Brian Cassidy.
+
+[Changes for 0.52 - 2005-12-04]
+
+* Christian Hansen pointed out 0.51's fix was bass ackwards.
+
+[Changes for 0.51 - 2005-12-03]
+
+* Locale::Maketext::Extract::Run's use of File::Find::find with
+ (follow => 1) may break on systems without symbolic links.
+ Reported by Brian Cassidy.
+
+[Changes for 0.50 - 2005-11-04]
+
+* Per suggestion from Sava Chankov, add the (quite outdated) webl10n
+ articles to docs/ directory.
+
+Patches from Mikael Sennerholm:
+
+* Added "localize" and "localise" to the list of recognized L10N
+ functions in Locale::Maketext::Extract.
+
+* Skip .svn directories in Locale::Maketext::Extract::Run.
+
+[Changes for 0.49 - 2005-04-13]
+
+* Stephen Quinney notes that we are too eager to die on nonexisting
+ files passed in as part of a lexicon_get_() source. We now silently
+ ignore nonexistant files, only reporting error when it exists but
+ cannot be read or parsed.
+
+[Changes for 0.48 - 2005-03-12]
+
+Patches from Thierry Vignaud:
+
+* In Gettext.pm, do not silently skip last entry of a catalog.
+
+* In Extract.pm, do not silently skip first entry of a catalog
+ if there's only one empty line tween header and first real message.
+
+[Changes for 0.47 - 2005-02-03]
+
+* EXISTS in Locale::Maketext::Lexicon has the potential to trigger deep
+ recursion. Reported by Wei-Hon Chen.
+
+[Changes for 0.46 - 2004-12-16]
+
+* Patch from Thierry Vignaud: Add a "_allow_empty" option to the
+ Gettext backend, to allow empty strings with
+
+* Harmen suggested a doc patch to better document the _AUTO key
+ for allowing empty or missing keys.
+
+* Changed all ::L10N to ::I18N in examples, to agree with the de
+ facto community standard.
+
+[Changes for 0.45 - 2004-10-26]
+
+* Fix decoding problem on Big5 containing "]", as reported by Jiing Deng.
+
+* For \*DATA calls, we can get the 'main' package from $0, skip tracing the
+ caller chain.
+
+[Changes for 0.44 - 2004-09-25]
+
+* Fixed a regression from 0.43 that prevented %*(...) interpolations.
+
+* Remove RCS headers from source files..
+
+[Changes for 0.43 - 2004-09-24]
+
+* Fix decoding problem on Big5 containing "\".
+
+[Changes for 0.42 - 2004-08-25]
+
+* Devel::DProf did not like the closure-based delay loading mechanism;
+ rewrite using OO-based promises instead.
+
+[Changes for 0.41 - 2004-08-24]
+
+* Delayed loading of lexicons now repects the original options.
+
+* Delay actualization of lexicon parsing until the first use.
+ (Suggested by Jesse Vincent)
+
+[Changes for 0.40 - 2004-08-22]
+
+* Moved under SVK management.
+
+* The 'locale' encoding now works on Win32, using
+ Win32::Console::OutputCP() to probe the code page.
+
+[Changes for 0.38 - 2004-04-26]
+
+* The quotemeta() introduced in 0.37 broke Locale::Maketext::Simple.
+
+* L::M::Extract now parses the '# loc' and '# loc_pair'
+ markers before end of lines, as used in RT.
+
+* New module, L::M::Extract::Run, that encapsulates the
+ xgettext.pl script.
+
+[Changes for 0.37 - 2004-04-21]
+
+* Thanks Christian Hansen for reminding me of the
+ quotemeta() bug.
+
+* Forgot to put quotemeta() around dir patterns; pointed
+ out by Jouke Visser. This caused tests on Win32 to fail.
+
+* Respect the LAGUAGES and LC_MESSAGES environment variables.
+
+[Changes for 0.36 - 2004-03-17]
+
+* "_encoding" now takes a special "locale" value, which will
+ get the encoding from user's locale.
+
+* Gettext used to break when the re-encoded lexicon
+ contains Maketext's escape characters like "[" and "~".
+
+* Extract.pm should not prepend an empty "" before a
+ single-line "string\n". Such treatments are meant for
+ multiline strings.
+
+* proper escaping of \ and " in _format.
+
+[Changes for 0.35 - 2004-02-19]
+
+* We no longer depend on Regexp::Common and Pod::Usage.
+
+* Extract strings inside functions that has spaces inside
+ the parens, like 'loc( "test" )', as suggested by Helmut
+ Lichtenberg.
+
+[Changes for 0.34 - 2003-12-31]
+
+* Update copyright years to 2004.
+
+* Fix Msgcat so it also handle CRLF gracefully.
+
+* .po files with DOSish CRLF line-ending was not parsed correctly.
+
+[Changes for 0.33 - 2003-12-08]
+
+* Multiline strings were produced with an extra "\n" by xgettext.pl.
+
+* "xgettext.pl -u" was broken.
+
+* Various documentation and error message fixes.
+
+[Changes for 0.32 - 2003-10-14]
+
+* Excise $& from ::Extract, ::Msgcat and ::Gettext.
+
+* Micro-optimize (encode|decode)_utf8 in the inner loop of
+ ::Gettext instead of (encode|decode)('utf8', ...).
+
+[Changes for 0.31 - 2003-10-13]
+
+* 0.30 still broken on Robert Spiers's darwin box, so
+rewrote them to totally avoid capturing parenthesis.
+
+* Also modify Extract.pm to be not reentrant.
+
+[Changes for 0.30 - 2003-10-13]
+
+* '*' in sources will now get properlly expanded by globbing.
+
+* The reentrant-regex transformation used in ::Gettext was
+ breaking 5.8.0 in subtle ways. Reimplemented to use a two-pass
+ approach.
+
+[Changes for 0.29 - 2003-10-09]
+
+* Update readme and pods to mention ::Extract.
+
+* Add test for extraction.
+
+* Refactor xgettext.pl out into Locale::Maketext::Extract.
+
+* Move bin/ into script/.
+
+[Changes for 0.28 - 2003-08-28]
+
+* Gettext was chomping everything. Fixed.
+
+* Support for Kwiki-style generic loc string: {{...}}
+
+[Changes for 0.27 - 2003-07-07]
+
+* Change tests to use utf8, not big5, for perl 5.6.1
+ people with broken Locale::Maketext (1.06).
+
+* Supress uninitialized warnings.
+
+* Jonas Lincoln reported that empty globs was causing problems.
+
+* Strip the extra parens off tt2 templates.
+
+* Be very extra magical and assume that main:: comes from
+ the caller if it is not yet opened.
+
+* Chia-Liang Kao pointed out that getting sources from glob
+ was not working.
+
+* "xgettext.pl -g" was documented but never enabled.
+
+* Fixed xgettext.pl's multiline handling.
+
+* TT2 regex was wrong; thanks Chia-Liang Kao for pointing out.
+
+[Changes for 0.26 - 2003-05-03]
+
+* Incoporated a modified version of Matthieu Arnold's patch
+ that enables Encode-specific testing for Perl 5.6.1 with
+ Encode::compat.
+
+[Changes for 0.25 - 2003-04-30]
+
+* Change t/TestPkg/L10N.pm to t/T_L10N.pm in accord with
+ imacat's arrangement.
+
+* Fixes cases where there are multiple '*'s in the glob entry.
+
+[Changes for 0.22 - 2003-04-28]
+
+* Document the choice of using the final '*' for
+ glob patterns that has multiple '*'s.
+
+* Add extra debug info.
+
+* prefer the last * for globbing languages.
+
+[Changes for 0.21 - 2003-04-27]
+
+* Remove unneccessary MO files.
+
+* Updates copyrights to 2003.
+
+* Mathieu Arnold pointed out that multiline handling was incorrect.
+
+* Implemented correct parsing of multiple "#, flag1, flag2".
+
+* "#, fuzzy" entries are now ignored, unless _use_fuzzy is
+ set to true.
+
+* Documentation fix: removed the ambiguous [ ... ] by imacat's request.
+
+* Filehandle sources are now seek()ed back to the original
+ position after reading; this allows using the same handle
+ for two language subclasses.
+
+[Changes for 0.20 - 2003-04-26]
+
+* Add a note that we now turns 'zh-tw' into 'zh_tw' during
+ import. this fixed imacat's major gripe with previous
+ versions of L::M::L.
+
+* Nixed the TODO test since it's not really on my todo.
+
+* Add copyright notice around parse_mo() by imacat's request.
+
+* Test suite from imacat's Locale::Maketext::Gettext.
+
+* Documenting the new _encoding, _decode and wildcard support.
+
+* Add IMACAT to authors and acknowledgement list.
+
+* New _decode and _encoding flag to control auto-recoding.
+
+* Globbing support for import lists.
+
+[Changes for 0.16 - 2003-02-22]
+
+* Gaal Yahas's patch to recognize __ in addition of _.
+
+* Wei-Hon Chen pointed out pod errors.
+
+[Changes for 0.15 - 2002-11-29]
+
+* Corrected %quant(,%1) -- should be %quant(%1).
+
+* POD tweaks.
+
+* Random fixes from Alain Barbet.
+
+* Merging from old lexicons now work properly with and without -u.
+
+[Changes for 0.14 - 2002-11-13]
+
+* Escapes are now applicable even with -u.
+
+[Changes for 0.13 - 2002-11-13]
+
+* Alain's parser patch. This makes multi-line handling a breeze.
+
+[Changes for 0.12 - 2002-11-07]
+
+* Patch from Alain Barbett: Fix multiline gettext strings.
+
+* Document "xgettext.pl -u" before I forget about it.
+
+[Changes for 0.11 - 2002-10-27]
+
+* First sane CPAN release version.
Added: Locale-Maketext-Lexicon/Makefile.PL
==============================================================================
--- (empty file)
+++ Locale-Maketext-Lexicon/Makefile.PL Sun Apr 2 05:40:20 2006
@@ -0,0 +1,17 @@
+#!/usr/bin/perl
+
+use 5.005;
+use inc::Module::Install;
+
+author ('Audrey Tang <audreyt at audreyt.org>');
+abstract ('Use other catalog formats in Maketext');
+name ('Locale-Maketext-Lexicon');
+version_from ('lib/Locale/Maketext/Lexicon.pm',);
+license ('perl');
+install_script ('script/xgettext.pl');
+requires (
+ 'Test::More' => '0.01',
+ 'Locale::Maketext' => '0.01',
+);
+
+WriteAll( sign => 1 );
Added: Locale-Maketext-Lexicon/README
==============================================================================
--- (empty file)
+++ Locale-Maketext-Lexicon/README Sun Apr 2 05:40:20 2006
@@ -0,0 +1,32 @@
+This is the README file for Locale::Maketext::Lexicon, a module providing
+lexicon-handling backends, for "Locale::Maketext" to read from other
+localization formats, such as PO files, MO files, or from databases via
+the "Tie" interface.
+
+For extracting translatable strings from source files, a "xgettext.pl"
+utility is also installed by default.
+
+You can also read my presentation "Web Localization in Perl" in the docs/
+directory. It gives an overview for the localization process, features
+a comparison between Gettext, Msgcat and Maketext, and talks about my
+experiences at localizing web applications based on HTML::Mason and the
+Template Toolkit.
+
+* Installation
+
+Locale::Maketext::Lexicon uses the standard perl module install process:
+
+ cpansign -v # optional; see the SIGNATURE file for details
+ perl Makefile.PL
+ make # or 'nmake' on Win32
+ make test
+ make install
+
+* Copyright
+
+Copyright 2002-2006 by Audrey Tang <audreyt at audreyt.org>.
+
+All rights reserved. You can redistribute and/or modify
+this bundle under the same terms as Perl itself.
+
+See <http://www.perl.com/perl/misc/Artistic.html>.
Added: Locale-Maketext-Lexicon/docs/webl10n.html
==============================================================================
--- (empty file)
+++ Locale-Maketext-Lexicon/docs/webl10n.html Sun Apr 2 05:40:20 2006
@@ -0,0 +1,777 @@
+<html><head><title>Web Localization in Perl</title>
+<style><!--
+P {text-align: justify}
+P.right {text-align: right}
+--></style></head><body>
+
+<h1>Web Localization in Perl</h1>
+<p class="right" align="right">
+Audrey Tang<br>
+OurInternet, Inc.<br>
+July 2002
+
+<p>
+<h2>Abstract</h2>
+
+<p>
+The practice of internationalization (i18n) enables applications to support multiple languages, date/currency formats and local customs (collectively known as <em>locales</em>); localization (L10n) then deals with the actual implementation of fitting the software into the needs of users in a certain locale. Today, <em>Web applications</em> are one of the key areas that's being massively localized, due to its nature of text-based interface representation formats.
+<p>
+In the Free Software world, many of the most flexible and widely-used technologies are built upon the <em>Perl</em> language, which has long been the language of choice for web application developers. This article presents the author's hands-on experience on localizing several Perl-based applications into Chinese, the detailed usage and pitfalls of common frameworks, as well as <em>best practice</em> tips for managing a localization project.
+
+<h2>Introduction</h2>
+
+<p class="right" align="right">
+<i>``There are a number of languages spoken by human beings in this world.''<br>-- Harald Tveit Alvestrand, in RFC 1766, ``Tags for the Identification of Languages''</i>
+<p>
+Why should someone localize their websites or web applications?
+<p>
+Let us imagine this very question being debated on the Web, with convincing arguments and explanations raised by various parties, in different languages. As a participant in this discussion, you may hear following points being made:
+
+<p align="center">
+<table border=2 align=center>
+<caption align=bottom><font size=-1>Figure 1: Reasons for Localization (<em>before</em> localization)</font></caption>
+<tr><td><ul>
+<li>ÐноÑÑÑÐ°Ð½Ð½Ð°Ñ Ð²Ð°Ð»ÑÑа, ÑоÑÐ¼Ð°Ñ Ð´Ð°ÑÑ, ÑзÑк и обÑÑаи могÑÑ ÐºÐ°Ð·Ð°ÑÑÑÑ Ð½Ð°Ð¼ пÑгаÑÑими
+<li>Menschen sind produktiver wenn sie in ihrer gewohnten Umgebung arbeiten
+<li>Tas veicina daudz labâku sapraðanu un mijiedarbîbu starp daâdâm kultrâm
+<li>Un progretto con molti collaboratori internazionali si evolverá piú in fretta e meglio
+<li>å°åºåçè¿ç¨, æå©æ¼è½¯ä»¶ç模ååä¸å¯ç§»æ¤æ§
+</ul></td></tr></table>
+
+<p>
+But, alas, it is not likely that all parties could understand all these points. This fact had naturally lead to the effect of <em>language barrier</em> -- our field of meaningful discussion are often restricted to a few <em>locale groups</em>: people who speak the same language and share the same culture with us.
+<p>
+However, that is truly sad since the arguments we missed are often valid ones, and usually offer new insights into our present condition. Therefore, it will be truly beneficial if the arguments, interfaces and other texts are translated for us:
+
+<p align="center">
+<table border=2 align=center>
+<caption align=bottom><font size=-1>Figure 2: Reasons for Localization (<em>localized</em> to English)</font></caption>
+<tr><td><ul>
+<li>It is a distraction to have to deal with interfaces that use foreign languages, date formats, currencies and customs
+<li>People are more productive when they operate in their native environments
+<li>It fosters understanding and communication between cultures
+<li>Projects with more international contributors will evolve faster and better
+<li>Localization tends to improve the software's modularity and portability
+</ul></td></tr></table>
+
+<p>
+As these arguments have pointed out, it is often not possible nor desirable to <em>just speak X</em>, be it Latin, Interlingua, Esperanto, Lojban or, well, English. At such times, localization (L10n) is needed.
+
+<p>
+For proprietary applications, L10n was typically done as a prerequisite of competing in a foreign market. That implies if the localization cost exceeds estimated profit in a given locale, the company would not localize its application at all, and it would be difficult (and maybe illegal) for users to do it themselves without the source code. If the vendor did not design its software with good i18n framework in mind -- well, then we're just out of luck.
+
+<p>
+Fortunately, the case is much simpler and rewarding with open-source applications. As with proprietary ones, the first few versions are often designed with only one locale in mind; but the difference is <em>anybody</em> is allowed to internationalize it <em>at any time</em>. As Sean M. Burke put it:
+
+<p>
+<blockquote>
+ The way that we can do better in open source is by writing our software
+ with the goal that localization should be easy both for the programmers
+ and maybe even for eager users. (After all, practically the definition
+ of "open source" is that it lets anyone be a programmer, if they are
+ interested enough and skilled enough.)
+</blockquote>
+
+<p>
+This article describes detailed techniques to make L10n easy for all parties involved. I will focus on <em>web-based</em> applications written in the <em>Perl</em> language, but the principle should also apply elsewhere.
+
+<h2>Localizing Static Websites</h2>
+<p class="right" align="right">
+<i>``It Has To Work.''<br>
+-- First Networking Truth, RFC 1925</i>
+
+<p>
+Web pages come in two different flavors: <em>static</em> ones provides the same content during many visits, until it is updated; <em>dynamic</em> pages may offer different information depends on various factors. They are commonly referred as <em>web documents</em> and <em>web applications</em>.
+<p>
+However, being static does not mean that all visitors must see the same <em>representation</em> -- different people may prefer different languages, styles or medium (e.g. via auditory outputs instead of visual ones). Part of the Web's strength is its ability to let the client <em>negotiate</em> with the server, and determine the most preferred representation.
+<p>
+For a concrete example, let us consider the author's hypothetical homepage <tt>http://www.autrijus.org/index.html</tt>, written in Chinese:
+
+<p align="center">
+<table border=2 align=center>
+<caption align=bottom><font size=-1>Listing 1. A simple Chinese page</font></caption>
+<tr><td><PRE>
+<html><head><title><B>åå®æ¼¢ - 家</B></title></head>
+<body><B>æ½å·¥ä¸, è«è¦è«</B></body></html>
+</PRE></td></tr></table>
+<p>
+One day, I decided to translate it for my English-speaking friends:
+
+<p align="center">
+<table border=2 align=center>
+<caption align=bottom><font size=-1>Listing 2. Translated page in English</font></caption>
+<tr><td><PRE>
+<html><head><title><B>Audrey.Home</B></title></head>
+<body><B>Sorry, this page is under construction.</B></body></html>
+</PRE></td></tr></table>
+
+<P>
+At this point, many websites would decide to offer a <em>language selection page</em> to let the visitor to pick their favorite language. An example is shown in Figure 3:
+
+<p align="center">
+<table border=2 align=center>
+<caption align=bottom><font size=-1>Figure 3: A typical language selection page</font></caption>
+<tr><td align="center" colspan=4 bgcolor=black>
+<font color="white">Please choose your language:</font>
+</td></tr><tr><td align="center">
+<font size=-1><u>ÄeÅ¡tina</u></font>
+</td><td align="center">
+<font size=-1><u>Deutsch</u></font>
+</td><td align="center">
+<font size=-1><u>English</u></font>
+</td><td align="center">
+<font size=-1><u>Español</u></font>
+</td></tr><tr><td align="center">
+<font size=-1><u>Français</u></font>
+</td><td align="center">
+<font size=-1><u>Hrvatski</u></font>
+</td><td align="center">
+<font size=-1><u>Italiano</u></font>
+</td><td align="center">
+<font size=-1><u>æ¥æ¬èª</u></font>
+</td></tr><tr><td align="center">
+<font size=-1><u>íêµì´</u></font>
+</td><td align="center">
+<font size=-1><u>Nederlands</u></font>
+</td><td align="center">
+<font size=-1><u>Polski</u></font>
+</td><td align="center">
+<font size=-1><u>Ð ÑÑÑкий ÑзÑк</u></font>
+</td></tr><tr><td align="center">
+<font size=-1><u>Slovensky</u></font>
+</td><td align="center">
+<font size=-1><u>Slovensci</u></font>
+</td><td align="center">
+<font size=-1><u>Svenska</u></font>
+</td><td align="center">
+<font size=-1><u>ä¸æ (GB)</u></font><br>
+<font size=-1><u>ä¸æ (Big5)</u></font>
+</td></tr></table>
+
+<p>
+For both non-technical users and automated programs, that page is confusing, redundant, and highly irritating. Besides demanding an extra search-and-click for each visits, it poses considerable amount of difficulty on <em>web agent</em> programmers, as they now have to parse the page and follow the correct link, which is a highly error-prone thing to do.
+
+<h3>MultiViews: The Easiest L10n Framework</h3>
+<p>
+Of course, it is much better if everybody could see their preferred language automatically. Thankfully, the <em>Content Negotiation</em> feature in HTTP 1.1 addressed this problem quite neatly.
+<p>
+Under this scheme, browsers will always send an <b><tt>Accept-Language</tt></b> header, which specifies one or more preferred language codes; for example, <tt>zh-tw, en-us, en</tt> would mean "Traditional Chinese, American English or English, in this order".
+<p>
+The web server, upon receiving this information, is responsible to present the request content in the most preferred language. Different web servers may implement this process differently; under Apache (the most popular web server), a technique called <b><tt>MultiViews</tt></b> is widely used.
+<p>
+Using <tt>MultiViews</tt>, I will save the English version as <tt>index.html.en</tt> (note the extra file extension), then put this line into Apache's configuration file <tt>httpd.conf</tt> or <tt>.htaccess</tt>:
+<p>
+<PRE>
+ Options <b>+MultiViews</b>
+</PRE>
+<p>
+After that, Apache will examine all requests to <tt>http://www.autrijus.org/index.html</tt>, and see if the client prefers <tt>'en'</tt> in its <tt>Accept-Language</tt> header. Hence, people who prefer English would see the English page; otherwise, the original <tt>index.html</tt> is displayed.
+<p>
+This technique allows gradual introduction of new localized versions of the same documents, so my international friends can contribute more languages over time -- <tt>index.html.fr</tt> for French, <tt>index.html.he</tt> for Hebrew, and so on.
+<p>
+Since a large share of online populace speak only their native language and English, most of the contributed versions would be translated from English, <em>not</em> Chinese. But because both versions represent the same contents, that is not a problem.
+<p>
+... or is it? What if I go back to update the original, <em>Chinese</em> page?
+
+<h3>The Difficulty on Keeping up Translations</h3>
+
+<p>
+As I modify the original page, the first thing I'd notice is that it's impossible to get my French and Hebrew friends to translate <em>from Chinese</em> -- clearly, using English as the <em>base version</em> would be necessary. The same reasoning also applies to most Free Software projects, even if the principal developers do not speak English natively.
+
+<p>
+Moreover, even if it is merely a change to the background color (e.g. <tt><body bgcolor=gold></tt>), I still need to modify all translated pages, in order to keep the layout consistent.
+
+<p>
+Now, if both the layout <em>and</em> contents are changed, things quickly become very complicated. Since the old HTML tags are gone, my translator friends must work from scratch every time! Unless all of them are HTML wizards, errors and conflicts will surely arise. If there are 20 regularly updated pages in my personal site, then pretty soon I will run out of translators -- or even out of friends.
+
+<p>
+As you can see, we need a way to <em>separate data and code</em> (i.e. text and tags), and <em>automate</em> the process of generating localized pages.
+
+<h3>Separate Data and Code with CGI.pm</h3>
+
+<p>
+Actually, the previous sentence pretty much summarized up the modern internationalization(i18n) process: To prepare a web application for localization, one must find a way to separate as much data from code as possible.
+
+<p>
+As the long-established Web development language of choice, Perl offers a bewildering array of modules and toolkits for website construction. The most popular one is probably <tt>CGI.pm</tt>, which has been merged into core perl release since 1997. Let us see a code snippet that uses it to automatically generate translated pages:
+
+<p align="center">
+<table border=2 align=center>
+<caption align=bottom><font size=-1>Listing 3. Localization with MultiViews and CGI.pm</font></caption>
+<tr><td><PRE>
+use CGI ':standard'; <i># our templating system</i>
+foreach my $language (qw(zh_tw en de fr)) {
+ open OUT, ">index.html.$language" or die $!;
+ print OUT start_html({ title => _(<b>"Audrey.Home"</b>) }),
+ _(<b>"Sorry, this page is under construction."</b>),
+ end_html;
+ sub _ { some_function($language, @_) } <i># XXX: put L10n framework here</i>
+}
+</PRE></td></tr></table>
+
+<p>
+Unlike the HTML pages, this program enforces data/code separation via <tt>CGI.pm</tt>'s HTML-related routines. Tags (e.g. <html>) now become functions calls (<tt>start_html()</tt>), and texts are turned into Perl strings. Therefore, when the localized version is written out to the corresponding static page (<tt>index.html.zh_tw</tt>, <tt>index.html.en</tt>, etc.), the HTML layout will always be identical for each of the four languages listed.
+
+<p>
+<a title="">The <tt>sub _</tt> function is responsible for localizing any text into the current <tt>$language</tt>, by passing the language and text strings to a hypothetical <tt>some_function()</tt>; the latter is known as our <em>localization framework</em>, and we will see three such frameworks in the following section.</a>
+
+<p>
+After writing the snippet, it is a simple matter to <tt>grep</tt> for all strings inside <tt>_(...)</tt>, <em>extract</em> them into a <em>lexicon</em>, and ask translators to fill it out. Note that here <em>lexicon</em> means a set of things that we know how to say in another language -- sometimes single words like (<tt>"Cancel"</tt>), but usually whole phrases (<tt>"Do you want to overwrite?"</tt> or <tt>"5 files found."</tt>). Strings in a lexicon are like entries in travelers' pocket phrasebooks, sometimes with blanks to fill in, as demonstrated in Figure 4:
+
+<p align="center">
+<table border=2 align=center>
+<caption align=bottom><font size=-1>Figure 4: An English => Haitian lexicon</font></caption>
+<tr>
+<td align="center" bgcolor=black><font color="white">English</font></td>
+<td align="center" bgcolor=black><font color="white">Haitian</font></td>
+</tr><tr><td align="center">
+This costs ___ dollars.
+</td><td>
+Bagay la kute ___ dola yo.
+</td></tr></table>
+
+<p>
+Ideally, the translator should focus solely on this lexicon, instead of peeking at HTML files or the source code. But there is the rub: different localization frameworks use different lexicon formats, so one has to choose the framework that suits the project best.
+
+<h2>Localization Frameworks</h2>
+
+<p align="right" class=right>
+<i>``It is more complicated than you think.''<br>
+-- Eighth Networking Truth, RFC 1925</i>
+
+<p>
+To implement the <tt>some_function()</tt> in figure 4, one needs a library to manipulate lexicon files, look up the corresponding strings in it, and maybe incrementally extract new strings to the lexicon. These are collectively known as a <em>localization framework</em>.
+
+<p>
+From my observation, frameworks mostly differ in their idea about how lexicons should be structured. Here, I will discuss the Perl interface for three such frameworks, starting from the venerable <tt>Msgcat</tt>.
+
+<h3>Msgcat -- Lexicons are Arrays</h3>
+
+<p>
+As one of the earliest L10n frameworks and part of XPG3/XPG4 standards, <tt>Msgcat</tt> enjoys ubiquity in all Un*x platforms. It represents the first-generation paradigm of lexicons: treat entries as numbered strings in an array (a.k.a. <em>message catalog</em>). This approach is straightforward to implement, needs little memory, and is very fast to look up. The <em>resource files</em> used in Windows programming and other platforms uses basically the same idea.
+
+<p>
+For each page or source file, <tt>Msgcat</tt> requires us to make a lexicon file for each language, as shown below:
+
+<p align="center">
+<table border=2 align=center>
+<caption align=bottom><font size=-1>Listing 4. A <tt>Msgcat</tt> lexicon</font></caption>
+<tr><td><PRE>
+$set <b>7</b> <i># $Id: nls/de/index.pl.m</i>
+<b>1</b> Audrey'.Haus
+<b>2</b> Wir bitten um Entschudigung. Diese Seite ist im Aufbau.
+</PRE></td></tr></table>
+
+<p>
+The above file contains the German translation for each text strings within <tt>index.html</tt>, which is represented by an unique <em>set number</em>, <b>7</b>. Once we finished building the lexicons for all pages, the <tt>gencat</tt> utility is then used to generate the binary lexicon:
+<p>
+<pre>
+ % gencat nls/de.cat nls/de/*.m
+</pre>
+
+<p>
+It is best to imagine the internals of the binary lexicon as a two-dimensional array, as shown in figure 5:
+
+<p align="center">
+<table border=2 align=center>
+<caption align=bottom><font size=-1>Figure 5: The content of <tt>nls/de.cat</tt></font></caption>
+<tr>
+<td align="right" bgcolor=black><font color="white">set_id<br>msg_id</font></td>
+<td align="center" bgcolor=black><font color="white">1</font></td>
+<td align="center" bgcolor=black><font color="white">2</font></td>
+<td align="center" bgcolor=black><font color="white">3</font></td>
+<td align="center" bgcolor=black><font color="white">4</font></td>
+<td align="center" bgcolor=black><font color="white">5</font></td>
+<td align="center" bgcolor=black><font color="white">6</font></td>
+<td align="center" bgcolor=black><font color="white">7</font></td>
+<td align="center" bgcolor=black><font color="white">8</font></td>
+<td align="center" bgcolor=black><font color="white">9</font></td>
+</tr><tr>
+<td align="center" bgcolor=black><font color="white">1</font></td>
+<td><i>...</i></td><td><i>...</i></td><td><i>...</i></td><td><i>...</i></td><td><i>...</i></td><td><i>...</i></td>
+<td><tt>Audrey'.Haus</tt></td>
+<td><i>...</i></td><td><i>...</i></td>
+</tr><tr>
+<td align="center" bgcolor=black><font color="white">2</font></td>
+<td><i>...</i></td><td></td><td><i>...</i></td><td><i>...</i></td><td><i>...</i></td><td><i>...</i></td>
+<td><tt>Wir bitten um Entschudigung...</tt></td>
+<td><i>...</i></td><td><i>...</i></td>
+</tr><tr>
+<td align="center" bgcolor=black><font color="white">3</font></td>
+<td><i>...</i></td><td></td><td><i>...</i></td><td><i>...</i></td><td></td><td><i>...</i></td><td><i>...</i></td><td><i>...</i></td><td><i>...</i></td>
+</tr></table>
+
+<p>
+To read from the lexicon file, we use the Perl module <tt>Locale::Msgcat</tt>, available from CPAN (the Comprehensive Perl Archive Network), and implement the earlier <tt>sub _()</tt> function like this:
+
+<p align="center">
+<table border=2 align=center>
+<caption align=bottom><font size=-1>Listing 5. Sample usage of <tt>Locale::Msgcat</tt></font></caption>
+<tr><td><PRE>
+use Locale::Msgcat;
+my $cat = Locale::Msgcat->new;
+$cat->catopen("nls/$language.cat", 1); <i># it's like a 2D array</i>
+sub _ { $cat->catgets(<b>7</b>, @_) } <i># <b>7</b> is the set_id for index.html</i>
+print _(<b>1</b>, "Audrey.House"); <i># <b>1</b> is the msg_id for this text</i>
+</PRE></td></tr></table>
+
+<p>
+Note that only the <tt>msg_id</tt> matters here; the string <tt>"Audrey.House"</tt> is only used as an optional fall-back when the lookup failed, as well as to improve the program's readability.
+
+<p>
+Because <tt>set_id</tt> and <tt>msg_id</tt> must both be <em>unique</em> and <em>immutable</em>, future revision may only delete entries, and never reassign the number to represent other strings. This characteristic makes revisions very costly, as observed by Drepper et al in the GNU gettext manuals:
+
+<p>
+<blockquote>
+Every time he comes to a translatable string he has to define a number
+(or a symbolic constant) which has also be defined in the message
+catalog file. He also has to take care for duplicate entries,
+duplicate message IDs etc. If he wants to have the same quality in the
+message catalog as the GNU <tt>gettext</tt> program provides he also has to
+put the descriptive comments for the strings and the location in all
+source code files in the message catalog. This is nearly a Mission:
+Impossible.
+</blockquote>
+
+<p>
+Therefore, one should consider using <tt>Msgcat</tt> only if the lexicon is very stable.
+<p>
+Another shortcoming that had plagued <tt>Msgcat</tt>-using programs is the <em>plurality</em> problem. Consider this code snippet:
+
+<p align="center">
+<table border=2 align=center>
+<caption align=bottom><font size=-1>Listing 6. Incorrect plural form handling</font></caption>
+<tr><td><PRE>
+printf(_(8, "<b>%d</b> files were deleted."), $files);
+</PRE></td></tr></table>
+
+<p>
+This is obviously incorrect when <tt>$files == 1</tt>, and <tt>"<b>%d</b> file(s) were deleted"</tt> is grammatically invalid as well. Hence, programmers are often forced to use two entries:
+
+<p align="center">
+<table border=2 align=center>
+<caption align=bottom><font size=-1>Listing 7. English-specific plural form handling</font></caption>
+<tr><td><PRE>
+printf(($files == 1) ? _(8, "<b>%d</b> file was deleted.")
+ : _(9, "<b>%d</b> files were deleted."), $files);
+</PRE></td></tr></table>
+
+<p>
+But even that is still bogus, because it is English-specific -- French uses singular with <tt>($files == 0)</tt>, and Slavic languages has three or four plural forms! Trying to retrofit those languages to the <tt>Msgcat</tt> infrastructure is often a futile exercise.
+
+<h3>Gettext -- Lexicons are Hashes</h3>
+
+Due to the various problems of <tt>Msgcat</tt>, the GNU Project has developed its own implementation of the Uniforum <tt>Gettext</tt> interface in 1995, written by Ulrich Drepper. It had since become the <em>de facto</em> L10n framework for C-based free software projects, and has been widely adopted by C++, Tcl and Python programmers.
+
+<p>
+Instead of requiring one lexicon for each source file, <tt>Gettext</tt> maintains a single lexicon (called a <em>PO file</em>) for each language of the entire project. For example, the German lexicon <tt>de.po</tt> for the homepage above would look like this:
+
+<p align="center">
+<table border=2 align=center>
+<caption align=bottom><font size=-1>Listing 8. A <tt>Gettext</tt> lexicon</font></caption>
+<tr><td><PRE>
+#: index.pl:4
+msgid "Audrey.Home"
+msgstr "Audrey'.Haus"
+
+#: index.pl:5
+msgid "Sorry, this site is under construction."
+msgstr "Wir bitten um Entschudigung. Diese Seite ist im Aufbau."
+</PRE></td></tr></table>
+
+<p>
+
+The <tt>#:</tt> lines are automatically generated from the source file by the program <tt>xgettext</tt>, which can extract strings inside invocations to <tt>gettext()</tt>, and sort them out into a lexicon.
+
+<p>
+Now, we may run <tt>msgfmt</tt> to compile the binary lexicon <tt>locale/de/LC_MESSAGES/web.mo</tt> from <tt>po/de.po</tt>:
+
+<pre>
+ % msgfmt locale/de/LC_MESSAGES/web.mo po/de.po
+</pre>
+
+<p>
+We can then access the binary lexicon using <tt>Locale::gettext</tt> from CPAN, as shown below:
+
+<p align=center>
+<table border=2 align=center>
+<caption align=bottom><font size=-1>Listing 9. Sample usage of <tt>Locale::gettext</tt></font></caption>
+<tr><td><PRE>
+use POSIX;
+use Locale::gettext;
+POSIX::setlocale(LC_MESSAGES, $language); <i># Set target language</i>
+textdomain("web"); <i># Usually the same as the application's name</i>
+sub _ { gettext(@_) } <i># it's just a shorthand for gettext()</i>
+print _("Sorry, this site is under construction.");
+</PRE></td></tr></table>
+
+<p>
+Recent versions (glibc 2.2+) of <tt>gettext</tt> had also introduced the <tt>ngettext("%d file", "%d files", $files)</tt> syntax. Unfortunately, <tt>Locale::gettext</tt> does not support that interface yet.
+
+<p>
+Also, <tt>gettext</tt> lexicons support multi-line strings, as well as reordering via <tt>printf</tt> and <tt>sprintf</tt>:
+
+<p align=center>
+<table border=2 align=center>
+<caption align=bottom><font size=-1>Listing 10. A multi-line entry with numbered arguments</font></caption>
+<tr><td><PRE>
+msgid ""
+"This is a multiline string"
+"with <b>%1$s</b> and <b>%2$s</b> as arguments"
+msgstr ""
+"ããã¯å¤ç·ã²ãå¤æ°ã¨ãã¦"
+"<b>%2$s</b> 㨠<b>%1$s</b> ã®ã§ãã"
+</PRE></td></tr></table>
+
+<p>
+Finally, GNU <tt>gettext</tt> comes with a very complete tool chain (msgattrib, msgcmp, msgconv, msgexec, msgfmt, msgcat, msgcomm...), which greatly simplified the process of merging, updating and managing lexicon files.
+
+<h3>Locale::Maketext -- Lexicons are Dispatch Tables!</h3>
+
+<p>
+First written in 1998 by Sean M. Burke, the <tt>Locale::Maketext</tt> module was revamped in May 2001 and included in Perl 5.8 core.
+
+<p>
+Unlike the function-based interface of <tt>Msgcat</tt> and <tt>Gettext</tt>, its basic design is object-oriented, with <tt>Locale::Maketext</tt> as an abstract base class, from which a <em>project class</em> is derived. The project class (with a name like <tt>MyApp::L10N</tt>) is in turn the base class for all the <em>language classes</em> in the project (with names like <tt>MyApp::L10N::it</tt>, <tt>MyApp::L10N::fr</tt>, etc.).
+
+<p>
+A language class is really a perl module containing a <tt>%Lexicon</tt> hash as class data, which contains strings in the native language (usually English) as keys, and localized strings as values. The language class may also contain some methods that are of use in interpreting phrases in the lexicon, or otherwise dealing with text in that language.
+
+<p>
+Here is an example:
+
+<p align=center>
+<table border=2 align=center>
+<caption align=bottom><font size=-1>Listing 11. A <tt>Locale::Maketext</tt> lexicon and its usage</font></caption>
+<tr><td><PRE>
+package MyApp::L10N;
+use base 'Locale::Maketext';
+
+package MyApp::L10N::de;
+use base 'MyApp::L10N';
+our %Lexicon = (
+ "[<b>quant</b>,_1,camel was,camels were] released." =>
+ "[<b>quant</b>,_1,Kamel wurde,Kamele wurden] freigegeben.",
+);
+
+package main;
+my $lh = MyApp::L10N->get_handle('de');
+print $lh->maketext("[<b>quant</b>,_1,camel was,camels were] released.", 5);
+</PRE></td></tr></table>
+
+<p>
+Under its <em>square bracket notation</em>, translators can make use of various linguistic-related functions inside their translated strings. The example above highlights includes built-in plurals and quantifiers support; for languages with other kinds of plural-form characteristics, it is a simple matter of implementing a corresponding <tt>quant()</tt> function. Ordinates and time formats are easy to add, too.
+
+<p>
+Each language class may also implement an <tt>->encoding</tt> method to describe the encoding of its lexicons, which may be linked with <tt>Encode</tt> for transcoding purposes. Language families are also inheritable and subclassable: missing entries in <tt>fr_ca.pm</tt> (Canadian French) would fallbacks to <tt>fr.pm</tt> (Generic French).
+
+<p>
+The handy built-in method <tt>->get_handle()</tt> with no arguments magically detects HTTP, POSIX and Win32 locale settings in CGI, mod_perl or command line; it spares the programmer to parse those settings manually.
+
+<p>
+However, <tt>Locale::Maketext</tt> is not without problems. The most serious issue is its lacking of a <em>toolchain</em> like GNU <tt>Gettext</tt>'s, due to the extreme flexibility of lexicon classes. For the same reason, there are also fewer support in text editors (e.g. the <em>PO Mode</em> in Emacs).
+
+<p>
+Finally, since different projects may use different styles to write the language class, the translator must know some basic Perl syntax -- or somebody has to type in for them.
+
+<h3>Locale::Maketext::Lexicon -- The Best of Both Worlds</h3>
+
+<p>
+Irritated by the irregularity of <tt>Locale::Maketext</tt> lexicons, I implemented a home-brew lexicon format for my company's internal use in May 2002, and asked the <em>perl-i18n</em> mailing list for ideas and feedbacks. Jesse Vincent suggested: "Why not simply standardize on <tt>Gettext</tt>'s PO File format?", so I implemented it to accept lexicons in various formats, handled by different <em>lexicon backend</em> modules. Thus, <tt>Locale::Maketext::Lexicon</tt> was born.
+
+<p>
+The design goal was to combine the flexibility of <tt>Locale::Maketext</tt> lexicon's expression with standard formats supported by utilities designed for <tt>Gettext</tt> and <tt>Msgcat</tt>. It also supports the <tt>Tie</tt> interface, which comes in handy for accessing lexicons stored in relational databases or DBM files.
+
+<p>
+The following program demonstrates a typical application using <tt>Locale::Maketext::Lexicon</tt> and the extended PO File syntax supported by the <tt>Gettext</tt> backend:
+
+<p align=center>
+<table border=2 align=center>
+<caption align=bottom><font size=-1>Listing 12. A sample application using <tt>Locale::Maketext::Lexicon</tt></font></caption>
+<tr><td bgcolor=black align=right><PRE><font color=white>1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+</font></PRE></td><td><PRE>
+use CGI ':standard';
+use base 'Locale::Maketext'; <i># inherits get_handle()</i>
+
+<i># Various lexicon formats and sources</i>
+use Locale::Maketext::Lexicon {
+ en => ['Auto'], fr => ['Tie' => 'DB_File', 'fr.db'],
+ de => ['Gettext' => \*DATA], zh_tw => ['Gettext' => 'zh_tw.mo'],
+};
+
+<i># Ordinate functions for each subclasses of 'main'</i>
+use Lingua::EN::Numbers::Ordinate; use Lingua::FR::Numbers::Ordinate;
+sub en::ord { ordinate($_[1]) } sub fr::ord { ordinate_fr($_[1]) }
+sub de::ord { "$_[1]." } sub zh_tw::ord { "第 $_[1] å" }
+
+my $lh = __PACKAGE__->get_handle; <i># magically gets the current locale</i>
+sub _ { $lh->maketext(@_) } <i># may also convert encodings if needed</i>
+
+print header, start_html, <i># [<b>*</b>,...] is a shorthand for [<b>quant</b>,...]</i>
+ _("You are my [<b>ord</b>,_1] guest in [<b>*</b>,_2,day].", $hits, $days), end_html;
+
+__DATA__
+# <i>The German lexicon, in extended PO File format</i>
+msgid "You are my <b>%ord(%1)</b> guest in <b>%*(%2,day)</b>."
+msgstr "Innerhalb <b>%*(%2,Tages,Tagen)</b>, sie sind mein <b>%ord(%1)</b> Gast."
+</PRE></td></tr></table>
+
+<p>
+Line 2 tells the current package <tt>main</tt> to inherit from <tt>Locale::Maketext</tt>, so it could acquire the <tt>get_handle</tt> method. Line 5-8 builds four <em>language classes</em> using a variety of lexicon formats and sources:
+
+<ul>
+<li>The <em>Auto</em> backend tells <tt>Locale::Maketext</tt> that no localizing is needed for the English language -- just use the lookup key as the returned string. It is especially useful if you are just starting to prototype a program, and does not want deal with the localization files yet.
+<li>The <em>Tie</em> backend links the French <tt>%Lexicon</tt> hash to a Berkeley DB file; entries will then be fetched whenever it is used, so it will not waste any memory on unused lexicon entries.
+<li>The <em>Gettext</em> backend reads a compiled MO file from disk for Chinese, and reads the German lexicon from the DATA filehandle in PO file format.
+</ul>
+
+<p>
+Line 11-13 implements the <tt>ord</tt> method for each language subclasses of the package <tt>main</tt>, which converts its argument to ordinate numbers (1th, 2nd, 3rd...) in that language. Two CPAN modules are used to handle English and French, while German and Chinese only needs straightforward string interpolation.
+
+<p>
+Line 15 gets a <em>language handle</em> object for the current package. Because we did not specify the language argument, it automatically guesses the current locale by probing the <tt>HTTP_ACCEPT_LANGUAGE</tt> environment variable, POSIX <tt>setlocale()</tt> settings, or via <tt>Win32::Locale</tt> on Windows. Line 16 sets up a simple wrapper funciton that passes all arguments to the handle's <tt>maketext</tt> method.
+
+<p>
+Finally, line 18-19 prints a message containing one string to be localized. The first argument <tt>$hits</tt> will be passed to the <tt>ord</tt> method, and the second argument <tt>$days</tt> will call the built-in <tt>quant</tt> method -- the <tt>[*...]</tt> notation is a shorthand for the previously discussed <tt>[quant,...]</tt>.
+
+<p>
+Line 22-24 is a sample lexicon, in extended PO file format. In addition to ordered arguments via <tt>%1</tt> and <tt>%2</tt>, it also supports <tt>%function(args...)</tt> in entries, which will be transformed to <tt>[function,args...]</tt>. Any <tt>%1</tt>, <tt>%2</tt>... sequences inside the <em>args</em> will have their percent signs (<tt>%</tt>) replaced by underscores (<tt>_</tt>).
+
+<h2>Case Studies</h2>
+
+<p class="right" align="right">
+<i>``One size never fits all.''<br>
+-- Tenth Networking Truth, RFC 1925</i>
+
+<p>
+Armed with the understanding of localization frameworks, let us see how it fits into real-world applications and technologies.
+
+<p>
+For web applications, the place to implement a L10n framework is almost inevitably its <em>representation system</em>, also known as <em>templating system</em>, because that layer determines the extent of an application's data/code separation. For example, the <em>Template Toolkit</em> encourages a clean 3-tier data/code/template model, while the equally popular <em>Mason</em> framework lets you easily mix perl code in a template. In this section, we will survey L10n strategies for those two different frameworks, and the general principle should also apply to <em>AxKit</em>, <em>HTML::Embperl</em>, and other templating systems.
+
+<h3>Request Tracker (Mason)</h3>
+
+<p>
+The <em>Request Tracker</em> is the first application that uses <tt>Locale::Maketext::Lexicon</tt> as its L10n framework. The <em>base language class</em> is <tt>RT::I18N</tt>, with subclasses reading <tt>*.po</tt> files stored in the same directory.
+
+<p>
+Additionally, its <tt>->maketext</tt> method was overridden to uses <tt>Encode</tt> (or in pre-5.8 versions of perl, my <tt>Encode::compat</tt>) to return UTF-8 data on-the-fly. For example, Chinese translator may submit lexicons encoded in <tt>Big5</tt>, but the system will always handle them natively as Unicode strings.
+
+<p>
+In the application's Perl code, all objects use the <Tt>$self->loc</tt> method, inherited from <tt>RT::Base</tt>:
+
+<p align=center>
+<table border=2 align=center>
+<caption align=bottom><font size=-1>Listing 13. RT's L10n implementation</font></caption>
+<tr><td><PRE>
+sub RT::Base::loc
+ { $self->CurrentUser->loc(@_) }
+sub RT::CurrentUser::loc
+ { $self->LanguageHandle->maketext(@_) }
+sub RT::CurrentUser::LanguageHandle
+ { $self->{'LangHandle'} ||= RT::I18N->get_handle(@_) }
+</PRE></td></tr></table>
+
+<p>
+As you can see, the current user's language settings is used, so different users can use the application in different languages simultaneously. For Mason templates, two styles were devised:
+
+<p align=center>
+<table border=2 align=center>
+<caption align=bottom><font size=-1>Listing 14. Two ways to mark strings in Mason templates</font></caption>
+<tr><td><PRE>
+% $m->print(<b>loc(</b>"<b>Another line of text</b>", $args...<b>)</b>);
+<&<b>|/l</b>, $args...&><b>Single line of text</b></&>
+</PRE></td></tr></table>
+<p>
+The first style, used in embedded perl chunks and <tt><%PERL></tt> sections, is made possible by exporting a global <tt>loc()</tt> function to the Mason interpreter; it automatically calls the current user's <tt>->loc</tt> method described above.
+
+<p>
+The second style uses the <em>filter component</em> feature in <tt>HTML::Mason</tt>, which takes the enclosed <tt>Single line of text</tt>, passes it to the <tt>/l</tt> component (possibly with arguments), and displays the returned string. Here is the implementation of that component:
+
+<p align=center>
+<table border=2 align=center>
+<caption align=bottom><font size=-1>Listing 15. Implementation of the <tt>html/l</tt> filter component</font></caption>
+<tr><td><PRE>
+% my $hand = $session{'CurrentUser'}->LanguageHandle;
+% $m->print($hand->maketext($m->content, @_));
+</PRE></td></tr></table>
+
+<p>
+With these constructs, it is a simple matter of extracting messages out of existing templates, comment them, and send to the translators. The initial extraction for 700+ entries took one week; the whole i18n/L10n process took less than two months.
+
+<h3>Slash (Template Toolkit)</h3>
+
+<p>
+Slash -- Slashdot Like Automated Storytelling Homepage -- is the code that runs Slashdot. More than that, however, Slash is an architecture for putting together web sites, built upon Andy Wardley's <em>Template Toolkit</em> module.
+
+<p>
+Due to the clean design of TT2, Slash features a careful separation of code and text, unlike RT/Mason. This largely eliminated the need to localize inside Perl source code.
+
+<p>
+Previous to this article's writing, various whole-template localizations based on the theme system had been attempted, including Chinese, Japanese, and Hebrew versions. However, merging with a new version was very difficult (not to mention plugins), and translations tend to lag behind a lot.
+
+<p>
+Now, let us consider a better approach: An <em>auto-extraction</em> layer above the template provider, based on <tt>HTML::Parser</tt> and <tt>Template::Parser</tt>. Its function would be like this:
+
+<p align=center>
+<table border=2 align=center>
+<caption align=bottom><font size=-1>Listing 16. Input and output of the TT2 extraction layer</font></caption>
+<tr><td bgcolor=black><font color=white>Input</font></td><td><PRE>
+<B>from the [% story.dept %] dept.</B>
+</PRE></td></tr>
+<tr><td bgcolor=black><font color=white>Output</font></td><td><PRE>
+<B>[%<b>|loc(</b> story.dept <b>)</b>%]from the [<b>_1</b>] dept.[%END%]</B>
+</PRE></td></tr></table>
+
+<p>
+The acute reader will point out that this layer suffer from the same linguistic problems as <tt>Msgcat</tt> does -- what if we want to make ordinates from <tt>[% story.dept %]</tt>, or expand the <tt>dept.</tt> to <tt>department</tt> / <tt>departments</tt>? The same problem has occurred in RT's web interface, where it had to localize messages returned by external modules, which may already contain interpolated variables, e.g. <tt>"Successfully deleted 7 ticket(s) in 'c:\temp'."</tt>.
+
+<p>
+My solution to this problem is to introduce a <em>fuzzy match</em> layer with the module <tt>Locale::Maketext::Fuzzy</tt>, which matches the interpolated string against the list of <em>candidate entries</em> in the current lexicon, to find one that can possibly yield the string (e.g. <tt>"Successfully deleted [*,_1,ticket] in '[_2]'."</tt>). If two or more candidates are found, -- after all, <tt>"Successfully [_1]."</tt> also matches the same string -- tie-breaking heuristics are used to determine the most likely candidate.
+
+<p>
+Combined with <tt>xgettext.pl</tt>, developers can supply compendium lexicons along with each plugin/theme, and the Slash system would employ a multi-layer lookup mechanism: Try plugin-specific entries first; then the theme's; then fallback to the global lexicon.
+
+<h2>Summary</h2>
+
+<p class="right" align="right">
+<i>``...perfection has been reached not when there is nothing left to add, but when there is nothing left to take away.''<br>
+-- Twelfth Networking Truth, RFC 1925</i>
+
+<p>
+From the two case studies above, it is quite easy to see an emergent pattern of how such efforts are carried. This section presents a 9-step guide in localizing <em>existing</em> web applications, as well as tips of how to implement them with minimal hassles.
+
+<h3>The Localization Process</h3>
+
+We can summarize the localization process as several steps, each depending on previous ones:
+
+<ol>
+<li>Assess the website's templating system
+<li>Choose a localization framework and hook it up
+<li>Write a program to locate text strings in templates, and put filters around them
+<li>Extract a test lexicon; fix obvious problems manually
+<li>Locate text strings in the source code by hand; replace them with <tt><b>_(</b>...<b>)</b></tt> calls
+<li>Extract another test lexicon and machine-translate it
+<li>Try the localized version out; fix any remaining problems
+<li>Extract the beta lexicon; mail it to your translator teams for review
+Fix problems reported by translators; extract the official lexicon and mail it out!
+<li>Periodically notify translators of new lexicon entries before each release
+</ol>
+
+Following these steps, one could manage a L10n project fairly easily, and keep the translations up-to-date and minimize errors.
+
+<h3>Localization Tips</H3>
+
+Finally, here are some tips for localizing Web applications, and other softwares in general:
+
+<ul>
+<li><em>Separate data and code</em>, both in design and in practice
+<li>Don't work on i18n/L10n before the website or application takes shape
+<li>Avoid graphic files with text in them
+<li>Leave enough spaces around labels and buttons -- do not overcrowd the UI
+<li>Use complete sentences, instead of concatenated fragments (see listing 17):
+</ul>
+
+<p align=center>
+<table border=2 align=center>
+<caption align=bottom><font size=-1>Listing 17. Fragmented vs. complete sentences</font></caption>
+<tr><td><PRE>
+_("Found ") . $files . _(" file(s)."); <i># Fragmented sentence - wrong!</i>
+sprintf(_("Found %s file(s)."), $files); <i># Complete (with sprintf)</i>
+_("Found [*,_1,file].", $files); <i># Complete (Locale::Maketext)</i>
+</PRE></td></tr></table>
+
+<ul>
+<li>Distinguish the same string in different contexts<br>
+e.g. "Home" in RT used to mean both "Homepage" and "Home Phone No."
+<li>Work with your translators as equals; do not apply lexicon patches by yourself
+<li>One person doing draft translations works best
+<li>In lexicons, provide as much comments and metadata as possible:
+</ul>
+
+<p align=center>
+<table border=2 align=center>
+<caption align=bottom><font size=-1>Listing 18. Comments in lexicons</font></caption>
+<tr><td><PRE>
+#: lib/RT/Transaction_Overlay.pm:579
+#. ($field, $self->OldValue, $self->NewValue)
+# <i>Note that 'changed to' here means 'has been modified to...'.</i>
+msgid "<b>%1 %2</b> changed to <b>%3</b>"
+msgstr "<b>%1 %2</b> cambiado a <b>%3</b>"
+</PRE></td></tr></table>
+
+<p>
+Using the <tt>xgettext.pl</tt> utility provided in the <tt>Locale::Maketext::Lexicon</tt> package, the source file, line number (marked by <tt>#:</tt>) and variables (marked by <tt>#.</tt>) can be deduced automatically and incrementally. It would also be very helpful to clarify the meaning of short or ambiguous with normal comments (marked by <tt>#</tt>), as shown in listing 18 above.
+
+<h2>Conclusion</h2>
+
+<p>
+For countries with language dissimilar to English, localization efforts is often the prerequisite for people to participate in other Free Software projects. In Taiwan, L10n projects like the CLE (Chinese Linux Environment), Debian-Chinese and FreeBSD-Chinese were (and still are) the principal place where community contributions are made. However, such efforts are also historically time-consuming, error-prone jobs, partly because of English-specific frameworks and rigid coding practices used by existing applications. The <em>entry barrier</em> for translators was unnecessarily high.
+
+<p>
+On the other hand, ever-increasing internationalization of the Web makes it increasingly likely that the interface to Web-based dynamic content service will be localized to two or more languages. For example, Sean M. Burke led enthusiastic users to localize the popular <em>Apache::MP3</em> module, which powers home-grown Internet jukeboxes everywhere, to dozens of languages in 2002. The module's author, Lincoln D. Stein, did not involve with the project at all -- all he needed to do was integrating the i18n patches and lexicons into the next release.
+
+<p>
+The Free Software projects are not abstractions filled with code, but rather depends on people caring enough to share code, as well as sharing useful feedback in order to improve each other's code. Hence, it is my sincere hope that techniques presented in this article will encourage programmers and eager users to actively internationalize existing applications, instead of passively translating for the relatively few applications with established i18n frameworks.
+
+<h2>Acknowledgments</h2>
+
+<p>
+Thanks to Jesse Vincent for suggesting <tt>Locale::Maketext::Lexicon</tt> to be written, and for allowing me to work with him on RT's L10n model. Thanks also to Sean M. Burke for coming up with <tt>Locale::Maketext</tt>, and encouraging me to experiment with alternative Lexicon syntaxes.
+
+<p>
+Thanks also go to my brilliant colleagues in OurInternet, Inc. for the hard work they did on localizing web applications: Hsin-Chan Chien, Chia-Liang Kao, Whiteg Weng and Jedi Lin. Also thanks to my fellow translators of the Llama book (<em>Learning Perl</em>), who showed me the power of distributed translation teamworks.
+
+<p>
+I would also like to thank to Nick Ing-Simmons, Dan Kogai and Jarkko Hietaniemi for teaching me how to use the <tt>Encode</tt> module, Bruno Haible for his kind permission for me to use his excellent work on GNU libiconv, and Tatsuhiko Miyagawa for proofreading early versions of my <tt>Locale::Maketext::Lexicon</tt> module. Thanks!
+
+<p>
+Finally, if you decide to follow the steps in this article and participate in software internationalization and localization, then you have my utmost gratitude; let's make the Web a truly <em>World Wide</em> place.
+
+<h2>Bibliography</h2>
+
+<p>
+Alvestrand, Harald Tveit. 1995. <em>RFC 1766: Tags for the Identification of Languages.</em>, <tt>ftp://ftp.isi.edu/in-notes/rfc1766.txt</tt>
+
+<p>
+Callon, Ross, editor. 1996. <em>RFC 1925: The Twelve Networking Truths.</em>, <tt>ftp://ftp.isi.edu/in-notes/rfc1925.txt</tt>
+
+<p>
+Drepper, Ulrich, Peter Miller, and François Pinard. 1995-2001. GNU <tt>gettext</tt>. Available in <tt>ftp://prep.ai.mit.edu/pub/gnu/</tt>, with extensive documents in the distribution package.
+
+<p>
+Burke and Lachler. 1999. <em>Localization and Perl: gettext breaks, Maketext fixes</em>, first published in The Perl Journal, issue 13.
+
+<p>
+Burke, Sean M. 2002. <em>Localizing Open-Source Software</em>, first published in the The Perl Journal, Fall 2002 edition.
+
+<p>
+W3C internationalization activity statement, 2001, <tt>http://www.w3.org/International/Activity.html</tt>
+
+<p>
+Mozilla i18n & L10n guidelines, 1999, <tt>http://www.mozilla.org/docs/refList/i18n/</tt>
+
+</body></html>
Added: Locale-Maketext-Lexicon/docs/webl10n.zh-tw.html
==============================================================================
--- (empty file)
+++ Locale-Maketext-Lexicon/docs/webl10n.zh-tw.html Sun Apr 2 05:40:20 2006
@@ -0,0 +1,758 @@
+<html><head><title>ç¨ Perl é²è¡ç¶²ç«æ¬åå</title>
+<style><!--
+P {text-align: justify}
+P.right {text-align: right}
+--></style></head><body>
+
+<h1>ç¨ Perl é²è¡ç¶²ç«æ¬åå</h1>
+<p class="right" align="right">
+åå®æ¼¢<br>
+å²ç¾ç¶²<br>
+2002å¹´12æ
+
+<p>
+<h2>æè¦</h2>
+
+<p>
+å°æç¨ç¨å¼é²è¡åéåï¼Internationalizationï¼i18nï¼çç®çï¼æ¯å¨ä½¿å®æ¯æ´å¤ç¨®èªè¨ãæ¥æãè²¨å¹£æ ¼å¼ï¼ä»¥ååå°çç¿ä¿çãå°åè¨å®ï¼localeï¼ããæ¥ä¸ä¾ï¼æ¬ååï¼Localizationï¼l10nï¼åè² è²¬å¯¦éå°è»é«è½è¯ï¼ä»¥ååç¹å®å°åç使ç¨è
éæ±ãç¶åï¼ç¶²é æç¨ç¨å¼ï¼Web Applicationï¼ç±æ¼ä»¥æåä½çºçé¢æè¿°çæ ¼å¼ï¼å·²æçºæç±éçæ¬ååå°è±¡ä¹ä¸ã
+<p>
+å¨èªç±è»é«çä¸ç裡ï¼è¨±å¤æå
·å½æ§ãæåæ¡è¿çæè¡ï¼æ¯ç¨Perlèªè¨éç¼çãå°ç¶²ç«æç¨ç¨å¼çéç¼è
ä¾èªªï¼Perlä¹æ¯é·æ以ä¾çä¸äºé¸æãæ¬ç¯æç« æ¯çè
å°Perlæç¨ç¨å¼é²è¡ä¸è¯çç¶é©è«ï¼å
å«å¯¦ä½æ¹å¼ã常ç¨å·¥å
·çåªå£ä¹èï¼ä»¥å管çæ¬ååå°æ¡æé注æçäºé
ã
+
+<h2>ç°¡ä»</h2>
+
+<p class="right" align="right">
+ãå¨éåä¸çä¸ï¼äººé¡çèªè¨çºæ¸ä¸å°ãã<br>
+ããï¼ï¼Harald Tveit Alvestrandï¼RFC 1766ï¼ãèªè¨èå¥æ¨è¨ã
+<p>
+網ç«å網é æç¨ç¨å¼ï¼çºä»éº¼è¦ä½æ¬ååå¢ï¼
+<p>
+è«è®è
æ³åä¸ä¸ï¼åè¨æ人å¨å
¨çè³è¨ç¶²ä¸æåºéååé¡ï¼è¨±å¤äººç¨ä¸åçèªè¨æåºæè¦ãå½¼æ¤è¨è«ã身çºå
¶ä¸çä¸ä»½åï¼ä½ å¯è½æè½å°éäºè«é»ï¼
+
+<p align="center">
+<table border=2 align=center>
+<caption align=bottom><font size=-1><b>å1: æ¬ååççç±(ä¸æåä¹å)</b></font></caption>
+<tr><td><ul>
+<li>ÐноÑÑÑÐ°Ð½Ð½Ð°Ñ Ð²Ð°Ð»ÑÑа, ÑоÑÐ¼Ð°Ñ Ð´Ð°ÑÑ, ÑзÑк и обÑÑаи могÑÑ ÐºÐ°Ð·Ð°ÑÑÑÑ Ð½Ð°Ð¼ пÑгаÑÑими
+<li>Menschen sind produktiver wenn sie in ihrer gewohnten Umgebung arbeiten
+<li>Tas veicina daudz labâku sapraðanu un mijiedarbîbu starp daâdâm kultrâm
+<li>Un progretto con molti collaboratori internazionali si evolverá piú in fretta e meglio
+<li>å°åºåçè¿ç¨, æå©æ¼è½¯ä»¶ç模ååä¸å¯ç§»æ¤æ§
+</ul></td></tr></table>
+
+<p>
+ä¸å¹¸çæ¯ï¼éäºè«é»ä¸¦éæ¯å人é½çå¾æãéå°±é æäºãèªè¨éç¤ãï¼ï¼è½å
±åè¨è«çå°è±¡ï¼å¾å¾ä¾·éå¨å°æ¸èªè¨ç¸åãæåç¸è¿çãå°å社群ãä¸ã
+<p>
+ç¶èï¼æåçä¸æçè«é»å¾å¾å¾æéçï¼ä¸¦ä¸è½å¸¶ä¾æ°ççæãæ以ï¼æ好è½æ人å°éäºæè¦ãæä½çé¢åå
¶ä»è³æç¿»è¯ææåçå¾æçæåï¼
+
+<p align="center">
+<table border=2 align=center>
+<caption align=bottom><font size=-1>å2: æ¬ååççç±(ä¸æåä¹å¾)</font></caption>
+<tr><td><ul>
+<li>è¦é·å°±å¤å°çèªè¨ãæ¥æã貨幣åç¿ä¿ï¼æ¯ä»¶ç
©äººçäºã
+<li>人å¨çæçæä½ç°å¢ä¸ï¼ææ¯è¼æçç¢åã
+<li>å®è½ä¿é²æåéçç解è交æµã
+<li>ææ許å¤åéå好çå°æ¡ï¼é²æ¥çé度æ´å¿«ï¼å質ä¹æ´å¥½ã
+<li>æ¬ååçéç¨ï¼æå©æ¼è»é«ç模çµåèå¯ç§»æ¤æ§ã
+</ul></td></tr></table>
+
+<p>
+æ£å¦åä¸åè«é»æè¿°ï¼é常ç¡æ³è¦æ±ææ人ãæ¸åæãï¼ä¸å¾æ¡ç¨æä¸èªãéç¨èªãä¸çèªãé輯èªææ¯è±æãéæï¼å°±éè¦æ¬ååäºã
+
+<p>
+å°å°å±¬è»é«èè¨ï¼æ¬ååé常æ¯æå
¥åå¤å¸å ´çå
決æ¢ä»¶ãè¥æ¯æå°çé æå©æ½¤ä½æ¼æ¬ååçææ¬ï¼å» å便ä¸æé²è¡ç¿»è¯ï¼å¨æ²ææºç¢¼çæ
æ³ä¸ï¼ç¶å°ç使ç¨è
è¦èªå·±é²è¡ç¿»è¯ï¼ä¾¿æ¯ä»¶å°é£ï¼ä¹å¯è½éæ³ï¼çä»»åãå¦æå» åè¨è¨è»é«æï¼å®å
¨æ²æèæ
®å°åéåçæ¶æ§ï¼é£æ´æ¯å天ä¹è¡ã
+
+<p>
+ç¸è¼ä¹ä¸ï¼å°éæ¾æºç¢¼çæç¨ç¨å¼é²è¡æ¬ååï¼åè¦ç°¡å®å¤äºãåå°å±¬è»é«ä¸æ¨£ï¼æ©æççæ¬é常ç¥çºå®ä¸èªè¨èè¨è¨ï¼ä¸åçæ¯ï¼ä»»ä½äººé½å¯ä»¥é¨æå ä¸åéåçæ¶æ§ãèª å¦Sean M. Burkeæè¿°ï¼
+
+<p>
+<blockquote>
+ãè¦è®éæ¾æºç¢¼åå¾æ´å¥½ï¼æåå¯ä»¥å¨å¯«ç¨å¼æå¤å çæï¼è®ç¨å¼å¡åç±å¿ç使ç¨è
ï¼é½è½è¼æé²è¡æ¬ååãï¼è©±èªªåä¾ï¼ãéæ¾æºç¢¼ãçæ¬æï¼å°±æ¯è®ä»»ä½ææé¡ãææè¡ç人ï¼é½è½æçºç¨å¼å¡ãï¼ã
+</blockquote>
+
+<p>
+æ¬ææè¿°çæå·§ï¼è½ææéä½æ¬ååçé£åº¦ãéç¶éé»æ¾å¨Perl寫ç網ç«æç¨ç¨å¼ä¸ï¼ä½æ¯å
¶ä¸çååæ該ä¹é©ç¨æ¼å
¶ä»å°æ¹ã
+
+<h2>å°éæ
網ç«é²è¡æ¬åå</h2>
+<p class="right" align="right">
+ãå®ä¸å®å¾åèµ·ä¾ãã<br>
+ããï¼ï¼ç¶²çµ¡ç¬¬ä¸ççï¼RFC 1925
+
+<p>
+網é å¯ä»¥ç²åçºå
©ç¨®ï¼ãéæ
ã網é ç´å°ä¸ä¸æ¬¡æ´æ°çºæ¢ï¼é¨æé½æä¾ç¸åçå
§å®¹ï¼ãåæ
ã網é åä¾æå種å ç´ ï¼æä¾ç¸æçè³è¨ãé常æå稱åè
çºã網é æ件ãï¼ç¨±å¾è
çºã網é æç¨ç¨å¼ãã
+<p>
+ä¸éï¼å°ä¸åç使ç¨è
ä¾èªªï¼éæ
網é 並ä¸ä¸å®å¾ä¿æç¸åçãåç¾æ¹å¼ãï¼ï¼å®çèªè¨ã樣å¼æåªä»å¯ä»¥å 人èç°ãï¼ä¾å¦ï¼è¦é人士å¯è½æå好ç¨èªé³ï¼ä¾ä»£æ¿å½±å輸åºãï¼å
¨çè³è¨ç¶²çé·èä¹ä¸ï¼å°±å¨å®è½è®å®¢æ¶ç«¯è伺æå¨é²è¡äº¤æ¶ï¼æ±ºå®æåé©çåç¾æ¹å¼ã
+<p>
+èå實ä¾ä¾èªªï¼åè¨çè
æåä¸æ網é ï¼æ¾å¨<tt>http://www.autrijus.org/index.html</tt>ï¼
+
+<p align="center">
+<table border=2 align=center>
+<caption align=bottom><font size=-1>å表1. åºæ¬çä¸æ網é </font></caption>
+<tr><td><PRE>
+<html><head><title><B>åå®æ¼¢ - 家</B></title></head>
+<body><B>æ½å·¥ä¸, è«è¦è«</B></body></html>
+</PRE></td></tr></table>
+<p>
+æ天æå¿è¡ä¾æ½®ï¼æ³è¦å°å®è¯æè±æç給å¤å°çæåçï¼
+
+<p align="center">
+<table border=2 align=center>
+<caption align=bottom><font size=-1>å表2. ç¿»è¯è±æç網é </font></caption>
+<tr><td><PRE>
+<html><head><title><B>Audrey.Home</B></title></head>
+<body><B>Sorry, this page is under construction.</B></body></html>
+</PRE></td></tr></table>
+
+<P>
+éæï¼è¨±å¤ç¶²ç«ææä¾ä¸åãèªè¨é¸æé é¢ãï¼è®è¨ªå®¢æé¸é©åçèªè¨ï¼å¦ä¸æ示ï¼
+
+<p align="center">
+<table border=2 align=center>
+<caption align=bottom><font size=-1>å3: å
¸åçèªè¨é¸æé é¢</font></caption>
+<tr><td align="center" colspan=4 bgcolor=black>
+<font color="white">Please choose your language:</font>
+</td></tr><tr><td align="center">
+<font size=-1><u>ÄeÅ¡tina</u></font>
+</td><td align="center">
+<font size=-1><u>Deutsch</u></font>
+</td><td align="center">
+<font size=-1><u>English</u></font>
+</td><td align="center">
+<font size=-1><u>Español</u></font>
+</td></tr><tr><td align="center">
+<font size=-1><u>Français</u></font>
+</td><td align="center">
+<font size=-1><u>Hrvatski</u></font>
+</td><td align="center">
+<font size=-1><u>Italiano</u></font>
+</td><td align="center">
+<font size=-1><u>æ¥æ¬èª</u></font>
+</td></tr><tr><td align="center">
+<font size=-1><u>íêµì´</u></font>
+</td><td align="center">
+<font size=-1><u>Nederlands</u></font>
+</td><td align="center">
+<font size=-1><u>Polski</u></font>
+</td><td align="center">
+<font size=-1><u>Ð ÑÑÑкий ÑзÑк</u></font>
+</td></tr><tr><td align="center">
+<font size=-1><u>Slovensky</u></font>
+</td><td align="center">
+<font size=-1><u>Slovensci</u></font>
+</td><td align="center">
+<font size=-1><u>Svenska</u></font>
+</td><td align="center">
+<font size=-1><u>ä¸æ (GB)</u></font><br>
+<font size=-1><u>ä¸æ (Big5)</u></font>
+</td></tr></table>
+
+<p>
+å°ä¸è¬ç使ç¨è
åç¨å¼ä¾èªªï¼é樣çé é¢é½æ¨¡ç³ä¸æ¸
ãå¤é¤è麻ç
©ãå®ä¸ä½è¿«ä½¿æ¯ä½è¨ªå®¢å¤æä¸åéµï¼ä¹å°ç¶²é 代çç¨å¼çä½è
é æéç¤ï¼åæéä¸é ççµæ§ãé¸ææ£ç¢ºçéçµï¼æ¯ä»¶å¾å®¹æåºé¯çäºã
+
+<h3>MultiViewsï¼æç°¡å®çæ¬ååæ¶æ§</h3>
+<p>
+ç¶ç¶ï¼è¦æ¯è½è®æ¯å人èªååå¾é©åçèªè¨ï¼é£æ¯æ好äºãHTTP 1.1çæä¾çãå
§å®¹äº¤æ¶ï¼Content Negotiationï¼ãåè½ï¼å°±æ¯éæéåç®æ¨ç好辦æ³ã
+<p>
+å¨å
§å®¹äº¤æ¶çæ¶æ§ä¸ï¼ç覽å¨æå³éã<tt>Accept-Language</tt>ãæ¨é ï¼ä»£è¡¨ä½¿ç¨è
å好çèªè¨ãèä¾ä¾èªªï¼ã<tt>zh-tw, en-us, en</tt>ãçææå°±æ¯ãæ£é«ä¸æãç¾å¼è±æï¼ä¸ç¶å°±æ¯è±æãã
+<p>
+網ç«ä¼ºæå¨æ¥å°éé
è³è¨ä¹å¾ï¼ä¾¿è² 責æé¸æåé©çèªè¨çæ¬ï¼å³å給使ç¨è
ã實ä½æ¤é
æµç¨çæ¹å¼ï¼å種伺æå¨å¯è½ææä¸åï¼å¨æåæ¡è¿çApacheä¸ï¼å¯ä»¥å©ç¨ã<tt>MultiViews</tt>ãæè¡ä¾éæã
+<p>
+è¦ä½¿ç¨<tt>MultiViews</tt>æï¼æåå°è±æçåæ<tt>index.html.en</tt>ï¼æ³¨æå¾é¢ç<tt>.en</tt>ï¼ï¼åå°<tt>httpd.conf</tt>æ<tt>.htaccess</tt>è¨å®æªè£¡ï¼å ä¸éä¸åï¼
+<p>
+<PRE>
+ Options <b>+MultiViews</b>
+</PRE>
+<p>
+å¨æ¤ä¹å¾ï¼Apacheå°±æå¨æ¥å°<tt>http://www.autrijus.org/index.html</tt>çè¦æ±æï¼æª¢æ¥å®¢æ¶ç«¯æ¯å¦æ¼<tt>Accept-Language</tt>裡å好è±æï¼<tt>en</tt>ï¼ãé樣ä¸ä¾ï¼è±èªç³»çè®è
å°±æçå°è±æé é¢ï¼å
¶ä»äººåçå°åæ¬ç<tt>index.html</tt>ã
+<p>
+å©ç¨éåæè¡ï¼æå¯ä»¥è«åéå人幫å¿ï¼é漸å ä¸æ°çç¿»è¯çæ¬ï¼ï¼æ³æçæ¯<tt>index.html.fr</tt>ï¼<tt>index.html.he</tt>代表å¸ä¼¯ä¾æççã
+<p>
+ç±æ¼ç¶²è·¯ä¸ç許å¤äººï¼é½ç¥æèªå·±çæ¯èªåè±æï¼å æ¤æ°ççæ¬é常é½ä¸æ¯å¾ä¸æï¼èæ¯ç±è±æç¿»è¯éå»çãä¸éï¼æ¢ç¶ä¸è±æçå
§å®¹ç¸åï¼éä¹ä¸æåé¡ã
+<p>
+...ççæ²åé¡äºåï¼è¦æ¯æåªå¤©æ´æ°äºä¸æçå¢ï¼
+
+<h3>ä¿æç¿»è¯ææ並ä¸å®¹æ</h3>
+
+<p>
+å¨ææ¹å®åæ¬ç網é ä¹å¾ï¼å°±æ馬ä¸ç¼ç¾ï¼è² 責æ³æåå¸ä¼¯ä¾æçæåæ²è¾¦æ³çæä¸æï¼ï¼é¡¯ç¶ï¼ææå¿
è¦æºåä¸ä»½è±æçãæ¨æºçæ¬ãã許å¤èªç±è»é«å°æ¡æ£æ¯å çºé樣ï¼å°±ç®æ ¸å¿åéçæ¯èªä¸æ¯è±æï¼ä»ç¶æ¡ç¨è±æä½çºç¨å¼çé è¨èªç³»ã
+
+<p>
+æ¤å¤ï¼å°±ç®ç¥æ¯æ¹äºèæ¯é¡è²ï¼åæ¯<tt><body bgcolor="gold"></tt>ï¼ï¼æéæ¯å¾æ´æ°ææçè¯æ¬ï¼å¥½è®æ ¼å¼ä¿æä¸è´ã
+
+<p>
+è¥æ¯æåææ´æ°æ ¼å¼åå
§å®¹ï¼äºæ
就麻ç
©äºãä¸æ¦å¤±å»äºåå
çHTMLæ¨ç±¤ï¼å¹«å¿ç¿»è¯çæåå°±å¿
éå¾é ä¾éï¼é¤éä»åé½æ¯HTML大師ï¼ä¸ç¶é¦¬ä¸å°±æåºé¯ãè¦æ¯ç¶²ç«ä¸æ20åç¶å¸¸æ´æ°çé é¢ï¼é£å¾å¿«å°±æ²äººè¯åç¿»è¯ï¼ï¼è³é£æåä¹ç¶ä¸æäºã
+
+<p>
+å¾ä¸é¢çä¾åçä¾ï¼é¡¯ç¶æå¿
è¦å°è³æèæºç¢¼ï¼ä¹å°±æ¯æååæ¨ç±¤ï¼åéï¼ä¸¦ä¸èªååç¿»è¯çæ¬ççç¢æµç¨ã
+
+<h3>å©ç¨CGI.pmå°è³æèæºç¢¼åé</h3>
+
+<p>
+äºå¯¦ä¸ï¼ä¸ä¸æ®µå·²ç¶ä¸èªéç¡äºç¾ä»£çåéåï¼i18nï¼æµç¨ï¼å¨é²è¡ç¶²ç«æç¨ç¨å¼æ¬ååä¹åï¼å¿
é å
æ¾åºååè³æèæºç¢¼çæ¹æ³ã
+
+<p>
+å¤å¹´ä»¥ä¾ï¼Perlå°±æ¯ç¶²é éç¼çé¦é¸èªè¨ï¼ä¹æä¾äºå¤ä¸åæ¸çç模çµè網ç«å»ºè£½å·¥å
·ãå
¶ä¸ææ®éçï¼è¦ç®æ¯èª1997年以ä¾å°±ä½µå
¥æ¨æºç¨å¼åº«ç<tt>CGI.pm</tt>ãåºä¸çç¯ä¾ç¨å¼ï¼å°±æ¯å©ç¨<tt>CGI.pm</tt>ä¾èªåç¢çç¿»è¯çæ¬ï¼
+
+<p align="center">
+<table border=2 align=center>
+<caption align=bottom><font size=-1>å表3. å©ç¨MultiViewsåCGI.pmé²è¡æ¬åå</font></caption>
+<tr><td><PRE>
+use CGI ':standard'; <i># æ¤ç¨å¼ç模ç系統ï¼Templating Systemï¼</i>
+foreach my $language (qw(zh_tw en de fr)) {
+ open OUT, ">index.html.$language" or die $!;
+ print OUT start_html({ title => _(<b>"Audrey.Home"</b>) }),
+ _(<b>"Sorry, this page is under construction."</b>),
+ end_html;
+ sub _ { some_function($language, @_) } <i># XXX: éè£ä¸æ¬ååæ¶æ§</i>
+}
+</PRE></td></tr></table>
+
+<p>
+æ¤ç¨å¼å©ç¨<tt>CGI.pm</tt>çHTML</tt>ç¸éå½å¼ï¼éæè³æèæºç¢¼çåéï¼éé»èå®ç´çHTMLé é¢ä¸åãæ¨ç±¤ï¼å¦<tt><html></tt>ï¼è®æäºå½å¼å¼å«ï¼<tt>start_html()</tt>ï¼ï¼æåå以å串表示ãå æ¤ï¼å¨ç¢çæ¯ä»½æ¬å°åçé é¢æï¼<tt>index.html.zh_tw</tt>ã<tt>index.html.en</tt>ççï¼ï¼å°±è½ä¿æç¸åçHTMLæ ¼å¼ã
+
+<p>
+ã<tt>sub _</tt>ãéåå½å¼è² 責å«ç¨<tt>some_function()</tt>ï¼å°è¼¸å
¥çè±æåå¥ç¿»è¯æ<tt>$language</tt>è®æ¸æ代表çèªè¨ã<tt>some_function()</tt>å°±æ¯æ¤ç¨å¼çãæ¬ååæ¶æ§ï¼localization frameworkï¼ãï¼å¨ä¸ä¸ç¯è£¡ï¼æåæä»ç´¹ä¸ç¨®ä¸åçæ¶æ§ã
+
+<p>
+寫å®ä¸é¢é段å°ç¨å¼å¾ï¼æåç¥éç¨<tt>grep</tt>æ¾åºææã<tt>_(...)</tt>ã裡çå串ï¼å°å®å解å°ä¸ä»½è©å
¸ï¼lexiconï¼æªè£¡ï¼åè«è¯è
å¡«å
¥æéçç¿»è¯å³å¯ãå¨æ¤ï¼ãè©å
¸ãçå®ç¾©æ¯å
©ç¨®èªè¨ä¹éçå°ç
§è¡¨ï¼å
¶ä¸æäºé
ç®ç¥æä¸åå®åï¼å¦ã<tt>Cancel</tt>ãï¼ï¼ä½é常åå
å«æ´åå¥åï¼å¦ã<tt>Do you want to overwrite?</tt>ãæã<tt>5 files found.</tt>ãï¼ãåè§å
æåä¸çãå¤èªéæãä¸æ¨£ï¼è©å
¸è£¡çå串ä¹è¨±éæå¾
å¡«ç空ç½ï¼å¦åä¸åçä¸æâ±æµ·å°èªè©å
¸æ示ï¼
+
+<p align="center">
+<table border=2 align=center>
+<caption align=bottom><font size=-1>å4: ä¸æ => æµ·å°èªè©å
¸</font></caption>
+<tr>
+<td align="center" bgcolor=black><font color="white">ä¸æ</font></td>
+<td align="center" bgcolor=black><font color="white">Haitian</font></td>
+</tr><tr><td align="center">
+éåæ±è¥¿è¦ ___ å¡é¢ã
+</td><td>
+Bagay la kute ___ dola yo.
+</td></tr></table>
+
+<p>
+å¨çæ³æ
æ³ä¸ï¼è¯è
ç¥éè¦ç¿»è¯è©å
¸çå
§å®¹ï¼ä¸ç¨ç®¡HTMLæç¨å¼ç¢¼è£¡å¯«äºä»éº¼ãå¯æ¯ï¼ç±æ¼å種æ¬ååæ¶æ§çè©å
¸æ ¼å¼å½¼æ¤ä¸åï¼æåå¾å
é¸åºæé©åéé
å°æ¡çæ¶æ§ã
+
+<h2>æ¬ååæ¶æ§ä¸è¦½</h2>
+
+<p align="right" class=right>
+ãå®æ¯ä½ æ³åä¸éè¤éãã<br>
+ããï¼ï¼ç¶²çµ¡ç¬¬å
«ççï¼RFC 1925
+
+<p>
+è¦å¯¦ä½ä¸ä¸æ®µè£¡ç<tt>some_function()</tt>å½å¼ï¼éè¦è½è®åè©å
¸æªãæ¥åºç¸ç¬¦çå串ãçè³å°æ°çé
ç®è§£å°è©å
¸è£¡çç¨å¼åº«ãé樣çç¨å¼åº«ï¼å°±ç¨±çºãæ¬ååæ¶æ§ãã
+
+<p>
+ä¾çè
çç¶é©ï¼æ¬ååæ¶æ§ä¹éçä¸åï¼é常表ç¾å¨è©å
¸æªçµæ§çå·®ç°ä¸ãæ¥ä¸ä¾ï¼è®æåççPerlå¦ä½ä½¿ç¨å
¶ä¸çä¸ç¨®æ¶æ§ï¼å¾æç°¡å®ç<tt>Msgcat</tt>éå§ã
+
+<h3>Msgcatï¼ç¨é£åç¶è©å
¸</h3>
+
+<p>
+Msgcatæ¯ææ©çæ¬ååæ¶æ§ï¼ä¹æ¯XPG3â±XPG4æ¨æºçä¸é¨ä»½ï¼å æ¤å¨Unixå¹³å°ä¸é¨èå¯å¾ãå®æ¯ç¬¬ä¸ä»£çè©å
¸æªæ¶æ§ï¼å°ååå串以編è表示ï¼å¾ªåºåæ¾å¨åçºãè¨æ¯æ¸
å®ï¼message catalogï¼ãçé£å裡ãé種æ¶æ§å®¹æ實ä½ãç¯çè¨æ¶é«ï¼ä¸¦ä¸æ¥è©¢é度å¾å¿«ãå¨Windowsçå¹³å°ä¸çãè³æºæªï¼resource fileï¼ãä¹æ¯åºæ¼ç¸åçæ¦å¿µã
+
+<p>
+å°æ¼æ¯å網é åç¨å¼æªï¼Msgcaté½éè¦ä¸ä»½ç¸æçè©å
¸æªï¼æ ¼å¼å¦ä¸ï¼
+
+<p align="center">
+<table border=2 align=center>
+<caption align=bottom><font size=-1>å表4. <tt>Msgcat</tt> è©å
¸æª</font></caption>
+<tr><td><PRE>
+$set <b>7</b> <i># $Id: nls/de/index.pl.m</i>
+<b>1</b> Audrey'.Haus
+<b>2</b> Wir bitten um Entschudigung. Diese Seite ist im Aufbau.
+</PRE></td></tr></table>
+
+<p>
+é份æªæ¡å
å«index.html裡ææå串çå¾·æç¿»è¯ï¼å®çãéåç·¨èï¼set idï¼ãæ¯7ãå¨åå®ææé é¢çç¿»è¯ä¹å¾ï¼æåå©ç¨gencatéæ¯ç¨å¼ä¾ç¢çäºé²å¶çè©å
¸æªï¼
+<p>
+<pre>
+ % gencat nls/de.cat nls/de/*.m
+</pre>
+
+<p>
+äºé²å¶è©å
¸æªçå
§å®¹ï¼å¯ä»¥æ³ææ¯å¦ä¸æ示çäºç¶é£åï¼
+
+<p align="center">
+<table border=2 align=center>
+<caption align=bottom><font size=-1>å5: <tt>nls/de.cat</tt> çå
§å®¹</font></caption>
+<tr>
+<td align="right" bgcolor=black><font color="white">set_id<br>msg_id</font></td>
+<td align="center" bgcolor=black><font color="white">1</font></td>
+<td align="center" bgcolor=black><font color="white">2</font></td>
+<td align="center" bgcolor=black><font color="white">3</font></td>
+<td align="center" bgcolor=black><font color="white">4</font></td>
+<td align="center" bgcolor=black><font color="white">5</font></td>
+<td align="center" bgcolor=black><font color="white">6</font></td>
+<td align="center" bgcolor=black><font color="white">7</font></td>
+<td align="center" bgcolor=black><font color="white">8</font></td>
+<td align="center" bgcolor=black><font color="white">9</font></td>
+</tr><tr>
+<td align="center" bgcolor=black><font color="white">1</font></td>
+<td><i>...</i></td><td><i>...</i></td><td><i>...</i></td><td><i>...</i></td><td><i>...</i></td><td><i>...</i></td>
+<td><tt>Audrey'.Haus</tt></td>
+<td><i>...</i></td><td><i>...</i></td>
+</tr><tr>
+<td align="center" bgcolor=black><font color="white">2</font></td>
+<td><i>...</i></td><td></td><td><i>...</i></td><td><i>...</i></td><td><i>...</i></td><td><i>...</i></td>
+<td><tt>Wir bitten um Entschudigung...</tt></td>
+<td><i>...</i></td><td><i>...</i></td>
+</tr><tr>
+<td align="center" bgcolor=black><font color="white">3</font></td>
+<td><i>...</i></td><td></td><td><i>...</i></td><td><i>...</i></td><td></td><td><i>...</i></td><td><i>...</i></td><td><i>...</i></td><td><i>...</i></td>
+</tr></table>
+
+<p>
+è¦å¾è©å
¸æªä¸è®åå串ï¼å°±å¾å©ç¨CPANï¼Perl ç¶åå
¸è網ï¼ä¸ç<tt>Locale::Msgcat</tt>模çµï¼ä¾å¯¦ä½åé¢æå°çã<tt>sub _</tt>ãï¼
+
+<p align="center">
+<table border=2 align=center>
+<caption align=bottom><font size=-1>å表5. ç¤ºç¯ <tt>Locale::Msgcat</tt> çç¨æ³</font></caption>
+<tr><td><PRE>
+use Locale::Msgcat;
+my $cat = Locale::Msgcat->new;
+$cat->catopen("nls/$language.cat", 1); <i># æ³ææ¯äºç¶é£å</i>
+sub _ { $cat->catgets(<b>7</b>, @_) } <i># <b>7</b> æ¯ index.html çéåç·¨èï¼set_idï¼</i>
+print _(<b>1</b>, "Audrey.House"); <i># <b>1</b> æ¯é串æåçè©¢æ¯ç·¨èï¼msg_idï¼</i>
+</PRE></td></tr></table>
+
+<p>
+è¦æ³¨æçæ¯ï¼ç¥æ第ä¸ååæ¸ï¼<tt>msg_id</tt>ï¼æä½ç¨ï¼æ¥å¨å¾é¢ç<tt>"Audrey.House"</tt>ç¥æ¯ä½çºæ¥è©¢å¤±ææçé è¨å³åå¼ï¼ä»¥åè®äººæ¯è¼å®¹æçæèå·²ã
+
+<p>
+å çº<tt>set_id</tt>å<tt>msg_id</tt>ççµåå¿
é ç¨ä¸ç¡äºãä¸è½æ´æ¹ï¼å æ¤æ´æ°æç¥è½åªé¤æåç·¨èï¼èç¡æ³éè¤å©ç¨ãéé
ç¹æ§å¾å¾é ææ´æ°å°é£ï¼æ£å¦Drepperç人å¨GNU <tt>gettext</tt>說ææ件裡æåºçï¼
+
+<p>
+<blockquote>
+ãç¨å¼å¡æ¯æ¬¡éå°è¦ç¿»è¯çå串æï¼é½å¾å
å®ç¾©ä¸åæ¸å¼ï¼æ常æ¸ç¬¦èï¼ï¼åå°å®å é²è¨æ¯æ¸
å®è£¡ãä»éå¾è²»å¿é¿å
éè¤çå串ãéè¤çè¨æ¯ç·¨èççãå¦ææ³å
·æèGNU <tt>gettext</tt>ä¸æ¨£çè¨æ¯æ¸
å®å質ï¼éå¾å¨è£¡é¢å ä¸èªªæï¼ä¸¦è¨»æç¨å¼è£¡ååå串çä½ç½®ãéç°¡ç´æ¯ãä¸å¯è½çä»»åããã
+</blockquote>
+
+<p>
+å æ¤ï¼ç¥æå¨è©å
¸ååç©©å®çæ
æ³ä¸ï¼çºå¯ä»¥èæ
®ä½¿ç¨<tt>Msgcat</tt>ä½çºæ¬å°åæ¶æ§ã
+
+<p>
+æ¡ç¨<tt>Msgcat</tt>çç¨å¼ï¼éç¶å¸¸éå°ãè¤æ¸å½¢å¼ãçåé¡ãè«çä¸åç¨å¼ï¼
+
+<p align="center">
+<table border=2 align=center>
+<caption align=bottom><font size=-1>å表6. é¯èª¤çè¤æ¸å½¢å¼èç</font></caption>
+<tr><td><PRE>
+printf(_(8, "<b>%d</b> files were deleted."), $files);
+</PRE></td></tr></table>
+
+<p>
+ç¶<tt>$files</tt>çå¼çº<tt>1</tt>æï¼è¼¸åºçè¨æ¯é¡¯ç¶æ¯é¯èª¤çï¼è<tt>"%d file(s) were deleted"</tt>ä¹ä¸åè±æææ³ãå æ¤ï¼æåéå¾åæå
©åå串表示ä¸å¯ï¼
+
+<p align="center">
+<table border=2 align=center>
+<caption align=bottom><font size=-1>å表7. ç¥èæ
®è±æçè¤æ¸å½¢å¼èç</font></caption>
+<tr><td><PRE>
+printf(($files == 1) ? _(8, "<b>%d</b> file was deleted.")
+ : _(9, "<b>%d</b> files were deleted."), $files);
+</PRE></td></tr></table>
+
+<p>
+ä½å°±ç®é樣寫ï¼å¨è±æ以å¤çèªè¨ä»ç¶è¡ä¸éï¼ï¼æ³æçå®æ¸å½¢å¼å¨<tt>$files</tt>çº<tt>0</tt>æä¹é©ç¨ï¼èæ¯æ夫èªç³»çè¤æ¸å½¢å¼æ´å¤éä¸å°å種ï¼æ³å¨<tt>Msgcat</tt>çæ¶æ§ä¸ç
§é¡§å°éäºçæ³ï¼å¿
ç¶æ¯å¾åç¡åã
+
+<h3>Gettextï¼ç¨éæ¹ç¶è©å
¸</h3>
+
+<p>
+çºäºè§£æ±º<tt>Msgcat</tt>ç諸å¤åé¡ï¼Ulrich Drepperå¨1995å¹´åèUniforumç<tt>Gettext</tt>çé¢ï¼çºGNUè¨åéç¼äºä¸å¥æ¬åå系統ãæå°å¦ä»ï¼å¨ä»¥Cèªè¨éç¼çèªç±è»é«ç¶ä¸ï¼GNU <tt>gettext</tt>å·²å¼ç¶æçºæ¬ååçæ¨æºæ¶æ§ï¼ä¹å»£åC++ãTclåPythonç¨å¼å¡çæ¡è¿ã
+
+<p>
+Gettextä¸éè¦çºæ¯ä»½æºç¢¼æªæ¡æºååèªçè©å
¸ï¼èæ¯çºæ´åå°æ¡ï¼éå°æ¯ç¨®èªè¨å製ä½ä¸ä»½è©å
¸ï¼å稱ä½ãPOæªãï¼ãèä¾ä¾èªªï¼ä¸è¿°ç¶²é çå¾·æè©å
¸æªã<tt>de.po</tt>ãå¯è½å
§å®¹å¦ä¸ï¼
+
+<p align="center">
+<table border=2 align=center>
+<caption align=bottom><font size=-1>å表8. <tt>Gettext</tt> è©å
¸æª</font></caption>
+<tr><td><PRE>
+#: index.pl:4
+msgid "Audrey.Home"
+msgstr "Audrey'.Haus"
+
+#: index.pl:5
+msgid "Sorry, this site is under construction."
+msgstr "Wir bitten um Entschudigung. Diese Seite ist im Aufbau."
+</PRE></td></tr></table>
+
+<p>
+
+以ã<tt>#:</tt>ãéé çå
©åæ¯ç±xgettextéæ¯ç¨å¼èªåç¢ççã該ç¨å¼ææ¾åºæºç¢¼è£¡å¼å«<tt>gettext()</tt>çå°æ¹ï¼å°å®åä¾åºè§£å°è©å
¸æªè£¡ã
+
+<p>
+æ¥ä¸ä¾ï¼æåå·è¡<tt>msgfmt</tt>ï¼å¾<tt>po/de.po</tt>ç¢çäºé²å¶çè©å
¸æª<tt>locale/de/LC_MESSAGES/web.mo</tt>ï¼
+
+<pre>
+ % msgfmt locale/de/LC_MESSAGES/web.mo po/de.po
+</pre>
+
+<p>
+é樣ä¸ä¾ï¼ç¨å¼å°±å¯ä»¥ç¨CPANä¸ç<tt>Locale::gettext</tt>模çµä¾ååäºé²å¶å·è¡æªï¼å¦ä¸æ示ï¼
+
+<p align=center>
+<table border=2 align=center>
+<caption align=bottom><font size=-1>å表9. ç¤ºç¯ <tt>Locale::gettext çç¨æ³</tt></font></caption>
+<tr><td><PRE>
+use POSIX;
+use Locale::gettext;
+POSIX::setlocale(LC_MESSAGES, $language); <i># è¨å®ç®çèªè¨</i>
+textdomain("web"); <i># é常åç¨å¼å稱ç¸å</i>
+sub _ { gettext(@_) } <i># ç¥æ¯ gettext() ç簡寫</i>
+print _("Sorry, this site is under construction.");
+</PRE></td></tr></table>
+
+<p>
+æ°çç<tt>gettext</tt>ï¼glibc 2.2ç以ä¸ï¼æ´æä¾äº<tt>ngettext("%d file", "%d files", $files)</tt>çè¤æ¸å½¢å¼èçï¼ä¸éï¼<tt>Locale::gettext</tt>模çµç®åéæªæ¯æ´æ¤é
çé¢ãçè
å·²å°ä¿®æ£æªéåºï¼å¸æ該模çµçä½è
æç¡å¿«èçã
+
+<p>
+Also, <tt>gettext</tt> lexicons support multi-line strings, as well as reordering via <tt>printf</tt> and <tt>sprintf</tt>:
+æ¤å¤ï¼<tt>gettext</tt>çè©å
¸æªä¹æ¯æ´å¤åå串ï¼ä»¥åå©ç¨<tt>printf</tt>è<tt>sprintf</tt>å½å¼éæçæ´æè®æ¸é åºåè½ï¼
+
+<p align=center>
+<table border=2 align=center>
+<caption align=bottom><font size=-1>å表10. 使ç¨ç·¨èåæ¸çå¤åå串</font></caption>
+<tr><td><PRE>
+msgid ""
+"This is a multiline string"
+"with <b>%1$s</b> and <b>%2$s</b> as arguments"
+msgstr ""
+"ããã¯å¤ç·ã²ãå¤æ°ã¨ãã¦"
+"<b>%2$s</b> 㨠<b>%1$s</b> ã®ã§ãã"
+</PRE></td></tr></table>
+
+<p>
+æå¾ï¼GNU <tt>gettext</tt>å¥ä»¶éæç¸ç¶å®æ´çå·¥å
·éï¼<tt>msgattrib</tt>ã<tt>msgcmp</tt>ã<tt>msgconv</tt>ã<tt>msgexec</tt>ã<tt>msgfmt</tt>ã<tt>msgcat</tt>ã<tt>msgcomm</tt>...ï¼ï¼å¤§å¹
ç°¡åäºåä½µãæ´æ°ã管çè©å
¸æªçæµç¨ã
+
+<h3>Locale::Maketextï¼ç¨ç©ä»¶ç¶è©å
¸ï¼</h3>
+
+<p>
+<tt>Locale::Maketext</tt>æ¼1998å¹´ç±Sean M. Burkeæéç¼ï¼ä¸¦æ¼2001å¹´5æä¿®è¨å¾ï¼ä½µå
¥Perl 5.8ççæ ¸å¿ç¨å¼åº«ã
+
+<p>
+æ¤æ¨¡çµè<tt>Msgcat</tt>å<tt>Gettext</tt>çå½å¼å°åçæ¶æ§ä¸åï¼æ¯ä»¥ç©ä»¶å°åçè¨è¨ï¼è®ã<tt>Locale::Maketext</tt>ãä½çºæ½è±¡çåºåºé¡å¥ï¼è¡çåºç¨æ¼ç¹å®å°æ¡çãå°æ¡é¡å¥ããå°æ¡é¡å¥ï¼å稱å¦ã<tt>MyApp::L10N</tt>ãï¼é²ä¸æ¥è¡çåºæ¸åãèªè¨é¡å¥ãï¼å¦ã<tt>MyApp::L10N::it</tt>ããã<tt>MyApp::L10N::fr</tt>ãççã
+>
+<p>
+æè¬çèªè¨é¡å¥ï¼å°±æ¯å
·æå
¨å<tt>%Lexicon</tt>éæ¹çPerl模çµã<tt>%Lexicon</tt>裡çéµæ¯åæï¼é常æ¯è±æï¼çå串ï¼éæ¹å¼åæ¯ç¿»è¯éçå串ãèªè¨é¡å¥éå¯ä»¥å®ç¾©æäºæ¹æ³ï¼ä¾èçè©å
¸ä¸çææ³è®æçäºé
ã
+
+<p>
+è«è¦åºä¸ç¯ä¾ï¼
+
+<p align=center>
+<table border=2 align=center>
+<caption align=bottom><font size=-1>å表11. <tt>Locale::Maketext</tt> è©å
¸æªå使ç¨ç¯ä¾</font></caption>
+<tr><td><PRE>
+package MyApp::L10N;
+use base 'Locale::Maketext';
+
+package MyApp::L10N::de;
+use base 'MyApp::L10N';
+our %Lexicon = (
+ "[<b>quant</b>,_1,camel was,camels were] released." =>
+ "[<b>quant</b>,_1,Kamel wurde,Kamele wurden] freigegeben.",
+);
+
+package main;
+my $lh = MyApp::L10N->get_handle('de');
+print $lh->maketext("[<b>quant</b>,_1,camel was,camels were] released.", 5);
+</PRE></td></tr></table>
+
+<p>
+å©ç¨Maketextçãæ¹æ¬èèªæ³ãï¼è¯è
å¯ä»¥å¨å串ä¸åç¨å種ææ³å½å¼ãä¸é¢çä¾å示ç¯äºå
§å»ºçè¤æ¸å½¢å¼èéè©æ¯æ´ï¼å°æ¼éè¦ç¹å¥èçè¤æ¸å½¢å¼çèªè¨ï¼ç¥é實ä½ç¸æç<tt>quant()</tt>å½å¼å³å¯ãå樣çï¼ä¹å¾å®¹æå å
¥è½æåºæ¸èæéæ ¼å¼çåè½ã
+
+<p>
+æ¯åèªè¨é¡å¥ï¼éå¯ä»¥å®ç¾©èªå·±ç<tt>->encoding</tt>æ¹æ³ï¼æè¿°å
¶ä¸çè©å
¸ç·¨ç¢¼ï¼éå¯ä»¥ç¨ä¾å³çµ¦ã<tt>Encode</tt>ã模çµï¼é²è¡å³æè½ç¢¼ãèªè¨é¡å¥éä¹å¯ä»¥ç¸äºç¹¼æ¿ï¼<tt>fr_ca.pm</tt>ï¼å æ¿å¤§æ³èªï¼è£¡ç¼ºå°çå串ï¼é è¨æç±<tt>fr.pm</tt>ï¼ä¸è¬æ³èªï¼è£ä¸ã
+
+<p>
+å
§å»ºç<tt>->get_handle()</tt>æ¹æ³ï¼å¨æ²æåæ¸æï¼æèªåå¨CGIãmod_perlèå½ä»¤åä¸ï¼åµæ¸¬HTTPãPOSIXåWin32çå°åè¨å®ï¼ç¨å¼å¡æ¯é åååµæ¸¬ï¼å°±å¯ä»¥ç«å»åç¾é©åçèªç³»çµ¦ä½¿ç¨è
ã
+
+<p>
+ä¸éï¼ã<tt>Locale::Maketext</tt>ã並éå®ç¾ç¡ç¼ºãå®æ大çåé¡ï¼å°±æ¯ç¼ºä¹å¦GNU <tt>gettext</tt>çå·¥å
·éï¼ä¹å çºè©å
¸æªçèªæ³å¤ªæå½æ§ï¼ä½¿å¾ç·¨è¼¯å¨é£æåEmacsçã<tt>PO Mode</tt>ãé樣çæ¯æ´æ¨¡å¼ã
+
+<p>
+æå¾ï¼å çºæ¡ç¨Perl模çµä½çºè©å
¸æªï¼å°è´è¯è
å¿
é çæåºæ¬çPerlèªæ³ï¼ï¼ä¸ç¶ç話ï¼å°±å¾æ人幫å¿åæ ¼å¼è½æäºã
+
+<h3>Locale::Maketext::Lexiconï¼å
©å
¨å
¶ç¾</h3>
+
+<p>
+2002å¹´5æï¼æææ¼<tt>Locale::Maketext</tt>è©å
¸å¤ªéé¬æ£çæ ¼å¼ï¼æèæ實ä½å
¬å¸å
§é¨ä½¿ç¨çè©å
¸æ ¼å¼ï¼ä¸¦å¨perl-i18néµéè«å£ä¸å¾µè©¢å¤§å®¶çæè¦ãJesse Vincentåéï¼ãçºä»éº¼ä¸ç´æ¥ä½¿ç¨<tt>gettext</tt>çPOæªæ ¼å¼å¢ï¼ãæ¼æ¯æå°±è¨è¨äºæ½æå¼çå¾ç«¯ç³»çµ±ï¼è½æ¥åå種ä¸åæ ¼å¼çè©å
¸ãé樣ä¸ä¾ï¼ã<tt>Locale::Maketext::Lexicon</tt>ãå°±èªçäºã
+
+<p>
+æ¤æ¨¡çµè¨è¨æçåè¡·ï¼å¨çµå<tt>Locale::Maketext</tt>å½æ§ç表éå¼ï¼è廣æ³æ¯æ´ç<tt>gettext</tt>æ<tt>Msgcat</tt>æªæ¡æ ¼å¼ãå®ä¹æ¯æ´ç¹«çµï¼Tieï¼çé¢ï¼è®è©å
¸ä¹è½åæ¾å¨éè¯å¼è³æ庫ï¼æDBMæªä¸ã
+
+<p>
+åºä¸çæç¨ç¨å¼ï¼ç¤ºç¯äº<tt>Locale::Maketext::Lexicon</tt>çæ¸ç¨®ç¨æ³ï¼ä»¥åãGettextãå¾ç«¯ææ¯æ´ç延伸POæ ¼å¼ï¼
+
+<p align=center>
+<table border=2 align=center>
+<caption align=bottom><font size=-1>å表12. <tt>Locale::Maketext::Lexicon</tt> çç¯ä¾ç¨å¼</font></caption>
+<tr><td bgcolor=black align=right><PRE><font color=white>1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+</font></PRE></td><td><PRE>
+use CGI ':standard';
+use base 'Locale::Maketext'; <i># ç¹¼æ¿ get_handle()</i>
+
+<i># å種è©å
¸æ ¼å¼åä¾æº</i>
+use Locale::Maketext::Lexicon {
+ en => ['Auto'], fr => ['Tie' => 'DB_File', 'fr.db'],
+ de => ['Gettext' => \*DATA], zh_tw => ['Gettext' => 'zh_tw.mo'],
+};
+
+<i># å° main çåååé¡å¥ï¼å®ç¾©è©²èªè¨è£¡çåºæ¸å½å¼</i>
+use Lingua::EN::Numbers::Ordinate; use Lingua::FR::Numbers::Ordinate;
+sub en::ord { ordinate($_[1]) } sub fr::ord { ordinate_fr($_[1]) }
+sub de::ord { "$_[1]." } sub zh_tw::ord { "第 $_[1] å" }
+
+my $lh = __PACKAGE__->get_handle; <i># èªååå¾ç®åçå°åè¨å®</i>
+sub _ { $lh->maketext(@_) } <i># å¦æéè¦ï¼ä¹å¯ä»¥èªåé²è¡è½ç¢¼</i>
+
+print header, start_html, <i># [<b>*</b>,...] æ¯ [<b>quant</b>,...] ç簡寫</i>
+ _("You are my [<b>ord</b>,_1] guest in [<b>*</b>,_2,day].", $hits, $days), end_html;
+
+__DATA__
+# <i>以延伸 PO æ ¼å¼å¯«æçå¾·æè©å
¸</i>
+msgid "You are my <b>%ord(%1)</b> guest in <b>%*(%2,day)</b>."
+msgstr "Innerhalb <b>%*(%2,Tages,Tagen)</b>, sie sind mein <b>%ord(%1)</b> Gast."
+</PRE></td></tr></table>
+
+<p>
+ç¨å¼ç第2åè®<tt>main</tt>å¥ä»¶ç¹¼æ¿<tt>Locale::Maketext</tt>ï¼ä»¥åå¾<tt>get_handle</tt>æ¹æ³ã第5â¼8åå®ç¾©äºååèªè¨é¡å¥ï¼åèªæ¡ç¨ä¸åçè©å
¸æ ¼å¼åä¾æºï¼
+
+<ul>
+<li>ãAutoãå¾ç«¯å訴<tt>Locale::Maketext</tt>ç¡é ç¹å¥èçè±æï¼ï¼ç´æ¥å°æ¥å°çéæ¹éµå³åå³å¯ãæ¤å¾ç«¯ç¹å¥é©ååéå§æ°å¯«ç¨å¼ãéä¸æ³èçæ¬å°åççæ³ã
+<li>ãTieãå¾ç«¯å°æ³æç<tt>%Lexicon</tt>éæ¹ç¹«çµå°ä¸åBerkeley DBæªï¼å
¶ä¸çå串å¨éè¦æçºæåç¨ï¼å æ¤ä¸æ浪費é¡å¤çè¨æ¶é«ã
+<li>ãGettextãå¾ç«¯å¾ç£ç¢ä¸è®åç·¨è¯éçäºé²å¶MOæªï¼ä½çºä¸æçè©å
¸ï¼æ¤å¤ï¼å®ä¹å¾<tt>DATA</tt>æªæ¡ä»£èåå¾POæªæ ¼å¼çå¾·æè©å
¸ã
+</ul>
+
+<p>
+第11â¼13å實ä½ååèªè¨é¡å¥ç<tt>ord</tt>æ¹æ³ï¼è² 責å°åæ¸è½æçºè©²èªè¨çåºæ¸ï¼å¦1stã2ndã3rd...ï¼ãè±æèæ³ææ¡ç¨äºå
©åCPAN模çµéæï¼èå¾·æåä¸æåç¥éè¦å串å®æå³å¯ã
+
+<p>
+第15ååå¾ç®åå¥ä»¶çãèªè¨ä»£èï¼language handleï¼ãç©ä»¶ãå çºæ²æ給å®åæ¸ï¼å®æèªååµæ¸¬HTTP_ACCEPT_LANGUAGEç°å¢è®æ¸ãPOSIXç<tt>setlocale()</tt>è¨å®ï¼ä»¥åWindowsç°å¢ä¸ç<tt>Win32::Locale</tt>ã第16å實ä½äºç°¡å®çæ¬å°åå½å¼ï¼å°åæ¸ç´æ¥äº¤çµ¦è©²ç©ä»¶ç<tt>maketext</tt>æ¹æ³èçã
+
+<p>
+æå¾ï¼ç¬¬18â¼19åæå°åºä¸åç¶ç±æ¬å°åèççè¨æ¯ã第ä¸ååæ¸<tt>$hits</tt>æ交給<tt>ord</tt>æ¹æ³ï¼è<tt>$days</tt>å交給å
§å»ºç<tt>quant</tt>æ¹æ³èçï¼ï¼ã<tt>[*...]</tt>ãæ¯åé¢æéçã<tt>[quant,...]</tt>ãç簡寫ã
+
+<p>
+第22â¼24åæ¯ä»¥å»¶ä¼¸POæ ¼å¼å¯«æçç¯ä¾è©å
¸ãé¤äºèç±<tt>%1</tt>å<tt>%2</tt>調ååæ¸é åºä¹å¾ï¼å®ä¹æ¯æ´ã<tt>%function(args...)</tt>ãèªæ³ï¼ä»£è¡¨<tt>Locale::Maketext</tt>èªæ³ä¸çã<tt>[function,args...]</tt>ãï¼args裡åºç¾ç<tt>%1</tt>ã<tt>%2</tt>çè®æ¸ï¼é½æèªåæ¿ææ<tt>_1</tt>ã<tt>_2</tt>ççã
+
+<h2>實ä¾è¨è«</h2>
+
+<p class="right" align="right">
+ãæ²æ大å°éå«éåäºãã<br>
+ããï¼ï¼ç¶²çµ¡ç¬¬åççï¼RFC 1925
+
+<p>
+ç解äºæ¬ååæ¶æ§çåçå¾ï¼è®æåä¾ççå®åå¦ä½æç¨å°å¯¦éçæç¨ç¨å¼ä¸ã
+
+<p>
+å°ç¶²ç«æç¨ç¨å¼ä¾èªªï¼æ¬ååæ¶æ§å¹¾ä¹é½å¨ãåç¾ç³»çµ±ãï¼æ稱ã模ç系統ãï¼ä¸å¯¦ä½ï¼å çºå®æ±ºå®äºæç¨ç¨å¼å¦ä½åéè³æèæºç¢¼ãèä¾ä¾èªªï¼ãTemplate Toolkitãé¼åµä¹¾æ·¨çä¸å±¤å¼è³æ/æºç¢¼/模çæ¶æ§ï¼å樣åæ¡è¿çãMasonã系統åå¾åå°Perlç¨å¼ç¢¼å
§åµå¨æ¨¡ç裡ãéä¸ç¯è£¡ï¼æåæçå°é©ç¨æ¼éå
©ç¨®æ¶æ§çæ¬ååæ¹å¼ï¼å
¶ä¸çååï¼ä¹å樣é©ç¨æ¼ãAxKitãããHTML::Embperlãç模ç系統ä¸ã
+
+<h3>Request Trackerï¼Masonï¼</h3>
+
+<p>
+ãRequest Trackerãæ¯ç¬¬ä¸å以<tt>Locale::Maketext::Lexicon</tt>ä½çºæ¬ååæ¶æ§çæç¨ç¨å¼ãå®çãåºåºèªè¨é¡å¥ãæ¯ã<tt>RT::I18N</tt>ãï¼åé¡å¥åç±åä¸åç®éä¸ç<tt>*.po</tt>æªæ¡ç¢çã
+
+<p>
+é¤æ¤ä¹å¤ï¼å®ç<tt>->maketext</tt>æ¹æ³ä¹å©ç¨ã<tt>Encode</tt>ã模çµï¼å¨5.8ç以åçPerlï¼åæ¯å©ç¨æçã<tt>Encode::compat</tt>ã模çµï¼ç´æ¥å³åUTF-8編碼çè³æãé樣ä¸ä¾ï¼è² 責ä¸æçè¯è
å°±å¯ä»¥ç¨Big5碼編輯è©å
¸æªï¼ç³»çµ±å»å¯ä»¥å°å®è¦çºè¬å碼ï¼Unicodeï¼èçã
+
+<p>
+å¨RTçPerlæºç¢¼è£¡ï¼ææç©ä»¶é½å©ç¨å¾<tt>RT::Base</tt>ç¹¼æ¿ç<tt>$self->loc</tt>æ¹æ³ä¾ç¿»è¯è¨æ¯ï¼
+
+<p align=center>
+<table border=2 align=center>
+<caption align=bottom><font size=-1>å表13. RT çæ¬ååæ¶æ§</font></caption>
+<tr><td><PRE>
+sub RT::Base::loc
+ { $self->CurrentUser->loc(@_) }
+sub RT::CurrentUser::loc
+ { $self->LanguageHandle->maketext(@_) }
+sub RT::CurrentUser::LanguageHandle
+ { $self->{'LangHandle'} ||= RT::I18N->get_handle(@_) }
+</PRE></td></tr></table>
+
+<p>
+å¦ä¸æ示ï¼ç¿»è¯ææ¡ç¨çæ¯ç¾è¡ä½¿ç¨è
çèªç³»ï¼å æ¤å¤å使ç¨è
å¯ä»¥èç±ä¸åèªè¨ï¼åæå·è¡éåç¨å¼ãå°Mason模çä¾èªªï¼åæ¡ç¨äºå
©ç¨®æ¹å¼ï¼
+
+<p align=center>
+<table border=2 align=center>
+<caption align=bottom><font size=-1>å表14. å
©ç¨®æ¨ç¤º Mason 模çä¸å串çæ¹å¼</font></caption>
+<tr><td><PRE>
+% $m->print(<b>loc(</b>"<b>Another line of text</b>", $args...<b>)</b>);
+<&<b>|/l</b>, $args...&><b>Single line of text</b></&>
+</PRE></td></tr></table>
+<p>
+第ä¸åçæ¹å¼ï¼ç¨å¨å
§åµçPerlæºç¢¼è<tt><%PERL></tt>段è½ä¹ä¸ï¼å®æèªåå«ç¨ç¾è¡ä½¿ç¨è
ç<tt>->loc</tt>æ¹æ³ï¼äº¤ç±ä¸è¿°çå½å¼èçã
+
+<p>
+第äºååå©ç¨<tt>HTML::Mason</tt>模çµææä¾çãé濾å
件ï¼filter componentï¼ãåè½ï¼å°ä¸éç"Single line of text"å³çµ¦ã<tt>/l</tt>ãå
件ï¼ä¹è¨±ééå åæ¸ï¼ï¼æå¾å顯示該å
件å³åçå串ãåºä¸æ¯æ¤å
件ç實ä½æ¹å¼ï¼
+
+<p align=center>
+<table border=2 align=center>
+<caption align=bottom><font size=-1>å表15. <tt>html/l</tt> é濾å
件ç實ä½æ¹å¼</font></caption>
+<tr><td><PRE>
+% my $hand = $session{'CurrentUser'}->LanguageHandle;
+% $m->print($hand->maketext($m->content, @_));
+</PRE></td></tr></table>
+
+<p>
+æäºéäºæ¹å¼ï¼ç¥è¦å°æ¢æ模ç裡çè¨æ¯è§£æè©å
¸æªï¼åå¯çµ¦è¯è
å°±è¡äºãæ¾åº700å¤åè¨æ¯çå·¥ä½ç´è²»æä¸åææï¼æ´ååéåâ±æ¬ååçæµç¨åè±äºä¸å°å
©åæçæéã
+
+<h3>Slashï¼Template Toolkitï¼</h3>
+
+<p>
+Slashï¼é¡ä¼¼æ¼Slashdotçèªå說æ¸é¦é ï¼Slashdot Like Automated Storytelling Homepageï¼æ¯Slashdotèå¾çç¨å¼ãä¸éï¼Slashæ´æ¯å®æ´ç網ç«ç建置ç°å¢ï¼å»ºç«å¨Andy WardleyçãTemplate Toolkitã模çµä¸ã
+
+<p>
+åºæ¼TT2乾淨çè¨è¨ï¼Slashå°æºç¢¼èè³æå¾¹åºåéï¼éé»èRTâ±Masonä¸åãå æ¤ï¼å¹¾ä¹æ²æå¿
è¦å¨Perlç¨å¼æªè£¡é²è¡æ¬ååçå·¥ä½ã
+
+<p>
+å¨æ¬ææ°å¯«ä¹åï¼æ許å¤ç´æ¥å°æ¨¡çç¿»è¯èæçãæ¬ååãçæ¬ï¼å
æ¬ä¸æãæ¥æãå¸ä¼¯ä¾æçãç¶èï¼å¨æ°çéåºæéè¦åçåä½µï¼mergeï¼åä½é常å°é£ï¼å¤æç¨å¼å°±æ´å¥æäºï¼ï¼å æ¤ç¿»è¯çæ¬å¾å¾å»¶é²è¨±ä¹
çºéåºã
+
+<p>
+é裡ï¼æåæåºä¸åè¼å¥½ç解決æ¹å¼ï¼å¨æ¨¡çæä¾å½å¼ä¸æ¶è¨ãèªå解è¯å±¤ãï¼å©ç¨<tt>HTML::Parser</tt>å<tt>Template::Parser</tt>實ä½ãå®çåè½å¦ä¸æ示ï¼
+
+<p align=center>
+<table border=2 align=center>
+<caption align=bottom><font size=-1>å表16. TT2 解è¯å±¤ç輸å
¥è輸åº</font></caption>
+<tr><td bgcolor=black><font color=white>輸å
¥</font></td><td><PRE>
+<B>from the [% story.dept %] dept.</B>
+</PRE></td></tr>
+<tr><td bgcolor=black><font color=white>輸åº</font></td><td><PRE>
+<B>[%<b>|loc(</b> story.dept <b>)</b>%]from the [<b>_1</b>] dept.[%END%]</B>
+</PRE></td></tr></table>
+
+<p>
+ç¼å°çè®è
æç¼ç¾ï¼é樣çè½è¯å±¤æ碰å°è·<tt>Msgcat</tt>ç¸åçåé¡ï¼ï¼è¦æ¯æåæ³å°<tt>[% story.dept %]</tt>è½æåºæ¸ï¼ææ¯æ<tt>dept.</tt>ä¾è¤æ¸å½¢å¼å±éæ<tt>department</tt>â±<tt>departments</tt>ç話ï¼è©²æ麼辦å¢ï¼å樣çåé¡ï¼ä¹åºç¾å¨RTç網é çé¢è£¡ï¼éè¦ç¿»è¯å¾å¤é¨æ¨¡çµå³åçè¨æ¯æï¼èä¾ä¾èªªï¼<tt>"Successfully deleted 7 ticket(s) in 'c:\temp'."</tt>é樣çå串è¦æ麼翻å¢ï¼
+
+<p>
+çè
ç解決æ¹æ³ï¼æ¯éç¼<tt>Locale::Maketext::Fuzzy</tt>模çµï¼ç¨ä¾å°å·²ç¶å®æè®æ¸çå串ï¼èè©å
¸æªé²è¡æ¯å°ï¼ï¼ä¾å¦åè¿°çå串ï¼å°±å¯è½ç¬¦å<tt>"Successfully deleted [*,_1,ticket] in '[_2]'."</tt>éåè¨æ¯ãè¦æ¯ç¬¦åçå串ä¸æ¢ä¸åï¼ç¢ç«ï¼<tt>"Successfully [_1]."</tt>ä¹ç®ç¬¦åï¼ï¼è©²æ¨¡çµæä¾å¾ªç¶é©æ³åï¼æ¾åºæé©åççæ¡ã
+
+<p>
+æé
<tt>xgettext.pl</tt>éæ¯ç¨å¼ï¼éç¼è
å¯ä»¥çºæ¯å¥å¤æç¨å¼åä½æ¯ä¸»é¡éä¸åèªçè©å
¸æªï¼ä¾Slash系統é²è¡å¤å±¤æ¬¡çèçï¼å
å試æ屬å¤æç¨å¼çè©å
¸ï¼æ¥èå試ä½æ¯ä¸»é¡ï¼æå¾çºä»¥å
¨åçè©å
¸æªä½çºé è¨ã
+
+<h2>總çµ</h2>
+
+<p class="right" align="right">
+ãæè¬ç¡åç¡ç¾ï¼ä¸¦éä¸è½åå ï¼èæ¯ä¸è½åæ¸ãã<br>
+ããï¼ï¼ç¶²çµ¡ç¬¬åäºççï¼RFC 1925
+
+<p>
+å¾ä»¥ä¸çå
©åä¾å裡ï¼å¯ä»¥ç´ç¥çåºæ¬ååçå
±éæµç¨ãéä¸ç¯æä»ç´¹å¦ä½ç¶ç±ååé段ï¼ä¾æ¬ååæ¢æç網ç«æç¨ç¨å¼ï¼ä»¥åä¸äºç¸éçå°ç¥è¨£ã
+
+<h3>æ¬ååçæµç¨</h3>
+
+æ¬ååçæµç¨ï¼å¯ä»¥ä¾åºç¸½çµæä¸åå¹¾é
æ¥é©ï¼
+
+<ol>
+<li>è©ä¼°ç¶²é ç模ç系統ã
+<li>é¸æä¸é
æ¬ååæ¶æ§ï¼å°å®è模ç系統éæ¥ä¸ã
+<li>ç¨ç¨å¼æ¾åºæ¨¡ç裡çæåå串ï¼ç¨é濾å½å¼é²è¡ä»£æã
+<li>解åºæ¸¬è©¦ç¨çè©å
¸ï¼æåä¿®ææ顯çåé¡ã
+<li>æåæ¾åºæºç¢¼è£¡çæåå串ï¼å°å®å代ææã<tt><b>_(</b>...<b>)</b></tT>ãå½å¼å¼å«ã
+<li>å解åºä¸ä»½æ¸¬è©¦è©å
¸ï¼äº¤ç±æ©å¨ç¿»è¯ã
+<li>實å°æ¸¬è©¦æ¬ååçæ¬ï¼ä¿®æé¤ä¸çåé¡ã
+<li>解åºè©¦ç¨ççè©å
¸ï¼å¯çµ¦ç¿»è¯åéæ ¡è¨ã
+<li>ä¿®æè¯è
åå ±çåé¡ï¼å¯åºæ£å¼ççè©å
¸ï¼
+<li>å¨æ¯åæ°çéåºåï¼å®ææ´çè©å
¸è£¡æ°çé
ç®ï¼éç¥è¯è
é²è¡ç¿»è¯ã
+</ol>
+
+ç
§è以ä¸çæ¥é©ï¼ä½ å°±å¯ä»¥è¼é¬å°ç®¡çæ¬ååçå°æ¡ï¼ä¸¦ä¸ä¿æç¿»è¯ææãæ¸å°é¯èª¤ã
+
+<h3>ä¸äºå°ç¥è¨£</H3>
+
+æå¾ï¼é裡æ¯å°ç¶²é èå
¶ä»æç¨ç¨å¼é²è¡æ¬ååæï¼å¹¾é»éè¦æ³¨æçäºé
ï¼
+
+<ul>
+<li>å¨è¨è¨å實ä½é段ï¼é½æå°è³æèæºç¢¼åéã
+<li>ä¸è¦å¨ç¶²ç«æç¨å¼æå½¢åï¼å°±å
è¨è¨åéå/æ¬ååçæ¶æ§ã
+<li>é¿å
å
§å«æåçåæªã
+<li>é çæ¨ç±¤åæéæéç空ç½ï¼ï¼é¿å
è®æä½ä»é¢é度ææ ã
+<li>使ç¨å®æ´çå¥åï¼èéç段ï¼
+</ul>
+
+<p align=center>
+<table border=2 align=center>
+<caption align=bottom><font size=-1>å表17. ç段èå®æ´å¥</font></caption>
+<tr><td><PRE>
+_("Found ") . $files . _(" file(s)."); <i># ä¸å®æ´çå¥åï¼ï¼é¯äºï¼</i>
+sprintf(_("Found %s file(s)."), $files); <i># å®æ´å¥ï¼å©ç¨sprintfï¼</i>
+_("Found [*,_1,file].", $files); <i># å®æ´å¥ï¼ç¨Locale::Maketextï¼</i>
+</PRE></td></tr></table>
+
+<ul>
+<li>ä¸åèªå¢ä¸çå樣å串ï¼æ該ä½åºååãèä¾ä¾èªªï¼RT裡ç<tt>"Home"</tt>åæ¬å
·æ<tt>"Homepage"</tt>å<tt>"Home Phone No."</tt>çå
©ç¨®ç¨æ³ã
+<li>å¹³çå°å¾
è¯è
ï¼å¥å°ä»åè¦çºä¸å±¬ï¼é¿å
æ
èªä¿®æ¹è©å
¸æªã
+<li>å
è®ä¸å人è¯åºè稿ï¼åè«å
¶ä»äººä¿®æ¹ã
+<li>ç¡éå¨è©å
¸æªè£¡æä¾è¨»è§£åæè¿°è³è¨ï¼
+</ul>
+
+<p align=center>
+<table border=2 align=center>
+<caption align=bottom><font size=-1>å表18. è©å
¸æªè£¡ç註解</font></caption>
+<tr><td><PRE>
+#: lib/RT/Transaction_Overlay.pm:579
+#. ($field, $self->OldValue, $self->NewValue)
+# <i>è«æ³¨æï¼åºä¸çãchanged toãæææ¯ãå·²æ¹çº...ãã</i>
+msgid "<b>%1 %2</b> changed to <b>%3</b>"
+msgstr "<b>%1 %2</b> cambiado a <b>%3</b>"
+</PRE></td></tr></table>
+
+<p>
+å©ç¨<tt>Locale::Maketext::Lexicon</tt>å¥ä»¶ä¸éçã<tt>xgettext.pl</tt>ãéæ¯ç¨å¼ï¼å¯ä»¥èªåç¢çè©å
¸æªä¸çæºç¢¼æªåãåèï¼ä»¥<tt>#:</tt>表示ï¼ãè®æ¸ï¼ä»¥<tt>#.</tt>表示ï¼ï¼ä¸¦é¨ææ´æ°ãå¦ä¸æ示ï¼å°å¤ªçææ義ä¸æçå¥åå ä¸è¨»è§£ï¼ä»¥<tt>#</tt>表示ï¼ï¼å°ç¿»è¯ä¹ææå¾å¤§ç幫å©ã
+
+<h2>çµè«</h2>
+
+<p>
+å°éè±èªç³»å家ç人æ°ä¾èªªï¼æ¬ååå¾å¾æ¯åèèªç±è»é«å°æ¡çåæã以èºç£èè¨ï¼CLEï¼ä¸æLinuxç°å¢ï¼ãDebian-ChineseåFreeBSD-Chineseçæ¬ååå°æ¡ï¼é½æ¯ç¤¾ç¾¤è²¢ç»çèéç¦é»ãç¶èï¼æ以è±æçºä¸»çåµåè»é«æ¶æ§æè³ï¼éå¾å¾ä¹æ¯èæè²»åã容æåºé¯çå·¥ä½ãå°è¯è
ä¾èªªï¼ãé²å
¥éç¤ãéæ¯å¤ªé«äºäºã
+
+<p>
+話說åä¾ï¼å¨æ¥æ¼¸æé«ç網é åéå趨å¢ä¸ï¼ç¶²ç«æç¨ç¨å¼ç¶å¸¸æç¿»è¯æå¤åèªè¨çæ©æãèä¾ä¾èªªï¼Sean M.Burkeå¨2002å¹´é å°ç±å¿ç使ç¨è
ï¼å°å»£åæ¡è¿çãApache::MP3ãç·ä¸é»æ模çµè¯ææ¸å種èªè¨ççæ¬ã模çµçåä½è
Lincoln D. Steinå®å
¨æªåèç¿»è¯ï¼ï¼ä»ç¥è¦å°ä¿®æ£æªåè©å
¸ä½µå
¥ä¸åéåºçæ¬å°±è¡äºã
+
+<p>
+èªç±è»é«ä¸¦éä¸å æ½è±¡çç¨å¼ï¼èæ¯é 人åå
±äº«æºç¢¼ãå½¼æ¤æä¾å»ºè°çç±æ
èåå¨çãå æ¤ï¼æèª æ°å°å¸ææ¬æä»ç´¹çæå·§ï¼è½é¼åµç¨å¼å¡å使ç¨è
å¨è¢«åç¿»è¯ä¹å¤ï¼ä¹è½ä¸»ååéåæ¢æçæç¨ç¨å¼ã
+
+<h2>è´è¬</h2>
+
+<p>
+æè¬Jesse Vincent建è°æéç¼<tt>Locale::Maketext::Lexicon</tt>ï¼ä»¥åè®æåå©ä»è¨è¨RTçæ¬ååæ¶æ§ãæä¹è¦æè¬Sean M. Burkeåµé äº<tt>Locale::Maketext</tt>ï¼ä¸¦é¼åµæ實é©å種ä¸åçè©å
¸æ ¼å¼ã
+
+<p>
+æè¬æå¨å²ç¾ç¶²çåäºåï¼ç°¡ä¿¡æãé«åè¯ãç¿åå©·ãæå
寰ï¼çºæ¬åå網é æç¨ç¨å¼ä»åºçåªåãä¹è¬è¬é§±é¦¬æ¸ï¼Perlå¸ç¿æåï¼çè¯è
群ï¼è®æå¸å°äºåæ£å¼ç¿»è¯åéçåéã
+
+<p>
+æè¬Nick Ing-Simmonsãå°é£¼å¼¾åJarkko Hietaniemiææå¦ä½å©ç¨<tt>Encode</tt>模çµï¼Bruno Haibleå
許æåç¨ä»å¼·å¤§çGNU libiconvï¼ä»¥åå®®å·éå½¥å°<tt>Locale::Maketext::Lexicon</tt>æ©æçæ¬çæ ¡è¨åå©ã
+
+<p>
+æå¾ï¼å¦æä½ é¡æä¾ç
§æ¬æ裡çæ¥é©ï¼åèè»é«çåéåèæ¬å°åï¼è«å®¹æåä½ è´ä¸æé«çè¬å¿±ï¼è®æåä¸èµ·æé ãå
¨çãçè³è¨ç¶²å§ï¼
+
+<h2>åèè³æ</h2>
+
+<ul>
+<li>Harald Tveit Alvestrandï¼ãRFC 1766: Tags for the Identification of Languagesãï¼1995å¹´ï¼<a href="ftp://ftp.isi.edu/in-notes/rfc1766.txt"><tt>ftp://ftp.isi.edu/in-notes/rfc1766.txt</tt></a>ã
+<li>Ross Callonç·¨ï¼ãRFC 1925: The Twelve Networking Truthsãï¼1996å¹´ï¼<a href="ftp://ftp.isi.edu/in-notes/rfc1925.txt"><tt>ftp://ftp.isi.edu/in-notes/rfc1925.txt</tt></a>
+<li>Ulrich DrepperãPeter MillerãFrancois Pinardï¼GNU "gettext"ï¼1995-2001å¹´ãéå¨<a href="ftp://prep.ai.mit.edu/pub/gnu/"><tt>ftp://prep.ai.mit.edu/pub/gnu/</tt></a>å¥ä»¶ç說ææ件ä¸ã
+<li>BurkeãLachlerï¼ãLocalization and Perl: gettext breaks, Maketext fixesãï¼1999å¹´ï¼åºçæ¼Perl Journalï¼ç¬¬13æã
+<li>Sean M. Burkeï¼ãLocalizing Open-Source Softwareãï¼2002å¹´ï¼åºçæ¼Perl Journalï¼2002å¹´ç§å£èã
+<li>W3C åéåè¡å宣è¨ï¼2001å¹´ï¼<a href="http://www.w3.org/International/Activity.html"><tt>http://www.w3.org/International/Activity.html</tt></a>
+<li>Mozilla åéååæ¬ååæå¼ï¼1999å¹´ï¼<a href="http://www.mozilla.org/docs/refList/i18n/"><tt>http://www.mozilla.org/docs/refList/i18n/</tt></a>
+</ul>
+
+</body></html>
Added: Locale-Maketext-Lexicon/inc/Module/Install.pm
==============================================================================
--- (empty file)
+++ Locale-Maketext-Lexicon/inc/Module/Install.pm Sun Apr 2 05:40:20 2006
@@ -0,0 +1,266 @@
+#line 1
+package Module::Install;
+
+# For any maintainers:
+# The load order for Module::Install is a bit magic.
+# It goes something like this...
+#
+# IF ( host has Module::Install installed, creating author mode ) {
+# 1. Makefile.PL calls "use inc::Module::Install"
+# 2. $INC{inc/Module/Install.pm} set to installed version of inc::Module::Install
+# 3. The installed version of inc::Module::Install loads
+# 4. inc::Module::Install calls "require Module::Install"
+# 5. The ./inc/ version of Module::Install loads
+# } ELSE {
+# 1. Makefile.PL calls "use inc::Module::Install"
+# 2. $INC{inc/Module/Install.pm} set to ./inc/ version of Module::Install
+# 3. The ./inc/ version of Module::Install loads
+# }
+
+use 5.004;
+use strict 'vars';
+
+use vars qw{$VERSION};
+BEGIN {
+ # All Module::Install core packages now require synchronised versions.
+ # This will be used to ensure we don't accidentally load old or
+ # different versions of modules.
+ # This is not enforced yet, but will be some time in the next few
+ # releases once we can make sure it won't clash with custom
+ # Module::Install extensions.
+ $VERSION = '0.61';
+}
+
+# Whether or not inc::Module::Install is actually loaded, the
+# $INC{inc/Module/Install.pm} is what will still get set as long as
+# the caller loaded module this in the documented manner.
+# If not set, the caller may NOT have loaded the bundled version, and thus
+# they may not have a MI version that works with the Makefile.PL. This would
+# result in false errors or unexpected behaviour. And we don't want that.
+my $file = join( '/', 'inc', split /::/, __PACKAGE__ ) . '.pm';
+unless ( $INC{$file} ) {
+ die <<"END_DIE";
+Please invoke ${\__PACKAGE__} with:
+
+ use inc::${\__PACKAGE__};
+
+not:
+
+ use ${\__PACKAGE__};
+
+END_DIE
+}
+
+use Cwd ();
+use File::Find ();
+use File::Path ();
+use FindBin;
+
+*inc::Module::Install::VERSION = *VERSION;
+ at inc::Module::Install::ISA = __PACKAGE__;
+
+sub autoload {
+ my $self = shift;
+ my $who = $self->_caller;
+ my $cwd = Cwd::cwd();
+ my $sym = "${who}::AUTOLOAD";
+ $sym->{$cwd} = sub {
+ my $pwd = Cwd::cwd();
+ if ( my $code = $sym->{$pwd} ) {
+ # delegate back to parent dirs
+ goto &$code unless $cwd eq $pwd;
+ }
+ $$sym =~ /([^:]+)$/ or die "Cannot autoload $who - $sym";
+ unshift @_, ($self, $1);
+ goto &{$self->can('call')} unless uc($1) eq $1;
+ };
+}
+
+sub import {
+ my $class = shift;
+ my $self = $class->new(@_);
+ my $who = $self->_caller;
+
+ unless ( -f $self->{file} ) {
+ require "$self->{path}/$self->{dispatch}.pm";
+ File::Path::mkpath("$self->{prefix}/$self->{author}");
+ $self->{admin} = "$self->{name}::$self->{dispatch}"->new( _top => $self );
+ $self->{admin}->init;
+ @_ = ($class, _self => $self);
+ goto &{"$self->{name}::import"};
+ }
+
+ *{"${who}::AUTOLOAD"} = $self->autoload;
+ $self->preload;
+
+ # Unregister loader and worker packages so subdirs can use them again
+ delete $INC{"$self->{file}"};
+ delete $INC{"$self->{path}.pm"};
+}
+
+sub preload {
+ my ($self) = @_;
+
+ unless ( $self->{extensions} ) {
+ $self->load_extensions(
+ "$self->{prefix}/$self->{path}", $self
+ );
+ }
+
+ my @exts = @{$self->{extensions}};
+ unless ( @exts ) {
+ my $admin = $self->{admin};
+ @exts = $admin->load_all_extensions;
+ }
+
+ my %seen;
+ foreach my $obj ( @exts ) {
+ while (my ($method, $glob) = each %{ref($obj) . '::'}) {
+ next unless exists &{ref($obj).'::'.$method};
+ next if $method =~ /^_/;
+ next if $method eq uc($method);
+ $seen{$method}++;
+ }
+ }
+
+ my $who = $self->_caller;
+ foreach my $name ( sort keys %seen ) {
+ *{"${who}::$name"} = sub {
+ ${"${who}::AUTOLOAD"} = "${who}::$name";
+ goto &{"${who}::AUTOLOAD"};
+ };
+ }
+}
+
+sub new {
+ my ($class, %args) = @_;
+
+ # ignore the prefix on extension modules built from top level.
+ my $base_path = Cwd::abs_path($FindBin::Bin);
+ unless ( Cwd::abs_path(Cwd::cwd()) eq $base_path ) {
+ delete $args{prefix};
+ }
+
+ return $args{_self} if $args{_self};
+
+ $args{dispatch} ||= 'Admin';
+ $args{prefix} ||= 'inc';
+ $args{author} ||= ($^O eq 'VMS' ? '_author' : '.author');
+ $args{bundle} ||= 'inc/BUNDLES';
+ $args{base} ||= $base_path;
+ $class =~ s/^\Q$args{prefix}\E:://;
+ $args{name} ||= $class;
+ $args{version} ||= $class->VERSION;
+ unless ( $args{path} ) {
+ $args{path} = $args{name};
+ $args{path} =~ s!::!/!g;
+ }
+ $args{file} ||= "$args{base}/$args{prefix}/$args{path}.pm";
+
+ bless( \%args, $class );
+}
+
+sub call {
+ my ($self, $method) = @_;
+ my $obj = $self->load($method) or return;
+ splice(@_, 0, 2, $obj);
+ goto &{$obj->can($method)};
+}
+
+sub load {
+ my ($self, $method) = @_;
+
+ $self->load_extensions(
+ "$self->{prefix}/$self->{path}", $self
+ ) unless $self->{extensions};
+
+ foreach my $obj (@{$self->{extensions}}) {
+ return $obj if $obj->can($method);
+ }
+
+ my $admin = $self->{admin} or die <<"END_DIE";
+The '$method' method does not exist in the '$self->{prefix}' path!
+Please remove the '$self->{prefix}' directory and run $0 again to load it.
+END_DIE
+
+ my $obj = $admin->load($method, 1);
+ push @{$self->{extensions}}, $obj;
+
+ $obj;
+}
+
+sub load_extensions {
+ my ($self, $path, $top) = @_;
+
+ unless ( grep { lc $_ eq lc $self->{prefix} } @INC ) {
+ unshift @INC, $self->{prefix};
+ }
+
+ local @INC = ($path, @INC);
+ foreach my $rv ( $self->find_extensions($path) ) {
+ my ($file, $pkg) = @{$rv};
+ next if $self->{pathnames}{$pkg};
+
+ local $@;
+ my $new = eval { require $file; $pkg->can('new') };
+ unless ( $new ) {
+ warn $@ if $@;
+ next;
+ }
+ $self->{pathnames}{$pkg} = delete $INC{$file};
+ push @{$self->{extensions}}, &{$new}($pkg, _top => $top );
+ }
+
+ $self->{extensions} ||= [];
+}
+
+sub find_extensions {
+ my ($self, $path) = @_;
+
+ my @found;
+ File::Find::find( sub {
+ my $file = $File::Find::name;
+ return unless $file =~ m!^\Q$path\E/(.+)\.pm\Z!is;
+ my $subpath = $1;
+ return if lc($subpath) eq lc($self->{dispatch});
+
+ $file = "$self->{path}/$subpath.pm";
+ my $pkg = "$self->{name}::$subpath";
+ $pkg =~ s!/!::!g;
+
+ # If we have a mixed-case package name, assume case has been preserved
+ # correctly. Otherwise, root through the file to locate the case-preserved
+ # version of the package name.
+ if ( $subpath eq lc($subpath) || $subpath eq uc($subpath) ) {
+ open PKGFILE, "<$subpath.pm" or die "find_extensions: Can't open $subpath.pm: $!";
+ my $in_pod = 0;
+ while ( <PKGFILE> ) {
+ $in_pod = 1 if /^=\w/;
+ $in_pod = 0 if /^=cut/;
+ next if ($in_pod || /^=cut/); # skip pod text
+ next if /^\s*#/; # and comments
+ if ( m/^\s*package\s+($pkg)\s*;/i ) {
+ $pkg = $1;
+ last;
+ }
+ }
+ close PKGFILE;
+ }
+
+ push @found, [ $file, $pkg ];
+ }, $path ) if -d $path;
+
+ @found;
+}
+
+sub _caller {
+ my $depth = 0;
+ my $call = caller($depth);
+ while ( $call eq __PACKAGE__ ) {
+ $depth++;
+ $call = caller($depth);
+ }
+ return $call;
+}
+
+1;
Added: Locale-Maketext-Lexicon/inc/Module/Install/Base.pm
==============================================================================
--- (empty file)
+++ Locale-Maketext-Lexicon/inc/Module/Install/Base.pm Sun Apr 2 05:40:20 2006
@@ -0,0 +1,70 @@
+#line 1
+package Module::Install::Base;
+
+$VERSION = '0.61';
+
+# Suspend handler for "redefined" warnings
+BEGIN {
+ my $w = $SIG{__WARN__};
+ $SIG{__WARN__} = sub { $w };
+}
+
+### This is the ONLY module that shouldn't have strict on
+# use strict;
+
+#line 41
+
+sub new {
+ my ($class, %args) = @_;
+
+ foreach my $method ( qw(call load) ) {
+ *{"$class\::$method"} = sub {
+ shift()->_top->$method(@_);
+ } unless defined &{"$class\::$method"};
+ }
+
+ bless( \%args, $class );
+}
+
+#line 61
+
+sub AUTOLOAD {
+ my $self = shift;
+ local $@;
+ my $autoload = eval { $self->_top->autoload } or return;
+ goto &$autoload;
+}
+
+#line 76
+
+sub _top { $_[0]->{_top} }
+
+#line 89
+
+sub admin {
+ $_[0]->_top->{admin} or Module::Install::Base::FakeAdmin->new;
+}
+
+sub is_admin {
+ $_[0]->admin->VERSION;
+}
+
+sub DESTROY {}
+
+package Module::Install::Base::FakeAdmin;
+
+my $Fake;
+sub new { $Fake ||= bless(\@_, $_[0]) }
+
+sub AUTOLOAD {}
+
+sub DESTROY {}
+
+# Restore warning handler
+BEGIN {
+ $SIG{__WARN__} = $SIG{__WARN__}->();
+}
+
+1;
+
+#line 138
Added: Locale-Maketext-Lexicon/inc/Module/Install/Can.pm
==============================================================================
--- (empty file)
+++ Locale-Maketext-Lexicon/inc/Module/Install/Can.pm Sun Apr 2 05:40:20 2006
@@ -0,0 +1,82 @@
+#line 1
+package Module::Install::Can;
+
+use strict;
+use Module::Install::Base;
+use Config ();
+### This adds a 5.005 Perl version dependency.
+### This is a bug and will be fixed.
+use File::Spec ();
+use ExtUtils::MakeMaker ();
+
+use vars qw{$VERSION @ISA};
+BEGIN {
+ $VERSION = '0.61';
+ @ISA = qw{Module::Install::Base};
+}
+
+
+# check if we can load some module
+### Upgrade this to not have to load the module if possible
+sub can_use {
+ my ($self, $mod, $ver) = @_;
+ $mod =~ s{::|\\}{/}g;
+ $mod .= '.pm' unless $mod =~ /\.pm$/i;
+
+ my $pkg = $mod;
+ $pkg =~ s{/}{::}g;
+ $pkg =~ s{\.pm$}{}i;
+
+ local $@;
+ eval { require $mod; $pkg->VERSION($ver || 0); 1 };
+}
+
+# check if we can run some command
+sub can_run {
+ my ($self, $cmd) = @_;
+
+ my $_cmd = $cmd;
+ return $_cmd if (-x $_cmd or $_cmd = MM->maybe_command($_cmd));
+
+ for my $dir ((split /$Config::Config{path_sep}/, $ENV{PATH}), '.') {
+ my $abs = File::Spec->catfile($dir, $_[1]);
+ return $abs if (-x $abs or $abs = MM->maybe_command($abs));
+ }
+
+ return;
+}
+
+# can we locate a (the) C compiler
+sub can_cc {
+ my $self = shift;
+ my @chunks = split(/ /, $Config::Config{cc}) or return;
+
+ # $Config{cc} may contain args; try to find out the program part
+ while (@chunks) {
+ return $self->can_run("@chunks") || (pop(@chunks), next);
+ }
+
+ return;
+}
+
+# Fix Cygwin bug on maybe_command();
+if ( $^O eq 'cygwin' ) {
+ require ExtUtils::MM_Cygwin;
+ require ExtUtils::MM_Win32;
+ if ( ! defined(&ExtUtils::MM_Cygwin::maybe_command) ) {
+ *ExtUtils::MM_Cygwin::maybe_command = sub {
+ my ($self, $file) = @_;
+ if ($file =~ m{^/cygdrive/}i and ExtUtils::MM_Win32->can('maybe_command')) {
+ ExtUtils::MM_Win32->maybe_command($file);
+ } else {
+ ExtUtils::MM_Unix->maybe_command($file);
+ }
+ }
+ }
+}
+
+1;
+
+__END__
+
+#line 157
Added: Locale-Maketext-Lexicon/inc/Module/Install/Fetch.pm
==============================================================================
--- (empty file)
+++ Locale-Maketext-Lexicon/inc/Module/Install/Fetch.pm Sun Apr 2 05:40:20 2006
@@ -0,0 +1,92 @@
+#line 1
+package Module::Install::Fetch;
+
+use strict;
+use Module::Install::Base;
+
+use vars qw{$VERSION @ISA};
+BEGIN {
+ $VERSION = '0.61';
+ @ISA = qw{Module::Install::Base};
+}
+
+sub get_file {
+ my ($self, %args) = @_;
+ my ($scheme, $host, $path, $file) =
+ $args{url} =~ m|^(\w+)://([^/]+)(.+)/(.+)| or return;
+
+ if ( $scheme eq 'http' and ! eval { require LWP::Simple; 1 } ) {
+ $args{url} = $args{ftp_url}
+ or (warn("LWP support unavailable!\n"), return);
+ ($scheme, $host, $path, $file) =
+ $args{url} =~ m|^(\w+)://([^/]+)(.+)/(.+)| or return;
+ }
+
+ $|++;
+ print "Fetching '$file' from $host... ";
+
+ unless (eval { require Socket; Socket::inet_aton($host) }) {
+ warn "'$host' resolve failed!\n";
+ return;
+ }
+
+ return unless $scheme eq 'ftp' or $scheme eq 'http';
+
+ require Cwd;
+ my $dir = Cwd::getcwd();
+ chdir $args{local_dir} or return if exists $args{local_dir};
+
+ if (eval { require LWP::Simple; 1 }) {
+ LWP::Simple::mirror($args{url}, $file);
+ }
+ elsif (eval { require Net::FTP; 1 }) { eval {
+ # use Net::FTP to get past firewall
+ my $ftp = Net::FTP->new($host, Passive => 1, Timeout => 600);
+ $ftp->login("anonymous", 'anonymous at example.com');
+ $ftp->cwd($path);
+ $ftp->binary;
+ $ftp->get($file) or (warn("$!\n"), return);
+ $ftp->quit;
+ } }
+ elsif (my $ftp = $self->can_run('ftp')) { eval {
+ # no Net::FTP, fallback to ftp.exe
+ require FileHandle;
+ my $fh = FileHandle->new;
+
+ local $SIG{CHLD} = 'IGNORE';
+ unless ($fh->open("|$ftp -n")) {
+ warn "Couldn't open ftp: $!\n";
+ chdir $dir; return;
+ }
+
+ my @dialog = split(/\n/, <<"END_FTP");
+open $host
+user anonymous anonymous\@example.com
+cd $path
+binary
+get $file $file
+quit
+END_FTP
+ foreach (@dialog) { $fh->print("$_\n") }
+ $fh->close;
+ } }
+ else {
+ warn "No working 'ftp' program available!\n";
+ chdir $dir; return;
+ }
+
+ unless (-f $file) {
+ warn "Fetching failed: $@\n";
+ chdir $dir; return;
+ }
+
+ return if exists $args{size} and -s $file != $args{size};
+ system($args{run}) if exists $args{run};
+ unlink($file) if $args{remove};
+
+ print(((!exists $args{check_for} or -e $args{check_for})
+ ? "done!" : "failed! ($!)"), "\n");
+ chdir $dir; return !$?;
+}
+
+1;
Added: Locale-Maketext-Lexicon/inc/Module/Install/Makefile.pm
==============================================================================
--- (empty file)
+++ Locale-Maketext-Lexicon/inc/Module/Install/Makefile.pm Sun Apr 2 05:40:20 2006
@@ -0,0 +1,186 @@
+#line 1
+package Module::Install::Makefile;
+
+use strict 'vars';
+use Module::Install::Base;
+use ExtUtils::MakeMaker ();
+
+use vars qw{$VERSION @ISA};
+BEGIN {
+ $VERSION = '0.61';
+ @ISA = qw{Module::Install::Base};
+}
+
+sub Makefile { $_[0] }
+
+my %seen = ();
+
+sub prompt {
+ shift;
+
+ # Infinite loop protection
+ my @c = caller();
+ if ( ++$seen{"$c[1]|$c[2]|$_[0]"} > 3 ) {
+ die "Caught an potential prompt infinite loop ($c[1]|$c[2]|$_[0])";
+ }
+
+ # In automated testing, always use defaults
+ if ( $ENV{AUTOMATED_TESTING} and ! $ENV{PERL_MM_USE_DEFAULT} ) {
+ local $ENV{PERL_MM_USE_DEFAULT} = 1;
+ goto &ExtUtils::MakeMaker::prompt;
+ } else {
+ goto &ExtUtils::MakeMaker::prompt;
+ }
+}
+
+sub makemaker_args {
+ my $self = shift;
+ my $args = ($self->{makemaker_args} ||= {});
+ %$args = ( %$args, @_ ) if @_;
+ $args;
+}
+
+# For mm args that take multiple space-seperated args,
+# append an argument to the current list.
+sub makemaker_append {
+ my $self = shift;
+ my $name = shift;
+ my $args = $self->makemaker_args;
+ $args->{name} = defined $args->{$name}
+ ? join( ' ', $args->{name}, @_ )
+ : join( ' ', @_ );
+}
+
+sub build_subdirs {
+ my $self = shift;
+ my $subdirs = $self->makemaker_args->{DIR} ||= [];
+ for my $subdir (@_) {
+ push @$subdirs, $subdir;
+ }
+}
+
+sub clean_files {
+ my $self = shift;
+ my $clean = $self->makemaker_args->{clean} ||= {};
+ %$clean = (
+ %$clean,
+ FILES => join(' ', grep length, $clean->{FILES}, @_),
+ );
+}
+
+sub libs {
+ my $self = shift;
+ my $libs = ref $_[0] ? shift : [ shift ];
+ $self->makemaker_args( LIBS => $libs );
+}
+
+sub inc {
+ my $self = shift;
+ $self->makemaker_args( INC => shift );
+}
+
+sub write {
+ my $self = shift;
+ die "&Makefile->write() takes no arguments\n" if @_;
+
+ my $args = $self->makemaker_args;
+ $args->{DISTNAME} = $self->name;
+ $args->{NAME} = $self->module_name || $self->name || $self->determine_NAME($args);
+ $args->{VERSION} = $self->version || $self->determine_VERSION($args);
+ $args->{NAME} =~ s/-/::/g;
+ if ( $self->tests ) {
+ $args->{test} = { TESTS => $self->tests };
+ }
+ if ($] >= 5.005) {
+ $args->{ABSTRACT} = $self->abstract;
+ $args->{AUTHOR} = $self->author;
+ }
+ if ( eval($ExtUtils::MakeMaker::VERSION) >= 6.10 ) {
+ $args->{NO_META} = 1;
+ }
+ if ( eval($ExtUtils::MakeMaker::VERSION) > 6.17 and $self->sign ) {
+ $args->{SIGN} = 1;
+ }
+ unless ( $self->is_admin ) {
+ delete $args->{SIGN};
+ }
+
+ # merge both kinds of requires into prereq_pm
+ my $prereq = ($args->{PREREQ_PM} ||= {});
+ %$prereq = ( %$prereq, map { @$_ } map { @$_ } grep $_,
+ ($self->build_requires, $self->requires) );
+
+ # merge both kinds of requires into prereq_pm
+ my $subdirs = ($args->{DIR} ||= []);
+ if ($self->bundles) {
+ foreach my $bundle (@{ $self->bundles }) {
+ my ($file, $dir) = @$bundle;
+ push @$subdirs, $dir if -d $dir;
+ delete $prereq->{$file};
+ }
+ }
+
+ if ( my $perl_version = $self->perl_version ) {
+ eval "use $perl_version; 1"
+ or die "ERROR: perl: Version $] is installed, "
+ . "but we need version >= $perl_version";
+ }
+
+ my %args = map { ( $_ => $args->{$_} ) } grep {defined($args->{$_})} keys %$args;
+ if ($self->admin->preop) {
+ $args{dist} = $self->admin->preop;
+ }
+
+ my $mm = ExtUtils::MakeMaker::WriteMakefile(%args);
+ $self->fix_up_makefile($mm->{FIRST_MAKEFILE} || 'Makefile');
+}
+
+sub fix_up_makefile {
+ my $self = shift;
+ my $makefile_name = shift;
+ my $top_class = ref($self->_top) || '';
+ my $top_version = $self->_top->VERSION || '';
+
+ my $preamble = $self->preamble
+ ? "# Preamble by $top_class $top_version\n"
+ . $self->preamble
+ : '';
+ my $postamble = "# Postamble by $top_class $top_version\n"
+ . ($self->postamble || '');
+
+ local *MAKEFILE;
+ open MAKEFILE, "< $makefile_name" or die "fix_up_makefile: Couldn't open $makefile_name: $!";
+ my $makefile = do { local $/; <MAKEFILE> };
+ close MAKEFILE or die $!;
+
+ $makefile =~ s/\b(test_harness\(\$\(TEST_VERBOSE\), )/$1'inc', /;
+ $makefile =~ s/( -I\$\(INST_ARCHLIB\))/ -Iinc$1/g;
+ $makefile =~ s/( "-I\$\(INST_LIB\)")/ "-Iinc"$1/g;
+ $makefile =~ s/^(FULLPERL = .*)/$1 "-Iinc"/m;
+ $makefile =~ s/^(PERL = .*)/$1 "-Iinc"/m;
+
+ open MAKEFILE, "> $makefile_name" or die "fix_up_makefile: Couldn't open $makefile_name: $!";
+ print MAKEFILE "$preamble$makefile$postamble" or die $!;
+ close MAKEFILE or die $!;
+
+ 1;
+}
+
+sub preamble {
+ my ($self, $text) = @_;
+ $self->{preamble} = $text . $self->{preamble} if defined $text;
+ $self->{preamble};
+}
+
+sub postamble {
+ my ($self, $text) = @_;
+ $self->{postamble} ||= $self->admin->postamble;
+ $self->{postamble} .= $text if defined $text;
+ $self->{postamble}
+}
+
+1;
+
+__END__
+
+#line 312
Added: Locale-Maketext-Lexicon/inc/Module/Install/Metadata.pm
==============================================================================
--- (empty file)
+++ Locale-Maketext-Lexicon/inc/Module/Install/Metadata.pm Sun Apr 2 05:40:20 2006
@@ -0,0 +1,310 @@
+#line 1
+package Module::Install::Metadata;
+
+use Module::Install::Base;
+ at ISA = qw{Module::Install::Base};
+
+$VERSION = '0.61';
+
+use strict 'vars';
+
+my @scalar_keys = qw{
+ name module_name abstract author version license
+ distribution_type perl_version tests
+};
+
+my @tuple_keys = qw{
+ build_requires requires recommends bundles
+};
+
+sub Meta { shift }
+sub Meta_ScalarKeys { @scalar_keys }
+sub Meta_TupleKeys { @tuple_keys }
+
+foreach my $key (@scalar_keys) {
+ *$key = sub {
+ my $self = shift;
+ return $self->{values}{$key} if defined wantarray and !@_;
+ $self->{values}{$key} = shift;
+ return $self;
+ };
+}
+
+foreach my $key (@tuple_keys) {
+ *$key = sub {
+ my $self = shift;
+ return $self->{values}{$key} unless @_;
+
+ my @rv;
+ while (@_) {
+ my $module = shift or last;
+ my $version = shift || 0;
+ if ( $module eq 'perl' ) {
+ $version =~ s{^(\d+)\.(\d+)\.(\d+)}
+ {$1 + $2/1_000 + $3/1_000_000}e;
+ $self->perl_version($version);
+ next;
+ }
+ my $rv = [ $module, $version ];
+ push @rv, $rv;
+ }
+ push @{ $self->{values}{$key} }, @rv;
+ @rv;
+ };
+}
+
+sub sign {
+ my $self = shift;
+ return $self->{'values'}{'sign'} if defined wantarray and !@_;
+ $self->{'values'}{'sign'} = ( @_ ? $_[0] : 1 );
+ return $self;
+}
+
+sub dynamic_config {
+ my $self = shift;
+ unless ( @_ ) {
+ warn "You MUST provide an explicit true/false value to dynamic_config, skipping\n";
+ return $self;
+ }
+ $self->{'values'}{'dynamic_config'} = $_[0] ? 1 : 0;
+ return $self;
+}
+
+sub all_from {
+ my ( $self, $file ) = @_;
+
+ unless ( defined($file) ) {
+ my $name = $self->name
+ or die "all_from called with no args without setting name() first";
+ $file = join('/', 'lib', split(/-/, $name)) . '.pm';
+ $file =~ s{.*/}{} unless -e $file;
+ die "all_from: cannot find $file from $name" unless -e $file;
+ }
+
+ $self->version_from($file) unless $self->version;
+ $self->perl_version_from($file) unless $self->perl_version;
+
+ # The remaining probes read from POD sections; if the file
+ # has an accompanying .pod, use that instead
+ my $pod = $file;
+ if ( $pod =~ s/\.pm$/.pod/i and -e $pod ) {
+ $file = $pod;
+ }
+
+ $self->author_from($file) unless $self->author;
+ $self->license_from($file) unless $self->license;
+ $self->abstract_from($file) unless $self->abstract;
+}
+
+sub provides {
+ my $self = shift;
+ my $provides = ( $self->{values}{provides} ||= {} );
+ %$provides = (%$provides, @_) if @_;
+ return $provides;
+}
+
+sub auto_provides {
+ my $self = shift;
+ return $self unless $self->is_admin;
+
+ unless (-e 'MANIFEST') {
+ warn "Cannot deduce auto_provides without a MANIFEST, skipping\n";
+ return $self;
+ }
+
+ # Avoid spurious warnings as we are not checking manifest here.
+
+ local $SIG{__WARN__} = sub {1};
+ require ExtUtils::Manifest;
+ local *ExtUtils::Manifest::manicheck = sub { return };
+
+ require Module::Build;
+ my $build = Module::Build->new(
+ dist_name => $self->{name},
+ dist_version => $self->{version},
+ license => $self->{license},
+ );
+ $self->provides(%{ $build->find_dist_packages || {} });
+}
+
+sub feature {
+ my $self = shift;
+ my $name = shift;
+ my $features = ( $self->{values}{features} ||= [] );
+
+ my $mods;
+
+ if ( @_ == 1 and ref( $_[0] ) ) {
+ # The user used ->feature like ->features by passing in the second
+ # argument as a reference. Accomodate for that.
+ $mods = $_[0];
+ } else {
+ $mods = \@_;
+ }
+
+ my $count = 0;
+ push @$features, (
+ $name => [
+ map {
+ ref($_) ? ( ref($_) eq 'HASH' ) ? %$_
+ : @$_
+ : $_
+ } @$mods
+ ]
+ );
+
+ return @$features;
+}
+
+sub features {
+ my $self = shift;
+ while ( my ( $name, $mods ) = splice( @_, 0, 2 ) ) {
+ $self->feature( $name, @$mods );
+ }
+ return $self->{values}->{features}
+ ? @{ $self->{values}->{features} }
+ : ();
+}
+
+sub no_index {
+ my $self = shift;
+ my $type = shift;
+ push @{ $self->{values}{no_index}{$type} }, @_ if $type;
+ return $self->{values}{no_index};
+}
+
+sub read {
+ my $self = shift;
+ $self->include_deps( 'YAML', 0 );
+
+ require YAML;
+ my $data = YAML::LoadFile('META.yml');
+
+ # Call methods explicitly in case user has already set some values.
+ while ( my ( $key, $value ) = each %$data ) {
+ next unless $self->can($key);
+ if ( ref $value eq 'HASH' ) {
+ while ( my ( $module, $version ) = each %$value ) {
+ $self->can($key)->($self, $module => $version );
+ }
+ }
+ else {
+ $self->can($key)->($self, $value);
+ }
+ }
+ return $self;
+}
+
+sub write {
+ my $self = shift;
+ return $self unless $self->is_admin;
+ $self->admin->write_meta;
+ return $self;
+}
+
+sub version_from {
+ my ( $self, $file ) = @_;
+ require ExtUtils::MM_Unix;
+ $self->version( ExtUtils::MM_Unix->parse_version($file) );
+}
+
+sub abstract_from {
+ my ( $self, $file ) = @_;
+ require ExtUtils::MM_Unix;
+ $self->abstract(
+ bless(
+ { DISTNAME => $self->name },
+ 'ExtUtils::MM_Unix'
+ )->parse_abstract($file)
+ );
+}
+
+sub _slurp {
+ my ( $self, $file ) = @_;
+
+ local *FH;
+ open FH, "< $file" or die "Cannot open $file.pod: $!";
+ do { local $/; <FH> };
+}
+
+sub perl_version_from {
+ my ( $self, $file ) = @_;
+
+ if (
+ $self->_slurp($file) =~ m/
+ ^
+ use \s*
+ v?
+ ([\d\.]+)
+ \s* ;
+ /ixms
+ )
+ {
+ $self->perl_version($1);
+ }
+ else {
+ warn "Cannot determine perl version info from $file\n";
+ return;
+ }
+}
+
+sub author_from {
+ my ( $self, $file ) = @_;
+ my $content = $self->_slurp($file);
+ if ($content =~ m/
+ =head \d \s+ (?:authors?)\b \s*
+ ([^\n]*)
+ |
+ =head \d \s+ (?:licen[cs]e|licensing|copyright|legal)\b \s*
+ .*? copyright .*? \d\d\d[\d.]+ \s* (?:\bby\b)? \s*
+ ([^\n]*)
+ /ixms) {
+ my $author = $1 || $2;
+ $author =~ s{E<lt>}{<}g;
+ $author =~ s{E<gt>}{>}g;
+ $self->author($author);
+ }
+ else {
+ warn "Cannot determine author info from $file\n";
+ }
+}
+
+sub license_from {
+ my ( $self, $file ) = @_;
+
+ if (
+ $self->_slurp($file) =~ m/
+ =head \d \s+
+ (?:licen[cs]e|licensing|copyright|legal)\b
+ (.*?)
+ (=head\\d.*|=cut.*|)
+ \z
+ /ixms
+ )
+ {
+ my $license_text = $1;
+ my @phrases = (
+ 'under the same (?:terms|license) as perl itself' => 'perl',
+ 'GNU public license' => 'gpl',
+ 'GNU lesser public license' => 'gpl',
+ 'BSD license' => 'bsd',
+ 'Artistic license' => 'artistic',
+ 'GPL' => 'gpl',
+ 'LGPL' => 'lgpl',
+ 'BSD' => 'bsd',
+ 'Artistic' => 'artistic',
+ );
+ while ( my ( $pattern, $license ) = splice( @phrases, 0, 2 ) ) {
+ $pattern =~ s{\s+}{\\s+}g;
+ if ( $license_text =~ /\b$pattern\b/i ) {
+ $self->license($license);
+ return 1;
+ }
+ }
+ }
+
+ warn "Cannot determine license info from $file\n";
+ return 'unknown';
+}
+
+1;
Added: Locale-Maketext-Lexicon/inc/Module/Install/Scripts.pm
==============================================================================
--- (empty file)
+++ Locale-Maketext-Lexicon/inc/Module/Install/Scripts.pm Sun Apr 2 05:40:20 2006
@@ -0,0 +1,49 @@
+#line 1
+package Module::Install::Scripts;
+
+use strict;
+use Module::Install::Base;
+use File::Basename ();
+
+use vars qw{$VERSION @ISA};
+BEGIN {
+ $VERSION = '0.61';
+ @ISA = qw(Module::Install::Base);
+}
+
+sub prompt_script {
+ my ($self, $script_file) = @_;
+
+ my ($prompt, $abstract, $default);
+ foreach my $line ( $self->_read_script($script_file) ) {
+ last unless $line =~ /^#/;
+ $prompt = $1 if $line =~ /^#\s*prompt:\s+(.*)/;
+ $default = $1 if $line =~ /^#\s*default:\s+(.*)/;
+ $abstract = $1 if $line =~ /^#\s*abstract:\s+(.*)/;
+ }
+ unless (defined $prompt) {
+ my $script_name = File::Basename::basename($script_file);
+ $prompt = "Do you want to install '$script_name'";
+ $prompt .= " ($abstract)" if defined $abstract;
+ $prompt .= '?';
+ }
+ return unless $self->prompt($prompt, ($default || 'n')) =~ /^[Yy]/;
+ $self->install_script($script_file);
+}
+
+sub install_script {
+ my $self = shift;
+ my $args = $self->makemaker_args;
+ my $exe_files = $args->{EXE_FILES} ||= [];
+ push @$exe_files, @_;
+}
+
+sub _read_script {
+ my ($self, $script_file) = @_;
+ local *SCRIPT;
+ open SCRIPT, $script_file
+ or die "Can't open '$script_file' for input: $!\n";
+ return <SCRIPT>;
+}
+
+1;
Added: Locale-Maketext-Lexicon/inc/Module/Install/Win32.pm
==============================================================================
--- (empty file)
+++ Locale-Maketext-Lexicon/inc/Module/Install/Win32.pm Sun Apr 2 05:40:20 2006
@@ -0,0 +1,64 @@
+#line 1
+package Module::Install::Win32;
+
+use strict;
+use Module::Install::Base;
+
+use vars qw{$VERSION @ISA};
+BEGIN {
+ $VERSION = '0.61';
+ @ISA = qw{Module::Install::Base};
+}
+
+# determine if the user needs nmake, and download it if needed
+sub check_nmake {
+ my $self = shift;
+ $self->load('can_run');
+ $self->load('get_file');
+
+ require Config;
+ return unless (
+ $^O eq 'MSWin32' and
+ $Config::Config{make} and
+ $Config::Config{make} =~ /^nmake\b/i and
+ ! $self->can_run('nmake')
+ );
+
+ print "The required 'nmake' executable not found, fetching it...\n";
+
+ require File::Basename;
+ my $rv = $self->get_file(
+ url => 'http://download.microsoft.com/download/vc15/Patch/1.52/W95/EN-US/Nmake15.exe',
+ ftp_url => 'ftp://ftp.microsoft.com/Softlib/MSLFILES/Nmake15.exe',
+ local_dir => File::Basename::dirname($^X),
+ size => 51928,
+ run => 'Nmake15.exe /o > nul',
+ check_for => 'Nmake.exe',
+ remove => 1,
+ );
+
+ if (!$rv) {
+ die <<'END_MESSAGE';
+
+-------------------------------------------------------------------------------
+
+Since you are using Microsoft Windows, you will need the 'nmake' utility
+before installation. It's available at:
+
+ http://download.microsoft.com/download/vc15/Patch/1.52/W95/EN-US/Nmake15.exe
+ or
+ ftp://ftp.microsoft.com/Softlib/MSLFILES/Nmake15.exe
+
+Please download the file manually, save it to a directory in %PATH% (e.g.
+C:\WINDOWS\COMMAND\), then launch the MS-DOS command line shell, "cd" to
+that directory, and run "Nmake15.exe" from there; that will create the
+'nmake.exe' file needed by this module.
+
+You may then resume the installation process described in README.
+
+-------------------------------------------------------------------------------
+END_MESSAGE
+ }
+}
+
+1;
Added: Locale-Maketext-Lexicon/inc/Module/Install/WriteAll.pm
==============================================================================
--- (empty file)
+++ Locale-Maketext-Lexicon/inc/Module/Install/WriteAll.pm Sun Apr 2 05:40:20 2006
@@ -0,0 +1,40 @@
+#line 1
+package Module::Install::WriteAll;
+
+use Module::Install::Base;
+ at ISA = qw(Module::Install::Base);
+
+$VERSION = '0.61';
+
+use strict;
+
+sub WriteAll {
+ my $self = shift;
+ my %args = (
+ meta => 1,
+ sign => 0,
+ inline => 0,
+ check_nmake => 1,
+ @_
+ );
+
+ $self->sign(1) if $args{sign};
+ $self->Meta->write if $args{meta};
+ $self->admin->WriteAll(%args) if $self->is_admin;
+
+ if ( $0 =~ /Build.PL$/i ) {
+ $self->Build->write;
+ } else {
+ $self->check_nmake if $args{check_nmake};
+ unless ( $self->makemaker_args->{'PL_FILES'} ) {
+ $self->makemaker_args( PL_FILES => {} );
+ }
+ if ($args{inline}) {
+ $self->Inline->write;
+ } else {
+ $self->Makefile->write;
+ }
+ }
+}
+
+1;
Added: Locale-Maketext-Lexicon/lib/Locale/Maketext/Extract.pm
==============================================================================
--- (empty file)
+++ Locale-Maketext-Lexicon/lib/Locale/Maketext/Extract.pm Sun Apr 2 05:40:20 2006
@@ -0,0 +1,492 @@
+package Locale::Maketext::Extract;
+$Locale::Maketext::Extract::VERSION = '0.11';
+
+use strict;
+
+=head1 NAME
+
+Locale::Maketext::Extract - Extract translatable strings from source
+
+=head1 SYNOPSIS
+
+ my $Ext = Locale::Maketext::Extract->new;
+ $Ext->read_po('messages.po');
+ $Ext->extract_file($_) for <*.pl>;
+ $Ext->compile;
+ $Ext->write_po('messages.po');
+
+=head1 DESCRIPTION
+
+This module can extract translatable strings from files, and write
+them back to PO files. It can also parse existing PO files and merge
+their contents with newly extracted strings.
+
+A command-line utility, L<xgettext.pl>, is installed with this module
+as well.
+
+Following formats of input files are supported:
+
+=over 4
+
+=item Perl source files
+
+Valid localization function names are: C<translate>, C<maketext>,
+C<gettext>, C<loc>, C<x>, C<_> and C<__>.
+
+=item HTML::Mason
+
+Strings inside C<E<lt>&|/lE<gt>I<...>E<lt>/&E<gt>> and
+C<E<lt>&|/locE<gt>I<...>E<lt>/&E<gt>> are extracted.
+
+=item Template Toolkit
+
+Strings inside C<[%|l%]...[%END%]> or C<[%|loc%]...[%END%]>
+are extracted.
+
+=item Text::Template
+
+Sentences between C<STARTxxx> and C<ENDxxx> are extracted individually.
+
+=item Generic Template
+
+Strings inside {{...}} are extracted.
+
+=back
+
+=head1 METHODS
+
+=head2 Constructor
+
+ new
+
+=cut
+
+sub new {
+ my $class = shift;
+ bless({ header => '', entries => {}, lexicon => {}, @_ }, $class);
+}
+
+=head2 Accessors
+
+ header, set_header
+ lexicon, set_lexicon, msgstr, set_msgstr
+ entries, set_entries, entry, add_entry, del_entry
+ clear
+
+=cut
+
+sub header { $_[0]{header} || _default_header() };
+sub set_header { $_[0]{header} = $_[1] };
+
+sub lexicon { $_[0]{lexicon} }
+sub set_lexicon { $_[0]{lexicon} = $_[1] || {}; delete $_[0]{lexicon}{''}; }
+
+sub msgstr { $_[0]{lexicon}{$_[1]} }
+sub set_msgstr { $_[0]{lexicon}{$_[1]} = $_[2] }
+
+sub entries { $_[0]{entries} }
+sub set_entries { $_[0]{entries} = $_[1] || {} }
+
+sub entry { @{$_[0]->entries->{$_[1]} || [] } }
+sub add_entry { push @{$_[0]->entries->{$_[1]}}, $_[2] }
+sub del_entry { delete $_[0]->entries->{$_[1]} }
+
+sub clear {
+ $_[0]->set_header;
+ $_[0]->set_lexicon;
+ $_[0]->set_entries;
+}
+
+=head2 PO File manipulation
+
+ read_po
+ write_po
+
+=cut
+
+sub read_po {
+ my ($self, $file, $verbatim) = @_;
+ my $header = '';
+
+ local *LEXICON;
+ open LEXICON, $file or die $!;
+ while (<LEXICON>) {
+ (1 .. /^$/) or last;
+ $header .= $_;
+ }
+ 1 while chomp $header;
+
+ $self->set_header("$header\n");
+
+ require Locale::Maketext::Lexicon::Gettext;
+ my $lexicon = Locale::Maketext::Lexicon::Gettext->parse($_, <LEXICON>);
+
+ $self->set_lexicon(
+ $verbatim ? { map _to_gettext($_), %$lexicon } : $lexicon
+ );
+ close LEXICON;
+}
+
+sub write_po {
+ my ($self, $file, $add_format) = @_;
+
+ local *LEXICON;
+ open LEXICON, ">$file" or die "Can't write to $file$!\n";
+
+ print LEXICON $self->header;
+
+ foreach my $msgid ($self->msgids) {
+ $self->normalize_space($msgid);
+ print LEXICON "\n";
+ print LEXICON $self->msg_positions($msgid);
+ print LEXICON $self->msg_variables($msgid);
+ print LEXICON $self->msg_format($msgid) if $add_format;
+ print LEXICON $self->msg_out($msgid);
+ }
+}
+
+=head2 Extraction
+
+ extract
+ extract_file
+
+=cut
+
+use constant NUL => 0;
+use constant BEG => 1;
+use constant PAR => 2;
+use constant QUO1 => 3;
+use constant QUO2 => 4;
+use constant QUO3 => 5;
+sub extract {
+ my $self = shift;
+ my $file = shift;
+ local $_ = shift;
+
+ my $entries = $self->entries;
+ my $line = 1; pos($_) = 0;
+
+ # Text::Template
+ if (/^STARTTEXT$/m and /^ENDTEXT$/m) {
+ require HTML::Parser;
+ require Lingua::EN::Sentence;
+
+ {
+ package MyParser;
+ @MyParser::ISA = 'HTML::Parser';
+ *{'text'} = sub {
+ my ($self, $str, $is_cdata) = @_;
+ my $sentences = Lingua::EN::Sentence::get_sentences($str) or return;
+ $str =~ s/\n/ /g; $str =~ s/^\s+//; $str =~ s/\s+$//;
+ $self->add_entry($str => [$file, $line]);
+ };
+ }
+
+ my $p = MyParser->new;
+ while (m/\G((.*?)^(?:START|END)[A-Z]+$)/smg) {
+ my ($str) = ($2);
+ $line += ( () = ($1 =~ /\n/g) ); # cryptocontext!
+ $p->parse($str); $p->eof;
+ }
+ $_ = '';
+ }
+
+ # HTML::Mason
+ $line = 1; pos($_) = 0;
+ while (m!\G(.*?<&\|/l(?:oc)?(.*?)&>(.*?)</&>)!sg) {
+ my ($vars, $str) = ($2, $3);
+ $line += ( () = ($1 =~ /\n/g) ); # cryptocontext!
+ $self->add_entry($str, [ $file, $line, $vars ]);
+ }
+
+ # Template Toolkit
+ $line = 1; pos($_) = 0;
+ while (m!\G(.*?\[%\s*\|l(?:oc)?(.*?)\s*%\](.*?)\[%\s*END\s*%\])!sg) {
+ my ($vars, $str) = ($2, $3);
+ $line += ( () = ($1 =~ /\n/g) ); # cryptocontext!
+ $vars =~ s/^\s*\(//;
+ $vars =~ s/\)\s*$//;
+ $self->add_entry($str, [ $file, $line, $vars ]);
+ }
+
+ # Generic Template:
+ $line = 1; pos($_) = 0;
+ while (m/\G(.*?(?<!\{)\{\{(?!\{)(.*?)\}\})/sg) {
+ my ($vars, $str) = ('', $2);
+ $line += ( () = ($1 =~ /\n/g) ); # cryptocontext!
+ $self->add_entry($str, [ $file, $line, $vars ]);
+ }
+
+ my $quoted = '(\')([^\\\']*(?:\\.[^\\\']*)*)(\')|(\")([^\\\"]*(?:\\.[^\\\"]*)*)(\")';
+
+ # Comment-based mark: "..." # loc
+ $line = 1; pos($_) = 0;
+ while (m/\G(.*?($quoted)[\}\)\],]*\s*\#\s*loc\s*$)/smog) {
+ my $str = substr($2, 1, -1);
+ $line += ( () = ( $1 =~ /\n/g ) ); # cryptocontext!
+ $str =~ s/\\(["'])/$1/g;
+ $self->add_entry($str, [ $file, $line, '' ]);
+ }
+
+ # Comment-based pair mark: "..." => "..." # loc_pair
+ $line = 1; pos($_) = 0;
+ while (m/\G(.*?(\w+)\s*=>\s*($quoted)[\}\)\],]*\s*\#\s*loc_pair\s*$)/smg) {
+ my $key = $2;
+ my $val = substr($3, 1, -1);
+ $line += ( () = ( $1 =~ /\n/g ) ); # cryptocontext!
+ $key =~ s/\\(["'])/$1/g;
+ $val =~ s/\\(["'])/$1/g;
+ $self->add_entry($key, [ $file, $line, '' ]);
+ $self->add_entry($val, [ $file, $line, '' ]);
+ }
+
+ # Perl code:
+ my ($state,$str,$vars,$quo)=(0);
+ pos($_) = 0;
+ my $orig = 1 + (() = ((my $__ = $_) =~ /\n/g));
+
+ PARSER: {
+ $_ = substr($_, pos($_)) if (pos($_));
+ my $line = $orig - (() = ((my $__ = $_) =~ /\n/g));
+
+ # various ways to spell the localization function
+ $state == NUL && m/\b(translate|maketext|gettext|__?|loc(?:ali[sz]e)?|x)/gc
+ && do { $state = BEG; redo };
+ $state == BEG && m/^([\s\t\n]*)/gc && redo;
+
+ # begin ()
+ $state == BEG && m/^([\S\(])\s*/gc
+ && do { $state = ( ($1 eq '(') ? PAR : NUL); redo };
+
+ # begin or end of string
+ $state == PAR && m/^(\')/gc && do { $state = $quo = QUO1; redo };
+ $state == QUO1 && m/^([^\']+)/gc && do { $str .= $1; redo };
+ $state == QUO1 && m/^\'/gc && do { $state = PAR; redo };
+
+ $state == PAR && m/^\"/gc && do { $state = $quo = QUO2; redo };
+ $state == QUO2 && m/^([^\"]+)/gc && do { $str .= $1; redo };
+ $state == QUO2 && m/^\"/gc && do { $state = PAR; redo };
+
+ $state == PAR && m/^\`/gc && do { $state = $quo = QUO3; redo };
+ $state == QUO3 && m/^([^\`]*)/gc && do { $str .= $1; redo };
+ $state == QUO3 && m/^\`/gc && do { $state = PAR; redo };
+
+ # end ()
+ $state == PAR && m/^\s*[\)]/gc && do {
+ $state = NUL;
+ $vars =~ s/[\n\r]//g if ($vars);
+ if ($quo == QUO1) {
+ $str =~ s/\\([\\'])/$1/g; # normalize q strings
+ }
+ else {
+ $str =~ s/(\\(?:[0x]..|c?.))/"qq($1)"/eeg; # normalize qq / qx strings
+ }
+ push @{$entries->{$str}}, [ $file, $line - (() = $str =~ /\n/g), $vars] if ($str);
+ undef $str; undef $vars;
+ redo;
+ };
+
+ # a line of vars
+ $state == PAR && m/^([^\)]*)/gc && do { $vars .= "$1\n"; redo };
+ }
+}
+
+sub extract_file {
+ my ($self, $file) = @_;
+
+ local($/, *FH);
+ open FH, $file or die $!;
+ $self->extract($file => scalar <FH>);
+ close FH;
+}
+
+=head2 Compilation
+
+ compile
+ normalize_space
+
+=cut
+
+sub compile {
+ my ($self, $verbatim) = @_;
+ my $entries = $self->entries;
+ my $lexicon = $self->lexicon;
+
+ foreach my $str (sort keys %$entries) {
+ my $ostr = $str;
+ my $entry = $entries->{$str};
+ my $lexi = $lexicon->{$ostr};
+
+ $str = _to_gettext($str, $verbatim);
+ $lexi = _to_gettext($lexi, $verbatim);
+
+ $lexicon->{$str} ||= '';
+ next if $ostr eq $str;
+
+ $lexicon->{$str} ||= $lexi;
+ delete $entries->{$ostr}; delete $lexicon->{$ostr};
+ $entries->{$str} = $entry;
+ }
+
+ return %$lexicon;
+}
+
+my %Escapes = map {("\\$_" => eval("qq(\\$_)"))} qw(t r f b a e);
+sub normalize_space {
+ my ($self, $msgid) = @_;
+ my $nospace = $msgid;
+ $nospace =~ s/ +$//;
+
+ return unless (!$self->has_msgid($msgid) and $self->has_msgid($nospace));
+
+ $self->set_msgstr(
+ $msgid => $self->msgstr($nospace) .
+ (' ' x (length($msgid) - length($nospace)))
+ );
+}
+
+=head2 Lexicon accessors
+
+ msgids, has_msgid,
+ msgstr, set_msgstr
+ msg_positions, msg_variables, msg_format, msg_out
+
+=cut
+
+sub msgids { sort keys %{$_[0]{lexicon}} }
+sub has_msgid { length $_[0]->msgstr($_[1]) }
+
+sub msg_positions {
+ my ($self, $msgid) = @_;
+ my %files = (map { ( " $_->[0]:$_->[1]" => 1 ) } $self->entry($msgid));
+ return join('', '#:', sort(keys %files), "\n");
+}
+
+sub msg_variables {
+ my ($self, $msgid) = @_;
+ my $out = '';
+
+ my %seen;
+ foreach my $entry ( grep { $_->[2] } $self->entry($msgid) ) {
+ my ($file, $line, $var) = @$entry;
+ $var =~ s/^\s*,\s*//; $var =~ s/\s*$//;
+ $out .= "#. ($var)\n" unless !length($var) or $seen{$var}++;
+ }
+
+ return $out;
+}
+
+sub msg_format {
+ my ($self, $msgid) = @_;
+ return "#, perl-maketext-format\n" if $msgid =~ /%(?:\d|\w+\([^\)]*\))/;
+ return '';
+}
+
+sub msg_out {
+ my ($self, $msgid) = @_;
+
+ return "msgid " . _format($msgid) .
+ "msgstr " . _format($self->msgstr($msgid));
+}
+
+=head2 Internal utilities
+
+ _default_header
+ _to_gettext
+ _escape
+ _format
+
+=cut
+
+sub _default_header {
+ return << '.';
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL at ADDRESS>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL at ADDRESS>\n"
+"Language-Team: LANGUAGE <LL at li.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=CHARSET\n"
+"Content-Transfer-Encoding: 8bit\n"
+.
+}
+
+sub _to_gettext {
+ my ($text, $verbatim) = @_;
+ return '' unless defined $text;
+
+ $text =~ s/\\/\\\\/g;
+ $text =~ s/\"/\\"/g;
+
+ while (my ($char, $esc) = each %Escapes) {
+ $text =~ s/$esc/$char/g;
+ }
+ return $text if $verbatim;
+
+ $text =~ s/((?<!~)(?:~~)*)\[_(\d+)\]/$1%$2/g;
+ $text =~ s/((?<!~)(?:~~)*)\[([A-Za-z#*]\w*),([^\]]+)\]/$1%$2("""$3""")/g;
+ $text = join('', map {
+ /^""".*"""$/ ? _escape(substr($_, 3, -3)) : $_
+ } split(/(""".*?""")/, $text));
+
+ $text =~ s/~([\~\[\]])/$1/g;
+ return $text;
+}
+
+sub _escape {
+ my $text = shift;
+ $text =~ s/\b_(\d+)/%$1/g;
+ return $text;
+}
+
+sub _format {
+ my $str = shift;
+
+ return "\"$str\"\n" unless $str =~ /\n/;
+ my $multi_line = ($str =~ /\n(?!\z)/);
+ $str =~ s/\n/\\n"\n"/g;
+ if ($str =~ /\n"$/) {
+ chop $str;
+ }
+ else {
+ $str .= "\"\n";
+ }
+ return $multi_line ? qq(""\n"$str) : qq("$str);
+}
+
+1;
+
+=head1 ACKNOWLEDGMENTS
+
+Thanks to Jesse Vincent for contributing to an early version of this
+module.
+
+Also to Alain Barbet, who effectively re-wrote the source parser with a
+flex-like algorithm.
+
+=head1 SEE ALSO
+
+L<xgettext.pl>, L<Locale::Maketext>, L<Locale::Maketext::Lexicon>
+
+=head1 AUTHORS
+
+Audrey Tang E<lt>audreyt at audreyt.orgE<gt>
+
+=head1 COPYRIGHT
+
+Copyright 2003, 2004 by Audrey Tang E<lt>audreyt at audreyt.orgE<gt>.
+
+This program is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+See L<http://www.perl.com/perl/misc/Artistic.html>
+
+=cut
Added: Locale-Maketext-Lexicon/lib/Locale/Maketext/Extract/Run.pm
==============================================================================
--- (empty file)
+++ Locale-Maketext-Lexicon/lib/Locale/Maketext/Extract/Run.pm Sun Apr 2 05:40:20 2006
@@ -0,0 +1,86 @@
+package Locale::Maketext::Extract::Run;
+
+use strict;
+use vars qw( @ISA @EXPORT_OK );
+
+use Cwd;
+use Config ();
+use File::Find;
+use Getopt::Long;
+use Locale::Maketext::Extract;
+use Exporter;
+
+use constant HAS_SYMLINK => ($Config::Config{d_symlink} ? 1 : 0);
+
+ at ISA = 'Exporter';
+ at EXPORT_OK = 'xgettext';
+
+sub xgettext { __PACKAGE__->run(@_) }
+
+sub run {
+ my $self = shift;
+ local @ARGV = @_;
+
+ my %opts;
+ Getopt::Long::Configure("no_ignore_case");
+ Getopt::Long::GetOptions( \%opts,
+ 'f|files-from:s@',
+ 'D|directory:s@',
+ 'u|unescaped',
+ 'g|gnu-gettext',
+ 'o|output:s@',
+ 'd|default-domain:s',
+ 'p|output-dir:s@',
+ 'h|help',
+ ) or help();
+ help() if $opts{h};
+
+ my @po = @{$opts{o} || [($opts{d}||'messages').'.po']};
+
+ foreach my $file (@{$opts{f}||[]}) {
+ open FILE, $file or die "Cannot open $file: $!";
+ while (<FILE>) {
+ push @ARGV, $_ if -r and !-d;
+ }
+ }
+
+ foreach my $dir (@{$opts{D}||[]}) {
+ File::Find::find( {
+ wanted => sub {
+ return if
+ ( -d ) ||
+ ( $File::Find::dir =~ m!\b(?:blib|autogen|var|m4|local|CVS|\.svn)\b! ) ||
+ ( /\.po$|\.bak$|~|,D|,B$/i ) ||
+ ( /^[\.#]/ );
+ push @ARGV, $File::Find::name;
+ },
+ follow => HAS_SYMLINK,
+ }, $dir );
+ }
+
+ @ARGV = ('-') unless @ARGV;
+ s!^.[/\\]!! for @ARGV;
+
+ my $cwd = getcwd();
+
+ foreach my $dir (@{$opts{p}||['.']}) {
+ foreach my $po (@po) {
+ my $Ext = Locale::Maketext::Extract->new;
+ $Ext->read_po($po, $opts{u}) if -r $po and -s _;
+ $Ext->extract_file($_) for grep !/\.po$/i, @ARGV;
+ $Ext->compile($opts{u}) or next;
+
+ chdir $dir;
+ $Ext->write_po($po, $opts{g});
+ chdir $cwd;
+ }
+ }
+}
+
+sub help {
+ local $SIG{__WARN__} = sub {};
+ { exec "perldoc $0"; }
+ { exec "pod2text $0"; }
+}
+
+1;
Added: Locale-Maketext-Lexicon/lib/Locale/Maketext/Lexicon.pm
==============================================================================
--- (empty file)
+++ Locale-Maketext-Lexicon/lib/Locale/Maketext/Lexicon.pm Sun Apr 2 05:40:20 2006
@@ -0,0 +1,485 @@
+package Locale::Maketext::Lexicon;
+$Locale::Maketext::Lexicon::VERSION = '0.56';
+
+use strict;
+
+=head1 NAME
+
+Locale::Maketext::Lexicon - Use other catalog formats in Maketext
+
+=head1 VERSION
+
+This document describes version 0.56 of Locale::Maketext::Lexicon,
+released April 2, 2006.
+
+=head1 SYNOPSIS
+
+As part of a localization class, automatically glob for available
+lexicons:
+
+ package Hello::I18N;
+ use base 'Locale::Maketext';
+ use Locale::Maketext::Lexicon {
+ '*' => [Gettext => '/usr/local/share/locale/*/LC_MESSAGES/hello.mo'],
+ _decode => 1, # decode lexicon entries into utf8-strings
+ _auto => 1, # fallback when a key is missing from lexicons
+ };
+
+Explicitly specify languages, during compile- or run-time:
+
+ package Hello::I18N;
+ use base 'Locale::Maketext';
+ use Locale::Maketext::Lexicon {
+ de => [Gettext => 'hello_de.po'],
+ fr => [
+ Gettext => 'hello_fr.po',
+ Gettext => 'local/hello/fr.po',
+ ],
+ };
+ # ... incrementally add new lexicons
+ Locale::Maketext::Lexicon->import({
+ de => [Gettext => 'local/hello/de.po'],
+ })
+
+Alternatively, as part of a localization subclass:
+
+ package Hello::I18N::de;
+ use base 'Hello::I18N';
+ use Locale::Maketext::Lexicon (Gettext => \*DATA);
+ __DATA__
+ # Some sample data
+ msgid ""
+ msgstr ""
+ "Project-Id-Version: Hello 1.3.22.1\n"
+ "MIME-Version: 1.0\n"
+ "Content-Type: text/plain; charset=iso8859-1\n"
+ "Content-Transfer-Encoding: 8bit\n"
+
+ #: Hello.pm:10
+ msgid "Hello, World!"
+ msgstr "Hallo, Welt!"
+
+ #: Hello.pm:11
+ msgid "You have %quant(%1,piece) of mail."
+ msgstr "Sie haben %quant(%1,Poststueck,Poststuecken)."
+
+=head1 DESCRIPTION
+
+This module provides lexicon-handling modules to read from other
+localization formats, such as I<Gettext>, I<Msgcat>, and so on.
+
+If you are unfamiliar with the concept of lexicon modules, please
+consult L<Locale::Maketext> and L<http://www.autrijus.org/webl10n/>
+first.
+
+A command-line utility L<xgettext.pl> is also installed with this
+module, for extracting translatable strings from source files.
+
+=head2 The C<import> function
+
+The C<import()> function accepts two forms of arguments:
+
+=over 4
+
+=item (I<format> => I<source> ... )
+
+This form takes any number of argument pairs (usually one);
+I<source> may be a file name, a filehandle, or an array reference.
+
+For each such pair, it pass the contents specified by the second
+argument to B<Locale::Maketext::Lexicon::I<format>>->parse as a
+plain list, and export its return value as the C<%Lexicon> hash
+in the calling package.
+
+In the case that there are multiple such pairs, the lexicon
+defined by latter ones overrides earlier ones.
+
+=item { I<language> => [ I<format>, I<source> ... ] ... }
+
+This form accepts a hash reference. It will export a C<%Lexicon>
+into the subclasses specified by each I<language>, using the process
+described above. It is designed to alleviate the need to set up a
+separate subclass for each localized language, and just use the catalog
+files.
+
+This module will convert the I<language> arguments into lowercase,
+and replace all C<-> with C<_>, so C<zh_TW> and C<zh-tw> will both
+map to the C<zh_tw> subclass.
+
+If I<language> begins with C<_>, it is taken as an option that
+controls how lexicons are parsed. See L</Options> for a list
+of available options.
+
+The C<*> is a special I<language>; it must be used in conjunction
+with a filename that also contains C<*>; all matched files with
+a valid language code in the place of C<*> will be automatically
+prepared as a lexicon subclass. If there is multiple C<*> in
+the filename, the last one is used as the language name.
+
+=back
+
+=head2 Options
+
+=over 4
+
+=item C<_auto>
+
+If set to a true value, missing lookups on lexicons are handled
+silently, as if an C<Auto> lexicon has been appended on all
+language lexicons.
+
+=item C<_decode>
+
+If set to a true value, source entries will be converted into
+utf8-strings (available in Perl 5.6.1 or later). This feature
+needs the B<Encode> or B<Encode::compat> module.
+
+Currently, only the C<Gettext> backend supports this option.
+
+=item C<_encoding>
+
+This option only has effect when C<_decode> is set to true.
+It specifies an encoding to store lexicon entries, instead of
+utf8-strings.
+
+If C<_encoding> is set to C<locale>, the encoding from the
+current locale setting is used.
+
+=head2 Subclassing format handlers
+
+If you wish to override how sources specified in different data types
+are handled, please use a subclass that overrides C<lexicon_get_I<TYPE>>.
+
+XXX: not documented well enough yet. Patches welcome.
+
+=head1 NOTES
+
+When you attempt to localize an entry missing in the lexicon, Maketext
+will throw an exception by default. To inhibit this behaviour, override
+the C<_AUTO> key in your language subclasses, for example:
+
+ $Hello::I18N::en::Lexicon{_AUTO} = 1; # autocreate missing keys
+
+If you want to implement a new C<Lexicon::*> backend module, please note
+that C<parse()> takes an array containing the B<source strings> from the
+specified filehandle or filename, which are I<not> C<chomp>ed. Although
+if the source is an array reference, its elements will probably not contain
+any newline characters anyway.
+
+The C<parse()> function should return a hash reference, which will be
+assigned to the I<typeglob> (C<*Lexicon>) of the language module. All
+it amounts to is that if the returned reference points to a tied hash,
+the C<%Lexicon> will be aliased to the same tied hash if it was not
+initialized previously.
+
+=cut
+
+our %Opts;
+sub option { shift if ref($_[0]); $Opts{lc $_[0]} }
+sub set_option { shift if ref($_[0]); $Opts{lc $_[0]} = $_[1] }
+
+sub encoding {
+ my $encoding = option(@_, 'encoding') or return;
+ return $encoding unless lc($encoding) eq 'locale';
+
+ no warnings 'uninitialized';
+ my ($country_language, $locale_encoding);
+
+ local $@;
+ eval {
+ require I18N::Langinfo;
+ $locale_encoding = I18N::Langinfo::langinfo(I18N::Langinfo::CODESET());
+ } or eval {
+ require Win32::Console;
+ $locale_encoding = 'cp'.Win32::Console::OutputCP();
+ };
+ if (!$locale_encoding) {
+ foreach my $key (qw( LANGUAGE LC_ALL LC_MESSAGES LANG )) {
+ $ENV{$key} =~ /^([^.]+)\.([^.:]+)/ or next;
+ ($country_language, $locale_encoding) = ($1, $2);
+ last;
+ }
+ }
+ if (defined $locale_encoding &&
+ lc($locale_encoding) eq 'euc' &&
+ defined $country_language) {
+ if ($country_language =~ /^ja_JP|japan(?:ese)?$/i) {
+ $locale_encoding = 'euc-jp';
+ } elsif ($country_language =~ /^ko_KR|korean?$/i) {
+ $locale_encoding = 'euc-kr';
+ } elsif ($country_language =~ /^zh_CN|chin(?:a|ese)?$/i) {
+ $locale_encoding = 'euc-cn';
+ } elsif ($country_language =~ /^zh_TW|taiwan(?:ese)?$/i) {
+ $locale_encoding = 'euc-tw';
+ }
+ }
+
+ return $locale_encoding;
+}
+
+sub import {
+ my $class = shift;
+ return unless @_;
+
+ my %entries;
+ if (UNIVERSAL::isa($_[0], 'HASH')) {
+ # a hashref with $lang as keys, [$format, $src ...] as values
+ %entries = %{$_[0]};
+ }
+ elsif (@_ % 2) {
+ %entries = ( '' => [ @_ ] );
+ }
+
+ # expand the wildcard entry
+ if (my $wild_entry = delete $entries{'*'}) {
+ while (my ($format, $src) = splice(@$wild_entry, 0, 2)) {
+ next if ref($src); # XXX: implement globbing for the 'Tie' backend
+
+ my $pattern = quotemeta($src);
+ $pattern =~ s/\\\*(?=[^*]+$)/\([-\\w]+\)/g or next;
+ $pattern =~ s/\\\*/.*?/g;
+ $pattern =~ s/\\\?/./g;
+ $pattern =~ s/\\\[/[/g;
+ $pattern =~ s/\\\]/]/g;
+ $pattern =~ s[\\\{(.*?)\\\\}][
+ '(?:'.join('|', split(/,/, $1)).')'
+ ]eg;
+
+ require File::Glob;
+ foreach my $file (File::Glob::bsd_glob($src)) {
+ $file =~ /$pattern/ or next;
+ push @{$entries{$1}}, ($format => $file) if $1;
+ }
+ delete $entries{$1}
+ unless !defined($1)
+ or exists $entries{$1} and @{$entries{$1}};
+ }
+ }
+
+ %Opts = ();
+ foreach my $key (grep /^_/, keys %entries) {
+ set_option(lc(substr($key, 1)) => delete($entries{$key}));
+ }
+ my $OptsRef = { %Opts };
+
+ while (my ($lang, $entry) = each %entries) {
+ my $export = caller;
+
+ if (length $lang) {
+ # normalize language tag to Maketext's subclass convention
+ $lang = lc($lang);
+ $lang =~ s/-/_/g;
+ $export .= "::$lang";
+ }
+
+ my @pairs = @{$entry||[]} or die "no format specified";
+
+ while (my ($format, $src) = splice(@pairs, 0, 2)) {
+ if (defined($src) and !ref($src) and $src =~ /\*/) {
+ unshift(@pairs, $format => $_) for File::Glob::bsd_glob($src);
+ next;
+ }
+
+ local $@;
+ my @content = eval {
+ $class->lexicon_get($src, scalar caller, $lang);
+ };
+ next if $@ and $@ eq 'next';
+ die $@ if $@;
+
+ no strict 'refs';
+ eval "use $class\::$format; 1" or die $@;
+
+ if (defined %{"$export\::Lexicon"}) {
+ if (ref(tied %{"$export\::Lexicon"}) eq __PACKAGE__) {
+ tied(%{"$export\::Lexicon"})->_force;
+ }
+ # clear the memoized cache for old entries:
+ Locale::Maketext->clear_isa_scan;
+ # be very careful not to pollute the possibly tied lexicon
+ *{"$export\::Lexicon"} = {
+ %{"$export\::Lexicon"},
+ %{"$class\::$format"->parse(@content)},
+ };
+ }
+ else {
+ my $promise;
+ tie %{"$export\::Lexicon"}, __PACKAGE__, {
+ Opts => $OptsRef,
+ Export => "$export\::Lexicon",
+ Class => "$class\::$format",
+ Content => \@content,
+ };
+ }
+
+ push(@{"$export\::ISA"}, scalar caller) if length $lang;
+ }
+ }
+}
+
+sub TIEHASH {
+ my ($class, $args) = @_;
+ return bless($args, $class);
+
+}
+
+{
+ no strict 'refs';
+ sub _force {
+ my $args = shift;
+ if (!$args->{Done}++) {
+ local *Opts = $args->{Opts};
+ *{$args->{Export}} = $args->{Class}->parse(@{$args->{Content}});
+ if (option('auto')) {
+ (\%{$args->{Export}})->{'_AUTO'} = 1;
+ }
+ }
+ return \%{$args->{Export}};
+ }
+ sub FETCH { _force($_[0])->{$_[1]} }
+ sub EXISTS { _force($_[0])->{$_[1]} }
+ sub DELETE { delete _force($_[0])->{$_[1]} }
+ sub SCALAR { scalar %{_force($_[0])} }
+ sub STORE { _force($_[0])->{$_[1]} = $_[2] }
+ sub CLEAR { %{_force($_[0])->{$_[1]}} = () }
+ sub NEXTKEY { each %{_force($_[0])} }
+ sub FIRSTKEY {
+ my $hash = _force($_[0]);
+ my $a = scalar keys %$hash;
+ each %$hash;
+ }
+}
+
+sub lexicon_get {
+ my ($class, $src, $caller, $lang) = @_;
+ return unless defined $src;
+
+ foreach my $type (qw(ARRAY HASH SCALAR GLOB), ref($src)) {
+ next unless UNIVERSAL::isa($src, $type);
+
+ my $method = 'lexicon_get_' . lc($type);
+ die "cannot handle source $type for $src: no $method defined"
+ unless $class->can($method);
+
+ return $class->$method($src, $caller, $lang);
+ }
+
+ # default handler
+ return $class->lexicon_get_($src, $caller, $lang);
+}
+
+# for scalarrefs and arrayrefs we just dereference the $src
+sub lexicon_get_scalar { ${$_[1]} }
+sub lexicon_get_array { @{$_[1]} }
+
+sub lexicon_get_hash {
+ my ($class, $src, $caller, $lang) = @_;
+ return map { $_ => $src->{$_} } sort keys %$src;
+}
+
+sub lexicon_get_glob {
+ my ($class, $src, $caller, $lang) = @_;
+
+ no strict 'refs';
+
+ # be extra magical and check for DATA section
+ if (eof($src) and $src eq \*{"$caller\::DATA"} or $src eq \*{"main\::DATA"}) {
+ # okay, the *DATA isn't initiated yet. let's read.
+ #
+ require FileHandle;
+ my $fh = FileHandle->new;
+ my $package = ( ($src eq \*{"main\::DATA"}) ? 'main' : $caller );
+
+ if ( $package eq 'main' and -e $0 ) {
+ $fh->open($0) or die "Can't open $0: $!";
+ }
+ else {
+ my $level = 1;
+ while ( my ($pkg, $filename) = caller($level++) ) {
+ next unless $pkg eq $package;
+ next unless -e $filename;
+ next;
+
+ $fh->open($filename) or die "Can't open $filename: $!";
+ last;
+ }
+ }
+
+ while (<$fh>) {
+ # okay, this isn't foolproof, but good enough
+ last if /^__DATA__$/;
+ }
+
+ return <$fh>;
+ }
+
+ # fh containing the lines
+ my $pos = tell($src);
+ my @lines = <$src>;
+ seek($src, $pos, 0);
+ return @lines;
+}
+
+# assume filename - search path, open and return its contents
+sub lexicon_get_ {
+ my ($class, $src, $caller, $lang) = @_;
+
+ require FileHandle;
+ require File::Spec;
+
+ my $fh = FileHandle->new;
+ my @path = split('::', $caller);
+ push @path, $lang if length $lang;
+
+ $src = (grep { -e } map {
+ my @subpath = @path[0..$_];
+ map { File::Spec->catfile($_, @subpath, $src) } @INC;
+ } -1 .. $#path)[-1] unless -e $src;
+
+ defined $src or die 'next';
+
+ $fh->open($src) or die "Cannot read $src (called by $caller): $!";
+ binmode($fh);
+ return <$fh>;
+}
+
+1;
+
+=head1 ACKNOWLEDGMENTS
+
+Thanks to Jesse Vincent for suggesting this module to be written.
+
+Thanks also to Sean M. Burke for coming up with B<Locale::Maketext>
+in the first place, and encouraging me to experiment with alternative
+Lexicon syntaxes.
+
+Thanks also to Yi Ma Mao for providing the MO file parsing subroutine,
+as well as inspiring me to implement file globbing and transcoding
+support.
+
+See the F<AUTHORS> file in the distribution for a list of people who
+have sent helpful patches, ideas or comments.
+
+=head1 SEE ALSO
+
+L<xgettext.pl> for extracting translatable strings from common template
+systems and perl source files.
+
+L<Locale::Maketext>, L<Locale::Maketext::Lexicon::Auto>,
+L<Locale::Maketext::Lexicon::Gettext>, L<Locale::Maketext::Lexicon::Msgcat>,
+L<Locale::Maketext::Lexicon::Tie>
+
+=head1 AUTHORS
+
+Audrey Tang E<lt>audreyt at audreyt.orgE<gt>
+
+=head1 COPYRIGHT
+
+Copyright 2002-2006 by Audrey Tang E<lt>audreyt at audreyt.orgE<gt>.
+
+This program is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+See L<http://www.perl.com/perl/misc/Artistic.html>
+
+=cut
Added: Locale-Maketext-Lexicon/lib/Locale/Maketext/Lexicon/Auto.pm
==============================================================================
--- (empty file)
+++ Locale-Maketext-Lexicon/lib/Locale/Maketext/Lexicon/Auto.pm Sun Apr 2 05:40:20 2006
@@ -0,0 +1,59 @@
+package Locale::Maketext::Lexicon::Auto;
+$Locale::Maketext::Lexicon::Auto::VERSION = '0.02';
+
+use strict;
+
+=head1 NAME
+
+Locale::Maketext::Lexicon::Auto - Auto fallback lexicon for Maketext
+
+=head1 SYNOPSIS
+
+ package Hello::I18N;
+ use base 'Locale::Maketext';
+ use Locale::Maketext::Lexicon {
+ en => ['Auto'],
+ # ... other languages
+ };
+
+=head1 DESCRIPTION
+
+This module builds a simple Lexicon hash that contains nothing but
+C<( '_AUTO' =E<gt> 1)>, which tells C<Locale::Maketext> that no
+localizing is needed -- just use the lookup key as the returned string.
+
+It is especially useful if you're starting to prototype a program, and
+do not want to deal with the localization files yet.
+
+=head1 CAVEATS
+
+If the key to C<-E<gt>maketext> begins with a C<_>, C<Locale::Maketext>
+will still throw an exception. See L<Locale::Maketext/CONTROLLING LOOKUP
+FAILURE> for how to prevent it.
+
+=cut
+
+sub parse {
+ return { _AUTO => 1 };
+}
+
+1;
+
+=head1 SEE ALSO
+
+L<Locale::Maketext>, L<Locale::Maketext::Lexicon>
+
+=head1 AUTHORS
+
+Audrey Tang E<lt>audreyt at audreyt.orgE<gt>
+
+=head1 COPYRIGHT
+
+Copyright 2002, 2003, 2004 by Audrey Tang E<lt>audreyt at audreyt.orgE<gt>.
+
+This program is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+See L<http://www.perl.com/perl/misc/Artistic.html>
+
+=cut
Added: Locale-Maketext-Lexicon/lib/Locale/Maketext/Lexicon/Gettext.pm
==============================================================================
--- (empty file)
+++ Locale-Maketext-Lexicon/lib/Locale/Maketext/Lexicon/Gettext.pm Sun Apr 2 05:40:20 2006
@@ -0,0 +1,274 @@
+package Locale::Maketext::Lexicon::Gettext;
+$Locale::Maketext::Lexicon::Gettext::VERSION = '0.14';
+
+use strict;
+
+=head1 NAME
+
+Locale::Maketext::Lexicon::Gettext - PO and MO file parser for Maketext
+
+=head1 SYNOPSIS
+
+Called via B<Locale::Maketext::Lexicon>:
+
+ package Hello::I18N;
+ use base 'Locale::Maketext';
+ use Locale::Maketext::Lexicon {
+ de => [Gettext => 'hello/de.mo'],
+ };
+
+Directly calling C<parse()>:
+
+ use Locale::Maketext::Lexicon::Gettext;
+ my %Lexicon = %{ Locale::Maketext::Lexicon::Gettext->parse(<DATA>) };
+ __DATA__
+ #: Hello.pm:10
+ msgid "Hello, World!"
+ msgstr "Hallo, Welt!"
+
+ #: Hello.pm:11
+ msgid "You have %quant(%1,piece) of mail."
+ msgstr "Sie haben %quant(%1,Poststueck,Poststuecken)."
+
+=head1 DESCRIPTION
+
+This module implements a perl-based C<Gettext> parser for
+B<Locale::Maketext>. It transforms all C<%1>, C<%2>, <%*>... sequences
+to C<[_1]>, C<[_2]>, C<[_*]>, and so on. It accepts either plain PO
+file, or a MO file which will be handled with a pure-perl parser
+adapted from Imacat's C<Locale::Maketext::Gettext>.
+
+Since version 0.03, this module also looks for C<%I<function>(I<args...>)>
+in the lexicon strings, and transform it to C<[I<function>,I<args...>]>.
+Any C<%1>, C<%2>... sequences inside the I<args> will have their percent
+signs (C<%>) replaced by underscores (C<_>).
+
+The name of I<function> above should begin with a letter or underscore,
+followed by any number of alphanumeric characters and/or underscores.
+As an exception, the function name may also consist of a single asterisk
+(C<*>) or pound sign (C<#>), which are C<Locale::Maketext>'s shorthands
+for C<quant> and C<numf>, respectively.
+
+As an additional feature, this module also parses MIME-header style
+metadata specified in the null msgstr (C<"">), and add them to the
+C<%Lexicon> with a C<__> prefix. For example, the example above will
+set C<__Content-Type> to C<text/plain; charset=iso8859-1>, without
+the newline or the colon.
+
+Any normal entry that duplicates a metadata entry takes precedence.
+Hence, a C<msgid "__Content-Type"> line occurs anywhere should override
+the above value.
+
+=head1 OPTIONS
+
+=head2 use_fuzzy
+
+When parsing PO files, fuzzy entries (entries marked with C<#, fuzzy>)
+are silently ignored. If you wish to use fuzzy entries, specify a true
+value to the C<_use_fuzzy> option:
+
+ use Locale::Maketext::Lexicon {
+ de => [Gettext => 'hello/de.mo'],
+ _use_fuzzy => 1,
+ };
+
+=head2 allow_empty
+
+When parsing PO files, empty entries (entries with C<msgstr "">) are
+silently ignored. If you wish to allow empty entries, specify a true
+value to the C<_allow_empty> option:
+
+ use Locale::Maketext::Lexicon {
+ de => [Gettext => 'hello/de.mo'],
+ _allow_empty => 1,
+ };
+
+=cut
+
+my ($InputEncoding, $OutputEncoding, $DoEncoding);
+
+sub input_encoding { $InputEncoding };
+sub output_encoding { $OutputEncoding };
+
+sub parse {
+ my $self = shift;
+ my (%var, $key, @ret);
+ my @metadata;
+
+ $InputEncoding = $OutputEncoding = $DoEncoding = undef;
+
+ use Carp;
+ Carp::cluck "Undefined source called\n" unless defined $_[0];
+
+ # Check for magic string of MO files
+ return parse_mo(join('', @_))
+ if ($_[0] =~ /^\x95\x04\x12\xde/ or $_[0] =~ /^\xde\x12\x04\x95/);
+
+ local $^W; # no 'uninitialized' warnings, please.
+
+ require Locale::Maketext::Lexicon;
+ my $UseFuzzy = Locale::Maketext::Lexicon::option('use_fuzzy');
+ my $AllowEmpty = Locale::Maketext::Lexicon::option('allow_empty');
+ my $process = sub {
+ if ( length($var{msgstr}) and ($UseFuzzy or !$var{fuzzy}) ) {
+ push @ret, (map transform($_), @var{'msgid', 'msgstr'});
+ }
+ elsif ( $AllowEmpty ) {
+ push @ret, (transform($var{msgid}), '');
+ }
+ push @metadata, parse_metadata($var{msgstr})
+ if $var{msgid} eq '';
+ %var = ();
+ };
+
+ # Parse PO files
+ foreach (@_) {
+ s/[\015\012]*\z//; # fix CRLF issues
+
+ /^(msgid|msgstr) +"(.*)" *$/ ? do { # leading strings
+ $var{$1} = $2;
+ $key = $1;
+ } :
+
+ /^"(.*)" *$/ ? do { # continued strings
+ $var{$key} .= $1;
+ } :
+
+ /^#, +(.*) *$/ ? do { # control variables
+ $var{$_} = 1 for split(/,\s+/, $1);
+ } :
+
+ /^ *$/ && %var ? do { # interpolate string escapes
+ $process->($_);
+ } : ();
+ }
+ # do not silently skip last entry
+ $process->() if keys %var != 0;
+
+ push @ret, map { transform($_) } @var{'msgid', 'msgstr'}
+ if length $var{msgstr};
+ push @metadata, parse_metadata($var{msgstr})
+ if $var{msgid} eq '';
+
+ return {@metadata, @ret};
+}
+
+sub parse_metadata {
+ return map {
+ (/^([^\x00-\x1f\x80-\xff :=]+):\s*(.*)$/) ?
+ ($1 eq 'Content-Type') ? do {
+ my $enc = $2;
+ if ($enc =~ /\bcharset=\s*([-\w]+)/i) {
+ $InputEncoding = $1 || '';
+ $OutputEncoding = Locale::Maketext::Lexicon::encoding() || '';
+ $InputEncoding = 'utf8' if $InputEncoding =~ /^utf-?8$/i;
+ $OutputEncoding = 'utf8' if $OutputEncoding =~ /^utf-?8$/i;
+ if ( Locale::Maketext::Lexicon::option('decode') and
+ (!$OutputEncoding or $InputEncoding ne $OutputEncoding)) {
+ require Encode::compat if $] < 5.007001;
+ require Encode;
+ $DoEncoding = 1;
+ }
+ }
+ ("__Content-Type", $enc);
+ } : ("__$1", $2)
+ : ();
+ } split(/\r*\n+\r*/, transform(pop));
+}
+
+sub transform {
+ my $str = shift;
+
+ if ($DoEncoding and $InputEncoding) {
+ $str = ($InputEncoding eq 'utf8')
+ ? Encode::decode_utf8($str)
+ : Encode::decode($InputEncoding, $str)
+ }
+
+ $str =~ s/\\([0x]..|c?.)/qq{"\\$1"}/eeg;
+
+ if ($DoEncoding and $OutputEncoding) {
+ $str = ($OutputEncoding eq 'utf8')
+ ? Encode::encode_utf8($str)
+ : Encode::encode($OutputEncoding, $str)
+ }
+
+ $str =~ s/([~\[\]])/~$1/g;
+ $str =~ s/(?<![%\\])%([A-Za-z#*]\w*)\(([^\)]*)\)/[$1,~~~$2~~~]/g;
+ $str = join('', map {
+ /^~~~.*~~~$/ ? unescape(substr($_, 3, -3)) : $_
+ } split(/(~~~.*?~~~)/, $str));
+ $str =~ s/(?<![%\\])%(\d+|\*)/\[_$1]/g;
+
+ return $str;
+}
+
+sub unescape {
+ join(',', map {
+ /^%(?:\d+|\*)$/ ? ("_" . substr($_, 1)) : $_
+ } split(/,/, $_[0]));
+}
+
+# This subroutine was derived from Locale::Maketext::Gettext::readmo()
+# under the Perl License; the original author is Yi Ma Mao (IMACAT).
+sub parse_mo {
+ my $content = shift;
+ my $tmpl = (substr($content, 0, 4) eq "\xde\x12\x04\x95") ? 'V' : 'N';
+
+ # Check the MO format revision number
+ # There is only one revision now: revision 0.
+ return if unpack($tmpl, substr($content, 4, 4)) > 0;
+
+ my ($num, $offo, $offt);
+ # Number of strings
+ $num = unpack $tmpl, substr($content, 8, 4);
+ # Offset to the beginning of the original strings
+ $offo = unpack $tmpl, substr($content, 12, 4);
+ # Offset to the beginning of the translated strings
+ $offt = unpack $tmpl, substr($content, 16, 4);
+
+ my (@metadata, @ret);
+ for (0 .. $num - 1) {
+ my ($len, $off, $stro, $strt);
+ # The first word is the length of the string
+ $len = unpack $tmpl, substr($content, $offo+$_*8, 4);
+ # The second word is the offset of the string
+ $off = unpack $tmpl, substr($content, $offo+$_*8+4, 4);
+ # Original string
+ $stro = substr($content, $off, $len);
+
+ # The first word is the length of the string
+ $len = unpack $tmpl, substr($content, $offt+$_*8, 4);
+ # The second word is the offset of the string
+ $off = unpack $tmpl, substr($content, $offt+$_*8+4, 4);
+ # Translated string
+ $strt = substr($content, $off, $len);
+
+ # Hash it
+ push @metadata, parse_metadata($strt) if $stro eq '';
+ push @ret, (map transform($_), $stro, $strt) if length $strt;
+ }
+
+ return {@metadata, @ret};
+}
+
+1;
+
+=head1 SEE ALSO
+
+L<Locale::Maketext>, L<Locale::Maketext::Lexicon>
+
+=head1 AUTHORS
+
+Audrey Tang E<lt>audreyt at audreyt.orgE<gt>
+
+=head1 COPYRIGHT
+
+Copyright 2002, 2003, 2004 by Audrey Tang E<lt>audreyt at audreyt.orgE<gt>.
+
+This program is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+See L<http://www.perl.com/perl/misc/Artistic.html>
+
+=cut
Added: Locale-Maketext-Lexicon/lib/Locale/Maketext/Lexicon/Msgcat.pm
==============================================================================
--- (empty file)
+++ Locale-Maketext-Lexicon/lib/Locale/Maketext/Lexicon/Msgcat.pm Sun Apr 2 05:40:20 2006
@@ -0,0 +1,123 @@
+package Locale::Maketext::Lexicon::Msgcat;
+$Locale::Maketext::Lexicon::Msgcat::VERSION = '0.02';
+
+use strict;
+
+=head1 NAME
+
+Locale::Maketext::Lexicon::Msgcat - Msgcat catalog parser Maketext
+
+=head1 SYNOPSIS
+
+ package Hello::I18N;
+ use base 'Locale::Maketext';
+ use Locale::Maketext::Lexicon {
+ en => ['Msgcat', 'en_US/hello.pl.m'],
+ };
+
+ package main;
+ my $lh = Hello::I18N->get_handle('en');
+ print $lh->maketext(1,2); # set 1, msg 2
+ print $lh->maketext("1,2"); # same thing
+
+=head1 DESCRIPTION
+
+This module parses one or more Msgcat catalogs in plain text format,
+and returns a Lexicon hash, which may be looked up either with a
+two-argument form (C<$set_id, $msg_id>) or as a single string
+(C<"$set_id,$msg_id">).
+
+=head1 NOTES
+
+All special characters (C<[>, C<]> and C<~>) in catalogs will be
+escaped so they lose their magic meanings. That means C<-E<gt>maketext>
+calls to this lexicon will I<not> take any additional arguments.
+
+=cut
+
+sub parse {
+ my $set = 0;
+ my $msg = undef;
+ my ($qr, $qq, $qc) = (qr//, '', '');
+ my @out;
+
+ # Set up the msgcat handler
+ { no strict 'refs';
+ *{Locale::Maketext::msgcat} = \&_msgcat; }
+
+ # Parse *.m files; Locale::Msgcat objects and *.cat are not yet supported.
+ foreach (@_) {
+ s/[\015\012]*\z//; # fix CRLF issues
+
+ /^\$set (\d+)/ ? do { # set_id
+ $set = int($1);
+ push @out, $1, "[msgcat,$1,_1]";
+ } :
+
+ /^\$quote (.)/ ? do { # quote character
+ $qc = $1;
+ $qq = quotemeta($1);
+ $qr = qr/$qq?/;
+ } :
+
+ /^(\d+) ($qr)(.*?)\2(\\?)$/ ? do { # msg_id and msg_str
+ local $^W;
+ push @out, "$set,".int($1);
+ if ($4) {
+ $msg = $3;
+ }
+ else {
+ push @out, unescape($qq, $qc, $3);
+ undef $msg;
+ }
+ } :
+
+ (defined $msg and /^($qr)(.*?)\1(\\?)$/) ? do { # continued string
+ local $^W;
+ if ($3) {
+ $msg .= $2;
+ }
+ else {
+ push @out, unescape($qq, $qc, $msg . $2);
+ undef $msg;
+ }
+ } : ();
+ }
+
+ push @out, '' if defined $msg;
+
+ return { @out };
+}
+
+sub _msgcat {
+ my ($self, $set_id, $msg_id, @args) = @_;
+ return $self->maketext(int($set_id).','.int($msg_id), @args)
+}
+
+sub unescape {
+ my ($qq, $qc, $str) = @_;
+ $str =~ s/(\\([ntvbrf\\$qq]))/($2 eq $qc) ? $qc : eval qq("$1")/e;
+ $str =~ s/([\~\[\]])/~$1/g;
+ return $str;
+}
+
+1;
+
+=head1 SEE ALSO
+
+L<Locale::Maketext>, L<Locale::Maketext::Lexicon>
+
+=head1 AUTHORS
+
+Audrey Tang E<lt>audreyt at audreyt.orgE<gt>
+
+=head1 COPYRIGHT
+
+Copyright 2002, 2003, 2004 by Audrey Tang E<lt>audreyt at audreyt.orgE<gt>.
+
+This program is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+See L<http://www.perl.com/perl/misc/Artistic.html>
+
+=cut
Added: Locale-Maketext-Lexicon/lib/Locale/Maketext/Lexicon/Tie.pm
==============================================================================
--- (empty file)
+++ Locale-Maketext-Lexicon/lib/Locale/Maketext/Lexicon/Tie.pm Sun Apr 2 05:40:20 2006
@@ -0,0 +1,67 @@
+package Locale::Maketext::Lexicon::Tie;
+$Locale::Maketext::Lexicon::Tie::VERSION = '0.03';
+
+use strict;
+use Symbol ();
+
+=head1 NAME
+
+Locale::Maketext::Lexicon::Tie - Use tied hashes as lexicons for Maketext
+
+=head1 SYNOPSIS
+
+ package Hello::I18N;
+ use base 'Locale::Maketext';
+ use Locale::Maketext::Lexicon {
+ en => [ Tie => [ DB_File => 'en.db' ] ],
+ };
+
+=head1 DESCRIPTION
+
+This module lets you easily C<tie> the C<%Lexicon> hash to a database
+or other data sources. It takes an array reference of arguments, and
+passes them directly to C<tie()>.
+
+Entries will then be fetched whenever it is used; this module does not
+cache them.
+
+=cut
+
+sub parse {
+ my $self = shift;
+ my $mod = shift;
+ my $sym = Symbol::gensym();
+
+ # Load the target module into memory
+ {
+ no strict 'refs';
+ eval "use $mod; 1" or die $@ unless defined %{"$mod\::"};
+ }
+
+ # Perform the actual tie
+ tie %{*$sym}, $mod, @_;
+
+ # Returns the GLOB reference, so %Lexicon will be tied too
+ return $sym;
+}
+
+1;
+
+=head1 SEE ALSO
+
+L<Locale::Maketext>, L<Locale::Maketext::Lexicon>
+
+=head1 AUTHORS
+
+Audrey Tang E<lt>audreyt at audreyt.orgE<gt>
+
+=head1 COPYRIGHT
+
+Copyright 2002, 2003, 2004 by Audrey Tang E<lt>audreyt at audreyt.orgE<gt>.
+
+This program is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+See L<http://www.perl.com/perl/misc/Artistic.html>
+
+=cut
Added: Locale-Maketext-Lexicon/script/xgettext.pl
==============================================================================
--- (empty file)
+++ Locale-Maketext-Lexicon/script/xgettext.pl Sun Apr 2 05:40:20 2006
@@ -0,0 +1,100 @@
+#!/usr/bin/perl
+use strict;
+use Locale::Maketext::Extract::Run 'xgettext';
+xgettext(@ARGV);
+
+=head1 NAME
+
+xgettext.pl - Extract translatable strings from source
+
+=head1 SYNOPSIS
+
+B<xgettext.pl> [I<OPTION>] [I<INPUTFILE>]...
+
+=head1 DESCRIPTION
+
+This program extracts translatable strings from given input files, or
+from B<STDIN> if none are given.
+
+Please see L<Locale::Maketext::Extract> for a list of supported input file
+formats.
+
+=head1 OPTIONS
+
+Mandatory arguments to long options are mandatory for short options too.
+Similarly for optional arguments.
+
+=head2 Input file location:
+
+=over 4
+
+=item I<INPUTFILE>...
+
+Files to extract messages from. If not specified, B<STDIN> is assumed.
+
+=item B<-f>, B<--files-from>=I<FILE>
+
+Get list of input files from I<FILE>.
+
+=item B<-D>, B<--directory>=I<DIRECTORY>
+
+Add I<DIRECTORY> to list for input files search.
+
+=back
+
+=head2 Output file location:
+
+=over 4
+
+=item B<-d>, B<--default-domain>=I<NAME>
+
+Use I<NAME>.po for output, instead of C<messages.po>.
+
+=item B<-o>, B<--output>=I<FILE>
+
+PO file name to be written or incrementally updated; C<-> means writing
+to B<STDOUT>.
+
+=item B<-p>, B<--output-dir>=I<DIR>
+
+Output files will be placed in directory I<DIR>.
+
+=back
+
+=head2 Output details:
+
+=over 4
+
+=item B<-u>, B<--unescaped>
+
+Disables conversion from B<Maketext> format to B<Gettext> format -- i.e.
+leave all brackets alone. This is useful if you are also using the
+B<Gettext> syntax in your program.
+
+=item B<-g>, B<--gnu-gettext>
+
+Enables GNU gettext interoperability by printing C<#, perl-maketext-format>
+before each entry that has C<%> variables.
+
+=back
+
+=head1 SEE ALSO
+
+L<Locale::Maketext::Extract>,
+L<Locale::Maketext::Lexicon::Gettext>,
+L<Locale::Maketext>
+
+=head1 AUTHORS
+
+Audrey Tang E<lt>audreyt at audreyt.orgE<gt>
+
+=head1 COPYRIGHT
+
+Copyright 2002, 2003, 2004 by Audrey Tang E<lt>audreyt at audreyt.orgE<gt>.
+
+This program is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+See L<http://www.perl.com/perl/misc/Artistic.html>
+
+=cut
Added: Locale-Maketext-Lexicon/t/1-basic.t
==============================================================================
--- (empty file)
+++ Locale-Maketext-Lexicon/t/1-basic.t Sun Apr 2 05:40:20 2006
@@ -0,0 +1,234 @@
+#!/usr/bin/perl -w
+use strict;
+use Test::More tests => 30;
+
+package Hello::I18N;
+use Test::More;
+use Tie::Hash;
+
+my $warned;
+$SIG{__WARN__} = sub { $warned++ };
+
+use_ok(base => 'Locale::Maketext');
+use_ok(
+ 'Locale::Maketext::Lexicon' => {
+ en => ['Auto'],
+ fr => ['Tie' => [ 'Tie::StdHash' ]],
+ de => ['Gettext' => \*::DATA],
+ zh_tw => ['Gettext' => 't/messages.mo'],
+ zh_cn => ['Msgcat' => 't/gencat.m'],
+ zh_hk => [
+ 'Msgcat' => 't/gencat.m',
+ 'Gettext' => 't/messages.po',
+ ],
+ }
+);
+
+ok(!$warned, 'no warnings on blank lines');
+
+Locale::Maketext::Lexicon->import({
+ de_de => ['Gettext' => \*::DATA],
+ _use_fuzzy => 1,
+});
+
+package main;
+
+################################################################
+
+ok(my $lh = Hello::I18N->get_handle('en-us'), 'Auto - get_handle');
+
+is(
+ $lh->maketext('Heute die Welt'),
+ 'Heute die Welt',
+ 'Auto - autofilling'
+);
+
+################################################################
+
+ok($lh = Hello::I18N->get_handle('de'), 'Gettext - get_handle');
+
+is(
+ $lh->maketext('Hello, World!'),
+ 'Hallo, Welt!',
+ 'Gettext - simple case'
+);
+is(
+ $lh->maketext('You have [*,_1,piece] of mail.', 10),
+ 'Sie haben 10 Poststuecken.',
+ 'Gettext - complex case'
+);
+is(
+ $lh->maketext('[_1] [_2] [_*]', 1, 2, 3),
+ '123 2 1',
+ 'Gettext - asterisk interpolation'
+);
+is(
+ $lh->maketext('[_1][_2][_*]', 1, 2, 3),
+ '12321',
+ 'Gettext - concatenated variables'
+);
+is(
+ $lh->maketext('[_1]()', 10),
+ '10()',
+ 'Gettext - correct parens'
+);
+is(
+ $lh->maketext('__Content-Type'),
+ 'text/plain; charset=ISO-8859-1',
+ 'Gettext - metadata'
+);
+is(
+ $lh->maketext('[_1]()', 10),
+ '10()',
+ 'Gettext - correct parens'
+);
+is(
+ $lh->maketext("\n\nKnowledge\nAnd\nNature\n\n"),
+"\n\n".
+"Ich wuenschte recht gelehrt zu werden,\n".
+"Und moechte gern, was auf der Erden\n".
+"Und in dem Himmel ist, erfassen,\n".
+"Die Wissenschaft und die Natur.\n\n",
+ 'Gettext - multiline'
+);
+
+is(
+ eval { $lh->maketext("The Hitchhiker's Guide to the Galaxy") },
+ undef,
+ 'Gettext - fuzzy entries are ignored'
+);
+
+ok($lh = Hello::I18N->get_handle('de_de'), 'Gettext - get_handle on DATA again');
+is(
+ eval { $lh->maketext("The Hitchhiker's Guide to the Galaxy") },
+ 'Der Fuehrer des Trampers zur Galaxie',
+ 'Gettext - fuzzy entries are recognized with _use_fuzzy'
+);
+
+################################################################
+
+SKIP: {
+ skip("no msgunfmt available", 2) unless `msgunfmt -V` and !$?;
+
+ ok($lh = Hello::I18N->get_handle('zh_tw'), 'Gettext - get_handle');
+
+ is(
+ $lh->maketext('This is a test'),
+ 'éæ¯æ¸¬è©¦',
+ 'Gettext - MO File'
+ );
+}
+
+################################################################
+
+ok($lh = Hello::I18N->get_handle('fr'), 'Tie - get_handle');
+$Hello::I18N::fr::Lexicon{"Good morning"} = 'Bon jour';
+$Hello::I18N::fr::Lexicon{"Good morning, [_1]"} = 'Bon jour, [_1]';
+
+is(
+ $lh->maketext('Good morning'),
+ 'Bon jour',
+ 'Tie - simple case'
+);
+
+is(
+ $lh->maketext('Good morning, [_1]', 'Sean'),
+ 'Bon jour, Sean',
+ 'Tie - complex case'
+);
+
+################################################################
+
+ok($lh = Hello::I18N->get_handle('zh_cn'), 'Msgcat - get_handle');
+is(
+ $lh->maketext(1, 1),
+ 'First string',
+ 'Msgcat - simple case'
+);
+is(
+ $lh->maketext(1, 2),
+ 'Second string',
+ 'Msgcat - continued string'
+);
+is(
+ $lh->maketext(1, 3),
+ 'Third string',
+ 'Msgcat - quote character'
+);
+is(
+ $lh->maketext(1, 4),
+ 'Fourth string',
+ 'Msgcat - quote character + continued string'
+);
+
+################################################################
+
+ok($lh = Hello::I18N->get_handle('zh_hk'), 'Multiple lexicons - get_handle');
+
+is(
+ $lh->maketext(1, 1),
+ 'First string',
+ 'Multiple lexicons - first'
+);
+
+is(
+ $lh->maketext('This is a test'),
+ 'éæ¯æ¸¬è©¦',
+ 'Multiple lexicons - second'
+);
+
+
+__DATA__
+msgid ""
+msgstr ""
+"Project-Id-Version: Test App 0.01\n"
+"POT-Creation-Date: 2002-05-02 11:36+0800\n"
+"PO-Revision-Date: 2002-05-13 02:00+0800\n"
+"Last-Translator: <audreyt at audreyt.org>\n"
+"Language-Team: German <audreyt at audreyt.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=ISO-8859-1\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: Hello.pm:10
+msgid "Hello, World!"
+msgstr "Hallo, Welt!"
+
+#: Hello.pm:11
+msgid "You have %*(%1,piece) of mail."
+msgstr "Sie haben %*(%1,Poststueck,Poststuecken)."
+
+#: Hello.pm:12
+msgid "%1()"
+msgstr "%1()"
+
+#: Hello.pm:13
+msgid "%1 %2 %*"
+msgstr "%* %2 %1"
+
+#: Hello.pm:14
+msgid "%1%2%*"
+msgstr "%*%2%1"
+
+#: Hello.pm:15
+msgid ""
+"\n"
+"\n"
+"Knowledge\n"
+"And\n"
+"Nature\n"
+"\n"
+msgstr ""
+"\n"
+"\n"
+"Ich wuenschte recht gelehrt zu werden,\n"
+"Und moechte gern, was auf der Erden\n"
+"Und in dem Himmel ist, erfassen,\n"
+"Die Wissenschaft und die Natur.\n"
+"\n"
+
+#: Hello.pm:16
+#, big, furry, fuzzy
+msgid "The Hitchhiker's Guide to the Galaxy"
+msgstr "Der Fuehrer des Trampers zur Galaxie"
+
Added: Locale-Maketext-Lexicon/t/2-lmg.t
==============================================================================
--- (empty file)
+++ Locale-Maketext-Lexicon/t/2-lmg.t Sun Apr 2 05:40:20 2006
@@ -0,0 +1,102 @@
+#! /usr/bin/perl -w
+# Basic test suite
+# 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.
+
+use strict;
+use Test;
+
+BEGIN {
+ plan(tests => 0), exit
+ unless eval { require Encode::compat; 1 }
+ or eval { require Encode; 1 }
+}
+BEGIN { plan tests => 14 }
+
+use FindBin;
+use File::Spec::Functions qw(catdir catfile);
+use lib $FindBin::Bin;
+use vars qw($LOCALEDIR);
+$LOCALEDIR = catdir($FindBin::Bin, "locale");
+
+# Basic checks
+use Encode qw(decode);
+
+# bindtextdomain
+eval {
+ require T_L10N;
+ $_ = T_L10N->get_handle("en");
+ $_->bindtextdomain("test", $LOCALEDIR);
+ $_ = $_->bindtextdomain("test");
+};
+# 1
+ok($@, "");
+# 2
+ok($_, "$LOCALEDIR");
+
+# textdomain
+eval {
+ require T_L10N;
+ $_ = T_L10N->get_handle("en");
+ $_->bindtextdomain("test", $LOCALEDIR);
+ $_->textdomain("test");
+ $_ = $_->textdomain;
+};
+# 3
+ok($@, "");
+# 4
+ok($_, "test");
+
+# readmo
+eval {
+ $_ = catfile($LOCALEDIR, "zh_TW", "LC_MESSAGES", "test.mo");
+ ($_, %_) = T_L10N->readmo($_);
+};
+# 5
+ok($@, "");
+# 6
+ok($_, "Big5");
+# 7
+ok(scalar(keys %_), 2);
+# 8
+ok($_{"Hello, world!"}, decode("Big5", "¤j®a¦n¡C"));
+
+# English
+eval {
+ require T_L10N;
+ $_ = T_L10N->get_handle("en");
+ $_->bindtextdomain("test", $LOCALEDIR);
+ $_->textdomain("test");
+ $_ = $_->maketext("Hello, world!");
+};
+# 9
+ok($@, "");
+# 10
+ok($_, "Hiya :)");
+
+# Traditional Chinese
+eval {
+ require T_L10N;
+ $_ = T_L10N->get_handle("zh-tw");
+ $_->bindtextdomain("test", $LOCALEDIR);
+ $_->textdomain("test");
+ $_ = $_->maketext("Hello, world!");
+};
+# 11
+ok($@, "");
+# 12
+ok($_, "¤j®a¦n¡C");
+
+# Simplified Chinese
+eval {
+ require T_L10N;
+ $_ = T_L10N->get_handle("zh-cn");
+ $_->bindtextdomain("test", $LOCALEDIR);
+ $_->textdomain("test");
+ $_ = $_->maketext("Hello, world!");
+};
+# 13
+ok($@, "");
+# 14
+ok($_, "´ó¼ÒºÃ¡£");
Added: Locale-Maketext-Lexicon/t/3-big-endian.t
==============================================================================
--- (empty file)
+++ Locale-Maketext-Lexicon/t/3-big-endian.t Sun Apr 2 05:40:20 2006
@@ -0,0 +1,61 @@
+#! /usr/bin/perl -w
+# Test the big endian MO files
+# 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.
+
+use strict;
+use Test;
+
+BEGIN {
+ plan(tests => 0), exit
+ unless eval { require Encode::compat; 1 }
+ or eval { require Encode; 1 }
+}
+BEGIN { plan tests => 6 }
+
+use FindBin;
+use File::Spec::Functions qw(catdir);
+use lib $FindBin::Bin;
+use vars qw($LOCALEDIR);
+$LOCALEDIR = catdir($FindBin::Bin, "locale");
+
+# Check reading big-endian PO files
+# English
+eval {
+ require T_L10N;
+ $_ = T_L10N->get_handle("en");
+ $_->bindtextdomain("test_be", $LOCALEDIR);
+ $_->textdomain("test_be");
+ $_ = $_->maketext("Hello, world!");
+};
+# 1
+ok($@, "");
+# 2
+ok($_, "Hiya :)");
+
+# Traditional Chinese
+eval {
+ require T_L10N;
+ $_ = T_L10N->get_handle("zh-tw");
+ $_->bindtextdomain("test_be", $LOCALEDIR);
+ $_->textdomain("test_be");
+ $_ = $_->maketext("Hello, world!");
+};
+# 3
+ok($@, "");
+# 4
+ok($_, "¤j®a¦n¡C");
+
+# Simplified Chinese
+eval {
+ require T_L10N;
+ $_ = T_L10N->get_handle("zh-cn");
+ $_->bindtextdomain("test_be", $LOCALEDIR);
+ $_->textdomain("test_be");
+ $_ = $_->maketext("Hello, world!");
+};
+# 5
+ok($@, "");
+# 6
+ok($_, "´ó¼ÒºÃ¡£");
Added: Locale-Maketext-Lexicon/t/4-encodings.t
==============================================================================
--- (empty file)
+++ Locale-Maketext-Lexicon/t/4-encodings.t Sun Apr 2 05:40:20 2006
@@ -0,0 +1,176 @@
+#! /usr/bin/perl -w
+# Test suite for the different encodings
+# 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.
+
+use strict;
+use Test;
+
+BEGIN {
+ %ENV = ();
+ plan(tests => 0), exit
+ unless eval { require Encode::compat; 1 }
+ or eval { require Encode; 1 }
+}
+BEGIN { plan tests => 22 }
+
+use FindBin;
+use File::Spec::Functions qw(catdir);
+use lib $FindBin::Bin;
+use vars qw($LOCALEDIR);
+$LOCALEDIR = catdir($FindBin::Bin, "locale");
+
+# Different encodings
+# English
+# Find the default encoding
+eval {
+ require T_L10N;
+ $_ = T_L10N->get_handle("en");
+ $_->bindtextdomain("test", $LOCALEDIR);
+ $_->textdomain("test");
+ $_ = $_->encoding;
+};
+# 1
+ok($@, "");
+# 2
+ok($_, "US-ASCII");
+
+# Traditional Chinese
+# Find the default encoding
+eval {
+ require T_L10N;
+ $_ = T_L10N->get_handle("zh-tw");
+ $_->bindtextdomain("test", $LOCALEDIR);
+ $_->textdomain("test");
+ $_ = $_->encoding;
+};
+# 3
+ok($@, "");
+# 4
+ok($_, "Big5");
+
+# Turn to Big5
+eval {
+ require T_L10N;
+ $_ = T_L10N->get_handle("zh-tw");
+ $_->bindtextdomain("test", $LOCALEDIR);
+ $_->textdomain("test");
+ $_->encoding("Big5");
+ $_ = $_->maketext("Hello, world!");
+};
+# 5
+ok($@, "");
+# 6
+ok($_, "¤j®a¦n¡C");
+
+# Turn to UTF-8
+eval {
+ require T_L10N;
+ $_ = T_L10N->get_handle("zh-tw");
+ $_->bindtextdomain("test", $LOCALEDIR);
+ $_->textdomain("test");
+ $_->encoding("UTF-8");
+ $_ = $_->maketext("Hello, world!");
+};
+# 7
+ok($@, "");
+# 8
+ok($_, "大家好ã");
+
+# Turn to UTF-16LE
+eval {
+ require T_L10N;
+ $_ = T_L10N->get_handle("zh-tw");
+ $_->bindtextdomain("test", $LOCALEDIR);
+ $_->textdomain("test");
+ $_->encoding("UTF-16LE");
+ $_ = $_->maketext("Hello, world!");
+};
+# 9
+ok($@, "");
+# 10
+ok($_, "'Y¶[}Y0");
+
+# Find the default encoding, in UTF-8
+eval {
+ require T_L10N;
+ $_ = T_L10N->get_handle("zh-tw");
+ $_->bindtextdomain("test_utf8", $LOCALEDIR);
+ $_->textdomain("test_utf8");
+ $_ = $_->encoding;
+};
+# 11
+ok($@, "");
+# 12
+ok($_, "UTF-8");
+
+# Turn to UTF-8
+eval {
+ require T_L10N;
+ $_ = T_L10N->get_handle("zh-tw");
+ $_->bindtextdomain("test_utf8", $LOCALEDIR);
+ $_->textdomain("test_utf8");
+ $_->encoding("UTF-8");
+ $_ = $_->maketext("Hello, world!");
+};
+# 13
+ok($@, "");
+# 14
+ok($_, "大家好ã");
+
+# Turn to Big5
+eval {
+ require T_L10N;
+ $_ = T_L10N->get_handle("zh-tw");
+ $_->bindtextdomain("test_utf8", $LOCALEDIR);
+ $_->textdomain("test_utf8");
+ $_->encoding("Big5");
+ $_ = $_->maketext("Hello, world!");
+};
+# 15
+ok($@, "");
+# 16
+ok($_, "¤j®a¦n¡C");
+
+# Turn to UTF-16LE
+eval {
+ require T_L10N;
+ $_ = T_L10N->get_handle("zh-tw");
+ $_->bindtextdomain("test_utf8", $LOCALEDIR);
+ $_->textdomain("test_utf8");
+ $_->encoding("UTF-16LE");
+ $_ = $_->maketext("Hello, world!");
+};
+# 17
+ok($@, "");
+# 18
+ok($_, "'Y¶[}Y0");
+
+# Find the default encoding
+# Simplified Chinese
+eval {
+ require T_L10N;
+ $_ = T_L10N->get_handle("zh-cn");
+ $_->bindtextdomain("test_utf8", $LOCALEDIR);
+ $_->textdomain("test_utf8");
+ $_ = $_->encoding;
+};
+# 19
+ok($@, "");
+# 20
+ok($_, "UTF-8");
+
+# Turn to GB2312
+eval {
+ require T_L10N;
+ $_ = T_L10N->get_handle("zh-cn");
+ $_->bindtextdomain("test_utf8", $LOCALEDIR);
+ $_->textdomain("test_utf8");
+ $_->encoding("GB2312");
+ $_ = $_->maketext("Hello, world!");
+};
+# 21
+ok($@, "");
+# 22
+ok($_, "´ó¼ÒºÃ¡£");
Added: Locale-Maketext-Lexicon/t/5-extract.t
==============================================================================
--- (empty file)
+++ Locale-Maketext-Lexicon/t/5-extract.t Sun Apr 2 05:40:20 2006
@@ -0,0 +1,49 @@
+#! /usr/bin/perl -w
+use lib '../lib';
+use strict;
+use Test::More tests => 22;
+
+use_ok('Locale::Maketext::Extract');
+my $Ext = Locale::Maketext::Extract->new;
+isa_ok($Ext => 'Locale::Maketext::Extract');
+
+extract_ok('_("123")' => 123, 'Simple extraction');
+
+extract_ok('_("[_1] is happy")' => '%1 is happy', '[_1] to %1');
+extract_ok('_("[_1] is happy")' => '[_1] is happy', '[_1] verbatim', 1);
+
+extract_ok('_("[*,_1] counts")' => '%*(%1) counts', '[*,_1] to %*(%1)');
+extract_ok('_("[*,_1] counts")' => '[*,_1] counts', '[*,_1] verbatim', 1);
+
+extract_ok('_("[*,_1,_2] counts")' => '%*(%1,%2) counts',
+ '[*,_1,_2] to %*(%1,%2)');
+extract_ok('_("[*,_1,_2] counts")' => '[*,_1,_2] counts',
+ '[*,_1,_2] verbatim', 1);
+
+extract_ok(q(_('foo\$bar')) => 'foo\\\\$bar', 'Escaped \$ in q');
+extract_ok(q(_("foo\$bar")) => 'foo$bar', 'Normalized \$ in qq');
+
+extract_ok(q(_('foo\x20bar')) => 'foo\\\\x20bar', 'Escaped \x in q');
+extract_ok(q(_("foo\x20bar")) => 'foo bar', 'Normalized \x in qq');
+
+extract_ok(q(_('foo\nbar')) => 'foo\\\\nbar', 'Escaped \n in qq');
+extract_ok(q(_("foo\nbar")) => "foo\nbar", 'Normalized \n in qq');
+extract_ok(qq(_("foo\nbar")) => "foo\nbar", 'Normalized literal \n in qq');
+
+extract_ok(q(_("foo\nbar")) => "foo\nbar", 'Trailing \n in qq');
+extract_ok(qq(_("foobar\n")) => "foobar\n", 'Trailing literal \n in qq');
+
+extract_ok(q(_('foo\bar')) => 'foo\\\\bar', 'Escaped \ in q');
+extract_ok(q(_('foo\\\\bar')) => 'foo\\\\bar', 'Normalized \\\\ in q');
+extract_ok(q(_("foo\bar")) => 'foo\bar', 'Interpolated \t in qq');
+
+extract_ok(q([% loc( 'foo "bar" baz' ) %]) => 'foo \\"bar\\" baz', 'Escaped double quote in text');
+
+sub extract_ok {
+ my ($text, $result, $info, $verbatim) = @_;
+ $Ext->extract('' => $text);
+ $Ext->compile($verbatim);
+ is(join('', %{$Ext->lexicon}), $result, $info);
+ $Ext->clear;
+}
+
Added: Locale-Maketext-Lexicon/t/T_L10N.pm
==============================================================================
--- (empty file)
+++ Locale-Maketext-Lexicon/t/T_L10N.pm Sun Apr 2 05:40:20 2006
@@ -0,0 +1,83 @@
+# 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 strict;
+use FindBin;
+use File::Spec;
+use base qw(Locale::Maketext);
+
+my (%Domains, $Domain);
+sub bindtextdomain {
+ my ($self, $domain, $dir) = @_;
+ return $Domains{$domain} unless $dir;
+ $Domains{$domain} = $dir;
+
+ require Locale::Maketext::Lexicon;
+ Locale::Maketext::Lexicon->import({
+ '*' => [
+ Gettext => File::Spec->catdir( $dir, qw(* LC_MESSAGES), "$domain.mo" )
+ ],
+ _decode => 1,
+ });
+}
+
+sub textdomain {
+ my ($self, $domain) = @_;
+ $Domain = $domain if $domain;
+ return $Domain;
+}
+
+sub readmo {
+ my ($self, $file) = @_;
+ local ($/, *FH);
+ open FH, $file;
+ binmode(FH);
+
+ require Locale::Maketext::Lexicon::Gettext;
+ my $hashref = Locale::Maketext::Lexicon::Gettext::parse_mo(<FH>);
+ delete @{$hashref}{grep /^__/, keys %$hashref};
+ return Locale::Maketext::Lexicon::Gettext->input_encoding, %$hashref;
+}
+
+sub encoding {
+ my ($self, $encoding) = @_;
+
+ if ($encoding) {
+ $self->{CUR_ENC} = $encoding;
+ }
+ elsif ( !$self->{CUR_ENC} ) {
+ $self->{CUR_ENC} = $1
+ if $self->SUPER::maketext('__Content-Type') =~ /\bcharset=\s*([-\w]+)/i;
+ }
+
+ $self->{CUR_ENC};
+}
+
+sub maketext {
+ my $self = shift;
+
+ require Encode::compat if ($] == 5.006001);
+ require Encode;
+ Encode::encode($self->encoding, $self->SUPER::maketext(@_));
+}
+
+1;
+
+package T_L10N::en;
+use base qw(T_L10N);
+
+1;
+
+package T_L10N::zh_tw;
+use base qw(T_L10N);
+
+1;
+
+package T_L10N::zh_cn;
+use base qw(T_L10N);
+
+1;
Added: Locale-Maketext-Lexicon/t/gencat.m
==============================================================================
--- (empty file)
+++ Locale-Maketext-Lexicon/t/gencat.m Sun Apr 2 05:40:20 2006
@@ -0,0 +1,10 @@
+$ by Audrey Tang <audreyt at audreyt.org>
+$set 1 # $Id: //member/autrijus/Locale-Maketext-Lexicon/t/gencat.m#1 $
+1 First string
+2 Second \
+str\
+ing
+$quote X
+3 XThird stringX
+4 XFourth X\
+XstringX
Added: Locale-Maketext-Lexicon/t/locale/en/LC_MESSAGES/test.mo
==============================================================================
Binary file. No diff available.
Added: Locale-Maketext-Lexicon/t/locale/en/LC_MESSAGES/test_be.mo
==============================================================================
Binary file. No diff available.
Added: Locale-Maketext-Lexicon/t/locale/en/LC_MESSAGES/test_utf8.mo
==============================================================================
Binary file. No diff available.
Added: Locale-Maketext-Lexicon/t/locale/zh_CN/LC_MESSAGES/test.mo
==============================================================================
Binary file. No diff available.
Added: Locale-Maketext-Lexicon/t/locale/zh_CN/LC_MESSAGES/test_be.mo
==============================================================================
Binary file. No diff available.
Added: Locale-Maketext-Lexicon/t/locale/zh_CN/LC_MESSAGES/test_utf8.mo
==============================================================================
Binary file. No diff available.
Added: Locale-Maketext-Lexicon/t/locale/zh_TW/LC_MESSAGES/test.mo
==============================================================================
Binary file. No diff available.
Added: Locale-Maketext-Lexicon/t/locale/zh_TW/LC_MESSAGES/test_be.mo
==============================================================================
Binary file. No diff available.
Added: Locale-Maketext-Lexicon/t/locale/zh_TW/LC_MESSAGES/test_utf8.mo
==============================================================================
Binary file. No diff available.
Added: Locale-Maketext-Lexicon/t/messages.mo
==============================================================================
Binary file. No diff available.
Added: Locale-Maketext-Lexicon/t/messages.po
==============================================================================
--- (empty file)
+++ Locale-Maketext-Lexicon/t/messages.po Sun Apr 2 05:40:20 2006
@@ -0,0 +1,10 @@
+msgid ""
+msgstr ""
+"Last-Translator: Audrey Tang <audreyt at audreyt.org>\n"
+"Language-Team: Chinese <contact at ourinet.com>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=utf-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+msgid "This is a test"
+msgstr "éæ¯æ¸¬è©¦"
More information about the Rt-commit
mailing list