Import Upstream version 0.69

This commit is contained in:
luoyaoming 2024-05-08 10:01:08 +08:00
parent 60b4741885
commit 7e5a3fd886
244 changed files with 59940 additions and 30450 deletions

44
.appveyor.yml Normal file
View File

@ -0,0 +1,44 @@
image:
- Visual Studio 2015
- Ubuntu
init:
- git config --global core.autocrlf true # checkout Windows-style, commit Unix-style
clone_depth: 1
environment:
PERL5LIB: /home/appveyor/perl5/lib/perl5
platform: x64
branches:
only:
- master
- appveyor
skip_tags: true
cache:
- C:\strawberry -> .appveyor.yml
install:
- cmd: if not exist "C:\strawberry" cinst strawberryperl
- cmd: set PATH=C:\strawberry\perl\bin;C:\strawberry\perl\site\bin;C:\strawberry\c\bin;C:\cygwin64\bin;%PATH%
- cmd: cd C:\projects\%APPVEYOR_PROJECT_NAME%
- cmd: rename "C:\Program Files\Git\usr\bin\sh.exe" "sh-ignored.exe" # this shell does like && and such
- sh: sudo apt-get install build-essential git libssl-dev
- sh: sudo apt-get install -y perl cpanminus opensp
- sh: export PATH=/home/appveyor/perl5/bin:$PATH
- perl -v
- sh: cpanm -v Locale::gettext # Needs libintl that I fail to install on appveyor/native
- sh: cpanm -v Text::WrapI18N
- cpanm http://search.cpan.org/CPAN/authors/id/R/RA/RAAB/SGMLSpm-1.1.tar.gz
- cpanm Unicode::GCString
- cpanm Term::ReadKey
- cpanm YAML::Tiny
- cpanm -v --installdeps --notest .
build_script:
- perl Build.PL
- ./Build test

40
.github/workflows/linux.yml vendored Normal file
View File

@ -0,0 +1,40 @@
# This workflow will build po4a on linux using Module::Build
name: Build on Linux CI
on:
push:
branches:
- '*'
tags-ignore:
- '*'
pull_request:
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Install Debian dependencies
run: |
sudo apt update
sudo apt install -y liblocale-gettext-perl libtext-wrapi18n-perl libunicode-linebreak-perl libpod-parser-perl libtest-pod-perl libyaml-tiny-perl libsyntax-keyword-try-perl
sudo apt install -y cpanminus gettext docbook-xml docbook-xsl docbook xsltproc
sudo apt install -y texlive-binaries texlive-latex-base opensp libsgmls-perl
- name: Install CPAN dependencies
run: |
cpanm Locale::gettext
cpanm http://search.cpan.org/CPAN/authors/id/R/RA/RAAB/SGMLSpm-1.1.tar.gz
cpanm Text::WrapI18N
cpanm Unicode::GCString
cpanm -v --installdeps --notest .
- name: Build
run: |
perl Build.PL
COLUMNS=120 ./Build verbose=1
- name: Test
run: ./Build test verbose=1

46
.github/workflows/windows.does_not_work vendored Normal file
View File

@ -0,0 +1,46 @@
# This does not work because the tests rely heavily on the diff utility
# that is not available on Windows. We could change it to use a Perl
# implementation of diff. PR would be very welcomed here.
# This workflow will build po4a on linux using Module::Build
name: Build on Windows CI
on:
push:
branches:
- '*'
tags-ignore:
- '*'
pull_request:
jobs:
build:
runs-on: windows-latest
steps:
- uses: actions/checkout@v2
- uses: shogo82148/actions-setup-perl@v1
with:
#perl-version: '5.32'
distribution: strawberry
install-modules-with: cpanm
install-modules: Term::ReadKey Unicode::GCString Syntax::Keyword::Try http://search.cpan.org/CPAN/authors/id/R/RA/RAAB/SGMLSpm-1.1.tar.gz YAML::Tiny
# Locale::gettext and Text::WrapI18N seem broken on windows.
- run: perl -V
# - name: Install Debian dependencies
# run: |
# sudo apt update
# sudo apt install -y liblocale-gettext-perl libtext-wrapi18n-perl libunicode-linebreak-perl libtest-pod-perl libyaml-tiny-perl libsyntax-keyword-try-perl
# sudo apt install -y cpanminus gettext docbook-xml docbook-xsl docbook xsltproc
# sudo apt install -y texlive-binaries texlive-latex-base opensp libsgmls-perl
- run: cpanm -v --installdeps --notest .
- name: Create the Build script
run: perl Build.PL
- name: Actual build
run: ./Build verbose=1
- name: Test
run: ./Build test verbose=1

10
.gitignore vendored Normal file
View File

@ -0,0 +1,10 @@
*~
Build
META.yml
META.json
MYMETA.json
MYMETA.yml
_build/
blib/
t/tmp/
.tidyall.d/

5
.tidyallrc Normal file
View File

@ -0,0 +1,5 @@
[PerlTidy]
select = po4a{,-gettextize,-normalize,-translate,-updatepo}
select = lib/**/*.pm
select = t/*.{t,pm}
argv = -utf8 -log -noll --maximum-line-length=120 --iterations=2 --cuddled-else

41
.travis.yml Normal file
View File

@ -0,0 +1,41 @@
language: perl
dist: bionic
addons:
apt:
packages:
- liblocale-gettext-perl
- libtext-wrapi18n-perl
- libunicode-linebreak-perl
- libtest-pod-perl
- gettext
- docbook-xml
- docbook-xsl
- docbook
- xsltproc
- texlive-binaries
- texlive-latex-base
- opensp
- libsgmls-perl
- docbook
before_install:
- cpanm Locale::gettext
- cpanm http://search.cpan.org/CPAN/authors/id/R/RA/RAAB/SGMLSpm-1.1.tar.gz
- cpanm Text::WrapI18N
- cpanm Unicode::GCString
install:
- cpanm -v --installdeps --notest .
script:
- perl Build.PL
- COLUMNS=120 ./Build verbose=1
- ./Build test verbose=1
notifications:
email:
on_success: change
on_failure: always
irc:
channels:
- "irc.debian.org#po4a"

3
.weblate Normal file
View File

@ -0,0 +1,3 @@
[weblate]
url = https://hosted.weblate.org/api/
translation = po4a/po4a

View File

@ -35,7 +35,7 @@ my $build = Po4aBuilder->new
dist_abstract => 'Maintain the translations of your documentation with ease (PO for anything)', dist_abstract => 'Maintain the translations of your documentation with ease (PO for anything)',
dist_author => ['Martin Quinson (mquinson#debian.org)', dist_author => ['Martin Quinson (mquinson#debian.org)',
'Denis Barbier <barbier@linuxfr.org>', 'Denis Barbier <barbier@linuxfr.org>',
'Nicolas Francois <nicolas.francois@centraliens.net>', 'Nicolas François <nicolas.francois@centraliens.net>',
'Neil Williams <linux@codehelp.co.uk>'] 'Neil Williams <linux@codehelp.co.uk>']
); );

View File

@ -64,6 +64,7 @@ po/bin/hu.po
po/bin/id.po po/bin/id.po
po/bin/it.po po/bin/it.po
po/bin/ja.po po/bin/ja.po
po/bin/ka.po
po/bin/kn.po po/bin/kn.po
po/bin/ko.po po/bin/ko.po
po/bin/nb.po po/bin/nb.po
@ -101,6 +102,7 @@ po/pod/ru.po
po/pod/sr_Cyrl.po po/pod/sr_Cyrl.po
po/pod/uk.po po/pod/uk.po
po/pod/zh_CHS.po po/pod/zh_CHS.po
po/pod/zh_Hant.po
msguntypot msguntypot
po4a po4a
po4a-gettextize po4a-gettextize
@ -239,6 +241,13 @@ t/cfg/single-podirectory-emptypot/po/fr.po
t/cfg/single-podirectory-emptypot/po/single.pot t/cfg/single-podirectory-emptypot/po/single.pot
t/cfg/single-podirectory-emptypot/po4a.conf t/cfg/single-podirectory-emptypot/po4a.conf
t/cfg/single-podirectory-emptypot/single.man.1 t/cfg/single-podirectory-emptypot/single.man.1
t/cfg/single-podirectory-emptypot-emptypo/_fr.po.expected
t/cfg/single-podirectory-emptypot-emptypo/_output
t/cfg/single-podirectory-emptypot-emptypo/_single.pot.expected
t/cfg/single-podirectory-emptypot-emptypo/fr.po
t/cfg/single-podirectory-emptypot-emptypo/po4a.conf
t/cfg/single-podirectory-emptypot-emptypo/single.man.1
t/cfg/single-podirectory-emptypot-emptypo/single.pot
t/cfg/multiple-fuzzy/po4a.conf t/cfg/multiple-fuzzy/po4a.conf
t/cfg/multiple-fuzzy/multiple.it.po t/cfg/multiple-fuzzy/multiple.it.po
t/cfg/multiple-fuzzy/multiple.man.de.1 t/cfg/multiple-fuzzy/multiple.man.de.1
@ -668,6 +677,11 @@ t/fmt/asciidoc/Footnotes.norm
t/fmt/asciidoc/Footnotes.po t/fmt/asciidoc/Footnotes.po
t/fmt/asciidoc/Footnotes.pot t/fmt/asciidoc/Footnotes.pot
t/fmt/asciidoc/Footnotes.trans t/fmt/asciidoc/Footnotes.trans
t/fmt/asciidoc/LineBreak.adoc
t/fmt/asciidoc/LineBreak.norm
t/fmt/asciidoc/LineBreak.po
t/fmt/asciidoc/LineBreak.pot
t/fmt/asciidoc/LineBreak.trans
t/fmt/asciidoc/Lists.adoc t/fmt/asciidoc/Lists.adoc
t/fmt/asciidoc/Lists.norm t/fmt/asciidoc/Lists.norm
t/fmt/asciidoc/Lists.norm.stderr t/fmt/asciidoc/Lists.norm.stderr
@ -675,6 +689,11 @@ t/fmt/asciidoc/Lists.po
t/fmt/asciidoc/Lists.pot t/fmt/asciidoc/Lists.pot
t/fmt/asciidoc/Lists.trans t/fmt/asciidoc/Lists.trans
t/fmt/asciidoc/Lists.trans.stderr t/fmt/asciidoc/Lists.trans.stderr
t/fmt/asciidoc/MacroIncludesHugo.adoc
t/fmt/asciidoc/MacroIncludesHugo.norm
t/fmt/asciidoc/MacroIncludesHugo.po
t/fmt/asciidoc/MacroIncludesHugo.pot
t/fmt/asciidoc/MacroIncludesHugo.trans
t/fmt/asciidoc/NoImageTarget.adoc t/fmt/asciidoc/NoImageTarget.adoc
t/fmt/asciidoc/NoImageTarget.norm t/fmt/asciidoc/NoImageTarget.norm
t/fmt/asciidoc/NoImageTarget.po t/fmt/asciidoc/NoImageTarget.po
@ -724,6 +743,11 @@ t/fmt/asciidoc/YamlFrontMatter.norm
t/fmt/asciidoc/YamlFrontMatter.po t/fmt/asciidoc/YamlFrontMatter.po
t/fmt/asciidoc/YamlFrontMatter.pot t/fmt/asciidoc/YamlFrontMatter.pot
t/fmt/asciidoc/YamlFrontMatter.trans t/fmt/asciidoc/YamlFrontMatter.trans
t/fmt/asciidoc/YamlFrontMatter_KeysPaths.adoc
t/fmt/asciidoc/YamlFrontMatter_KeysPaths.norm
t/fmt/asciidoc/YamlFrontMatter_KeysPaths.po
t/fmt/asciidoc/YamlFrontMatter_KeysPaths.pot
t/fmt/asciidoc/YamlFrontMatter_KeysPaths.trans
t/fmt/asciidoc/YamlFrontMatter_Option.adoc t/fmt/asciidoc/YamlFrontMatter_Option.adoc
t/fmt/asciidoc/YamlFrontMatter_Option.norm t/fmt/asciidoc/YamlFrontMatter_Option.norm
t/fmt/asciidoc/YamlFrontMatter_Option.po t/fmt/asciidoc/YamlFrontMatter_Option.po
@ -795,6 +819,20 @@ t/fmt/man/macros.norm
t/fmt/man/macros.po t/fmt/man/macros.po
t/fmt/man/macros.pot t/fmt/man/macros.pot
t/fmt/man/macros.trans t/fmt/man/macros.trans
t/fmt/man/macro-def.man
t/fmt/man/macro-def.norm
t/fmt/man/macro-def.po
t/fmt/man/macro-def.pot
t/fmt/man/macro-def.stderr-missing-behavior
t/fmt/man/macro-def.trans
t/fmt/man/macro-defuse.man
t/fmt/man/macro-defuse.norm
t/fmt/man/macro-defuse.po
t/fmt/man/macro-defuse.po-noarg
t/fmt/man/macro-defuse.pot
t/fmt/man/macro-defuse.pot-noarg
t/fmt/man/macro-defuse.stderr-missing-behavior
t/fmt/man/macro-defuse.trans
t/fmt/man/mdoc.man t/fmt/man/mdoc.man
t/fmt/man/mdoc.norm t/fmt/man/mdoc.norm
t/fmt/man/mdoc.po t/fmt/man/mdoc.po
@ -985,14 +1023,26 @@ t/fmt/txt-markdown/Rules.pot
t/fmt/txt-markdown/Rules.trans t/fmt/txt-markdown/Rules.trans
t/fmt/txt-markdown/YamlFrontMatter.md t/fmt/txt-markdown/YamlFrontMatter.md
t/fmt/txt-markdown/YamlFrontMatter.norm t/fmt/txt-markdown/YamlFrontMatter.norm
t/fmt/txt-markdown/YamlFrontMatter.po
t/fmt/txt-markdown/YamlFrontMatter.pot
t/fmt/txt-markdown/YamlFrontMatter.trans
t/fmt/txt-markdown/YamlFrontMatter_fake.md
t/fmt/txt-markdown/YamlFrontMatter_fake.norm
t/fmt/txt-markdown/YamlFrontMatter_fake.norm.stderr
t/fmt/txt-markdown/YamlFrontMatter_fake.po
t/fmt/txt-markdown/YamlFrontMatter_fake.pot
t/fmt/txt-markdown/YamlFrontMatter_fake.trans
t/fmt/txt-markdown/YamlFrontMatter_fake.trans.stderr
t/fmt/txt-markdown/YamlFrontMatter_KeysPaths.md
t/fmt/txt-markdown/YamlFrontMatter_KeysPaths.norm
t/fmt/txt-markdown/YamlFrontMatter_KeysPaths.po
t/fmt/txt-markdown/YamlFrontMatter_KeysPaths.pot
t/fmt/txt-markdown/YamlFrontMatter_KeysPaths.trans
t/fmt/txt-markdown/YamlFrontMatter_Options.md t/fmt/txt-markdown/YamlFrontMatter_Options.md
t/fmt/txt-markdown/YamlFrontMatter_Options.norm t/fmt/txt-markdown/YamlFrontMatter_Options.norm
t/fmt/txt-markdown/YamlFrontMatter_Options.po t/fmt/txt-markdown/YamlFrontMatter_Options.po
t/fmt/txt-markdown/YamlFrontMatter_Options.pot t/fmt/txt-markdown/YamlFrontMatter_Options.pot
t/fmt/txt-markdown/YamlFrontMatter_Options.trans t/fmt/txt-markdown/YamlFrontMatter_Options.trans
t/fmt/txt-markdown/YamlFrontMatter.po
t/fmt/txt-markdown/YamlFrontMatter.pot
t/fmt/txt-markdown/YamlFrontMatter.trans
t/fmt/wml/basic.norm t/fmt/wml/basic.norm
t/fmt/wml/basic.po t/fmt/wml/basic.po
t/fmt/wml/basic.pot t/fmt/wml/basic.pot
@ -1014,6 +1064,11 @@ t/fmt/xhtml/includessi.norm
t/fmt/xhtml/includessi.po t/fmt/xhtml/includessi.po
t/fmt/xhtml/includessi.pot t/fmt/xhtml/includessi.pot
t/fmt/xhtml/includessi.trans t/fmt/xhtml/includessi.trans
t/fmt/xhtml/table.html
t/fmt/xhtml/table.norm
t/fmt/xhtml/table.po
t/fmt/xhtml/table.pot
t/fmt/xhtml/table.trans
t/fmt/xml/attribute-novalue.desc t/fmt/xml/attribute-novalue.desc
t/fmt/xml/attribute-novalue.norm t/fmt/xml/attribute-novalue.norm
t/fmt/xml/attribute-novalue.po t/fmt/xml/attribute-novalue.po
@ -1045,12 +1100,22 @@ t/fmt/xml-dia/transl.norm
t/fmt/xml-dia/transl.po t/fmt/xml-dia/transl.po
t/fmt/xml-dia/transl.pot t/fmt/xml-dia/transl.pot
t/fmt/xml-dia/transl.trans t/fmt/xml-dia/transl.trans
t/fmt/xml/inside-foldattribute.norm
t/fmt/xml/inside-foldattribute.po
t/fmt/xml/inside-foldattribute.pot
t/fmt/xml/inside-foldattribute.trans
t/fmt/xml/inside-foldattribute.xml
t/fmt/xml/options.desc t/fmt/xml/options.desc
t/fmt/xml/options.norm t/fmt/xml/options.norm
t/fmt/xml/options.po t/fmt/xml/options.po
t/fmt/xml/options.pot t/fmt/xml/options.pot
t/fmt/xml/options.trans t/fmt/xml/options.trans
t/fmt/xml/options.xml t/fmt/xml/options.xml
t/fmt/xml/placeholder-empty.norm
t/fmt/xml/placeholder-empty.po
t/fmt/xml/placeholder-empty.pot
t/fmt/xml/placeholder-empty.trans
t/fmt/xml/placeholder-empty.xml
t/fmt/yaml/basic.norm t/fmt/yaml/basic.norm
t/fmt/yaml/basic.po t/fmt/yaml/basic.po
t/fmt/yaml/basic.pot t/fmt/yaml/basic.pot
@ -1081,6 +1146,13 @@ t/fmt/yaml/utf8.po
t/fmt/yaml/utf8.pot t/fmt/yaml/utf8.pot
t/fmt/yaml/utf8.trans t/fmt/yaml/utf8.trans
t/fmt/yaml/utf8.yaml t/fmt/yaml/utf8.yaml
t/gettextize/TEST_OK.md
t/gettextize/test_dups.err
t/gettextize/test_dups.md
t/gettextize/test_dups.po
t/gettextize/test_ok.err
t/gettextize/test_ok.md
t/gettextize/test_ok.po
t/add.t t/add.t
t/cfg-args.t t/cfg-args.t
@ -1107,6 +1179,7 @@ t/fmt-xhtml.t
t/fmt-xml-dia.t t/fmt-xml-dia.t
t/fmt-xml.t t/fmt-xml.t
t/fmt-yaml.t t/fmt-yaml.t
t/gettextize.t
t/pod.t t/pod.t

40
MANIFEST.SKIP Normal file
View File

@ -0,0 +1,40 @@
^\.git/
^\.gitignore
^\.svn/
\.bak$
~$
.travis.yml
.weblate
.appveyor.yml
\.tidyall.d/
\.tidyallrc
\.github/
po/bin/zanata.xml
po/pod/zanata.xml
# Avoid configuration metadata file
^MYMETA\.
# Avoid Module::Build generated and utility files.
\bBuild$
\bBuild.bat$
\b_build
\bBuild.COM$
\bBUILD.COM$
\bbuild.com$
^MANIFEST\.SKIP
# Avoid archives of this distribution
\bpo4a-[\d\.\_]+
^blib/
^lib/Locale/Po4a/Debconf.pm
^lib/Locale/Po4a/NewsDebian.pm
^extension/
^testsuite/
^t/tmp/
^MYMETA\.yml$
^MYMETA\.json$
^share/Makefile$
^share/doc/.*/po4a.*\.xml

134
META.json
View File

@ -1,134 +0,0 @@
{
"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"
}

View File

@ -1,86 +0,0 @@
---
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'

230
NEWS
View File

