update for rename of bugs/Re-use_translated_content_instead_of_skipping_if_previously...
[ikiwiki] / doc / bugs / po:_second_or_subsequent_inline_of_translated_page_inlines_.po_file__44___not_translated_content.mdwn
1 There is an issue where an initial "inline" directive would be
2 translated correctly but subsequent inlines of the same page would
3 result in the raw contents of the ".po" file (ie. starting with the raw
4 copyright headers!) being inserted into the page instead.
5
6 For example, given a "index.mdwn" containing:
7
8     \[[!inline pages="inline" raw="yes"]]
9     \[[!inline pages="inline" raw="yes"]]
10
11 … and an "index.de.po" of:
12
13     msgid "\[[!inline pages=\"inline\" raw=\"yes\"]]\n"
14     msgstr "\[[!inline pages=\"inline.de\" raw=\"yes\"]]\n"
15
16 … together with an "inline.mdwn" of:
17
18    This is inlined content.
19
20 … and an "inline.de.po" of:
21
22     msgid "This is inlined content."
23     msgstr "This is German inlined content."
24
25 §
26
27 This would result in the following translation:
28
29     This is the inlined content.
30     # SOME DESCRIPTIVE TITLE
31     # Copyright (C) YEAR Free Software Foundation, Inc.
32     # This file is distributed under the same license as the PACKAGE package.
33     # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
34
35 … instead of (of course)
36
37     This is the inlined content.
38     This is the inlined content.
39
40 [[Initially proposed patch from Chris Lamb|bugs/po:_second_or_subsequent_inline_of_translated_page_inlines_.po_file,_not_translated_content/20180628-patch.txt]]
41
42 [[!tag patch]]
43
44 > Thank you Chris! I've reviewed the patch (with my "original author of the po plugin" hat on) and it looks good to me. I'm not 100% sure about `alreadyfiltered` being the best name for something that's not a predicated anymore but it's good enough. Then I wore my end-user hat and confirmed that with Chris' patch applied, the reproducer we had for this bug at Tails works fine. So IMO we're good to go and I recommend to apply this patch. Thanks in advance! -- [[intrigeri]]
45
46 > Any update on getting this merged? — [[lamby]], Fri, 24 Aug 2018 12:36:37 +0200
47
48 > Indeed, would love to see this merged! What might be the next steps here? — [[lamby]],  Thu, 18 Oct 2018 17:57:37 -0400
49
50 > I've filed this in Debian GNU/Linux at <https://bugs.debian.org/911356> — [[lamby]], Thu, 18 Oct 2018 20:18:58 -0400
51
52 >> As I said on the Debian bug, I think we badly need test coverage for
53 >> this sort of thing, otherwise it *will* regress. The po plugin is
54 >> relatively complicated and hooks into lots of places in ikiwiki,
55 >> and I don't think any of the active ikiwiki maintainers use it
56 >> themselves, which means it can easily break (or have pre-existing
57 >> bugs) without anyone realising.
58 >>
59 >> For now I've added a failing test-case for this particular bug.
60 >> --[[smcv]]
61
62 ---
63
64 Review from [[smcv]]:
65
66 The patch attached to the Debian bug and the patch pasted here (which
67 I've moved to an attachment) appear to be different, but I'm not going to
68 do a line-by-line review of the patches and their differences for now
69 because I'm not sure their approach is fully correct.
70
71 As we know, the two hardest things in computer science are naming, cache
72 invalidation and off-by-one errors. Unfortunately this patch has issues
73 with naming and cache invalidation. It's effectively turning the
74 `alreadyfiltered` mechanism into a cache of memoized results of calling
75 `po_to_markup()` on pages' content, keyed by the page name and the
76 `destpage`, which is either the page name again or the name of a page
77 into which the `page` is to be inlined (even though the result of
78 `po_to_markup()` doesn't actually vary with the `destpage`).
79
80 This naturally raises the usual concerns about having a cache:
81
82 * How large does it grow?
83 * Do we invalidate it every time we need to?
84 * Do we even need it?
85
86 The cache size is mainly a concern for large wikis being rebuilt. If you
87 have a wiki with 1000 translated pages in 3 languages each, each of which
88 is inlined into an average of one other page, then by the time you finish
89 a rebuild you'll be holding 6000 translated pages in memory. If we change
90 the `alreadyfiltered` mechanism to be keyed by the page name and not the
91 (page, destpage) pair, that drops to 3000, but that's still
92 O(pages \* languages) which seems like a lot. As a general design
93 principle, ikiwiki tries not to hold full content in RAM for more than
94 the currently-processed page.
95
96 We invalidate the `alreadyfiltered` for a (page, page) pair in an
97 editcontent hook, and we never invalidate (page, destpage) pairs for
98 page != destpage at all. Are we sure there is no other circumstance in
99 which the content of a page can change?
100
101 One of the things I tried doing for a simple solution was to remove the
102 cache altogether, because I wasn't sure why we had this `alreadyfiltered`
103 mechanism in the first place. This passes tests, which suggests that
104 either the `alreadyfiltered` mechanism is unnecessary, or our regression
105 test coverage for `po` is insufficient.
106
107 Looking back at the history of the `po` plugin, it seems that the
108 `alreadyfiltered` mechanism was introduced (under a different name,
109 with less abstraction) by [[intrigeri]] in commit 1e874b3f:
110
111     po plugin[filter]: avoid converting more than once per destfile
112
113     Only the first filter function call on a given {page,destpage} must convert it
114     from the PO file, subsequent calls must leave the passed $content unmodified.
115
116     Else, preprocessing loops are the rule.
117
118 I don't understand this. Under what circumstances would we pass content
119 through the filter hooks, and then pass it back through the same filter
120 hooks? Can we not do that, instead? If at all possible we should at
121 least have test coverage for the situation where this happened (but I
122 can't add this without knowing what it was).
123
124 I feel as though it should be an invariant that the output of a filter
125 hook is never passed back through filter hooks: otherwise every filter
126 hook would have to be able to be able to detect and skip processing
127 its own output, which is not necessarily even possible. For instance,
128 suppose you had a plugin with a filter that turned tab-separated text
129 files into `<table>` markup: every HTML file that doesn't contain tabs
130 is trivially a TSV file with one column, so you can't know whether a
131 blob of text is TSV or HTML.
132
133 I wondered whether the loops referenced in 1e874b3f might have been
134 fixed in 192ce7a2:
135
136     remove unnecessary and troublesome filter calls
137
138     This better defines what the filter hook is passed, to only be the raw,
139     complete text of a page. Not some snippet, or data read in from an
140     unrelated template.
141
142     Several plugins that filtered text that originates from an (already
143     filtered) page were modified not to do that. Note that this was not
144     done very consistently before; other plugins that receive text from a
145     page called preprocess on it w/o first calling filter.
146
147     The template plugin gets text from elsewhere, and was also changed not to
148     filter it. That leads to one known regression -- the embed plugin cannot
149     be used to embed stuff in templates now. But that plugin is deprecated
150     anyway.
151
152     Later we may want to increase the coverage of what is filtered. Perhaps
153     a good goal would be to allow writing a filter plugin that filters
154     out unwanted words, from any input. We're not there yet; not only
155     does the template plugin load unfiltered text from its templates now,
156     but so can the table plugin, and other plugins that use templates (like
157     inline!). I think we can cross that bridge when we come to it. If I wanted
158     such a censoring plugin, I'd probably make it use a sanitize hook instead,
159     for the better coverage.
160
161     For now I am concentrating on the needs of the two non-deprecated users
162     of filter. This should fix bugs/po_vs_templates, and it probably fixes
163     an obscure bug around txt's use of filter for robots.txt.
164
165 but I'm not sure that any of the redundant filtering removed in that
166 commit was actually relevant to `po` users?
167
168 [[intrigeri]], can you shed any light on this?
169
170 Naming is the easy part of this review: the `alreadyfiltered` family of
171 functions are not named like cache getter/setter functions. This could
172 be resolved by renaming.
173
174 ---
175
176 [[!template id=gitbranch branch=smcv/wip/po-filter-every-time browse="https://git.pseudorandom.co.uk/smcv/ikiwiki.git/log/refs/heads/wip/po-filter-every-time" author="[[Simon_McVittie|smcv]]"]]
177
178 If it's valid to remove the `alreadyfiltered` mechanism, my
179 `wip/po-filter-every-time` branch does that. Please test?