X-Git-Url: http://git.vanrenterghem.biz/git.ikiwiki.info.git/blobdiff_plain/458ccebdd3f47dbae79d4129c043e049e954d7cc..c440a39ba4ba6726ab67b0e1d04b064ae84f67ef:/doc/plugins/po.mdwn
diff --git a/doc/plugins/po.mdwn b/doc/plugins/po.mdwn
index b587c79c4..d16d13edc 100644
--- a/doc/plugins/po.mdwn
+++ b/doc/plugins/po.mdwn
@@ -6,15 +6,17 @@ gettext, using [po4a](http://po4a.alioth.debian.org/).
It depends on the Perl `Locale::Po4a::Po` library (`apt-get install po4a`).
+[[!toc levels=2]]
+
Introduction
============
A language is chosen as the "master" one, and any other supported
language is a "slave" one.
-A page written in the "master" language is a "master" page, and is
-written in any supported format but PO. It does not have to be named
-a special way: migration from/to this plugin does not imply heavy
+A page written in the "master" language is a "master" page. It can be
+of any page type supported by ikiwiki, except `po`. It does not have to be
+named a special way: migration to this plugin does not imply any page
renaming work.
Example: `bla/page.mdwn` is a "master" Markdown page written in
@@ -22,7 +24,7 @@ English; if `usedirs` is enabled, it is rendered as
`bla/page/index.en.html`, else as `bla/page.en.html`.
Any translation of a "master" page into a "slave" language is called
-a "slave" page; it is written in the gettext PO format. PO is now
+a "slave" page; it is written in the gettext PO format. `po` is now
a page type supported by ikiwiki.
Example: `bla/page.fr.po` is the PO "message catalog" used to
@@ -33,7 +35,10 @@ rendered as `bla/page/index.fr.html`, else as `bla/page.fr.html`
Configuration
=============
-`po_master_language` is used to set the master language in
+Supported languages
+-------------------
+
+`po_master_language` is used to set the "master" language in
`ikiwiki.setup`, such as:
po_master_language => { 'code' => 'en', 'name' => 'English' }
@@ -41,9 +46,9 @@ Configuration
`po_slave_languages` is used to set the list of supported "slave"
languages, such as:
- po_slave_languages => { 'fr' => { 'name' => 'Français', },
- 'es' => { 'name' => 'Castellano', },
- 'de' => { 'name' => 'Deutsch', }
+ po_slave_languages => { 'fr' => 'Français',
+ 'es' => 'Castellano',
+ 'de' => 'Deutsch',
}
Decide which pages are translatable
@@ -53,44 +58,43 @@ The `po_translatable_pages` setting configures what pages are
translatable. It is a [[ikiwiki/PageSpec]], so you have lots of
control over what kind of pages are translatable.
-The `*.LL.po` translations files are anyway not considered as being
-translatable, so you don't need to worry about excluding them manually
-from this [[ikiwiki/PageSpec]].
+The `.po` files are not considered as being translatable, so you don't need to
+worry about excluding them explicitly from this [[ikiwiki/PageSpec]].
Internal links
--------------
-One can use the `po_link_to_` option in `ikiwiki.setup` to choose how
+The `po_link_to` option in `ikiwiki.setup` is used to decide how
internal links should be generated, depending on web server features
and site-specific preferences.
### Default linking behavior
If `po_link_to` is unset, or set to `default`, ikiwiki's default
-linking behavior is preserved: `[[page]]` links to the master
+linking behavior is preserved: `\[[destpage]]` links to the master
language's page.
-### Link to negotiated language
+### Link to current language
-If `po_link_to` is set to `negotiated`, `[[page]]` links to the
-negotiated preferred language, *i.e.* `foo/page/`.
+If `po_link_to` is set to `current`, `\[[destpage]]` links to the
+`destpage`'s version written in the current page's language, if
+available, *i.e.*:
-(In)compatibility notes:
+- `foo/destpage/index.LL.html` if `usedirs` is enabled
+- `foo/destpage.LL.html` if `usedirs` is disabled
-- `po_link_to => negotiated` provides no useful behavior if `usedirs`
- is disabled; this option combination is neither implemented nor
- allowed
-- `po_link_to => negotiated` provides no useful behavior if the web
- server does not support Content Negotiation
+### Link to negotiated language
-### Link to current language
+If `po_link_to` is set to `negotiated`, `\[[page]]` links to the
+negotiated preferred language, *i.e.* `foo/page/`.
-If `po_link_to` is set to `current` and the destination page is either
-a translatable page or a translation, `[[page]]` links to the current
-page's language, *i.e.*:
+(In)compatibility notes:
-- `foo/page/index.LL.html` if `usedirs` is enabled
-- `foo/page.LL.html` if `usedirs` is disabled
+- if `usedirs` is disabled, it does not make sense to set `po_link_to`
+ to `negotiated`; this option combination is neither implemented
+ nor allowed.
+- if the web server does not support Content Negotiation, setting
+ `po_link_to` to `negotiated` will produce a unusable website.
Server support
@@ -99,17 +103,16 @@ Server support
Apache
------
-Using `mod_negotiation` makes it really easy to have Apache serve the
-page in the client's preferred language, if available. This is the
-default Debian Apache configuration.
+Using Apache `mod_negotiation` makes it really easy to have Apache
+serve any page in the client's preferred language, if available.
+This is the default Debian Apache configuration.
When `usedirs` is enabled, one has to set `DirectoryIndex index` for
the wiki context.
Setting `DefaultLanguage LL` (replace `LL` with your default MIME
-language) for the wiki context can be needed, to ensure
+language code) for the wiki context can help to ensure
`bla/page/index.en.html` is served as `Content-Language: LL`.
-**FIXME**: is it still needed with the new `.en.html` naming convention?
lighttpd
--------
@@ -120,82 +123,427 @@ lighttpd unfortunately does not support content negotiation.
emulate this?
+Usage
+=====
+
+Templates
+---------
+
+When `po_link_to` is not set to `negotiated`, one should replace some
+occurrences of `BASEURL` with `HOMEPAGEURL` to get correct links to
+the wiki homepage.
+
+The `ISTRANSLATION` and `ISTRANSLATABLE` variables can be used to
+display things only on translatable or translation pages.
+
+### Display page's versions in other languages
+
+The `OTHERLANGUAGES` loop provides ways to display other languages'
+versions of the same page, and the translations' status.
+
+One typically adds the following code to `templates/page.tmpl`:
+
+
+
+
+--- /usr/share/perl5/Locale/Po4a/Common.pm 2008-07-21 14:54:52.000000000 -0400 ++++ Common.pm 2008-11-11 18:27:34.000000000 -0500 +@@ -30,8 +30,16 @@ + use strict; + use warnings; + +-BEGIN { +- if (eval { require Text::WrapI18N }) { ++sub import { ++ my $class=shift; ++ my $wrapi18n=1; ++ if ($_[0] eq 'nowrapi18n') { ++ shift; ++ $wrapi18n=0; ++ } ++ $class->export_to_level(1, $class, @_); ++ ++ if ($wrapi18n && eval { require Text::WrapI18N }) { + + # Don't bother determining the wrap column if we cannot wrap. + my $col=$ENV{COLUMNS}; +-Display available translations ------------------------------- +##### Term::ReadKey -The [[linguas|plugins/contrib/linguas]] plugin has some code that can -be used as a basis to display the existing translations, and allow to -navigate between them. +`Term::ReadKey` is not a hard dependency in our case, *i.e.* po4a +works nicely without it. But the po4a Debian package recommends +`libterm-readkey-perl`, so it will probably be installed on most +systems using the po plugin. + +If `$ENV{COLUMNS}` is not set, `Locale::Po4a::Common` uses +`Term::ReadKey::GetTerminalSize()` to get the terminal size. How safe +is this? + +Part of `Term::ReadKey` is written in C. Depending on the runtime +platform, this function use ioctl, environment, or C library function +calls, and may end up running the `resize` command (without +arguments). + +IMHO, using Term::ReadKey has too far reaching implications for us to +be able to guarantee anything wrt. security. Since it is anyway of no +use in our case, I suggest we define `ENV{COLUMNS}` before loading +`Locale::Po4a::Common`, just to be on the safe side. Joey? +[[--intrigeri]] + +> Update: adding an option to disable `Text::WrapI18N`, as Nicolas +> François suggested, would as a bonus disable `Term::ReadKey` +> as well. [[--intrigeri]] -View translation status ------------------------ +### msgmerge -One should be able to view some information about the translation -completeness, either for a given page or for the whole wiki. +`refreshpofiles()` runs this external program. A po4a developer +answered he does "not expect any security issues from it". -This should not be too hard using gettext tools. If this is -implemented as a -[[HTML::Template|http://search.cpan.org/search?mode=dist&query=HTML%3A%3ATemplate]] -loop, a page using it should depend on any "master" and "slave" pages -whose status is being displayed. +### Fuzzing input -Automatic PO files update -------------------------- +I was not able to find any public information about gettext or po4a +having been tested with a fuzzing program, such as `zzuf` or `fusil`. +Moreover, some gettext parsers seem to be quite +[easy to crash](http://fusil.hachoir.org/trac/browser/trunk/fuzzers/fusil-gettext), +so it might be useful to bang msgmerge/po4a's heads against such +a program in order to easily detect some of the most obvious DoS. +[[--intrigeri]] + +> po4a was not fuzzy-tested, but according to one of its developers, +> "it would be really appreciated". [[--intrigeri]] + +Test conditions: + +- a 21M file containing 100 concatenated copies of all the files in my + `/usr/share/common-licenses/`; I had no existing PO file or + translated versions at hand, which renders these tests + quite incomplete. +- po4a was the Debian 0.34-2 package; the same tests were also run + after replacing the `Text` module with the CVS one (the core was not + changed in CVS since 0.34-2 was released), without any significant + difference in the results. +- Perl 5.10.0-16 + +#### po4a-gettextize + +`po4a-gettextize` uses more or less the same po4a features as our +`refreshpot` function. + +Without specifying an input charset, zzuf'ed `po4a-gettextize` quickly +errors out, complaining it was not able to detect the input charset; +it leaves no incomplete file on disk. + +So I had to pretend the input was in UTF-8, as does the po plugin. + +Two ways of crashing were revealed by this command-line: + + zzuf -vc -s 0:100 -r 0.1:0.5 \ + po4a-gettextize -f text -o markdown -M utf-8 -L utf-8 \ + -m LICENSES >/dev/null + +They are: + + Malformed UTF-8 character (UTF-16 surrogate 0xdcc9) in substitution iterator at /usr/share/perl5/Locale/Po4a/Po.pm line 1443. + Malformed UTF-8 character (fatal) at /usr/share/perl5/Locale/Po4a/Po.pm line 1443. + +and + + Malformed UTF-8 character (UTF-16 surrogate 0xdcec) in substitution (s///) at /usr/share/perl5/Locale/Po4a/Po.pm line 1443. + Malformed UTF-8 character (fatal) at /usr/share/perl5/Locale/Po4a/Po.pm line 1443. + +Perl seems to exit cleanly, and an incomplete PO file is written on +disk. I not sure whether if this is a bug in Perl or in `Po.pm`. + +> It's fairly standard perl behavior when fed malformed utf-8. As long as it doesn't +> crash ikiwiki, it's probably acceptable. Ikiwiki can do some similar things itself when fed malformed utf-8 (doesn't crash tho) --[[Joey]] -Committing changes to a "master" page must: +#### po4a-translate -1. update the POT file and the PO files for the supported languages, - before putting them under version control -2. trigger a refresh of the corresponding HTML slave pages +`po4a-translate` uses more or less the same po4a features as our +`filter` function. -The former is to be done at a time when: +Without specifying an input charset, same behaviour as +`po4a-gettextize`, so let's specify UTF-8 as input charset as of now. -- we know which "master" page was modified, and thus, which POT/PO - files have to be updated: the `needsbuild` hook is the first one to - run that provides us with the necessary information -- we can modify the list of pages needing a refresh; this is - `needsbuild` hook's job + zzuf -cv \ + po4a-translate -d -f text -o markdown -M utf-8 -L utf-8 \ + -k 0 -m LICENSES -p LICENSES.fr.po -l test.fr -The latter can be implemented by making any "slave" page depend on the -corresponding "master" page. The `add_depends` function can achieve -this, if used in a **FIXME** hook. +... prints tons of occurences of the following error, but a complete +translated document is written (obviously with some weird chars +inside): -UI consistency: rename "Edit" button on slave pages ---------------------------------------------------- + Use of uninitialized value in string ne at /usr/share/perl5/Locale/Po4a/TransTractor.pm line 854. + Use of uninitialized value in string ne at /usr/share/perl5/Locale/Po4a/TransTractor.pm line 840. + Use of uninitialized value in pattern match (m//) at /usr/share/perl5/Locale/Po4a/Po.pm line 1002. -It may be surprising to some, after having pressed *Edit* on a wiki -page, to end up editing a strange looking PO file. The *Edit* button -displayed on "slave" pages must therefore be renamed *Improve -translation* . +While: -Pages selection depending on language -------------------------------------- + zzuf -cv -s 0:10 -r 0.001:0.3 \ + po4a-translate -d -f text -o markdown -M utf-8 -L utf-8 \ + -k 0 -m LICENSES -p LICENSES.fr.po -l test.fr -To improve user navigation in a multi-lingual wiki, site developers -must be enabled to write: +... seems to lose the fight, at the `readpo(LICENSES.fr.po)` step, +against some kind of infinite loop, deadlock, or any similar beast. +It does not seem to eat memory, though. + +Whatever format module is used does not change anything. This is thus +probably a bug in po4a's core or in a lib it depends on. + +The sub `read`, in `TransTractor.pm`, seems to be a good debugging +starting point. + +#### msgmerge + +`msgmerge` is run in our `refreshpofiles` function. I did not manage +to crash it with `zzuf`. + +gettext/po4a rough corners +-------------------------- + +- fix infinite loop when synchronizing two ikiwiki (when checkouts + live in different directories): say bla.fr.po has been updated in + repo2; pulling repo2 from repo1 seems to trigger a PO update, that + changes bla.fr.po in repo1; then pushing repo1 to repo2 triggers + a PO update, that changes bla.fr.po in repo2; etc.; quickly fixed in + `629968fc89bced6727981c0a1138072631751fee`, by disabling references + in Pot files. Using `Locale::Po4a::write_if_needed` might be + a cleaner solution. (warning: this function runs the external + `diff` program, have to check security) +- new translations created in the web interface must get proper + charset/encoding gettext metadata, else the next automatic PO update + removes any non-ascii chars; possible solution: put such metadata + into the Pot file, and let it propagate; should be fixed in + `773de05a7a1ee68d2bed173367cf5e716884945a`, time will tell. + +Better links +------------ - \[[!map pages="dev/* and lang(LL)" feeds="no"]] +### Page title in links - \[[!map pages="dev/* and currentlang()" feeds="no"]] +To use the page titles set with the [[meta|plugins/meta]] plugin when +rendering links would be very much nicer, than the current +"filename.LL" format. This is actually a duplicate for +[[bugs/pagetitle_function_does_not_respect_meta_titles]]. + +Going to work on this in my `meta` branch. + +### Translation status in links + +See [[contrib/po]]. + +### Backlinks + +They are not updated when the source page changes (e.g. meta title). + +Page formats +------------ + +Markdown is well supported, great, but what about others? + +The [[po|plugins/po]] uses `Locale::Po4a::Text` for every page format; +this can be expected to work out of the box with most other wiki-like +formats supported by ikiwiki. Some of their ad-hoc syntax might be +parsed in a strange way, but the worst problems I can imagine would be +wrapping issues; e.g. there is code in po4a dedicated to prevent +re-wrapping the underlined Markdown headers. + +While it would be easy to better support formats such as [[html]] or +LaTeX, by using for each one the dedicated po4a module, this can be +problematic from a security point of view. + +**TODO**: test the more popular formats and write proper documentation +about it. Translation quality assurance ----------------------------- -Modifying a PO file via the CGI must only be allowed if the new -version is a valid PO file. As a bonus, check that it provides a more +Modifying a PO file via the CGI must be forbidden if the new version +is not a valid PO file. As a bonus, check that it provides a more complete translation than the existing one. A new `cansave` type of hook would be needed to implement this. @@ -203,40 +551,24 @@ A new `cansave` type of hook would be needed to implement this. Note: committing to the underlying repository is a way to bypass this check. -Translating online ------------------- - -As PO is a wiki page type, we already have an online PO editor, that -is ikiwiki's CGI. - -A message-by-message interface could also be implemented at some -point; a nice way to do offline translation work (without VCS access) -still has to be offered, though. - -Translating offline without VCS access --------------------------------------- - -The following workflow should be made possible for translators without -VCS access who need to edit the PO files in another editor than a web -browser: +Creating new pages on the web +----------------------------- -- download the page's PO file -- use any PO editor to update the translation -- upload the updated PO file +See [[contrib/po]]. -Implementation note: a generic mechanism to upload a page's source is -needed: it's only an alternative way to allow saving a the modified -page's source with the CGI. +Robustness tests +---------------- -### Short-term workflow +- adding and removing pages from `po_translatable_pages` +- creating and removing pages listed in `po_translatable_pages` +- enabling the plugin, with and without `po_translatable_pages` set +- disabling the plugin +- test with `usedirs` disabled +- test with `indexpages` enabled -A possible workaround is: +Documentation +------------- -- pretend to edit the PO file online -- copy the PO file content from the textarea -- cancel the edit -- paste the content into a local file. -- edit the local file in any PO editor -- pretend to edit the PO file online -- paste the modified local file's content into the edit textarea -- save +Maybe write separate documentation depending on the people it targets: +translators, wiki administrators, hackers. This plugin may be complex +enough to deserve this.