mirror of https://gitee.com/openkylin/po4a.git
Import Upstream version 0.66
This commit is contained in:
commit
424e60c585
|
@ -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>
|
|
@ -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;
|
|
@ -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
|
|
@ -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.
|
|
@ -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"
|
||||
}
|
|
@ -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'
|
|
@ -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;
|
|
@ -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.
|
|
@ -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)
|
|
@ -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)
|
||||
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
PO4A-HEADER:mode=after;position=^=head1 AUTEURS;beginboundary=^=head1
|
||||
|
||||
=head1 TRADUCTION
|
||||
|
||||
Martin Quinson (mquinson#debian.org)
|
||||
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
PO4A-HEADER:mode=after;position=^=head1 AUTORZY;beginboundary=^=head1
|
||||
|
||||
=head1 TŁUMACZENIE
|
||||
|
||||
Robert Luberda <robert@debian.org>
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
PO4A-HEADER:mode=after;position=^=head1 作者;beginboundary=^=head1
|
||||
|
||||
=head1 翻译
|
||||
|
||||
taotieren <admin@taotieren.com>
|
||||
|
|
@ -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.
|
|
@ -0,0 +1,6 @@
|
|||
PO4A-HEADER:mode=after;position=^\.SH AUTOR;beginboundary=^\.SH
|
||||
|
||||
.SH TRADUCCION
|
||||
|
||||
Omar Campagne <ocampagne@gmail.com>
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
PO4A-HEADER:mode=after;position=^\.SH AUTEUR;beginboundary=^\.SH
|
||||
|
||||
.SH TRADUCTION
|
||||
|
||||
Nicolas François <nicolas.francois@centraliens.net>
|
||||
|
|
@ -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>
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
PO4A-HEADER:mode=after;position=^\.SH AUTOR;beginboundary=^\.SH
|
||||
|
||||
.SH TŁUMACZENIE
|
||||
|
||||
Robert Luberda <robert@debian.org>
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
PO4A-HEADER:mode=after;position=^\.SH 作者;beginboundary=^\.SH
|
||||
|
||||
.SH 翻译
|
||||
|
||||
taotieren <admin@taotieren.com>
|
||||
|
|
@ -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
|
File diff suppressed because it is too large
Load Diff
|
@ -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).
|
|
@ -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
|
|
@ -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
|
|
@ -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;
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -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;
|
||||
}
|
|
@ -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
|
||||
®ister_generic_command &is_closed &translate_buffer
|
||||
®ister_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;
|
|
@ -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).
|
|
@ -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).
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
||||
®ister_generic_command
|
||||
®ister_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;
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -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
|
|
@ -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
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -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
|
||||
®ister_generic_command &is_closed &translate_buffer
|
||||
®ister_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;
|
|
@ -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
|
@ -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).
|
|
@ -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'} = ".";
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -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
|
|
@ -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__;
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -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
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
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
Loading…
Reference in New Issue