Import Upstream version 0.66

This commit is contained in:
openKylinBot 2022-05-14 17:38:16 +08:00
commit 424e60c585
1158 changed files with 387949 additions and 0 deletions

14
.mailmap Normal file
View File

@ -0,0 +1,14 @@
Allan Nordhøy <epost@anotheragency.no>
BFG <Tom.Fredrik@blenning.no>
Dr. Tobias Quathamer <toddy@debian.org> <toddy15@users.noreply.github.com>
Dr. Tobias Quathamer <toddy@debian.org>
Helge Kreutzmann <debian@helgefjell.de>
Jean-Baptiste <jean-baptiste@holcroft.fr>
Marco Ciampa <ciampix@posteo.net>
Martin Quinson <martin.quinson@ens-rennes.fr> <624847+mquinson@users.noreply.github.com>
Martin Quinson <martin.quinson@ens-rennes.fr>
Martin Quinson <martin.quinson@ens-rennes.fr> <martin.quinson@loria.fr>
Rafael Fontenelle <rafaelff@gnome.org>
Trần Ngọc Quân <vnwildman@gmail.com>
Yuri Chornoivan <yurchor@ukr.net>
Zero King <l2dy@icloud.com>

42
Build.PL Normal file
View File

@ -0,0 +1,42 @@
#!/usr/bin/perl
use lib q(.);
use Po4aBuilder;
my $build = Po4aBuilder->new
( module_name => 'po4a',
license => 'gpl',
dist_version_from => 'lib/Locale/Po4a/TransTractor.pm', # finds $VERSION
requires => { 'Pod::Parser' => 0 }, # Used for building po4a itself
configure_requires => { 'Module::Build' => 0.42 },
recommends => {'Text::WrapI18N' => 0, # Only used for wrapping long
# error/wraning lines.
'Term::ReadKey' => 0, # Needed to detect terminal
# width. Not needed if you
# don't have Text::WrapI18N.
'SGMLS' => 0, # Needed for the Sgml module.
'Unicode::GCString' => 0, # Used by the Text module (asciidoc)
'Locale::gettext' => '1.01', # Only used for
# translating the
# po4a's messages.
'YAML::Tiny' => 0, # Used by the Yaml module
},
test_requires => {'SGMLS' => 0, # Needed for the Sgml module.
'Unicode::GCString' => 0, # Used by the Text module (asciidoc)
},
script_files => ['po4a-gettextize', 'po4a-updatepo',
'po4a-translate', 'po4a-normalize', 'po4a', 'msguntypot',
'scripts/po4a-display-man', 'scripts/po4a-display-pod'],
add_to_cleanup => ['t/tmp','po/bin/*.gmo', 'blib', '_build'],
dist_abstract => 'Maintain the translations of your documentation with ease (PO for anything)',
dist_author => ['Martin Quinson (mquinson#debian.org)',
'Denis Barbier <barbier@linuxfr.org>',
'Nicolas Francois <nicolas.francois@centraliens.net>',
'Neil Williams <linux@codehelp.co.uk>']
);
$build->create_build_script;

328
CONTRIBUTING.md Normal file
View File

