Merge lp:~dholbach/help-app/1425010 into lp:~ubuntu-touch-coreapps-drivers/help-app/trunk

Proposed by Daniel Holbach on 2015-02-24
Status: Merged
Merged at revision: 54
Proposed branch: lp:~dholbach/help-app/1425010
Merge into: lp:~ubuntu-touch-coreapps-drivers/help-app/trunk
Diff against target: 427 lines (+109/-125)
11 files modified
.bzrignore (+2/-1)
Makefile (+0/-1)
edit-here/Makefile (+1/-0)
edit-here/content/pages/apps.md (+0/-1)
edit-here/content/pages/faq.md (+0/-1)
edit-here/content/pages/get-in-touch.md (+0/-1)
edit-here/content/pages/index.md (+0/-1)
edit-here/generate-pot (+0/-2)
edit-here/pelicanconf.py (+6/-4)
edit-here/po/de.po (+3/-3)
edit-here/translations.py (+97/-110)
To merge this branch: bzr merge lp:~dholbach/help-app/1425010
Reviewer Review Type Date Requested Status
Daniel Holbach (community) Approve on 2015-02-26
David Planella 2015-02-24 Needs Fixing on 2015-02-26
Review via email: mp+250798@code.launchpad.net

Description of the Change

This is not a complete fix for bug 1425010, but is where I got stuck for now. Links are broken again.

To post a comment you must log in.
lp:~dholbach/help-app/1425010 updated on 2015-02-25
83. By Daniel Holbach on 2015-02-25

add code for adding a fake en-US profile - write translated files to pages/<lang>/<file.md>

84. By Daniel Holbach on 2015-02-25

define location of .pot file just once

85. By Daniel Holbach on 2015-02-25

rewrite links again, we are going to go with the solution of <filename>.<lang>.html, change paths accordingly

86. By Daniel Holbach on 2015-02-25

remove stray print statements

87. By Daniel Holbach on 2015-02-25

don't generate pointless duplicates of our pages, the 'original' is the 'en-us' variant

88. By Daniel Holbach on 2015-02-25

make pep8 happy

89. By Daniel Holbach on 2015-02-25

update .bzrignore list

90. By Daniel Holbach on 2015-02-25

remove 'lang' from markdown files and its special-casing, we get it for free from the generated filenames - we can likely remove more code now

91. By Daniel Holbach on 2015-02-25

remove unused code

92. By Daniel Holbach on 2015-02-25

simplify code some more

93. By Daniel Holbach on 2015-02-25

simplify Makefile logic ('make html' will work in top and 'edit-here' directory)

94. By Daniel Holbach on 2015-02-25

restructure functions in the translations classes to make more sense

95. By Daniel Holbach on 2015-02-25

make pep8 happy

David Planella (dpm) wrote :

Looks good to me, although as discussed yesterday on IRC, I'm still not sure about creating the fake en_US translation.

For the broken links, it seems what is happening is that during the build pelican is trying to find e.g. faq.html, which is not built (we build faq.en-us.html instead) so it gets confused and outputs the link as './'. If the index.md file is modified as below, then the generated links work (but for English only).

[Take me to the FAQ!]({filename}faq.en-us.md)

review: Needs Fixing
lp:~dholbach/help-app/1425010 updated on 2015-02-26
96. By Daniel Holbach on 2015-02-26

rewrite links also for the case of en_US

David Planella (dpm) wrote :

This works well now. I've noticed something else, not sure if it needs to be fixed in this branch, though:

Whenever I click on the top-level 'de' links to switch to the German translation, the German pages contain an additional 'en' link that does not work. I think it should just be 'de' also for the German pages.

Translations: en-us en

Going forward, once we can load translations based on the user language, I'd recommend removing the translation links - languages should be set automatically for a smooth experience.

Daniel Holbach (dholbach) wrote :

I filed bug 1425924.

Daniel Holbach (dholbach) wrote :

<dholbach> dpm, the links were added automagically by pelican - I'll see what we can do and file a separate bug if that's ok
 dpm, I think we're now at the point where we can start adding content, fixing the styling and everything else
 dpm, shall I fix it in the same branch or in a separate branch?
<dpm> dholbach, yeah, let's do a separate branch
<dholbach> ok cool
 I'll land the other one, ok?