@ -1,6 +1,234 @@
This file presents the major changes of each version, focusing on This file presents the major changes of each version, focusing on
user-visible changes while the day to day changes are in the git logs. user-visible changes while the day to day changes are in the git logs.
=======================================================================
___ __ ___
__ __/ _ \ / /_ / _ \
\ \ / / | | | '_ \ (_) | Happy New Year release.
\ V /| |_| | (_) \__, |
\_/ \___(_)___/ /_/ (Released on 2023/01/01)
Markdown:
* Parts of a fenced divs are now translated separately (GitHub's #381)
YAML and Yaml Front Matter:
* Use the implementation of YFM to parse YAML files too.
- This reduces the code dupplication, and brings the nice features over:
Numbers and arrays are not quoted anymore in Yaml files, as it should be.
- This adds the yfm_paths option to Markdown and Asciidoc YFM,
fixing GitHub's #392 along the way.
- Please note that '-o keys' is now case-sensitive in Yaml documents.
* Do not translate nor quote booleans, so that they are not treated as string.
Thanks Gissmo for GitHub's PR #393, even if I had to rework it quite a bit.
XML:
* Correctly handle placeholders of empty elements (GitHub's #389)
* Translate attributes within attribute-folded inline and placeholder (GH #391)
alt attribute of <img> can be properly handled with:
-o 'attributes=<image>alt' -o 'inline=<image>' -o foldattributes
* Thanks k-yaegashi for the pull request fixing both issues
Project organization:
* Do not leak the generation date in our manpages (GitHub's #387)
Thanks Arnout Engelen for the pull request.
Translations:
* New translation: Georgian, thanks Temuri Doghonadze.
* Updated: Dutch, thanks Frans Spiesschaert.
* Updated: French, thanks Jérémie Tarot & Brandelune.
* Updated: Italian, thanks Marco Ciampa.
* Updated: Japanese, thanks gemmaro.
* Updated: Portuguese, thanks Hugo Carvalho.
* Updated: Portuguese (Brazil), thanks Rafael Fontenelle.
* Updated: Ukrainian, thanks Yuri Chornoivan (українська).
Status of the binary translation:
* 100%: fr, it, nl, pt_BR, uk.
* >95%: eo (96%), nb (96%), pt (99%), sr_Cyrl (99%).
* >90%: de (94%), es (94%), hr (94%), hu (92%), ru (93%), zh_CN (94%), zh_Hant (94%).
* >70%: id (77%), sv (72%).
* >50%: cs (57%), da (62%), et (67%), eu (56%), ja (63%), pl (62%), sl (56%), vi (67%).
* >33%: ca (45%).
* Starting: ace (2%), af (7%), ar (14%), ka (16%), kn (7%), ko (18%), zh_HK (3%).
Status of the documentation translation:
* 100%: it, uk.
* >95%: de (98%), fr (99%), ja (96%), nl (99%), sr_Cyrl (98%).
* >90%: pt (93%), pt_BR (94%), zh_CHS (94%), zh_Hant (94%).
* >80%: es (83%).
* >50%: pl (66%), ru (64%).
* >33%: ca (43%).
* Starting: eo (2%), hr (0%), hu (0%), nb (4%).
=======================================================================
___ __ ___
__ __/ _ \ / /_ ( _ )
\ \ / / | | | '_ \ / _ \ High Hopes release.
\ V /| |_| | (_) | (_) |
\_/ \___(_)___/ \___/ (Released on 2022/09/04)
This release dedication is here to remind you that Hope is always important.
Plus, the song of that name by Pink Floyd also deserves a dedication.
Project organization:
* Add a deprecation warning to po4a-translate and po4a-updatepo stating
that po4a is the prefered interface.
* Rewrite the po4a-gettextize documentation (Debian's #1016695) [Mt].
* Rewrite the intro of po4a doc, and add a Quick start tutorial [Mt].
gettextization:
* Rework completely the deduplication of msgids introduced in v0.67.
- Instead of adding spaces and require the users to merge them
manually after an extremely painful analysis of the produced file,
automatically merge the msgstr together.
- The result will look something like:
#, fuzzy
msgid "hello"
msgstr ""
"#-#-#-#-# file reference 1 (type: Title #) #-#-#-#-#\n"
"HELLO\n"
"#-#-#-#-# file reference 2 (type: Title ##) #-#-#-#-#\n"
"SUBTITLE\n"
- Closes Debian's #1017742, that was asking for help in the review [Mt].
Markdown:
* Allow lists to start the line with no leading spaces before the '-'
sign (Github's #378). This uncovers a bug in our test suite that
was enforcing the buggy behavior :( [Mt]
* Properly deal with fenced div blocks (Github's #381) [Mt]
Man:
* Bold text should never be wrapped as groff seems to reset the font
on new line (Debian's #1016753) [Mt].
* Properly deal with comments around macro definitions [Mt]
(Debian's #1017837).
Translations:
* Updated: Croatian, thanks Milo Ivir.
* Updated: Esperanto, thanks Marco Ciampa.
* Updated: German, thanks Helge Kreutzmann.
* Updated: Italian, thanks Marco Ciampa.
* Updated: Japanese, thanks gemmaro.
* Updated: Portuguese, thanks Hugo Carvalho.
* Updated: Serbian (cyrillic), thanks Ivan Pesic.
* Updated: Ukrainian, thanks Yuri Chornoivan (українська) & Artem.
Status of the binary translation:
* 100%: de, pt, sr_Cyrl, uk.
* >95%: eo (97%), es (95%), fr (97%), hr (95%), it (97%), nb (97%),
nl (95%), pt_BR (97%), zh_CN (95%), zh_Hant (95%).
* >90%: hu (93%), ru (93%).
* >70%: id (78%), sv (72%).
* >50%: cs (58%), da (62%), et (68%), eu (56%), ja (62%), pl (62%),
sl (56%), vi (67%).
* >33%: ca (45%).
* Starting: ace (2%), af (7%), ar (14%), kn (7%), ko (18%), zh_HK (3%).
Status of the documentation translation:
* 100%: it, uk.
* >95%: de (99%), fr (95%), pt_BR (95%), sr_Cyrl (99%).
* >90%: nl (94%), pt (94%), zh_CHS (94%), zh_Hant (94%).
* >80%: es (83%).
* >70%: ja (72%).
* >50%: pl (66%), ru (64%).
* >33%: ca (43%).
* Starting: eo (2%), hr (0%), hu (0%), nb (4%).
=======================================================================
___ __ _____
__ __/ _ \ / /|___ |
\ \ / / | | | '_ \ / / The Maryna Viazovska Release.
\ V /| |_| | (_) / /
\_/ \___(_)___/_/ (Released on 2022/07/14)
Maryna Viazovska was recently awarded a Fields Medal for her work on
the sphere-packing problem in higher dimension.
Asciidoc:
* Support Hugo shortcode on macros (GitHub's #352) [Danilo G. Baio]
* Support automatically numbered callouts (GitHub's #351) [J.N. Avila]
* Support line breaks (GitHub's #199) [J.N. Avila]
gettextization:
* Deduplicate the msgids, to prevent the structure desynchronizations
when the same string is used at several locations, with mismatching types.
(GitHub's #334) [mquinson]
* Add some tests for the gettextization feature. [mquinson]
po4a-translate:
* Do not check whether the po file is uptodate, as it's impossible to do
that correctly when several master files are merged in the same POT.
(GitHub's #156) [mquinson]
Man:
* Improve the error message when using '.de', '.if', '.ie' to hint about
the option 'groff_code=verbatim|translate' (GitHub's #361) [mquinson]
* Implement '.el' (else branch) as translate_joined [mquinson]
* Implement '.de1' (define with no check) as '.de' [mquinson]
* Implement '.dei', '.dei1' (define with subst) (Debian's #710678) [MQ]
* Implement '.it' (input trap) as untranslated (GitHub's #339) [mquinson]
* Make a sensible warning when user-defined macros are not explained to po4a
with the 'untranslated' option and friends. [mquinson]
Markdown:
* New option 'yfm_lenient' to proceed even if the YAML Front Matter parser fails.
Useful when your file starts with an horizontal ruler that is not a YFM.
(GitHub's #365) [mquinson]
This introduces a NEW DEPENDENCY: Syntax::Keyword::Try
* Do not quote the YFM lists to not break them (GitHub's #304) [mquinson].
Thanks @dbaio for the analysis and @ynojima for an initial patch in doc-l10n-kit.
xHTML:
* Add a test for tables and stop claiming that they may be broken
(GitHub's #366, thanks Petter Reinholdtsen).
Other bug fixes:
* Hide an ugly warning about uninitialized string (GitHub's #360) [mquinson]
Translations:
* Updated: Chinese (simplified and traditional), thanks taotieren.
* Updated: Dutch, thanks Frans Spiesschaert.
* Updated: Esperanto, thanks Marco Ciampa.
* Updated: French, thanks jmichault and Jérémie Tarot.
* Updated: German, thanks Helge Kreutzmann.
* Updated: Italian, thanks Marco Ciampa.
* Updated: Norwegian Bokmål, thanks Petter Reinholdtsen.
* Updated: Portuguese, thanks Hugo Carvalho & Silvério Santos.
* Updated: Portuguese (Brazil), thanks Rafael Fontenelle and Luiz Fernando Ranghetti.
* Updated: Serbian (cyrillic), thanks Ivan Pesic.
* Updated: Spanish, thanks Francisco Serrador.
* Updated: Ukrainian, thanks Yuri Chornoivan (українська).
* Updated: Vietnamese, thanks Trần Ngọc Quân.
Status of the binary translation:
* 5 languages = 100%: de, fr, nb, pt_BR, sr_Cyrl.
* 11 languages >= 95%: eo (97%), es (97%), hr (97%), hu (95%),
it (97%), nl (97%), pt (97%), ru (96%),
uk (98%), zh_CN (97%), zh_Hant(97%).
* 1 language >= 80%: id (80%).
* 1 language >= 70%: sv (74%).
* 8 languages >= 50%: cs (59%), da (62%), et (69%), eu (58%),
ja (64%), pl (64%), sl (57%), vi (69%).
* 1 language >= 33%: ca (46%).
* 6 starting languages: ace (2%), af (7%), ar (14%), kn (7%),
ko(19%), zh_HK (3%).
Status of the documentation translation:
* 2 languages = 100%: fr, uk.
* 8 languages >= 95%: de (98%), it (99%), nl (98%), pt (97%),
pt_BR (99%), sr_Cyrl (99%), zh_CHS (98%),
zh_Hant (98%).
* 1 language >= 80%: es (87%).
* 1 language >= 70%: ja (74%).
* 2 languages >= 50%: pl (67%), ru (68%).
* 1 language >= 33%: ca (44%).
* 4 starting languages: eo (2%), hr (0%), hu (0%), nb (4%).
======================================================================= =======================================================================
___ __ __ ___ __ __
__ __/ _ \ / /_ / /_ __ __/ _ \ / /_ / /_
@ -61,7 +289,7 @@ Status of the documentation translation:
1 language >= 70%: es (78%). 1 language >= 70%: es (78%).
3 languages >= 50%: ja (69%), pl (69%), ru (69%). 3 languages >= 50%: ja (69%), pl (69%), ru (69%).
1 language >= 33%: ca (44%). 1 language >= 33%: ca (44%).
4 starting languages: eo (2%), hr (0%), hu (0%), nb (3%). 4 starting languages: eo (2%), hr (0%), hu (0%), nb (3%).
======================================================================= =======================================================================
___ __ ____ ___ __ ____

View File

@ -14,8 +14,8 @@ sub ACTION_build {
$self->depends_on('code'); $self->depends_on('code');
$self->depends_on('docs'); $self->depends_on('docs');
$self->depends_on('distmeta'); # regenerate META.yml $self->depends_on('distmeta'); # regenerate META.yml
$self->depends_on('man'); $self->depends_on('man') unless ($^O eq 'MSWin32');
$self->depends_on('postats'); $self->depends_on('postats') unless ($^O eq 'MSWin32');
} }
sub make_files_writable { sub make_files_writable {
@ -234,27 +234,29 @@ sub ACTION_man {
} }
$parser->parse_from_file ($file, $out); $parser->parse_from_file ($file, $out);
system("gzip -9 -f $out") and die; system("gzip -9 -n -f $out") and die;
unlink "$file" || die; unlink "$file" || die;
} }
# Install the manpages written in XML DocBook if ($^O ne 'MSWin32') {
foreach $file (qw(po4a-display-man.xml po4a-display-pod.xml)) { # Install the manpages written in XML DocBook
copy ( File::Spec->catdir("share", "doc", $file), $man1path) or die; 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$,) { foreach $file (@{$self->rscan_dir($manpath, qr{\.xml$})}) {
my ($outdir, $section, $outfile) = ($1, $2, $3); if ($file =~ m,(.*/man(.))/([^/]*)\.xml$,) {
if (-e "/usr/share/xml/docbook/stylesheet/docbook-xsl/manpages/docbook.xsl") { # Location on Debian at least my ($outdir, $section, $outfile) = ($1, $2, $3);
print "Convert $outdir/$outfile.$section (local docbook.xsl file). "; if (-e "/usr/share/xml/docbook/stylesheet/docbook-xsl/manpages/docbook.xsl") { # Location on Debian at least
system("xsltproc -o $outdir/$outfile.$section --nonet /usr/share/xml/docbook/stylesheet/docbook-xsl/manpages/docbook.xsl $file") and die; print "Convert $outdir/$outfile.$section (local docbook.xsl file). ";
} else { # Not found locally, use the XSL file online system("xsltproc -o $outdir/$outfile.$section --nonet /usr/share/xml/docbook/stylesheet/docbook-xsl/manpages/docbook.xsl $file") and die;
print "Convert $outdir/$outfile.$section (online docbook.xsl file). "; } else { # Not found locally, use the XSL file online
system("xsltproc -o $outdir/$outfile.$section --nonet http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl $file") and die; 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; }
system ("gzip -9 -n -f $outdir/$outfile.$section") and die;
}
unlink "$file" || die;
} }
unlink "$file" || die;
} }
} }

View File

@ -1,6 +1,6 @@
# Introduction to Po4a # 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) [![Build Status](https://img.shields.io/github/actions/workflow/status/mquinson/po4a/linux.yml?style=flat-square&branch=main)](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) [![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 The goal of po4a (PO for anything) project is to ease translations (and
@ -130,7 +130,7 @@ the git tree), use the PERLLIB environment variable as such:
This program is free software; you may redistribute it and/or modify it This program is free software; you may redistribute it and/or modify it
under the terms of GPL (see COPYING file). under the terms of GPL (see COPYING file).
Copyright © 2002-2021 by SPI, inc. Copyright © 2002-2022 by SPI, inc.
Authors: Authors:
- Denis Barbier <barbier@linuxfr.org> - Denis Barbier <barbier@linuxfr.org>

View File

@ -3684,10 +3684,10 @@ po4a (0.18-1) unstable; urgency=low
"Displays and keeps" macros. This was needed to deal with the man pages "Displays and keeps" macros. This was needed to deal with the man pages
of shadow and dpkg. of shadow and dpkg.
- No groff nbsp (ie '\ ') on the last pos of the line, or groff adds an - No groff nbsp (ie '\ ') on the last pos of the line, or groff adds an
extra space. Thanks to Nicolas Francois for the info. extra space. Thanks to Nicolas François for the info.
- Convert \- to - on the fly. The asteatic of the printed man page may - Convert \- to - on the fly. The asteatic of the printed man page may
suffer (a hyphen is different of a minus sign in ps/pdf), but makes suffer (a hyphen is different of a minus sign in ps/pdf), but makes
translator's life easier. Thanks to Nicolas Francois for the expertise. translator's life easier. Thanks to Nicolas François for the expertise.
- Stick to groff_man(7) intead of man(7), and consider that when IP is - Stick to groff_man(7) intead of man(7), and consider that when IP is
given only one argument, that's the designator (which we have to given only one argument, that's the designator (which we have to
translate) instead of the column indentation. Thanks Francois. translate) instead of the column indentation. Thanks Francois.

5
debian/changelog vendored
View File

@ -1,5 +0,0 @@
po4a (0.66-ok1) yangtze; urgency=medium
* Build for openKylin.
-- openKylinBot <openKylinBot@openkylin.com> Mon, 25 Apr 2022 22:03:04 +0800

64
debian/control vendored
View File

@ -1,64 +0,0 @@
Source: po4a
Section: text
Priority: optional
Maintainer: Martin Quinson <mquinson@debian.org>
Uploaders: Jonas Smedegaard <dr@jones.dk>,
Denis Barbier <barbier@debian.org>,
Dr. Tobias Quathamer <toddy@debian.org>
Standards-Version: 4.6.0
Build-Depends-Indep: docbook,
docbook-xml,
docbook-xsl,
gettext,
libpod-parser-perl,
libsgmls-perl,
libterm-readkey-perl,
libunicode-linebreak-perl,
libyaml-tiny-perl,
opensp,
perl,
texlive-binaries,
texlive-latex-base,
xsltproc
Build-Depends:
debhelper-compat (= 13),
libmodule-build-perl
Rules-Requires-Root: no
Homepage: https://po4a.org/
Vcs-Git: https://salsa.debian.org/debian/po4a.git
Vcs-Browser: https://salsa.debian.org/debian/po4a
Package: po4a
Architecture: all
Multi-Arch: foreign
Depends: gettext,
libpod-parser-perl (>= 1.63),
libsgmls-perl,
libyaml-tiny-perl,
opensp,
${misc:Depends},
${perl:Depends}
Recommends: liblocale-gettext-perl,
libterm-readkey-perl,
libtext-wrapi18n-perl,
libunicode-linebreak-perl
Description: tools to ease the translation of documentation
po4a eases the translation of documentation and other textual document.
Even more interestingly, it makes it easy to maintain these
translations when the original document changes. This is done by
using the gettext tools in this new domain.
.
This package contains the main libraries of po4a, and the necessary
sub-modules to handle the following formats:
.
- Dia: uncompressed Dia diagrams.
- INI: the INI format
- KernelHelp: Help messages of each kernel compilation option.
- LaTeX: generic TeX or LaTeX format.
- Man: either roff or mdoc format.
- POD: Perl documentation format.
- SGML: either DebianDoc or DocBook DTD.
- RubyDoc: Ruby documentation format.
- Texinfo: the info page format.
- XML: very configurable (preconfigured for DocBook, XHTML, Guide, WML).
- Yaml: Yaml documents.

50
debian/copyright vendored
View File

@ -1,50 +0,0 @@
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Upstream-Name: po4a
Upstream-Contact: Martin Quinson <mquinson@debian.org>
Source: https://po4a.org
Files: *
Copyright: © 2002-2022 Martin Quinson <mquinson@debian.org>
© 2002-2022 Software in the Public Interest, Inc.
© 1995 David Megginson <dmeggins@aix1.uottawa.ca>
© 2004 Jordi Vilalta <jvprat@gmail.com>
© 2005-2009 Nicolas FRANÇOIS <nicolas.francois@centraliens.net>
© 2006 BitDefender
© 2009 Neil Williams <linux@codehelp.co.uk>
© 2018 Zero King (https://github.com/l2dy)
License: GPL-2.0+
Files: lib/Locale/Po4a/RubyDoc.pm
Copyright: © 2016-2017 Francesco Poli <invernomuto@paranoici.org>
© 2004 MoonWolf <moonwolf@moonwolf.com>
© 2011-2012 Youhei SASAKI <uwabami@gfd-dennou.org>
License: GPL-2.0+
Files: lib/Locale/Po4a/Yaml.pm
Copyright: © 2018 Brian Exelbierd
License: GPL-2.0+
Files: debian/*
Copyright: © 2002-2022 Martin Quinson <mquinson@debian.org>
© 2005-2009 Nicolas FRANÇOIS <nicolas.francois@centraliens.net>
© 2009 Neil Williams <linux@codehelp.co.uk>
© 2003-2013 Denis Barbier <barbier@debian.org>
© 2018-2020 Dr. Tobias Quathamer <toddy@debian.org>
License: GPL-2.0+
License: GPL-2.0+
This package 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 package is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>
.
On Debian systems, the complete text of the GNU General
Public License version 2 can be found in "/usr/share/common-licenses/GPL-2".

4
debian/dirs vendored
View File

@ -1,4 +0,0 @@
usr/bin
usr/share/man/fr/man1
usr/share/man/man1
usr/share/perl5/Locale/Po4a

4
debian/docs vendored
View File

@ -1,4 +0,0 @@
CONTRIBUTING.md
README.maintainers
README.md
NEWS

View File

@ -1,91 +0,0 @@
#!/usr/bin/perl -w
use strict;
use warnings;
# Copyright 2009 by Javier Fernández-Sanguino Peña <jfs@debian.org>
#
# 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
# Quick and dirty script to compare two msgids (when using PO files
# generated with --previous) with wdiff to find the differences between
# them.
# Usage: perl compare-msgids.pl < XX.po
use File::Temp;
my $DIFF1 = new File::Temp(TEMPLATE => "compare-msgids.XXXXXX");
my $DIFF2 = new File::Temp(TEMPLATE => "compare-msgids.XXXXXX");
my $fileh="";
my $diffblock = 0; # Does the current block has a --previous string?
my $nowrap = 0; # Can the current be rewrapped?
my $ref = ""; # Line reference for the current block
while (my $line = <STDIN>) {
$fileh="";
chomp $line;
$ref .= $line."\n" if $line =~ /^#: /;
$ref = "" if ($line eq "");
if ( $diffblock and $line =~ /^msgstr/ ) {
$diffblock = 0;
$nowrap = 0;
print $DIFF1 "\n\n";
print $DIFF2 "\n\n";
}
$diffblock = 1 if ( $line =~ /^\#\| msgid/ ) ;
$nowrap = 1 if ( $line =~ /^#,.*no-wrap/ ) ;
if ($diffblock) {
if (length $ref) {
print $DIFF1 $ref."\n";
print $DIFF1 $ref."\n";
print $DIFF2 $ref."\n";
$ref = "";
}
$fileh = $DIFF1 if ( $line =~ /^\#\| msgid/ ) ;
$fileh = $DIFF1 if ( $line =~ /^\#\| "/ ) ;
$fileh = $DIFF2 if ( $line =~ /^msgid/ ) ;
$fileh = $DIFF2 if ( $line =~ /^"/ ) ;
if ($fileh ne "") {
$line =~ s/^\#\| //;
print $fileh "\n" if ( $line =~ /^msgid_plural "/);
$line =~ s/^"//;
$line =~ s/^msgid "//;
$line =~ s/^msgid_plural "//;
$line =~ s/"$//;
print $fileh $line;
print $fileh "\n" if ($nowrap and $line =~ m/\\n$/);
}
}
}
close $DIFF1; close $DIFF2;
system ("wdiff", "-3", $DIFF1->filename, $DIFF2->filename)
or die "Failed to run wdiff.";
exit;

10
debian/gitlab-ci.yml vendored
View File

@ -1,10 +0,0 @@
# See https://salsa.debian.org/salsa-ci-team/pipeline
---
include:
- https://salsa.debian.org/salsa-ci-team/pipeline/raw/master/salsa-ci.yml
- https://salsa.debian.org/salsa-ci-team/pipeline/raw/master/pipeline-jobs.yml
variables:
SALSA_CI_REPROTEST_ENABLE_DIFFOSCOPE: 1
SALSA_CI_DISABLE_BUILD_PACKAGE_ANY: '1'

View File

@ -1 +0,0 @@
debian/examples/compare-msgids.pl

10
debian/rules vendored
View File

@ -1,10 +0,0 @@
#!/usr/bin/make -f
%:
dh $@
# Replace the shebang line in perl scripts to
# comply with Policy 10.4
override_dh_auto_install:
dh_auto_install
sed -i -e "1 s,^.*perl.*$$,#!/usr/bin/perl," $(CURDIR)/debian/po4a/usr/bin/*

View File

@ -1 +0,0 @@
3.0 (native)

3
debian/watch vendored
View File

@ -1,3 +0,0 @@
version=4
opts=filenamemangle=s/.+\/v?(\d\S+)\.tar\.gz/po4a-$1\.tar\.gz/ \
https://github.com//mquinson/po4a/tags .*/v?(\d\S+)\.tar\.gz

View File

@ -247,7 +247,7 @@ 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 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: 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 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 use, and it fully automates the process. When you invoke L<po4a(1)>, it both
updates the PO files and regenerate the translation files that need to. If 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. everything is already up to date, L<po4a(1)> does not change any file.
@ -671,7 +671,8 @@ is a French onomatopoetic that we use in place of yuck :) I may have a strange s
=head2 What about the other translation tools for documentation using gettext? =head2 What about the other translation tools for documentation using gettext?
As far as I know, there are only two of them: There are a few of them. Here is a possibly incomplete list, and more tools are
coming at the horizon.
=over =over
@ -691,6 +692,19 @@ 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 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. the DebianDoc DTD, which is more or less a deprecated DTD.
=item B<xml2po.py>
Used by the GIMP Documentation Team since 2004, works quite well even if,
as the name suggests, only with XML files and needs specially configured
makefiles.
=item B<Sphinx>
The Sphinx Documentation Project also uses gettext extensively to manage its
translations. Unfortunately, it works only for a few text formats, rest and
markdown, although it is perhaps the only tool that does this managing the whole
translation process.
=back =back
The main advantages of po4a over them are the ease of extra content addition The main advantages of po4a over them are the ease of extra content addition

View File

@ -107,17 +107,18 @@ Switch parsing rules to compatibility with different tools. Available options ar
"asciidoc" or "asciidoctor". Asciidoctor has stricter parsing rules, such as "asciidoc" or "asciidoctor". Asciidoctor has stricter parsing rules, such as
equality of length of opening and closing block fences. equality of length of opening and closing block fences.
=item B<yfm_keys>
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.
=item B<nolinting> =item B<nolinting>
Disable linting messages. When the source code cannot be fixed for clearer document structure, these messages are useless. Disable linting messages. When the source code cannot be fixed for clearer document structure, these messages are useless.
=item B<yfm_keys>
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-sensitive
match. If B<yfm_paths> and B<yfm_keys> are used together, values are included if
they are matched by at least one of the options. Array values are always translated,
unless the B<yfm_skip_array> option is provided.
=cut =cut
my %yfm_keys = (); my %yfm_keys = ();
@ -130,6 +131,19 @@ Do not translate array values in the YAML Front Matter section.
my $yfm_skip_array = 0; my $yfm_skip_array = 0;
=item B<yfm_paths>
Comma-separated list of hash paths to process for extraction in the YAML
Front Matter section, all other paths are skipped. Paths are matched with a
case-sensitive match. If B<yfm_paths> and B<yfm_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<yfm_skip_array> option is
provided.
=cut
my %yfm_paths = ();
=back =back
=head1 INLINE CUSTOMIZATION =head1 INLINE CUSTOMIZATION
@ -142,12 +156,12 @@ The following commands are recognized:
=item B<//po4a: macro >I<name>B<[>I<attribute list>B<]> =item B<//po4a: macro >I<name>B<[>I<attribute list>B<]>
This permits to describe in detail the parameters of a B<macro>; This describes in detail the parameters of a B<macro>;
I<name> must be a valid macro name, and it ends with an underscore I<name> must be a valid macro name, and it ends with an underscore
if the target must be translated. if the target must be translated.
The I<attribute list> argument is a comma separated list which The I<attribute list> argument is a comma separated list which
contains informations about translatable arguments. This list contains contains information about translatable arguments. This list contains
either numbers, to define positional parameters, or named attributes. either numbers, to define positional parameters, or named attributes.
If a plus sign (B<+>) is prepended to I<name>, then the macro and its If a plus sign (B<+>) is prepended to I<name>, then the macro and its
@ -156,11 +170,11 @@ attribute list in this case, but brackets must be present.
=item B<//po4a: style >B<[>I<attribute list>B<]> =item B<//po4a: style >B<[>I<attribute list>B<]>
This permits to describe in detail which attributes of a style must This describes in detail which attributes of a style must
be translated. be translated.
The I<attribute list> argument is a comma separated list which The I<attribute list> argument is a comma separated list which
contains informations about translatable arguments. This list contains contains information about translatable arguments. This list contains
either numbers, to define positional parameters, or named attributes. either numbers, to define positional parameters, or named attributes.
The first attribute is the style name, it will not be translated. The first attribute is the style name, it will not be translated.
@ -205,6 +219,7 @@ sub initialize {
$self->{options}{'compat'} = 'asciidoc'; $self->{options}{'compat'} = 'asciidoc';
$self->{options}{'yfm_keys'} = ''; $self->{options}{'yfm_keys'} = '';
$self->{options}{'yfm_skip_array'} = 0; $self->{options}{'yfm_skip_array'} = 0;
$self->{options}{'yfm_paths'} = '';
$self->{options}{'nolinting'} = 0; $self->{options}{'nolinting'} = 0;
foreach my $opt ( keys %options ) { foreach my $opt ( keys %options ) {
@ -228,8 +243,11 @@ sub initialize {
$_ =~ s/^\s+|\s+$//g; # Trim the keys before using them $_ =~ s/^\s+|\s+$//g; # Trim the keys before using them
$yfm_keys{$_} = 1 $yfm_keys{$_} = 1
} ( split( ',', $self->{options}{'yfm_keys'} ) ); } ( split( ',', $self->{options}{'yfm_keys'} ) );
map {
$_ =~ s/^\s+|\s+$//g; # Trim the keys before using them
$yfm_paths{$_} = 1
} ( split( ',', $self->{options}{'yfm_paths'} ) );
# map { print STDERR "key $_\n"; } (keys %yfm_keys);
$yfm_skip_array = $self->{options}{'yfm_skip_array'}; $yfm_skip_array = $self->{options}{'yfm_skip_array'};
$self->{translate} = { $self->{translate} = {
@ -349,6 +367,7 @@ BEGIN {
my $UnicodeGCString_available = 0; my $UnicodeGCString_available = 0;
$UnicodeGCString_available = 1 if ( eval { require Unicode::GCString } ); $UnicodeGCString_available = 1 if ( eval { require Unicode::GCString } );
eval { eval {
sub chars($$$) { sub chars($$$) {
my $text = shift; my $text = shift;
my $encoder = shift; my $encoder = shift;
@ -372,6 +391,22 @@ BEGIN {
}; };
} }
sub translate {
my ( $self, $str, $ref, $type ) = @_;
my (%options) = @_;
if ( ($options{'wrap'}==1) && ($str =~ / \+\n/) ) {
$options{'wrap'} = 0;
$str =~ s/([^+])\n/$1 /g;
$str =~ s/ \+\n/\n/g;
$str = $self->SUPER::translate( $str, $ref, $type, %options);
$str =~ s/\n/ +\n/g;
$options{'wrap'} = 1;
} else {
$str = $self->SUPER::translate( $str, $ref, $type, %options );
}
return $str;
}
sub parse { sub parse {
my $self = shift; my $self = shift;
my ( $line, $ref ) = $self->shiftline(); my ( $line, $ref ) = $self->shiftline();
@ -389,7 +424,8 @@ sub parse {
my $yamlarray = YAML::Tiny->read_string($yfm) my $yamlarray = YAML::Tiny->read_string($yfm)
|| die "Couldn't read YAML Front Matter ($!)\n$yfm\n"; || die "Couldn't read YAML Front Matter ($!)\n$yfm\n";
$self->handle_yaml( $ref, $yamlarray, \%yfm_keys, $yfm_skip_array ); $self->handle_yaml( 1, $ref, $yamlarray, \%yfm_keys, $yfm_skip_array, \%yfm_paths );
$self->pushline("---\n");
( $line, $ref ) = $self->shiftline(); # Pass the final '---' ( $line, $ref ) = $self->shiftline(); # Pass the final '---'
} }
@ -439,7 +475,7 @@ sub parse {
and ( $self->{type} eq "Table" ) and ( $self->{type} eq "Table" )
and ( $line !~ m/^\|===/ ) and ( $line !~ m/^\|===/ )
and ( $self->{options}{"tablecells"} ) and ( $self->{options}{"tablecells"} )
and (not defined $self->{disabletablecells})) and ( not defined $self->{disabletablecells} ) )
{ {
# inside a table, and we should split per cell # inside a table, and we should split per cell
my $new_line = ""; my $new_line = "";
@ -463,8 +499,10 @@ sub parse {
my @parts = map { ( $_, shift @texts ) } @seps; my @parts = map { ( $_, shift @texts ) } @seps;
foreach my $part (@parts) { foreach my $part (@parts) {
if ( not defined $part ) { if ( not defined $part ) {
# allows concatenation and will be stripped anyway
$part = " "; } # allows concatenation and will be stripped anyway
$part = " ";
}
if ( $part =~ /\|$/ ) { if ( $part =~ /\|$/ ) {
# this is a cell separator. End the previous cell # this is a cell separator. End the previous cell
@ -695,9 +733,9 @@ sub parse {
} }
print STDERR "Starting verse\n" if $debug{parse}; print STDERR "Starting verse\n" if $debug{parse};
} }
if ((( $line =~ m/^\[format=(['"]?)(csv|tsv|dsv)\1,/ ) || if ( ( ( $line =~ m/^\[format=(['"]?)(csv|tsv|dsv)\1,/ ) || ( $line =~ m/^\[separator=[^\|]/ ) )
( $line =~ m/^\[separator=[^\|]/ )) && && $self->{options}{'tablecells'} )
$self->{options}{'tablecells'}) { {
warn wrap_mod( warn wrap_mod(
"$ref", "$ref",
dgettext( dgettext(
@ -705,7 +743,7 @@ sub parse {
"Po4a's tablecells mode only supports PSV formatted tables with '|' separators. Disabling tablecells and falling back to block mode for this table." "Po4a's tablecells mode only supports PSV formatted tables with '|' separators. Disabling tablecells and falling back to block mode for this table."
) )
); );
$self->{disabletablecells} = 1; $self->{disabletablecells} = 1;
} }
undef $self->{bullet}; undef $self->{bullet};
undef $self->{indent}; undef $self->{indent};
@ -787,7 +825,7 @@ sub parse {
} }
@comments = (); @comments = ();
} elsif ( not defined $self->{verbatim} } elsif ( not defined $self->{verbatim}
and ( $line =~ m/^([\w\d][\w\d-]*)(::)(\S*)\[(.*)\]$/ ) ) and ( $line =~ m/^([\w\d][\w\d-]*)(::)(\S*|\S*\{.*\}\S*)\[(.*)\]$/ ) )
{ {
my $macroname = $1; my $macroname = $1;
my $macrotype = $2; my $macrotype = $2;
@ -852,7 +890,7 @@ sub parse {
$self->{indent} = $indent; $self->{indent} = $indent;
$self->{bullet} = $bullet; $self->{bullet} = $bullet;
} elsif ( not defined $self->{verbatim} } elsif ( not defined $self->{verbatim}
and ( $line =~ m/^((?:<?[0-9]+)?> +)(.*)$/ ) ) and ( $line =~ m/^((?:<?(?:[0-9]|\.)+)?> +)(.*)$/ ) )
{ {
my $bullet = $1; my $bullet = $1;
my $text = $2; my $text = $2;
@ -966,8 +1004,9 @@ sub parse {
} else { } else {
# End the Table # End the Table
if ( $self->{options}{'tablecells'} and if ( $self->{options}{'tablecells'}
not defined $self->{disabletablecells} ) { and not defined $self->{disabletablecells} )
{
do_stripped_unwrapped_paragraph( $self, $paragraph, $wrapped_mode ); do_stripped_unwrapped_paragraph( $self, $paragraph, $wrapped_mode );
$self->pushline("\n"); $self->pushline("\n");
} else { } else {
@ -975,7 +1014,7 @@ sub parse {
} }
undef $self->{verbatim}; undef $self->{verbatim};
undef $self->{type}; undef $self->{type};
undef $self->{disabletablecells}; undef $self->{disabletablecells};
$paragraph = ""; $paragraph = "";
} }
$self->pushline( $line . "\n" ); $self->pushline( $line . "\n" );
@ -996,21 +1035,21 @@ sub parse {
$wrapped_mode = 0; $wrapped_mode = 0;
} }
if ( ( $paragraph ne "" && $self->{bullet} && length( $self->{indent} || "" ) == 0 ) if ( ( $paragraph ne "" && $self->{bullet} && length( $self->{indent} || "" ) == 0 ) )
&& ( !$self->{options}{'nolinting'} ) )
{ {
if ( !$self->{options}{'nolinting'} ) {
# Second line of an item block is not indented. It is unindented # Second line of an item block is not indented. It is unindented
# (and allowed) additional text or a new list item. # (and allowed) additional text or a new list item.
warn wrap_mod( warn wrap_mod(
"$ref", "$ref",
dgettext( dgettext(
"po4a", "po4a",
"It seems that you are adding unindented content to an item. " "It seems that you are adding unindented content to an item. "
. "The standard allows this, but you may still want to change your document " . "The standard allows this, but you may still want to change your document "
. "to use indented text to provide better visual clues to writers." . "to use indented text to provide better visual clues to writers."
) )
); );
}
} else { } else {
undef $self->{bullet}; undef $self->{bullet};
undef $self->{indent}; undef $self->{indent};
@ -1056,7 +1095,7 @@ sub do_paragraph {
# } # }
# $type .= " verbatim: '".($self->{verbatim}||"NONE")."' bullet: '$b' indent: '".($self->{indent}||"NONE")."' type: '".($self->{type}||"NONE")."'"; # $type .= " verbatim: '".($self->{verbatim}||"NONE")."' bullet: '$b' indent: '".($self->{indent}||"NONE")."' type: '".($self->{type}||"NONE")."'";
if ( not $wrap and not defined $self->{verbatim} ) { if ( not defined $self->{verbatim} ) {
# Detect bullets # Detect bullets
# | * blah blah # | * blah blah
@ -1121,8 +1160,10 @@ sub do_paragraph {
"comment" => join( "\n", @comments ), "comment" => join( "\n", @comments ),
"wrap" => $wrap "wrap" => $wrap
); );
my $bullet = $self->{bullet} || "";
# print STDERR "translated: '$t', $bullet\n";
my $unwrap_result = !$self->{options}{'forcewrap'} && $wrap; my $unwrap_result = !$self->{options}{'forcewrap'} && $wrap && (! ($t =~ /\+\n/) ) ;
if ($unwrap_result) { if ($unwrap_result) {
$t =~ s/(\n| )+/ /g; $t =~ s/(\n| )+/ /g;
} }
@ -1318,3 +1359,7 @@ Tested successfully on simple AsciiDoc files.
This program is free software; you may redistribute it and/or modify it This program is free software; you may redistribute it and/or modify it
under the terms of GPL (see the COPYING file). under the terms of GPL (see the COPYING file).
__END__
# LocalWords: Charset charset AsciiDoc tablecells po UTF gettext msgid nostrip

View File

@ -107,7 +107,7 @@ sub show_version {
gettext( gettext(
"%s version %s.\n" "%s version %s.\n"
. "Written by Martin Quinson and Denis Barbier.\n\n" . "Written by Martin Quinson and Denis Barbier.\n\n"
. "Copyright © 2002-2021 Software in the Public Interest, Inc.\n" . "Copyright © 2002-2022 Software in the Public Interest, Inc.\n"
. "This is free software; see source code for copying\n" . "This is free software; see source code for copying\n"
. "conditions. There is NO warranty; not even for\n" . "conditions. There is NO warranty; not even for\n"
. "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." . "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."

View File

@ -139,7 +139,7 @@ sub parse {
This module is loosely inspired from /usr/lib/dpkg/parsechangelog/debian, which is: This module is loosely inspired from /usr/lib/dpkg/parsechangelog/debian, which is:
Copyright © 1996 Ian Jackson. This is free software; see the GNU Copyright © 1996 Ian Jackson. This is free software; see the GNU
General Public Licence version 2 or later for copying conditions. There General Public License version 2 or later for copying conditions. There
is NO warranty. is NO warranty.
The adaptation for po4a was done by: The adaptation for po4a was done by:

View File

@ -119,7 +119,7 @@ Increase verbosity.
=item B<groff_code> =item B<groff_code>
This option permits to change the behavior of the module when it encounter This option controls the behavior of the module when it encounter
a .de, .ie or .if section. It can take the following values: a .de, .ie or .if section. It can take the following values:
=over =over
@ -148,8 +148,8 @@ should be preferred.
This option specifies that the file was generated, and that po4a should not This option specifies that the file was generated, and that po4a should not
try to detect if the man pages was generated from another format. try to detect if the man pages was generated from another format.
This permits to use po4a on generated man pages. This option is mandatory to use po4a on generated man pages.
This option does not take any argument. Note that translating generated pages instead of sources ones is often more fragile, and thus a bad idea.
=item B<mdoc> =item B<mdoc>
@ -173,8 +173,8 @@ This mdoc issue can also be solved with an addendum like this one:
=back =back
The following options permit to specify the behavior of a new macro The following options specify the behavior of a user-defined macro
(defined with a .de request), or of a macro not supported by po4a. (with a .de request), or of a classical macro that is not supported by po4a.
They take as argument a comma-separated list of macros. They take as argument a comma-separated list of macros.
For example: For example:
@ -216,7 +216,7 @@ I<begin> command; any ending command stop the no_wrap mode.
If you have a I<begin> (respectively I<end>) macro that has no I<end> If you have a I<begin> (respectively I<end>) macro that has no I<end>
(respectively I<begin>), you can specify an existing I<end> (like fi) or (respectively I<begin>), you can specify an existing I<end> (like fi) or
I<begin> (like nf) as a counterpart. I<begin> (like nf) as a counterpart.
These macros (and their arguments) wont be translated. These macros (and their arguments) won't be translated.
=item B<inline> =item B<inline>
@ -380,7 +380,7 @@ L<po4a(7)|po4a.7>
=head1 AUTHORS =head1 AUTHORS
Denis Barbier <barbier@linuxfr.org> Denis Barbier <barbier@linuxfr.org>
Nicolas Francois <nicolas.francois@centraliens.net> Nicolas François <nicolas.francois@centraliens.net>
Martin Quinson (mquinson#debian.org) Martin Quinson (mquinson#debian.org)
=head1 COPYRIGHT AND LICENSE =head1 COPYRIGHT AND LICENSE
@ -1036,15 +1036,14 @@ sub pre_trans {
$str =~ s/>/E<gt>/sg; $str =~ s/>/E<gt>/sg;
$str =~ s/</E<lt>/sg; $str =~ s/</E<lt>/sg;
$str =~ s/EE<lt>gt>/E<gt>/g; # could be done in a smarter way? $str =~ s/EE<lt>gt>/E<gt>/g; # could be done in a smarter way?
while ( $str =~ m/^(.*)PO4A-INLINE:(.*?):PO4A-INLINE(.*)$/s ) { while ( $str =~ m/^(.*)PO4A-INLINE:(.*?):PO4A-INLINE(.*)$/s ) {
my ( $t1, $t2, $t3 ) = ( $1, $2, $3 ); my ( $t1, $t2, $t3 ) = ( $1, $2, $3 );
$str = "$1E<$2>"; $str = "$1E<$2>";
if ($mdoc_mode) { if ($mdoc_mode) {
# When a punctuation sign must be joined to an argument, mdoc # When a punctuation sign must be joined to an argument, mdoc allows such a construct:
# permits to use such a construct:
# .Ar file1 , file2 , file3 ) . # .Ar file1 , file2 , file3 ) .
# Here, we move the punctuation out of the E<...> tag. # Here, we move the punctuation out of the E<...> tag.
# This is reverted in post_trans. # This is reverted in post_trans.
@ -1202,7 +1201,12 @@ sub post_trans {
} elsif ( $first eq '>' ) { } elsif ( $first eq '>' ) {
$lvl--; $lvl--;
} }
$done .= $first if ( $lvl > 0 ); if ($first eq "\n") {
# Don't accept \n within B<> parameters as troff seems to reset font on new line (Debian's #1016753)
$done .= ' ' if ( $lvl > 0);
} else {
$done .= $first if ( $lvl > 0);
}
$rest = substr( $rest, 1 ); $rest = substr( $rest, 1 );
} }
die wrap_ref_mod( $ref || $self->{ref}, die wrap_ref_mod( $ref || $self->{ref},
@ -2192,14 +2196,78 @@ $macro{'bp'} = \&untranslated;
# .ad c Start line adjustment in mode c (c=l,r,b,n). # .ad c Start line adjustment in mode c (c=l,r,b,n).
$macro{'ad'} = \&untranslated; $macro{'ad'} = \&untranslated;
# .de macro Define or redefine macro until .. is encountered. my %ds_variables;
$macro{'de'} = sub {
# .ds stringvar anything
# Set stringvar to anything.
$macro{'ds'} = sub {
my ( $self, $m ) = ( shift, shift );
my $name = shift;
my $string = "@_";
$ds_variables{$name} = $string;
# indicate to which variable this corresponds. The translator can
# find references to this string in the translation "\*(name" or
# "\*[name]"
$self->{type} = "ds $name";
$self->pushline( $m . " " . $self->r($name) . " " . $self->translate($string) . "\n" );
};
# .de macro [end] Define or redefine macro until end is encountered (end=.. by default).
# .de1 macro [end] Define or redefine macro until end is encountered (end=.. by default), turns off compatibility mode while executing the macro.
# .dei macro [end] Define or redefine macro until end is encountered (end=.. by default) substituting parameters with '.ds' content
# .dei1 macro [end] Define or redefine macro until end is encountered (end=.. by default) substituting parameters and turning compatibility off
$macro{'de'} = $macro{'de1'} = $macro{'dei'} = $macro{'dei1'} = sub {
my $self = shift; my $self = shift;
if ( $groff_code ne "fail" ) { if ( $groff_code ne "fail" ) {
my $paragraph = "@_"; my $paragraph = "@_";
my $end = "."; my $end = ".";
if ( $paragraph =~ /^[.'][\t ]*de[\t ]+([^\t ]+)[\t ]+([^\t ]+)[\t ]$/ ) { my $comment;
my $macroname = $_[1];
$macroname = $ds_variables{$macroname} if ( $_[0] eq "dei" || $_[0] eq "dei1" );
if ($macroname =~ m/(.*?)\\"(.*)$/) {
# Remove comments after the macro name (Debian's #1017837)
($macroname, $comment) = ($1, $2);
}
$macroname =~ s/^ *//;
$macroname =~ s/ *$//;
unless (exists $macro{$macroname} || exists $inline{$macroname}) {
if (defined $comment) {
$comment =~ s/^ *//;
$comment =~ s/ *$//;
warn wrap_mod(
"po4a::man",
dgettext(
"po4a",
"This page defines a new macro '%s' with '%s' (inline comment: %s), but you did not specify the expected po4a behavior "
. "when '%s' is used. You will get an error if this macro is actually used in your page.\n"
. "Add your macro to one of the '%s', '%s', '%s', '%s', '%s' or '%s' parameters to avoid issues.\n"
. "For example, passing '%s' to po4a will ensure that the defined macro remains hidden from translators.\n"
. "Please refer to the manpage of Locale::Po4a::Man for more info on these parameters.\n"
),
$macroname, $_[0], $comment, $macroname,
'untranslated', 'noarg', 'translate_joined', 'translate_each', 'no_wrap', 'inline', "-o untranslated=$macroname");
} else {
warn wrap_mod(
"po4a::man",
dgettext(
"po4a",
"This page defines a new macro '%s' with '%s', but you did not specify the expected po4a behavior "
. "when '%s' is used. You will get an error if this macro is actually used in your page.\n"
. "Add your macro to one of the '%s', '%s', '%s', '%s', '%s' or '%s' parameters to avoid issues.\n"
. "For example, passing '%s' to po4a will ensure that the defined macro remains hidden from translators.\n"
. "Please refer to the manpage of Locale::Po4a::Man for more info on these parameters.\n"
),
$macroname,
$_[0],$macroname,
'untranslated', 'noarg', 'translate_joined', 'translate_each', 'no_wrap', 'inline', "-o untranslated=$macroname"
);
}
}
if ( $paragraph =~ /^[.'][\t ]*d[ei1]*[\t ]+([^\t ]+)[\t ]+([^\t ]+)[\t ]$/ ) {
$end = $2; $end = $2;
$end = $ds_variables{$end} if ( $_[0] eq "dei" || $_[0] eq "dei1" );
} }
my ( $line, $ref ) = $self->SUPER::shiftline(); my ( $line, $ref ) = $self->SUPER::shiftline();
chomp $line; chomp $line;
@ -2223,26 +2291,17 @@ $macro{'de'} = sub {
"po4a::man", "po4a::man",
dgettext( dgettext(
"po4a", "po4a",
"This page defines a new macro with '.de'. Since po4a is not a real groff parser, this is not supported." "This page defines a new macro with '%s'. Since po4a is not a real groff parser, this is not supported. "
) . "The option '%s' gets these macros copied verbatim in the translated file, but it's not very robust. "
. "'%s' shows these macros to the translators, but groff macros are not user-friendly for translators."
),
$_[0],
"groff_code=verbatim",
"groff_code=translate"
); );
} }
}; };
# .ds stringvar anything
# Set stringvar to anything.
$macro{'ds'} = sub {
my ( $self, $m ) = ( shift, shift );
my $name = shift;
my $string = "@_";
# indicate to which variable this corresponds. The translator can
# find references to this string in the translation "\*(name" or
# "\*[name]"
$self->{type} = "ds $name";
$self->pushline( $m . " " . $self->r($name) . " " . $self->translate($string) . "\n" );
};
# .fam Return to previous font family. # .fam Return to previous font family.
# .fam name Set the current font family to name. # .fam name Set the current font family to name.
$macro{'fam'} = \&untranslated; $macro{'fam'} = \&untranslated;
@ -2328,13 +2387,20 @@ $macro{'ie'} = $macro{'if'} = sub {
"po4a::man", "po4a::man",
dgettext( dgettext(
"po4a", "po4a",
"This page uses conditionals with '%s'. Since po4a is not a real groff parser, this is not supported." "This page uses conditionals with '%s'. Since po4a is not a real groff parser, this is not supported by default. "
. "The option '%s' gets these macros copied verbatim in the translated file, but it's not very robust. "
. "'%s' shows these macros to the translators, but groff macros are not user-friendly for translators."
), ),
$_[0] $_[0],
"groff_code=verbatim",
"groff_code=translate"
); );
} }
}; };
# .el anything Display the parameter (part of a if-then-else with .ie)
$macro{'el'} = \&translate_joined;
# .in N Change indent according to N (default scaling indicator m). # .in N Change indent according to N (default scaling indicator m).
$macro{'in'} = \&untranslated; $macro{'in'} = \&untranslated;
@ -2352,6 +2418,9 @@ $macro{'ig'} = sub {
} }
}; };
# .it n macro Set an input line trap.
$macro{'it'} = \&untranslated;
# .lf N file Set input line number to N and filename to file. # .lf N file Set input line number to N and filename to file.
$macro{'lf'} = \&untranslated; $macro{'lf'} = \&untranslated;
@ -2417,9 +2486,9 @@ $macro{'ti'} = \&untranslated;
### ###
$macro{'TS'} = sub { $macro{'TS'} = sub {
my $self = shift; my $self = shift;
my ( $in_headers, $tab, $buffer ) = ( 1, "\t", "" ); my ( $in_headers, $tab, $buffer ) = ( 1, "\t", "" );
my ( $in_textblock, $preline, $postline ) = ( 0, "", "" ); my ( $in_textblock, $preline, $postline ) = ( 0, "", "" );
my ( $line, $ref ) = $self->shiftline(); my ( $line, $ref ) = $self->shiftline();
my @options; my @options;
# Push table start # Push table start
@ -2801,3 +2870,7 @@ sub define_mdoc_macros {
$macro{'br'} = \&noarg; $macro{'br'} = \&noarg;
} # end of define_mdoc_macros } # end of define_mdoc_macros
__END__
# LocalWords: Charset charset po UTF gettext msgid nostrip

View File

@ -265,6 +265,9 @@ sub initialize {
# count_doc: number of strings in the document # count_doc: number of strings in the document
# (duplicate strings counted multiple times) # (duplicate strings counted multiple times)
$self->{count_doc} = 0; $self->{count_doc} = 0;
$self->{gettextize_types} = (); # Type of each msgid found in the doc, in order
# We cannot use {$msgid}{'type'} as a type because for duplicate entries, the type is overwritten.
# So we have to copy the same info to this separate array, which is accessed through type_doc()
$self->{header_comment} = $self->{header_comment} =
" SOME DESCRIPTIVE TITLE\n" " SOME DESCRIPTIVE TITLE\n"
. " Copyright (C) YEAR " . " Copyright (C) YEAR "
@ -646,7 +649,7 @@ sub write_if_needed {
my $basename = basename($filename); my $basename = basename($filename);
( undef, $tmp_filename ) = File::Temp::tempfile( ( undef, $tmp_filename ) = File::Temp::tempfile(
$basename . "XXXX", $basename . "XXXX",
DIR => $ENV{TMPDIR} || "/tmp", DIR => File::Spec->tmpdir(),
OPEN => 0, OPEN => 0,
UNLINK => 0 UNLINK => 0
); );
@ -657,169 +660,6 @@ sub write_if_needed {
} }
} }
=item gettextize($$)
This function produces one translated message catalog from two catalogs, an
original and a translation. This process is described in L<po4a(7)|po4a.7>,
section I<Gettextization: how does it work?>.
=cut
sub gettextize {
my $this = shift;
my $class = ref($this) || $this;
my ( $poorig, $potrans ) = ( shift, shift );
my $pores = Locale::Po4a::Po->new();
my $please_fail = 0;
my $toobad = dgettext( "po4a",
"\nThe gettextization failed (once again). Don't give up, "
. "gettextizing is a subtle art, but this is only needed once "
. "to convert a project to the gorgeous luxus offered by po4a "
. "to translators."
. "\nPlease refer to the po4a(7) documentation, the section "
. "\"HOWTO convert a pre-existing translation to po4a?\" "
. "contains several hints to help you in your task" );
# Don't fail right now when the entry count does not match. Instead, give
# it a try so that the user can see where we fail (which is probably where
# the problem is).
if ( $poorig->count_entries_doc() > $potrans->count_entries_doc() ) {
warn wrap_mod(
"po4a gettextize",
dgettext(
"po4a",
"Original has more strings than the translation (%d>%d). "
. "Please fix it by editing the translated version to add "
. "some dummy entry."
),
$poorig->count_entries_doc(),
$potrans->count_entries_doc()
);
$please_fail = 1;
} elsif ( $poorig->count_entries_doc() < $potrans->count_entries_doc() ) {
warn wrap_mod(
"po4a gettextize",
dgettext(
"po4a",
"Original has less strings than the translation (%d<%d). "
. "Please fix it by removing the extra entry from the "
. "translated file. You may need an addendum (cf po4a(7)) "
. "to reput the chunk in place after gettextization. A "
. "possible cause is that a text duplicated in the original "
. "is not translated the same way each time. Remove one of "
. "the translations, and you're fine."
),
$poorig->count_entries_doc(),
$potrans->count_entries_doc()
);
$please_fail = 1;
}
if ( $poorig->get_charset =~ /^utf-8$/i ) {
$potrans->to_utf8;
$pores->set_charset("UTF-8");
} else {
my $charset = $potrans->get_charset();
$charset = "UTF-8" if $charset eq "CHARSET";
$pores->set_charset($charset);
}
print "Po character sets:\n"
. " original="
. $poorig->get_charset . "\n"
. " translated="
. $potrans->get_charset . "\n"
. " result="
. $pores->get_charset . "\n"
if $debug{'encoding'};
for (
my ( $o, $t ) = ( 0, 0 ) ;
$o < $poorig->count_entries_doc() && $t < $potrans->count_entries_doc() ;
$o++, $t++
)
{
#
# Extract some informations
my ( $orig, $trans ) = ( $poorig->msgid_doc($o), $potrans->msgid_doc($t) );
# print STDERR "Matches [[$orig]]<<$trans>>\n";
my ( $reforig, $reftrans ) = ( $poorig->{po}{$orig}{'reference'}, $potrans->{po}{$trans}{'reference'} );
my ( $typeorig, $typetrans ) = ( $poorig->{po}{$orig}{'type'}, $potrans->{po}{$trans}{'type'} );
#
# Make sure the type of both string exist
#
die wrap_mod( "po4a gettextize", "Internal error: type of original string number %s " . "isn't provided", $o )
if ( $typeorig eq '' );
die wrap_mod( "po4a gettextize", "Internal error: type of translated string number %s " . "isn't provided", $o )
if ( $typetrans eq '' );
#
# Make sure both type are the same
#
if ( $typeorig ne $typetrans ) {
$pores->write("gettextization.failed.po");
eval {
# Recode $trans into current charset, if possible
require I18N::Langinfo;
I18N::Langinfo->import(qw(langinfo CODESET));
my $codeset = langinfo( CODESET() );
Encode::from_to( $trans, $potrans->get_charset, $codeset );
};
die wrap_msg(
dgettext( "po4a",
"po4a gettextization: Structure disparity between "
. "original and translated files:\n"
. "msgid (at %s) is of type '%s' while\n"
. "msgstr (at %s) is of type '%s'.\n"
. "Original text: %s\n"
. "Translated text: %s\n"
. "(result so far dumped to gettextization.failed.po)" )
. "%s",
$reforig,
$typeorig,
$reftrans,
$typetrans,
$orig, $trans, $toobad
);
}
#
# Push the entry
#
my $flags;
if ( defined $poorig->{po}{$orig}{'flags'} ) {
$flags = $poorig->{po}{$orig}{'flags'} . " fuzzy";
} else {
$flags = "fuzzy";
}
$pores->push_raw(
'msgid' => $orig,
'msgstr' => $trans,
'flags' => $flags,
'type' => $typeorig,
'reference' => $reforig,
'conflict' => 1,
'transref' => $potrans->{po}{$trans}{'reference'}
)
unless ( defined( $pores->{po}{$orig} )
and ( $pores->{po}{$orig}{'msgstr'} eq $trans ) )
# FIXME: maybe we should be smarter about what reference should be
# sent to push_raw.
}
# make sure we return a useful error message when entry count differ
die "$toobad\n" if $please_fail;
return $pores;
}
=item filter($) =item filter($)
This function extracts a catalog from an existing one. Only the entries having This function extracts a catalog from an existing one. Only the entries having
@ -1401,14 +1241,15 @@ sub push_raw {
if ($keep_conflict) { if ($keep_conflict) {
if ( $self->{po}{$msgid}{'msgstr'} =~ m/^#-#-#-#-# .* #-#-#-#-#\\n/s ) { if ( $self->{po}{$msgid}{'msgstr'} =~ m/^#-#-#-#-# .* #-#-#-#-#\\n/s ) {
$msgstr = $self->{po}{$msgid}{'msgstr'} . "\\n#-#-#-#-# $transref #-#-#-#-#\\n" . $msgstr; $msgstr = $self->{po}{$msgid}{'msgstr'} . "\\n#-#-#-#-# $transref (type: $type) #-#-#-#-#\\n" . $msgstr;
} else { } else {
$msgstr = $msgstr =
"#-#-#-#-# " "#-#-#-#-# "
. $self->{po}{$msgid}{'transref'} . $self->{po}{$msgid}{'transref'}
. " #-#-#-#-#\\n" . " (type " . $self->{po}{$msgid}{'type'}
. ") #-#-#-#-#\\n"
. $self->{po}{$msgid}{'msgstr'} . "\\n" . $self->{po}{$msgid}{'msgstr'} . "\\n"
. "#-#-#-#-# $transref #-#-#-#-#\\n" . "#-#-#-#-# $transref (type: $type) #-#-#-#-#\\n"
. $msgstr; . $msgstr;
} }
@ -1451,11 +1292,11 @@ sub push_raw {
$self->{po}{$msgid}{'comment'} = $comment; $self->{po}{$msgid}{'comment'} = $comment;
$self->{po}{$msgid}{'automatic'} = $automatic; $self->{po}{$msgid}{'automatic'} = $automatic;
$self->{po}{$msgid}{'previous'} = $previous; $self->{po}{$msgid}{'previous'} = $previous;
if ( defined( $self->{po}{$msgid}{'pos_doc'} ) ) {
$self->{po}{$msgid}{'pos_doc'} .= " " . $self->{count_doc}++; $self->{po}{$msgid}{pos_doc} = () unless (defined( $self->{po}{$msgid}{pos_doc}));
} else { CORE::push( @{ $self->{po}{$msgid}{pos_doc} }, $self->{count_doc}++);
$self->{po}{$msgid}{'pos_doc'} = $self->{count_doc}++; CORE::push( @{ $self->{gettextize_types} }, $type);
}
unless ( defined( $self->{po}{$msgid}{'pos'} ) ) { unless ( defined( $self->{po}{$msgid}{'pos'} ) ) {
$self->{po}{$msgid}{'pos'} = $self->{count}++; $self->{po}{$msgid}{'pos'} = $self->{count}++;
} }
@ -1515,37 +1356,6 @@ sub count_entries_doc($) {
return $self->{count_doc}; return $self->{count_doc};
} }
=item equals_msgid(po)
Returns ($uptodate, $diagnostic) with $uptodate indicating whether all msgid of the current po file are
also present in the one passed as parameter (all other fields are ignored in the file comparison).
Informally, if $uptodate returns false, then the po files would be changed when going through B<po4a-updatepo>.
If $uptodate is false, then $diagnostic contains a diagnostic of why this is so.
=cut
sub equals_msgid($$) {
my ( $self, $other ) = ( shift, shift );
unless ( $self->count_entries() == $other->count_entries() ) {
return (
0,
wrap_msg(
dgettext( "po4a", "The amount of entries differ between files: %d is not %d" ),
$self->count_entries(),
$other->count_entries()
)
);
}
foreach my $msgid ( keys %{ $self->{po} } ) {
unless ( defined( $self->{po}{$msgid} ) && defined( $other->{po}{$msgid} ) ) {
return ( 0, wrap_msg( dgettext( "po4a", "msgid declared in one file only: %s\n" ), $msgid ) );
}
}
return ( 1, "" );
}
=item msgid($) =item msgid($)
Returns the msgid of the given number. Returns the msgid of the given number.
@ -1573,13 +1383,29 @@ sub msgid_doc($$) {
my $num = shift; my $num = shift;
foreach my $msgid ( keys %{ $self->{po} } ) { foreach my $msgid ( keys %{ $self->{po} } ) {
foreach my $pos ( split / /, $self->{po}{$msgid}{'pos_doc'} ) { foreach my $pos ( @{ $self->{po}{$msgid}{'pos_doc'} } ) {
return $msgid if ( $pos eq $num ); return $msgid if ( $pos eq $num );
} }
} }
return undef; return undef;
} }
=item type_doc($)
Returns the type of the msgid with the given position in the document. This is
probably only useful to gettextization, and it's stored separately from
{$msgid}{'type'} because the later location may be overwritten by another type
when the $msgid is duplicated in the master document.
=cut
sub type_doc($$) {
my $self = shift;
my $num = shift;
return ${ $self->{gettextize_types} }[$num];
}
=item get_charset() =item get_charset()
Returns the character set specified in the PO header. If it hasn't been Returns the character set specified in the PO header. If it hasn't been
@ -1689,7 +1515,7 @@ sub escape_text {
# on the 80th char, but without changing the meaning of the string) # on the 80th char, but without changing the meaning of the string)
sub quote_text { sub quote_text {
my $string = shift; my $string = shift;
my $do_wrap = shift; # either 'no' or 'newlines', or column at which we should wrap my $do_wrap = shift // 'no'; # either 'no' or 'newlines', or column at which we should wrap
return '""' unless length($string); return '""' unless length($string);

View File

@ -399,7 +399,7 @@ sub parse_file {
my ( $tmpfh, $tmpfile ) = File::Temp::tempfile( my ( $tmpfh, $tmpfile ) = File::Temp::tempfile(
"po4a-XXXX", "po4a-XXXX",
SUFFIX => ".sgml", SUFFIX => ".sgml",
DIR => $ENV{TMPDIR} || "/tmp", DIR => File::Spec->tmpdir(),
UNLINK => 0 UNLINK => 0
); );
print $tmpfh $origfile; print $tmpfh $origfile;
@ -434,7 +434,7 @@ sub parse_file {
# Get the prolog # Get the prolog
{ {
$prolog = $origfile; $prolog = $origfile;
my $lvl; # number of '<' seen without matching '>' my $lvl; # number of '<' seen without matching '>'
my $pos = 0; # where in the document (in chars) while detecting prolog boundaries my $pos = 0; # where in the document (in chars) while detecting prolog boundaries
unless ( $prolog =~ s/^(.*<!DOCTYPE).*$/$1/is ) { unless ( $prolog =~ s/^(.*<!DOCTYPE).*$/$1/is ) {
@ -773,7 +773,7 @@ sub parse_file {
while ( $origfile =~ /^(.*?)&$key(;.*$|[^-_:.A-Za-z0-9].*$|$)/s ) { while ( $origfile =~ /^(.*?)&$key(;.*$|[^-_:.A-Za-z0-9].*$|$)/s ) {
# Since we will include a new file, we # Since we will include a new file, we
# must do a new round of substitutions. # must do a new round of substitutions.
$dosubstitution = 1; $dosubstitution = 1;
my ( $begin, $end ) = ( $1, $2 ); my ( $begin, $end ) = ( $1, $2 );
$end = "" unless ( defined $end ); $end = "" unless ( defined $end );
@ -789,7 +789,7 @@ sub parse_file {
# add the refs # add the refs
my $len = $entincl{$key}{'length'}; # number added by the inclusion my $len = $entincl{$key}{'length'}; # number added by the inclusion
my $pre = ( $begin =~ tr/\n/\n/ ); # number of \n my $pre = ( $begin =~ tr/\n/\n/ ); # number of \n
my $post = ( $end =~ tr/\n/\n/ ); my $post = ( $end =~ tr/\n/\n/ );
print "XX Add a ref. pre=$pre; len=$len; post=$post\n" print "XX Add a ref. pre=$pre; len=$len; post=$post\n"
if $debug{'refs'}; if $debug{'refs'};
@ -852,7 +852,7 @@ sub parse_file {
my ( $tmpfh, $tmpfile ) = File::Temp::tempfile( my ( $tmpfh, $tmpfile ) = File::Temp::tempfile(
"po4a-XXXX", "po4a-XXXX",
SUFFIX => ".sgml", SUFFIX => ".sgml",
DIR => $ENV{TMPDIR} || "/tmp", DIR => File::Spec->tmpdir(),
UNLINK => 0 UNLINK => 0
); );
print $tmpfh $origfile; print $tmpfh $origfile;

View File

@ -166,8 +166,7 @@ this environment does not take any parameters.
=back =back
Using these options permits to override the behaviour of the commands defined Use these options to override the default behavior of the defined commands.
in the default lists.
=head1 INLINE CUSTOMIZATION =head1 INLINE CUSTOMIZATION
@ -184,7 +183,7 @@ treated as the arguments of the I<command2> command.
=item B<% po4a: command> I<command1> I<parameters> =item B<% po4a: command> I<command1> I<parameters>
This permit to describe in detail the parameters of the I<command1> This describes in detail the parameters of the I<command1>
command. command.
This information will be used to check the number of arguments and their This information will be used to check the number of arguments and their
types. types.
@ -206,7 +205,7 @@ As for an asterisk, the command will be extracted if it appear at an
extremity of a block, but the parameters won't be translated separately. extremity of a block, but the parameters won't be translated separately.
The translator will have to translate the command concatenated to all its The translator will have to translate the command concatenated to all its
parameters. parameters.
This permits to keep more context, and is useful for commands with small This keeps more context, and is useful for commands with small
words in parameter, which can have multiple meanings (and translations). words in parameter, which can have multiple meanings (and translations).
Note: In this case you don't have to specify which parameters are Note: In this case you don't have to specify which parameters are
@ -244,9 +243,9 @@ command.
=item B<% po4a: environment> I<env> I<parameters> =item B<% po4a: environment> I<env> I<parameters>
This permits to define the parameters accepted by the I<env> environment. This defines the parameters accepted by the I<env> environment and specifies the ones to be translated.
This information is later used to check the number of arguments of the This information is later used to check the number of arguments of the
\begin command, and permit to specify which one must be translated. \begin command.
The syntax of the I<parameters> argument is the same as described for the The syntax of the I<parameters> argument is the same as described for the
others commands. others commands.
The first parameter of the \begin command is the name of the environment. The first parameter of the \begin command is the name of the environment.
@ -264,7 +263,7 @@ Indicates that an environment should be split according to the given
regular expression. regular expression.
The regular expression is delimited by quotes. The regular expression is delimited by quotes.
It should not create any backreference. It should not create any back-reference.
You should use (?:) if you need a group. You should use (?:) if you need a group.
It may also need some escapes. It may also need some escapes.
@ -1121,7 +1120,7 @@ sub parse_definition_line {
} }
} elsif ( $line =~ /^separator\s+(\w+(?:\[#[0-9]+\])?)\s+\"(.*)\"\s*$/ ) { } elsif ( $line =~ /^separator\s+(\w+(?:\[#[0-9]+\])?)\s+\"(.*)\"\s*$/ ) {
my $env = $1; # This is not necessarily an environment. my $env = $1; # This is not necessarily an environment.
# It can also be smth like 'title[#1]'. # It can also be something like 'title[#1]'.
$env_separators{$env} = $2; $env_separators{$env} = $2;
} elsif ( $line =~ /^verbatim\s+environment\s+(\w+)\s+$/ ) { } elsif ( $line =~ /^verbatim\s+environment\s+(\w+)\s+$/ ) {
register_verbatim_environment($1); register_verbatim_environment($1);
@ -1721,7 +1720,7 @@ attached to the following string.
=item Some commands should be added to the environment stack =item Some commands should be added to the environment stack
These commands should be specified by couples. These commands should be specified by couples.
This could allow to specify commands beginning or ending a verbatim This can be used to specify commands beginning or ending a verbatim
environment. environment.
=item Others =item Others
@ -1754,3 +1753,7 @@ under the terms of GPL (see the COPYING file).
=cut =cut
1; 1;
__END__
# LocalWords: Charset charset po UTF gettext msgid nostrip LaTeX

View File

@ -58,6 +58,7 @@ use vars qw(@ISA @EXPORT);
use Locale::Po4a::TransTractor; use Locale::Po4a::TransTractor;
use Locale::Po4a::Common; use Locale::Po4a::Common;
use YAML::Tiny; use YAML::Tiny;
use Syntax::Keyword::Try;
=head1 OPTIONS ACCEPTED BY THIS MODULE =head1 OPTIONS ACCEPTED BY THIS MODULE
@ -122,7 +123,7 @@ my $breaks;
=item B<debianchangelog> =item B<debianchangelog>
Handle the header and footer of Handle the header and footer of
released versions, which only contain non translatable informations. released versions, which only contain non translatable information.
=cut =cut
@ -148,14 +149,41 @@ my $markdown = 0;
=item B<yfm_keys> (markdown-only) =item B<yfm_keys> (markdown-only)
Comma-separated list of keys to process for translation in the YAML Front Matter 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 section. All other keys are skipped. Keys are matched with a case-sensitive
match. Array values are always translated, unless the B<yfm_skip_array> option match. If B<yfm_paths> and B<yfm_keys> are used together, values are included if
is provided. they are matched by at least one of the options. Array values are always translated,
unless the B<yfm_skip_array> option is provided.
=cut =cut
my %yfm_keys = (); my %yfm_keys = ();
=item B<yfm_lenient> (markdown only)
Allow the YAML Front Matter parser to fail on malformated headers. This is
particularly helpful when your file starts with a horizontal ruler instead
of a YAML Front Matter, but you insist on using three dashes only for your
ruler.
=cut
my $yfm_lenient = 0;
=item B<yfm_paths> (markdown only)
=item B<yfm_paths>
Comma-separated list of hash paths to process for extraction in the YAML
Front Matter section, all other paths are skipped. Paths are matched with a
case-sensitive match. If B<yfm_paths> and B<yfm_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<yfm_skip_array> option is
provided.
=cut
my %yfm_paths = ();
=item B<yfm_skip_array> (markdown-only) =item B<yfm_skip_array> (markdown-only)
Do not translate array values in the YAML Front Matter section. Do not translate array values in the YAML Front Matter section.
@ -200,6 +228,8 @@ sub initialize {
$self->{options}{'fortunes'} = 1; $self->{options}{'fortunes'} = 1;
$self->{options}{'markdown'} = 1; $self->{options}{'markdown'} = 1;
$self->{options}{'yfm_keys'} = ''; $self->{options}{'yfm_keys'} = '';
$self->{options}{'yfm_lenient'} = 0;
$self->{options}{'yfm_paths'} = '';
$self->{options}{'yfm_skip_array'} = 0; $self->{options}{'yfm_skip_array'} = 0;
$self->{options}{'nobullets'} = 0; $self->{options}{'nobullets'} = 0;
$self->{options}{'keyvalue'} = 1; $self->{options}{'keyvalue'} = 1;
@ -229,11 +259,16 @@ sub initialize {
$_ =~ s/^\s+|\s+$//g; # Trim the keys before using them $_ =~ s/^\s+|\s+$//g; # Trim the keys before using them
$yfm_keys{$_} = 1 $yfm_keys{$_} = 1
} ( split( ',', $self->{options}{'yfm_keys'} ) ); } ( split( ',', $self->{options}{'yfm_keys'} ) );
map {
$_ =~ s/^\s+|\s+$//g; # Trim the keys before using them
$yfm_paths{$_} = 1
} ( split( ',', $self->{options}{'yfm_paths'} ) );
# map { print STDERR "key $_\n"; } (keys %yfm_keys); # map { print STDERR "key $_\n"; } (keys %yfm_keys);
$yfm_skip_array = $self->{options}{'yfm_skip_array'}; $yfm_skip_array = $self->{options}{'yfm_skip_array'};
$yfm_lenient = $self->{options}{'yfm_lenient'};
} else { } else {
foreach my $opt (qw(yfm_keys yfm_skip_array)) { foreach my $opt (qw(yfm_keys yfm_lenient yfm_skip_array)) {
die wrap_mod( "po4a::text", dgettext( "po4a", "Option %s is only valid when parsing markdown files." ), die wrap_mod( "po4a::text", dgettext( "po4a", "Option %s is only valid when parsing markdown files." ),
$opt ) $opt )
if exists $options{$opt}; if exists $options{$opt};
@ -301,12 +336,12 @@ sub parse_fallback {
if ( if (
$markdown $markdown
and ( and (
$line =~ /\S $/ # explicit newline $line =~ /\S $/ # explicit newline
or $line =~ /"""$/ or $line =~ /"""$/
) )
) )
{ # """ textblock inside macro begin { # """ textblock inside macro begin
# Markdown markup needing separation _after_ this line # Markdown markup needing separation _after_ this line
$end_of_paragraph = 1; $end_of_paragraph = 1;
} else { } else {
undef $self->{bullet}; undef $self->{bullet};
@ -576,18 +611,59 @@ sub parse_markdown_bibliographic_information {
sub parse_markdown_yaml_front_matter { sub parse_markdown_yaml_front_matter {
my ( $self, $line, $blockref ) = @_; my ( $self, $line, $blockref ) = @_;
my $yfm; my $yfm;
my @saved_ctn;
my ( $nextline, $nextref ) = $self->shiftline(); my ( $nextline, $nextref ) = $self->shiftline();
push @saved_ctn, ( $nextline, $nextref );
while ( defined($nextline) ) { while ( defined($nextline) ) {
last if ( $nextline =~ /^(---|\.\.\.)$/ ); last if ( $nextline =~ /^(---|\.\.\.)$/ );
$yfm .= $nextline; $yfm .= $nextline;
( $nextline, $nextref ) = $self->shiftline(); ( $nextline, $nextref ) = $self->shiftline();
push @saved_ctn, ( $nextline, $nextref );
} }
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 ); my $yamlarray; # the parsed YFM content
return; my $yamlres; # containing the parse error, if any
try {
$yamlarray = YAML::Tiny->read_string($yfm);
} catch {
$yamlres = $@;
}
if ( defined($yamlres) ) {
if ($yfm_lenient) {
$yamlres =~ s/ at .*$//; # Remove the error localisation in YAML::Tiny die message, if any (for our test)
warn wrap_mod(
"po4a::text",
dgettext(
"po4a",
"Proceeding even if the YAML Front Matter could not be parsed. Remove the 'yfm_lenient' option for a stricter behavior.\nIgnored error: %s"
),
$yamlres
);
my $len = ( scalar @saved_ctn ) - 1;
while ( $len >= 0 ) {
$self->unshiftline( $saved_ctn[ $len - 1 ], $saved_ctn[$len] );
# print STDERR "Unshift ".$saved_ctn[ $len - 1] ." | ". $saved_ctn[$len] ."\n";
$len -= 2;
}
return 0; # Not a valid YAML
} else {
die wrap_mod(
"po4a::text",
dgettext(
"po4a",
"Could not get the YAML Front Matter from the file. If you did not intend to add a YAML front matter "
. "but an horizontal ruler, please use '----' instead, or pass the 'yfm_lenient' option.\nError: %s\nContent of the YFM: %s"
),
$yamlres, $yfm
);
}
}
$self->handle_yaml( 1, $blockref, $yamlarray, \%yfm_keys, $yfm_skip_array, \%yfm_paths );
$self->pushline("---\n");
return 1; # Valid YAML
} }
sub parse_markdown { sub parse_markdown {
@ -602,8 +678,11 @@ sub parse_markdown {
parse_markdown_bibliographic_information( $self, $line, $ref ); parse_markdown_bibliographic_information( $self, $line, $ref );
return ( $paragraph, $wrapped_mode, $expect_header, $end_of_paragraph ); return ( $paragraph, $wrapped_mode, $expect_header, $end_of_paragraph );
} elsif ( $line =~ /^---$/ ) { } elsif ( $line =~ /^---$/ ) {
parse_markdown_yaml_front_matter( $self, $line, $ref ); if ( parse_markdown_yaml_front_matter( $self, $line, $ref ) ) { # successfully parsed
return ( $paragraph, $wrapped_mode, $expect_header, $end_of_paragraph ); return ( $paragraph, $wrapped_mode, $expect_header, $end_of_paragraph );
}
# If it wasn't a YFM paragraph after all, stop expecting a header and keep going
} }
} }
if ( ( $line =~ m/^(={4,}|-{4,})$/ ) if ( ( $line =~ m/^(={4,}|-{4,})$/ )
@ -682,6 +761,68 @@ sub parse_markdown {
$self->pushline($nextline); $self->pushline($nextline);
$paragraph = ""; $paragraph = "";
$end_of_paragraph = 1; $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);
# print STDERR "----------------\n";
# print STDERR "line: $line\n";
# print STDERR "fence: '$fence'; fencechar: '$fencechar'; info: '$info_string'\n";
# fenced div block (fenced with ::: where code blocks are fenced with ` or ~)
# https://pandoc.org/MANUAL.html#divs-and-spans
my $info = join( "|" , map {chomp $_;$_} @info_string );
my $type = "Fenced div block" . ( $info ? " ($info)" : "" );
do_paragraph( $self, $paragraph, $wrapped_mode );
$wrapped_mode = 0;
$paragraph = "";
$self->pushline("$line\n");
do_paragraph( $self, $paragraph, $wrapped_mode );
$paragraph = "";
my $lvl = 1;
while ( $lvl > 0 ) {
my ( $nextline, $nextref ) = $self->shiftline();
die wrap_mod(
"po4a::text",
dgettext(
"po4a", "Malformed fenced div block: Block starting at %s not closed before the end of the file."
),
$ref
) unless ( defined($nextline) );
# print STDERR "within $lvl: $nextline";
if ( $nextline =~ /^\s*:::+\s*$/ ) {
my $info = join( "|" , map {chomp $_;$_} @info_string );
$type = "Fenced div block" . ( $info ? " ($info)" : "" );
if ($paragraph ne "") {
do_paragraph( $self, $paragraph, $wrapped_mode, $type );
$paragraph = "";
}
$self->pushline($nextline);
$lvl--;
while (scalar @info_string > $lvl) {
pop @info_string;
}
} elsif ( $nextline =~ /^([ ]{0,3})(([:])\3{2,})(\s*)([^`]*)\s*$/ ) {
if ($paragraph ne "") {
do_paragraph( $self, $paragraph, $wrapped_mode, $type );
$paragraph = "";
}
$self->pushline($nextline);
push @info_string, $5;
$lvl++;
} else {
$paragraph .= $nextline;
}
}
$paragraph = "";
$end_of_paragraph = 1;
# print STDERR "Out now ------------\n";
} elsif ( } elsif (
$line =~ /^\s*\[\[\!\S+\s*$/ # macro begin $line =~ /^\s*\[\[\!\S+\s*$/ # macro begin
or $line =~ /^\s*"""\s*\]\]\s*$/ or $line =~ /^\s*"""\s*\]\]\s*$/
@ -698,8 +839,8 @@ sub parse_markdown {
$paragraph = "$line\n"; $paragraph = "$line\n";
$wrapped_mode = 0; $wrapped_mode = 0;
$end_of_paragraph = 1; $end_of_paragraph = 1;
} elsif ( $line =~ /^"""/ ) { # """ textblock inside macro end } elsif ( $line =~ /^"""/ ) { # """ textblock inside macro end
# Markdown markup needing separation _before_ this line # Markdown markup needing separation _before_ this line
do_paragraph( $self, $paragraph, $wrapped_mode ); do_paragraph( $self, $paragraph, $wrapped_mode );
$paragraph = "$line\n"; $paragraph = "$line\n";
$wrapped_mode = $defaultwrap; $wrapped_mode = $defaultwrap;
@ -787,15 +928,10 @@ sub do_paragraph {
$wrap = 0 unless $defaultwrap; $wrap = 0 unless $defaultwrap;
# DEBUG # DEBUG
# my $b; # $type .= " verbatim: '".($self->{verbatim}//"NONE")."' bullet: '$bullets' wrap: '$wrap' indent: '".($self->{indent}//"NONE")."' type: '".($self->{type}//"NONE")."'";
# if (defined $self->{bullet}) { # print STDERR "$type\n";
# $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} ) { if ( $bullets and not defined $self->{verbatim} ) {
# Detect bullets # Detect bullets
# | * blah blah # | * blah blah
@ -803,7 +939,7 @@ sub do_paragraph {
# | ^-- aligned # | ^-- aligned
# <empty line> # <empty line>
# #
# Other bullets supported: # The leading spaces are optional, and other bullets are supported:
# - blah o blah + blah # - blah o blah + blah
# 1. blah 1) blah (1) blah # 1. blah 1) blah (1) blah
TEST_BULLET: TEST_BULLET:
@ -896,3 +1032,7 @@ This program is free software; you may redistribute it and/or modify it
under the terms of GPL (see the COPYING file). under the terms of GPL (see the COPYING file).
=cut =cut
__END__
# LocalWords: Charset charset po UTF gettext msgid nostrip GPL

View File

@ -11,7 +11,7 @@ use warnings;
use subs qw(makespace); use subs qw(makespace);
use vars qw($VERSION @ISA @EXPORT); use vars qw($VERSION @ISA @EXPORT);
$VERSION = "0.66"; $VERSION = "0.69";
@ISA = qw(DynaLoader); @ISA = qw(DynaLoader);
@EXPORT = qw(new process translate @EXPORT = qw(new process translate
read write readpo writepo read write readpo writepo
@ -544,11 +544,6 @@ of use:
($percent,$hit,$queries) = $document->stats(); ($percent,$hit,$queries) = $document->stats();
print "We found translations for $percent\% ($hit from $queries) of strings.\n"; print "We found translations for $percent\% ($hit from $queries) of strings.\n";
=item is_po_uptodate()
Returns ($uptodate, $diagnostic) where $uptodate is whether the input po and the output po match (if not, it means that the input po should be updated)
and $diagnostic is a string explaining why the po file is not uptodate, when this happens.
=back =back
=cut =cut
@ -573,10 +568,6 @@ sub stats {
return $_[0]->{TT}{po_in}->stats_get(); return $_[0]->{TT}{po_in}->stats_get();
} }
sub is_po_uptodate($) {
return $_[0]->{TT}{po_in}->equals_msgid( $_[0]->{TT}{po_out} );
}
=head2 Manipulating addenda =head2 Manipulating addenda
=over 4 =over 4
@ -1186,11 +1177,12 @@ sub encode_from_to {
return $text; return $text;
} }
# Push the translation of a Yaml Front-Matter header that was parsed by YAML::Tiny # Push the translation of a Yaml document or Yaml Front-Matter header, parsed by YAML::Tiny in any case
# $is_yfm is a boolean indicating whether we are dealing with a Front Matter (true value) or whole document (false value)
sub handle_yaml { sub handle_yaml {
my ( $self, $blockref, $yamlarray, $yfm_keys, $yfm_skip_array ) = @_; my ( $self, $is_yfm, $blockref, $yamlarray, $yfm_keys, $yfm_skip_array, $yfm_paths ) = @_;
die "Empty YAML Front Matter" unless ( length($yamlarray) > 0 ); die "Empty YAML " . ($is_yfm?"Front Matter":"document") unless ( length($yamlarray) > 0 );
my ( $indent, $ctx ) = ( 0, "" ); my ( $indent, $ctx ) = ( 0, "" );
foreach my $cursor (@$yamlarray) { foreach my $cursor (@$yamlarray) {
@ -1205,13 +1197,13 @@ sub handle_yaml {
} elsif ( !ref $cursor ) { } elsif ( !ref $cursor ) {
$self->pushline("---\n"); $self->pushline("---\n");
$self->pushline( $self->pushline(
format_scalar( $self->translate( $cursor, $blockref, "YAML Front Matter (scalar)", "wrap" => 0 ) ) ); format_scalar( $self->translate( $cursor, $blockref, "YAML ".($is_yfm?"Front Matter ":"")."(scalar)", "wrap" => 0 ) ) );
# A list at the root # A list at the root
} elsif ( ref $cursor eq 'ARRAY' ) { } elsif ( ref $cursor eq 'ARRAY' ) {
if (@$cursor) { if (@$cursor) {
$self->pushline("---\n"); $self->pushline("---\n");
do_array( $self, $blockref, $cursor, $indent, $ctx, $yfm_keys, $yfm_skip_array ); do_array( $self, $is_yfm, $blockref, $cursor, $indent, $ctx, $yfm_keys, $yfm_skip_array, $yfm_paths );
} else { } else {
$self->pushline("---[]\n"); $self->pushline("---[]\n");
} }
@ -1220,7 +1212,7 @@ sub handle_yaml {
} elsif ( ref $cursor eq 'HASH' ) { } elsif ( ref $cursor eq 'HASH' ) {
if (%$cursor) { if (%$cursor) {
$self->pushline("---\n"); $self->pushline("---\n");
do_hash( $self, $blockref, $cursor, $indent, $ctx, $yfm_keys, $yfm_skip_array ); do_hash( $self, $is_yfm, $blockref, $cursor, $indent, $ctx, $yfm_keys, $yfm_skip_array, $yfm_paths );
} else { } else {
$self->pushline("--- {}\n"); $self->pushline("--- {}\n");
} }
@ -1228,7 +1220,6 @@ sub handle_yaml {
} else { } else {
die( "Cannot serialize " . ref($cursor) ); die( "Cannot serialize " . ref($cursor) );
} }
$self->pushline("---\n");
} }
# Escape the string to make it valid in YAML. # Escape the string to make it valid in YAML.
@ -1262,7 +1253,7 @@ sub handle_yaml {
} }
sub do_array { sub do_array {
my ( $self, $blockref, $array, $indent, $ctx, $yfm_keys, $yfm_skip_array ) = @_; my ( $self, $is_yfm, $blockref, $array, $indent, $ctx, $yfm_keys, $yfm_skip_array, $yfm_paths ) = @_;
foreach my $el (@$array) { foreach my $el (@$array) {
my $header = ( ' ' x $indent ) . '- '; my $header = ( ' ' x $indent ) . '- ';
my $type = ref $el; my $type = ref $el;
@ -1271,14 +1262,14 @@ sub handle_yaml {
$self->pushline( $header . YAML::Tiny::_dump_scalar( "dummy", $el, 0 ) . "\n" ); $self->pushline( $header . YAML::Tiny::_dump_scalar( "dummy", $el, 0 ) . "\n" );
} else { } else {
$self->pushline( $header $self->pushline( $header
. format_scalar( $self->translate( $el, $blockref, "YAML Front Matter:$ctx", "wrap" => 0 ) ) . format_scalar( $self->translate( $el, $blockref, ($is_yfm?"Yaml Front Matter ":"")."Array Element:$ctx", "wrap" => 0 ) )
. "\n" ); . "\n" );
} }
} elsif ( $type eq 'ARRAY' ) { } elsif ( $type eq 'ARRAY' ) {
if (@$el) { if (@$el) {
$self->pushline( $header . "\n" ); $self->pushline( $header . "\n" );
do_array( $self, $blockref, $el, $indent + 1, $ctx, $yfm_keys, $yfm_skip_array ); do_array( $self, $is_yfm, $blockref, $el, $indent + 1, $ctx, $yfm_keys, $yfm_skip_array, $yfm_paths );
} else { } else {
$self->pushline( $header . " []\n" ); $self->pushline( $header . " []\n" );
} }
@ -1286,7 +1277,7 @@ sub handle_yaml {
} elsif ( $type eq 'HASH' ) { } elsif ( $type eq 'HASH' ) {
if ( keys %$el ) { if ( keys %$el ) {
$self->pushline( $header . "\n" ); $self->pushline( $header . "\n" );
do_hash( $self, $blockref, $el, $indent + 1, $ctx, $yfm_keys, $yfm_skip_array ); do_hash( $self, $is_yfm, $blockref, $el, $indent + 1, $ctx, $yfm_keys, $yfm_skip_array, $yfm_paths );
} else { } else {
$self->pushline( $header . " {}\n" ); $self->pushline( $header . " {}\n" );
} }
@ -1298,28 +1289,38 @@ sub handle_yaml {
} }
sub do_hash { sub do_hash {
my ( $self, $blockref, $hash, $indent, $ctx, $yfm_keys, $yfm_skip_array ) = @_; my ( $self, $is_yfm, $blockref, $hash, $indent, $ctx, $yfm_keys, $yfm_skip_array, $yfm_paths ) = @_;
foreach my $name ( sort keys %$hash ) { foreach my $name ( sort keys %$hash ) {
my $el = $hash->{$name}; my $el = $hash->{$name};
my $header = ( ' ' x $indent ) . YAML::Tiny::_dump_scalar( "dummy", $name, 1 ) . ":"; my $header = ( ' ' x $indent ) . YAML::Tiny::_dump_scalar( "dummy", $name, 1 ) . ":";
my $type = ref $el; my $type = ref $el;
if ( !$type ) { if ( !$type ) {
my %keys = %{$yfm_keys}; my %keys = %{$yfm_keys};
if ( ( not %keys ) || $keys{$name} ) { # either no key is provided, or the key we need is also provided my %paths = %{$yfm_paths};
$self->pushline( my $path = "$ctx $name" =~ s/^\s+|\s+$//gr; # Need to trim the path, at least when there is no ctx yet
$header . ' '
. format_scalar( if ( ($el eq 'false') or ($el eq 'true') ) { # Do not translate not quote booleans
$self->translate( $el, $blockref, "YAML Front Matter:$ctx $name", "wrap" => 0 ) $self->pushline("$header $el\n");
) } elsif ( ( scalar %keys > 0 && exists $keys{$name}) or # the key we need is provided
. "\n" ( scalar %paths > 0 && exists $paths{$path}) or # that path is provided
); ( scalar %keys == 0 && scalar %paths == 0) ) { # no key and no path provided
my $translation = $self->translate( $el, $blockref, ($is_yfm?"Yaml Front Matter ":"")."Hash Value:$ctx $name", "wrap" => 0 );
if ( $el =~ /^\[.*\]$/ ) { # Do not quote the lists
$self->pushline( $header . " $translation\n" );
} else {
# add extra quotes to the parameter, as a protection to the extra chars that the translator could add
$self->pushline( $header . ' ' . format_scalar($translation) . "\n" );
}
} else { } else {
# Work around a bug in YAML::Tiny that quotes numbers # Work around a bug in YAML::Tiny that quotes numbers
# See https://github.com/Perl-Toolchain-Gang/YAML-Tiny#additional-perl-specific-notes # See https://github.com/Perl-Toolchain-Gang/YAML-Tiny#additional-perl-specific-notes
if ( Scalar::Util::looks_like_number($el) ) { if ( Scalar::Util::looks_like_number($el) ) {
$self->pushline("$header $el\n"); $self->pushline("$header $el\n");
} elsif ( $el =~ /^\[.*\]$/ ) { # Do not quote the lists either
$self->pushline("$header $el\n");
} else { } else {
$self->pushline( $header . ' ' . YAML::Tiny::_dump_scalar( "dummy", $el ) . "\n" ); $self->pushline( $header . ' ' . YAML::Tiny::_dump_scalar( "dummy", $el ) . "\n" );
} }
@ -1328,7 +1329,7 @@ sub handle_yaml {
} elsif ( $type eq 'ARRAY' ) { } elsif ( $type eq 'ARRAY' ) {
if (@$el) { if (@$el) {
$self->pushline( $header . "\n" ); $self->pushline( $header . "\n" );
do_array( $self, $blockref, $el, $indent + 1, "$ctx $name", $yfm_keys, $yfm_skip_array ); do_array( $self, $is_yfm, $blockref, $el, $indent + 1, "$ctx $name", $yfm_keys, $yfm_skip_array, $yfm_paths );
} else { } else {
$self->pushline( $header . " []\n" ); $self->pushline( $header . " []\n" );
} }
@ -1336,7 +1337,7 @@ sub handle_yaml {
} elsif ( $type eq 'HASH' ) { } elsif ( $type eq 'HASH' ) {
if ( keys %$el ) { if ( keys %$el ) {
$self->pushline( $header . "\n" ); $self->pushline( $header . "\n" );
do_hash( $self, $blockref, $el, $indent + 1, "$ctx $name", $yfm_keys, $yfm_skip_array ); do_hash( $self, $is_yfm, $blockref, $el, $indent + 1, "$ctx $name", $yfm_keys, $yfm_skip_array, $yfm_paths );
} else { } else {
$self->pushline( $header . " {}\n" ); $self->pushline( $header . " {}\n" );
} }

View File

@ -86,7 +86,7 @@ sub read {
my $tmp_filename; my $tmp_filename;
( undef, $tmp_filename ) = File::Temp::tempfile( ( undef, $tmp_filename ) = File::Temp::tempfile(
"po4aXXXX", "po4aXXXX",
DIR => $ENV{TMPDIR} || "/tmp", DIR => File::Spec->tmpdir(),
SUFFIX => ".xml", SUFFIX => ".xml",
OPEN => 0, OPEN => 0,
UNLINK => 0 UNLINK => 0

View File

@ -66,8 +66,7 @@ This module is fully functional, as it relies in the L<Locale::Po4a::Xml>
module. This only defines the translatable tags and attributes. module. This only defines the translatable tags and attributes.
"It works for me", which means I use it successfully on my personal Web site. "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 However, YMMV: please let me know if something doesn't work for you.
particular, tables are getting no testing whatsoever, as we don't use them.
=head1 SEE ALSO =head1 SEE ALSO

View File

@ -1071,7 +1071,7 @@ sub CDATA_trans {
sub tag_break_alone { sub tag_break_alone {
my ( $self, @tag ) = @_; my ( $self, @tag ) = @_;
my $struct = $self->get_path( $self->get_tag_name(@tag) ); my $struct = $self->get_path( $self->get_tag_name(@tag) );
if ( $self->get_translate_options($struct) =~ m/i/ ) { if ( $self->get_translate_options($struct) =~ m/[ip]/ ) {
return 0; return 0;
} else { } else {
return 1; return 1;
@ -1701,140 +1701,16 @@ sub treat_content {
# Append or remove the opening/closing tag from the tag path # Append or remove the opening/closing tag from the tag path
if ( $tag_types[$type]->{'end'} eq "" ) { if ( $tag_types[$type]->{'end'} eq "" ) {
if ( $tag_types[$type]->{'beginning'} eq "" ) { if ( $tag_types[$type]->{'beginning'} eq "" ) {
$self->treat_content_open_tag(\@tag, \@paragraph, \@text);
# tag is <tag >
my $cur_tag_name = $self->get_tag_name(@tag);
my $t_opts = $self->get_translate_options( $self->get_path($cur_tag_name) );
if ( $t_opts =~ m/p/ ) {
# tag has a placeholder option, append a "<placeholder
# type=cur_tag_name id =id_index>" tag to @paragraph.
# using $self->get_tag_name(@tag) as cur_tag_name and
# using $#{$save_holders[$#save_holders]->{'sub_translations'}} + 1
# as id_index
my $last_holder = $save_holders[$#save_holders];
my $placeholder_str =
"<placeholder type=\""
. $cur_tag_name
. "\" id=\""
. ( $#{ $last_holder->{'sub_translations'} } + 1 ) . "\"/>";
push @paragraph, ( $placeholder_str, $text[1] );
my @saved_paragraph = @paragraph;
$last_holder->{'paragraph'} = \@saved_paragraph;
# Then we must push a new holder into @save_holders
my @new_paragraph = ();
my @sub_translations = ();
my %folded_attributes;
my %new_holder = (
'paragraph' => \@new_paragraph,
'open' => $self->join_lines(@text),
'translation' => "",
'close' => undef,
'sub_translations' => \@sub_translations,
'folded_attributes' => \%folded_attributes
);
push @save_holders, \%new_holder;
# reset @text holding the whole tag with attributes
# to empty
@text = ();
# reset the current @paragraph (for the current holder)
# to empty.
@paragraph = ();
} elsif ( $t_opts =~ m/f/ ) {
# tag has a "f" option for folded attributes
my $tag_full = $self->join_lines(@text);
my $tag_ref = $text[1];
if ( $tag_full =~ m/^<\s*\S+\s+\S.*>$/s ) {
my $holder = $save_holders[$#save_holders];
my $id = 0;
foreach ( keys %{ $holder->{folded_attributes} } ) {
$id = $_ + 1 if ( $_ >= $id );
}
$holder->{folded_attributes}->{$id} = $tag_full;
@text = ( "<$cur_tag_name po4a-id=$id>", $tag_ref );
}
}
unless ( $t_opts =~ m/n/ ) {
# unless "n" for custom (such as non-XML HTML) tag, update @path
push @path, $cur_tag_name;
}
} elsif ( $tag_types[$type]->{'beginning'} eq "/" ) { } elsif ( $tag_types[$type]->{'beginning'} eq "/" ) {
$self->treat_content_close_tag(\@tag, \@paragraph, \@text);
# tag is </tag>
# Verify this closing tag matches with the last opening tag
# while removing the last opening tag in @path
my $test = pop @path;
my $name = $self->get_tag_name(@tag);
if ( !defined($test)
|| $test ne $name )
{
my $ontagerror = $self->{options}{'ontagerror'};
if ( $ontagerror eq "warn" ) {
warn wrap_ref_mod(
$tag[1],
"po4a::xml",
dgettext(
"po4a",
"Unexpected closing tag </%s> found. The main document may be wrong. Continuing…"
),
$name
);
} elsif ( $ontagerror ne "silent" ) {
die wrap_ref_mod(
$tag[1],
"po4a::xml",
dgettext(
"po4a", "Unexpected closing tag </%s> found. The main document may be wrong."
),
$name
);
}
}
if ( $self->get_translate_options( $self->get_path( $self->get_tag_name(@tag) ) ) =~ m/p/ ) {
# this closing tag has a placeholder option
# revert @path to include this tag for translate_paragraph
push @path, $self->get_tag_name(@tag);
# Now translate this paragraph if needed.
# This will call pushline and append the
# translation to the current holder's translation.
$self->translate_paragraph(@paragraph);
# remove this tag from @path
pop @path;
# Now that this holder is closed, we can remove
# the holder from the stack.
my $holder = pop @save_holders;
# We need to keep the translation of this holder
my $translation = $holder->{'open'} . $holder->{'translation'};
$translation .= $self->join_lines(@text);
@text = ();
# Then we store the translation in the previous
# holder's sub_translations array
my $previous_holder = $save_holders[$#save_holders];
push @{ $previous_holder->{'sub_translations'} }, $translation;
# We also need to restore the @paragraph array, as
# it was before we encountered the holder.
@paragraph = @{ $previous_holder->{'paragraph'} };
}
} }
} elsif ( $tag_types[$type]->{'beginning'} eq ""
&& $tag_types[$type]->{'end'} eq "/" ) {
# As for empty-element tag,
# treat as if both open and close tags exist
$self->treat_content_open_tag(\@tag, \@paragraph, \@text);
$self->treat_content_close_tag(\@tag, \@paragraph, \@text);
} }
push @paragraph, @text; push @paragraph, @text;
} }
@ -1908,6 +1784,174 @@ sub treat_content {
return $eof; return $eof;
} }
# Processes open tags during getting texts.
# Performs special process for placeholder and attribute folding.
sub treat_content_open_tag {
my $self = shift;
my ($tag, $paragraph, $text) = @_;
# tag is <tag >
my $cur_tag_name = $self->get_tag_name(@$tag);
my $t_opts = $self->get_translate_options( $self->get_path($cur_tag_name) );
if ( $t_opts =~ m/p/ ) {
# tag has a placeholder option, append a "<placeholder
# type=cur_tag_name id =id_index>" tag to @$paragraph.
# using $self->get_tag_name(@$tag) as cur_tag_name and
# using $#{$save_holders[$#save_holders]->{'sub_translations'}} + 1
# as id_index
my $last_holder = $save_holders[$#save_holders];
my $placeholder_str =
"<placeholder type=\""
. $cur_tag_name
. "\" id=\""
. ( $#{ $last_holder->{'sub_translations'} } + 1 ) . "\"/>";
push @$paragraph, ( $placeholder_str, $text->[1] );
my @saved_paragraph = @$paragraph;
$last_holder->{'paragraph'} = \@saved_paragraph;
# make attributes be able to be translated
my $open_tag = $self->join_lines(@$text);
if ($open_tag =~ m/^<(\s*)(\S+\s+\S.*)>$/s) {
my ($ws, $tag_inner) = ($1, $2);
$tag_inner =~ s|(\s*/)$||;
my $postfix = $1;
push @path, $cur_tag_name;
$open_tag = "<" . $ws . $self->treat_attributes($tag_inner)
. $postfix . ">";
pop @path;
}
# Then we must push a new holder into @save_holders
my @new_paragraph = ();
my @sub_translations = ();
my %folded_attributes;
my %new_holder = (
'paragraph' => \@new_paragraph,
'open' => $open_tag,
'translation' => "",
'close' => undef,
'sub_translations' => \@sub_translations,
'folded_attributes' => \%folded_attributes
);
push @save_holders, \%new_holder;
# reset @$text holding the whole tag with attributes
# to empty
@$text = ();
# reset the current @$paragraph (for the current holder)
# to empty.
@$paragraph = ();
} elsif ( $t_opts =~ m/f/ ) {
# tag has a "f" option for folded attributes
my $tag_full = $self->join_lines(@$text);
my $tag_ref = $text->[1];
if ( $tag_full =~ m/^<(\s*)(\S+\s+\S.*)>$/s ) {
my ($ws, $tag_inner) = ($1, $2);
my $holder = $save_holders[$#save_holders];
my $id = 0;
foreach ( keys %{ $holder->{folded_attributes} } ) {
$id = $_ + 1 if ( $_ >= $id );
}
# make attributes be able to be translated
$tag_inner =~ s|(\s*/)$||;
my $postfix = $1;
push @path, $cur_tag_name;
$holder->{folded_attributes}->{$id} =
"<" . $ws . $self->treat_attributes($tag_inner)
. $postfix . ">";
pop @path;
@$text = ( "<$cur_tag_name po4a-id=$id>", $tag_ref );
}
}
unless ( $t_opts =~ m/n/ ) {
# unless "n" for custom (such as non-XML HTML) tag, update @path
push @path, $cur_tag_name;
}
}
# Processes close tags during getting texts.
# Performs special process for placeholder.
sub treat_content_close_tag {
my $self = shift;
my ($tag, $paragraph, $text) = @_;
# tag is </tag>
# Verify this closing tag matches with the last opening tag
# while removing the last opening tag in @path
my $test = pop @path;
my $name = $self->get_tag_name(@$tag);
if ( !defined($test)
|| $test ne $name )
{
my $ontagerror = $self->{options}{'ontagerror'};
if ( $ontagerror eq "warn" ) {
warn wrap_ref_mod(
$tag->[1],
"po4a::xml",
dgettext(
"po4a",
"Unexpected closing tag </%s> found. The main document may be wrong. Continuing…"
),
$name
);
} elsif ( $ontagerror ne "silent" ) {
die wrap_ref_mod(
$tag->[1],
"po4a::xml",
dgettext(
"po4a", "Unexpected closing tag </%s> found. The main document may be wrong."
),
$name
);
}
}
if ( $self->get_translate_options( $self->get_path( $self->get_tag_name(@$tag) ) ) =~ m/p/ ) {
# this closing tag has a placeholder option
# revert @path to include this tag for translate_paragraph
push @path, $self->get_tag_name(@$tag);
# Now translate this paragraph if needed.
# This will call pushline and append the
# translation to the current holder's translation.
$self->translate_paragraph(@$paragraph);
# remove this tag from @path
pop @path;
# Now that this holder is closed, we can remove
# the holder from the stack.
my $holder = pop @save_holders;
# We need to keep the translation of this holder
my $translation = $holder->{'open'} . $holder->{'translation'};
$translation .= $self->join_lines(@$text);
@$text = ();
# Then we store the translation in the previous
# holder's sub_translations array
my $previous_holder = $save_holders[$#save_holders];
push @{ $previous_holder->{'sub_translations'} }, $translation;
# We also need to restore the @$paragraph array, as
# it was before we encountered the holder.
@$paragraph = @{ $previous_holder->{'paragraph'} };
}
}
# Translate a @paragraph array of (string, reference). # Translate a @paragraph array of (string, reference).
# The $translate argument indicates if the strings must be translated or # The $translate argument indicates if the strings must be translated or
# just pushed # just pushed

View File

@ -4,140 +4,6 @@
# under the terms of GPL (see COPYING). # 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 =encoding UTF-8
=head1 NAME =head1 NAME
@ -163,7 +29,7 @@ These are this module's particular options:
=item B<keys> =item B<keys>
Space-separated list of hash keys to process for extraction, all Space-separated list of hash keys to process for extraction, all
other keys are skipped. Keys are matched with a case-insensitive match. other keys are skipped. Keys are matched with a case-sensitive match.
If B<paths> and B<keys> are used together, values are included if they are If B<paths> and B<keys> are used together, values are included if they are
matched by at least one of the options. matched by at least one of the options.
Arrays values are always returned unless the B<skip_array> option is Arrays values are always returned unless the B<skip_array> option is
@ -172,7 +38,7 @@ provided.
=item B<paths> =item B<paths>
Comma-separated list of hash paths to process for extraction, all Comma-separated list of hash paths to process for extraction, all
other paths are skipped. Paths are matched with a case-insensitive match. other paths are skipped. Paths are matched with a case-sensitive match.
If B<paths> and B<keys> are used together, values are included if they are If B<paths> and B<keys> are used together, values are included if they are
matched by at least one of the options. matched by at least one of the options.
Arrays values are always returned unless the B<skip_array> option is Arrays values are always returned unless the B<skip_array> option is
@ -195,8 +61,81 @@ L<Locale::Po4a::TransTractor(3pm)>, L<po4a(7)|po4a.7>
=head1 COPYRIGHT AND LICENSE =head1 COPYRIGHT AND LICENSE
Copyright © 2017 Brian Exelbierd. Copyright © 2017 Brian Exelbierd.
Copyright © 2022 Martin Quinson <mquinson#debian.org>.
This program is free software; you may redistribute it and/or modify it This program is free software; you may redistribute it and/or modify it
under the terms of GPL (see the COPYING file). under the terms of GPL (see the COPYING file).
=cut =cut
############################################################################
# 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();
my %yfm_keys = ();
my %yfm_paths = ();
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};
}
map {
$_ =~ s/^\s+|\s+$//g; # Trim the keys before using them
$yfm_keys{$_} = 1
} ( split( /[, ]/, $self->{options}{keys} ) );
# map { print STDERR "key: '$_'\n"; } (keys %yfm_keys);
map {
$_ =~ s/^\s+|\s+$//g; # Trim the keys before using them
$yfm_paths{$_} = 1
} ( split( /,/, $self->{options}{paths} ));
}
sub parse {
my $self = shift;
my $yfm;
my ( $nextline, $ref ) = $self->shiftline();
while ( defined($nextline) ) {
$yfm .= $nextline;
my $nextref;
( $nextline, $nextref ) = $self->shiftline();
}
my $yamlarray = YAML::Tiny->read_string($yfm)
|| die "YAML::Tiny failed to parse the content of $ref: $!";
$self->handle_yaml( 0, $ref, $yamlarray, \%yfm_keys, $self->{options}{skip_array}, \%yfm_paths );
}
1;
__END__

View File

@ -196,7 +196,7 @@ my $msgmergeOpts = ($noprevious ? "" : "--previous");
# Get all po files and report differences in them # Get all po files and report differences in them
my ($pofile); my ($pofile);
(undef,$pofile)=File::Temp::tempfile("po4aXXXX", (undef,$pofile)=File::Temp::tempfile("po4aXXXX",
DIR => "/tmp", DIR => File::Spec->tmpdir(),
SUFFIX => ".po", SUFFIX => ".po",
OPEN => 0, OPEN => 0,
UNLINK => 0) UNLINK => 0)

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

1421
po/bin/ka.po Normal file

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

10
po/bin/zanata.xml Normal file
View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<!-- This file allows to automatically download the translations from the zanata servers -->
<!-- for n in ??.po ; do zanata po pull --lang basename $n .po ; done -->
<config xmlns="http://zanata.org/namespace/config/">
<url>https://translate.zanata.org/</url>
<project>po4a</project>
<project-version>bin</project-version>
<project-type>gettext</project-type>
</config>

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

10
po/pod/zanata.xml Normal file
View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<!-- This file allows to automatically download the translations from the zanata servers -->
<!-- for n in ??.po ; do zanata po pull --lang basename $n .po ; done -->
<config xmlns="http://zanata.org/namespace/config/">
<url>https://translate.zanata.org/</url>
<project>po4a</project>
<project-version>pod</project-version>
<project-type>gettext</project-type>
</config>

File diff suppressed because it is too large Load Diff

12235
po/pod/zh_Hant.po Normal file

File diff suppressed because it is too large Load Diff

261
po4a
View File

@ -27,43 +27,24 @@ it decouples the translation of content from its document structure.
Please refer to the page L<po4a(7)> for a gentle introduction to this Please refer to the page L<po4a(7)> for a gentle introduction to this
project. project.
When you run the B<po4a> program for the first time, with only a Upon execution, B<po4a> parses all documentation files specified in its
configuration file and the documents to translate (called master configuration file. It updates the PO files (containing the translation) to
documents), it produces a POT file (also called translation template) reflect any change to the documentation, and produce a translated documentation
that contains all of the translatable strings in the document in a form by injecting the content's translation (found in the PO files) into the
that eases the work of translators. structure of the original master document.
Those POT files can either be translated with a specific editor such
as the B<GNOME Translation Editor>, KDE's B<Lokalize> or B<poedit>,
or they can be integrated in an online localization platform such as
B<weblate> or B<pootle>. The translation result is a set of PO files,
one per language.
When you run the B<po4a> program with both the master documents and
the PO files, it produces the translated documents by injecting the
content's translation (found in the PO files) into the structure of
the original master document.
If the master documents changed in the meanwhile, po4a will update the PO
and POT files accordingly, so that the translators can easily detect the
modifications and update their work. Depending on your settings, po4a will
discard the partially translated documents, or produce a document mixing
English (for the new or modified paragraphs) and the target language
(for paragraphs where translation is already in the PO file).
By default, the translated documents are produced when at least 80%
of their content is translated (see the I<--keep> option below).
Discarding translations as soon as they are not 100% may be
discouraging for the translators, while showing "translations" that
are too incomplete may be troubling for the end users.
=head2 Graphical overview
At first, the PO files only contain the strings to translate from the original
documentation. This file format allows the translators to manually provide a
translation for each paragraph extracted by B<po4a>. If the documentation is
modified after translation, B<po4a> marks the corresponding translations as
"fuzzy" in the PO file to request a manual review by the translators. The
translators can also provide so-called "addendum", that are extra content
stating for example who did the translation and how to report bugs.
master documents ---+---->-------->---------+ master documents ---+---->-------->---------+
(doc authoring) | | (doc authoring) | |
V (po4a executions) >-----+--> translations V (po4a executions) >-----+--> translated
| | | | | | documents
existing PO files -->--> updated PO files >-+ | existing PO files -->--> updated PO files >-+ |
^ | | ^ | |
| V | | V |
@ -72,13 +53,100 @@ are too incomplete may be troubling for the end users.
| |
addendum -->--------------------------------------+ addendum -->--------------------------------------+
The master documents are authored by the documentation writers. Any changes are The workflow of B<po4a> is asynchronous, as suited to open-source projects. The
automatically reflected by po4a in the PO files, that are then updated by the documentation writers author the master documents at their own pace. The
translators. All changes to the PO files (either manual or by po4a) are translators review and update the translations in the PO files. The maintainers
automatically reflected in translated documents. You can mimic this behavior rerun B<po4a> on need, to reflect any change to the original documentation to
using the L<po4a-updatepo(1)> and L<po4a-translate(1)> scripts in makefiles, but the PO files, and to produce updated documentation translations, by injecting
this quickly becomes bothersome and repetitive (see L<po4a(7)>). It is highly the latest translation into the latest document structure.
recommended to use the B<po4a> program in your build process.
By default, a given translated document is produced when at least 80% of its
content is translated. The untranslated text is kept in the original language.
The produced documentation thus mixes languages if the translation is not
complete. You can change the 80% threshold with the I<--keep> option described
below. Note however that discarding translations as soon as they are not 100%
may be discouraging for the translators whose work will almost never be shown to
the users, while showing "translations" that are too incomplete may be troubling
for the end users.
Storing the translated documentation files in the version control system is
probably a bad idea, since they are automatically generated. The precious files
are the PO files, that contain the hard work of your fellow translators. Also,
some people find it easier to interact with the translators through an online
platform such as S<weblate>, but this is naturally fully optional.
=head2 Quick start tutorial
Let's assume you maintain a program named B<foo> which has a man page
F<man/foo.1> written in English (the bridge language in most open-source
projects, but B<po4a> can be used from or to any language). Some times ago,
someone provided a German translation named F<man/foo.de.1> and disappeared.
This is a problem because you just got a bug report saying that your
documentation contains a gravely misleading information that must be fixed in
all languages, but you don't speak German so you can only modify the original,
not the translation. Now, another contributor wants to contribute a translation
to Japanese, a language that you don't master either.
It is time to convert your documentation to B<po4a> to solve your documentation
maintenance nightmares. You want to update the doc when needed, you want to ease
the work of your fellow translators, and you want to ensure that your users
never see any outdated and thus misleading documentation.
The conversion includes two steps: setup the po4a infrastructure, and convert
the previous German translation to salvage the previous work. This latter part
is done using S<po4a-gettextize>, as follows. As detailed in the documentation
of L<po4a-gettextize(1)>, this process rarely fully automatic, but once it's
done, the B<de.po> file containing the German translation can be integrated in
your po4a workflow.
po4a-gettextize --format man --master foo.1 --localized foo.de.1 --po de.po
Let's now configure po4a. With the appropriate file layout, your configuration
file could be as simple as this:
[po_directory] man/po4a/
[type: man] man/foo.1 $lang:man/translated/foo.$lang.1
It specifies that all PO files (containing the work of the translators) are the
F<man/po4a/> directory, and that you have only one master file, F<man/foo.1>. If
you had several master files, you would have several lines similar to the second
one. Each such line also specify where to write the corresponding translation
files. Here, the German translation of F<man/foo.1> is in
F<man/translated/foo.de.1>.
The last thing we need to complete the configuration of B<po4a> is a POT file
containing the template material that should be used to start a new translation.
Simply create an empty file with the S<.pot> extension in the specified
S<po_directory> (e.g. F<man/po4a/foo.pot>), and B<po4a> will fill it with the
expected content.
Here is a recap of the files in this setup:
├── man/
│ ├── foo.1 <- The original man page, in English.
│ ├── po4a/
│ │ ├── de.po <- The German PO translation, from gettextization.
│ │ └── foo.pot <- The POT template of future translations (empty at first)
│ └── translated/ <- Directory where the translations will be created
└── po4a.cfg <- The configuration file.
Once setup, executing B<po4a> will parse your documentation, update the POT
template file, use it to update the PO translation files, and use them to update
the documentation translation files. All in one command:
po4a --verbose po4a.cfg
This it. B<po4a> is now fully configured. Once you've fixed your error in
F<man/foo.1>, the offending paragraph in the German translation will be replaced by
the fixed text in English. Mixing languages is not optimal, but it's the only
way to remove errors in translations that you don't even understand, and ensure
that the content presented to the users is never misleading. Updating the German
translation is also much easier in the corresponding PO file, so the language
mix-up may not last long. Finally, when the Japanese translator gives you a
S<jp.po> translated file, just drop it in F<man/po4a/po/>. A translated page
will appear as F<man/translated/foo.jp.1> (provided that enough content is
translated) when you run B<po4a> again.
=head1 OPTIONS =head1 OPTIONS
@ -259,7 +327,7 @@ content. If set to B<newlines>, po4a will only split the msgid and msgstr after
newlines in the content. If set to B<no>, po4a will not wrap the po file at all. newlines in the content. If set to B<no>, po4a will not wrap the po file at all.
The reference comments are always wrapped by the gettext tools that we use internally. The reference comments are always wrapped by the gettext tools that we use internally.
Note that this option has no impact on how the msgid and msgstr are wrapped, ie Note that this option has no impact on how the msgid and msgstr are wrapped, i.e.
on how newlines are added to the content of these strings. on how newlines are added to the content of these strings.
=item B<--master-language> =item B<--master-language>
@ -300,7 +368,7 @@ Note: B<$lang> will be extended to the current language.
=item B<--no-previous> =item B<--no-previous>
This option removes B<--previous> from the options passed to B<msgmerge>. This option removes B<--previous> from the options passed to B<msgmerge>.
This permits to support versions of B<gettext> earlier than 0.16. This is necessary to support versions of B<gettext> earlier than 0.16.
=item B<--previous> =item B<--previous>
@ -387,16 +455,20 @@ C<$master> in the name of your PO files on the C<[po4a_paths]> line, as follows.
[po4a_paths] doc/$master/$master.pot $lang:doc/$master/$lang.po [po4a_paths] doc/$master/$master.pot $lang:doc/$master/$lang.po
With this line, po4a will produce separate POT and PO files for each document to translate. With this line, po4a will produce separate POT and PO files for each document to translate.
For example, if you have 3 documents and 5 languages, this will result in 3 POT files and For example, if you have 3 documents and 5 languages, this will result in 3 POT files and
15 PO files. These files are named as specified on the C<po4a_paths> template, with 15 PO files. These files are named as specified on the C<po4a_paths> template, with
C<$master> substituted to the basename of each document to translate. In case of name C<$master> substituted to the basename of each document to translate. In case of name
conflict, you can specify the POT file to use as follows, with the C<pot=> parameter. conflict, you can specify the POT file to use as follows, with the C<pot=> parameter.
This feature can also be used to group several translated files into the same POT file.
This feature can also be used to group several translated files into the same
POT file. The following example only produces 2 POT files: F<l10n/po/foo.pot>
(containing the material from F<foo/gui.xml>) and F<l10n/po/bar.pot> (containing
the material from both F<bar/gui.xml> and F<bar/cli.xml>).
[po4a_langs] de fr ja [po4a_langs] de fr ja
[po4a_paths] l10n/po/$master.pot $lang:l10n/po/$master.$lang.po [po4a_paths] l10n/po/$master.pot $lang:l10n/po/$master.$lang.po
[type: xml] foo/gui.xml $lang:foo/gui.$lang.xml pot=foo-gui [type: xml] foo/gui.xml $lang:foo/gui.$lang.xml pot=foo
[type: xml] bar/gui.xml $lang:bar/gui.$lang.xml pot=bar [type: xml] bar/gui.xml $lang:bar/gui.$lang.xml pot=bar
[type: xml] bar/cli.xml $lang:bar/cli.$lang.xml pot=bar [type: xml] bar/cli.xml $lang:bar/cli.$lang.xml pot=bar
@ -404,7 +476,7 @@ In split mode, B<po4a> builds a temporary compendium during the PO update, to
share the translations between all the PO files. If two PO files have different share the translations between all the PO files. If two PO files have different
translations for the same string, B<po4a> will mark this string as fuzzy and translations for the same string, B<po4a> will mark this string as fuzzy and
will submit both translations in all the PO files containing this string. When will submit both translations in all the PO files containing this string. When
unfuzzied by the translator, the translation is automatically used in every PO unfuzzied by the translator, the translation is automatically used in every PO
files. files.
=head2 Specifying the documents to translate =head2 Specifying the documents to translate
@ -599,78 +671,20 @@ these strings will be left unmodified when producing the translated documents.
This naturally decreases the level of translation, so you may need the C<--keep> This naturally decreases the level of translation, so you may need the C<--keep>
option to ensure that the document is produced anyway. option to ensure that the document is produced anyway.
=head2 CONFIGURATION EXAMPLE
TODO: Is this section really useful?
Let's assume you maintain a program named B<foo> which has a man page F<man/foo.1>
which naturally is maintained in English only. Now you as the upstream or
downstream maintainer want to create and maintain the translation.
First you need to create the POT file necessary to send to translators
using L<po4a-gettextize(1)>.
So for our case we would call
cd man && po4a-gettextize -f man -m foo.1 -p foo.pot
You would then send this file to the appropriate language lists or offer
it for download somewhere on your website.
Now let's assume you received three translations before your next release:
F<de.po> (including an addendum F<de.add>), F<sv.po> and F<pt.po>.
Since you don't want to change your F<Makefile>(s) whenever a new translation
arrives you can use B<po4a> with an appropriate configuration file in your F<Makefile>.
Let's call it F<po4a.cfg>. In our example it would look like the following:
[po_directory] man/po4a/po/
[type: man] man/foo.1 $lang:man/translated/$lang/foo.1 \
add_$lang:?man/po4a/add_$lang/$lang.add opt:"-k 80"
In this example we assume that your generated man pages (and all PO and addenda
files) should be stored in F<man/translated/$lang/> (respectively in F<man/po4a/po/> and
F<man/po4a/add_$lang/>) below the current directory. In our example
the F<man/po4a/po/> directory would include F<de.po>, F<pt.po> and F<sv.po>,
and the F<man/po4a/add_de/> directory would include F<de.add>.
Note the use of the modifier B<?> as only the German translation (F<de.po>) is
accompanied by an addendum.
To actually build the translated man pages you would then (once!) add the
following line in the B<build> target of the appropriate F<Makefile>:
po4a po4a.cfg
Once this is set up you don't need to touch the F<Makefile> when a new
translation arrives, i.e. if the French team sends you F<fr.po> and F<fr.add>
then you simply drop them respectively in F<man/po4a/po/> and
F<man/po4a/add_fr/> and the next time the program is built the
French translation is automatically build as well in F<man/translated/fr/>.
Note that you still need an appropriate target to install localized manual
pages with English ones.
Finally if you do not store generated files into your version control system,
you will need a line in your B<clean> target as well:
-rm -rf man/translated
=head1 SEE ALSO =head1 SEE ALSO
L<po4a-gettextize(1)>, L<po4a-gettextize(1)>,
L<po4a-normalize(1)>,
L<po4a-translate(1)>,
L<po4a-updatepo(1)>,
L<po4a(7)>. L<po4a(7)>.
=head1 AUTHORS =head1 AUTHORS
Denis Barbier <barbier@linuxfr.org> Denis Barbier <barbier@linuxfr.org>
Nicolas Francois <nicolas.francois@centraliens.net> Nicolas François <nicolas.francois@centraliens.net>
Martin Quinson (mquinson#debian.org) Martin Quinson (mquinson#debian.org)
=head1 COPYRIGHT AND LICENSE =head1 COPYRIGHT AND LICENSE
Copyright 2002-2020 by SPI, inc. Copyright 2002-2022 by SPI, inc.
This program is free software; you may redistribute it and/or modify it This program is free software; you may redistribute it and/or modify it
under the terms of GPL (see the COPYING file). under the terms of GPL (see the COPYING file).
@ -1119,15 +1133,16 @@ while (<CONFIG>) {
next unless defined $basedir; next unless defined $basedir;
my $candidate = "$basedir/$po_directory"; my $candidate = "$basedir/$po_directory";
if ( -e $candidate && -d $candidate ) { if ( -e $candidate && -d $candidate ) {
$found = 1;
print STDERR wrap_msg( gettext("Using '%s' as a %s."), $candidate, "po_directory" )
if $po4a_opts{"verbose"};
opendir PO_DIR, opendir PO_DIR,
$candidate $candidate
or die wrap_ref_mod( "$config_file:$nb", "", gettext("Cannot list directory '%s' in '%s' "), or die wrap_ref_mod( "$config_file:$nb", "", gettext("Cannot list directory '%s' in '%s' "),
$po_directory, $basedir ); $po_directory, $basedir );
map { push @po_files, "$po_directory/$_" if -e "$candidate/$_"; } ( sort readdir PO_DIR ); map { push @po_files, "$po_directory/$_" if -e "$candidate/$_"; } ( sort readdir PO_DIR );
# Only display the message the first time we find the right directory
print STDERR wrap_msg( gettext("Using '%s' as a %s."), $candidate, "po_directory" )
if $po4a_opts{"verbose"} && $found == 0 && scalar @po_files;
$found = 1;
} }
} }
unless ($found) { unless ($found) {
@ -1431,7 +1446,12 @@ if ( $pot_filename =~ m/\$master/ ) {
if ( not scalar @{ $po4a_opts{"partial"} } ) { if ( not scalar @{ $po4a_opts{"partial"} } ) {
if ( -e find_input_file($pot_filename) ) { if ( -e find_input_file($pot_filename) ) {
my $modtime = ( stat find_input_file($pot_filename) )[9]; my @statfile = stat find_input_file($pot_filename);
my $filesize = $statfile[7];
my $modtime = $statfile[9];
# The POT files needs to be generated if it's currently empty (first run?)
$update_pot_file = 1 if $filesize == 0;
# The POT needs to be re-generated if a master document is more recent # The POT needs to be re-generated if a master document is more recent
# than the POT. # than the POT.
@ -1535,7 +1555,7 @@ if ($update_pot_file) {
if ( $po4a_opts{"split"} ) { if ( $po4a_opts{"split"} ) {
( undef, $pot_filename ) = File::Temp::tempfile( ( undef, $pot_filename ) = File::Temp::tempfile(
"po4a-temp-pot-XXXX", "po4a-temp-pot-XXXX",
DIR => $ENV{TMPDIR} || "/tmp", DIR => File::Spec->tmpdir(),
SUFFIX => ".pot", SUFFIX => ".pot",
OPEN => 0, OPEN => 0,
UNLINK => 0 UNLINK => 0
@ -1583,7 +1603,7 @@ if ( $po4a_opts{"split"} ) {
unless ( $po4a_opts{"force"} ) { unless ( $po4a_opts{"force"} ) {
( undef, $tmp_file ) = File::Temp::tempfile( ( undef, $tmp_file ) = File::Temp::tempfile(
"po4aXXXX", "po4aXXXX",
DIR => $ENV{TMPDIR} || "/tmp", DIR => File::Spec->tmpdir(),
SUFFIX => ".pot", SUFFIX => ".pot",
OPEN => 0, OPEN => 0,
UNLINK => 0 UNLINK => 0
@ -1616,7 +1636,7 @@ if ( $po4a_opts{"split"} ) {
my $tmp_bigpo; my $tmp_bigpo;
( undef, $tmp_bigpo ) = File::Temp::tempfile( ( undef, $tmp_bigpo ) = File::Temp::tempfile(
"po4aXXXX", "po4aXXXX",
DIR => $ENV{TMPDIR} || "/tmp", DIR => File::Spec->tmpdir(),
SUFFIX => "-$lang.po", SUFFIX => "-$lang.po",
OPEN => 0, OPEN => 0,
UNLINK => 0 UNLINK => 0
@ -1705,7 +1725,7 @@ if ( not $po4a_opts{"no-update"} ) {
# updated (unless --force was specified). # updated (unless --force was specified).
( undef, $tmp_file ) = File::Temp::tempfile( ( undef, $tmp_file ) = File::Temp::tempfile(
"po4aXXXX", "po4aXXXX",
DIR => $ENV{TMPDIR} || "/tmp", DIR => File::Spec->tmpdir(),
SUFFIX => ".po", SUFFIX => ".po",
OPEN => 0, OPEN => 0,
UNLINK => 0 UNLINK => 0
@ -1990,3 +2010,6 @@ sub is_older {
} }
__END__ __END__
# LocalWords: Charset charset AsciiDoc tablecells po UTF gettext msgid nostrip
# LocalWords: xml docbook

View File

@ -17,7 +17,7 @@ po4a-gettextize - convert an original file (and its translation) to a PO file
=head1 SYNOPSIS =head1 SYNOPSIS
B<po4a-gettextize> B<-f> I<fmt> B<-m> I<master.doc> [B<-l> I<XX.doc>] B<-p> I<XX.po> B<po4a-gettextize> B<-f> I<fmt> B<-m> I<master.doc> B<-l> I<XX.doc> B<-p> I<XX.po>
(I<XX.po> is the output, all others are inputs) (I<XX.po> is the output, all others are inputs)
@ -28,20 +28,21 @@ the classical gettext tools. The main feature of po4a is that it decouples the
translation of content from its document structure. Please refer to the page translation of content from its document structure. Please refer to the page
L<po4a(7)> for a gentle introduction to this project. L<po4a(7)> for a gentle introduction to this project.
The B<po4a-gettextize> script is in charge of converting documentation files into The B<po4a-gettextize> script helps you converting your previously existing
PO files. You only need it to setup your translation project with po4a, never afterward. translations into a po4a-based workflow. This is only to be done once to salvage
an existing translation while converting to po4a, not on a regular basis after
the conversion of your project. This tedious process is explained in details in
Section 'Converting a manual translation to po4a' below.
If you start from scratch, B<po4a-gettextize> will extract the translatable You must provide both a master file (e.g., the source in English) and an
strings from the documentation and write a POT file. If you provide a previously existing translated file (e.g., a previous translation attempt without po4a). If
existing translated file with the B<-l> flag, B<po4a-gettextize> will try to use you provide more than one master or translation files, they will be used in
the translations that it contains in the produced PO file. This process remains sequence, but it may be easier to gettextize each page or chapter separately and
tedious and manual, as explained in Section 'Converting a manual translation to then use B<msgmerge> to merge all produced PO files. As you wish.
po4a' below.
If the master document has non-ASCII characters, the new generated PO file will If the master document has non-ASCII characters, the new generated PO file will
be in UTF-8. Else (if the master document is completely in ASCII), the generated be in UTF-8. If the master document is completely in ASCII, the generated
PO will use the encoding of the translated input document, or UTF-8 if no PO will use the encoding of the translated input document.
translated document is provided.
=head1 OPTIONS =head1 OPTIONS
@ -79,8 +80,8 @@ catalog will be written to the standard output.
=item B<-o>, B<--option> =item B<-o>, B<--option>
Extra option(s) to pass to the format plugin. See the documentation of each Extra option(s) to pass to the format plugin. See the documentation of each
plugin for more information about the valid options and their meanings. For plugin for more information about the valid options and their meanings. For
example, you could pass '-o tablecells' to the AsciiDoc parser, while the example, you could pass '-o tablecells' to the AsciiDoc parser, while the
text parser would accept '-o tabs=split'. text parser would accept '-o tabs=split'.
=item B<-h>, B<--help> =item B<-h>, B<--help>
@ -91,6 +92,11 @@ Show a short help message.
List the documentation formats understood by po4a. List the documentation formats understood by po4a.
=item B<-k> B<--keep-temps>
Keep the temporary master and localized POT files built before merging.
This can be useful to understand why these files get desynchronized, leading to gettextization problems
=item B<-V>, B<--version> =item B<-V>, B<--version>
Display the version of the script and exit. Display the version of the script and exit.
@ -125,19 +131,24 @@ Set the package version for the POT header. The default is "VERSION".
=head2 Converting a manual translation to po4a =head2 Converting a manual translation to po4a
B<po4a-gettextize> will try to extract the content of any provided translation B<po4a-gettextize> synchronizes the master and localized files to extract their
file, and use this content as msgstr in the produced PO file. Be warned that content into a PO file. The content of the master file gives the B<msgid> while
this process is very fragile: the Nth string of the translated file is supposed the content of the localized file gives the B<msgstr>. This process is somewhat
to be the translation of the Nth string in the original. This will naturally not fragile: the Nth string of the translated file is supposed to be the translation
work unless both files share exactly the same structure. of the Nth string in the original.
Gettextization works best if you manage to retrieve the exact version of the
original document that was used for translation. Even so, you may need to fiddle
with both master and localized files to align their structure if it was changed
by the original translator, so working on files' copies is advised.
Internally, each po4a parser reports the syntactical type of each extracted Internally, each po4a parser reports the syntactical type of each extracted
strings. This is how desynchronization are detected during the gettextization. strings. This is how desynchronization are detected during the gettextization.
For example, if the files have the following structure, it is very unlikely that In the example depicted below, it is very unlikely that the 4th string in
the 4th string in translation (of type 'chapter') is the translation of the 4th translation (of type 'chapter') is the translation of the 4th string in original
string in original (of type 'paragraph'). It is more likely that a new (of type 'paragraph'). It is more likely that a new paragraph was added to the
paragraph was added to the original, or that two original paragraphs were merged original, or that two original paragraphs were merged together in the
together in the translation. translation.
Original Translation Original Translation
@ -148,73 +159,57 @@ together in the translation.
chapter paragraph chapter paragraph
paragraph paragraph paragraph paragraph
B<po4a-gettextize> will verbosely diagnose any detected structure B<po4a-gettextize> will verbosely diagnose any structure desynchronization. When
desynchronization. When this happens, you should manually edit the files (this this happens, you should manually edit the files to add fake paragraphs or
probably requires that you have some notions of the target language). You must remove some content here and there until the structure of both files actually
add fake paragraphs or remove some content in one of the documents (or both) to match. Some tricks are given below to salvage the most of the existing
fix the reported disparities, until the structure of both documents perfectly translation while doing so.
match. Some tricks are given in the next section.
Even when the document is successfully processed, undetected disparities and If you are lucky enough to have a perfect match in the file structures out of
silent errors are still possible. That is why any translation associated the box, building a correct PO file is a matter of seconds. Otherwise, you will
automatically by po4a-gettextize is marked as I<fuzzy> to require an manual soon understand why this process has such an ugly name :) Even so,
inspection by humans. One has to check that each retrieved msgstr is actually gettextization often remains faster than translating everything again. I
the translation of the associated msgid, and not the string before or after. gettextized the French translation of the whole Perl documentation in one day
despite the I<many> synchronization issues. Given the amount of text (2Mb of
original text), restarting the translation without first salvaging the old
translations would have required several months of work. In addition, this grunt
work is the price to pay to get the comfort of po4a. Once converted, the
synchronization between master documents and translations will always be fully
automatic.
As you can see, the key here is to have the exact same structure in the After a successful gettextization, the produced documents should be manually
translated document and in the original one. The best is to do the checked for undetected disparities and silent errors, as explained below.
gettextization on the exact version of F<master.doc> that was used for the
translation, and only update the PO file against the latest master file once the
gettextization was successful.
If you are lucky enough to have a a perfect match in the file structures, =head3 Hints and tricks for the gettextization process
building a correct PO file is a matter of seconds. Otherwise, you will soon
understand why this process has such an ugly name :) But remember that this
grunt work is the price to pay to get the comfort of po4a afterward. Once
converted, the synchronization between master documents and translations will
always be fully automatic.
Even when things go wrong, gettextization often remains faster than translating The gettextization stops as soon as a desynchronization is detected. When this
everything again. I was able to gettextize the existing French translation of happens, you need to edit the files as much as needed to re-align the files'
the whole Perl documentation in one day, even though the structure of many structures. B<po4a-gettextize> is rather verbose when things go wrong. It
documents were desynchronized. That was more than two megabytes of original text reports the strings that don't match, their positions in the text, and the type
(2 millions of characters): restarting the translation from scratch would have of each of them. Moreover, the PO file generated so far is dumped as
required several months of work. F<gettextization.failed.po> for further inspection.
=head2 Hints and tricks for the gettextization process Here are some tricks to help you in this tedious process and ensure that you
salvage the most of the previous translation:
The gettextization stops as soon as a desynchronization is detected. In theory,
it should probably be possible resynchronize the gettextization later in the
documents using e.g. the same algorithm than the L<diff(1)> utility. But a manual
intervention would still be mandatory to manually match the elements that
couldn't be automatically matched, explaining why automatic resynchronization is
not implemented (yet?).
When this happens, the whole game comes down to the alignment of these damn
files' structures again through manual edits. B<po4a-gettextize> is rather
verbose about what went wrong when it happens. It reports the strings that don't
match, their positions in the text, and the type of each of them. Moreover, the
PO file generated so far is dumped as F<gettextization.failed.po> for further
inspection.
Here are some other tricks to help you in this tedious process:
=over =over
=item =item
Remove all extra content of the translations, such as the section giving credits Remove all extra content of the translations, such as the section giving credits
to the translators. You can add them back in po4a afterward, using an addenda to the translators. They should be added separately to B<po4a> as addendas (see
(see L<po4a(7)>). L<po4a(7)>).
=item =item
If you need to edit the files to align their structures, you should prefer When editing the files to align their structures, prefer editing the translation
editing the translation if possible. Indeed, if the changes to the original are if possible. Indeed, if the changes to the original are too intrusive, the old
too intrusive, the old and new versions will not be matched during the PO and new versions will not be matched during the first po4a run after
update, and the corresponding translation will be dumped anyway. But do not gettextization (see below). Any unmatched translation will be dumped anyway.
hesitate to also edit the original document if required: the important thing is That being said, you still want to edit the original document if it's too hard
to get a first PO file to start with. to get the gettextization to proceed otherwise, even if it means that one
paragraph of the translation is dumped. The important thing is to get a first PO
file to start with.
=item =item
@ -225,9 +220,11 @@ when synchronizing the PO file with the document.
=item =item
You should probably inform the original author of any structural change in the You should probably inform the original author of any structural change in the
translation that seems justified. Issues in the original document should reported translation that seems justified. Issues in the original document should
to the author. Fixing them in your translation only fixes them for a part of the reported to the author. Fixing them in your translation only fixes them for a
community. Plus, it is impossible to do so when using po4a ;) part of the community. Plus, it is impossible to do so when using po4a ;) But
you probably want to wait until the end of the conversion to B<po4a> before
changing the original files.
=item =item
@ -247,48 +244,74 @@ line and the content of the item.
Sometimes, the desynchronization message seems odd because the translation is Sometimes, the desynchronization message seems odd because the translation is
attached to the wrong original paragraph. It is the sign of an undetected issue attached to the wrong original paragraph. It is the sign of an undetected issue
earlier in the process. Search for the actual desynchronization point by earlier in the process. Search for the actual desynchronization point by
inspecting F<gettextization.failed.po>, and fix the problem where it really is. inspecting the file F<gettextization.failed.po> that was produced, and fix the
problem where it really is.
=item =item
In some unfortunate settings, you will get the feeling that po4a ate some parts Other issues may come from duplicated strings in either the original or
of the text, either the original or the translation. F<gettextization.failed.po> translation. Duplicated strings are merged in PO files, with two references.
indicates that both files matched as expected up to the paragraph N. But then, This constitutes a difficulty for the gettextization algorithm, that is a simple
an (unsuccessful) attempt is made to match the N+1 paragraph in the original one to one pairing between the B<msgid>s of both the master and the localized
file not with the N+1 paragraph in the translation as it should, but with the files. It is however believed that recent versions of po4a deal properly with
N+2 paragraph. Just as if the N+1 paragraph that you see in the document simply duplicated strings, so you should report any remaining issue that you may encounter.
disappeared from the file during the process.
This unfortunate situation happens when the same paragraph is repeated over
the document. In that case, no new entry is created in the PO file, but a
new reference is added to the existing one instead.
So, the previous situation occurs when two similar but different paragraphs are
translated in the exact same way. This will apparently remove a paragraph of the
translation. To fix the problem, it is sufficient to slightly alter one of the
translations in the document. You can also prefer to kill the second paragraph
in the original document.
To the opposite, if the same paragraph appearing twice in the original document
is not translated in the exact same way at both locations, you will get the
feeling that one paragraph of the original document just vanished. Just copy the
best translation over the other one in the translated document to fix the
problem.
=item
As a final note, do not be too surprised if the first synchronization of your PO
file takes a long time. This is because most of the msgid of the PO file
resulting from the gettextization don't match exactly any element of the POT
file built from the recent master files. This forces gettext to search for the
closest one using a costly string proximity algorithm.
For example, the first B<po4a-updatepo> of the Perl documentation's French
translation (5.5 MB PO file) took about 48 hours (yes, two days) while the
subsequent ones only take a dozen of seconds.
=back =back
=head2 Reviewing files produced by B<po4a-gettextize>
Any file produced by B<po4a-gettextize> should be manually reviewed, even when
the script terminates successfully. You should skim over the PO file, ensuring
that the B<msgid> and B<msgstr> actually match. It is not necessary to ensure
that the translation is perfectly correct yet, as all entries are marked as
fuzzy translations anyway. You only need to check for obvious matching issues
because badly matched translations will be dumped in subsequent steps while you
want to salvage them.
Fortunately, this step does not require to master the target languages as you
only want to recognize similar elements in each B<msgid> and its corresponding
B<msgstr>. As a speaker of French, English, and some German myself, I can do
this for all European languages at least, even if I cannot say one word of most
of these languages. I sometimes manage to detect matching issues in non-Latin
languages by looking at string length, phrase structures (does the amount of
interrogation marks match?) and other clues, but I prefer when someone else can
review those languages.
If you detect a mismatch, edit the original and translation files as if
B<po4a-gettextize> reported an error, and try again. Once you have a decent PO
file for your previous translation, backup it until you get po4a working
correctly.
=head2 Running B<po4a> for the first time
The easiest way to setup po4a is to write a B<po4a.conf> configuration file, and
use the integrated po4a program (B<po4a-updatepo> and B<po4a-translate> are
deprecated). Please check the "CONFIGURATION FILE" Section in L<po4a(1)>
documentation for more details.
When B<po4a> runs for the first time, the current version of the master
documents will be used to update the PO files containing the old translations
that you salvaged through gettextization. This can take quite a long time,
because many of the B<msgid>s of from the gettextization do not exactly match
the elements of the POT file built from the recent master files. This forces
gettext to search for the closest one using a costly string proximity algorithm.
For example, the first run over the Perl documentation's French translation (5.5
MB PO file) took about 48 hours (yes, two days) while the subsequent ones only
take seconds.
=head2 Moving your translations to production
After this first run, the PO files are ready to be reviewed by translators. All
entries were marked as fuzzy in the PO file by B<po4a-gettextization>, forcing
their careful review before use. Translators should take each entry to verify
that the salvaged translation actually match the current original text, update
the translation on need, and remove the fuzzy markers.
Once enough fuzzy markers are removed, B<po4a> will start generating the
translation files on disk, and you're ready to move your translation workflow to
production. Some projects find it useful to rely on weblate to coordinate
between translators and maintainers, but that's beyond B<po4a>' scope.
=head1 SEE ALSO =head1 SEE ALSO
L<po4a(1)>, L<po4a(1)>,
@ -300,12 +323,12 @@ L<po4a(7)>.
=head1 AUTHORS =head1 AUTHORS
Denis Barbier <barbier@linuxfr.org> Denis Barbier <barbier@linuxfr.org>
Nicolas Francois <nicolas.francois@centraliens.net> Nicolas François <nicolas.francois@centraliens.net>
Martin Quinson (mquinson#debian.org) Martin Quinson (mquinson#debian.org)
=head1 COPYRIGHT AND LICENSE =head1 COPYRIGHT AND LICENSE
Copyright 2002-2020 by SPI, inc. Copyright 2002-2022 by SPI, inc.
This program is free software; you may redistribute it and/or modify it This program is free software; you may redistribute it and/or modify it
under the terms of GPL (see the COPYING file). under the terms of GPL (see the COPYING file).
@ -324,8 +347,170 @@ use Locale::Po4a::Common;
use Pod::Usage qw(pod2usage); use Pod::Usage qw(pod2usage);
our %debug = (
'encoding' => 0,
);
Locale::Po4a::Common::textdomain('po4a'); Locale::Po4a::Common::textdomain('po4a');
# This function produces one translated message catalog from two catalogs, an
# original and a translation. This process is described in L<po4a(7)|po4a.7>,
# section I<Gettextization: how does it work?>.
sub gettextize {
my ( $poorig, $potrans ) = ( shift, shift );
my $pores = Locale::Po4a::Po->new();
my $please_fail = 0;
my $toobad = dgettext( "po4a",
"\nThe gettextization failed (once again). Don't give up, "
. "gettextizing is a subtle art, but this is only needed once "
. "to convert a project to the gorgeous luxus offered by po4a "
. "to translators."
. "\nPlease refer to the po4a(7) documentation, the section "
. "\"HOWTO convert a pre-existing translation to po4a?\" "
. "contains several hints to help you in your task" );
# Don't fail right now when the entry count does not match. Instead, give
# it a try so that the user can see where we fail (which is probably where
# the problem is).
if ( $poorig->count_entries_doc() > $potrans->count_entries_doc() ) {
warn wrap_mod(
"po4a gettextize",
dgettext(
"po4a",
"Original has more strings than the translation (%d>%d). "
. "Please fix it by editing the translated version to add "
. "some dummy entry."
),
$poorig->count_entries_doc(),
$potrans->count_entries_doc()
);
$please_fail = 1;
} elsif ( $poorig->count_entries_doc() < $potrans->count_entries_doc() ) {
warn wrap_mod(
"po4a gettextize",
dgettext(
"po4a",
"Original has less strings than the translation (%d<%d). "
. "Please fix it by removing the extra entry from the "
. "translated file. You may need an addendum (cf po4a(7)) "
. "to reput the chunk in place after gettextization. A "
. "possible cause is that a text duplicated in the original "
. "is not translated the same way each time. Remove one of "
. "the translations, and you're fine."
),
$poorig->count_entries_doc(),
$potrans->count_entries_doc()
);
$please_fail = 1;
}
if ( $poorig->get_charset =~ /^utf-8$/i ) {
$potrans->to_utf8;
$pores->set_charset("UTF-8");
} else {
my $charset = $potrans->get_charset();
$charset = "UTF-8" if $charset eq "CHARSET";
$pores->set_charset($charset);
}
print "Po character sets:\n"
. " original="
. $poorig->get_charset . "\n"
. " translated="
. $potrans->get_charset . "\n"
. " result="
. $pores->get_charset . "\n"
if $debug{'encoding'};
for (
my ( $o, $t ) = ( 0, 0 ) ;
$o < $poorig->count_entries_doc() && $t < $potrans->count_entries_doc() ;
$o++, $t++
)
{
#
# Extract some informations
my ( $orig, $trans ) = ( $poorig->msgid_doc($o), $potrans->msgid_doc($t) );
# print STDERR "Matches [[$orig]]<<$trans>>\n";
my ( $reforig, $reftrans ) = ( $poorig->{po}{$orig}{'reference'}, $potrans->{po}{$trans}{'reference'} );
my ( $typeorig, $typetrans ) = ( $poorig->type_doc($o), $potrans->type_doc($t) );
#
# Make sure the type of both string exist
#
die wrap_mod( "po4a gettextize", "Internal error: type of original string number %s isn't provided", $o )
if ( $typeorig eq '' );
die wrap_mod( "po4a gettextize", "Internal error: type of translated string number %s isn't provided", $o )
if ( $typetrans eq '' );
#
# Make sure both type are the same
#
if ( $typeorig ne $typetrans ) {
$pores->write("gettextization.failed.po");
eval {
# Recode $trans into current charset, if possible
require I18N::Langinfo;
I18N::Langinfo->import(qw(langinfo CODESET));
my $codeset = langinfo( CODESET() );
Encode::from_to( $trans, $potrans->get_charset, $codeset );
};
die wrap_msg(
dgettext( "po4a",
"po4a gettextization: Structure disparity between "
. "original and translated files:\n"
. "msgid (at %s) is of type '%s' while\n"
. "msgstr (at %s) is of type '%s'.\n"
. "Original text: %s\n"
. "Translated text: %s\n"
. "(result so far dumped to gettextization.failed.po)" )
. "%s",
$reforig,
$typeorig,
$reftrans,
$typetrans,
$orig, $trans, $toobad
);
}
#
# Push the entry
#
my $flags;
if ( defined $poorig->{po}{$orig}{'flags'} ) {
$flags = $poorig->{po}{$orig}{'flags'} . " fuzzy";
} else {
$flags = "fuzzy";
}
$pores->push_raw(
'msgid' => $orig,
'msgstr' => $trans,
'flags' => $flags,
'type' => $typeorig,
'reference' => $reforig,
'conflict' => 1,
'transref' => $potrans->{po}{$trans}{'reference'}
)
unless ( defined( $pores->{po}{$orig} )
and ( $pores->{po}{$orig}{'msgstr'} eq $trans ) )
# FIXME: maybe we should be smarter about what reference should be
# sent to push_raw.
}
# make sure we return a useful error message when entry count differ
die "$toobad\n" if $please_fail;
return $pores;
}
sub show_version { sub show_version {
Locale::Po4a::Common::show_version("po4a-gettextize"); Locale::Po4a::Common::show_version("po4a-gettextize");
exit 0; exit 0;
@ -341,12 +526,13 @@ my %opts = (
); );
my ($pofile) = ('-'); my ($pofile) = ('-');
my ( @masterfile, @locfile, $help_fmt, $help, $type, @options ); my ( @masterfile, @locfile, $help_fmt, $help, $keep_temps, $type, @options );
my ( $mastchar, $locchar ); my ( $mastchar, $locchar );
Getopt::Long::config( 'bundling', 'no_getopt_compat', 'no_auto_abbrev' ); Getopt::Long::config( 'bundling', 'no_getopt_compat', 'no_auto_abbrev' );
GetOptions( GetOptions(
'help|h' => \$help, 'help|h' => \$help,
'help-format' => \$help_fmt, 'help-format' => \$help_fmt,
'keep-temps|k' => \$keep_temps,
'master|m=s' => \@masterfile, 'master|m=s' => \@masterfile,
'localized|l=s' => \@locfile, 'localized|l=s' => \@locfile,
@ -382,11 +568,25 @@ foreach (@options) {
} }
} }
if (scalar @locfile == 0) {
die wrap_msg(gettext("You must provide the same amount of master files and localized files to synchronize them, ".
"as po4a-gettextize is intended to synchronize master files and previously existing translations. ".
"If just want to extract POT files of your master files, please use po4a-updatepo. ".
"Please note that the most convenient way of using po4a is to write a po4a.conf file and use the integrated po4a(1) program."))
}
# Check file existence # Check file existence
foreach my $file ( @masterfile, @locfile ) { foreach my $file ( @masterfile, @locfile ) {
$file eq '-' || -e $file || die wrap_msg( gettext("File %s does not exist."), $file ); $file eq '-' || -e $file || die wrap_msg( gettext("File %s does not exist."), $file );
} }
print wrap_msg(
gettext(
"po4a-gettextize is only useful to convert previously existing translations to a PO based workflow. "
. "Once you successfully converted your project to po4a, you should use the po4a(1) program to maintain it and update your translations."
)
);
# Declare the TransTractor parsers # Declare the TransTractor parsers
my ( $mastertt, $transtt ) = ( Locale::Po4a::Chooser::new( $type, %opts ), Locale::Po4a::Chooser::new( $type, %opts ) ); my ( $mastertt, $transtt ) = ( Locale::Po4a::Chooser::new( $type, %opts ), Locale::Po4a::Chooser::new( $type, %opts ) );
@ -408,27 +608,30 @@ $mastertt->parse;
# translation files to extract two PO files. A third PO file is built from them # translation files to extract two PO files. A third PO file is built from them
# taking strings from the second as translation of strings from the first. # taking strings from the second as translation of strings from the first.
unless ( scalar @locfile >= 1 ) { # Let's merge the two transtractor files
# Ok, outputing the pot extracted from original is enough foreach my $file (@locfile) {
$mastertt->writepo($pofile); $transtt->read( $file, $file );
} else {
# We have to merge two transtractor files
foreach my $file (@locfile) {
$transtt->read( $file, $file );
}
# We force the conversion to utf if the master document wasn't in ascii
$transtt->{TT}{utf_mode} = !$mastertt->{TT}{ascii_input};
$transtt->detected_charset($locchar);
$transtt->{TT}{po_in}->set_charset($locchar);
$transtt->parse;
my $mergedpo = Locale::Po4a::Po->gettextize( $mastertt->getpoout(), $transtt->getpoout() );
$mergedpo->write($pofile);
} }
# We force the conversion to utf if the master document wasn't in ascii
$transtt->{TT}{utf_mode} = !$mastertt->{TT}{ascii_input};
$transtt->detected_charset($locchar);
$transtt->{TT}{po_in}->set_charset($locchar);
$transtt->parse;
if ($keep_temps) {
$mastertt->getpoout()->write("po4atemp.master.po");
$transtt->getpoout()->write("po4atemp.localized.po");
print wrap_msg(
dgettext(
"po4a",
"Temporary master and localized POT files dumped to po4atemp.master.po and po4atemp.localized.po"
)
);
}
my $mergedpo = gettextize( $mastertt->getpoout(), $transtt->getpoout() );
$mergedpo->write($pofile);
__END__ __END__

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