@ -0,0 +1,328 @@
# po4a welcomes contributions!
Even if you have never contributed to any Open Source project in the past,
you are welcome: we are willing to help and mentor you here. Check on
[First Timer](https://www.firsttimersonly.com/) to get started with
the basics of Open Source development and Social Coding.
# Software Architecture
po4a is architectured around the idea of TransTractors, that are
specific parsers in charge of separating the document structure from
the translatable content, and to reinject the translated content back
into the structure.
You can learn more on TransTractors in
[their documentation](https://po4a.org/man/man3/Locale::Po4a::TransTractor.3pm.php),
or by browsing the code of
[all existing ones](https://github.com/mquinson/po4a/tree/master/lib/Locale/Po4a).
Also don't miss the [project overview](https://po4a.org/man/man7/po4a.7.php)
if you did not read it yet.
Several binaries are built around these TransTractors, each of them
are dedicated to one step of the [translation workflow](https://po4a.org/man/man7/po4a.7.php#lbAJ)
([po4a-translate](https://po4a.org/man/man1/po4a-translate.1.php),
[po4a-updatepo](https://po4a.org/man/man1/po4a-updatepo.1.php), and
also
[po4a-gettextize](https://po4a.org/man/man1/po4a-gettextize.1.php)).
Some [other tools](https://po4a.org/man/) and are built on top of the
transtractors.
Finally, the [po4a command](https://po4a.org/man/man1/po4a.1.php) tool
takes automatically care of the translation workflow, updating the po
files and translations when needed.
# Finding something to hack
- Check the [GitHub issues](https://github.com/mquinson/po4a/issues).
Search in particular for the tasks are marked [beginner
friendly](https://github.com/mquinson/po4a/issues?q=is%3Aissue+is%3Aopen+label%3A%22beginner+friendly%22),
as they should be accessible even if you're just starting with the
po4a development.
- Check the [Debian bug reports](https://bugs.debian.org/cgi-bin/pkgreport.cgi?src=po4a),
since most of these reports are not related to Debian in any way.
Actually, they should be forwarded to the GitHub issue tracker, but
it's easier to read them on Debian directly.
[Some of them](https://bugs.debian.org/cgi-bin/pkgreport.cgi?src=po4a;tag=newcomer)
are tagged as "new comer" (this list may be currently empty when you
click it, though).
- Check the [TODO](https://github.com/mquinson/po4a/blob/master/TODO)
file in the archive. This file often gets outdated, but you may find
some inspiring notes.
- Add support for a new format. The best is to add support for a
format that you need yourself, or to convince some prospective users.
There is no better testing to a new TransTractor than the
translation of a large document used in production somewhere. Don't
forget to add all relevant tests to your format.
- po4a comes with a fairly large amount of documentation. You are
welcome to fix or report any typo or errors. It would be good to improve
this documentation to follow the [Best Practices](http://www.writethedocs.org/guide/)
from WriteTheDocs, and many sections would need a full rewriting to
be in proper english. We should however refrain from superfluous
changes when possible to reduce the burden on our translators (hint:
rephrasing globish to english is NOT a superfluous change).
# Testing your changes
Of course you should make sure that your PR does not break any test to
get accepted. If you fix an issue or add a feature, we may be
reluctant to integrate your change without a new dedicated test, to
ensure that bugs won't resurface in the future.
```sh
perl Build.PL
./Build test
```
*Test dependencies:*
- On Debian: check the .travis.yml file in the root directory for a full list.
- On Fedora 24 (if you installed from the rpm po4a package):
`perl-SGMLSpm perl-TermReadKey perl-Text-WrapI18N perl-Module-Build
perl-Test-Simple perl-Unicode-LineBreak perl-HTML-TokeParser-Simple
docbook-dtds`
- On openSUSE Leap 15.2:
`perl-SGML-Parser-OpenSP perl-TermReadKey perl-Text-WrapI18N perl-Module-Build
perl-Test-Simple perl-Unicode-LineBreak perl-HTML-TokeParser-Simple
docbook-dtds`
When writing or improving a test, you probably want to select the test
to run, and make it verbose. The tests are executed from the "_t_"
directory.
```
./Build test --test_files t/25-yaml.t verbose=1
```
The PERL5LIB variable can be used to run your modified modules without
reinstalling everything:
```
PERL5LIB=../lib/ perl ../po4a-normalize -f text -o markdown t-20-text/PandocYamlFrontMatter.md
```
To the opposite, if you want to test the installed binaries instead of
the local ones, simply set the AUTOPKGTEST_TMP variable:
```
AUTOPKGTEST_TMP=1 ./Build test
```
The test harness changes the permissions of many files and directories
to ensure that each test remain limited to their own directory. If you
interrupt the tests, they may fail to restore the previous situation.
In this case, just run the first "test" that is in charge of restoring
any possible mess that the other tests could introduce.
```
./Build test --test_files t/00-perms.t
```
## Writing a test
In order to define a new test, you can use some convenience
helpers. If you follow some conventions, you don't have to
write much boilerplate code.
Each test is defined using a perl hash with several keys.
Every test needs to have the key "_doc_", which contains
a short description of the test.
If you need to test the output of a module, it should suffice
to define a second key in the hash, named "_normalize_".
This key points to a string which can be used for the
script `po4a-normalize`. See for example the YAML tests
for some easy test definitions.
The "_normalize_" tests expect to find at least four files
in the corresponding test directory:
1. The master file used as input for po4a.
2. The expected .pot file, using the same name as the master
file. The extension is changed to "_.pot_".
3. The expected translated file, again using the same name
as the master file. The extension is changed to "_.out_".
4. The expected messages on stderr, again using the same name
as the master file. The extension is changed to "_.err_".
Here's an example. If you define the following hash:
```
push @tests,
{
'doc' => "YAML UTF-8 test",
'normalize' => "-f yaml -M UTF-8 t-25-yaml/yamlutf8.yaml",
};
```
... you need to have at least the following four files:
```
t-25-yaml/yamlutf8.yaml
t-25-yaml/yamlutf8.pot
t-25-yaml/yamlutf8.out
t-25-yaml/yamlutf8.err
```
You can also check that the translation works, using the file
name of the master file, with the extension changed to
"_.trans.po_". The actual language does not matter, the
extension is always the same. Similarly to the above files,
you also need to add the expected translated output and
the expected messages from stderr:
```
t-25-yaml/yamlutf8.trans.po
t-25-yaml/yamlutf8.trans.out
t-25-yaml/yamlutf8.trans.err
```
If you need to have more control over your tests, you can
use the "_run_" and "_test_" keys in the hash. The "_run_"
key defines the commands to run; the "_test_" key has
the commands to check the generated output.
Last, not least, you can mark a test as TODO with the
hash key "_todo_". Usually, it's best to write a short
description or to add a link to the online bug report.
Example:
```
push @tests,
{
'doc' => 'WML normalisation test',
'normalize' => "-f wml t-22-wml/general.wml",
'todo' => "https://github.com/mquinson/po4a/issues/138",
};
```
# Submitting Your Patch
Before all, please run ``tidyall -git`` to ensure that your changes
stick to the project quality standards. You should also consider using
a [git pre-commit hook](https://metacpan.org/pod/Code::TidyAll::Git::Precommit)
to that extend.
When submitting a patch, please either fill a Pull Request on
[mquinson/po4a](https://github.com/mquinson/po4a) on GitHub or a Merge
Request on [mquinson/po4a](https://salsa.debian.org/mquinson/po4a)
salsa instance of GitLab. If you go for the salsa server, please do
not fill your MR against the debian/po4a repository that is dedicated
to the packaging of the software (unless, of course, your change is
against the packaging). Your request should be based on the latest
code in the master branch. Please rebase your work as needed.
Finally, all PRs should include an update the the NEWS file. Please follow
the format and briefly describe the change and provide a reference to
the PR or issue. Please place your update at the bottom of the list
in the appropriate section for the next, as yet unreleased, version.
Please add sections as needed for various formats.
# Translating
You can translate the runtime messages, the documentation and the
website. Please prefer the weblate interface at
https://hosted.weblate.org/projects/po4a/ even if we also accept pull
requests for that.
On need, you can manually refresh the translation files as follows:
```sh
perl Build.PL
./Build postats # Refresh the pot and po files (both doc and bin)
```
The documentation is written using the PerlDoc format (pod), as
described here: http://perldoc.perl.org/perlpod.html
# Reminder for the po4a maintainers
This is mostly a note to ourselves. But who knows? Maybe you are (or
soon will be) one of us? You're welcome here.
## Interacting with weblate
It's easy to get a conflict when changing the po files while the
translators are working. To avoid this, you should use the [weblate
client](https://docs.weblate.org/en/latest/wlc.html#wlc). Add the
following content to `~/.config/weblate`:
```
[keys]
https://hosted.weblate.org/api/ = APIKEY (find it on https://hosted.weblate.org/accounts/profile/#api)
```
Then, when you have to change the po files, lock weblate, flush
weblate, integrate the changes locally, push your changes to the git,
pull them on weblate, and unlock it. You need to be a maintainer of
the project on weblate for that.
```sh
git pull salsa master ; git push # Get the German and Italian translations
wlc lock
wlc commit
wlc push
# Merge the pull request on github
git pull
# Do and commit your local changes
perl Build.PL
./Build
git commit -m "update POT files" po/*/*.pot # don't commit PO files to reduce conflicts; weblate update them
git push
git push salsa
wlc pull
wlc unlock
```
Here is how to integrate a PR that fixes typos in english without
fuzzying the translations (using msguntypot):
```sh
wlc lock && wlc commit && wlc push
# Merge the weblate PR on github
git pull
# Merge the other PR on github
git pull
rm -rf po_orig ; cp -r po po_orig # Copy existing po files
# Fix the typo in the doc
./Build postats # Refresh the pot and po files (both doc and bin)
cp po_orig/bin/*.po po/bin # Restore po files; msguntypot will handle typos in msgids
cp po_orig/pod/*.po po/pod
msguntypot -o po_orig/bin/po4a.pot -n po/bin/po4a.pot po/bin/*.po
msguntypot -o po_orig/pod/po4a-pod.pot -n po/pod/po4a-pod.pot po/pod/*.po
rm -rf po_orig
git commit -m "unfuzzy translations after the typo fixes in english" po
git push
wlc pull && wlc unlock
```
## Releasing po4a
Here is the checklist of things to remember when releasing po4a:
- Integrate all pending translations:
- `wlc commit && wlc push`
- merge the pull request
- `git pull && git pull salsa master`
- Bump the version number in lib/Locale/Po4a/TransTractor.pm and
regenerate the building script: `perl Build.PL`
- Check that `./Build test` reports no error.
- Generate translation statistics: `./Build postats`
- Check NEWS
- It documents all recent changes found in git logs.
- It contains a release name and a release date.
- It contains the translation statistics. Paste here the output from the command above.
- Build the archive: `./Build dist`
- Interrupt it if the MANIFEST is out of sync, and then fix it by
adding the missing files to MANIFEST (or MANIFEST.SKIP if they
should not be released to the users)
- Commit your changes, eg with commit log like "Releasing v0.XXX"
- Tag the git and push it: `git tag v0.XXX && git push --tags`
- Edit the release on [GitHub](https://github.com/mquinson/po4a/releases ).
- Reuse the release name and paste the changelog of this release.
- Also upload the tarball to the github release: the file META.yml
is missing from the tarball generated automatically (see #115).
- Announce the release on the Mailing List.
- Add a News entry to the website, update VERSION, rebuild it, and re-push it
- Put a template in NEWS (using `figlet v0.XXX`)
- Change the version in lib/Locale/Po4a/TransTractor.pm to 0.XX-alpha

341
COPYING Normal file
View File

@ -0,0 +1,341 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Library General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) 19yy <name of author>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) 19yy name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Library General
Public License instead of this License.

1161
MANIFEST Normal file

File diff suppressed because it is too large Load Diff

134
META.json Normal file
View File

@ -0,0 +1,134 @@
{
"abstract" : "Maintain the translations of your documentation with ease (PO for anything)",
"author" : [
"Martin Quinson (mquinson#debian.org)",
"Denis Barbier <barbier@linuxfr.org>",
"Nicolas Francois <nicolas.francois@centraliens.net>",
"Neil Williams <linux@codehelp.co.uk>"
],
"dynamic_config" : 1,
"generated_by" : "Module::Build version 0.4231",
"license" : [
"gpl_1"
],
"meta-spec" : {
"url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec",
"version" : 2
},
"name" : "po4a",
"prereqs" : {
"configure" : {
"requires" : {
"Module::Build" : "0.42"
}
},
"runtime" : {
"recommends" : {
"Locale::gettext" : "1.01",
"SGMLS" : "0",
"Term::ReadKey" : "0",
"Text::WrapI18N" : "0",
"Unicode::GCString" : "0",
"YAML::Tiny" : "0"
},
"requires" : {
"Pod::Parser" : "0"
}
},
"test" : {
"requires" : {
"SGMLS" : "0",
"Unicode::GCString" : "0"
}
}
},
"provides" : {
"Locale::Po4a::AsciiDoc" : {
"file" : "lib/Locale/Po4a/AsciiDoc.pm"
},
"Locale::Po4a::BibTeX" : {
"file" : "lib/Locale/Po4a/BibTeX.pm"
},
"Locale::Po4a::Chooser" : {
"file" : "lib/Locale/Po4a/Chooser.pm"
},
"Locale::Po4a::Common" : {
"file" : "lib/Locale/Po4a/Common.pm"
},
"Locale::Po4a::Debconf" : {
"file" : "lib/Locale/Po4a/InProgress/Debconf.pm"
},
"Locale::Po4a::Dia" : {
"file" : "lib/Locale/Po4a/Dia.pm"
},
"Locale::Po4a::Docbook" : {
"file" : "lib/Locale/Po4a/Docbook.pm"
},
"Locale::Po4a::Guide" : {
"file" : "lib/Locale/Po4a/Guide.pm"
},
"Locale::Po4a::Halibut" : {
"file" : "lib/Locale/Po4a/Halibut.pm"
},
"Locale::Po4a::Ini" : {
"file" : "lib/Locale/Po4a/Ini.pm"
},
"Locale::Po4a::KernelHelp" : {
"file" : "lib/Locale/Po4a/KernelHelp.pm"
},
"Locale::Po4a::LaTeX" : {
"file" : "lib/Locale/Po4a/LaTeX.pm"
},
"Locale::Po4a::Man" : {
"file" : "lib/Locale/Po4a/Man.pm"
},
"Locale::Po4a::NewsDebian" : {
"file" : "lib/Locale/Po4a/InProgress/NewsDebian.pm"
},
"Locale::Po4a::Po" : {
"file" : "lib/Locale/Po4a/Po.pm"
},
"Locale::Po4a::Pod" : {
"file" : "lib/Locale/Po4a/Pod.pm"
},
"Locale::Po4a::RubyDoc" : {
"file" : "lib/Locale/Po4a/RubyDoc.pm"
},
"Locale::Po4a::Sgml" : {
"file" : "lib/Locale/Po4a/Sgml.pm"
},
"Locale::Po4a::TeX" : {
"file" : "lib/Locale/Po4a/TeX.pm"
},
"Locale::Po4a::Texinfo" : {
"file" : "lib/Locale/Po4a/Texinfo.pm"
},
"Locale::Po4a::Text" : {
"file" : "lib/Locale/Po4a/Text.pm"
},
"Locale::Po4a::TransTractor" : {
"file" : "lib/Locale/Po4a/TransTractor.pm",
"version" : "0.66"
},
"Locale::Po4a::Wml" : {
"file" : "lib/Locale/Po4a/Wml.pm"
},
"Locale::Po4a::Xhtml" : {
"file" : "lib/Locale/Po4a/Xhtml.pm"
},
"Locale::Po4a::Xml" : {
"file" : "lib/Locale/Po4a/Xml.pm"
},
"Locale::Po4a::Yaml" : {
"file" : "lib/Locale/Po4a/Yaml.pm"
}
},
"release_status" : "stable",
"resources" : {
"license" : [
"http://www.gnu.org/licenses/old-licenses/gpl-1.0.txt"
]
},
"version" : "0.66",
"x_serialization_backend" : "JSON::PP version 4.04"
}

86
META.yml Normal file
View File

@ -0,0 +1,86 @@
---
abstract: 'Maintain the translations of your documentation with ease (PO for anything)'
author:
- 'Martin Quinson (mquinson#debian.org)'
- 'Denis Barbier <barbier@linuxfr.org>'
- 'Nicolas Francois <nicolas.francois@centraliens.net>'
- 'Neil Williams <linux@codehelp.co.uk>'
build_requires:
SGMLS: '0'
Unicode::GCString: '0'
configure_requires:
Module::Build: '0.42'
dynamic_config: 1
generated_by: 'Module::Build version 0.4231, CPAN::Meta::Converter version 2.150010'
license: gpl
meta-spec:
url: http://module-build.sourceforge.net/META-spec-v1.4.html
version: '1.4'
name: po4a
provides:
Locale::Po4a::AsciiDoc:
file: lib/Locale/Po4a/AsciiDoc.pm
Locale::Po4a::BibTeX:
file: lib/Locale/Po4a/BibTeX.pm
Locale::Po4a::Chooser:
file: lib/Locale/Po4a/Chooser.pm
Locale::Po4a::Common:
file: lib/Locale/Po4a/Common.pm
Locale::Po4a::Debconf:
file: lib/Locale/Po4a/InProgress/Debconf.pm
Locale::Po4a::Dia:
file: lib/Locale/Po4a/Dia.pm
Locale::Po4a::Docbook:
file: lib/Locale/Po4a/Docbook.pm
Locale::Po4a::Guide:
file: lib/Locale/Po4a/Guide.pm
Locale::Po4a::Halibut:
file: lib/Locale/Po4a/Halibut.pm
Locale::Po4a::Ini:
file: lib/Locale/Po4a/Ini.pm
Locale::Po4a::KernelHelp:
file: lib/Locale/Po4a/KernelHelp.pm
Locale::Po4a::LaTeX:
file: lib/Locale/Po4a/LaTeX.pm
Locale::Po4a::Man:
file: lib/Locale/Po4a/Man.pm
Locale::Po4a::NewsDebian:
file: lib/Locale/Po4a/InProgress/NewsDebian.pm
Locale::Po4a::Po:
file: lib/Locale/Po4a/Po.pm
Locale::Po4a::Pod:
file: lib/Locale/Po4a/Pod.pm
Locale::Po4a::RubyDoc:
file: lib/Locale/Po4a/RubyDoc.pm
Locale::Po4a::Sgml:
file: lib/Locale/Po4a/Sgml.pm
Locale::Po4a::TeX:
file: lib/Locale/Po4a/TeX.pm
Locale::Po4a::Texinfo:
file: lib/Locale/Po4a/Texinfo.pm
Locale::Po4a::Text:
file: lib/Locale/Po4a/Text.pm
Locale::Po4a::TransTractor:
file: lib/Locale/Po4a/TransTractor.pm
version: '0.66'
Locale::Po4a::Wml:
file: lib/Locale/Po4a/Wml.pm
Locale::Po4a::Xhtml:
file: lib/Locale/Po4a/Xhtml.pm
Locale::Po4a::Xml:
file: lib/Locale/Po4a/Xml.pm
Locale::Po4a::Yaml:
file: lib/Locale/Po4a/Yaml.pm
recommends:
Locale::gettext: '1.01'
SGMLS: '0'
Term::ReadKey: '0'
Text::WrapI18N: '0'
Unicode::GCString: '0'
YAML::Tiny: '0'
requires:
Pod::Parser: '0'
resources:
license: http://www.gnu.org/licenses/old-licenses/gpl-1.0.txt
version: '0.66'
x_serialization_backend: 'CPAN::Meta::YAML version 0.018'

1760
NEWS Normal file

File diff suppressed because it is too large Load Diff

333
Po4aBuilder.pm Executable file
View File

@ -0,0 +1,333 @@
package Po4aBuilder;
use Module::Build;
use File::Basename;
use File::Path qw(mkpath rmtree);
use File::Spec;
use File::Copy qw(copy);
use File::stat;
@ISA = qw(Module::Build);
sub ACTION_build {
my $self = shift;
$self->depends_on('code');
$self->depends_on('docs');
$self->depends_on('distmeta'); # regenerate META.yml
$self->depends_on('man');
$self->depends_on('postats');
}
sub make_files_writable {
my $self = shift;
my $dir = shift;
my $files = $self->rscan_dir($dir, sub {-f});
foreach my $file (@$files) {
my $current_mode = stat($file)->mode;
chmod $current_mode | oct(200), $file;
}
}
sub perl_scripts {
return ('po4a-gettextize', 'po4a-updatepo', 'po4a-translate',
'po4a-normalize', 'po4a', 'msguntypot');
}
# Update po/bin/*.po files
sub ACTION_binpo {
my $self = shift;
my ($cmd, $sources);
$self->depends_on('code');
$self->make_files_writable("po/bin");
my @all_files = sort((perl_scripts(), @{$self->rscan_dir('lib',qr{\.pm$})}));
unless ($self->up_to_date(\@all_files, "po/bin/po4a.pot")) {
print "XX Update po/bin/po4a.pot\n";
chdir "po/bin";
$sources = join ("", map {" ../../".$_ } @all_files);
$cmd = "xgettext ";
$cmd .= "--from-code=utf-8 ";
$cmd .= "-L Perl ";
$cmd .= "--add-comments ";
$cmd .= "--msgid-bugs-address po4a\@packages.debian.org ";
$cmd .= "--package-name po4a ";
$cmd .= "--package-version ".$self->dist_version()." ";
$cmd .= "$sources ";
$cmd .= "-o po4a.pot.new";
system($cmd) && die;
chdir "../..";
if ( -e "po/bin/po4a.pot") {
$diff = qx(diff -q -I'#:' -I'POT-Creation-Date:' -I'PO-Revision-Date:' po/bin/po4a.pot po/bin/po4a.pot.new);
if ( $diff eq "" ) {
unlink "po/bin/po4a.pot.new" || die;
# touch it
my ($atime, $mtime) = (time,time);
utime $atime, $mtime, "po/bin/po4a.pot";
} else {
rename "po/bin/po4a.pot.new", "po/bin/po4a.pot" || die;
}
} else {
rename "po/bin/po4a.pot.new", "po/bin/po4a.pot" || die;
}
} else {
print "XX po/bin/po4a.pot uptodate.\n";
}
foreach (@{$self->rscan_dir('po/bin',qr{\.po$})}) {
my $lang = fileparse($_, qw{.po});
# DO NOT update languages. They are updated by the weblate robot directly
if (0) {
unless ($self->up_to_date("po/bin/po4a.pot", $_)) {
print "XX Sync $_: ";
system("msgmerge --previous $_ po/bin/po4a.pot -o $_.new") && die;
# Typically all that changes was a date. I'd
# prefer not to commit such changes, so detect
# and ignore them.
$diff = qx(diff -q -I'#:' -I'POT-Creation-Date:' -I'PO-Revision-Date:' $_ $_.new);
if ($diff eq "") {
unlink "$_.new" || die;
# touch it
my ($atime, $mtime) = (time,time);
utime $atime, $mtime, $_;
} else {
rename "$_.new", $_ || die;
}
} else {
print "XX $_ uptodate.\n";
}
}
unless ($self->up_to_date($_,"blib/po/$lang/LC_MESSAGES/po4a.mo")) {
mkpath( File::Spec->catdir( 'blib', 'po', $lang, "LC_MESSAGES" ), 0, oct(755) );
system("msgfmt -o blib/po/$lang/LC_MESSAGES/po4a.mo $_") && die;
}
}
}
sub ACTION_install {
my $self = shift;
require ExtUtils::Install;
# print ("KEYS\n");
# foreach my $k ($self->install_types()) {
# print ("$k -> ".$self->install_destination($k)."\n");
# }
my $mandir = $self->install_destination('libdoc');
$mandir =~ s,/man3$,,;
$self->install_path(man => $mandir);
my $localedir = $self->install_destination('libdoc');
$localedir =~ s,/man/man3$,/locale,;
$self->install_path(po => $localedir);
ExtUtils::Install::install($self->install_map, !$self->quiet, 0, $self->{args}{uninst}||0);
}
sub ACTION_dist {
my ($self) = @_;
$self->depends_on('distcheck');
$self->depends_on('test');
$self->depends_on('binpo');
$self->depends_on('docpo');
$self->depends_on('distdir');
my $dist_dir = $self->dist_dir;
$self->make_files_writable($dist_dir);
if ( -e "$dist_dir.tar.gz") {
# Delete the distfile if it already exists
unlink "$dist_dir.tar.gz" || die;
}
$self->make_tarball($dist_dir);
$self->delete_filetree($dist_dir);
}
sub ACTION_docpo {
my $self = shift;
$self->depends_on('code');
$self->make_files_writable("po/pod");
my $cmd = "perl -Ilib po4a "; # Use this version of po4a
$cmd .= "--previous ";
$cmd .= "--no-translations ";
$cmd .= "--msgid-bugs-address devel\@lists.po4a.org ";
$cmd .= "--package-name po4a ";
$cmd .= "--package-version ".$self->dist_version()." ";
$cmd .= $ENV{PO4AFLAGS}." " if defined($ENV{PO4AFLAGS});
$cmd .= "po/pod.cfg";
system($cmd)
and die;
}
sub ACTION_man {
my $self = shift;
$self->depends_on('docpo');
use Pod::Man;
use Encode;
# Translate binaries manpages
my %options;
$options{utf8} = 1;
my $parser = Pod::Man->new (%options);
my $manpath = File::Spec->catdir( 'blib', 'man' );
File::Path::rmtree( $manpath, 0, 1);
my $cmd = "perl -Ilib po4a "; # Use this version of po4a
$cmd .= $ENV{PO4AFLAGS}." " if defined($ENV{PO4AFLAGS});
$cmd .= "--previous po/pod.cfg";
system($cmd) and die;
my $man1path = File::Spec->catdir( $manpath, 'man1' );
my $man3path = File::Spec->catdir( $manpath, 'man3' );
my $man5path = File::Spec->catdir( $manpath, 'man5' );
my $man7path = File::Spec->catdir( $manpath, 'man7' );
File::Path::mkpath( $man1path, 0, oct(755) ) or die;
File::Path::mkpath( $man3path, 0, oct(755) ) or die;
File::Path::mkpath( $man5path, 0, oct(755) ) or die;
File::Path::mkpath( $man7path, 0, oct(755) ) or die;
copy ( File::Spec->catdir("doc", "po4a.7.pod"), $man7path) or die;
foreach $file (perl_scripts()) {
$file =~ m,([^/]*)$,;
copy($file, File::Spec->catdir($man1path, "$1.1p.pod")) or die "Cannot copy $file over";
}
foreach $file (@{$self->rscan_dir('lib',qr{\.pm$})}) {
$file =~ m,([^/]*).pm$,;
copy($file, File::Spec->catdir($man3path, "Locale::Po4a::$1.3pm.pod")) or die;
}
$self->delete_filetree( File::Spec->catdir("blib", "bindoc") );
$self->delete_filetree( File::Spec->catdir("blib", "libdoc") );
foreach $file (@{$self->rscan_dir($manpath, qr{\.pod$})}) {
next if $file =~ m/^man7/;
my $out = $file;
$out =~ s/\.pod$//;
$parser->{name} = $out;
$parser->{name} =~ s/^.*\///;
$parser->{name} =~ s/^(.*).(1p|3pm|5|7)/$1/;
$parser->{section} = $2;
if ($parser->{section} ne "3pm") {
$parser->{name} = uc $parser->{name};
}
my $lang = $out;
$lang =~ s/^blib\/man\/([^\/]*)\/.*$/$1/;
if ($lang =~ m/man\d/) {
$parser->{release} = $parser->{center} = "Po4a Tools";
} else {
my $command;
$command = "msggrep -K -E -e \"Po4a Tools\" po/pod/$lang.po |";
$command .= "msgconv -t UTF-8 | ";
$command .= "msgexec /bin/sh -c '[ -n \"\$MSGEXEC_MSGID\" ] ";
$command .= "&& cat || cat > /dev/null'";
my $title = `$command 2> /dev/null`;
$title = "Po4a Tools" unless length $title;
$title = Encode::decode_utf8($title);
$parser->{release} = $parser->{center} = $title;
}
$parser->parse_from_file ($file, $out);
system("gzip -9 -f $out") and die;
unlink "$file" || die;
}
# Install the manpages written in XML DocBook
foreach $file (qw(po4a-display-man.xml po4a-display-pod.xml)) {
copy ( File::Spec->catdir("share", "doc", $file), $man1path) or die;
}
foreach $file (@{$self->rscan_dir($manpath, qr{\.xml$})}) {
if ($file =~ m,(.*/man(.))/([^/]*)\.xml$,) {
my ($outdir, $section, $outfile) = ($1, $2, $3);
if (-e "/usr/share/xml/docbook/stylesheet/docbook-xsl/manpages/docbook.xsl") { # Location on Debian at least
print "Convert $outdir/$outfile.$section (local docbook.xsl file). ";
system("xsltproc -o $outdir/$outfile.$section --nonet /usr/share/xml/docbook/stylesheet/docbook-xsl/manpages/docbook.xsl $file") and die;
} else { # Not found locally, use the XSL file online
print "Convert $outdir/$outfile.$section (online docbook.xsl file). ";
system("xsltproc -o $outdir/$outfile.$section --nonet http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl $file") and die;
}
system ("gzip -9 -f $outdir/$outfile.$section") and die;
}
unlink "$file" || die;
}
}
sub ACTION_postats {
my $self = shift;
$self->depends_on('binpo');
$self->depends_on('docpo');
print("-------------\n");
$self->postats( File::Spec->catdir("po", "bin"));
print("-------------\n");
$self->postats( File::Spec->catdir("po", "pod"));
print("-------------\n");
$self->postats( File::Spec->catdir("..", "po4a-website", "po")) if -d File::Spec->catdir("..", "po4a-website", "po");
}
sub postats {
my ($self,$dir) = (shift,shift);
my $potfiles = $self->rscan_dir($dir,qr{\.pot$});
die "No POT file found in $dir" unless scalar $potfiles;
my $potfile = pop @$potfiles;
my $potsize = stat($potfile)->size;
print "$dir (pot: $potsize)\n";
my @files = @{$self->rscan_dir($dir,qr{\.po$})};
my (@t100,@t95,@t90,@t80,@t70,@t50,@t33,@t20,@starting);
foreach (sort @files) {
$file = $_;
my $lang = fileparse($file, qw{.po});
my $stat = `msgfmt -o /dev/null -c --statistics $file 2>&1`;
my ($trans, $fuzz, $untr) = (0,0,0);
if ($stat =~ /(\d+)\D+?(\d+)\D+?(\d+)/) {
($trans, $fuzz, $untr) = ($1,$2,$3);
} elsif ($stat =~ /(\d+)\D+?(\d+)/) {
($trans, $fuzz) = ($1,$2);
} elsif ($stat =~ /(\d+)/) {
($trans) = ($1);
} else {
print "Unparsable content\n";
}
my $total = $trans+$fuzz+$untr;
my $ratio = $trans / $total * 100;
# print "ratio: $ratio| trans: $trans; fuzz: $fuzz; untr: $untr\n";
my $Ratio = int($ratio);
print " $lang (".(int($ratio*100)/100)."%): $stat";
if ($ratio == 100) {
push @t100, $lang;
} elsif ($ratio >= 95) {
push @t95, "$lang ($Ratio%)";
} elsif ($ratio >= 90) {
push @t90, "$lang ($Ratio%)";
} elsif ($ratio >= 80) {
push @t80, "$lang ($Ratio%)";
} elsif ($ratio >= 70) {
push @t70, "$lang ($Ratio%)";
} elsif ($ratio >= 50) {
push @t50, "$lang ($Ratio%)";
} elsif ($ratio >= 33) {
push @t33, "$lang ($Ratio%)";
} elsif ($ratio >= 20) {
push @t20, "$lang ($Ratio%)";
} else {
push @starting, "$lang ($Ratio%)";
}
}
print "$dir (pot: $potsize)\n";
print " ".(scalar (@t100))." language".(scalar (@t100)==1?' ':'s')." = 100%: ". (join(", ", @t100)).".\n" if (scalar(@t100)>0);
print " ".(scalar (@t95))." language".(scalar (@t95)==1?' ':'s')." >= 95%: ". (join(", ", @t95)).".\n" if (scalar(@t95)>0);
print " ".(scalar (@t90))." language".(scalar (@t90)==1?' ':'s')." >= 90%: ". (join(", ", @t90)).".\n" if (scalar(@t90)>0);
print " ".(scalar (@t80))." language".(scalar (@t80)==1?' ':'s')." >= 80%: ". (join(", ", @t80)).".\n" if (scalar(@t80)>0);
print " ".(scalar (@t70))." language".(scalar (@t70)==1?' ':'s')." >= 70%: ". (join(", ", @t70)).".\n" if (scalar(@t70)>0);
print " ".(scalar (@t50))." language".(scalar (@t50)==1?' ':'s')." >= 50%: ". (join(", ", @t50)).".\n" if (scalar(@t50)>0);
print " ".(scalar (@t33))." language".(scalar (@t33)==1?' ':'s')." >= 33%: ". (join(", ", @t33)).".\n" if (scalar(@t33)>0);
print " ".(scalar (@t20))." language".(scalar (@t20)==1?' ':'s')." >= 20%: ". (join(", ", @t20)).".\n" if (scalar(@t20)>0);
print " ".(scalar (@starting))." starting language".(scalar (@starting)==1?' ':'s').": ". (join(", ", @starting)).".\n" if (scalar(@starting)>0);
}
1;

133
README.maintainers Normal file
View File

@ -0,0 +1,133 @@
This document provides some general rules to deploy a translation process
that will ease the work of maintainers (upstream and distribution
maintainers) and translators (or translation teams).
Translators usually fetch a POT (for a new translation) or retrieve the current
PO for their language, then they translate the untranslated strings and
update the translation of the strings marked as fuzzy.
Updating files
--------------
Translators need to know if a PO has to be updated:
* they can verify the POs in the version control system or in the
distributed archives/packages
* they can be informed by the translation teams, which automatically
check the status of the POs in various packages.
We want to avoid translators to be notified by a user reporting that
some strings are not translated even if the PO contains neither untranslated nor
fuzzy string.
Thus it is important to ensure that the POTs are up-to-date with the
original documents and that the POs contain the same strings as the
POTs.
1. Upstream maintainers should update the POTs according to the original
documents and update the POs according to these up-to-date POTs when they
distribute an archive.
2. If the switch to po4a was done in a distribution, the source package
should also contain up-to-date translation materials.
3. If the documentation is patched by the distribution, the maintainer
must not forget to update the POTs and POs.
It is important to ensure that the translation materials are updated
automatically.
Architecture
------------
A standardized architecture of the source tree will help the translation
teams when they try to detect the POTs that need to be updated.
Thus we recommend the following architecture:
/
/doc/
/doc/en/
/doc/en/
/doc/po4a/
/doc/po4a/add_<ll>/
/doc/po4a/po4a.cfg
/doc/po4a/po/
/doc/po4a/po/<pkg>.pot
/doc/po4a/po/<ll>.po
/doc/translated/<ll>/
Or, if you want to avoid a big POT and split it according to the packages,
documents, formats, or subjects, you can use the following architecture:
/
/doc/
/doc/en/
/doc/en/
/doc/po4a/
/doc/po4a/add_<ll>/
/doc/po4a/<pkg1>/po4a.cfg
/doc/po4a/<pkg1>/po/
/doc/po4a/<pkg1>/po/<pkg1>.pot
/doc/po4a/<pkg1>/po/<ll>.po
/doc/translated/<ll>/
It is important to avoid a build failure if a generated
translation cannot be generated (the PO is too outdated, an addendum
cannot be applied, etc.). You should therefore use wildcards or test if the file
was generated in the 'install' or 'dist' rules
Examples
========
Using po4a upstream
-------------------
When po4a is used upstream, we recommend to run po4a in the 'dist' rule.
This will update the POT and POs, and will generate the translated documents.
These translated documents can be distributed in the source archive if the
maintainer don't want to add a build dependency on po4a. You should then
add an autoconf check on po4a. It will allow you to update the documentation
if po4a is available on your system. If po4a is not available, documents
will be distributed without being synced with the original version, but the
build process won't fail.
It is important to distribute the POT and POs in the source archive.
A typical dist rule could then be:
dist:
po4a <package>.cfg
...
If automake is used, the following could also be used.
dist-hook:
po4a <package>.cfg
...
Using po4a in a distribution
----------------------------
(Debian packaging is taken as an example, you will have to adapt this to
your distribution)
To ensure that the source of a Debian package contains only up-to-date POT
and POs, you should run po4a in the 'clean' rule
of debian/rules. The translated documents can be generated in the 'build'
(or 'build-indep') rule:
clean:
# Update the POT and POs
cd <...>/po4a && po4a --no-translations <package>.cfg
# Delete translated documentation
rm -rf <...>/translated
build:
# Generate the translations
cd <...>/po4a && po4a <package>.cfg
However, you should try to avoid distribution-specific build systems, to
ensure the portability of your software.

137
README.md Normal file
View File

@ -0,0 +1,137 @@
# Introduction to Po4a
[![Build Status](https://img.shields.io/github/workflow/status/mquinson/po4a/Build%20on%20Linux%20CI?style=flat-square)](https://github.com/mquinson/po4a/actions/workflows/linux.yml)
[![First Timers Friendly](https://img.shields.io/badge/Beginners-Welcome-brightgreen?style=flat-square)](https://www.firsttimersonly.com)
The goal of po4a (PO for anything) project is to ease translations (and
more interestingly, the maintenance of translations) using gettext
tools on areas where they were not expected like documentation.
In po4a each documentation format is handled by a module. Presently, we have a
module for the following formats:
- asciidoc: AsciiDoc format.
- dia: uncompressed Dia diagrams.
- docbook: DocBook XML.
- guide: Gentoo Linux's XML documentation format.
- halibut: Simon Tatham's documentation production system.
- ini: INI format.
- kernelhelp: Help messages of each kernel compilation option.
- latex: LaTeX format.
- bibtex: bibtex format.
- man: Good old manual page format.
- pod: Perl Online Documentation format.
- sgml: either DebianDoc or DocBook DTD.
- texinfo: The info page format.
- tex: generic TeX documents (see also latex).
- text: simple text document.
- wml: WML documents.
- xhtml: XHTML documents.
- xml: generic XML documents (see also docbook).
- yaml: YAML documents.
- rubydoc: RubyDoc (RD) documents.
# Installation
To install this module type the following:
```bash
perl Build.PL
./Build
./Build install
```
# Contributing
po4a is particularly welcoming contributions from the community. If
you are new to Open Source, we'd love to mentor you for your first
contributions. Please see the
[CONTRIBUTING](https://github.com/mquinson/po4a/blob/master/CONTRIBUTING.md)
file to see how you could help.
# Use without installation
If you want to use a version without installing it (e.g. directly from
the git tree), use the PERLLIB environment variable as such:
```bash
PERLLIB=~/git-checkouts/po4a/lib ~/git-checkouts/po4a/po4a-gettextize [usual args]
```
# Po4a dependencies
* Locale::gettext (v1.01)
This module being itself internationalized, it needs the Locale::gettext
library to translate its own messages.
If it is not present, then po4a's messages won't be translated, but
po4a will remain fully functional.
* Text::WrapI18N
This module is used to format po4a's warnings and error messages. It
permits to wrap long error messages without splitting words.
If it is not present, the formatting of messages will be different,
but po4a will remain fully functional.
* Term::ReadKey
This module is used to retrieve the terminal's line width. It is not
used if Text::WrapI18N is not available.
If it is not present, the line width can be specified with the COLUMN
environment variable.
## SGML module specific dependencies
* SGMLS (1.03ii)
This is a set of Perl5 routines for processing the output from the onsgmls
SGML parser.
* opensp (1.5.2) OpenJade group's SGML parsing tools
This is the SGML parser we use.
* docbook: used in the tests. Without this package, the test fails with:
```
onsgmls:<OSFD>0:1:59:W: cannot generate system identifier for public text "-//OASIS//DTD DocBook V4.1//EN"
onsgmls:<OSFD>0:6:0:E: reference to entity "REFENTRY" for which no system identifier could be generated
onsgmls:<OSFD>0:1:0: entity was defined here
onsgmls:<OSFD>0:6:0:E: DTD did not contain element declaration for document type name
po4a::sgml: Error while running onsgmls -p. Please check if onsgmls and the DTD are installed.
```
You don't need it if you don't want to run the tests.
## Text module specific dependencies
* Unicode::GCString
This module is used to compute text width; it is needed by AsciiDoc to
determine two line titles in encodings different from ASCII.
https://github.com/hatukanezumi/Unicode-LineBreak
## YAML module specific dependencies
* YAML::Tiny
This module is used to parse and serialize the YAML file.
# Project hosting
- Webpage: https://po4a.org
- Source code: https://github.com/mquinson/po4a
- Bug tracker: https://github.com/mquinson/po4a/issues
- Source of the web pages: https://github.com/mquinson/po4a-website
# Copyright and license
This program is free software; you may redistribute it and/or modify it
under the terms of GPL (see COPYING file).
Copyright © 2002-2021 by SPI, inc.
Authors:
- Denis Barbier <barbier@linuxfr.org>
- Martin Quinson (mquinson#debian.org)

59
TODO Normal file
View File

@ -0,0 +1,59 @@
********
* TODO *
********
* Update the PO files in the build/dist process
Check xml2rfc
Better error handling (errno, errstr)
> >What we really need is a function embeeding the call to warn and sprintf,
> >and reputting the module in front of it and the "\n" afterward (but not the
> >dgettext since it has to be there to be able to extract the strings). It
> >would help making the code more readable. We could also implement a sort of
> >wrapping function setting the module name in front of every line. There is
> >a module for that in Perl.
>
> I thought about it some time ago. It would be nice :)
** TransTractor.pm
- process(): $self->addendum($file) || die "An addendum failed\n";
- poheader()
>> +$parser->{TT}{utf_mode} = 1;
>> +$parser->{TT}{file_in_charset} = $mastchar;
> What about a function in transtractor allowing to set the mastchar ? I'd
> prefer to leave the modification of the {TT} internals to the library code,
> not the binaries one.
** po-debconf:
- change the pot header as documented in po4a(7)
** Po.pm
- close filehandle in write (and same in TransTractor.pm).
- verbose => type in automatic comments
** Sgml.pm
- move the tag lists to an external file
** Man.pm:
- handle .ce (11 pages need it)
>>>
Text lines can be centered by using the ce' request. The line
after `ce' is centered (horizontally) on the page. To center more than
one line, use `.ce N' (where N is the number of lines to center),
followed by the N lines. To center many lines without counting them,
type:
.ce 1000
lines to center
.ce 0
The .ce 0' request tells groff' to center zero more lines, in other
words, stop centering.
<<<
The problem is that the number of lines may change during the
translation...
- same story about .ul N (underline N lines)

4251
changelog Normal file

File diff suppressed because it is too large Load Diff

7
doc/addendum.ca Normal file
View File

@ -0,0 +1,7 @@
PO4A-HEADER:mode=after;position=^=head1 AUTORS;beginboundary=^=head1
=head1 TRADUCCIÓ
Carme Cirera <menxu@hotmail.com>
Jordi Vilalta <jvprat@gmail.com>

7
doc/addendum.es Normal file
View File

@ -0,0 +1,7 @@
PO4A-HEADER:mode=after;position=^=head1 AUTORES;beginboundary=^=head1
=head1 TRADUCCION
Jordi Vilalta <jvprat@gmail.com>
Omar Campagne <ocampagne@gmail.com>

6
doc/addendum.fr Normal file
View File

@ -0,0 +1,6 @@
PO4A-HEADER:mode=after;position=^=head1 AUTEURS;beginboundary=^=head1
=head1 TRADUCTION
Martin Quinson (mquinson#debian.org)

7
doc/addendum.it Normal file
View File

@ -0,0 +1,7 @@
PO4A-HEADER:mode=after;position=^=head1 AUTORI;beginboundary=^=head1
=head1 TRADUZIONE
Danilo Piazzalunga <danilopiazza@libero.it>
Marco Ciampa <ciampix@posteo.net>

7
doc/addendum.ja Normal file
View File

@ -0,0 +1,7 @@
PO4A-HEADER:mode=after;position=^=head1 著者;beginboundary=^=head1
=head1 訳者
倉澤 望 <nabetaro@debian.or.jp>
Debian JP Documentation ML <debian-doc@debian.or.jp>

6
doc/addendum.pl Normal file
View File

@ -0,0 +1,6 @@
PO4A-HEADER:mode=after;position=^=head1 AUTORZY;beginboundary=^=head1
=head1 TŁUMACZENIE
Robert Luberda <robert@debian.org>

6
doc/addendum.zh_CN Normal file
View File

@ -0,0 +1,6 @@
PO4A-HEADER:mode=after;position=^=head1 作者;beginboundary=^=head1
=head1 翻译
taotieren <admin@taotieren.com>

14
doc/addendum_man.de Normal file
View File

@ -0,0 +1,14 @@
PO4A-HEADER:mode=after;position=^\.TH;beginboundary=^\.SH "AUTOR"
.SH ÜBERSETZUNG
Diese Übersetzung wurde 2011 von Helge Kreutzmann erstellt. Sie unterliegt
der GNU GPL Version 2 (oder neuer).
Um die englische Originalversion zu lesen, geben Sie »man -L C BEFEHL« ein.
Fehler in der Übersetzung melden Sie bitte über die Fehlerdatenbank (BTS)
von Debian oder indem Sie eine E-Mail an
.nh
<\fIdebian\-l10\-german@lists.debian.org\fR>,
.hy
schreiben.

6
doc/addendum_man.es Normal file
View File

@ -0,0 +1,6 @@
PO4A-HEADER:mode=after;position=^\.SH AUTOR;beginboundary=^\.SH
.SH TRADUCCION
Omar Campagne <ocampagne@gmail.com>

6
doc/addendum_man.fr Normal file
View File

@ -0,0 +1,6 @@
PO4A-HEADER:mode=after;position=^\.SH AUTEUR;beginboundary=^\.SH
.SH TRADUCTION
Nicolas François <nicolas.francois@centraliens.net>

7
doc/addendum_man.ja Normal file
View File

@ -0,0 +1,7 @@
PO4A-HEADER:mode=after;position=^\.SH 著者;beginboundary=^\.SH
.SH 訳者
倉澤 望 <nabetaro@debian.or.jp>
Debian JP Documentation ML <debian-doc@debian.or.jp>

6
doc/addendum_man.pl Normal file
View File

@ -0,0 +1,6 @@
PO4A-HEADER:mode=after;position=^\.SH AUTOR;beginboundary=^\.SH
.SH TŁUMACZENIE
Robert Luberda <robert@debian.org>

6
doc/addendum_man.zh_CN Normal file
View File

@ -0,0 +1,6 @@
PO4A-HEADER:mode=after;position=^\.SH 作者;beginboundary=^\.SH
.SH 翻译
taotieren <admin@taotieren.com>

822
doc/po4a.7.pod Normal file
View File

@ -0,0 +1,822 @@
=encoding UTF-8
=head1 NAME
po4a - framework to translate documentation and other materials
=head1 Introduction
po4a (PO for anything) eases the maintenance of documentation translation using
the classical gettext tools. The main feature of po4a is that it decouples the
translation of content from its document structure.
This document serves as an introduction to the po4a project with a focus on
potential users considering whether to use this tool and on the curious wanting
to understand why things are the way they are.
=head1 Why po4a?
The philosophy of Free Software is to make the technology truly available to
everyone. But licensing is not the only consideration: untranslated free
software is useless for non-English speakers. Therefore, we still have some
work to do to make software available to everybody.
This situation is well understood by most projects and everybody is now
convinced of the necessity to translate everything. Yet, the actual
translations represent a huge effort of many individuals, crippled by small
technical difficulties.
Thankfully, Open Source software is actually very well translated using the
gettext tool suite. These tools are used to extract the strings to translate
from a program and present the strings to translate in a standardized format
(called PO files, or translation catalogs). A whole ecosystem of tools has
emerged to help the translators actually translate these PO files. The result
is then used by gettext at run time to display translated messages to the end
users.
Regarding documentation, however, the situation still somewhat disappointing.
At first translating documentation may seem to be easier than translating a
program as it would seem that you just have to copy the documentation source
file and start translating the content. However, when the original
documentation is modified, keeping track of the modifications quickly turns
into a nightmare for the translators. If done manually, this task is unpleasant
and error prone.
Outdated translations are often worse than no translation at all. End-users can
be tricked by documentation describing an old behavior of the program.
Furthermore, they cannot interact directly with the maintainers since they
don't speak English. Additionally, the maintainer cannot fix the problem as
they don't know every language in which their documentation is translated.
These difficulties, often caused by poor tooling, can undermine the motivation
of volunteer translators, further aggravating the problem.
B<The goal of the po4a project is to ease the work of documentation translators>.
In particular, it makes documentation translations I<maintainable>.
The idea is to reuse and adapt the gettext approach to this field. As with
gettext, texts are extracted from their original locations and presented to
translators as PO translation catalogs. The translators can leverage the
classical gettext tools to monitor the work to do, collaborate and organize as
teams. po4a then injects the translations directly into the documentation
structure to produce translated source files that can be processed and
distributed just like the English files. Any paragraph that is not translated
is left in English in the resulting document, ensuring that the end users never
see an outdated translation in the documentation.
This automates most of the grunt work of the translation maintenance.
Discovering the paragraphs needing an update becomes very easy, and the process
is completely automated when elements are reordered without further
modification. Specific verification can also be used to reduce the chance of
formatting errors that would result in a broken document.
Please also see the B<FAQ> below in this document for a more complete list
of the advantages and disadvantages of this approach.
=head2 Supported formats
Currently, this approach has been successfully implemented to several kinds
of text formatting formats:
=over
=cut
# TRANSLATOR: 'man' refers to manpages and should probably not be translated
=item man (mature parser)
The good old manual pages' format, used by so many programs out there. po4a
support is very welcome here since this format is somewhat difficult to use and
not really friendly to newbies.
The L<Locale::Po4a::Man(3pm)|Man> module also supports the mdoc format, used by
the BSD man pages (they are also quite common on Linux).
=item AsciiDoc (mature parser)
This format is a lightweight markup format intended to ease the authoring of
documentation. It is for example used to document the git system. Those
manpages are translated using po4a.
See L<Locale::Po4a::AsciiDoc> for details.
=item pod (mature parser)
This is the Perl Online Documentation format. The language and extensions
themselves are documented using this format in addition to most existing Perl
scripts. It makes easy to keep the documentation close to the actual code by
embedding them both in the same file. It makes programmer's life easier, but
unfortunately, not the translator's, until you use po4a.
See L<Locale::Po4a::Pod> for details.
=item sgml (mature parser)
Even if superseded by XML nowadays, this format is still used for documents
which are more than a few screens long. It can even be used for complete books.
Documents of this length can be very challenging to update. B<diff> often
reveals useless when the original text was re-indented after update.
Fortunately, po4a can help you after that process.
Currently, only DebianDoc and DocBook DTD are supported, but adding support for
a new one is really easy. It is even possible to use po4a on an unknown SGML
DTD without changing the code by providing the needed information on the
command line. See L<Locale::Po4a::Sgml(3pm)> for details.
=item TeX / LaTeX (mature parser)
The LaTeX format is a major documentation format used in the Free Software
world and for publications.
The L<Locale::Po4a::LaTeX(3pm)|LaTeX> module was tested with the Python
documentation, a book and some presentations.
=item text (mature parser)
The Text format is the base format for many formats that include long blocks of
text, including Markdown, fortunes, YAML front matter section, debian/changelog,
and debian/control.
This supports the common format used in Static Site Generators, READMEs, and
other documentation systems. See L<Locale::Po4a::Text(3pm)|Text> for details.
=item xml and XHMTL (probably mature parser)
The XML format is a base format for many documentation formats.
Currently, the DocBook DTD (see L<Locale::Po4a::Docbook(3pm)> for details) and
XHTML are supported by po4a.
=item BibTex (probably mature parser)
The BibTex format is used alongside LaTex for formatting lists of references (bibliographies).
See L<Locale::Po4a::BibTex> for details.
=item Docbook (probably mature parser)
A XML-based markup language that uses semantic tags to describe documents.
See L<Locale::Po4a:Docbook> for greater details.
=item Guide XML (probably mature parser)
A XML documentation format. This module was developed specifically to help with supporting
and maintaining translations of Gentoo Linux documentation up until at least March 2016
(Based on the Wayback Machine). Gentoo have since moved to the DevBook XML format.
See L<Locale::Po4a:Guide> for greater details.
=item Wml (probably mature parser)
The Web Markup Language, do not mixup WML with the WAP stuff used on cell phones.
This module relies on the Xhtml module, which itself relies on the XmL module.
See L<Locale::Po4a::Wml> for greater details.
=item Yaml (probably mature parser)
A strict superset of JSON. YAML is often used as systems or configuration projects.
YAML is at the core of Red Hat's Ansible.
See L<Locale::Po4a::Yaml> for greater details.
=item RubyDoc (probably mature parser)
The Ruby Document (RD) format, originally the default documentation format for Ruby
and Ruby projects before converted to RDoc in 2002. Though apparently the Japanese
version of the Ruby Reference Manual still use RD.
See L<Locale::Po4a::RubyDoc> for greater details.
=item Halibut (probably experimental parser)
A documentation production system, with elements similar to TeX, debiandoc-sgml,
TeXinfo, and others, developed by Simon Tatham, the developer of PuTTY.
See L<Locale::Po4a:Halibut> for greater details.
=item Ini (probably experimental parser)
Configuration file format popularized by MS-DOS.
See L<Locale::Po4a::Ini> for greater details.
=item texinfo (very highly experimental parser)
All of the GNU documentation is written in this format (it's even one of the
requirements to become an official GNU project). The support for
L<Locale::Po4a::Texinfo(3pm)|Texinfo> in po4a is still at the beginning.
Please report bugs and feature requests.
=item Others supported formats
Po4a can also handle some more rare or specialized formats, such as the
documentation of compilation options for the 2.4+ Linux kernels (L<Locale::Po4a::KernelHelp>) or the diagrams
produced by the dia tool (L<Locale::Po4a:Dia>). Adding a new format is often very easy and the main
task is to come up with a parser for your target format. See
L<Locale::Po4a::TransTractor(3pm)> for more information about this.
=item Unsupported formats
Unfortunately, po4a still lacks support for several documentation formats. Many
of them would be easy to support in po4a. This includes formats not just used
for documentation, such as, package descriptions (deb and rpm), package
installation scripts questions, package changelogs, and all the specialized
file formats used by programs such as game scenarios or wine resource files.
=back
=head1 Using po4a
Historically, po4a was built around four scripts, each fulfilling a specific
task. L<po4a-gettextize(1)> helps bootstrapping translations and optionally
converting existing translation projects to po4a. L<po4a-updatepo(1)> reflects
the changes to the original documentation into the corresponding po files.
L<po4a-translate(1)> builds translated source file from the original file and
the corresponding PO file. In addition, L<po4a-normalize(1)> is mostly useful to
debug the po4a parsers, as it produces an untranslated document from the
original one. It makes it easier to spot the glitches introduced by the parsing
process.
Most projects only require the features of L<po4a-updatepo(1)> and
L<po4a-translate(1)>, but these scripts proved to be cumbersome and error prone
to use. If the documentation to translate is split over several source files, it
is difficult to keep the PO files up to date and build the documentation files
correctly. As an answer, a all-in-one tool was provided: L<po4a(1)>. This tool
takes a configuration file describing the structure of the translation project:
the location of the PO files, the list of files to translate, and the options to
use, and it fully automatizes the process. When you invoke L<po4a(1)>, it both
updates the PO files and regenerate the translation files that need to. If
everything is already up to date, L<po4a(1)> does not change any file.
The rest of this section gives an overview of how use the scripts' interface of
po4a. Most users will probably prefer to use the all-in-one tool, that is
described in the documentation of L<po4a(1)>.
=head2 Graphical overview of the po4a scripts
The following schema gives an overview of how each po4a script can be used.
Here, F<master.doc> is an example name for the documentation to be translated;
F<XX.doc> is the same document translated in the language XX while F<doc.XX.po>
is the translation catalog for that document in the XX language. Documentation
authors will mostly be concerned with F<master.doc> (which can be a manpage, an
XML document, an asciidoc file or similar); the translators will be mostly
concerned with the PO file, while the end users will only see the F<XX.doc> file.
master.doc
|
V
+<-----<----+<-----<-----<--------+------->-------->-------+
: | | :
{translation} | { update of master.doc } :
: | | :
XX.doc | V V
(optional) | master.doc ->-------->------>+
: | (new) |
V V | |
[po4a-gettextize] doc.XX.po -->+ | |
| (old) | | |
| ^ V V |
| | [po4a-updatepo] |
V | | V
translation.pot ^ V |
| | doc.XX.po |
| | (fuzzy) |
{ translation } | | |
| ^ V V
| | {manual editing} |
| | | |
V | V V
doc.XX.po --->---->+<---<-- doc.XX.po addendum master.doc
(initial) (up-to-date) (optional) (up-to-date)
: | | |
: V | |
+----->----->----->------> + | |
| | |
V V V
+------>-----+------<------+
|
V
[po4a-translate]
|
V
XX.doc
(up-to-date)
This schema is complicated, but in practice only the right part (involving
L<po4a-updatepo(1)> and L<po4a-translate(1)>) is used once the project is setup
and configured.
The left part depicts how L<po4a-gettextize(1)> can be used to convert an
existing translation project to the po4a infrastructure. This script takes an
original document and its translated counterpart, and tries to build the
corresponding PO file. Such manual conversion is rather cumbersome (see the
L<po4a-gettextize(1)> documentation for more details), but it is only needed
once to convert your existing translations. If you don't have any translation to
convert, you can forget about this and focus on the right part of the schema.
On the top right part, the action of the original author is depicted, updating
the documentation. The middle right part depicts the automatic actions of
L<po4a-updatepo(1)>. The new material is extracted and compared against the
exiting translation. The previous translation is used for the parts that didn't
change, while partially modified parts are connected to the previous translation
with a "fuzzy" marker indicating that the translation must be updated. New or
heavily modified material is left untranslated.
Then, the I<manual editing> reported depicts the action of the translators, that
modify the PO files to provide translations to every original string and
paragraph. This can be done using either a specific editor such as the B<GNOME
Translation Editor>, KDE's B<Lokalize> or B<poedit>, or using an online
localization platform such as B<weblate> or B<pootle>. The translation result is
a set of PO files, one per language. Please refer to the gettext documentation
for more details.
The bottom part of the figure shows how L<po4a-translate(1)> creates a
translated source document from the F<master.doc> original document and the
F<doc.XX.po> translation catalog that was updated by the translators. The
structure of the document is reused, while the original content is replaced by
its translated counterpart. Optionally, an addendum can be used to add some
extra text to the translation. This is often used to add the name of the
translator to the final document. See below for details.
As noted before, the L<po4a(1)> program combines the effects of the separated
scripts, updating the PO files and the translated document in one invocation.
The underlying logic remains the same.
=head2 Starting a new translation
If you use L<po4a(1)>, there is no specific step to start a translation. You
just have to list the languages in the configuration file, and the missing PO
files are automatically created. Naturally, the translator then have to provide
translations for every content used in your documents. L<po4a(1)> also creates a
POT file, that is a PO template file. Potential translators can translate your
project into a new language by renaming this file and providing the translations
in their language.
If you prefer to use the individual scripts separately, you should use
L<po4a-gettextize(1)> as follows to create the POT file. This file can then be
copied into F<XX.po> to initiate a new translation.
$ po4a-gettextize --format <format> --master <master.doc> --po <translation.pot>
The master document is used in input, while the POT file is the output of this process.
=head2 Integrating changes to the original document
The script to use for that is L<po4a-updatepo(1)> (please refer to its
documentation for details):
$ po4a-updatepo --format <format> --master <new_master.doc> --po <old_doc.XX.po>
The master document is used in input, while the PO file is updated: it is used
both in input and output.
=head2 Generating a translated document
Once you're done with the translation, you want to get the translated
documentation and distribute it to users along with the original one.
For that, use the L<po4a-translate(1)> program as follows:
$ po4a-translate --format <format> --master <master.doc> --po <doc.XX.po> --localized <XX.doc>
Both the master and PO files are used in input, while the localized file is the
output of this process.
=head2 Using addenda to add extra text to translations
Adding new text to the translation is probably the only thing that is easier in
the long run when you translate files manually :). This happens when you want
to add an extra section to the translated document, not corresponding to any
content in the original document. The classical use case is to give credits to
the translation team, and to indicate how to report translation-specific
issues.
With po4a, you have to specify B<addendum> files, that can be conceptually
viewed as patches applied to the localized document after processing. Each
addendum must be provided as a separate file, which format is however very
different from the classical patches. The first line is a I<header line>,
defining the insertion point of the addendum (with an unfortunately cryptic
syntax -- see below) while the rest of the file is added verbatim at the
determined position.
The header line must begin with the string B<PO4A-HEADER:>, followed by a
semi-colon separated list of I<key>B<=>I<value> fields.
For example, the following header declares an addendum that must be placed
at the very end of the translation.
PO4A-HEADER: mode=eof
Things are more complex when you want to add your extra content in the middle
of the document. The following header declares an addendum that must be
placed after the XML section containing the string C<About this document> in
translation.
=cut
#TRANSLATORS: you can keep the French example here, or invert it: Use a header in your own language at the beginning, and then say that it shouldn't be in English, translating the second header example to English
=pod
PO4A-HEADER: position=About this document; mode=after; endboundary=</section>
In practice, when trying to apply an addendum, po4a searches for the first line
matching the C<position> argument (this can be a regexp). Do not forget that
po4a considers the B<translated> document here. This documentation is in
English, but your line should probably read as follows if you intend your
addendum to apply to the French translation of the document.
PO4A-HEADER: position=À propos de ce document; mode=after; endboundary=</section>
Once the C<position> is found in the target document, po4a searches for the next
line after the C<position> that matches the provided C<endboundary>. The
addendum is added right B<after> that line (because we provided an
I<endboundary>, i.e. a boundary ending the current section).
The exact same effect could be obtained with the following header, that is equivalent:
PO4A-HEADER: position=About this document; mode=after; beginboundary=<section>
Here, po4a searches for the first line matching C<<section>> after the line
matching C<About this document> in the translation, and add the addendum
B<before> that line since we provided a I<beginboundary>, i.e. a boundary marking
the beginning of the next section. So this header line requires to place the
addendum after the section containing C<About this document>, and instruct po4a
that a section starts with a line containing the C<<section>> tag. This is
equivalent to the previous example because what you really want is to add this
addendum either after C</section>> or before C<<section>>.
You can also set the insertion I<mode> to the value C<before>, with a similar
semantic: combining C<mode=before> with an C<endboundary> will put the addendum
just B<after> the matched boundary, that the last potential boundary line before
the C<position>. Combining C<mode=before> with an C<beginboundary> will put the
addendum just B<before> the matched boundary, that the last potential boundary
line before the C<position>.
Mode | Boundary kind | Used boundary | Insertion point compared to the boundary
========|===============|========================|=========================================
'before'| 'endboundary' | last before 'position' | Right after the selected boundary
'before'|'beginboundary'| last before 'position' | Right before the selected boundary
'after' | 'endboundary' | first after 'position' | Right after the selected boundary
'after' |'beginboundary'| first after 'position' | Right before the selected boundary
'eof' | (none) | n/a | End of file
=head3 Hint and tricks about addenda
=over
=item
Remember that these are regexp. For example, if you want to match the end of a
nroff section ending with the line C<.fi>, do not use C<.fi> as B<endboundary>,
because it will match with C<the[ fi]le>, which is obviously not what you
expect. The correct B<endboundary> in that case is: C<^\.fi$>.
=item
White spaces ARE important in the content of the C<position> and boundaries. So
the two following lines B<are different>. The second one will only be found if
there is enough trailing spaces in the translated document.
PO4A-HEADER: position=About this document; mode=after; beginboundary=<section>
PO4A-HEADER: position=About this document ; mode=after; beginboundary=<section>
=item
Although this context search may be considered to operate roughly on each line
of the B<translated> document, it actually operates on the internal data string of
the translated document. This internal data string may be a text spanning a
paragraph containing multiple lines or may be a XML tag itself alone. The
exact I<insertion point> of the addendum must be before or after the internal
data string and can not be within the internal data string.
=item
Pass the B<-vv> argument to po4a to understand how the addenda are added to the
translation. It may also help to run po4a in debug mode to see the actual
internal data string when your addendum does not apply.
=back
=head3 Addenda examples
=over
=item
If you want to add something after the following nroff section:
.SH "AUTHORS"
You should select a two step approach by setting B<mode=after>. Then you should
narrow down search to the line after B<AUTHORS> with the B<position> argument
regex. Then, you should match the beginning of the next section (i.e.,
B<^\.SH>) with the B<beginboundary> argument regex. That is to say:
PO4A-HEADER:mode=after;position=AUTHORS;beginboundary=\.SH
=item
If you want to add something right after a given line (e.g. after the line
"Copyright Big Dude"), use a B<position> matching this line, B<mode=after> and
give a B<beginboundary> matching any line.
PO4A-HEADER:mode=after;position=Copyright Big Dude, 2004;beginboundary=^
=item
If you want to add something at the end of the document, give a B<position>
matching any line of your document (but only one line. Po4a won't proceed if
it's not unique), and give an B<endboundary> matching nothing. Don't use simple
strings here like B<"EOF">, but prefer those which have less chance to be in
your document.
PO4A-HEADER:mode=after;position=About this document;beginboundary=FakePo4aBoundary
=back
=head3 More detailed example
Original document (POD formatted):
|=head1 NAME
|
|dummy - a dummy program
|
|=head1 AUTHOR
|
|me
Then, the following addendum will ensure that a section (in French) about
the translator is added at the end of the file (in French, "TRADUCTEUR"
means "TRANSLATOR", and "moi" means "me").
|PO4A-HEADER:mode=after;position=AUTEUR;beginboundary=^=head
|
|=head1 TRADUCTEUR
|
|moi
|
To put your addendum before the AUTHOR, use the following header:
PO4A-HEADER:mode=after;position=NOM;beginboundary=^=head1
This works because the next line matching the B<beginboundary> /^=head1/ after
the section "NAME" (translated to "NOM" in French), is the one declaring the
authors. So, the addendum will be put between both sections. Note that if
another section is added between NAME and AUTHOR sections later, po4a
will wrongfully put the addenda before the new section.
To avoid this you may accomplish the same using B<mode>=I<before>:
PO4A-HEADER:mode=before;position=^=head1 AUTEUR
=head1 How does it work?
This chapter gives you a brief overview of the po4a internals, so that you
may feel more confident to help us maintaining and improving it. It may also
help you understanding why it does not do what you expected, and how to
solve your problems.
The po4a architecture is object oriented. The
L<Locale::Po4a::TransTractor(3pm)|TransTractor> class is the common ancestor to
all po4a parsers. This strange name comes from the fact that it is at the same
time in charge of translating document and extracting strings.
More formally, it takes a document to translate plus a PO file containing
the translations to use as input while producing two separate outputs:
Another PO file (resulting of the extraction of translatable strings from
the input document), and a translated document (with the same structure than
the input one, but with all translatable strings replaced with content of
the input PO). Here is a graphical representation of this:
Input document --\ /---> Output document
\ TransTractor:: / (translated)
+-->-- parse() --------+
/ \
Input PO --------/ \---> Output PO
(extracted)
This little bone is the core of all the po4a architecture. If you omit the input
PO and the output document, you get B<po4a-gettextize>. If you provide both
input and disregard the output PO, you get B<po4a-translate>. The B<po4a> calls
TransTractor twice and calls B<msgmerge -U> between these TransTractor
invocations to provide one-stop solution with a single configuration file.
Please see L<Locale::Po4a::TransTractor(3pm)> for more details.
=head1 Open-source projects using po4a
Here is a very partial list of projects that use po4a in production for their
documentation. If you want to add your project to the list, just drop
us an email (or a Merge Request).
=over
=item
adduser (man): users and groups management tool.
=item
apt (man, docbook): Debian package manager.
=item
aptitude (docbook, svg): terminal-based package manager for Debian
=item
L<F-Droid website|https://gitlab.com/fdroid/fdroid-website> (markdown):
installable catalogue of FOSS (Free and Open Source
Software) applications for the Android platform.
=item
L<git|https://github.com/jnavila/git-manpages-l10n> (asciidoc):
distributed version-control system for tracking changes in source code.
=item
L<Linux manpages|https://salsa.debian.org/manpages-l10n-team/manpages-l10n> (man)
This project provides an infrastructure for translating many manpages
to different languages, ready for integration into several major
distributions (Arch Linux, Debian and derivatives, Fedora).
=item
L<Stellarium|https://github.com/Stellarium/stellarium> (HTML):
a free open source planetarium for your computer. po4a is used to
translate the sky culture descriptions.
=item
Other item to sort out:
L<https://gitlab.com/fdroid/fdroid-website/>
L<https://github.com/fsfe/reuse-docs/pull/61>
=back
=head1 FAQ
=head2 How do you pronounce po4a?
I personally vocalize it as L<pouah|https://en.wiktionary.org/wiki/pouah>, which
is a French onomatopoetic that we use in place of yuck :) I may have a strange sense of humor :)
=head2 What about the other translation tools for documentation using gettext?
As far as I know, there are only two of them:
=over
=item B<poxml>
This is the tool developed by KDE people to handle DocBook XML. AFAIK, it
was the first program to extract strings to translate from documentation to
PO files, and inject them back after translation.
It can only handle XML, and only a particular DTD. I'm quite unhappy with
the handling of lists, which end in one big msgid. When the list become big,
the chunk becomes harder to swallow.
=item B<po-debiandoc>
This program done by Denis Barbier is a sort of precursor of the po4a SGML
module, which more or less deprecates it. As the name says, it handles only
the DebianDoc DTD, which is more or less a deprecated DTD.
=back
The main advantages of po4a over them are the ease of extra content addition
(which is even worse there) and the ability to achieve gettextization.
=head2 SUMMARY of the advantages of the gettext based approach
=over 2
=item
The translations are not stored along with the original, which makes it
possible to detect if translations become out of date.
=item
The translations are stored in separate files from each other, which prevents
translators of different languages from interfering, both when submitting
their patch and at the file encoding level.
=item
It is based internally on B<gettext> (but B<po4a> offers a very simple
interface so that you don't need to understand the internals to use it).
That way, we don't have to re-implement the wheel, and because of their
wide use, we can think that these tools are more or less bug free.
=item
Nothing changed for the end-user (beside the fact translations will
hopefully be better maintained). The resulting documentation file
distributed is exactly the same.
=item
No need for translators to learn a new file syntax and their favorite PO
file editor (like Emacs' PO mode, Lokalize or Gtranslator) will work just fine.
=item
gettext offers a simple way to get statistics about what is done, what should
be reviewed and updated, and what is still to do. Some example can be found
at those addresses:
- https://docs.kde.org/stable5/en/kdesdk/lokalize/project-view.html
- http://www.debian.org/intl/l10n/
=back
But everything isn't green, and this approach also has some disadvantages
we have to deal with.
=over 2
=item
Addenda are… strange at the first glance.
=item
You can't adapt the translated text to your preferences, like splitting a
paragraph here, and joining two other ones there. But in some sense, if
there is an issue with the original, it should be reported as a bug anyway.
=item
Even with an easy interface, it remains a new tool people have to learn.
One of my dreams would be to integrate somehow po4a to Gtranslator or
Lokalize. When a documentation file is opened, the strings are
automatically extracted, and a translated file + po file can be
written to disk. If we manage to do an MS Word (TM) module (or at
least RTF) professional translators may even use it.
=back
=head1 SEE ALSO
=over
=item
The documentation of the all-in-one tool that you should use: L<po4a(1)>.
=item
The documentation of the individual po4a scripts: L<po4a-gettextize(1)>,
L<po4a-updatepo(1)>, L<po4a-translate(1)>, L<po4a-normalize(1)>.
=item
The additional helping scripts:
L<msguntypot(1)>, L<po4a-display-man(1)>, L<po4a-display-pod(1)>.
=item
The parsers of each formats, in particular to see the options accepted by each
of them: L<Locale::Po4a::AsciiDoc(3pm)> L<Locale::Po4a::Dia(3pm)>,
L<Locale::Po4a::Guide(3pm)>, L<Locale::Po4a::Ini(3pm)>,
L<Locale::Po4a::KernelHelp(3pm)>, L<Locale::Po4a::Man(3pm)>,
L<Locale::Po4a::RubyDoc(3pm)>, L<Locale::Po4a::Texinfo(3pm)>,
L<Locale::Po4a::Text(3pm)>, L<Locale::Po4a::Xhtml(3pm)>,
L<Locale::Po4a::Yaml(3pm)>, L<Locale::Po4a::BibTeX(3pm)>,
L<Locale::Po4a::Docbook(3pm)>, L<Locale::Po4a::Halibut(3pm)>,
L<Locale::Po4a::LaTeX(3pm)>, L<Locale::Po4a::Pod(3pm)>,
L<Locale::Po4a::Sgml(3pm)>, L<Locale::Po4a::TeX(3pm)>,
L<Locale::Po4a::Wml(3pm)>, L<Locale::Po4a::Xml(3pm)>.
=item
The implementation of the core infrastructure:
L<Locale::Po4a::TransTractor(3pm)> (particularly important to understand the
code organization), L<Locale::Po4a::Chooser(3pm)>, L<Locale::Po4a::Po(3pm)>,
L<Locale::Po4a::Common(3pm)>. Please also check the F<CONTRIBUTING.md> file in
the source tree.
=back
=head1 AUTHORS
Denis Barbier <barbier,linuxfr.org>
Martin Quinson (mquinson#debian.org)
=cut
LocalWords: PO gettext SGML XML texinfo perl gettextize fr Lokalize KDE updatepo
LocalWords: Gtranslator gettextization VCS regexp boundary
LocalWords: lang TransTractor debconf diff poxml debiandoc LocalWords
LocalWords: Denis barbier linuxfr org Quinson

1320
lib/Locale/Po4a/AsciiDoc.pm Normal file

File diff suppressed because it is too large Load Diff

146
lib/Locale/Po4a/BibTeX.pm Normal file
View File

@ -0,0 +1,146 @@
#!/usr/bin/perl -w
# Po4a::BibTeX.pm
#
# extract and translate translatable strings from BibTeX documents
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
#
########################################################################
=encoding UTF-8
=head1 NAME
Locale::Po4a::BibTeX - convert BibTeX documents from/to PO files
=head1 DESCRIPTION
The po4a (PO for anything) project goal is to ease translations (and more
interestingly, the maintenance of translations) using gettext tools on
areas where they were not expected like documentation.
Locale::Po4a::BibTeX is a module to help the translation of
bibliographies in the BibTeX format into other [human] languages.
Fields values are extracted and proposed for translation.
=head1 OPTIONS ACCEPTED BY THIS MODULE
NONE.
=head1 STATUS OF THIS MODULE
It is a very simple module, but still young.
=cut
package Locale::Po4a::BibTeX;
use 5.006;
use strict;
use warnings;
require Exporter;
use vars qw(@ISA @EXPORT);
@ISA = qw(Locale::Po4a::TransTractor);
@EXPORT = qw();
use Locale::Po4a::TransTractor;
use Locale::Po4a::Common;
sub initialize { }
sub parse {
my $self = shift;
my ( $line, $ref );
my $paragraph = "";
my $field = "";
my $id = "";
my $wrapped_mode = 1;
( $line, $ref ) = $self->shiftline();
while ( defined($line) ) {
chomp($line);
#print "tutu: '$line'\n";
$self->{ref} = "$ref";
if ( $id eq ""
and $line =~ m/^\@.*?\s*\{\s*(.*),\s*$/ )
{
$id = $1;
$self->pushline( $line . "\n" );
} elsif ( $id ne ""
and $field eq ""
and $line =~ m/^((.*?)\s*=\s*)([^ "{].*?)(\s*,?\s*)$/ )
{
my $end = ( defined $4 ) ? $4 : "";
$self->pushline( $1 . $self->translate( $3, $self->{ref}, "$2 ($id)", "wrap" => 1 ) . $end . "\n" );
$field = "";
$paragraph = "";
} elsif ( $id ne ""
and $field eq ""
and $line =~ m/^((.*?)\s*=\s*)(.*)$/ )
{
$field = $2;
$paragraph = $3 . "\n";
$self->pushline($1);
} elsif ( $field ne "" ) {
$paragraph .= "$line\n";
} elsif ( $line =~ m/^\s*(\%.*)?$/ ) {
$self->pushline( $line . "\n" );
} elsif ( $line =~ m/^\s*\}\s*$/ ) {
$self->pushline( $line . "\n" );
$id = "";
} else {
print "unsupported line: '$line'\n";
}
if ( $paragraph =~ m/^(\s*\{)(.*)(\}\s*,?\s*)$/s
or $paragraph =~ m/^(\s*")(.*)("\s*,?\s*)$/s
or $paragraph =~ m/^(\s*)([^ "{].*)(\s*,?\s*)$/s )
{
$self->pushline( $1 . $self->translate( $2, $self->{ref}, "$field ($id)", "wrap" => 1 ) . $3 );
$field = "";
$paragraph = "";
}
( $line, $ref ) = $self->shiftline();
}
if ( $paragraph =~ m/^(\s*\{)(.*)(\}\s*,?\s*)$/s
or $paragraph =~ m/^(\s*")(.*)("\s*,?\s*)$/s
or $paragraph =~ m/^(\s*)(.*)(\s*,?\s*)$/s )
{
$self->pushline( $self->translate( $1, $self->{ref}, "$field ($id)", "wrap" => 1 ) . $2 );
$field = "";
$paragraph = "";
}
}
sub do_paragraph {
my ( $self, $paragraph, $wrap ) = ( shift, shift, shift );
$self->pushline( $self->translate( $paragraph, $self->{ref}, "Plain text", "wrap" => $wrap ) );
}
1;
=head1 AUTHORS
Nicolas François <nicolas.francois@centraliens.net>
=head1 COPYRIGHT AND LICENSE
Copyright © 2006 Nicolas FRANÇOIS <nicolas.francois@centraliens.net>.
This program is free software; you may redistribute it and/or modify it
under the terms of GPL (see the COPYING file).

178
lib/Locale/Po4a/Chooser.pm Normal file
View File

@ -0,0 +1,178 @@
# Locale::Po4a::Chooser -- Manage po4a modules
#
# This program is free software; you may redistribute it and/or modify it
# under the terms of GPL (see COPYING).
#
# This module converts POD to PO file, so that it becomes possible to
# translate POD formatted documentation. See gettext documentation for
# more info about PO files.
############################################################################
# Modules and declarations
############################################################################
package Locale::Po4a::Chooser;
use 5.006;
use strict;
use warnings;
use Locale::Po4a::Common;
sub new {
my ($module) = shift;
my (%options) = @_;
die wrap_mod( "po4a::chooser", gettext("Please provide a module name") )
unless defined $module;
my $modname;
if ( $module eq 'kernelhelp' ) {
$modname = 'KernelHelp';
} elsif ( $module eq 'newsdebian' ) {
$modname = 'NewsDebian';
} elsif ( $module eq 'latex' ) {
$modname = 'LaTeX';
} elsif ( $module eq 'bibtex' ) {
$modname = 'BibTex';
} elsif ( $module eq 'tex' ) {
$modname = 'TeX';
} elsif ( $module eq 'asciidoc' ) {
$modname = 'AsciiDoc';
} elsif ( $module eq 'Rd' || $module eq 'rubydoc' ) {
$modname = 'RubyDoc';
} else {
$modname = ucfirst($module);
}
if ( !UNIVERSAL::can( "Locale::Po4a::$modname", 'new' ) ) {
eval qq{use Locale::Po4a::$modname};
if ($@) {
my $error = $@;
warn wrap_msg( gettext("Unknown format type: %s."), $module );
warn wrap_mod( "po4a::chooser", gettext("Module loading error: %s"), $error )
unless defined $options{'quiet'};
list(1);
}
}
return "Locale::Po4a::$modname"->new(%options);
}
sub list {
warn wrap_msg(
gettext("List of valid formats:")
# ."\n - ".gettext("bibtex: BibTex bibliography format.")
. "\n - "
. gettext("asciidoc: AsciiDoc format.")
. "\n - "
. gettext("dia: uncompressed Dia diagrams.")
. "\n - "
. gettext("docbook: DocBook XML.")
. "\n - "
. gettext("guide: Gentoo Linux's XML documentation format.")
. "\n - "
. gettext("ini: INI format.")
. "\n - "
. gettext("kernelhelp: Help messages of each kernel compilation option.")
. "\n - "
. gettext("latex: LaTeX format.")
. "\n - "
. gettext("man: Good old manual page format.")
. "\n - "
. gettext("pod: Perl Online Documentation format.")
. "\n - "
. gettext("rubydoc: Ruby Documentation (RD) format.")
. "\n - "
. gettext("sgml: either DebianDoc or DocBook DTD.")
. "\n - "
. gettext("texinfo: The info page format.")
. "\n - "
. gettext("tex: generic TeX documents (see also latex).")
. "\n - "
. gettext("text: simple text document.")
. "\n - "
. gettext("wml: WML documents.")
. "\n - "
. gettext("xhtml: XHTML documents.")
. "\n - "
. gettext("xml: generic XML documents (see also docbook).")
. "\n - "
. gettext("yaml: YAML documents.")
);
exit shift;
}
##############################################################################
# Module return value and documentation
##############################################################################
1;
__END__
=encoding UTF-8
=head1 NAME
Locale::Po4a::Chooser - manage po4a modules
=head1 DESCRIPTION
Locale::Po4a::Chooser is a module to manage po4a modules. Previously, all po4a
binaries used to know all po4a modules (pod, man, sgml, etc). This made the
addition of a new module boring, because you had to make sure that the documentation is synchronized
in all modules, and that each of them can access the new module.
Now, you just have to call the Locale::Po4a::Chooser::new() function,
passing the name of module as argument.
The function Locale::Po4a::Chooser::list() lists the available
formats, and exits with the value passed as argument. So, we call
Locale::Po4a::Chooser::list(0) when requested for the list of
formats, and Locale::Po4a::Chooser::list(1) when passed an invalid
format name.
=head1 SEE ALSO
=over 4
=item About po4a:
L<Locale::Po4a::Po(3pm)>,
L<Locale::Po4a::TransTractor(3pm)>,
L<po4a(7)|po4a.7>
=item About modules:
L<Locale::Po4a::Dia(3pm)>,
L<Locale::Po4a::Docbook(3pm)>,
L<Locale::Po4a::Guide(3pm)>,
L<Locale::Po4a::Halibut(3pm)>,
L<Locale::Po4a::Ini(3pm)>,
L<Locale::Po4a::KernelHelp(3pm)>,
L<Locale::Po4a::LaTeX(3pm)>,
L<Locale::Po4a::Man(3pm)>,
L<Locale::Po4a::Pod(3pm)>,
L<Locale::Po4a::RubyDoc(3pm)>,
L<Locale::Po4a::Sgml(3pm)>,
L<Locale::Po4a::TeX(3pm)>,
L<Locale::Po4a::Texinfo(3pm)>,
L<Locale::Po4a::Text(3pm)>,
L<Locale::Po4a::Wml(3pm)>.
L<Locale::Po4a::Xhtml(3pm)>,
L<Locale::Po4a::Xml(3pm)>,
L<Locale::Po4a::Wml(3pm)>,
L<Locale::Po4a::Yaml(3pm)>.
=back
=head1 AUTHORS
Denis Barbier <barbier@linuxfr.org>
Martin Quinson (mquinson#debian.org)
=head1 COPYRIGHT AND LICENSE
Copyright © 2002-2005, 2014, 2017 SPI, Inc.
This program is free software; you may redistribute it and/or modify it
under the terms of GPL (see the COPYING file).
=cut

252
lib/Locale/Po4a/Common.pm Normal file
View File

@ -0,0 +1,252 @@
# Locale::Po4a::Common -- Common parts of the po4a scripts and utils
#
# Copyright © 2005 Jordi Vilalta <jvprat@gmail.com>
#
# This program is free software; you may redistribute it and/or modify it
# under the terms of GPL (see COPYING).
#
# This module has common utilities for the various scripts of po4a
=encoding UTF-8
=head1 NAME
Locale::Po4a::Common - common parts of the po4a scripts and utils
=head1 DESCRIPTION
Locale::Po4a::Common contains common parts of the po4a scripts and some useful
functions used along the other modules.
If needed, you can disable the use of Text::WrapI18N as such:
use Locale::Po4a::Common qw(nowrapi18n);
use Locale::Po4a::Text;
instead of:
use Locale::Po4a::Text;
The ordering is important here: as most Locale::Po4a modules load themselves
Locale::Po4a::Common, the first time this module is loaded determines whether Text::WrapI18N is used.
=cut
package Locale::Po4a::Common;
require Exporter;
use vars qw(@ISA @EXPORT);
@ISA = qw(Exporter);
@EXPORT = qw(wrap_msg wrap_mod wrap_ref_mod textdomain gettext dgettext);
use 5.006;
use strict;
use warnings;
sub import {
my $class = shift;
my $wrapi18n = 1;
if ( exists $_[0] && defined $_[0] && $_[0] eq 'nowrapi18n' ) {
shift;
$wrapi18n = 0;
}
$class->export_to_level( 1, $class, @_ );
return if defined &wrapi18n;
if ( $wrapi18n && -t STDERR && -t STDOUT && eval { require Text::WrapI18N } ) {
# Don't bother determining the wrap column if we cannot wrap.
my $col = $ENV{COLUMNS};
if ( !defined $col ) {
my @term = eval "use Term::ReadKey; Term::ReadKey::GetTerminalSize()";
$col = $term[0] if ( !$@ );
# If GetTerminalSize() failed we will fallback to a safe default.
# This can happen if Term::ReadKey is not available
# or this is a terminal-less build or such strange condition.
}
$col = 76 if ( !defined $col );
eval ' use Text::WrapI18N qw($columns);
$columns = $col;
';
eval ' sub wrapi18n($$$) { Text::WrapI18N::wrap($_[0],$_[1],$_[2]) } ';
} else {
# If we cannot wrap, well, that's too bad. Survive anyway.
eval ' sub wrapi18n($$$) { $_[0].$_[2] } ';
}
}
sub min($$) {
return $_[0] < $_[1] ? $_[0] : $_[1];
}
=head1 FUNCTIONS
=head2 Showing output messages
=over
=item
show_version($)
Shows the current version of the script, and a short copyright message. It
takes the name of the script as an argument.
=cut
sub show_version {
my $name = shift;
print sprintf(
gettext(
"%s version %s.\n"
. "Written by Martin Quinson and Denis Barbier.\n\n"
. "Copyright © 2002-2021 Software in the Public Interest, Inc.\n"
. "This is free software; see source code for copying\n"
. "conditions. There is NO warranty; not even for\n"
. "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
),
$name,
$Locale::Po4a::TransTractor::VERSION
) . "\n";
}
=item
wrap_msg($@)
This function displays a message the same way as sprintf() does, but wraps
the result so that they look nice on the terminal.
=cut
sub wrap_msg($@) {
my $msg = shift;
my @args = @_;
# print "'$msg' ; ".(scalar @args)." $args[0] $args[1]\n";
return wrapi18n( "", "", sprintf( $msg, @args ) ) . "\n";
}
=item
wrap_mod($$@)
This function works like wrap_msg(), but it takes a module name as the first
argument, and leaves a space at the left of the message.
=cut
sub wrap_mod($$@) {
my ( $mod, $msg ) = ( shift, shift );
my @args = @_;
$mod .= ": ";
my $spaces = " " x min( length($mod), 15 );
return wrapi18n( $mod, $spaces, sprintf( $msg, @args ) ) . "\n";
}
=item
wrap_ref_mod($$$@)
This function works like wrap_msg(), but it takes a file:line reference as the
first argument, a module name as the second one, and leaves a space at the left
of the message.
=back
=cut
sub wrap_ref_mod($$$@) {
my ( $ref, $mod, $msg ) = ( shift, shift, shift );
my @args = @_;
if ( !$mod ) {
# If we don't get a module name, show the message like wrap_mod does
return wrap_mod( $ref, $msg, @args );
} else {
$ref .= ": ";
my $spaces = " " x min( length($ref), 15 );
$msg = "$ref($mod)\n$msg";
return wrapi18n( "", $spaces, sprintf( $msg, @args ) ) . "\n";
}
}
=head2 Wrappers for other modules
=over
=item
Locale::Gettext
When the Locale::Gettext module cannot be loaded, this module provide dummy
(empty) implementation of the following functions. In that case, po4a
messages won't get translated but the program will continue to work.
If Locale::gettext is present, this wrapper also calls
setlocale(LC_MESSAGES, "") so callers don't depend on the POSIX module
either.
=over
=item
bindtextdomain($$)
=item
textdomain($)
=item
gettext($)
=item
dgettext($$)
=back
=back
=cut
BEGIN {
if ( eval { require Locale::gettext } ) {
import Locale::gettext;
require POSIX;
POSIX::setlocale( &POSIX::LC_MESSAGES, '' );
} else {
eval '
sub bindtextdomain($$) { }
sub textdomain($) { }
sub gettext($) { shift }
sub dgettext($$) { return $_[1] }
'
}
}
1;
__END__
=head1 AUTHORS
Jordi Vilalta <jvprat@gmail.com>
=head1 COPYRIGHT AND LICENSE
Copyright © 2005 SPI, Inc.
This program is free software; you may redistribute it and/or modify it
under the terms of GPL (see the COPYING file).
=cut

114
lib/Locale/Po4a/Dia.pm Normal file
View File

@ -0,0 +1,114 @@
#!/usr/bin/perl
# Po4a::Dia.pm
#
# extract and translate translatable strings from Dia diagrams.
#
# This code extracts plain text from string tags on uncompressed Dia
# diagrams.
#
# Copyright © 2004 Jordi Vilalta <jvprat@gmail.com>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
#
########################################################################
=encoding UTF-8
=head1 NAME
Locale::Po4a::Dia - convert uncompressed Dia diagrams from/to PO files
=head1 DESCRIPTION
The po4a (PO for anything) project goal is to ease translations (and more
interestingly, the maintenance of translations) using gettext tools on
areas where they were not expected like documentation.
Locale::Po4a::Dia is a module to help the translation of diagrams in the
uncompressed Dia format into other [human] languages.
You can get Dia (the graphical editor for these diagrams) from:
http://www.gnome.org/projects/dia/
=head1 TRANSLATING WITH PO4A::DIA
This module only translates uncompressed Dia diagrams. You can save your
uncompressed diagrams with Dia itself, unchecking the "Compress diagram
files" at the "Save Diagram" dialog.
Another way is to uncompress the dia files from command line with:
gunzip < original.dia > uncompressed.dia
=head1 STATUS OF THIS MODULE
This module is fully functional, as it relies in the L<Locale::Po4a::Xml>
module. This only defines the translatable tags (E<lt>dia:stringE<gt>), and
filters the internal strings (the content of the E<lt>dia:diagramdataE<gt>
tag), not interesting for translation.
=head1 SEE ALSO
L<Locale::Po4a::TransTractor(3pm)>, L<Locale::Po4a::Xml(3pm)>, L<po4a(7)|po4a.7>
=head1 AUTHORS
Jordi Vilalta <jvprat@gmail.com>
=head1 COPYRIGHT AND LICENSE
Copyright © 2004 Jordi Vilalta <jvprat@gmail.com>
This program is free software; you may redistribute it and/or modify it
under the terms of GPL (see the COPYING file).
=cut
package Locale::Po4a::Dia;
use 5.006;
use strict;
use warnings;
use Locale::Po4a::Common;
use Locale::Po4a::Xml;
use vars qw(@ISA);
@ISA = qw(Locale::Po4a::Xml);
sub initialize {
my $self = shift;
my %options = @_;
$self->SUPER::initialize(%options);
$self->{options}{'nostrip'} = 1;
$self->{options}{'_default_translated'} .= ' <dia:string>';
print "Call treat_options\n" if $self->{options}{'debug'};
$self->treat_options;
}
sub found_string {
my ( $self, $text, $ref, $options ) = @_;
return $text if $text =~ m/^\s*$/s;
#We skip the paper type string
if ( $self->get_path() !~ /<dia:diagramdata>/ ) {
$text =~ /^#(.*)#$/s;
$text = "#" . $self->translate( $1, $ref, "String", 'wrap' => $self->{options}{'wrap'} ) . "#";
}
return $text;
}

2060
lib/Locale/Po4a/Docbook.pm Normal file

File diff suppressed because it is too large Load Diff

153
lib/Locale/Po4a/Guide.pm Normal file
View File

@ -0,0 +1,153 @@
#!/usr/bin/perl
# Po4a::Guide.pm
#
# extract and translate translatable strings from Guide XML documents.
#
# This code extracts plain text from tags and attributes on Guide XML
# documents.
#
# Copyright © 2004 Jordi Vilalta <jvprat@gmail.com>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
#
########################################################################
=encoding UTF-8
=head1 NAME
Locale::Po4a::Guide - convert Guide XML documents from/to PO files
=head1 DESCRIPTION
The po4a (PO for anything) project goal is to ease translations (and more
interestingly, the maintenance of translations) using gettext tools on
areas where they were not expected like documentation.
Locale::Po4a::Guide is a module to help in the translation of the Gentoo
Linux documentation in the Guide XML format into other [human] languages.
This format is documented here: http://www.gentoo.org/doc/en/xml-guide.xml
=head1 STATUS OF THIS MODULE
This module is fully functional, as it relies in the L<Locale::Po4a::Xml>
module. This only defines the translatable tags and attributes.
The only known issue is that it doesn't include files with the <include
href="..."> tag, but you can translate all those files alone, and it's usually
better to have them separated.
=head1 SEE ALSO
L<Locale::Po4a::TransTractor(3pm)>, L<Locale::Po4a::Xml(3pm)>, L<po4a(7)|po4a.7>
=head1 AUTHORS
Jordi Vilalta <jvprat@gmail.com>
=head1 COPYRIGHT AND LICENSE
Copyright © 2004 Jordi Vilalta <jvprat@gmail.com>
This program is free software; you may redistribute it and/or modify it
under the terms of GPL (see the COPYING file).
=cut
package Locale::Po4a::Guide;
use 5.006;
use strict;
use warnings;
use Locale::Po4a::Common;
use Locale::Po4a::Xml;
use vars qw(@ISA);
@ISA = qw(Locale::Po4a::Xml);
sub initialize {
my $self = shift;
my %options = @_;
#TODO: <include href="..."> includes a file
$self->SUPER::initialize(%options);
$self->{options}{'_default_translated'} .= '
w<abstract>
<author>
<b>
<brite>
<c>
<codenote>
<comment>
<const>
<date>
w<dd>
w<dt>
<e>
<i>
<ident>
w<impo>
<keyword>
w<li>
<mail>
w<note>
w<p>
<path>
W<pre>
<stmt>
<sub>
w<subtitle>
w<summary>
<sup>
w<th>
w<ti>
w<title>
<uri>
<var>
<version>
w<warn>';
$self->{options}{'_default_attributes'} .= '
<author>title
<figure>caption
<figure>link
<figure>short
<guide>lang
<guide>link
<p>by
<pre>caption';
$self->{options}{'_default_inline'} .= '
<b>
<brite>
<c>
<const>
<e>
<i>
<ident>
<img>
<keyword>
<mail>
<path>
<stmt>
<sub>
<sup>
<uri>
<var>';
print "Call treat_options\n" if $self->{options}{'debug'};
$self->treat_options;
}

448
lib/Locale/Po4a/Halibut.pm Normal file
View File

@ -0,0 +1,448 @@
#!/usr/bin/perl -w
# Copyright © 2004-2008 Nicolas FRANÇOIS <nicolas.francois@centraliens.net>
#
# This file is part of po4a.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with po4a; if not, write to the Free Software
# Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
#
########################################################################
=encoding UTF-8
=head1 NAME
Locale::Po4a::Halibut - convert Halibut documents and derivates from/to PO files
=head1 DESCRIPTION
The po4a (PO for anything) project goal is to ease translations (and more
interestingly, the maintenance of translations) using gettext tools on
areas where they were not expected like documentation.
Locale::Po4a::Halibut is a module to help the translation of Halibut documents into
other [human] languages.
This module contains the definitions of common Halibut commands and
environments.
=head1 STATUS OF THIS MODULE
This module is still beta.
Please send feedback and feature requests.
=head1 CAVEAT
Some constructs are badly supported. The known ones are documented below.
=head2 Verbatim blocks
\c foo
\c bar
The verbatim block is not considered as a whole. Each line will be
translated separately.
=head1 SEE ALSO
L<Locale::Po4a::TeX(3pm)|Locale::Po4a::TeX>,
L<Locale::Po4a::TransTractor(3pm)|Locale::Po4a::TransTractor>,
L<po4a(7)|po4a.7>
=head1 AUTHORS
Nicolas François <nicolas.francois@centraliens.net>
=head1 COPYRIGHT AND LICENSE
Copyright © 2004-2008 Nicolas FRANÇOIS <nicolas.francois@centraliens.net>.
This program is free software; you may redistribute it and/or modify it
under the terms of GPL (see COPYING file).
=cut
package Locale::Po4a::Halibut;
use 5.006;
use strict;
use warnings;
require Exporter;
use vars qw($VERSION @ISA @EXPORT);
$VERSION = $Locale::Po4a::TeX::VERSION;
@ISA = qw(Locale::Po4a::TeX);
@EXPORT = qw();
use Locale::Po4a::Common;
use Locale::Po4a::TeX;
use subs qw(&parse_definition_file
&register_generic_command &is_closed &translate_buffer
&register_verbatim_environment
&generic_command
&in_verbatim
&get_leading_command);
*parse_definition_file = \&Locale::Po4a::TeX::parse_definition_file;
*get_leading_command = \&Locale::Po4a::TeX::get_leading_command;
*register_generic_command = \&Locale::Po4a::TeX::register_generic_command;
*register_verbatim_environment = \&Locale::Po4a::TeX::register_verbatim_environment;
*generic_command = \&Locale::Po4a::TeX::generic_command;
*is_closed = \&Locale::Po4a::TeX::is_closed;
*in_verbatim = \&Locale::Po4a::TeX::in_verbatim;
*translate_buffer = \&Locale::Po4a::TeX::translate_buffer;
use vars qw($RE_ESCAPE $ESCAPE
$RE_VERBATIM
$RE_COMMENT $RE_PRE_COMMENT
$no_wrap_environments $separated_commands
%commands %environments
%command_categories %separated
%env_separators %debug
%translate_buffer_env
@exclude_include @comments);
*RE_ESCAPE = \$Locale::Po4a::TeX::RE_ESCAPE;
*ESCAPE = \$Locale::Po4a::TeX::ESCAPE;
*RE_VERBATIM = \$Locale::Po4a::TeX::RE_VERBATIM;
*RE_COMMENT = \$Locale::Po4a::TeX::RE_COMMENT;
*RE_PRE_COMMENT = \$Locale::Po4a::TeX::RE_PRE_COMMENT;
*no_wrap_environments = \$Locale::Po4a::TeX::no_wrap_environments;
*separated_commands = \$Locale::Po4a::TeX::separated_commands;
*commands = \%Locale::Po4a::TeX::commands;
*environments = \%Locale::Po4a::TeX::environments;
*command_categories = \%Locale::Po4a::TeX::command_categories;
*separated = \%Locale::Po4a::TeX::separated;
*env_separators = \%Locale::Po4a::TeX::env_separators;
*debug = \%Locale::Po4a::TeX::debug;
*translate_buffer_env = \%Locale::Po4a::TeX::translate_buffer_env;
*exclude_include = \@Locale::Po4a::TeX::exclude_include;
*comments = \@Locale::Po4a::TeX::comments;
#$ESCAPE = "\\";
#$RE_ESCAPE = "\\\\";
#$RE_VERBATIM = "\@example";
$RE_VERBATIM = "PO4A_FAKE_VERBATIM";
#$RE_COMMENT = "\\\@(?:c|comment)\\b";
$RE_COMMENT = "PO4A_FAKE_COMMENT";
sub docheader {
return "\\# This file was generated with po4a. Translate the source file.\n" . "\n";
}
my %break_line = ();
# translate_line_command indicate if the arguments to the command handled
# by line_command() should be translated:
# undefined: arguments are not translated
# 0: there should be no arguments
# 1: arguments should be translated
my %translate_line_command = ();
sub parse {
my $self = shift;
my ( $line, $ref );
my $paragraph = ""; # Buffer where we put the paragraph while building
my @env = (); # environment stack
my $t = "";
# $docheader_pushed = 0;
LINE:
undef $self->{type};
( $line, $ref ) = $self->shiftline();
while ( defined($line) ) {
chomp($line);
$self->{ref} = "$ref";
if ( $line =~ /^\s*\\\s*po4a\s*:/ ) {
parse_definition_line( $self, $line );
goto LINE;
}
my $t;
( $paragraph, $t, @env ) = parse_line( $self, $line, $paragraph, \@env );
$self->pushline($t);
# Reinit the loop
( $line, $ref ) = $self->shiftline();
undef $self->{type};
}
if ( length($paragraph) ) {
( $t, @env ) = translate_buffer( $self, $paragraph, undef, @env );
$self->pushline($t);
$paragraph = "";
}
} # end of parse
sub parse_line {
my $self = shift;
my $line = shift;
my $paragraph = shift;
my $env = shift;
my @e = @$env;
my $translated = "";
my $closed = 1;
if ( !in_verbatim(@e) ) {
$closed = is_closed($paragraph);
}
# if (not $closed) {
# print "not closed. line: '$line'\n para: '$paragraph'\n";
# }
#warn "closed'$closed'$line'$paragraph'\n";
if ( $closed and $line =~ /^\s*$/ ) {
# An empty line. This indicates the end of the current
# paragraph.
$paragraph .= $line . "\n";
if ( length($paragraph) ) {
( $translated, @e ) = translate_buffer( $self, $paragraph, undef, @e );
$paragraph = "";
}
} elsif ( $line =~ m/^\\input / ) {
if ( length($paragraph) ) {
( $translated, @e ) = translate_buffer( $self, $paragraph, undef, @e );
$paragraph = "";
}
$translated .= $line . "\n";
} elsif ( $line =~ m/^$RE_COMMENT/ ) {
$translated = $line . "\n";
} elsif ( $closed
and ( is_closed($line) or $line =~ /^\\[ce] / )
and ( $line =~ /^\\([^ ]*?)( +.*)?$/ ) )
{
my ( $command, $variant, $args, $buffer );
if ( $break_line{$1} ) {
my @a = ();
$variant = "";
$args = \@a;
$command = $1;
$buffer = $2 || "";
} else {
( $command, $variant, $args, $buffer ) = get_leading_command( $self, $line );
}
if (
$break_line{$command}
and not( ( $command eq "c" or $command eq "e" )
and defined $args->[0] )
)
{
# NOTE: This is just a workaround: "\c " is a verbatim line
# and \c{...} is just a verbatim block
my $t;
if ( length($paragraph) ) {
( $t, @e ) = translate_buffer( $self, $paragraph, undef, @e );
$translated .= $t;
$paragraph = "";
}
( $t, @e ) = generic_command( $self, $command, $variant, $args, \@e );
$translated .= $t;
my $arg = $buffer;
my @args = ();
if ( defined $arg and length $arg ) {
# FIXME: keep the spaces ?
$arg =~ s/\s*$//s;
@args = ( " ", $arg );
}
( $t, @e ) = line_command( $self, $command, "", \@args, \@e, 1 );
$translated .= $t . "\n";
} else {
# continue the same paragraph
$paragraph .= $line . "\n";
}
} else {
# continue the same paragraph
$paragraph .= $line . "\n";
}
return ( $paragraph, $translated, @e );
}
sub line_command {
my $self = shift;
my ( $command, $variant, $args, $env ) = ( shift, shift, shift, shift );
my $no_wrap = shift;
print "line_command($command,$variant,@$args,@$env,$no_wrap)="
if ( $debug{'commands'} );
my $translated = ""; # $ESCAPE.$command;
my $line = $args->[1];
#warn "line_command: '$line'\n";
if ( defined $line and length $line ) {
if ( defined $translate_line_command{$command}
and $translate_line_command{$command} )
{
# $no_wrap could be forced to 1, but it should already be set
$no_wrap = 1;
$line =~ s/^(\s*)//;
my $spaces = $1 || "";
my ( $t, $e ) = $self->translate_buffer( $line, $no_wrap, @$env, $command );
#warn "line_command: '$t'\n";
$translated .= $spaces . $t;
} else {
$translated .= $line;
}
}
print "($translated,@$env)\n"
if ( $debug{'commands'} );
return ( $translated, @$env );
}
# 3.2 Simple inline formatting commands
# 3.2.1 `\e': Emphasising text
# inline. extract only if alone
register_generic_command("-e,{_}");
$translate_line_command{e} = 1;
$break_line{e} = 1;
# 3.2.2 `\c' and `\cw': Displaying computer code inline
# inline. extract only if alone
# NOTE: \c and \c{...} differs.
# \c is marked as a break_line command, but this is reversed in
# parse_line when the \c{...} form is used.
register_generic_command("-c,{_}");
$translate_line_command{c} = 1;
$break_line{c} = 1;
register_generic_command("-cw,{_}");
# 3.2.3 `\q': Quotation marks
# inline. extract only if alone
register_generic_command("-q,{_}");
# 3.2.4 `\-' and `\_': Non-breaking hyphens and spaces
# inline.
# 3.2.5 `\date': Automatic date generation
# inline.
# 3.2.6 `\W': WWW hyperlinks
# inline. extract only if alone
register_generic_command("-W,{_}");
# 3.2.7 `\u': Specifying arbitrary Unicode characters
# inline.
# 3.2.8 `\k' and `\K': Cross-references to other sections
# inline. They should not be translated. extract only if alone
# FIXME: it will expand to "Section ..." or "section ..."
# Section and section should be translated.
register_generic_command("-k,{}");
register_generic_command("-K,{}");
# 3.2.9 `\#': Inline comments
# inline. But can be removed from the head or tail.
register_generic_command("-#,{}");
$translate_line_command{"#"} = 0;
$break_line{"#"} = 1;
# 3.3 Paragraph-level commands
# 3.3.1 `\c': Displaying whole paragraphs of computer code
# see above
# 3.3.2 `\b', `\n', `\dt', `\dd', `\lcont': Lists
register_generic_command("*b,");
register_generic_command("*n,"); # FIXME: \n{this-one} not supported?
register_generic_command("*dd,");
register_generic_command("*dt,");
# 3.3.2.4 Continuing list items into further paragraphs
register_generic_command("*lcont,{_}"); # registered, but redefined
$commands{lcont} = sub {
my $self = shift;
my ( $command, $variant, $args, $env ) = ( shift, shift, shift, shift );
my $no_wrap = shift;
my ( $t, @e ) = ( "", @$env );
my $translated = $ESCAPE . $command . $variant . "{";
my $text = $args->[1];
my $paragraph = "";
while ($text =~ s/^(.*?)\n(.*)$/$2/s
or $text =~ s/^([^\n]+)$//s )
{
( $paragraph, $t, @e ) = parse_line( $self, $1, $paragraph, \@e );
$translated .= $t;
}
( $t, @e ) = translate_buffer( $self, $paragraph, $no_wrap, @e );
$translated .= $t;
$translated .= "}";
return ( $translated, @$env );
};
# 3.3.3 `\rule': Horizontal rules
register_generic_command("rule,"); # TODO: TBC does it break paragraphs
# 3.3.4 `\quote': Indenting multiple paragraphs as a long quotation
register_generic_command("*quote,{_}"); # TODO: TBC
# 3.3.5 `\C', `\H', `\S', `\A', `\U': Chapter and section headings
# FIXME: What happens if the line is rewrapped?
# NOTE: The name of the section is not translated.
register_generic_command("*C,{}");
register_generic_command("*S0,{}"); # Synonym for \H
register_generic_command("*H,{}");
register_generic_command("*S,{}");
register_generic_command("*S1,{}"); # Synonym for \S
register_generic_command("*S2,{}");
register_generic_command("*S3,{}"); # FIXME: and so on
# FIXME: \S{question-about-fish}{Question}
register_generic_command("*A,{}");
register_generic_command("*U,{}");
# 3.3.6 `\copyright', `\title', `\versionid': Miscellaneous blurb commands
register_generic_command("*title,");
register_generic_command("*copyright,");
register_generic_command("*versionid,");
# 3.4 Creating a bibliography
# nocite
register_generic_command("*nocite,{}");
# B
register_generic_command("*B,{}");
# BR
register_generic_command("*BR,{}"); # FIXME: \BR{freds-book} [Fred1993]
# 3.5 Creating an index
# 3.5.1 Simple indexing
# \i: inline \i{index} or \i\x{grep}
# \ii
register_generic_command("-ii,{_}");
# \IM: inline. Variable number of arguments
register_generic_command("*IM,{_}");
$translate_line_command{IM} = 1;
$break_line{IM} = 1;
# 3.6 Configuring Halibut
# \cfg
register_generic_command("+cfg,{}{_}"); # NOTE: the new command is not registered
# 3.7 Defining macros
register_generic_command("*define,{}"); # FIXME: line
$translate_line_command{define} = 1;
$break_line{define} = 1;
1;

View File

@ -0,0 +1,226 @@
#!/usr/bin/perl -w
# Po4a::Debconf.pm
#
# extract and translate translatable strings from debconf templates
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
#
########################################################################
=encoding UTF-8
=head1 NAME
Locale::Po4a::Debconf - convert debconf templates from/to PO files
=head1 DESCRIPTION
The po4a (PO for anything) project goal is to ease translations (and more
interestingly, the maintenance of translations) using gettext tools on
areas where they were not expected like documentation.
Locale::Po4a::Debconf is a module to help the translation of the debconf
templates into other [human] languages.
=head1 OPTIONS ACCEPTED BY THIS MODULE
NONE.
=head1 STATUS OF THIS MODULE
Not tested.
DO NOT USE THIS MODULE TO PRODUCE TEMPLATES. It's only good to extract data.
=cut
# Note that the following works. It may help to write a multi-translate
# sub toto {
# do shift;
# }
# toto({print "ok"});
package Locale::Po4a::Debconf;
use 5.006;
use strict;
use warnings;
require Exporter;
use vars qw(@ISA @EXPORT);
@ISA = qw(Locale::Po4a::TransTractor);
@EXPORT = qw();
use Locale::Po4a::TransTractor;
use Locale::Po4a::Common;
sub initialize { }
sub parse {
my $self = shift;
my ( $line, $lref );
my ( $field, $value, $extended, $ref, $type ) = ( '', '', '', '', '' );
my $verb = 0; # whether we are in verbatim mode
my $escape = sub {
my $str = shift;
$str =~ s/"/\\"/g;
return $str;
};
# function in charge of pushing the accumulated material to output
my $handle_field = sub {
my $field = shift;
my $value = shift;
my $extended = shift;
my $ref = shift;
my $type = shift;
$field =~ s/^(_*)(.*)$/$2/;
my $undercount = length($1) || 0; # number of _ leading the field name
# Only one leading _: regular translated field
if ( $undercount == 1 ) {
# the untranslated field
$self->pushline("$field: $value");
map { $self->pushline( ' ' . ( $_ || '.' ) ) } split( /\n/, $extended );
my $eval = '$self->pushline("' . $field . '[FIXME:LANGCODE.ENCODING]: "'; # what to multi-eval
$eval .=
'.$self->translate("' . $escape->($value) . "\",\"$ref\",\"$type/$field\",wrap=>1)" . '."\n".' . "\n";
my $count = 0;
foreach my $para ( split( /\n\n/, $extended ) ) {
my $wrap = 1;
if ( $para =~ /(^|\n)\s/m ) {
$wrap = 0;
}
$eval .= ( $count ? '.' : '' );
$count++;
$eval .=
'$self->translate("'
. $escape->($para)
. "\",\"$ref\",\"$type/$field\[$count\]\",wrap=>$wrap)" . "\n";
}
$eval .= ")\n";
print STDERR $eval if $self->{options}{'debug'};
eval $eval;
print STDERR "XXXXXXXXXXXXXXXXX\n" if $self->{options}{'debug'};
# two leading _: split on coma and multi-translate each part. No extended value.
} elsif ( $undercount == 2 ) {
$self->pushline("$field: $value"); # the untranslated field
my $eval = '$self->pushline("' . $field . 'FIXME[LANGCODE]: "'; # what to multi-eval
my $first = 1;
for my $part ( split( /(?<!\\), */, $value, 0 ) ) {
$part =~ s/\\,/,/g;
$eval .=
( $first ? '' : '.", "' )
. '.$self->translate("'
. $escape->($part)
. "\",\"$ref\",\"$type/$field chunk\",wrap=>1)";
$first = 0;
}
$eval .= ")\n";
print $eval if $self->{options}{'debug'};
eval $eval;
# no leading _: don't touch it
} else {
$self->pushline("$field: $value");
map { $self->pushline( ' ' . ( $_ || '.' ) ) } split( /\n/, $extended );
}
};
# main loop
( $line, $lref ) = $self->shiftline();
while ( defined($line) ) {
# a new field (within a stanza)
if ( $line =~ /^([-_.A-Za-z0-9]*):\s?(.*)/ ) {
$handle_field->( $field, $value, $extended, $ref, $type ); # deal with previously accumulated
( $field, $value, $extended, $verb ) = ( '', '', '', 0 );
$field = $1;
$value = $2;
$value =~ s/\s*$//;
$extended = '';
$ref = $lref;
$type = $value if $field eq 'Type';
die wrap_mod( "po4a::debconf", dgettext( "po4a", "Translated field in master document: %s" ), $field )
if $field =~ m/-/;
# paragraph separator within extended value
} elsif ( $line =~ /^\s\.$/ ) {
$extended .= "\n\n";
# continuation of extended value
} elsif ( $line =~ /^\s(.*)/ ) {
my $bit = $1;
$verb = 1 if ( $bit =~ m/^\s/ );
$bit =~ s/\s*$//;
$extended .= ( $verb ? "\n" : ' ' ) if length $extended && $extended !~ /[\n ]$/;
$extended .= $bit . ( $verb ? "\n" : "" );
# this may be an empty line closing the stanza, a comment or even a parse error (if file not DebConf-clean).
} else {
$handle_field->( $field, $value, $extended, $ref, $type );
( $field, $value, $extended, $verb ) = ( '', '', '', 0 );
$self->pushline($line);
}
( $line, $lref ) = $self->shiftline();
}
$handle_field->( $field, $value, $extended, $ref, $type );
}
1;
=head1 AUTHORS
This module is loosely inspired from both po-debconf and debconf code. The
adaptation for po4a was done by:
Martin Quinson (mquinson#debian.org)
=head1 COPYRIGHT AND LICENSE
Copyright © 2005 SPI, Inc.
This program is free software; you may redistribute it and/or modify it
under the terms of GPL (see the COPYING file).

View File

@ -0,0 +1,155 @@
#!/usr/bin/perl -w
# Po4a::NewsDebian.pm
#
# extract and translate translatable strings from a NEWS.Debian documents
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
#
########################################################################
=encoding UTF-8
=head1 NAME
Locale::Po4a::NewsDebian - convert NEWS.Debian documents from/to PO files
=head1 DESCRIPTION
The po4a (PO for anything) project goal is to ease translations (and more
interestingly, the maintenance of translations) using gettext tools on
areas where they were not expected like documentation.
Locale::Po4a::NewsDebian is a module to help the translation of the
NEWS.Debian files into other [human] languages. Those files are where
maintainer are supposed to write the important news about their package.
=head1 OPTIONS ACCEPTED BY THIS MODULE
NONE.
=head1 STATUS OF THIS MODULE
Not tested.
A finer split of the entries may be preferable (search for /^ */, for
example), but this version is more robust and NEWS.Debian entries are not
supposed to change that often.
=cut
package Locale::Po4a::NewsDebian;
use 5.006;
use strict;
use warnings;
require Exporter;
use vars qw(@ISA @EXPORT);
@ISA = qw(Locale::Po4a::TransTractor);
@EXPORT = qw();
use Locale::Po4a::TransTractor;
use Locale::Po4a::Common;
sub initialize { }
sub parse {
my $self = shift;
my ($blanklines) = (""); # We want to preserve the blank lines inside the entry, and strip the extrem ones
my ($body) = ""; # the accumulated paragraph
my ($bodyref) = "";
my ($bodytype) = "";
my ( $line, $lref );
# main loop
( $line, $lref ) = $self->shiftline();
print "seen >>$line<<\n" if $self->{options}{'debug'};
while ( defined($line) ) {
# Begining of an entry
if ( $line =~ m/^(\w[-+0-9a-z.]*) \(([^\(\) \t]+)\)((\s+[-0-9a-z]+)+)\;/i ) {
die wrap_ref_mod( $lref, "po4a::newsdebian",
dgettext( "po4a", "Begin of a new entry before the end of previous one" ) )
if ( length($body) );
$self->pushline( $line . "\n" );
# Signature of this entry
$bodyref = $lref;
$bodytype = $line;
# eat all leading empty lines
( $line, $lref ) = $self->shiftline();
while ( defined($line) && $line =~ m/^\s*$/ ) {
print "Eat >>$line<<\n" if $self->{options}{'debug'};
( $line, $lref ) = $self->shiftline();
}
# ups, ate one line too much. Put it back.
$self->unshiftline( $line, $lref );
# get ready to read the entry (cleanups)
$blanklines = "";
# End of current entry
} elsif ( $line =~ m/^ \-\- (.*) <(.*)> .*$/ )
{ #((\w+\,\s*)?\d{1,2}\s+\w+\s+\d{4}\s+\d{1,2}:\d\d:\d\d\s+[-+]\d{4}(\s+\([^\\\(\)]\))?) *$/) {
$self->translate( $body, $bodyref, $bodytype, wrap => 0 );
$body = "";
# non-specific line
} else {
if ( $line =~ /^\s*$/ ) {
$blanklines .= "$line";
} else {
$body .= $blanklines . $line;
$blanklines = "";
}
}
( $line, $lref ) = $self->shiftline();
print "seen >>" . ( $line || '' ) . "<<\n" if $self->{options}{'debug'};
}
}
1;
=head1 AUTHORS
This module is loosely inspired from /usr/lib/dpkg/parsechangelog/debian, which is:
Copyright © 1996 Ian Jackson. This is free software; see the GNU
General Public Licence version 2 or later for copying conditions. There
is NO warranty.
The adaptation for po4a was done by:
Martin Quinson (mquinson#debian.org)
=head1 COPYRIGHT AND LICENSE
Copyright © 1996 Ian Jackson.
Copyright © 2005 SPI, Inc.
This program is free software; you may redistribute it and/or modify it
under the terms of GPL (see the COPYING file).

120
lib/Locale/Po4a/Ini.pm Normal file
View File

@ -0,0 +1,120 @@
# Locale::Po4a::Ini -- Convert ini files to PO file, for translation.
#
# This program is free software; you may redistribute it and/or modify it
# under the terms of GPL (see COPYING).
#
############################################################################
# Modules and declarations
############################################################################
use Locale::Po4a::TransTractor qw(process new);
use Locale::Po4a::Common;
package Locale::Po4a::Ini;
use 5.006;
use strict;
use warnings;
require Exporter;
use vars qw(@ISA @EXPORT $AUTOLOAD);
@ISA = qw(Locale::Po4a::TransTractor);
@EXPORT = qw();
my $debug = 0;
sub initialize { }
sub parse {
my $self = shift;
my ( $line, $ref );
my $par;
LINE:
( $line, $ref ) = $self->shiftline();
while ( defined($line) ) {
chomp($line);
print STDERR "begin\n" if $debug;
if ( $line =~ /\"/ ) {
print STDERR "Start of line containing \".\n" if $debug;
# Text before the first quote
$line =~ m/(^[^"\r\n]*)"/;
my $pre_text = $1;
print STDERR " PreText=" . $pre_text . "\n" if $debug;
# The text for translation
$line =~ m/"([^\r\n]*)"/;
my $quoted_text = $1;
print STDERR " QuotedText=" . $quoted_text . "\n" if $debug;
# Text after last quote
$line =~ m/"([^"\n]*$)/;
my $post_text = $1;
print STDERR " PostText=" . $post_text . "\n" if $debug;
# Translate the string it
$par = $self->translate( $quoted_text, $ref, $pre_text );
# Escape the \n characters
$par =~ s/\n/\\n/g;
# Now push the result
$self->pushline( $pre_text . '"' . $par . '"' . $post_text . "\n" );
print STDERR "End of line containing \".\n" if $debug;
} else {
print STDERR "Other stuff\n" if $debug;
$self->pushline("$line\n");
}
# Reinit the loop
( $line, $ref ) = $self->shiftline();
}
}
##############################################################################
# Module return value and documentation
##############################################################################
1;
__END__
=encoding UTF-8
=head1 NAME
Locale::Po4a::Ini - convert INI files from/to PO files
=head1 DESCRIPTION
Locale::Po4a::Ini is a module to help the translation of INI files into other
[human] languages.
The module searches for lines of the following format and extracts the quoted
text:
identificator="text than can be translated"
NOTE: If the text is not quoted, it will be ignored.
=head1 SEE ALSO
L<Locale::Po4a::TransTractor(3pm)>, L<po4a(7)|po4a.7>
=head1 AUTHORS
Razvan Rusu <rrusu@bitdefender.com>
Costin Stroie <cstroie@bitdefender.com>
=head1 COPYRIGHT AND LICENSE
Copyright © 2006 BitDefender
This program is free software; you may redistribute it and/or modify it
under the terms of GPL (see the COPYING file).
=cut

View File

@ -0,0 +1,170 @@
# Locale::Po4a::KernelHelp -- Convert kernel configuration help from/to PO files
#
# This program is free software; you may redistribute it and/or modify it
# under the terms of GPL (see COPYING).
#
# See gettext documentation for more info about PO files.
############################################################################
# Modules and declarations
############################################################################
use Pod::Parser;
use Locale::Po4a::TransTractor qw(process new);
use Locale::Po4a::Common;
package Locale::Po4a::KernelHelp;
use 5.006;
use strict;
use warnings;
require Exporter;
use vars qw(@ISA @EXPORT $AUTOLOAD);
@ISA = qw(Locale::Po4a::TransTractor);
@EXPORT = qw(); # new process write read writepo readpo);
my $debug = 0;
sub initialize { }
sub parse {
my $self = shift;
my ( $line, $ref );
my $paragraph = ""; # Buffer where we put the paragraph while building
my ($status) = 0; # Syntax of KH is:
# description<nl>variable<nl>help text<nl><nl>
# Status will be:
# 0 1 2 3 0
my ( $desc, $variable );
LINE:
( $line, $ref ) = $self->shiftline();
while ( defined($line) ) {
chomp($line);
print STDERR "status=$status;Seen >>$line<<:" if $debug;
if ( $line =~ /^\#/ ) {
print STDERR "comment.\n" if $debug;
$self->pushline("$line\n");
} elsif ( $status == 0 ) {
if ( $line =~ /\S/ ) {
print STDERR "short desc.\n" if $debug;
$desc = $line;
$status++;
} else {
print STDERR "empty line.\n" if $debug;
$self->pushline("$line\n");
}
} elsif ( $status == 1 ) {
print STDERR "var name.\n" if $debug;
$variable = $line;
$status++;
$self->pushline( $self->translate( $desc, $ref, "desc_$variable" ) . "\n$variable\n" );
} elsif ( $status == 2 ) {
$line =~ s/^ //;
if ( $line =~ /\S/ ) {
print STDERR "paragraph line.\n" if $debug;
$paragraph .= $line . "\n";
} else {
print STDERR "end of paragraph.\n" if $debug;
$status++;
$paragraph = $self->translate( $paragraph, $ref, "helptxt_$variable" );
$paragraph =~ s/^/ /gm;
$self->pushline("$paragraph\n");
$paragraph = "";
}
} elsif ( $status == 3 ) {
if ( $line =~ s/^ // ) {
if ( $line =~ /\S/ ) {
print "begin of paragraph.\n" if $debug;
$paragraph = $line . "\n";
$status--;
} else {
print "end of config option.\n" if $debug;
$status = 0;
$self->pushline("\n");
}
} else {
$self->unshiftline( $line, $ref );
$status = 0;
}
} else {
die wrap_ref_mod( $ref, "po4a::kernelhelp", gettext("Syntax error") );
}
# Reinit the loop
( $line, $ref ) = $self->shiftline();
}
}
sub docheader {
return <<EOT;
#
# *****************************************************
# * GENERATED FILE, DO NOT EDIT *
# * THIS IS NO SOURCE FILE, BUT RESULT OF COMPILATION *
# *****************************************************
#
# This file was generated by po4a(7). Do not store it (in VCS, for example),
# but store the PO file used as source file by pod-translate.
#
# In fact, consider this as a binary, and the PO file as a regular .c file:
# If the PO get lost, keeping this translation up-to-date will be harder.
#
EOT
}
1;
##############################################################################
# Module return value and documentation
##############################################################################
1;
__END__
=encoding UTF-8
=head1 NAME
Locale::Po4a::KernelHelp - convert kernel configuration help from/to PO files
=head1 DESCRIPTION
Locale::Po4a::KernelHelp is a module to help the translation of
documentation for the Linux kernel configuration options into other [human]
languages.
=head1 STATUS OF THIS MODULE
This module is just written, and needs more tests. Most of the needed work
will concern the tools used to parse this file (and configure the kernel),
so that they accept to read the documentation from another (translated)
file.
=head1 SEE ALSO
L<Pod::Parser>,
L<Locale::Po4a::Man(3pm)>,
L<Locale::Po4a::Pod(3pm)>,
L<Locale::Po4a::TransTractor(3pm)>,
L<po4a(7)|po4a.7>
=head1 AUTHORS
Denis Barbier <barbier@linuxfr.org>
Martin Quinson (mquinson#debian.org)
=head1 COPYRIGHT AND LICENSE
Copyright © 2002 SPI, Inc.
This program is free software; you may redistribute it and/or modify it
under the terms of GPL (see the COPYING file).
=cut

397
lib/Locale/Po4a/LaTeX.pm Normal file
View File

@ -0,0 +1,397 @@
#!/usr/bin/perl -w
# Copyright © 2004, 2005 Nicolas FRANÇOIS <nicolas.francois@centraliens.net>
#
# This file is part of po4a.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with po4a; if not, write to the Free Software
# Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
#
########################################################################
=encoding UTF-8
=head1 NAME
Locale::Po4a::LaTeX - convert LaTeX documents and derivates from/to PO files
=head1 DESCRIPTION
The po4a (PO for anything) project goal is to ease translations (and more
interestingly, the maintenance of translations) using gettext tools on
areas where they were not expected like documentation.
Locale::Po4a::LaTeX is a module to help the translation of LaTeX documents into
other [human] languages. It can also be used as a base to build modules for
LaTeX-based documents.
This module contains the definitions of common LaTeX commands and
environments.
See the L<Locale::Po4a::TeX(3pm)|Locale::Po4a::TeX> manpage for the list
of recognized options.
=head1 SEE ALSO
L<Locale::Po4a::TeX(3pm)|Locale::Po4a::TeX>,
L<Locale::Po4a::TransTractor(3pm)|Locale::Po4a::TransTractor>,
L<po4a(7)|po4a.7>
=head1 AUTHORS
Nicolas François <nicolas.francois@centraliens.net>
=head1 COPYRIGHT AND LICENSE
Copyright © 2004, 2005 Nicolas FRANÇOIS <nicolas.francois@centraliens.net>.
This program is free software; you may redistribute it and/or modify it
under the terms of GPL (see COPYING file).
=cut
package Locale::Po4a::LaTeX;
use 5.006;
use strict;
use warnings;
require Exporter;
use vars qw($VERSION @ISA @EXPORT);
$VERSION = $Locale::Po4a::TeX::VERSION;
@ISA = qw(Locale::Po4a::TeX);
@EXPORT = qw();
use Locale::Po4a::TeX;
use subs qw(&generic_command
&parse_definition_file
&register_generic_command
&register_generic_environment);
*parse_definition_file = \&Locale::Po4a::TeX::parse_definition_file;
*generic_command = \&Locale::Po4a::TeX::generic_command;
*register_generic_command = \&Locale::Po4a::TeX::register_generic_command;
*register_generic_environment = \&Locale::Po4a::TeX::register_generic_environment;
use vars qw($RE_ESCAPE $ESCAPE
$no_wrap_environments
%commands %environments
%separated_command %separated_environment
%command_parameters %environment_parameters
%env_separators
@exclude_include);
*RE_ESCAPE = \$Locale::Po4a::TeX::RE_ESCAPE;
*ESCAPE = \$Locale::Po4a::TeX::ESCAPE;
*no_wrap_environments = \$Locale::Po4a::TeX::no_wrap_environments;
*commands = \%Locale::Po4a::TeX::commands;
*environments = \%Locale::Po4a::TeX::environments;
*separated_command = \%Locale::Po4a::TeX::separated_command;
*separated_environment = \%Locale::Po4a::TeX::separated_environment;
*env_separators = \%Locale::Po4a::TeX::env_separators;
*exclude_include = \@Locale::Po4a::TeX::exclude_include;
*command_parameters = \%Locale::Po4a::TeX::command_parameters;
*environment_parameters = \%Locale::Po4a::TeX::environment_parameters;
# documentclass:
# Only read the documentclass in order to find some po4a directives.
# FIXME: The documentclass could contain translatable strings.
# Maybe it should be implemented as \include{}.
register_generic_command("*documentclass,[]{}");
# We use register_generic_command to define the number and types of
# parameters. The function is then overwritten:
$commands{'documentclass'} = sub {
my $self = shift;
my ( $command, $variant, $args, $env ) = ( shift, shift, shift, shift );
my $no_wrap = shift;
# Only try to parse the file. We don't want to fail or parse this file
# if it is a standard documentclass.
my $name = ( $args->[0] eq '[' ) ? $args->[3] : $args->[1];
parse_definition_file( $self, $name . ".cls", 1 );
my ( $t, @e ) = generic_command( $self, $command, $variant, $args, $env, $no_wrap );
return ( $t, @$env );
};
# LaTeX 2
# I chose not to translate files, counters, lengths
register_generic_command("*addcontentsline,{}{}{_}");
register_generic_command("address,{_}"); # lines are seperated by \\
register_generic_command("*addtocontents,{}{_}");
register_generic_command("*addtocounter,{}{}");
register_generic_command("*addtolength,{}{}");
register_generic_command("*addvspace,{}");
register_generic_command("alph,{}"); # another language may not want this alphabet
register_generic_command("arabic,{}"); # another language may not want an arabic numbering
register_generic_command("*author,{_}"); # authors are separated by \and
register_generic_command("bibitem,[]{}");
register_generic_command("*bibliographystyle,{}"); # BibTeX
register_generic_command("*bibliography,{}"); # BibTeX
register_generic_command("*centerline,{_}");
register_generic_command("*caption,[]{_}");
register_generic_command("cc,{_}");
register_generic_command("circle,[]{}");
register_generic_command("cite,[_]{}");
register_generic_command("cline,{}");
register_generic_command("closing,{_}");
register_generic_command("dashbox,{}"); # followed by a (w,h) argument
register_generic_command("date,{_}");
register_generic_command("*enlargethispage,{}");
register_generic_command("ensuremath,{_}");
register_generic_command("*fbox,{_}");
register_generic_command("fnsymbol,{}");
register_generic_command("*footnote,[]{_}");
register_generic_command("*footnotemark,[]");
register_generic_command("*footnotetext,[]{_}");
register_generic_command("frac,{_}{_}");
register_generic_command("*frame,{_}");
register_generic_command("*framebox,[][]{_}"); # There is another form in picture environment
register_generic_command("*hbox,{}");
register_generic_command("*hspace,[]{}");
register_generic_command("*hyphenation,{_}"); # Translators may wish to add/remove words
register_generic_command("*include,{}");
#register_generic_command("includeonly,{}"); # should not be supported for now
register_generic_command("*index,{_}");
register_generic_command("*input,{}");
register_generic_command("*item,[_]");
register_generic_command("*label,{}");
register_generic_command("lefteqn,{_}");
register_generic_command("line,"); # The first argument is (x,y)
register_generic_command("*linebreak,[]");
register_generic_command("linethickness,{}");
register_generic_command("location,{_}");
register_generic_command("makebox,[][]{_}"); # There's another form in picture environment
register_generic_command("makelabels,{}");
register_generic_command("*markboth,[]{_}{_}");
register_generic_command("*markright,{_}");
register_generic_command("mathcal,{_}");
register_generic_command("mathop,{_}");
register_generic_command("mbox,{_}");
register_generic_command("multicolumn,{}{}{_}");
register_generic_command("multiput,"); # The first arguments are (x,y)(dx,dy)
register_generic_command("name,{_}");
register_generic_command("*newcommand,{}[][]{_}");
register_generic_command("*newcounter,{}[]");
register_generic_command("*newenvironment,{}[]{_}{_}");
register_generic_command("*newfont,{}{}");
register_generic_command("*newlength,{}");
register_generic_command("*newsavebox,{}");
register_generic_command("*newtheorem,{}[]{_}[]"); # Two forms, {}[]{_} or {}{_}[]
register_generic_command("nocite,{}");
register_generic_command("nolinebreak,[]");
register_generic_command("*nopagebreak,[]");
register_generic_command("opening,{_}");
register_generic_command("oval,"); # The first argument is (w,h)
register_generic_command("overbrace,{_}");
register_generic_command("overline,{_}");
register_generic_command("*pagebreak,[]");
register_generic_command("*pagenumbering,{_}");
register_generic_command("pageref,{}");
register_generic_command("*pagestyle,{}");
register_generic_command("*parbox,[][][]{}{_}");
register_generic_command("providecommand,{}[][]{_}");
register_generic_command("put,"); # The first argument is (x,y)
register_generic_command("raisebox,{}[][]{_}");
register_generic_command("ref,{}");
register_generic_command("*refstepcounter,{}");
register_generic_command("*renewcommand,{}[][]{_}");
register_generic_command("*renewenvironment,{}[]{_}{_}");
register_generic_command("roman,{}"); # another language may not want a roman numbering
register_generic_command("rule,[]{}{}");
register_generic_command("savebox,{}"); # Optional arguments in 2nd & 3rd position
register_generic_command("sbox,{}{_}");
register_generic_command("*setcounter,{}{}");
register_generic_command("*setlength,{}{}");
register_generic_command("*settodepth,{}{_}");
register_generic_command("*settoheight,{}{_}");
register_generic_command("*settowidth,{}{_}");
register_generic_command("shortstack,[]{_}");
register_generic_command("signature,{_}");
register_generic_command("sqrt,[_]{_}");
register_generic_command("stackrel,{_}{_}");
register_generic_command("stepcounter,{}");
register_generic_command("*subfigure,[_]{_}");
register_generic_command("symbol,{_}");
register_generic_command("telephone,{_}");
register_generic_command("thanks,{_}");
register_generic_command("*thispagestyle,{}");
register_generic_command("*title,{_}");
register_generic_command("typeout,{_}");
register_generic_command("typein,[]{_}");
register_generic_command("twocolumn,[_]");
register_generic_command("underbrace,{_}");
register_generic_command("underline,{_}");
register_generic_command("*usebox,{}");
register_generic_command("usecounter,{}");
register_generic_command("*usepackage,[]{}");
register_generic_command("value,{}");
register_generic_command("vector,"); # The first argument is (x,y)
register_generic_command("vphantom,{_}");
register_generic_command("*vspace,[]{}");
register_generic_command("*vbox,{}");
register_generic_command("*vcenter,{}");
register_generic_command("*part,[_]{_}");
register_generic_command("*chapter,[_]{_}");
register_generic_command("*section,[_]{_}");
register_generic_command("*subsection,[_]{_}");
register_generic_command("*subsubsection,[_]{_}");
register_generic_command("*paragraph,[_]{_}");
register_generic_command("*subparagraph,[_]{_}");
register_generic_command("textrm,{_}");
register_generic_command("textit,{_}");
register_generic_command("emph,{_}");
register_generic_command("textmd,{_}");
register_generic_command("textbf,{_}");
register_generic_command("textup,{_}");
register_generic_command("textsl,{_}");
register_generic_command("textsf,{_}");
register_generic_command("textsc,{_}");
register_generic_command("texttt,{_}");
register_generic_command("textnormal,{_}");
register_generic_command("mathrm,{_}");
register_generic_command("mathsf,{_}");
register_generic_command("mathtt,{_}");
register_generic_command("mathit,{_}");
register_generic_command("mathnormal,{_}");
register_generic_command("mathversion,{}");
register_generic_command("*contentspage,");
register_generic_command("*tablelistpage,");
register_generic_command("*figurepage,");
register_generic_command("*PassOptionsToPackage,{}{}");
register_generic_command("*ifthenelse,{}{_}{_}");
# graphics
register_generic_command("*includegraphics,[]{}");
register_generic_command("*graphicspath,{}");
register_generic_command("*resizebox,{}{}{_}");
register_generic_command("*scalebox,{}{_}");
register_generic_command("*rotatebox,{}{_}");
# url
register_generic_command("UrlFont,{}");
register_generic_command("*urlstyle,{}");
# hyperref
register_generic_command("href,{}{_}"); # 1:URL
register_generic_command("url,{}"); # URL
register_generic_command("nolinkurl,{}"); # URL
register_generic_command("hyperbaseurl,{}"); # URL
register_generic_command("hyperimage,{}"); # URL
register_generic_command("hyperdef,{}{}{_}"); # 1:category, 2:name
register_generic_command("hyperref,{}{}{}{_}"); # 1:URL, 2:category, 3:name
register_generic_command("hyperlink,{}{_}"); # 1:name
register_generic_command("*hypersetup,{_}");
register_generic_command("hypertarget,{}{_}"); # 1:name
register_generic_command("autoref,{}"); # 1:label
register_generic_command("*selectlanguage,{}");
# color
register_generic_command("*definecolor,{}{}{}");
register_generic_command("*textcolor,{}{_}");
register_generic_command("*colorbox,{}{_}");
register_generic_command("*fcolorbox,{}{}{_}");
register_generic_command("*pagecolor,{_}");
register_generic_command("*color,{}");
# equations/theorems
register_generic_command("*qedhere,");
register_generic_command("*qedsymbol,");
register_generic_command("*theoremstyle,{}");
register_generic_command("*proclaim,{_}");
register_generic_command("*endproclaim,");
register_generic_command("*shoveleft,{_}");
register_generic_command("*shoveright,{_}");
# commands without arguments. This is better than untranslated or
# translate_joined because the number of arguments will be checked.
foreach (
qw(a *appendix *backmatter backslash *baselineskip *baselinestretch bf
*bigskip boldmath cal cdots *centering *cleardoublepage *clearpage
ddots dotfill em flushbottom *footnotesize frenchspacing
*frontmatter *glossary *hfill *hline hrulefill huge Huge indent it
kill large Large LARGE ldots left linewidth listoffigures
listoftables *mainmatter *makeatletter *makeglossary *makeindex
*maketitle *medskip *newline *newpage noindent nonumber *normalsize
not *null *onecolumn *par parindent *parskip *printindex protect ps
pushtabs *qquad *quad raggedbottom raggedleft raggedright right rm
sc scriptsize sf sl small *smallskip *startbreaks *stopbreaks
*tableofcontents textwidth textheight tiny today tt unitlength
vdots verb *vfill *vline *fussy *sloppy
aleph hbar imath jmath ell wp Re Im prime nabla surd angle forall
exists partial infty triangle Box Diamond flat natural sharp
clubsuit diamondsuit heartsuit spadesuit dag ddag S P copyright
pounds Delta ASCII
rmfamily itshape mdseries bfseries upshape slshape sffamily scshape
ttfamily *normalfont width height depth totalheight
*fboxsep *fboxrule
*itemi *itemii *itemiii *itemiv
*theitemi *theitemii *theitemiii *theitemiv)
)
{
register_generic_command("$_,");
}
# standard environments.
# FIXME: All these definitions should be re-checked
foreach (
qw(abstract align align* cases center description displaymath document enumerate
eqnarray eqnarray* equation equation* flushleft flushright footnotesize itemize
letter lrbox multline multline* proof quotation quote
sloppypar tabbing theorem titlepage
trivlist verbatim verbatim* verse wrapfigure)
)
{
register_generic_environment("$_,");
}
register_generic_environment("tabular,[]{}");
register_generic_environment("tabular*,{}{}");
register_generic_environment("tabularx,{}{}");
register_generic_environment("multicols,{}");
register_generic_environment("list,{_}{}");
register_generic_environment("array,[]{}");
register_generic_environment("figure,[]");
register_generic_environment("minipage,[]{}");
register_generic_environment("picture,{}{}");
register_generic_environment("table,[]");
register_generic_environment("thebibliography,{_}");
# Commands and environments with separators.
# & is the cell separator, \\ is the line separator
# '\' is escaped twice
$env_separators{'array'} = $env_separators{'tabular'} = $env_separators{'tabularx'} = "(?:&|\\\\\\\\|\\\\hline)";
$env_separators{'trivlist'} = $env_separators{'list'} = $env_separators{'description'} = $env_separators{'enumerate'} =
$env_separators{'itemize'} = "\\\\item";
$env_separators{'thebibliography'} = "\\\\bibitem";
$env_separators{'displaymath'} = $env_separators{'eqnarray'} = $env_separators{'eqnarray*'} =
$env_separators{'flushleft'} = $env_separators{'flushright'} = $env_separators{'center'} =
$env_separators{'author{#1}'} = $env_separators{'title{#1}'} = "\\\\\\\\";
# tabbing
1;

2803
lib/Locale/Po4a/Man.pm Normal file

File diff suppressed because it is too large Load Diff

1823
lib/Locale/Po4a/Po.pm Normal file

File diff suppressed because it is too large Load Diff

328
lib/Locale/Po4a/Pod.pm Normal file
View File

@ -0,0 +1,328 @@
# Locale::Po4a::Pod -- Convert POD data to PO file, for translation.
#
# This program is free software; you may redistribute it and/or modify it
# under the terms of GPL (see COPYING file).
#
# This module converts POD to PO file, so that it becomes possible to
# translate POD formatted documentation. See gettext documentation for
# more info about PO files.
############################################################################
# Modules and declarations
############################################################################
use Pod::Parser;
use Locale::Po4a::TransTractor qw(process new get_out_charset);
package Locale::Po4a::Pod;
use 5.006;
use strict;
use warnings;
require Exporter;
use vars qw(@ISA);
@ISA = qw(Locale::Po4a::TransTractor Pod::Parser);
use Carp qw(croak confess);
sub initialize { }
sub translate {
my ( $self, $str, $ref, $type ) = @_;
my (%options) = @_;
$str = $self->pre_trans( $str, $ref, $type );
$str = $self->SUPER::translate( $str, $ref, $type, %options );
$str = $self->post_trans( $str, $ref, $type );
return $str;
}
sub pre_trans {
my ( $self, $str, $ref, $type ) = @_;
return $str;
}
sub post_trans {
my ( $self, $str, $ref, $type ) = @_;
# Change ascii non-breaking space to POD one
my $nbs_out = "\xA0";
my $enc_length = Encode::from_to( $nbs_out, "latin1", $self->get_out_charset );
if ( defined $enc_length ) {
while ( $str =~ m/(^|.*\s)(\S+?)\Q$nbs_out\E(\S+?)(\s.*$|$)/s ) {
my ( $begin, $m1, $m2, $end ) = ( $1, $2, $3, $4 );
$str = ( defined $begin ) ? $begin : "";
# Remove the non-breaking spaces in the string that will be
# between S<...>
$m2 =~ s/\Q$nbs_out\E/ /g;
$str .= "S<$m1 $m2>";
$str .= ( defined $end ) ? $end : "";
}
}
return $str;
}
sub command {
my ( $self, $command, $paragraph, $line_num ) = @_;
# print STDOUT "cmd: '$command' '$paragraph' at $line_num\n";
if ( $command eq 'back'
|| $command eq 'cut'
|| $command eq 'pod' )
{
$self->pushline("=$command\n\n");
} elsif ( $command eq 'over' ) {
$self->pushline( "=$command $paragraph" . ( length($paragraph) ? "" : "\n\n" ) );
} elsif ( $command eq 'encoding' ) {
my $charset = $paragraph;
$charset =~ s/^\s*(.*?)\s*$/$1/s;
$self->detected_charset($charset)
# The =encoding line will be added by docheader
} else {
$paragraph = $self->translate( $paragraph, $self->{DOCPOD}{refname} . ":$line_num", "=$command", "wrap" => 1 );
$self->pushline("=$command $paragraph\n\n");
}
}
sub verbatim {
my ( $self, $paragraph, $line_num ) = @_;
# print "verb: '$paragraph' at $line_num\n";
if ( $paragraph eq "\n" ) {
$self->pushline("$paragraph\n");
return;
}
$paragraph = $self->translate( $paragraph, $self->{DOCPOD}{refname} . ":$line_num", "verbatim" );
$paragraph =~ s/\n$//m;
$self->pushline("$paragraph\n");
}
sub textblock {
my ( $self, $paragraph, $line_num ) = @_;
# print "text: '$paragraph' at $line_num\n";
if ( $paragraph eq "\n" ) {
$self->pushline("$paragraph\n");
return;
}
# Fix a pretty damned bug.
# Podlators don't wrap explicitelly the text, and groff won't seem to
# wrap any line begining with a space. So, we have to consider as
# verbatim not only the paragraphs whose first line is indented, but
# the paragraph containing an indented line.
# That way, we'll declare more paragraphs as verbatim than needed, but
# that's harmless (only less confortable for translators).
if ( $paragraph =~ m/^[ \t]/m ) {
$self->verbatim( $paragraph, $line_num );
return;
}
$paragraph = $self->translate( $paragraph, $self->{DOCPOD}{refname} . ":$line_num", 'textblock', "wrap" => 1 );
$paragraph =~ s/ *\n/ /gm; # Unwrap the content, to ensure that C<> markup is not split on several lines
$self->pushline("$paragraph\n\n");
}
sub end_pod { }
sub read {
my ( $self, $filename, $refname ) = @_;
push @{ $self->{DOCPOD}{infile} }, ( $filename, $refname );
$self->Locale::Po4a::TransTractor::read( $filename, $refname );
}
sub parse {
my $self = shift;
my @list = @{ $self->{DOCPOD}{infile} };
while ( scalar @list ) {
my ( $filename, $refname ) = ( shift @list, shift @list );
$self->{DOCPOD}{refname} = $refname;
$self->parse_from_file($filename);
}
}
sub docheader {
my $self = shift;
my $encoding = $self->get_out_charset();
if ( ( defined $encoding )
and ( length $encoding )
and ( $encoding ne "ascii" ) )
{
$encoding = "\n=encoding $encoding\n";
} else {
$encoding = "";
}
return <<EOT;
*****************************************************
* GENERATED FILE, DO NOT EDIT *
* THIS IS NO SOURCE FILE, BUT RESULT OF COMPILATION *
*****************************************************
This file was generated by po4a(7). Do not store it (in VCS, for example),
but store the PO file used as source file by po4a-translate.
In fact, consider this as a binary, and the PO file as a regular .c file:
If the PO get lost, keeping this translation up-to-date will be harder.
$encoding
EOT
}
1;
##############################################################################
# Module return value and documentation
##############################################################################
1;
__END__
=encoding UTF-8
=head1 NAME
Locale::Po4a::Pod - convert POD data from/to PO files
=head1 SYNOPSIS
use Locale::Po4a::Pod;
my $parser = Locale::Po4a::Pod->new (sentence => 0, width => 78);
# Read POD from STDIN and write to STDOUT.
$parser->parse_from_filehandle;
# Read POD from file.pod and write to file.txt.
$parser->parse_from_file ('file.pod', 'file.txt');
=head1 DESCRIPTION
Locale::Po4a::Pod is a module to help the translation of documentation in
the POD format (the preferred language for documenting Perl) into other
[human] languages.
=head1 STATUS OF THIS MODULE
I think that this module is rock stable, and there is only one known bug
with F</usr/lib/perl5/Tk/MainWindow.pod> (and some other
pages, see below) which contains:
C<" #n">
Lack of luck, in the po4a version, this was split on the space by the
wrapping. As result, in the original version, the man page contains
" #n"
and mine contains
"" #n""
which is logic since CE<lt>foobarE<gt> is rewritten "foobar".
Complete list of pages having this problem on my box (from 564 pages; note
that it depends on the chosen wrapping column):
/usr/lib/perl5/Tk/MainWindow.pod
/usr/share/perl/5.8.0/overload.pod
/usr/share/perl/5.8.0/pod/perlapi.pod
/usr/share/perl/5.8.0/pod/perldelta.pod
/usr/share/perl/5.8.0/pod/perlfaq5.pod
/usr/share/perl/5.8.0/pod/perlpod.pod
/usr/share/perl/5.8.0/pod/perlre.pod
/usr/share/perl/5.8.0/pod/perlretut.pod
=head1 INTERNALS
As a derived class from Pod::Parser, Locale::Po4a::Pod supports the same
methods and interfaces. See L<Pod::Parser> for all the details; briefly,
one creates a new parser with C<< Locale::Po4a::Pod->new() >> and then
calls either parse_from_filehandle() or parse_from_file().
new() can take options, in the form of key/value pairs, that control the
behavior of the parser. The recognized options common to all Pod::Parser
children are:
=over 4
=item B<alt>
If set to a true value, selects an alternate output format that, among other
things, uses a different heading style and marks B<=item> entries with a
colon in the left margin. Defaults to false.
=item B<code>
If set to a true value, the non-POD parts of the input file will be included
in the output. Useful for viewing code documented with POD blocks with the
POD rendered and the code left intact.
=item B<indent>
The number of spaces to indent regular text, and the default indentation for
B<=over> blocks. Defaults to 4.
=item B<loose>
If set to a true value, a blank line is printed after a B<=head1> heading.
If set to false (the default), no blank line is printed after B<=head1>,
although one is still printed after B<=head2>. This is the default because
it's the expected formatting for manual pages; if you're formatting
arbitrary text documents, setting this to true may result in more pleasing
output.
=item B<quotes>
Sets the quote marks used to surround CE<lt>> text. If the value is a
single character, it is used as both the left and right quote; if it is two
characters, the first character is used as the left quote and the second as
the right quote; and if it is four characters, the first two are used as
the left quote and the second two as the right quote.
This may also be set to the special value B<none>, in which case no quote
marks are added around CE<lt>> text.
=item B<sentence>
If set to a true value, Locale::Po4a::Pod will assume that each sentence
ends in two spaces, and will try to preserve that spacing. If set to
false, all consecutive whitespace in non-verbatim paragraphs is compressed
into a single space. Defaults to true.
=item B<width>
The column at which to wrap text on the right-hand side. Defaults to 76.
=back
=head1 SEE ALSO
L<Pod::Parser>,
L<Locale::Po4a::Man(3pm)>,
L<Locale::Po4a::TransTractor(3pm)>,
L<po4a(7)|po4a.7>
=head1 AUTHORS
Denis Barbier <barbier@linuxfr.org>
Martin Quinson (mquinson#debian.org)
=head1 COPYRIGHT AND LICENSE
Copyright © 2002 SPI, Inc.
This program is free software; you may redistribute it and/or modify it
under the terms of GPL (see the COPYING file).
=cut

525
lib/Locale/Po4a/RubyDoc.pm Normal file
View File

@ -0,0 +1,525 @@
# Locale::Po4a::RubyDoc -- Convert Ruby Document data to PO file, for translation
# version 0.15
#
# Copyright © 2016-2017 Francesco Poli <invernomuto@paranoici.org>
#
# This work is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This work is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this work; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#
#
# Parts of the code (such as many regular expressions) were adapted
# from the source of rdtool, under the terms of the GNU General Public
# License, version 2 or later.
# These parts are originally:
# Copyright © 2004 MoonWolf <moonwolf@moonwolf.com>
# Copyright © 2011-2012 Youhei SASAKI <uwabami@gfd-dennou.org>
#
# The initialize code was adapted from the source of Locale::Po4a::Text,
# under the terms of the GNU General Public License, version 2 or later.
# This code was originally:
# Copyright © 2005-2008 Nicolas FRANÇOIS <nicolas.francois@centraliens.net>
#
############################################################################
#
# This module converts Ruby Document (RD) format to PO files, so that Ruby
# Document formatted texts may be translated. See gettext documentation
# for more details about PO files.
#
############################################################################
package Locale::Po4a::RubyDoc;
use Locale::Po4a::TransTractor qw(process new);
use Locale::Po4a::Common;
use 5.006;
use strict;
use warnings;
require Exporter;
use vars qw(@ISA);
@ISA = qw(Locale::Po4a::TransTractor);
######################
# Global variables #
######################
my $insiderubydoc = 0;
#############
# Methods #
#############
sub initialize {
my $self = shift;
my %options = @_;
$self->{options}{'debug'} = 1;
$self->{options}{'verbose'} = 1;
$self->{options}{'puredoc'} = 0;
foreach my $opt ( keys %options ) {
die wrap_mod( "po4a::rubydoc", dgettext( "po4a", "Unknown option: %s" ), $opt )
unless exists $self->{options}{$opt};
$self->{options}{$opt} = $options{$opt};
}
if ( defined $options{'puredoc'} ) {
# initially assume to be already inside the Ruby Document
$insiderubydoc = 1;
} else {
# initially assume to be outside the Ruby Document
$insiderubydoc = 0;
}
}
sub docheader {
return <<EOT;
#
# *****************************************************
# * GENERATED FILE, DO NOT EDIT *
# * THIS IS NO SOURCE FILE, BUT RESULT OF COMPILATION *
# *****************************************************
#
# This file was generated by po4a-translate(1). Do not store it (in VCS,
# for example), but store the PO file used as source file by po4a-translate.
#
# In fact, consider this as a binary, and the PO file as a regular source file:
# If the PO gets lost, keeping this translation up-to-date will be harder.
#
EOT
}
sub parse {
my $self = shift;
# start with baseline and firstindent corresponding to no indentation
my $baseline = 0;
my $firstindent = 0;
# start in non-verbatim mode
my $verbmode = 0;
# we have not yet seen any Term, hence we are not yet waiting for a
# Description
my $waitfordesc = 0;
my $methodterm = "";
# flag to remember that we have reached the end of the document
my $eof = 0;
PARAGRAPH: while () {
# start accumulating a new paragraph and corresponding variables
my ( $para, $pref, $ptype, $pwrap, $symbol, $tail ) = ( "", "", "", 1, "", "" );
LINE: while () {
# fetch next line and its reference
my ( $line, $lref ) = $self->shiftline();
unless ( defined($line) ) {
# we reached the end of the document
$eof = 1;
last LINE;
}
if ( $line =~ /^=begin\s*(\bRD\b.*)?\s*$/ ) {
# we are entering a Ruby Document part
$insiderubydoc = 1;
$baseline = 0;
$verbmode = 0;
$waitfordesc = 0;
$self->pushline($line);
next PARAGRAPH;
}
if ( $line =~ /^=end/ ) {
# we are exiting a Ruby Document part
$insiderubydoc = 0;
$baseline = 0;
$verbmode = 0;
$waitfordesc = 0;
$tail = $line;
last LINE;
}
# do nothing while outside the Ruby Document
next PARAGRAPH unless ($insiderubydoc);
# we encountered a Comment: ignore it entirely
next LINE if ( $line =~ /^#/ );
if ( $line =~ /^(={1,4})(?!=)\s*(?=\S)(.*)/
or $line =~ /^(\+{1,2})(?!\+)\s*(?=\S)(.*)/ )
{
# we encountered a Headline: this is a paragraph on its own
if ( length($para) ) {
# we already have some paragraph to be processed:
# reput the current line in input and end paragraph
$self->unshiftline( $line, $lref );
last LINE;
} else {
# we are at the beginning of a paragraph, but a Headline
# is a single-line paragraph: define the variables
# and end paragraph
$symbol = "$1 ";
$para = $2;
$pref = $lref;
$ptype = "Headline $1";
$baseline = 0;
$verbmode = 0;
$waitfordesc = 0;
last LINE;
}
}
if ( $line =~ /^<<<\s*(\S+)/ ) {
# we encountered an Include line: end paragraph
$tail = $line;
last LINE;
}
# compute indentation
$line =~ /^(\s*)/;
my $indent = length($1);
if ($verbmode) {
# use verbatim mode rules
# -----------------------
if ( $indent >= $firstindent ) {
# indentation matches first line or is deeper:
# the Verbatim goes on
$para .= $line;
next LINE;
} else {
# indentation is shallower than first line:
# reput the current line in input, exit verbatim mode
# and end paragraph
$self->unshiftline( $line, $lref );
$verbmode = 0;
$waitfordesc = 0;
last LINE;
}
} else {
# use non-verbatim mode rules
# ---------------------------
if ( $line =~ /^\s*$/ ) {
# we encountered a WHITELINE: end paragraph
$tail = $line;
last LINE;
}
if ( $line =~ /^(\s*)\*(\s*)(.*)/ ) {
# we encountered the first line of a ItemListItem
if ( length($para) ) {
# we already have some paragraph to be processed:
# reput the current line in input and end paragraph
$self->unshiftline( $line, $lref );
last LINE;
} else {
# we are at the beginning of a paragraph:
# define the variables
$symbol = "$1*$2";
$para .= $3;
$pref = $lref;
$ptype = "ItemListItem *";
$baseline = length($symbol);
$waitfordesc = 0;
next LINE;
}
}
if ( $line =~ /^(\s*)(\(\d+\))(\s*)(.*)/ ) {
# we encountered the first line of an EnumListItem
if ( length($para) ) {
# we already have some paragraph to be processed:
# reput the current line in input and end paragraph
$self->unshiftline( $line, $lref );
last LINE;
} else {
# we are at the beginning of a paragraph:
# define the variables
$symbol = "$1$2$3";
$para .= $4;
$pref = $lref;
$ptype = "EnumListItem $2";
$baseline = length($symbol);
$waitfordesc = 0;
next LINE;
}
}
if ( $line =~ /^(\s*):(\s*)(.*)/ ) {
# we encountered the Term line of a DescListItem
if ( length($para) ) {
# we already have some paragraph to be processed:
# reput the current line in input and end paragraph
$self->unshiftline( $line, $lref );
last LINE;
} else {
# we are at the beginning of a paragraph, but the Term
# part of a DescListItem is a single-line paragraph:
# define the variables and end paragraph
$symbol = "$1:$2";
$para = $3;
$pref = $lref;
$ptype = "DescListItem Term :";
$baseline = length($symbol);
$waitfordesc = 1;
last LINE;
}
}
if ( $line =~ /^(\s*)---(?!-|\s*$)(\s*)(.*)/ ) {
# we encountered the Term line of a MethodListItem
if ( length($para) ) {
# we already have some paragraph to be processed:
# reput the current line in input and end paragraph
$self->unshiftline( $line, $lref );
last LINE;
} else {
# we are at the beginning of a paragraph, but the Term
# part of a MethodListItem is a single-line paragraph;
# moreover, it's not translatable: end paragraph
$baseline = length("$1---$2");
$waitfordesc = 2;
$tail = $line;
$methodterm = "--- $3";
last LINE;
}
}
# we apparently encountered a STRINGLINE
if ( length($para) ) {
# we already have some paragraph to be processed:
if ( $indent == $baseline ) {
# indentation matches baseline:
# append the STRINGLINE to the paragraph
$para .= $line;
} else {
# indentation differs from baseline:
# reput the current line in input and end paragraph
$self->unshiftline( $line, $lref );
last LINE;
}
} else {
# we are at the beginning of a paragraph:
# define the variables
if ($waitfordesc) {
# we were waiting for a DescListItem Description:
# we have just found it
if ( $waitfordesc == 1 ) {
$ptype = "DescListItem Description";
} else {
$ptype = "MethodListItem Description $methodterm";
}
$baseline = $indent;
$waitfordesc = 0;
# reproduce the original indentation
$symbol = " " x $indent;
} else {
if ( $indent > $baseline ) {
# indentation is deeper than baseline:
# we are entering a Verbatim
$verbmode = 1;
$ptype = "Verbatim";
$pwrap = 0;
$firstindent = $indent;
} else {
# indentation is not deeper than baseline:
# this is a TextBlock
$ptype = "TextBlock";
$baseline = $indent;
# reproduce the original indentation
$symbol = " " x $indent;
}
}
$para .= $line;
$pref = $lref;
}
}
}
if ( length($para) ) {
# set wrap column at 76 - identation, but never less than 26
my $ni = length($symbol);
my $wc = 76 - $ni;
$wc = 26 if ( $wc < 26 );
# get the translated paragraph
my $translated = $self->translate(
$para,
$pref,
$ptype,
'wrap' => $pwrap,
'wrapcol' => $wc
);
if ($pwrap) {
# reformat the translated paragraph
my $is = " " x $ni;
chomp $translated;
$translated =~ s/\n/\n$is/g;
$translated .= "\n";
}
# push the paragraph to the translated document
$self->pushline( $symbol . $translated );
}
if ( length($tail) ) {
# push the non translatable tail to the translated document
$self->pushline($tail);
}
# stop processing, if we have already reached the end of the document
return if ($eof);
}
}
##########################
# Module documentation #
##########################
1;
__END__
=encoding UTF-8
=head1 NAME
Locale::Po4a::RubyDoc -- Convert Ruby Document data from/to PO files
=head1 DESCRIPTION
The po4a (PO for anything) project goal is to ease translations (and more
interestingly, the maintenance of translations) using gettext tools on
areas where they were not expected like documentation.
Locale::Po4a::RubyDoc is a module to help the translation of documentation in
the Ruby Document (RD) format (a language used to document Ruby) into other
[human] languages.
=head1 STATUS OF THIS MODULE
This module has been successfully tested on simple Ruby Document files
covering a good part of the format syntax.
A known limitation is that it fails to properly recognize the stacked
structure of input Ruby Document: this implies that when, for instance,
an EnumListItem consists of more than one Block, only the first Block
is actually recognized as EnumListItem, while the subsequent ones are
considered just as TextBlocks...
=head1 OPTIONS ACCEPTED BY THIS MODULE
This module supports the following option:
=over
=item B<puredoc>
Handle files entirely made of Ruby Document formatted text (without
any "=begin" line).
By default, this module only handles Ruby Document formatted text
between "=begin" and "=end" lines (hence ignoring, among other things,
everything that precedes the first "=begin" line).
=back
=head1 SEE ALSO
L<Locale::Po4a::TransTractor(3pm)>
=head1 AUTHORS
Francesco Poli <invernomuto@paranoici.org>
=head1 COPYRIGHT AND LICENSE
Copyright © 2016-2017 Francesco Poli <invernomuto@paranoici.org>
This work is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This work is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this work; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Parts of the code (such as many regular expressions) were adapted
from the source of rdtool, under the terms of the GNU General Public
License, version 2 or later.
These parts are originally:
Copyright © 2004 MoonWolf <moonwolf@moonwolf.com>
Copyright © 2011-2012 Youhei SASAKI <uwabami@gfd-dennou.org>
The initialize code was adapted from the source of Locale::Po4a::Text,
under the terms of the GNU General Public License, version 2 or later.
This code was originally:
Copyright © 2005-2008 Nicolas FRANÇOIS <nicolas.francois@centraliens.net>
=cut

1364
lib/Locale/Po4a/Sgml.pm Normal file

File diff suppressed because it is too large Load Diff

1756
lib/Locale/Po4a/TeX.pm Normal file

File diff suppressed because it is too large Load Diff

595
lib/Locale/Po4a/Texinfo.pm Normal file
View File

@ -0,0 +1,595 @@
#!/usr/bin/perl -w
# Copyright © 2004-2007 Nicolas FRANÇOIS <nicolas.francois@centraliens.net>
#
# This file is part of po4a.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with po4a; if not, write to the Free Software
# Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
#
########################################################################
=encoding UTF-8
=head1 NAME
Locale::Po4a::Texinfo - convert Texinfo documents and derivates from/to PO files
=head1 DESCRIPTION
The po4a (PO for anything) project goal is to ease translations (and more
interestingly, the maintenance of translations) using gettext tools on
areas where they were not expected like documentation.
Locale::Po4a::Texinfo is a module to help the translation of Texinfo documents into
other [human] languages.
This module contains the definitions of common Texinfo commands and environments.
Only the comments starting with 'TRANSLATORS' are added to the PO files to guide the translators.
=head1 STATUS OF THIS MODULE
This module is still beta.
Please send feedback and feature requests.
=head1 SEE ALSO
L<Locale::Po4a::TeX(3pm)|Locale::Po4a::TeX>,
L<Locale::Po4a::TransTractor(3pm)|Locale::Po4a::TransTractor>,
L<po4a(7)|po4a.7>
=head1 AUTHORS
Nicolas François <nicolas.francois@centraliens.net>
=head1 COPYRIGHT AND LICENSE
Copyright © 2004-2007 Nicolas FRANÇOIS <nicolas.francois@centraliens.net>.
This program is free software; you may redistribute it and/or modify it
under the terms of GPL (see COPYING file).
=cut
package Locale::Po4a::Texinfo;
use 5.006;
use strict;
use warnings;
require Exporter;
use vars qw($VERSION @ISA @EXPORT);
$VERSION = $Locale::Po4a::TeX::VERSION;
@ISA = qw(Locale::Po4a::TeX);
@EXPORT = qw();
use Locale::Po4a::Common;
use Locale::Po4a::TeX;
use subs qw(
&parse_definition_file
&register_generic_command &is_closed &translate_buffer
&register_verbatim_environment
&generic_command
&in_verbatim);
*parse_definition_file = \&Locale::Po4a::TeX::parse_definition_file;
*register_generic_command = \&Locale::Po4a::TeX::register_generic_command;
*register_verbatim_environment = \&Locale::Po4a::TeX::register_verbatim_environment;
*generic_command = \&Locale::Po4a::TeX::generic_command;
*is_closed = \&Locale::Po4a::TeX::is_closed;
*in_verbatim = \&Locale::Po4a::TeX::in_verbatim;
*translate_buffer = \&Locale::Po4a::TeX::translate_buffer;
use vars qw($RE_ESCAPE $ESCAPE
$RE_VERBATIM
$RE_COMMENT $RE_PRE_COMMENT
$no_wrap_environments $separated_commands
%commands %environments
%command_categories %separated
%env_separators %debug
%translate_buffer_env
@exclude_include @comments);
*RE_ESCAPE = \$Locale::Po4a::TeX::RE_ESCAPE;
*ESCAPE = \$Locale::Po4a::TeX::ESCAPE;
*RE_VERBATIM = \$Locale::Po4a::TeX::RE_VERBATIM;
*RE_COMMENT = \$Locale::Po4a::TeX::RE_COMMENT;
*RE_PRE_COMMENT = \$Locale::Po4a::TeX::RE_PRE_COMMENT;
*no_wrap_environments = \$Locale::Po4a::TeX::no_wrap_environments;
*separated_commands = \$Locale::Po4a::TeX::separated_commands;
*commands = \%Locale::Po4a::TeX::commands;
*environments = \%Locale::Po4a::TeX::environments;
*command_categories = \%Locale::Po4a::TeX::command_categories;
*separated = \%Locale::Po4a::TeX::separated;
*env_separators = \%Locale::Po4a::TeX::env_separators;
*debug = \%Locale::Po4a::TeX::debug;
*translate_buffer_env = \%Locale::Po4a::TeX::translate_buffer_env;
*exclude_include = \@Locale::Po4a::TeX::exclude_include;
*comments = \@Locale::Po4a::TeX::comments;
$ESCAPE = "\@";
$RE_ESCAPE = "\@";
$RE_VERBATIM = "\@example";
$RE_COMMENT = "\\\@(?:c|comment)\\b";
$RE_PRE_COMMENT = "(?<!\@)(?:\@\@)*";
my %break_line = ();
# translate_line_command indicate if the arguments to the command handled
# by line_command() should be translated:
# undefined: arguments are not translated
# 0: there should be no arguments
# 1: arguments should be translated
my %translate_line_command = ();
foreach (
qw/example smallexample tex display smalldisplay verbatim format smallformat
flushleft flushright lisp smalllisp ignore/
)
{
register_verbatim_environment($_);
$commands{$_} = \&environment_line_command;
$translate_line_command{$_} = 0; # There should be no arguments
$break_line{$_} = 1;
}
my $docheader_pushed = 0;
# The header shall not be written before the Texinfo header (which include
# the \input command that define the texinfo macros)
sub docheader {
return "";
}
sub push_docheader {
return if $docheader_pushed;
my $self = shift;
$self->pushline(<<END);
\@c ===========================================================================
\@c
\@c This file was generated with po4a. Translate the source file.
\@c
\@c ===========================================================================
END
$docheader_pushed = 1;
}
sub parse {
my $self = shift;
my ( $line, $ref );
my $paragraph = ""; # Buffer where we put the paragraph while building
my @env = (); # environment stack
my $t = "";
$docheader_pushed = 0;
LINE:
undef $self->{type};
( $line, $ref ) = $self->shiftline();
while ( defined($line) ) {
chomp($line);
$self->{ref} = "$ref";
if ( $line =~ /^\s*@\s*po4a\s*:/ ) {
parse_definition_line( $self, $line );
goto LINE;
}
my $closed = 1;
if ( !in_verbatim(@env) ) {
$closed = is_closed($paragraph);
}
# if (not $closed) {
# print "not closed. line: '$line'\n para: '$paragraph'\n";
# }
if ( $closed and $line =~ /^\s*$/ ) {
# An empty line. This indicates the end of the current
# paragraph.
$paragraph .= $line . "\n";
if ( length($paragraph) ) {
( $t, @env ) = translate_buffer( $self, $paragraph, undef, @env );
$self->pushline($t);
$paragraph = "";
}
} elsif ( $line =~ m/^\\input / ) {
if ( length($paragraph) ) {
( $t, @env ) = translate_buffer( $self, $paragraph, undef, @env );
$self->pushline($t);
$paragraph = "";
}
$self->pushline( $line . "\n" );
$self->push_docheader();
} elsif ( $line =~ m/^$RE_COMMENT/ ) {
if ( $line =~ m/^\@(?:c|comment).*?TRANSLATORS:(.*)$/ ) {
$self->add_comment($1);
}
$self->push_docheader();
$self->pushline( $line . "\n" );
} elsif ( $closed
and ( $line =~ /^@([^ ]*?)(?: +(.*))?$/ )
and ( defined $commands{$1} )
and ( $break_line{$1} ) )
{
if ( length($paragraph) ) {
( $t, @env ) = translate_buffer( $self, $paragraph, undef, @env );
$self->pushline($t);
$paragraph = "";
}
my $arg = $2;
my @args = ();
if ( defined $arg and length $arg ) {
# FIXME: keep the spaces ?
$arg =~ s/\s*$//s;
@args = ( " ", $arg );
}
( $t, @env ) = &{ $commands{$1} }( $self, $1, "", \@args, \@env, 1 );
$self->pushline( $t . "\n" );
} else {
# continue the same paragraph
$paragraph .= $line . "\n";
}
# Reinit the loop
( $line, $ref ) = $self->shiftline();
undef $self->{type};
}
if ( length($paragraph) ) {
( $t, @env ) = translate_buffer( $self, $paragraph, undef, @env );
$self->pushline($t);
$paragraph = "";
}
} # end of parse
sub line_command {
my $self = shift;
my ( $command, $variant, $args, $env ) = ( shift, shift, shift, shift );
my $no_wrap = shift;
print "line_command($command,$variant,@$args,@$env,$no_wrap)="
if ( $debug{'commands'} );
my $translated = $ESCAPE . $command;
my $line = $args->[1];
if ( defined $line and length $line ) {
if ( defined $translate_line_command{$command}
and $translate_line_command{$command} )
{
# $no_wrap could be forced to 1, but it should already be set
my ( $t, $e ) = $self->translate_buffer( $line, $no_wrap, @$env, $command );
$translated .= " " . $t;
} else {
$translated .= " " . $line;
}
}
print "($translated,@$env)\n"
if ( $debug{'commands'} );
return ( $translated, @$env );
}
sub defindex_line_command {
my $self = shift;
my ( $command, $variant, $args, $env ) = ( shift, shift, shift, shift );
my $no_wrap = shift;
print "line_command($command,$variant,@$args,@$env,$no_wrap)="
if ( $debug{'commands'} );
my $idx = $$args[1] . "index";
$commands{$idx} = \&line_command;
$break_line{$idx} = 1;
$translate_line_command{$idx} = 1;
return line_command( $self, $command, $variant, $args, $env, $no_wrap );
}
sub translate_buffer_menu {
my ( $self, $buffer, $no_wrap, @env ) = ( shift, shift, shift, @_ );
print STDERR "translate_buffer_menu($buffer,$no_wrap,@env)="
if ( $debug{'translate_buffer'} );
my $translated_buffer = "";
my $spaces = "";
if ( $buffer =~ m/(\s*)$/s ) {
$spaces = $1;
}
while ( $buffer =~ m/^(.*?)((?:\n|^)\* )(.*)$/s ) {
my $sep = $2;
$buffer = $3;
my ( $t, @e ) = $self->translate_buffer_menuentry( $1, $no_wrap, @env, "menuentry" );
$translated_buffer .= $t . $sep;
}
my ( $t, @e ) = $self->translate_buffer_menuentry( $buffer, $no_wrap, @env, "menuentry" );
$translated_buffer .= $t;
$translated_buffer .= $spaces;
print STDERR "($translated_buffer,@env)\n"
if ( $debug{'translate_buffer'} );
return ( $translated_buffer, @env );
}
$translate_buffer_env{"menu"} = \&translate_buffer_menu;
$translate_buffer_env{"detailmenu"} = \&translate_buffer_menu;
$translate_buffer_env{"direntry"} = \&translate_buffer_menu;
my $menu_width = 78;
my $menu_sep_width = 30;
sub translate_buffer_menuentry {
my ( $self, $buffer, $no_wrap, @env ) = ( shift, shift, shift, @_ );
print STDERR "translate_buffer_menuentry($buffer,$no_wrap,@env)="
if ( $debug{'translate_buffer'} );
my $translated_buffer = "";
if ( $buffer =~ m/^(.*?)(::)\s+(.*)$/s
or $buffer =~ m/^(.*?: .*?)(\.)\s+(.*)$/s )
{
my ( $name, $sep, $description ) = ( $1, $2, $3 );
my ( $t, @e ) = $self->translate_buffer( $name, $no_wrap, @env );
$translated_buffer = $t . $sep . " ";
my $l = length($translated_buffer) + 2;
if ( $l < $menu_sep_width - 1 ) {
$translated_buffer .= ' ' x ( $menu_sep_width - 1 - $l );
$l = $menu_sep_width - 1;
}
( $t, @e ) = $self->translate_buffer( $description, $no_wrap, @env );
# Replace newlines with space for proper wrapping
# See https://github.com/mquinson/po4a/issues/122
$t =~ s/\n/ /sg;
# Remove trailing spaces
$t =~ s/\s*$//;
$t = Locale::Po4a::Po::wrap( $t, $menu_width - $l - 2 );
my $spaces = ' ' x ( $l + 2 );
$t =~ s/\n/\n$spaces/sg;
$translated_buffer .= $t;
} else {
# FIXME: no-wrap if a line start by a space
my ( $t, @e ) = $self->translate_buffer( $buffer, $no_wrap, @env );
$translated_buffer = $t;
}
print STDERR "($translated_buffer,@env)\n"
if ( $debug{'translate_buffer'} );
return ( $translated_buffer, @env );
}
sub translate_buffer_ignore {
my ( $self, $buffer, $no_wrap, @env ) = ( shift, shift, shift, @_ );
print STDERR "translate_buffer_ignore($buffer,$no_wrap,@env);\n"
if ( $debug{'translate_buffer'} );
return ( $buffer, @env );
}
$translate_buffer_env{"ignore"} = \&translate_buffer_ignore;
foreach (
qw(appendix section cindex findex kindex opindex pindex tindex vindex subsection
dircategory subtitle include
exdent center unnumberedsec
heading unnumbered unnumberedsubsec
unnumberedsubsubsec appendixsec appendixsubsec
appendixsubsubsec majorheading chapheading subheading
subsubheading shorttitlepage
subsubsection top item itemx chapter settitle
title author)
)
{
$commands{$_} = \&line_command;
$break_line{$_} = 1;
$translate_line_command{$_} = 1;
}
foreach (
qw(c comment clear set setfilename setchapternewpage vskip synindex
syncodeindex need fonttextsize printindex headings finalout sp
definfoenclose)
)
{
$commands{$_} = \&line_command;
$break_line{$_} = 1;
}
foreach (qw(defcodeindex defindex)) {
$commands{$_} = \&defindex_line_command;
$break_line{$_} = 1;
}
# definfoenclose: command definition => translate?
foreach (
qw(insertcopying page bye summarycontents shortcontents contents
noindent)
)
{
$commands{$_} = \&line_command;
$break_line{$_} = 1;
$translate_line_command{$_} = 0;
}
foreach (
qw(defcv deffn
defivar defmac defmethod defop
defopt defspec deftp deftypecv
deftypefn deftypefun
deftypeivar deftypemethod
deftypeop deftypevar deftypevr
defun defvar defvr)
)
{
$commands{$_} = \&environment_line_command;
$translate_line_command{$_} = 1;
$break_line{$_} = 1;
}
foreach (
qw(defcvx deffnx defivarx defmacx defmethodx defopx defoptx
defspecx deftpx deftypecvx deftypefnx deftypefunx deftypeivarx
deftypemethodx deftypeopx deftypevarx deftypevrx defunx
defvarx defvrx)
)
{
$commands{$_} = \&line_command;
$translate_line_command{$_} = 1;
$break_line{$_} = 1;
}
foreach (
qw(titlefont w i r b sansserif sc slanted strong t cite email
footnote indicateurl emph ref xref pxref inforef kbd key
acronym),
# The following commands could cause problems since their arguments
# have a semantic and a translator could decide not to translate code but
# still translate theses short words if they appear in another context.
qw(file command dfn dmn option math code samp var)
)
{
register_generic_command("-$_,{_}");
}
register_generic_command("*anchor,{_}");
register_generic_command("*refill,");
$translate_line_command{'node'} = 1;
$no_wrap_environments .= " node";
$break_line{'node'} = 1;
# @node Comments, Minimum, Conventions, Overview
$commands{'node'} = sub {
my $self = shift;
my ( $command, $variant, $args, $env ) = ( shift, shift, shift, shift );
my $no_wrap = shift;
print "node($command,$variant,@$args,@$env,$no_wrap)="
if ( $debug{'commands'} );
my $translated = $ESCAPE . $command;
my $line = $args->[1];
if ( defined $line and length $line ) {
my @pointers = split( /, */, $line );
my @t;
foreach (@pointers) {
push @t, $self->translate( $_, $self->{ref}, $command, "wrap" => 0 );
}
$translated .= " " . join( ", ", @t );
}
print "($translated,@$env)\n"
if ( $debug{'commands'} );
return ( $translated, @$env );
};
sub environment_command {
my $self = shift;
my ( $command, $variant, $args, $env ) = ( shift, shift, shift, shift );
my $no_wrap = shift;
print "environment_command($command,$variant,@$args,@$env,$no_wrap)="
if ( $debug{'commands'} );
my ( $t, @e ) = ( "", () );
( $t, @e ) = generic_command( $self, $command, $variant, $args, $env, $no_wrap );
@e = ( @$env, $command );
print "($t,@e)\n"
if ( $debug{'commands'} );
return ( $t, @e );
}
sub environment_line_command {
my $self = shift;
my ( $command, $variant, $args, $env ) = ( shift, shift, shift, shift );
my $no_wrap = shift;
print "environment_command_line($command,$variant,@$args,@$env,$no_wrap)="
if ( $debug{'commands'} );
my ( $t, @e ) = ( "", () );
( $t, @e ) = line_command( $self, $command, $variant, $args, $env, $no_wrap );
@e = ( @$env, $command );
print "($t,@e)\n"
if ( $debug{'commands'} );
return ( $t, @e );
}
## push the environment in the environment stack, and do not translate
## the command
#sub push_environment {
# my $self = shift;
# my ($command,$variant,$args,$env) = (shift,shift,shift,shift);
# print "push_environment($command,$variant,@$args,@$env)="
# if ($debug{'environments'});
#
# my ($t,@e) = generic_command($self,$command,$variant,$args,$env);
#
# print "($t,@e)\n"
# if ($debug{'environments'});
# return ($t,@e);
#}
#
foreach (
qw(detailmenu menu titlepage group copying
documentdescription cartouche
direntry
ifdocbook ifhtml ifinfo ifplaintext iftex ifxml
ifnotdocbook ifnothtml ifnotinfo ifnotplaintext ifnottex ifnotxml)
)
{
$commands{$_} = \&environment_line_command;
$translate_line_command{$_} = 0;
$break_line{$_} = 1;
}
foreach (qw(enumerate multitable ifclear ifset)) {
$commands{$_} = \&environment_line_command;
$break_line{$_} = 1;
}
foreach (
qw(quotation smallquotation
indentedblock smallindentedblock
raggedright)
)
{
$commands{$_} = \&environment_line_command;
$translate_line_command{$_} = 1;
$break_line{$_} = 1;
}
$env_separators{'format'} = "(?:(?:^|\n)\\\*|END-INFO-DIR-ENTRY|START-INFO-DIR-ENTRY)";
$env_separators{'multitable'} = "(?:\@item|\@tab)";
my $end_command = $commands{'end'};
register_generic_command("*end, ");
$commands{'end'} = $end_command;
$break_line{'end'} = 1;
register_generic_command("*macro, ");
$commands{'macro'} = \&environment_command;
$break_line{'macro'} = 1;
register_generic_command("*itemize, ");
$commands{'itemize'} = \&environment_command;
$break_line{'itemize'} = 1;
register_generic_command("*table, ");
$commands{'table'} = \&environment_command;
$break_line{'table'} = 1;
# TODO: is_closed, use a regexp: \ does not escape the closing brace.
# TBC on LaTeX.
# In Texinfo, it appears with the "code" command. Maybe this command should
# be used as verbatim. (Expressions.texi)
# TODO: @include @ignore
# TBC: node Indices
1;

898
lib/Locale/Po4a/Text.pm Normal file
View File

@ -0,0 +1,898 @@
#!/usr/bin/perl -w
# Po4a::Text.pm
#
# extract and translate translatable strings from a text documents
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
#
########################################################################
=encoding UTF-8
=head1 NAME
Locale::Po4a::Text - convert text documents from/to PO files
=head1 DESCRIPTION
The po4a (PO for anything) project goal is to ease translations (and more
interestingly, the maintenance of translations) using gettext tools on
areas where they were not expected like documentation.
Locale::Po4a::Text is a module to help the translation of text documents into
other [human] languages.
Paragraphs are split on empty lines (or lines containing only spaces or
tabulations).
If a paragraph contains a line starting by a space (or tabulation), this
paragraph won't be rewrapped.
=cut
package Locale::Po4a::Text;
use 5.006;
use strict;
use warnings;
require Exporter;
use vars qw(@ISA @EXPORT);
@ISA = qw(Locale::Po4a::TransTractor);
@EXPORT = qw();
use Locale::Po4a::TransTractor;
use Locale::Po4a::Common;
use YAML::Tiny;
=head1 OPTIONS ACCEPTED BY THIS MODULE
These are this module's particular options:
=over
=item B<keyvalue>
Treat paragraphs that look like a key value pair as verbatim (with the no-wrap flag in the PO file).
Key value pairs are defined as a line containing one or more non-colon
and non-space characters followed by a colon followed by at least one
non-space character before the end of the line.
=cut
my $keyvalue = 0;
=item B<nobullets>
Deactivate the detection of bullets.
By default, when a bullet is detected, the bullet paragraph is not considered
as a verbatim paragraph (with the no-wrap flag in the PO file). Instead, the
corresponding paragraph is rewrapped in the translation.
=cut
my $bullets = 1;
=item B<tabs=>I<mode>
Specify how tabulations shall be handled. The I<mode> can be any of:
=over
=item B<split>
Lines with tabulations introduce breaks in the current paragraph.
=item B<verbatim>
Paragraph containing tabulations will not be re-wrapped.
=back
By default, tabulations are considered as spaces.
=cut
my $tabs = "";
=item B<breaks=>I<regex>
A regular expression matching lines which introduce breaks.
The regular expression will be anchored so that the whole line must match.
=cut
my $breaks;
=item B<debianchangelog>
Handle the header and footer of
released versions, which only contain non translatable informations.
=cut
my $debianchangelog = 0;
=item B<fortunes>
Handle the fortunes format, which separate fortunes with a line which
consists in '%' or '%%', and use '%%' as the beginning of a comment.
=cut
my $fortunes = 0;
=item B<markdown>
Handle some special markup in Markdown-formatted texts.
=cut
my $markdown = 0;
=item B<yfm_keys> (markdown-only)
Comma-separated list of keys to process for translation in the YAML Front Matter
section. All other keys are skipped. Keys are matched with a case-insensitive
match. Array values are always translated, unless the B<yfm_skip_array> option
is provided.
=cut
my %yfm_keys = ();
=item B<yfm_skip_array> (markdown-only)
Do not translate array values in the YAML Front Matter section.
=cut
my $yfm_skip_array = 0;
=item B<control>[B<=>I<taglist>]
Handle control files.
A comma-separated list of tags to be translated can be provided.
=cut
my %control = ();
=item B<neverwrap>
Prevent po4a from wrapping any lines. This means that every content is handled verbatim, even simple paragraphs.
=cut
my $defaultwrap = 1;
my $parse_func = \&parse_fallback;
my @comments = ();
=back
=cut
sub initialize {
my $self = shift;
my %options = @_;
$self->{options}{'control'} = "";
$self->{options}{'breaks'} = 1;
$self->{options}{'debianchangelog'} = 1;
$self->{options}{'debug'} = 1;
$self->{options}{'fortunes'} = 1;
$self->{options}{'markdown'} = 1;
$self->{options}{'yfm_keys'} = '';
$self->{options}{'yfm_skip_array'} = 0;
$self->{options}{'nobullets'} = 0;
$self->{options}{'keyvalue'} = 1;
$self->{options}{'tabs'} = 1;
$self->{options}{'verbose'} = 1;
$self->{options}{'neverwrap'} = 1;
foreach my $opt ( keys %options ) {
die wrap_mod( "po4a::text", dgettext( "po4a", "Unknown option: %s" ), $opt )
unless exists $self->{options}{$opt};
$self->{options}{$opt} = $options{$opt};
}
$keyvalue = 1 if ( defined $options{'keyvalue'} );
$bullets = 0 if ( defined $options{'nobullets'} );
$tabs = $options{'tabs'} if ( defined $options{'tabs'} );
$breaks = $options{'breaks'} if ( defined $options{'breaks'} );
$defaultwrap = 0 if ( defined $options{'neverwrap'} );
$parse_func = \&parse_debianchangelog if ( defined $options{'debianchangelog'} );
$parse_func = \&parse_fortunes if ( defined $options{'fortunes'} );
if ( defined $options{'markdown'} ) {
$parse_func = \&parse_markdown;
$markdown = 1;
map {
$_ =~ s/^\s+|\s+$//g; # Trim the keys before using them
$yfm_keys{$_} = 1
} ( split( ',', $self->{options}{'yfm_keys'} ) );
# map { print STDERR "key $_\n"; } (keys %yfm_keys);
$yfm_skip_array = $self->{options}{'yfm_skip_array'};
} else {
foreach my $opt (qw(yfm_keys yfm_skip_array)) {
die wrap_mod( "po4a::text", dgettext( "po4a", "Option %s is only valid when parsing markdown files." ),
$opt )
if exists $options{$opt};
}
}
if ( defined $options{'control'} ) {
$parse_func = \&parse_control;
if ( $options{'control'} eq "1" ) {
$control{''} = 1;
} else {
foreach my $tag ( split( ',', $options{'control'} ) ) {
$control{$tag} = 1;
}
}
}
}
sub parse_fallback {
my ( $self, $line, $ref, $paragraph, $wrapped_mode, $expect_header, $end_of_paragraph ) = @_;
if (
( $line =~ /^\s*$/ )
or ( defined $breaks
and $line =~ m/^$breaks$/ )
)
{
# Break paragraphs on lines containing only spaces
do_paragraph( $self, $paragraph, $wrapped_mode );
$paragraph = "";
$wrapped_mode = $defaultwrap unless defined( $self->{verbatim} );
$self->pushline( $line . "\n" );
undef $self->{controlkey};
} elsif ( $line =~ /^-- $/ ) {
# Break paragraphs on email signature hint
do_paragraph( $self, $paragraph, $wrapped_mode );
$paragraph = "";
$wrapped_mode = $defaultwrap;
$self->pushline( $line . "\n" );
} elsif ( $line =~ /^=+$/
or $line =~ /^_+$/
or $line =~ /^-+$/ )
{
$wrapped_mode = 0;
$paragraph .= $line . "\n";
do_paragraph( $self, $paragraph, $wrapped_mode );
$paragraph = "";
$wrapped_mode = $defaultwrap;
} elsif ( $tabs eq "split" and $line =~ m/\t/ and $paragraph !~ m/\t/s ) {
$wrapped_mode = 0;
do_paragraph( $self, $paragraph, $wrapped_mode );
$paragraph = "$line\n";
$wrapped_mode = 0;
} elsif ( $tabs eq "split" and $line !~ m/\t/ and $paragraph =~ m/\t/s ) {
do_paragraph( $self, $paragraph, $wrapped_mode );
$paragraph = "$line\n";
$wrapped_mode = $defaultwrap;
} else {
if ( $line =~ /^\s/ ) {
# A line starting by a space indicates a non-wrap
# paragraph
$wrapped_mode = 0;
}
if (
$markdown
and (
$line =~ /\S $/ # explicit newline
or $line =~ /"""$/
)
)
{ # """ textblock inside macro begin
# Markdown markup needing separation _after_ this line
$end_of_paragraph = 1;
} else {
undef $self->{bullet};
undef $self->{indent};
}
# TODO: comments
$paragraph .= $line . "\n";
}
return ( $paragraph, $wrapped_mode, $expect_header, $end_of_paragraph );
}
sub parse_debianchangelog {
my ( $self, $line, $ref, $paragraph, $wrapped_mode, $expect_header, $end_of_paragraph ) = @_;
if (
$expect_header
and $line =~ /^(\w[-+0-9a-z.]*)\ \(([^\(\) \t]+)\) # src, version
\s+([-+0-9a-z.]+); # distribution
\s*urgency\s*\=\s*(.*\S)\s*$/ix
)
{ #
do_paragraph( $self, $paragraph, $wrapped_mode );
$paragraph = "";
$self->pushline("$line\n");
$expect_header = 0;
} elsif ( $line =~
m/^ \-\- (.*) <(.*)> ((\w+\,\s*)?\d{1,2}\s+\w+\s+\d{4}\s+\d{1,2}:\d\d:\d\d\s+[-+]\d{4}(\s+\([^\\\(\)]+\)))$/ )
{
# Found trailer
do_paragraph( $self, $paragraph, $wrapped_mode );
$paragraph = "";
$self->pushline("$line\n");
$expect_header = 1;
} else {
return parse_fallback( $self, $line, $ref, $paragraph, $wrapped_mode, $expect_header, $end_of_paragraph );
}
return ( $paragraph, $wrapped_mode, $expect_header, $end_of_paragraph );
}
sub parse_fortunes {
my ( $self, $line, $ref, $paragraph, $wrapped_mode, $expect_header, $end_of_paragraph ) = @_;
# Always include paragraphs in no-wrap mode,
# because the formatting of the fortunes
# is usually hand-crafted and matters.
$wrapped_mode = 0;
# Check if there are more lines in the file.
my $last_line_of_file = 0;
my ( $nextline, $nextref ) = $self->shiftline();
if ( defined $nextline ) {
# There is a next line, put it back.
$self->unshiftline( $nextline, $nextref );
} else {
# Nope, no more lines available.
$last_line_of_file = 1;
}
# Is the line the end of a fortune or the last line of the file?
if ( $line =~ m/^%%?\s*$/ or $last_line_of_file ) {
# Add the last line to the paragraph
if ($last_line_of_file) {
$paragraph .= $line;
}
# Remove the last newline for the translation.
chomp($paragraph);
do_paragraph( $self, $paragraph, $wrapped_mode );
$paragraph = "";
# Add the last newline again for the output.
$self->pushline("\n");
# Also add the separator line, if this is not the end of the file.
if ( !$last_line_of_file ) {
$self->pushline("$line\n");
}
} else {
$paragraph .= $line . "\n";
}
return ( $paragraph, $wrapped_mode, $expect_header, $end_of_paragraph );
}
sub parse_control {
my ( $self, $line, $ref, $paragraph, $wrapped_mode, $expect_header, $end_of_paragraph ) = @_;
if ( $line =~ m/^([^ :]*): *(.*)$/ ) {
warn wrap_mod( "po4a::text", dgettext( "po4a", "Unrecognized section: %s" ), $paragraph )
unless $paragraph eq "";
my $tag = $1;
my $val = $2;
my $t;
if ( $control{''} or $control{$tag} ) {
$t = $self->translate(
$val, $self->{ref},
$tag . ( defined $self->{controlkey} ? ", " . $self->{controlkey} : "" ),
"wrap" => 0
);
} else {
$t = $val;
}
if ( not defined $self->{controlkey} ) {
$self->{controlkey} = "$tag: $val";
}
$self->pushline("$tag: $t\n");
$paragraph = "";
$wrapped_mode = $defaultwrap;
$self->{bullet} = "";
$self->{indent} = " ";
} elsif ( $line eq " ." ) {
do_paragraph( $self, $paragraph, $wrapped_mode,
"Long Description" . ( defined $self->{controlkey} ? ", " . $self->{controlkey} : "" ) );
$paragraph = "";
$self->pushline( $line . "\n" );
$self->{bullet} = "";
$self->{indent} = " ";
} elsif ( $line =~ m/^ Link: +(.*)$/ ) {
do_paragraph( $self, $paragraph, $wrapped_mode,
"Long Description" . ( defined $self->{controlkey} ? ", " . $self->{controlkey} : "" ) );
my $link = $1;
my $t1 = $self->translate( "Link: ", $self->{ref}, "Link", "wrap" => 0 );
my $t2 = $self->translate(
$link, $self->{ref},
"Link" . ( defined $self->{controlkey} ? ", " . $self->{controlkey} : "" ),
"wrap" => 0
);
$self->pushline(" $t1$t2\n");
$paragraph = "";
} elsif ( defined $self->{indent}
and $line =~ m/^$self->{indent}\S/ )
{
$paragraph .= $line . "\n";
$self->{type} = "Long Description" . ( defined $self->{controlkey} ? ", " . $self->{controlkey} : "" );
} else {
return parse_fallback( $self, $line, $ref, $paragraph, $wrapped_mode, $expect_header, $end_of_paragraph );
}
return ( $paragraph, $wrapped_mode, $expect_header, $end_of_paragraph );
}
# Support pandoc's format of specifying bibliographic information.
#
# If the first line starts with a percent sign, the following
# is considered to be title, author, and date.
#
# If the information spans multiple lines, the following
# lines must be indented with space.
# If information is omitted, it's just a percent sign
# and a blank line.
#
# Examples with missing title resp. missing authors:
#
# %
# % Author
#
# % My title
# %
# % June 14, 2018
sub parse_markdown_bibliographic_information {
my ( $self, $line, $ref ) = @_;
my ( $nextline, $nextref );
# The first match is always the title or an empty string (no title).
if ( $line =~ /^%(.*)$/ ) {
my $title = $1;
# Remove leading and trailing whitespace
$title =~ s/^\s+|\s+$//g;
# If there's some text, look for continuation lines
if ( length($title) ) {
( $nextline, $nextref ) = $self->shiftline();
while ( $nextline =~ /^\s+(.+)$/ ) {
$nextline = $1;
$nextline =~ s/^\s+|\s+$//g;
$title .= " " . $nextline;
( $nextline, $nextref ) = $self->shiftline();
}
# Now the title should be complete, give it to translation.
my $t = $self->translate( $title, $ref, "Pandoc title block", "wrap" => $defaultwrap );
$t = Locale::Po4a::Po::wrap($t);
my $first_line = 1;
foreach my $translated_line ( split /\n/, $t ) {
if ($first_line) {
$first_line = 0;
$self->pushline( "% " . $translated_line . "\n" );
} else {
$self->pushline( " " . $translated_line . "\n" );
}
}
} else {
# Title has been empty, fetch the next line
# if that are the authors.
$self->pushline("%\n");
( $nextline, $nextref ) = $self->shiftline();
}
# The next line can contain the author or an empty string.
if ( $nextline =~ /^%(.*)$/ ) {
my $author_ref = $nextref;
my $authors = $1;
# If there's some text, look for continuation lines
if ( length($authors) ) {
( $nextline, $nextref ) = $self->shiftline();
while ( $nextline =~ /^\s+(.+)$/ ) {
$nextline = $1;
$authors .= ";" . $nextline;
( $nextline, $nextref ) = $self->shiftline();
}
# Now the authors should be complete, split them by semicolon
my $first_line = 1;
foreach my $author ( split /;/, $authors ) {
$author =~ s/^\s+|\s+$//g;
# Skip empty authors
next unless length($author);
my $t = $self->translate( $author, $author_ref, "Pandoc title block" );
if ($first_line) {
$first_line = 0;
$self->pushline( "% " . $t . "\n" );
} else {
$self->pushline( " " . $t . "\n" );
}
}
} else {
# Authors has been empty, fetch the next line
# if that is the date.
$self->pushline("%\n");
( $nextline, $nextref ) = $self->shiftline();
}
# The next line can contain the date.
if ( $nextline =~ /^%(.*)$/ ) {
my $date = $1;
# Remove leading and trailing whitespace
$date =~ s/^\s+|\s+$//g;
my $t = $self->translate( $date, $nextref, "Pandoc title block" );
$self->pushline( "% " . $t . "\n" );
# Now we're done with the bibliographic information
return;
}
}
# The line did not start with a percent sign, to stop
# parsing bibliographic information and return the
# line to the normal parsing.
$self->unshiftline( $nextline, $nextref );
return;
}
}
# Support YAML Front Matter in Markdown documents
#
# If the text starts with a YAML ---\n separator, the full text until
# the next YAML ---\n separator is considered YAML metadata. The ...\n
# "end of document" separator can be used at the end of the YAML
# block.
#
sub parse_markdown_yaml_front_matter {
my ( $self, $line, $blockref ) = @_;
my $yfm;
my ( $nextline, $nextref ) = $self->shiftline();
while ( defined($nextline) ) {
last if ( $nextline =~ /^(---|\.\.\.)$/ );
$yfm .= $nextline;
( $nextline, $nextref ) = $self->shiftline();
}
die "Could not get the YAML Front Matter from the file." if ( length($yfm) == 0 );
my $yamlarray = YAML::Tiny->read_string($yfm)
|| die "Couldn't read YAML Front Matter ($!)\n$yfm\n";
$self->handle_yaml( $blockref, $yamlarray, \%yfm_keys, $yfm_skip_array );
return;
}
sub parse_markdown {
my ( $self, $line, $ref, $paragraph, $wrapped_mode, $expect_header, $end_of_paragraph ) = @_;
if ($expect_header) {
# It is only possible to find and parse the bibliographic
# information or the YAML Front Matter from the first line.
# Anyway, stop expecting header information for the next run.
$expect_header = 0;
if ( $line =~ /^%(.*)$/ ) {
parse_markdown_bibliographic_information( $self, $line, $ref );
return ( $paragraph, $wrapped_mode, $expect_header, $end_of_paragraph );
} elsif ( $line =~ /^---$/ ) {
parse_markdown_yaml_front_matter( $self, $line, $ref );
return ( $paragraph, $wrapped_mode, $expect_header, $end_of_paragraph );
}
}
if ( ( $line =~ m/^(={4,}|-{4,})$/ )
and ( defined($paragraph) )
and ( $paragraph =~ m/^[^\n]*\n$/s )
and ( length($paragraph) == ( length($line) + 1 ) ) )
{
# XXX: There can be any number of underlining according
# to the documentation. This detection, which avoid
# translating the formatting, is only supported if
# the underlining has the same size as the header text.
# Found title
$wrapped_mode = 0;
my $level = $line;
$level =~ s/^(.).*$/$1/;
# Remove the trailing newline from the title
chomp($paragraph);
my $t = $self->translate(
$paragraph, $self->{ref}, "Title $level",
"wrap" => 0,
"flags" => "markdown-text"
);
# Add the newline again for the output
$self->pushline( $t . "\n" );
$paragraph = "";
$wrapped_mode = $defaultwrap;
$self->pushline( ( $level x length($t) ) . "\n" );
} elsif ( $line =~ m/^(#{1,6})( +)(.*?)( +\1)?$/ ) {
my $titlelevel1 = $1;
my $titlespaces = $2;
my $title = $3;
my $titlelevel2 = $4 || "";
# Found one line title
do_paragraph( $self, $paragraph, $wrapped_mode );
$wrapped_mode = 0;
$paragraph = "";
my $t = $self->translate(
$title, $self->{ref}, "Title $titlelevel1",
"wrap" => 0,
"flags" => "markdown-text"
);
$self->pushline( $titlelevel1 . $titlespaces . $t . $titlelevel2 . "\n" );
$wrapped_mode = $defaultwrap;
} elsif ( $line =~ /^[ ]{0,3}([*_-])\s*(?:\1\s*){2,}$/ ) {
# Horizontal rule
do_paragraph( $self, $paragraph, $wrapped_mode );
$self->pushline( $line . "\n" );
$paragraph = "";
$end_of_paragraph = 1;
} elsif ( $line =~ /^([ ]{0,3})(([~`])\3{2,})(\s*)([^`]*)\s*$/ ) {
my $fence_space_before = $1;
my $fence = $2;
my $fencechar = $3;
my $fence_space_between = $4;
my $info_string = $5;
# fenced code block
my $type = "Fenced code block" . ( $info_string ? " ($info_string)" : "" );
do_paragraph( $self, $paragraph, $wrapped_mode );
$wrapped_mode = 0;
$paragraph = "";
$self->pushline("$line\n");
do_paragraph( $self, $paragraph, $wrapped_mode );
$paragraph = "";
my ( $nextline, $nextref ) = $self->shiftline();
while ( $nextline !~ /^\s{0,3}$fence$fencechar*\s*$/ ) {
$paragraph .= "$nextline";
( $nextline, $nextref ) = $self->shiftline();
}
do_paragraph( $self, $paragraph, $wrapped_mode, $type );
$self->pushline($nextline);
$paragraph = "";
$end_of_paragraph = 1;
} elsif (
$line =~ /^\s*\[\[\!\S+\s*$/ # macro begin
or $line =~ /^\s*"""\s*\]\]\s*$/
)
{ # """ textblock inside macro end
# Avoid translating Markdown lines containing only markup
do_paragraph( $self, $paragraph, $wrapped_mode );
$paragraph = "";
$wrapped_mode = $defaultwrap;
$self->pushline("$line\n");
} elsif ( $line =~ /^\s*\[\[\!\S[^\]]*\]\]\s*$/ ) { # sole macro
# Preserve some Markdown markup as a single line
do_paragraph( $self, $paragraph, $wrapped_mode );
$paragraph = "$line\n";
$wrapped_mode = 0;
$end_of_paragraph = 1;
} elsif ( $line =~ /^"""/ ) { # """ textblock inside macro end
# Markdown markup needing separation _before_ this line
do_paragraph( $self, $paragraph, $wrapped_mode );
$paragraph = "$line\n";
$wrapped_mode = $defaultwrap;
} else {
return parse_fallback( $self, $line, $ref, $paragraph, $wrapped_mode, $expect_header, $end_of_paragraph );
}
return ( $paragraph, $wrapped_mode, $expect_header, $end_of_paragraph );
}
sub parse {
my $self = shift;
my ( $line, $ref );
my $paragraph = "";
my $wrapped_mode = $defaultwrap;
my $expect_header = 1;
my $end_of_paragraph = 0;
( $line, $ref ) = $self->shiftline();
my $file = $ref;
$file =~ s/:[0-9]+$// if defined($line);
while ( defined($line) ) {
$ref =~ m/^(.*):[0-9]+$/;
if ( $1 ne $file ) {
$file = $1;
do_paragraph( $self, $paragraph, $wrapped_mode );
$paragraph = "";
$wrapped_mode = $defaultwrap;
$expect_header = 1;
}
chomp($line);
$self->{ref} = "$ref";
( $paragraph, $wrapped_mode, $expect_header, $end_of_paragraph ) =
&$parse_func( $self, $line, $ref, $paragraph, $wrapped_mode, $expect_header, $end_of_paragraph );
# paragraphs starting by a bullet, or numbered
# or paragraphs with a line containing many consecutive spaces
# (more than 3)
# are considered as verbatim paragraphs
$wrapped_mode = 0 if ( $paragraph =~ m/^(\*|[0-9]+[.)] )/s
or $paragraph =~ m/[ \t][ \t][ \t]/s );
$wrapped_mode = 0 if ( $tabs eq "verbatim"
and $paragraph =~ m/\t/s );
# Also consider keyvalue paragraphs verbatim, if requested
$wrapped_mode = 0 if ( $keyvalue == 1
and $paragraph =~ m/^[^ :]+:.*[^\s].*$/s );
if ($markdown) {
# Some Markdown markup can (or might) not survive wrapping
$wrapped_mode = 0
if (
$paragraph =~ /^>/ms # blockquote
or $paragraph =~ /^( {8}|\t)/ms # monospaced
or $paragraph =~ /^\$(\S+[{}]\S*\s*)+/ms # Xapian macro
or $paragraph =~ /<(?![a-z]+[:@])/ms # maybe html (tags but not wiki <URI>)
or $paragraph =~ /^[^<]+>/ms # maybe html (tag with vertical space)
or $paragraph =~ /\S $/ms # explicit newline
or $paragraph =~ /\[\[\!\S[^\]]+$/ms # macro begin
);
}
if ($end_of_paragraph) {
do_paragraph( $self, $paragraph, $wrapped_mode );
$paragraph = "";
$wrapped_mode = $defaultwrap;
$end_of_paragraph = 0;
}
( $line, $ref ) = $self->shiftline();
}
if ( length $paragraph ) {
do_paragraph( $self, $paragraph, $wrapped_mode );
}
}
sub do_paragraph {
my ( $self, $paragraph, $wrap ) = ( shift, shift, shift );
my $type = shift || $self->{type} || "Plain text";
my $flags = "";
if ( $type eq "Plain text" and $markdown ) {
$flags = "markdown-text";
}
return if ( $paragraph eq "" );
$wrap = 0 unless $defaultwrap;
# DEBUG
# my $b;
# if (defined $self->{bullet}) {
# $b = $self->{bullet};
# } else {
# $b = "UNDEF";
# }
# $type .= " verbatim: '".($self->{verbatim}||"NONE")."' bullet: '$b' indent: '".($self->{indent}||"NONE")."' type: '".($self->{type}||"NONE")."'";
if ( $bullets and not $wrap and not defined $self->{verbatim} ) {
# Detect bullets
# | * blah blah
# |<spaces> blah
# | ^-- aligned
# <empty line>
#
# Other bullets supported:
# - blah o blah + blah
# 1. blah 1) blah (1) blah
TEST_BULLET:
if ( $paragraph =~ m/^(\s*)((?:[-*o+]|([0-9]+[.\)])|\([0-9]+\))\s+)([^\n]*\n)(.*)$/s ) {
my $para = $5;
my $bullet = $2;
my $indent1 = $1;
my $indent2 = "$1" . ( ' ' x length $bullet );
my $text = $4;
while ( $para !~ m/^$indent2(?:[-*o+]|([0-9]+[.\)])|\([0-9]+\))\s+/
and $para =~ s/^$indent2(\S[^\n]*\n)//s )
{
$text .= $1;
}
# TODO: detect if a line starts with the same bullet
if ( $text !~ m/\S[ \t][ \t][ \t]+\S/s ) {
my $bullet_regex = quotemeta( $indent1 . $bullet );
$bullet_regex =~ s/[0-9]+/\\d\+/;
if ( $para eq ''
or $para =~ m/^(\s*)((?:[-*o+]|([0-9]+[.\)])|\([0-9]+\))\s+)([^\n]*\n)(.*)$/s
or $para =~ m/^$bullet_regex\S/s )
{
my $trans = $self->translate(
$text,
$self->{ref},
"Bullet: '$indent1$bullet'",
"flags" => "markdown-text",
"wrap" => $defaultwrap,
"wrapcol" => -( length $indent2 )
);
$trans =~ s/^/$indent1$bullet/s;
$trans =~ s/\n(.)/\n$indent2$1/sg;
$self->pushline( $trans . "\n" );
if ( $para eq '' ) {
return;
} else {
# Another bullet
$paragraph = $para;
goto TEST_BULLET;
}
}
}
}
}
my $end = "";
if ($wrap) {
$paragraph =~ s/^(.*?)(\n*)$/$1/s;
$end = $2 || "";
}
my $t = $self->translate(
$paragraph,
$self->{ref},
$type,
"comment" => join( "\n", @comments ),
"flags" => $flags,
"wrap" => $wrap
);
@comments = ();
if ( defined $self->{bullet} ) {
my $bullet = $self->{bullet};
my $indent1 = $self->{indent};
my $indent2 = $indent1 . ( ' ' x length($bullet) );
$t =~ s/^/$indent1$bullet/s;
$t =~ s/\n(.)/\n$indent2$1/sg;
}
$self->pushline( $t . $end );
}
1;
=head1 STATUS OF THIS MODULE
Tested successfully on simple text files and NEWS.Debian files.
=head1 AUTHORS
Nicolas François <nicolas.francois@centraliens.net>
=head1 COPYRIGHT AND LICENSE
Copyright © 2005-2008 Nicolas FRANÇOIS <nicolas.francois@centraliens.net>.
Copyright © 2008-2009, 2018 Jonas Smedegaard <dr@jones.dk>.
Copyright © 2020 Martin Quinson <mquinson#debian.org>.
This program is free software; you may redistribute it and/or modify it
under the terms of GPL (see the COPYING file).
=cut

File diff suppressed because it is too large Load Diff

210
lib/Locale/Po4a/Wml.pm Normal file
View File

@ -0,0 +1,210 @@
#!/usr/bin/perl -w
# Po4a::Wml.pm
#
# extract and translate translatable strings from a WML (web markup language) documents
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
#
########################################################################
=encoding UTF-8
=head1 NAME
Locale::Po4a::Wml - convert WML (web markup language) documents from/to PO files
=head1 DESCRIPTION
The po4a (PO for anything) project goal is to ease translations (and more
interestingly, the maintenance of translations) using gettext tools on
areas where they were not expected like documentation.
Locale::Po4a::Wml is a module to help the translation of WML documents into
other [human] languages. Do not mixup the WML we are speaking about here
(web markup language) and the WAP crap used on cell phones.
Please note that this module relies upon the Locale::Po4a::Xhtml
module, which also relies upon the Locale::Po4a::Xml module. This
means that all tags for web page expressions are assumed to be written
in the XHTML syntax.
=head1 OPTIONS ACCEPTED BY THIS MODULE
NONE.
=head1 STATUS OF THIS MODULE
This module works for some simple documents, but is still young.
Currently, the biggest issue of the module is probably that it cannot
handle documents that contain non-XML inline tags such as <email
"foo@example.org">, which are often defined in the WML. Improvements
will be added in the future releases.
=cut
package Locale::Po4a::Wml;
use 5.006;
use strict;
use warnings;
require Exporter;
use vars qw(@ISA @EXPORT);
@ISA = qw(Locale::Po4a::Xhtml);
@EXPORT = qw();
use Locale::Po4a::Common;
use Locale::Po4a::Xhtml;
use File::Temp;
sub initialize {
my $self = shift;
my %options = @_;
$self->SUPER::initialize(%options);
print "Call treat_options\n" if $self->{options}{'debug'};
$self->treat_options;
}
sub read {
my ( $self, $filename, $refname ) = @_;
my $tmp_filename;
( undef, $tmp_filename ) = File::Temp::tempfile(
"po4aXXXX",
DIR => $ENV{TMPDIR} || "/tmp",
SUFFIX => ".xml",
OPEN => 0,
UNLINK => 0
) or die wrap_msg( gettext("Cannot create a temporary XML file: %s"), $! );
my $file;
open FILEIN, "$filename" or die "Cannot read $filename: $!\n";
{
$/ = undef;
$file = <FILEIN>;
}
$/ = "\n";
# Mask perl cruft out of XML sight
while (( $file =~ m|^(.*?)<perl>(.*?)</perl>(.*?)$|ms )
or ( $file =~ m|^(.*?)<:(.*?):>(.*)$|ms ) )
{
my ( $pre, $in, $post ) = ( $1, $2, $3 );
$in =~ s/</PO4ALT/g;
$in =~ s/>/PO4AGT/g;
$file = "${pre}<!--PO4ABEGINPERL${in}PO4AENDPERL-->$post";
}
# Mask mp4h cruft
while ( $file =~ s|^#(.*)$|<!--PO4ASHARPBEGIN$1PO4ASHARPEND-->|m ) {
my $line = $1;
print STDERR "PROTECT HEADER: $line\n"
if $self->{options}{'debug'};
# If the wml tag has a title attribute, use a fake
# <title> xml tag to enable the extraction
# for translation in the xml parser.
if ( $line =~ m/title="([^"]*)"/ ) {
$file = "<title>$1</title>\n" . $file;
}
}
# Validate define-tag tag's argument
$file =~ s|(<define-tag\s+)([^\s>]+)|$1PO4ADUMMYATTR="$2"|g;
# Flush the result to disk
open OUTFILE, ">$tmp_filename";
print OUTFILE $file;
close INFILE;
close OUTFILE or die "Cannot write $tmp_filename: $!\n";
push @{ $self->{DOCXML}{infile} }, $tmp_filename;
$self->{DOCWML}{$tmp_filename} = $filename;
$self->Locale::Po4a::TransTractor::read( $tmp_filename, $refname );
unlink "$tmp_filename";
}
sub parse {
my $self = shift;
foreach my $filename ( @{ $self->{DOCXML}{infile} } ) {
$self->Locale::Po4a::Xml::parse_file($filename);
my $org_filename = $self->{DOCWML}{$filename};
# Fix the references
foreach my $msgid ( keys %{ $self->{TT}{po_out}{po} } ) {
$self->{TT}{po_out}{po}{$msgid}{'reference'} =~ s|$filename(:\d+)|$org_filename$1|o;
}
# Get the document back (undoing our WML masking)
# FIXME: need to join the file first, and then split?
my @doc_out;
my $cnt = 0;
my $title_node;
my $title;
foreach my $line ( @{ $self->{TT}{doc_out} } ) {
if ( !$cnt ) {
if ( !$title_node && $line =~ m/<title>/ ) {
$title_node = $line;
} elsif ($title_node) {
$title_node .= $line;
if ( $title_node =~ m/<title>(.*?)<\/title>/ ) {
$title = $1;
$cnt = 1;
}
} else {
$cnt = 1;
}
} else {
if ( $line =~ s/^<!--PO4ASHARPBEGIN(.*?)PO4ASHARPEND-->/#$1/mg && $title ) {
$line =~ s/title="[^"]*"$/title="$title"/mg;
}
$line =~ s/<!--PO4ABEGINPERL(.*?)PO4AENDPERL-->/<:$1:>/sg;
$line =~ s/(<define-tag\s+)PO4ADUMMYATTR="([^"]*)"/$1$2/g;
$line =~ s/PO4ALT/</sg;
$line =~ s/PO4AGT/>/sg;
push @doc_out, $line;
}
}
# Do a simple left trim
foreach my $line (@doc_out) {
if ( $line =~ m/\s+/ ) {
shift @doc_out;
} else {
last;
}
}
$self->{TT}{doc_out} = \@doc_out;
}
}
1;
=head1 AUTHORS
Martin Quinson (mquinson#debian.org)
Noriada Kobayashi <nori1@dolphin.c.u-tokyo.ac.jp>
=head1 COPYRIGHT AND LICENSE
Copyright © 2005 SPI, Inc.
This program is free software; you may redistribute it and/or modify it
under the terms of GPL (see the COPYING file).

246
lib/Locale/Po4a/Xhtml.pm Normal file
View File

@ -0,0 +1,246 @@
#!/usr/bin/perl
# Po4a::Xhtml.pm
#
# extract and translate translatable strings from XHTML documents.
#
# This code extracts plain text from tags and attributes from strict XHTML
# documents.
#
# Copyright © 2005 Yves Rütschlé <po4a@rutschle.net>
# Copyright © 2007-2008 Nicolas François <nicolas.francois@centraliens.net>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
#
########################################################################
=encoding UTF-8
=head1 NAME
Locale::Po4a::Xhtml - convert XHTML documents from/to PO files
=head1 DESCRIPTION
The po4a (PO for anything) project goal is to ease translations (and more
interestingly, the maintenance of translations) using gettext tools on
areas where they were not expected like documentation.
Locale::Po4a::Xhtml is a module to help the translation of XHTML documents into
other [human] languages.
=head1 OPTIONS ACCEPTED BY THIS MODULE
These are this module's particular options:
=over 4
=item B<includessi>[B<=>I<rootpath>]
Include files specified by an include SSI (Server Side Includes) element
(e.g. <!--#include virtual="/foo/bar.html" -->).
B<Note:> You should use it only for static files.
An additional I<rootpath> parameter can be specified. It specifies the root
path to find files included by a B<virtual> attribute.
=back
=head1 STATUS OF THIS MODULE
This module is fully functional, as it relies in the L<Locale::Po4a::Xml>
module. This only defines the translatable tags and attributes.
"It works for me", which means I use it successfully on my personal Web site.
However, YMMV: please let me know if something doesn't work for you. In
particular, tables are getting no testing whatsoever, as we don't use them.
=head1 SEE ALSO
L<Locale::Po4a::TransTractor(3pm)>, L<Locale::Po4a::Xml(3pm)>, L<po4a(7)|po4a.7>
=head1 AUTHORS
Yves Rütschlé <po4a@rutschle.net>
Nicolas François <nicolas.francois@centraliens.net>
=head1 COPYRIGHT AND LICENSE
Copyright © 2004 Yves Rütschlé <po4a@rutschle.net>
Copyright © 2007-2008 Nicolas François <nicolas.francois@centraliens.net>
This program is free software; you may redistribute it and/or modify it
under the terms of GPL (see the COPYING file).
=cut
package Locale::Po4a::Xhtml;
use 5.006;
use strict;
use warnings;
use Locale::Po4a::Xml;
use vars qw(@tag_types);
*tag_types = \@Locale::Po4a::Xml::tag_types;
use Locale::Po4a::Common;
use Carp qw(croak);
use vars qw(@ISA);
@ISA = qw(Locale::Po4a::Xml);
sub tag_extract_SSI {
my ( $self, $remove ) = ( shift, shift );
my ( $eof, @tag ) = $self->get_string_until(
"-->",
{
include => 1,
remove => $remove,
unquoted => 1
}
);
my ( $t, $r ) = @tag;
if ( $t =~ m/<!--#include (file|virtual)="(.*?)"\s-->/s ) {
my $includefile;
if ( $1 eq "file" ) {
$includefile = ".";
} else {
$includefile = $self->{options}{'includessi'};
}
$includefile .= $2;
if ( !$remove ) {
$self->get_string_until(
"-->",
{
include => 1,
remove => 1,
unquoted => 1
}
);
}
my $linenum = 0;
my @include;
open( my $in, $includefile )
or croak wrap_mod( "po4a::xml", dgettext( "po4a", "Cannot read from %s: %s" ), $includefile, $! );
while ( defined( my $includeline = <$in> ) ) {
$linenum++;
my $includeref = $includefile . ":$linenum";
push @include, ( $includeline, $includeref );
}
close $in
or croak wrap_mod( "po4a::xml", dgettext( "po4a", "Cannot close %s after reading: %s" ), $includefile, $! );
while (@include) {
my ( $ir, $il ) = ( pop @include, pop @include );
$self->unshiftline( $il, $ir );
}
$t =~ s/<!--#include/<!-- SSI included by po4a: /;
$self->unshiftline( $t, $r );
}
return ( $eof, @tag );
}
sub initialize {
my $self = shift;
my %options = @_;
$self->{options}{'includessi'} = '';
$self->SUPER::initialize(%options);
$self->{options}{'wrap'} = 1;
$self->{options}{'doctype'} = $self->{options}{'doctype'} || 'html';
# Default tags are translated (text rewrapped), and introduce a break.
# The following list indicates the list of tags which should be
# translated without rewrapping.
$self->{options}{'_default_translated'} .= '
W<pre>
';
# The following list indicates the list of tags which should be
# translated inside the current block, without introducing a break.
$self->{options}{'_default_inline'} .= '
<a>
<abbr>
<acronym>
<b>
<big>
<bdo>
<button>
<cite>
<code>
<del>
<dfn>
<em>
<i>
<ins>
<input>
<kbd>
<label>
<object>
<q>
<samp>
<select>
<small>
<span>
<strong>
<sub>
<sup>
<textarea>
<tt>
<u>
<var>
';
# Ignored tags: <img>
# Technically, <img> is an inline tag, but setting it as such is
# annoying, and not usually useful, unless you use images to
# write text (in which case you have bigger problems than this
# program not inlining img: you now have to translate all your
# images. That'll teach you).
# If you choose to translate images, you may also want to set
# <map> as placeholder and <area> as inline.
$self->{options}{'_default_attributes'} .= '
alt
lang
title
';
$self->{options}{'optionalclosingtag'} = 1;
print "Call treat_options\n" if $self->{options}{'debug'};
$self->treat_options;
if ( defined $self->{options}{'includessi'}
and length $self->{options}{'includessi'} )
{
foreach (@tag_types) {
if ( $_->{beginning} eq "!--#" ) {
$_->{f_extract} = \&tag_extract_SSI;
}
}
# FIXME: the directory may be named "1" ;(
if ( $self->{options}{'includessi'} eq "1" ) {
$self->{options}{'includessi'} = ".";
}
}
}

2494
lib/Locale/Po4a/Xml.pm Normal file

File diff suppressed because it is too large Load Diff

202
lib/Locale/Po4a/Yaml.pm Normal file
View File

@ -0,0 +1,202 @@
# Locale::Po4a::Yaml -- Convert yaml files to PO file, for translation.
#
# This program is free software; you may redistribute it and/or modify it
# under the terms of GPL (see COPYING).
#
############################################################################
# Modules and declarations
############################################################################
package Locale::Po4a::Yaml;
use Locale::Po4a::TransTractor;
use Locale::Po4a::Common;
use YAML::Tiny;
use Scalar::Util;
use Encode;
use 5.006;
use strict;
use warnings;
require Exporter;
use vars qw(@ISA @EXPORT $AUTOLOAD);
@ISA = qw(Locale::Po4a::TransTractor);
@EXPORT = qw();
sub initialize {
my $self = shift;
my %options = @_;
$self->{options}{'keys'} = '';
$self->{options}{'paths'} = '';
$self->{options}{'debug'} = 0;
$self->{options}{'verbose'} = 1;
$self->{options}{'skip_array'} = 0;
foreach my $opt ( keys %options ) {
die wrap_mod( "po4a::yaml", dgettext( "po4a", "Unknown option: %s" ), $opt )
unless exists $self->{options}{$opt};
$self->{options}{$opt} = $options{$opt};
}
$self->{options}{keys} =~ s/^\s*//;
foreach my $attr ( split( /\s+/, $self->{options}{keys} ) ) {
$self->{keys}{ lc($attr) } = '';
}
$self->{options}{paths} =~ s/^\s*//;
foreach my $attr ( split( /,/, $self->{options}{paths} ) ) {
$self->{paths}{ lc( $attr =~ s/^\s+|\s+$//gr ) } = '';
}
}
sub read {
my ( $self, $filename, $refname ) = @_;
push @{ $self->{DOCPOD}{infile} }, $filename;
$self->Locale::Po4a::TransTractor::read( $filename, $refname );
}
sub parse {
my $self = shift;
map { $self->parse_file($_) } @{ $self->{DOCPOD}{infile} };
}
sub parse_file {
my ( $self, $filename ) = @_;
my $yaml = YAML::Tiny->read($filename)
|| die "Couldn't read YAML file $filename : $!";
for my $i ( 0 .. $#{$yaml} ) {
&walk_yaml( $self, $yaml->[$i], "" );
}
$self->pushline( Encode::encode_utf8( $yaml->write_string() ) );
}
sub walk_yaml {
my $self = shift;
my $el = shift;
my $ctx = shift;
my ( $line, $reference ) = $self->shiftline();
$reference =~ s/:[0-9]+$/:0/;
if ( ref $el eq 'HASH' ) {
print STDERR "begin a hash\n" if $self->{'options'}{'debug'};
foreach my $key ( sort keys %$el ) {
if ( ref $el->{$key} ne ref "" ) {
&walk_yaml( $self, $el->{$key}, "$ctx $key" );
} else {
my $path = "$ctx $key" =~ s/^\s+|\s+$//gr;
print STDERR "working on path '$path'\n" if $self->{'options'}{'debug'};
my $keysdefined = $self->{options}{keys} ne "";
my $keymatches = exists $self->{keys}{ lc($key) };
my $pathsdefined = $self->{options}{paths} ne "";
my $pathmatches = exists $self->{paths}{ lc($path) };
next
if (
!(
( $keysdefined and $keymatches )
or ( $pathsdefined and $pathmatches )
or ( not $keysdefined and not $pathsdefined )
)
);
print STDERR " * path survived check\n" if $self->{'options'}{'debug'};
my $trans = $self->translate(
Encode::encode_utf8( $el->{$key} ),
$reference,
"Hash Value:$ctx $key",
'wrap' => 0
);
$el->{$key} = Encode::decode_utf8($trans); # Save the translation
}
}
} elsif ( ref $el eq 'ARRAY' ) {
print STDERR "begin an array\n" if $self->{'options'}{'debug'};
for my $i ( 0 .. $#{$el} ) {
if ( ref $el->[$i] ne ref "" ) {
&walk_yaml( $self, $el->[$i], "$ctx" );
} elsif ( !$self->{options}{skip_array} ) { # translate that element only if not asked to skip arrays
my $trans =
$self->translate( Encode::encode_utf8( $el->[$i] ), $reference, "Array Element:$ctx", 'wrap' => 0 );
$el->[$i] = Encode::decode_utf8($trans); # Save the translation
}
}
} else {
print STDERR "got a string - this is unexpected in yaml\n" if $self->{'options'}{'debug'};
my $trans = $self->translate( Encode::encode_utf8($$el), $reference, "String:$ctx", 'wrap' => 0 );
$$el = Encode::decode_utf8($trans); # Save the translation
}
}
##############################################################################
# Module return value and documentation
##############################################################################
1;
__END__
=encoding UTF-8
=head1 NAME
Locale::Po4a::Yaml - convert YAML files from/to PO files
=head1 DESCRIPTION
Locale::Po4a::Yaml is a module to help the translation of Yaml files into other
[human] languages.
The module extracts the value of YAML hashes and arrays. Hash keys are
not extracted.
NOTE: This module parses the YAML file with YAML::Tiny.
=head1 OPTIONS ACCEPTED BY THIS MODULE
These are this module's particular options:
=over
=item B<keys>
Space-separated list of hash keys to process for extraction, all
other keys are skipped. Keys are matched with a case-insensitive match.
If B<paths> and B<keys> are used together, values are included if they are
matched by at least one of the options.
Arrays values are always returned unless the B<skip_array> option is
provided.
=item B<paths>
Comma-separated list of hash paths to process for extraction, all
other paths are skipped. Paths are matched with a case-insensitive match.
If B<paths> and B<keys> are used together, values are included if they are
matched by at least one of the options.
Arrays values are always returned unless the B<skip_array> option is
provided.
=item B<skip_array>
Do not translate array values.
=back
=head1 SEE ALSO
L<Locale::Po4a::TransTractor(3pm)>, L<po4a(7)|po4a.7>
=head1 AUTHORS
Brian Exelbierd <bex@pobox.com>
=head1 COPYRIGHT AND LICENSE
Copyright © 2017 Brian Exelbierd.
This program is free software; you may redistribute it and/or modify it
under the terms of GPL (see the COPYING file).
=cut

237
msguntypot Normal file
View File

@ -0,0 +1,237 @@
#! /usr/bin/env perl
eval 'exec perl -S $0 ${1+"$@"}'
if $running_under_some_shell;
# msg untypo pot -- Update the PO files when you remove a typo in POT file not needing any translation update
#
# Copyright 2005 by Martin Quinson (mquinson#debian.fr)
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of GPL (see COPYING).
my $VERSION=$Locale::Po4a::TransTractor::VERSION;
=head1 NAME
msguntypot - update PO files when a typo is fixed in POT file
=head1 SYNOPSIS
B<msguntypot> B<-o> I<old_pot> B<-n> I<new_pot> I<pofiles> ...
=head1 DESCRIPTION
When you fix a trivial error which surely doesn't affect translations (e.g.
a typo) in a POT file, you should unfuzzy the corresponding msgstr in the
translated PO files to avoid so extra work to the translators.
This task is difficult and error prone when done manually, and this tool is
there to help doing so correctly. You just need to provide the two versions
of the POT file: before the edition and after as marked in the above
synopsis, and it all becomes automatic.
=head1 HOW TO USE IT
In short, when you discover a typo in one of your [english] message, do the
following:
=over
=item - Regenerate your POT and PO files.
make -C po/ update-po # for message program translations
debconf-updatepo # for debconf translations
po4a po4a.conf # for po4a based documentation translations
or something else, depending on your project's building settings. You know
how to make sure your POT and PO files are uptodate, don't you??
=item - Make a copy of your POT file.
cp myfile.pot myfile.pot.orig
=item - Make a copy of all your PO files.
mkdir po_fridge; cp *.po po_fridge
=item - Fix your typo.
$EDITOR the_file_in_which_there_is_a_typo
=item - Regenerate your POT and PO files.
See above.
=back
At this point, the typo fix fuzzied all the translations, and this
unfortunate change is the only one between the PO files of your main
directory and the one from the fridge. Here is how to solve this.
=over
=item - Discard fuzzy translation, restore the ones from the fridge.
cp po_fridge/*.po .
=item - Manually merge the PO files with the new POT file, but taking the useless fuzzy into account.
msguntypot -o myfile.pot.orig -n myfile.pot *.po
=item - Cleanups.
rm -rf myfile.pot.orig po_fridge
=back
You're done. The typo was eradicated from msgstr of both your POT and PO
files, and the PO files were not fuzzyied in the process. Your translators
love you already.
=head1 SEE ALSO
Despite its name, this tool is not part of the gettext tool suite. It is
instead part of po4a. More precisely, it's a random Perl script using the
fine po4a modules. For more information about po4a, please see:
L<po4a(7)>
=head1 AUTHORS
Martin Quinson (mquinson#debian,org)
=head1 COPYRIGHT AND LICENSE
Copyright 2005 by SPI, inc.
This program is free software; you may redistribute it and/or modify it
under the terms of GPL (see the COPYING file).
=cut
use 5.006;
use strict;
use warnings;
use Getopt::Long qw(GetOptions);
use Locale::Po4a::TransTractor;
use Locale::Po4a::Common;
use Pod::Usage qw(pod2usage);
use File::Temp;
Locale::Po4a::Common::textdomain('po4a');
sub show_version {
Locale::Po4a::Common::show_version("msguntypot");
exit 0;
}
my ($help,$debug,@verbose,$quiet,$noprevious);
@verbose = ();
$debug = 0;
my ($newfile,$oldfile)=("","");
Getopt::Long::config('bundling', 'no_getopt_compat', 'no_auto_abbrev');
GetOptions(
'help|h' => \$help,
'new|n=s' => \$newfile,
'old|o=s' => \$oldfile,
'verbose|v' => \@verbose,
'debug|d' => \$debug,
'quiet|q' => \$quiet,
'no-previous' => \$noprevious,
'version|V' => \&show_version
) or pod2usage();
# Argument check
$help && pod2usage (-verbose => 1, -exitval => 0);
my ($verbose) = (scalar @verbose);
$verbose = 1 if $debug;
$verbose = -1 if $quiet;
my %options = (
"verbose" => $verbose,
"debug" => $debug);
# Argument checking
defined($oldfile) && length($oldfile) || die wrap_msg(gettext("Mandatory argument '%s' missing."), "-o");
-e $oldfile || die wrap_msg(gettext("File %s does not exist."), $oldfile);
defined($newfile) && length($newfile) || die wrap_msg(gettext("Mandatory argument '%s' missing."), "-n");
-e $newfile || die wrap_msg(gettext("File %s does not exist."), $newfile);
# Parse files
my $newpot=Locale::Po4a::Po->new();
my $oldpot=Locale::Po4a::Po->new();
$newpot->read($newfile);
$oldpot->read($oldfile);
die wrap_msg(gettext("The new and old POT files have different amount of strings (%d != %d).".
" Something's seriously wrong here."),
$newpot->count_entries(), $oldpot->count_entries())
if ($newpot->count_entries() != $oldpot->count_entries());
# Compare them and find differences between them
my (%diff)=();
for (my ($o,$n)=(0,0) ;
$o<$oldpot->count_entries() && $n<$newpot->count_entries();
$o++,$n++) {
my ($oldstr,$newstr)=($oldpot->msgid($o),$newpot->msgid($n));
$diff{$oldstr} = $newstr
if ($oldstr ne $newstr);
}
print wrap_msg(gettext("Found %d modified entries."),scalar keys %diff) if $verbose;
my $msgmergeOpts = ($noprevious ? "" : "--previous");
# Get all po files and report differences in them
my ($pofile);
(undef,$pofile)=File::Temp::tempfile("po4aXXXX",
DIR => "/tmp",
SUFFIX => ".po",
OPEN => 0,
UNLINK => 0)
or die wrap_msg(gettext("Cannot create a temporary PO file: %s"), $!);
my $pocount = 0;
while (my $poarg = shift) {
$pocount ++;
print wrap_msg(gettext("Handling %s"),$poarg) if $verbose;
if (system("msgmerge $msgmergeOpts -o $pofile $poarg $oldfile 2>/dev/null")) {
my $msg = $!;
unlink ($pofile);
die wrap_msg(gettext("Could not run msgmerge: %s"), $msg);
}
my $po=Locale::Po4a::Po->new();
$po->read($pofile);
for (my $n=0 ; $n<$po->count_entries(); $n++) {
my $str=$po->msgid($n);
next unless defined $str;
my $newstr = $diff{$str};
if (defined $newstr) {
$po->{po}{ $newstr } = { %{ $po->{po}{ $str } } };
$po->{po}{ $str } = ();
delete $po->{po}{ $str };
print " Changed \"$str\" to \"$newstr\"\n" if ($verbose);
}
}
$po->write($poarg);
}
unlink($pofile);
print wrap_msg(gettext("Modified %d entries in %d files."),scalar keys %diff,$pocount);
exit 0;
__END__;

1333
po/bin/ace.po Normal file

File diff suppressed because it is too large Load Diff

1382
po/bin/af.po Normal file

File diff suppressed because it is too large Load Diff

1393
po/bin/ar.po Normal file

File diff suppressed because it is too large Load Diff

1574
po/bin/ca.po Normal file

File diff suppressed because it is too large Load Diff

1634
po/bin/cs.po Normal file

File diff suppressed because it is too large Load Diff

1586
po/bin/da.po Normal file

File diff suppressed because it is too large Load Diff

1703
po/bin/de.po Normal file

File diff suppressed because it is too large Load Diff

1509
po/bin/eo.po Normal file

File diff suppressed because it is too large Load Diff

1735
po/bin/es.po Normal file

File diff suppressed because it is too large Load Diff

1685
po/bin/et.po Normal file

File diff suppressed because it is too large Load Diff

1645
po/bin/eu.po Normal file

File diff suppressed because it is too large Load Diff

1704
po/bin/fr.po Normal file

File diff suppressed because it is too large Load Diff

1578
po/bin/hr.po Normal file

File diff suppressed because it is too large Load Diff

1606
po/bin/hu.po Normal file

File diff suppressed because it is too large Load Diff

1630
po/bin/id.po Normal file

File diff suppressed because it is too large Load Diff

1526
po/bin/it.po Normal file

File diff suppressed because it is too large Load Diff

1649
po/bin/ja.po Normal file

File diff suppressed because it is too large Load Diff

1376
po/bin/kn.po Normal file

File diff suppressed because it is too large Load Diff

1450
po/bin/ko.po Normal file

File diff suppressed because it is too large Load Diff

1690
po/bin/nb.po Normal file

File diff suppressed because it is too large Load Diff

1711
po/bin/nl.po Normal file

File diff suppressed because it is too large Load Diff

1723
po/bin/pl.po Normal file

File diff suppressed because it is too large Load Diff

1335
po/bin/po4a.pot Normal file

File diff suppressed because it is too large Load Diff

1640
po/bin/pt.po Normal file

File diff suppressed because it is too large Load Diff

1699
po/bin/pt_BR.po Normal file

File diff suppressed because it is too large Load Diff

1694
po/bin/ru.po Normal file

File diff suppressed because it is too large Load Diff

1642
po/bin/sl.po Normal file

File diff suppressed because it is too large Load Diff

1581
po/bin/sr_Cyrl.po Normal file

File diff suppressed because it is too large Load Diff

1648
po/bin/sv.po Normal file

File diff suppressed because it is too large Load Diff

1707
po/bin/uk.po Normal file

File diff suppressed because it is too large Load Diff

1728
po/bin/vi.po Normal file

File diff suppressed because it is too large Load Diff

1439
po/bin/zh_CN.po Normal file

File diff suppressed because it is too large Load Diff

1365
po/bin/zh_HK.po Normal file

File diff suppressed because it is too large Load Diff

1370
po/bin/zh_Hant.po Normal file

File diff suppressed because it is too large Load Diff

86
po/pod.cfg Normal file
View File

@ -0,0 +1,86 @@
[po_directory] po/pod
[po4a_alias:docbook] docbook opt:"-M UTF-8 -L UTF-8"
[po4a_alias:pod] pod opt:"-M UTF-8 -L UTF-8"
[po4a_alias:man] man opt:"-M UTF-8 -L UTF-8"
[options] opt:"--porefs=counter"
[type:docbook] share/doc/po4a-display-man.xml $lang:blib/man/$lang/man1/po4a-display-man.xml
[type:docbook] share/doc/po4a-display-pod.xml $lang:blib/man/$lang/man1/po4a-display-pod.xml
[type: pod] po4a $lang:blib/man/$lang/man1/po4a.1p.pod \
add_$lang:?doc/addendum.$lang
[type: pod] po4a-gettextize $lang:blib/man/$lang/man1/po4a-gettextize.1p.pod \
add_$lang:?doc/addendum.$lang
[type: pod] po4a-normalize $lang:blib/man/$lang/man1/po4a-normalize.1p.pod \
add_$lang:?doc/addendum.$lang
[type: pod] po4a-translate $lang:blib/man/$lang/man1/po4a-translate.1p.pod \
add_$lang:?doc/addendum.$lang
[type: pod] po4a-updatepo $lang:blib/man/$lang/man1/po4a-updatepo.1p.pod \
add_$lang:?doc/addendum.$lang
[type: pod] msguntypot $lang:blib/man/$lang/man1/msguntypot.1p.pod \
add_$lang:?doc/addendum.$lang
[type: pod] doc/po4a.7.pod $lang:blib/man/$lang/man7/po4a.7.pod \
add_$lang:?doc/addendum.$lang
[type: pod] lib/Locale/Po4a/AsciiDoc.pm $lang:blib/man/$lang/man3/Locale::Po4a::AsciiDoc.3pm.pod \
add_$lang:?doc/addendum.$lang
[type: pod] lib/Locale/Po4a/BibTeX.pm $lang:blib/man/$lang/man3/Locale::Po4a::BibTeX.3pm.pod \
add_$lang:?doc/addendum.$lang
[type: pod] lib/Locale/Po4a/Chooser.pm $lang:blib/man/$lang/man3/Locale::Po4a::Chooser.3pm.pod \
add_$lang:?doc/addendum.$lang
[type: pod] lib/Locale/Po4a/Common.pm $lang:blib/man/$lang/man3/Locale::Po4a::Common.3pm.pod \
add_$lang:?doc/addendum.$lang
[type: pod] lib/Locale/Po4a/Dia.pm $lang:blib/man/$lang/man3/Locale::Po4a::Dia.3pm.pod \
add_$lang:?doc/addendum.$lang
[type: pod] lib/Locale/Po4a/Docbook.pm $lang:blib/man/$lang/man3/Locale::Po4a::Docbook.3pm.pod \
add_$lang:?doc/addendum.$lang
[type: pod] lib/Locale/Po4a/Guide.pm $lang:blib/man/$lang/man3/Locale::Po4a::Guide.3pm.pod \
add_$lang:?doc/addendum.$lang
[type: pod] lib/Locale/Po4a/Halibut.pm $lang:blib/man/$lang/man3/Locale::Po4a::Halibut.3pm.pod \
add_$lang:?doc/addendum.$lang
[type: pod] lib/Locale/Po4a/Ini.pm $lang:blib/man/$lang/man3/Locale::Po4a::Ini.3pm.pod \
add_$lang:?doc/addendum.$lang
[type: pod] lib/Locale/Po4a/KernelHelp.pm $lang:blib/man/$lang/man3/Locale::Po4a::KernelHelp.3pm.pod \
add_$lang:?doc/addendum.$lang
[type: pod] lib/Locale/Po4a/LaTeX.pm $lang:blib/man/$lang/man3/Locale::Po4a::LaTeX.3pm.pod \
add_$lang:?doc/addendum.$lang
[type: pod] lib/Locale/Po4a/Man.pm $lang:blib/man/$lang/man3/Locale::Po4a::Man.3pm.pod \
add_$lang:?doc/addendum.$lang
[type: pod] lib/Locale/Po4a/Po.pm $lang:blib/man/$lang/man3/Locale::Po4a::Po.3pm.pod \
add_$lang:?doc/addendum.$lang
[type: pod] lib/Locale/Po4a/Pod.pm $lang:blib/man/$lang/man3/Locale::Po4a::Pod.3pm.pod \
add_$lang:?doc/addendum.$lang
[type: pod] lib/Locale/Po4a/RubyDoc.pm $lang:blib/man/$lang/man3/Locale::Po4a::RubyDoc.3pm.pod \
add_$lang:?doc/addendum.$lang
[type: pod] lib/Locale/Po4a/Sgml.pm $lang:blib/man/$lang/man3/Locale::Po4a::Sgml.3pm.pod \
add_$lang:?doc/addendum.$lang
[type: pod] lib/Locale/Po4a/TeX.pm $lang:blib/man/$lang/man3/Locale::Po4a::TeX.3pm.pod \
add_$lang:?doc/addendum.$lang
[type: pod] lib/Locale/Po4a/Texinfo.pm $lang:blib/man/$lang/man3/Locale::Po4a::Texinfo.3pm.pod \
add_$lang:?doc/addendum.$lang
[type: pod] lib/Locale/Po4a/Text.pm $lang:blib/man/$lang/man3/Locale::Po4a::Text.3pm.pod \
add_$lang:?doc/addendum.$lang
[type: pod] lib/Locale/Po4a/TransTractor.pm $lang:blib/man/$lang/man3/Locale::Po4a::TransTractor.3pm.pod \
add_$lang:?doc/addendum.$lang
[type: pod] lib/Locale/Po4a/Wml.pm $lang:blib/man/$lang/man3/Locale::Po4a::Wml.3pm.pod \
add_$lang:?doc/addendum.$lang
[type: pod] lib/Locale/Po4a/Xhtml.pm $lang:blib/man/$lang/man3/Locale::Po4a::Xhtml.3pm.pod \
add_$lang:?doc/addendum.$lang
[type: pod] lib/Locale/Po4a/Xml.pm $lang:blib/man/$lang/man3/Locale::Po4a::Xml.3pm.pod \
add_$lang:?doc/addendum.$lang
[type: pod] lib/Locale/Po4a/Yaml.pm $lang:blib/man/$lang/man3/Locale::Po4a::Yaml.3pm.pod \
add_$lang:?doc/addendum.$lang
# Deactivated modules
# not tested
#[type: pod] lib/Locale/Po4a/Debconf.pm $lang:blib/man/$lang/man3/Locale::Po4a::Debconf.3pm.pod \
# add_$lang:?doc/addendum.$lang
# deprecated, use Xhtml instead
#[type: pod] lib/Locale/Po4a/Html.pm $lang:blib/man/$lang/man3/Locale::Po4a::Html.3pm.pod \
# add_$lang:?doc/addendum.$lang
# deprecated, use Text instead
#[type: pod] lib/Locale/Po4a/NewsDebian.pm $lang:blib/man/$lang/man3/Locale::Po4a::NewsDebian.3pm.pod \
# add_$lang:?doc/addendum.$lang

14017
po/pod/ca.po Normal file

File diff suppressed because it is too large Load Diff

16985
po/pod/de.po Normal file

File diff suppressed because it is too large Load Diff

9917
po/pod/eo.po Normal file

File diff suppressed because it is too large Load Diff

16488
po/pod/es.po Normal file

File diff suppressed because it is too large Load Diff

16570
po/pod/fr.po Normal file

File diff suppressed because it is too large Load Diff

9880
po/pod/hr.po Normal file

File diff suppressed because it is too large Load Diff

9921
po/pod/hu.po Normal file

File diff suppressed because it is too large Load Diff

12633
po/pod/it.po Normal file

File diff suppressed because it is too large Load Diff

15753
po/pod/ja.po Normal file

File diff suppressed because it is too large Load Diff

10020
po/pod/nb.po Normal file

File diff suppressed because it is too large Load Diff

12944
po/pod/nl.po Normal file

File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More