<dpm> dholbach, yeah. ...

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file '.bzrignore'
2--- .bzrignore 2015-02-20 16:06:22 +0000
3+++ .bzrignore 2015-02-26 11:59:44 +0000
4@@ -1,5 +1,6 @@
5 app/www
6 *.click
7 edit-here/cache
8-edit-here/content/pages/lang-??-*
9 edit-here/backup
10+edit-here/po/en_US.po
11+edit-here/content/pages/*.*.md
12
13=== modified file 'Makefile'
14--- Makefile 2015-02-23 10:21:42 +0000
15+++ Makefile 2015-02-26 11:59:44 +0000
16@@ -11,7 +11,6 @@
17 cd app && click build . && mv *.click ..
18
19 html: clean
20- cd edit-here && ./generate-translations
21 make -C edit-here html
22
23 serve:
24
25=== modified file 'edit-here/Makefile'
26--- edit-here/Makefile 2015-02-13 16:08:50 +0000
27+++ edit-here/Makefile 2015-02-26 11:59:44 +0000
28@@ -58,6 +58,7 @@
29 @echo ' '
30
31 html:
32+ ./generate-translations
33 $(PELICAN) $(INPUTDIR) -o $(OUTPUTDIR) -s $(CONFFILE) $(PELICANOPTS)
34
35 clean:
36
37=== modified file 'edit-here/content/pages/apps.md'
38--- edit-here/content/pages/apps.md 2015-02-20 16:05:55 +0000
39+++ edit-here/content/pages/apps.md 2015-02-26 11:59:44 +0000
40@@ -1,5 +1,4 @@
41 Title: FAQ - Apps
42-Lang: en
43
44 Are you looking for a good way to do X and can't find it in the app
45 store? Want some suggestions to solve a particular problem?
46
47=== modified file 'edit-here/content/pages/faq.md'
48--- edit-here/content/pages/faq.md 2015-02-20 16:05:55 +0000
49+++ edit-here/content/pages/faq.md 2015-02-26 11:59:44 +0000
50@@ -1,5 +1,4 @@
51 Title: Get your questions answered.
52-Lang: en
53
54 We divided the questions up into categories, so you can spot yours more
55 easily. Here we go:
56
57=== modified file 'edit-here/content/pages/get-in-touch.md'
58--- edit-here/content/pages/get-in-touch.md 2015-02-20 16:05:55 +0000
59+++ edit-here/content/pages/get-in-touch.md 2015-02-26 11:59:44 +0000
60@@ -1,5 +1,4 @@
61 Title: Get in touch
62-Lang: en
63
64 Ubuntu has a huge community. You can easily get in touch with experts and
65 other enthusiasts. One good way to do so, particularly if you **have
66
67=== modified file 'edit-here/content/pages/index.md'
68--- edit-here/content/pages/index.md 2015-02-20 16:05:55 +0000
69+++ edit-here/content/pages/index.md 2015-02-26 11:59:44 +0000
70@@ -1,5 +1,4 @@
71 Title: Welcome to Help for Ubuntu for devices!
72-Lang: en
73
74 The world-wide Ubuntu community wants to give you the best possible
75 experience on your Ubuntu device. This is why we collected:
76
77=== modified file 'edit-here/generate-pot'
78--- edit-here/generate-pot 2015-02-24 09:17:31 +0000
79+++ edit-here/generate-pot 2015-02-26 11:59:44 +0000
80@@ -10,8 +10,6 @@
81
82 def main():
83 translations = Translations()
84- if not translations.clean_documents():
85- sys.exit(1)
86 translations.generate_pot_file()
87 if not translations.update_po_files():
88 sys.exit(1)
89
90=== modified file 'edit-here/pelicanconf.py'
91--- edit-here/pelicanconf.py 2015-02-24 09:35:13 +0000
92+++ edit-here/pelicanconf.py 2015-02-26 11:59:44 +0000
93@@ -14,11 +14,13 @@
94
95 RELATIVE_URLS = True
96 DIRECT_TEMPLATES = []
97+
98 SLUGIFY_SOURCE = 'basename'
99-PAGE_URL = '{slug}.html'
100-PAGE_SAVE_AS = '{slug}.html'
101-PAGE_LANG_URL = '{slug}.html'
102-PAGE_LANG_SAVE_AS = '{slug}.html'
103+FILENAME_METADATA = '(?P<slug>\S+)\.(?P<lang>\S+)\.*'
104+PAGE_URL = ''
105+PAGE_SAVE_AS = ''
106+PAGE_LANG_URL = '{slug}.{lang}.html'
107+PAGE_LANG_SAVE_AS = '{slug}.{lang}.html'
108
109 # Feed generation is usually not desired when developing
110 FEED_ALL_ATOM = None
111
112=== modified file 'edit-here/po/de.po'
113--- edit-here/po/de.po 2015-02-24 13:17:06 +0000
114+++ edit-here/po/de.po 2015-02-26 11:59:44 +0000
115@@ -33,7 +33,7 @@
116 #. type: Bullet: ' * '
117 #: content/pages/faq.md:7
118 msgid "[Apps]({filename}apps.md)"
119-msgstr "[Apps]({filename}lang-de-apps.md)"
120+msgstr "[Apps]({filename}apps.de.md)"
121
122 #. type: Bullet: ' * '
123 #: content/pages/faq.md:7
124@@ -81,14 +81,14 @@
125 #, fuzzy, no-wrap
126 #| msgid "[Take me to the FAQ!](faq.html) \n"
127 msgid "[Take me to the FAQ!]({filename}faq.md) \n"
128-msgstr "[Zu den Antworten!]({filename}lang-de-faq.md) \n"
129+msgstr "[Zu den Antworten!]({filename}faq.de.md) \n"
130
131 #. type: Plain text
132 #: content/pages/index.md:14
133 #, fuzzy
134 #| msgid "[Get in touch](get-in-touch.html)"
135 msgid "[Get in touch]({filename}get-in-touch.md)"
136-msgstr "[Kontakt]({filename}lang-de-get-in-touch.md)"
137+msgstr "[Kontakt]({filename}get-in-touch.de.md)"
138
139 #. type: Plain text
140 #: content/pages/apps.md:2
141
142=== modified file 'edit-here/translations.py'
143--- edit-here/translations.py 2015-02-24 13:16:43 +0000
144+++ edit-here/translations.py 2015-02-26 11:59:44 +0000
145@@ -6,44 +6,35 @@
146 import re
147 import shutil
148 import subprocess
149-import tempfile
150
151 from pelicanconf import PATH
152
153-METADATA_TAGS = [
154- 'title',
155- 'lang',
156- 'date',
157- ]
158-
159 # This defines how complete we expect translations to be before we
160 # generate HTML from them. Needs to be string.
161 TRANSLATION_COMPLETION_PERCENTAGE = '0'
162
163+BCP47_OVERRIDES = (
164+ ('zh_CN', 'zh-hans'),
165+ ('zh_TW', 'zh-hant'),
166+)
167+
168
169 class PO4A(object):
170- def __init__(self, translations_dir, temp_dir):
171+ def __init__(self, pot_file):
172 self.default_args = [
173 '-f', 'text',
174 '-o', 'markdown',
175 '-M', 'utf-8',
176 ]
177- self.translations_dir = translations_dir
178- self.temp_dir = temp_dir
179+ self.pot_file = pot_file
180
181- def run(self, po4a_command, additional_args, with_output=False,
182- working_dir=None):
183- if not working_dir:
184- working_dir = self.temp_dir
185- pwd = os.getcwd()
186+ def run(self, po4a_command, additional_args, with_output=False):
187 args = copy.copy(self.default_args)
188 args += additional_args
189- os.chdir(working_dir)
190 if with_output:
191 ret = subprocess.Popen([po4a_command]+args, stdout=subprocess.PIPE)
192 else:
193 ret = subprocess.call([po4a_command]+args)
194- os.chdir(pwd)
195 return ret
196
197 def gettextize(self, documents):
198@@ -51,7 +42,7 @@
199 for document in documents:
200 args += ['-m', document]
201 args += [
202- '-p', os.path.join(self.translations_dir, 'help.pot'),
203+ '-p', self.pot_file,
204 '-L', 'utf-8',
205 ]
206 return self.run('po4a-gettextize', args)
207@@ -74,29 +65,80 @@
208 '-p', lang.file_name,
209 '-L', 'utf-8',
210 ]
211- return self.run('po4a-translate', args, with_output=True,
212- working_dir=os.path.join(PATH, '..'))
213+ return self.run('po4a-translate', args, with_output=True)
214+
215+
216+class Documents(object):
217+ def __init__(self):
218+ self.docs = []
219+ for dirpath, dirnames, filenames in os.walk(PATH):
220+ for filename in filenames:
221+ self.docs += [os.path.join(dirpath, filename)]
222+
223+ def translated_doc(self, file_name, lang):
224+ match = [doc for doc in self.docs
225+ if os.path.basename(doc) == os.path.basename(file_name)]
226+ if not match:
227+ return None
228+ return '%s.%s.md' % (match[0].split('.md')[0],
229+ lang.bcp47_code)
230+
231+ def _call_po4a_translate(self, doc, lang, po4a):
232+ res = po4a.translate(doc, lang)
233+ output = codecs.decode(res.communicate()[0])
234+ broken_title_line = [line for line in output.split('\n')
235+ if line.lower().startswith('title:')][0]
236+ rest = [line for line in output.split('\n')
237+ if not line.lower().startswith('title')]
238+ output = '\n'.join(rest)
239+ return (broken_title_line, output)
240+
241+ def write_translated_markdown(self, lang, po4a):
242+ for doc in self.docs:
243+ (broken_title_line, output) = \
244+ self._call_po4a_translate(doc, lang, po4a)
245+ new_path = self.translated_doc(doc, lang)
246+ text = "%s\nDate:\n\n" % (broken_title_line)
247+ text += output
248+ if os.path.exists(new_path):
249+ os.remove(new_path)
250+ if not os.path.exists(os.path.dirname(new_path)):
251+ os.makedirs(os.path.dirname(new_path))
252+ with open(new_path, 'w', encoding='utf-8') as f:
253+ f.write(text)
254
255
256 class Language(object):
257- def __init__(self, po_file):
258+ def __init__(self, po_file, documents):
259 self.file_name = po_file
260- self.gettext_lang_code = os.path.basename(po_file).split('.po')[0]
261+ self.gettext_code = os.path.basename(po_file).split('.po')[0]
262+ self.bcp47_code = self._find_bcp47_code()
263+ self.documents = documents
264+
265+ def _find_bcp47_code(self):
266+ if self.gettext_code not in [c[0] for c in BCP47_OVERRIDES]:
267+ return self.gettext_code.lower().replace('_', '-')
268+ return [c[1] for c in BCP47_OVERRIDES
269+ if c[0] == self.gettext_code][0]
270
271 def rewrite_links(self):
272 po_file = polib.pofile(self.file_name)
273- for entry_group in [po_file.untranslated_entries(),
274- po_file.translated_entries(),
275- po_file.fuzzy_entries()]:
276+ link_regex = r'\[.+?\]\(\{filename\}(.+?)\)'
277+ for entry_group in [po_file.translated_entries(),
278+ po_file.fuzzy_entries(),
279+ po_file.untranslated_entries()]:
280 for entry in entry_group:
281 if '{filename}' in entry.msgid:
282- if not entry.msgstr:
283+ link_msgid = re.findall(link_regex, entry.msgid)[0]
284+ link_msgstr = list(re.findall(link_regex, entry.msgstr))
285+ translated_doc = os.path.basename(
286+ self.documents.translated_doc(
287+ link_msgid, self))
288+ if not link_msgstr:
289 entry.msgstr = entry.msgid
290- link = re.findall(r'\[.+?\]\(\{filename\}(.+?)\)',
291- entry.msgid)[0]
292- entry.msgstr = entry.msgstr.replace(
293- link,
294- 'lang-%s-%s' % (self.gettext_lang_code, link))
295+ link_msgstr = [link_msgid]
296+ entry.msgstr = entry.msgstr.replace(link_msgstr[0],
297+ translated_doc)
298 po_file.save(self.file_name)
299
300
301@@ -105,21 +147,19 @@
302 self._cleanup()
303 self.translations_dir = os.path.abspath(os.path.join(PATH, '../po'))
304 self.available_languages = []
305+ self.documents = Documents()
306 for po_filename in glob.glob(self.translations_dir+'/*.po'):
307- self.available_languages += [Language(po_filename)]
308- self.documents = self._find_documents()
309- self.temp_dir = tempfile.mkdtemp()
310- self.po4a = PO4A(self.translations_dir, self.temp_dir)
311+ self.available_languages += [Language(po_filename, self.documents)]
312+ self.fake_lang_code = 'en_US'
313+ self.fake_po_file = os.path.join(self.translations_dir,
314+ '%s.po' % self.fake_lang_code)
315+ self.pot_file = os.path.join(self.translations_dir,
316+ "help.pot")
317+ self.po4a = PO4A(self.pot_file)
318
319 def __del__(self):
320- shutil.rmtree(self.temp_dir)
321-
322- def _find_documents(self):
323- documents = []
324- for dirpath, dirnames, filenames in os.walk(PATH):
325- for filename in filenames:
326- documents += [os.path.join(dirpath, filename)]
327- return documents
328+ if os.path.exists(self.fake_po_file):
329+ os.remove(self.fake_po_file)
330
331 def _cleanup(self):
332 r = subprocess.Popen(['bzr', 'ignored'], stdout=subprocess.PIPE)
333@@ -133,80 +173,27 @@
334 except NotADirectoryError:
335 os.remove(f)
336
337- def _remove_metadata(self, filename):
338- new_dir = os.path.join(self.temp_dir, os.path.dirname(filename))
339- os.makedirs(new_dir, exist_ok=True)
340- new_filename = os.path.join(new_dir, os.path.basename(filename))
341- shutil.copy(filename, new_filename)
342- lines = open(new_filename).readlines()
343- title_line = [x for x in lines if
344- (x.startswith('Title:') or x.startswith('title:'))]
345- index = 0
346- for line in lines:
347- if not [x for x in METADATA_TAGS
348- if line.lower().startswith(x+':')] and \
349- line.strip() != '':
350- index = lines.index(line)
351- break
352- if not title_line:
353- print('No line starting with "Title: " found in "%s".' %
354- new_filename)
355- return False
356- os.remove(new_filename)
357- with open(new_filename, 'w', encoding='utf-8') as new_file:
358- new_file.write(title_line[0]+'\n')
359- new_file.write(''.join(lines[index:]))
360- return True
361-
362- def clean_documents(self):
363- for document in self.documents:
364- if not self._remove_metadata(document):
365- return False
366- return True
367-
368 def generate_pot_file(self):
369- return self.po4a.gettextize(self.documents)
370+ return self.po4a.gettextize(self.documents.docs)
371
372 def update_po_files(self):
373- return self.po4a.updatepo(self.available_languages, self.documents)
374-
375- def _call_po4a_translate(self, doc, lang):
376- res = self.po4a.translate(doc, lang)
377- output = codecs.decode(res.communicate()[0])
378- broken_title_line = [line for line in output.split('\n')
379- if line.lower().startswith('title:')][0]
380- rest = [line for line in output.split('\n')
381- if not line.lower().startswith('title')]
382- output = '\n'.join(rest)
383- return (broken_title_line, output)
384-
385- def _new_header(self, lang, broken_title_line):
386- title_line = broken_title_line
387- for metadata_tag in [x for x in METADATA_TAGS if not x == 'title']:
388- title_line = title_line.split(metadata_tag)[0]
389- title_line = title_line.split(metadata_tag.capitalize())[0]
390- title_line = title_line.split(metadata_tag.upper())[0]
391- return "%s\nLang: %s\nDate:\n\n" % \
392- (title_line, lang.gettext_lang_code)
393+ return self.po4a.updatepo(self.available_languages,
394+ self.documents.docs)
395
396 def rewrite_links(self):
397+ self._generate_fake_pofile()
398 for lang in self.available_languages:
399 lang.rewrite_links()
400
401+ # we generate a fake translation for en-US which is going to be
402+ # the default
403+ def _generate_fake_pofile(self):
404+ if os.path.exists(self.fake_po_file):
405+ os.remove(self.fake_po_file)
406+ shutil.copy(self.pot_file, self.fake_po_file)
407+ self.available_languages += [Language(self.fake_po_file,
408+ self.documents)]
409+
410 def generate_translations(self):
411 for lang in self.available_languages:
412- for doc in self.documents:
413- (broken_title_line, output) = \
414- self._call_po4a_translate(doc, lang)
415- new_path = os.path.join(PATH, 'pages',
416- 'lang-%s-%s' %
417- (lang.gettext_lang_code,
418- os.path.basename(doc)))
419- text = self._new_header(lang, broken_title_line)
420- text += output
421- if os.path.exists(new_path):
422- os.remove(new_path)
423- if not os.path.exists(os.path.dirname(new_path)):
424- os.makedirs(os.path.dirname(new_path))
425- with open(new_path, 'w', encoding='utf-8') as f:
426- f.write(text)
427+ self.documents.write_translated_markdown(lang, self.po4a)

Subscribers

People subscribed via source and target branches