Merge lp:~dholbach/help-app/1429896 into lp:~ubuntu-touch-coreapps-drivers/help-app/trunk
- 1429896
- Merge into trunk
Status: | Merged | ||||
---|---|---|---|---|---|
Merged at revision: | 115 | ||||
Proposed branch: | lp:~dholbach/help-app/1429896 | ||||
Merge into: | lp:~ubuntu-touch-coreapps-drivers/help-app/trunk | ||||
Diff against target: |
2553 lines (+697/-877) 18 files modified
.bzrignore (+5/-6) HACKING (+26/-24) Makefile (+54/-106) debian/help-app-web.docs (+1/-1) edit-here/develop_server.sh (+0/-103) edit-here/fabfile.py (+0/-73) edit-here/publishconf.py (+0/-24) internals/generate-pot (+1/-1) internals/generate-translations (+1/-1) internals/pelicanconf.py (+9/-7) internals/run-tests (+4/-1) internals/tests/test_files.py (+8/-3) internals/tests/test_links.py (+6/-2) internals/tests/test_translations.py (+2/-2) internals/translations/build.py (+304/-0) internals/translations/po4a.py (+62/-0) internals/translations/utils.py (+19/-326) po/help.pot (+195/-197) |
||||
To merge this branch: | bzr merge lp:~dholbach/help-app/1429896 | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Nicholas Skaggs (community) | Approve | ||
Review via email:
|
Commit message
.
├── content
│ ├── images
│ └── pages
├── debian
│ └── source
├── internals
│ ├── tests
│ └── translations
├── po
└── static
├── app
└── themes
├── app
│ ├── static
│ │ ├── css
│ │ └── js
│ └── templates
└── web
├── static
│ ├── css
│ ├── img
│ └── js
│ └── plugins
└── templates
Description of the change
- 119. By Daniel Holbach
-
remove some superfluous code
- 120. By Daniel Holbach
-
break po4a bits out into separate module
- 121. By Daniel Holbach
-
update HACKING doc
- 122. By Daniel Holbach
-
update packaging to use proper build path
- 123. By Daniel Holbach
-
fix cleanup
- 124. By Daniel Holbach
-
requiring the 'clean' target to be run before is unnecessary - it's done as part of instantiating Translations anyway

Daniel Holbach (dholbach) wrote : | # |
If you could let me know how to reproduce this, that'd be great. :)

Nicholas Skaggs (nskaggs) wrote : | # |
Apart from it not building on my box, I'm happy with this merge. Now to solve that pesky not building thing . . .
- 125. By Daniel Holbach
-
normalise path of fake po file

Nicholas Skaggs (nskaggs) wrote : | # |
Yay! This looks wonderful.
Preview Diff
1 | === modified file '.bzrignore' | |||
2 | --- .bzrignore 2015-03-18 06:57:45 +0000 | |||
3 | +++ .bzrignore 2015-03-19 17:24:01 +0000 | |||
4 | @@ -1,8 +1,7 @@ | |||
6 | 1 | app/www | 1 | build/ |
7 | 2 | *.click | 2 | *.click |
8 | 3 | *.user | 3 | *.user |
14 | 4 | edit-here/cache | 4 | cache |
15 | 5 | edit-here/backup | 5 | backup |
16 | 6 | edit-here/po/en_US.po | 6 | po/en_US.po |
17 | 7 | edit-here/content/pages/*.*.md | 7 | content/pages/*.*.md |
13 | 8 | web/ | ||
18 | 9 | 8 | ||
19 | === modified file 'HACKING' | |||
20 | --- HACKING 2015-03-18 10:37:42 +0000 | |||
21 | +++ HACKING 2015-03-19 17:24:01 +0000 | |||
22 | @@ -50,22 +50,24 @@ | |||
23 | 50 | Directory structure | 50 | Directory structure |
24 | 51 | ------------------- | 51 | ------------------- |
25 | 52 | 52 | ||
42 | 53 | 53 | . | |
43 | 54 | ├── app ← Everything related to the devices app. | 54 | ├── content ← Here is the place to edit the content. |
44 | 55 | │ └── www ← The viewable content of the app. | 55 | │ ├── images ← Images to be used in the docs. |
45 | 56 | ├── edit-here | 56 | │ └── pages ← Actual text to be edited. |
46 | 57 | │ ├── content | 57 | ├── debian ← Everything related to .deb packaging. |
47 | 58 | │ │ └── pages ← Here is the place to edit the content. | 58 | ├── internals ← Build tools, pelican config. |
48 | 59 | │ ├── po ← Translations. | 59 | │ ├── tests ← Code for automated testing. |
49 | 60 | │ ├── tests ← Code for automated testing goes here. | 60 | │ └── translations ← Python code for handling translations. |
50 | 61 | │ └── themes ← Themes files, both templates and css/js. | 61 | ├── po ← Translations (.pot and po files). |
51 | 62 | │ ├── phone ← Phone/device app theme. | 62 | └── static |
52 | 63 | │ └── web ← Online build (.ubuntu.com) theme. | 63 | ├── app ← Static files for the click app. |
53 | 64 | └── web ← The viewable content of of the online build. | 64 | └── themes ← Themes files, both templates and css/js. |
54 | 65 | 65 | ├── app | |
55 | 66 | Note: Everything in ./app/www and ./web/ is automatically generated. | 66 | └── web |
56 | 67 | Edit content in ./edit-here/content/pages/ and edit themes in | 67 | |
57 | 68 | ./edit-here/themes/. | 68 | Note: Everything in .build is automatically generated. |
58 | 69 | Edit content in ./content/pages/ and edit themes in | ||
59 | 70 | ./static/themes/. | ||
60 | 69 | 71 | ||
61 | 70 | 72 | ||
62 | 71 | Prerequisites | 73 | Prerequisites |
63 | @@ -96,7 +98,7 @@ | |||
64 | 96 | This app is structured in a very simple way. All the content is and all your | 98 | This app is structured in a very simple way. All the content is and all your |
65 | 97 | edits happen in | 99 | edits happen in |
66 | 98 | 100 | ||
68 | 99 | ./edit-here/content/pages/ | 101 | ./content/pages/ |
69 | 100 | 102 | ||
70 | 101 | The markup for the text is Markdown, which is very easy to learn. Just have | 103 | The markup for the text is Markdown, which is very easy to learn. Just have |
71 | 102 | a look at http://daringfireball.net/projects/markdown/ and some of the | 104 | a look at http://daringfireball.net/projects/markdown/ and some of the |
72 | @@ -104,18 +106,18 @@ | |||
73 | 104 | 106 | ||
74 | 105 | Once you're happy with your edits, change to the top-level directory and run | 107 | Once you're happy with your edits, change to the top-level directory and run |
75 | 106 | 108 | ||
77 | 107 | make html | 109 | make app |
78 | 108 | 110 | ||
79 | 109 | This will also generate translated pages. | 111 | This will also generate translated pages. |
80 | 110 | 112 | ||
82 | 111 | You can find the updated HTML in ./app/www/. | 113 | You can find the updated HTML in ./build/app/www/. |
83 | 112 | 114 | ||
84 | 113 | To launch the app, you can use ubuntu-html5-app-launcher in the www dir, or | 115 | To launch the app, you can use ubuntu-html5-app-launcher in the www dir, or |
85 | 114 | just call | 116 | just call |
86 | 115 | 117 | ||
87 | 116 | make launch | 118 | make launch |
88 | 117 | 119 | ||
90 | 118 | (This will also run the 'make html' command for you.) | 120 | (This will also run the 'make app' command for you.) |
91 | 119 | 121 | ||
92 | 120 | 122 | ||
93 | 121 | Creating a click | 123 | Creating a click |
94 | @@ -137,7 +139,7 @@ | |||
95 | 137 | 139 | ||
96 | 138 | make web | 140 | make web |
97 | 139 | 141 | ||
99 | 140 | Find the resulting files in ./web in the top-level directory. | 142 | Find the resulting files in the ./build/web directory. |
100 | 141 | 143 | ||
101 | 142 | We plan to make use of | 144 | We plan to make use of |
102 | 143 | http://www.w3.org/International/questions/qa-apache-lang-neg for publishing | 145 | http://www.w3.org/International/questions/qa-apache-lang-neg for publishing |
103 | @@ -148,7 +150,7 @@ | |||
104 | 148 | 150 | ||
105 | 149 | We love automated testing! We added a couple of test cases to | 151 | We love automated testing! We added a couple of test cases to |
106 | 150 | 152 | ||
108 | 151 | ./edit-here/tests/ | 153 | ./internals/tests/ |
109 | 152 | 154 | ||
110 | 153 | and it would be great if you added more. We want to run these tests | 155 | and it would be great if you added more. We want to run these tests |
111 | 154 | whenever we build the package or publish the docs or whatever else. Just | 156 | whenever we build the package or publish the docs or whatever else. Just |
112 | @@ -162,7 +164,7 @@ | |||
113 | 162 | While you can just generate and install the click package manually, | 164 | While you can just generate and install the click package manually, |
114 | 163 | we recommend the SDK for testing the phone app. | 165 | we recommend the SDK for testing the phone app. |
115 | 164 | 166 | ||
117 | 165 | You can run the `make html` target to generate the HTML files and then | 167 | You can run the `make appl` target to generate the HTML files and then |
118 | 166 | open the app/help.ubuntuhtmlproject file with the Ubuntu SDK IDE. | 168 | open the app/help.ubuntuhtmlproject file with the Ubuntu SDK IDE. |
119 | 167 | 169 | ||
120 | 168 | From there, you can set up a desktop kit to run it on your host, | 170 | From there, you can set up a desktop kit to run it on your host, |
121 | @@ -177,7 +179,7 @@ | |||
122 | 177 | 179 | ||
123 | 178 | https://launchpad.net/help-app | 180 | https://launchpad.net/help-app |
124 | 179 | 181 | ||
126 | 180 | To update the .pot file, simply run the following command in the edit-here/ | 182 | To update the .pot file, simply run the following command in the top-level |
127 | 181 | directory: | 183 | directory: |
128 | 182 | 184 | ||
129 | 183 | make update-pot | 185 | make update-pot |
130 | 184 | 186 | ||
131 | === removed file 'Makefile' | |||
132 | --- Makefile 2015-03-18 10:36:10 +0000 | |||
133 | +++ Makefile 1970-01-01 00:00:00 +0000 | |||
134 | @@ -1,28 +0,0 @@ | |||
135 | 1 | #!/usr/bin/make -f | ||
136 | 2 | |||
137 | 3 | ignored := $(shell bzr ignored | cut -d' ' -f1) | ||
138 | 4 | |||
139 | 5 | clean: | ||
140 | 6 | ifneq ($(strip $(ignored)),) | ||
141 | 7 | $(foreach fn, $(ignored), $(shell rm -r $(fn);)) | ||
142 | 8 | endif | ||
143 | 9 | |||
144 | 10 | check: clean | ||
145 | 11 | make -C edit-here check | ||
146 | 12 | |||
147 | 13 | click: html | ||
148 | 14 | cd app && click build . && mv *.click .. | ||
149 | 15 | |||
150 | 16 | html: clean | ||
151 | 17 | make -C edit-here html | ||
152 | 18 | |||
153 | 19 | web: clean | ||
154 | 20 | make -C edit-here web | ||
155 | 21 | |||
156 | 22 | update-pot: | ||
157 | 23 | cd edit-here && ./generate-pot | ||
158 | 24 | |||
159 | 25 | launch: | ||
160 | 26 | make -C edit-here launch | ||
161 | 27 | |||
162 | 28 | .PHONY: click html web update-pot clean check | ||
163 | 29 | 0 | ||
164 | === renamed file 'edit-here/Makefile' => 'Makefile' | |||
165 | --- edit-here/Makefile 2015-03-18 10:36:10 +0000 | |||
166 | +++ Makefile 2015-03-19 17:24:01 +0000 | |||
167 | @@ -4,126 +4,74 @@ | |||
168 | 4 | 4 | ||
169 | 5 | BASEDIR=$(CURDIR) | 5 | BASEDIR=$(CURDIR) |
170 | 6 | INPUTDIR=$(BASEDIR)/content | 6 | INPUTDIR=$(BASEDIR)/content |
196 | 7 | OUTPUTDIR=$(BASEDIR)/../app/www | 7 | INTERNALS_DIR=$(BASEDIR)/internals |
197 | 8 | CONFFILE=$(BASEDIR)/pelicanconf.py | 8 | PO_DIR=$(BASEDIR)/po |
198 | 9 | PUBLISHCONF=$(BASEDIR)/publishconf.py | 9 | STATIC_DIR=$(BASEDIR)/static |
199 | 10 | 10 | ||
200 | 11 | OUTPUTDIR_WEB=$(BASEDIR)/../web | 11 | CONFFILE=$(INTERNALS_DIR)/pelicanconf.py |
201 | 12 | THEMEDIR_WEB=$(BASEDIR)/themes/web | 12 | |
202 | 13 | 13 | BUILD_DIR=$(BASEDIR)/build | |
203 | 14 | FTP_HOST=localhost | 14 | APP_DIR=$(BUILD_DIR)/app |
204 | 15 | FTP_USER=anonymous | 15 | OUTPUTDIR_APP=$(APP_DIR)/www |
205 | 16 | FTP_TARGET_DIR=/ | 16 | |
206 | 17 | 17 | OUTPUTDIR_WEB=$(BUILD_DIR)/web/www | |
207 | 18 | SSH_HOST=localhost | 18 | THEMEDIR_WEB=$(STATIC_DIR)/themes/web |
183 | 19 | SSH_PORT=22 | ||
184 | 20 | SSH_USER=root | ||
185 | 21 | SSH_TARGET_DIR=/var/www | ||
186 | 22 | |||
187 | 23 | S3_BUCKET=my_s3_bucket | ||
188 | 24 | |||
189 | 25 | CLOUDFILES_USERNAME=my_rackspace_username | ||
190 | 26 | CLOUDFILES_API_KEY=my_rackspace_api_key | ||
191 | 27 | CLOUDFILES_CONTAINER=my_cloudfiles_container | ||
192 | 28 | |||
193 | 29 | DROPBOX_DIR=~/Dropbox/Public/ | ||
194 | 30 | |||
195 | 31 | GITHUB_PAGES_BRANCH=gh-pages | ||
208 | 32 | 19 | ||
209 | 33 | DEBUG ?= 0 | 20 | DEBUG ?= 0 |
210 | 34 | ifeq ($(DEBUG), 1) | 21 | ifeq ($(DEBUG), 1) |
211 | 35 | PELICANOPTS += -D | 22 | PELICANOPTS += -D |
212 | 36 | endif | 23 | endif |
213 | 37 | 24 | ||
216 | 38 | MD_FILES=$(wildcard content/pages/*.md) | 25 | MD_FILES=$(wildcard $(INPUTDIR)/pages/*.md) |
217 | 39 | PO_FILES=$(wildcard po/*.po) | 26 | PO_FILES=$(wildcard $(PO_DIR)/*.po) |
218 | 27 | |||
219 | 28 | ignored := $(shell bzr ignored | cut -d' ' -f1) | ||
220 | 29 | |||
221 | 30 | clean: | ||
222 | 31 | ifneq ($(strip $(ignored)),) | ||
223 | 32 | $(foreach fn, $(ignored), $(shell rm -r $(fn);)) | ||
224 | 33 | endif | ||
225 | 40 | 34 | ||
226 | 41 | help: | 35 | help: |
227 | 42 | @echo 'Makefile for a pelican Web site ' | 36 | @echo 'Makefile for a pelican Web site ' |
228 | 43 | @echo ' ' | 37 | @echo ' ' |
229 | 44 | @echo 'Usage: ' | 38 | @echo 'Usage: ' |
231 | 45 | @echo ' make html (re)generate the web site ' | 39 | @echo ' make web (re)generate the (online) web site ' |
232 | 40 | @echo ' make app (re)generate the (offline) content ' | ||
233 | 41 | @echo ' for the phone app ("html" is an ' | ||
234 | 42 | @echo ' alias) ' | ||
235 | 43 | @echo ' make click generate click for the phone app ' | ||
236 | 44 | @echo ' make launch launch the phone app ' | ||
237 | 45 | @echo ' make translations generate translated markdown ' | ||
238 | 46 | @echo ' make update-pot update the .pot file ' | ||
239 | 46 | @echo ' make clean remove the generated files ' | 47 | @echo ' make clean remove the generated files ' |
240 | 47 | @echo ' make regenerate regenerate files upon modification ' | ||
241 | 48 | @echo ' make publish generate using production settings ' | ||
242 | 49 | @echo ' make serve [PORT=8000] serve site at http://localhost:8000' | ||
243 | 50 | @echo ' make devserver [PORT=8000] start/restart develop_server.sh ' | ||
244 | 51 | @echo ' make stopserver stop local server ' | ||
245 | 52 | @echo ' make ssh_upload upload the web site via SSH ' | ||
246 | 53 | @echo ' make rsync_upload upload the web site via rsync+ssh ' | ||
247 | 54 | @echo ' make dropbox_upload upload the web site via Dropbox ' | ||
248 | 55 | @echo ' make ftp_upload upload the web site via FTP ' | ||
249 | 56 | @echo ' make s3_upload upload the web site via S3 ' | ||
250 | 57 | @echo ' make cf_upload upload the web site via Cloud Files' | ||
251 | 58 | @echo ' make github upload the web site via gh-pages ' | ||
252 | 59 | @echo ' ' | 48 | @echo ' ' |
253 | 60 | @echo 'Set the DEBUG variable to 1 to enable debugging, e.g. make DEBUG=1 html' | 49 | @echo 'Set the DEBUG variable to 1 to enable debugging, e.g. make DEBUG=1 html' |
254 | 61 | @echo ' ' | 50 | @echo ' ' |
255 | 62 | 51 | ||
256 | 63 | check: | 52 | check: |
323 | 64 | ./run-tests | 53 | cd $(INTERNALS_DIR); ./run-tests |
324 | 65 | 54 | ||
325 | 66 | web: | 55 | translations: |
326 | 67 | ./generate-translations | 56 | cd $(INTERNALS_DIR); ./generate-translations |
327 | 68 | $(PELICAN) $(INPUTDIR) -o $(OUTPUTDIR_WEB) -s $(CONFFILE) $(PELICANOPTS) -t $(THEMEDIR_WEB) | 57 | |
328 | 69 | 58 | web: translations | |
329 | 70 | html: | 59 | cd $(INTERNALS_DIR); $(PELICAN) $(INPUTDIR) -o $(OUTPUTDIR_WEB) -s $(CONFFILE) $(PELICANOPTS) -t $(THEMEDIR_WEB) |
330 | 71 | ./generate-translations | 60 | |
331 | 72 | $(PELICAN) $(INPUTDIR) -o $(OUTPUTDIR) -s $(CONFFILE) $(PELICANOPTS) | 61 | app: translations |
332 | 73 | cp index.html $(OUTPUTDIR) | 62 | cd $(INTERNALS_DIR); $(PELICAN) $(INPUTDIR) -o $(OUTPUTDIR_APP) -s $(CONFFILE) $(PELICANOPTS) |
333 | 74 | 63 | cp $(STATIC_DIR)/index.html $(OUTPUTDIR_APP) | |
334 | 75 | clean: | 64 | cp $(STATIC_DIR)/app/* $(APP_DIR) |
335 | 76 | [ ! -d $(OUTPUTDIR) ] || rm -rf $(OUTPUTDIR) | 65 | |
336 | 77 | 66 | html: app | |
337 | 78 | launch: html | 67 | |
338 | 79 | cd ../app/; `grep '^Exec' help.desktop | tail -1 | sed 's/^Exec=//' | sed 's/%.//'` & | 68 | click: app |
339 | 80 | 69 | cd $(APP_DIR) && click build . && mv *.click $(BASEDIR) | |
340 | 81 | #regenerate: | 70 | |
341 | 82 | # $(PELICAN) -r $(INPUTDIR) -o $(OUTPUTDIR) -s $(CONFFILE) $(PELICANOPTS) | 71 | launch: app |
342 | 83 | # | 72 | cd $(APP_DIR); `grep '^Exec' help.desktop | tail -1 | sed 's/^Exec=//' | sed 's/%.//'` & |
343 | 84 | #serve: | 73 | |
344 | 85 | #ifdef PORT | 74 | update-pot: |
345 | 86 | # cd $(OUTPUTDIR) && $(PY) -m pelican.server $(PORT) | 75 | cd $(INTERNALS_DIR) && ./generate-pot |
346 | 87 | #else | 76 | |
347 | 88 | # cd $(OUTPUTDIR) && $(PY) -m pelican.server | 77 | .PHONY: html help clean web check launch click app update-pot translations |
282 | 89 | #endif | ||
283 | 90 | # | ||
284 | 91 | #devserver: | ||
285 | 92 | #ifdef PORT | ||
286 | 93 | # $(BASEDIR)/develop_server.sh restart $(PORT) | ||
287 | 94 | #else | ||
288 | 95 | # $(BASEDIR)/develop_server.sh restart | ||
289 | 96 | #endif | ||
290 | 97 | # | ||
291 | 98 | #stopserver: | ||
292 | 99 | # kill -9 `cat pelican.pid` | ||
293 | 100 | # kill -9 `cat srv.pid` | ||
294 | 101 | # @echo 'Stopped Pelican and SimpleHTTPServer processes running in background.' | ||
295 | 102 | # | ||
296 | 103 | #publish: | ||
297 | 104 | # $(PELICAN) $(INPUTDIR) -o $(OUTPUTDIR) -s $(PUBLISHCONF) $(PELICANOPTS) | ||
298 | 105 | # | ||
299 | 106 | #ssh_upload: publish | ||
300 | 107 | # scp -P $(SSH_PORT) -r $(OUTPUTDIR)/* $(SSH_USER)@$(SSH_HOST):$(SSH_TARGET_DIR) | ||
301 | 108 | # | ||
302 | 109 | #rsync_upload: publish | ||
303 | 110 | # rsync -e "ssh -p $(SSH_PORT)" -P -rvzc --delete $(OUTPUTDIR)/ $(SSH_USER)@$(SSH_HOST):$(SSH_TARGET_DIR) --cvs-exclude | ||
304 | 111 | # | ||
305 | 112 | #dropbox_upload: publish | ||
306 | 113 | # cp -r $(OUTPUTDIR)/* $(DROPBOX_DIR) | ||
307 | 114 | # | ||
308 | 115 | #ftp_upload: publish | ||
309 | 116 | # lftp ftp://$(FTP_USER)@$(FTP_HOST) -e "mirror -R $(OUTPUTDIR) $(FTP_TARGET_DIR) ; quit" | ||
310 | 117 | # | ||
311 | 118 | #s3_upload: publish | ||
312 | 119 | # s3cmd sync $(OUTPUTDIR)/ s3://$(S3_BUCKET) --acl-public --delete-removed --guess-mime-type | ||
313 | 120 | # | ||
314 | 121 | #cf_upload: publish | ||
315 | 122 | # cd $(OUTPUTDIR) && swift -v -A https://auth.api.rackspacecloud.com/v1.0 -U $(CLOUDFILES_USERNAME) -K $(CLOUDFILES_API_KEY) upload -c $(CLOUDFILES_CONTAINER) . | ||
316 | 123 | # | ||
317 | 124 | #github: publish | ||
318 | 125 | # ghp-import -m "Generate Pelican site" -b $(GITHUB_PAGES_BRANCH) $(OUTPUTDIR) | ||
319 | 126 | # git push origin $(GITHUB_PAGES_BRANCH) | ||
320 | 127 | # | ||
321 | 128 | .PHONY: html help clean web check | ||
322 | 129 | #regenerate serve devserver publish ssh_upload rsync_upload dropbox_upload ftp_upload s3_upload cf_upload github | ||
348 | 130 | 78 | ||
349 | === renamed directory 'edit-here/content' => 'content' | |||
350 | === modified file 'debian/help-app-web.docs' | |||
351 | --- debian/help-app-web.docs 2015-03-09 15:47:10 +0000 | |||
352 | +++ debian/help-app-web.docs 2015-03-19 17:24:01 +0000 | |||
353 | @@ -1,1 +1,1 @@ | |||
355 | 1 | web/* | 1 | build/web/www/* |
356 | 2 | 2 | ||
357 | === removed directory 'edit-here' | |||
358 | === removed file 'edit-here/develop_server.sh' | |||
359 | --- edit-here/develop_server.sh 2015-02-09 13:40:52 +0000 | |||
360 | +++ edit-here/develop_server.sh 1970-01-01 00:00:00 +0000 | |||
361 | @@ -1,103 +0,0 @@ | |||
362 | 1 | #!/usr/bin/env bash | ||
363 | 2 | ## | ||
364 | 3 | # This section should match your Makefile | ||
365 | 4 | ## | ||
366 | 5 | PY=${PY:-python} | ||
367 | 6 | PELICAN=${PELICAN:-pelican} | ||
368 | 7 | PELICANOPTS= | ||
369 | 8 | |||
370 | 9 | BASEDIR=$(pwd) | ||
371 | 10 | INPUTDIR=$BASEDIR/content | ||
372 | 11 | OUTPUTDIR=$(BASEDIR)/../app/www | ||
373 | 12 | CONFFILE=$BASEDIR/pelicanconf.py | ||
374 | 13 | |||
375 | 14 | ### | ||
376 | 15 | # Don't change stuff below here unless you are sure | ||
377 | 16 | ### | ||
378 | 17 | |||
379 | 18 | SRV_PID=$BASEDIR/srv.pid | ||
380 | 19 | PELICAN_PID=$BASEDIR/pelican.pid | ||
381 | 20 | |||
382 | 21 | function usage(){ | ||
383 | 22 | echo "usage: $0 (stop) (start) (restart) [port]" | ||
384 | 23 | echo "This starts Pelican in debug and reload mode and then launches" | ||
385 | 24 | echo "an HTTP server to help site development. It doesn't read" | ||
386 | 25 | echo "your Pelican settings, so if you edit any paths in your Makefile" | ||
387 | 26 | echo "you will need to edit your settings as well." | ||
388 | 27 | exit 3 | ||
389 | 28 | } | ||
390 | 29 | |||
391 | 30 | function alive() { | ||
392 | 31 | kill -0 $1 >/dev/null 2>&1 | ||
393 | 32 | } | ||
394 | 33 | |||
395 | 34 | function shut_down(){ | ||
396 | 35 | PID=$(cat $SRV_PID) | ||
397 | 36 | if [[ $? -eq 0 ]]; then | ||
398 | 37 | if alive $PID; then | ||
399 | 38 | echo "Stopping HTTP server" | ||
400 | 39 | kill $PID | ||
401 | 40 | else | ||
402 | 41 | echo "Stale PID, deleting" | ||
403 | 42 | fi | ||
404 | 43 | rm $SRV_PID | ||
405 | 44 | else | ||
406 | 45 | echo "HTTP server PIDFile not found" | ||
407 | 46 | fi | ||
408 | 47 | |||
409 | 48 | PID=$(cat $PELICAN_PID) | ||
410 | 49 | if [[ $? -eq 0 ]]; then | ||
411 | 50 | if alive $PID; then | ||
412 | 51 | echo "Killing Pelican" | ||
413 | 52 | kill $PID | ||
414 | 53 | else | ||
415 | 54 | echo "Stale PID, deleting" | ||
416 | 55 | fi | ||
417 | 56 | rm $PELICAN_PID | ||
418 | 57 | else | ||
419 | 58 | echo "Pelican PIDFile not found" | ||
420 | 59 | fi | ||
421 | 60 | } | ||
422 | 61 | |||
423 | 62 | function start_up(){ | ||
424 | 63 | local port=$1 | ||
425 | 64 | echo "Starting up Pelican and HTTP server" | ||
426 | 65 | shift | ||
427 | 66 | $PELICAN --debug --autoreload -r $INPUTDIR -o $OUTPUTDIR -s $CONFFILE $PELICANOPTS & | ||
428 | 67 | pelican_pid=$! | ||
429 | 68 | echo $pelican_pid > $PELICAN_PID | ||
430 | 69 | cd $OUTPUTDIR | ||
431 | 70 | $PY -m pelican.server $port & | ||
432 | 71 | srv_pid=$! | ||
433 | 72 | echo $srv_pid > $SRV_PID | ||
434 | 73 | cd $BASEDIR | ||
435 | 74 | sleep 1 | ||
436 | 75 | if ! alive $pelican_pid ; then | ||
437 | 76 | echo "Pelican didn't start. Is the Pelican package installed?" | ||
438 | 77 | return 1 | ||
439 | 78 | elif ! alive $srv_pid ; then | ||
440 | 79 | echo "The HTTP server didn't start. Is there another service using port" $port "?" | ||
441 | 80 | return 1 | ||
442 | 81 | fi | ||
443 | 82 | echo 'Pelican and HTTP server processes now running in background.' | ||
444 | 83 | } | ||
445 | 84 | |||
446 | 85 | ### | ||
447 | 86 | # MAIN | ||
448 | 87 | ### | ||
449 | 88 | [[ ($# -eq 0) || ($# -gt 2) ]] && usage | ||
450 | 89 | port='' | ||
451 | 90 | [[ $# -eq 2 ]] && port=$2 | ||
452 | 91 | |||
453 | 92 | if [[ $1 == "stop" ]]; then | ||
454 | 93 | shut_down | ||
455 | 94 | elif [[ $1 == "restart" ]]; then | ||
456 | 95 | shut_down | ||
457 | 96 | start_up $port | ||
458 | 97 | elif [[ $1 == "start" ]]; then | ||
459 | 98 | if ! start_up $port; then | ||
460 | 99 | shut_down | ||
461 | 100 | fi | ||
462 | 101 | else | ||
463 | 102 | usage | ||
464 | 103 | fi | ||
465 | 104 | 0 | ||
466 | === removed file 'edit-here/fabfile.py' | |||
467 | --- edit-here/fabfile.py 2015-02-09 13:40:52 +0000 | |||
468 | +++ edit-here/fabfile.py 1970-01-01 00:00:00 +0000 | |||
469 | @@ -1,73 +0,0 @@ | |||
470 | 1 | from fabric.api import * | ||
471 | 2 | import fabric.contrib.project as project | ||
472 | 3 | import os | ||
473 | 4 | import sys | ||
474 | 5 | import SimpleHTTPServer | ||
475 | 6 | import SocketServer | ||
476 | 7 | |||
477 | 8 | # Local path configuration (can be absolute or relative to fabfile) | ||
478 | 9 | env.deploy_path = '../app/www' | ||
479 | 10 | DEPLOY_PATH = env.deploy_path | ||
480 | 11 | |||
481 | 12 | # Remote server configuration | ||
482 | 13 | production = 'root@localhost:22' | ||
483 | 14 | dest_path = '/var/www' | ||
484 | 15 | |||
485 | 16 | # Rackspace Cloud Files configuration settings | ||
486 | 17 | env.cloudfiles_username = 'my_rackspace_username' | ||
487 | 18 | env.cloudfiles_api_key = 'my_rackspace_api_key' | ||
488 | 19 | env.cloudfiles_container = 'my_cloudfiles_container' | ||
489 | 20 | |||
490 | 21 | |||
491 | 22 | def clean(): | ||
492 | 23 | if os.path.isdir(DEPLOY_PATH): | ||
493 | 24 | local('rm -rf {deploy_path}'.format(**env)) | ||
494 | 25 | local('mkdir {deploy_path}'.format(**env)) | ||
495 | 26 | |||
496 | 27 | def build(): | ||
497 | 28 | local('pelican -s pelicanconf.py') | ||
498 | 29 | |||
499 | 30 | def rebuild(): | ||
500 | 31 | clean() | ||
501 | 32 | build() | ||
502 | 33 | |||
503 | 34 | def regenerate(): | ||
504 | 35 | local('pelican -r -s pelicanconf.py') | ||
505 | 36 | |||
506 | 37 | def serve(): | ||
507 | 38 | os.chdir(env.deploy_path) | ||
508 | 39 | |||
509 | 40 | PORT = 8000 | ||
510 | 41 | class AddressReuseTCPServer(SocketServer.TCPServer): | ||
511 | 42 | allow_reuse_address = True | ||
512 | 43 | |||
513 | 44 | server = AddressReuseTCPServer(('', PORT), SimpleHTTPServer.SimpleHTTPRequestHandler) | ||
514 | 45 | |||
515 | 46 | sys.stderr.write('Serving on port {0} ...\n'.format(PORT)) | ||
516 | 47 | server.serve_forever() | ||
517 | 48 | |||
518 | 49 | def reserve(): | ||
519 | 50 | build() | ||
520 | 51 | serve() | ||
521 | 52 | |||
522 | 53 | def preview(): | ||
523 | 54 | local('pelican -s publishconf.py') | ||
524 | 55 | |||
525 | 56 | def cf_upload(): | ||
526 | 57 | rebuild() | ||
527 | 58 | local('cd {deploy_path} && ' | ||
528 | 59 | 'swift -v -A https://auth.api.rackspacecloud.com/v1.0 ' | ||
529 | 60 | '-U {cloudfiles_username} ' | ||
530 | 61 | '-K {cloudfiles_api_key} ' | ||
531 | 62 | 'upload -c {cloudfiles_container} .'.format(**env)) | ||
532 | 63 | |||
533 | 64 | @hosts(production) | ||
534 | 65 | def publish(): | ||
535 | 66 | local('pelican -s publishconf.py') | ||
536 | 67 | project.rsync_project( | ||
537 | 68 | remote_dir=dest_path, | ||
538 | 69 | exclude=".DS_Store", | ||
539 | 70 | local_dir=DEPLOY_PATH.rstrip('/') + '/', | ||
540 | 71 | delete=True, | ||
541 | 72 | extra_opts='-c', | ||
542 | 73 | ) | ||
543 | 74 | 0 | ||
544 | === removed directory 'edit-here/local' | |||
545 | === removed file 'edit-here/local/__init__.py' | |||
546 | === removed file 'edit-here/publishconf.py' | |||
547 | --- edit-here/publishconf.py 2015-02-09 13:40:52 +0000 | |||
548 | +++ edit-here/publishconf.py 1970-01-01 00:00:00 +0000 | |||
549 | @@ -1,24 +0,0 @@ | |||
550 | 1 | #!/usr/bin/env python | ||
551 | 2 | # -*- coding: utf-8 -*- # | ||
552 | 3 | from __future__ import unicode_literals | ||
553 | 4 | |||
554 | 5 | # This file is only used if you use `make publish` or | ||
555 | 6 | # explicitly specify it as your config file. | ||
556 | 7 | |||
557 | 8 | import os | ||
558 | 9 | import sys | ||
559 | 10 | sys.path.append(os.curdir) | ||
560 | 11 | from pelicanconf import * | ||
561 | 12 | |||
562 | 13 | SITEURL = '' | ||
563 | 14 | RELATIVE_URLS = False | ||
564 | 15 | |||
565 | 16 | FEED_ALL_ATOM = 'feeds/all.atom.xml' | ||
566 | 17 | CATEGORY_FEED_ATOM = 'feeds/%s.atom.xml' | ||
567 | 18 | |||
568 | 19 | DELETE_OUTPUT_DIRECTORY = True | ||
569 | 20 | |||
570 | 21 | # Following items are often useful when publishing | ||
571 | 22 | |||
572 | 23 | #DISQUS_SITENAME = "" | ||
573 | 24 | #GOOGLE_ANALYTICS = "" | ||
574 | 25 | 0 | ||
575 | === added directory 'internals' | |||
576 | === renamed file 'edit-here/__init__.py' => 'internals/__init__.py' | |||
577 | === renamed file 'edit-here/generate-pot' => 'internals/generate-pot' | |||
578 | --- edit-here/generate-pot 2015-03-02 10:30:45 +0000 | |||
579 | +++ internals/generate-pot 2015-03-19 17:24:01 +0000 | |||
580 | @@ -5,7 +5,7 @@ | |||
581 | 5 | 5 | ||
582 | 6 | import sys | 6 | import sys |
583 | 7 | 7 | ||
585 | 8 | from translations import Translations | 8 | from translations.build import Translations |
586 | 9 | 9 | ||
587 | 10 | 10 | ||
588 | 11 | def main(): | 11 | def main(): |
589 | 12 | 12 | ||
590 | === renamed file 'edit-here/generate-translations' => 'internals/generate-translations' | |||
591 | --- edit-here/generate-translations 2015-03-02 10:30:45 +0000 | |||
592 | +++ internals/generate-translations 2015-03-19 17:24:01 +0000 | |||
593 | @@ -4,7 +4,7 @@ | |||
594 | 4 | 4 | ||
595 | 5 | import sys | 5 | import sys |
596 | 6 | 6 | ||
598 | 7 | from translations import Translations | 7 | from translations.build import Translations |
599 | 8 | 8 | ||
600 | 9 | 9 | ||
601 | 10 | def main(): | 10 | def main(): |
602 | 11 | 11 | ||
603 | === renamed file 'edit-here/pelicanconf.py' => 'internals/pelicanconf.py' | |||
604 | --- edit-here/pelicanconf.py 2015-03-17 16:40:17 +0000 | |||
605 | +++ internals/pelicanconf.py 2015-03-19 17:24:01 +0000 | |||
606 | @@ -6,7 +6,9 @@ | |||
607 | 6 | SITENAME = u'Ubuntu for devices: Help' | 6 | SITENAME = u'Ubuntu for devices: Help' |
608 | 7 | SITEURL = '' | 7 | SITEURL = '' |
609 | 8 | 8 | ||
611 | 9 | PATH = 'content' | 9 | TOP_LEVEL_DIR = '../' |
612 | 10 | PATH = TOP_LEVEL_DIR+'content/' | ||
613 | 11 | TRANSLATIONS_DIR = TOP_LEVEL_DIR+'po/' | ||
614 | 10 | 12 | ||
615 | 11 | TIMEZONE = 'Europe/Paris' | 13 | TIMEZONE = 'Europe/Paris' |
616 | 12 | 14 | ||
617 | @@ -17,9 +19,9 @@ | |||
618 | 17 | 19 | ||
619 | 18 | SLUGIFY_SOURCE = 'basename' | 20 | SLUGIFY_SOURCE = 'basename' |
620 | 19 | FILENAME_METADATA = '(?P<slug>\S+)\.(?P<lang>\S+)\.*' | 21 | FILENAME_METADATA = '(?P<slug>\S+)\.(?P<lang>\S+)\.*' |
624 | 20 | PAGE_URL = '' | 22 | PAGE_URL = '' |
625 | 21 | PAGE_SAVE_AS = '' | 23 | PAGE_SAVE_AS = '' |
626 | 22 | PAGE_LANG_URL = '{slug}.{lang}.html' | 24 | PAGE_LANG_URL = '{slug}.{lang}.html' |
627 | 23 | PAGE_LANG_SAVE_AS = '{slug}.{lang}.html' | 25 | PAGE_LANG_SAVE_AS = '{slug}.{lang}.html' |
628 | 24 | 26 | ||
629 | 25 | # Feed generation is usually not desired when developing | 27 | # Feed generation is usually not desired when developing |
630 | @@ -38,13 +40,13 @@ | |||
631 | 38 | DEFAULT_PAGINATION = False | 40 | DEFAULT_PAGINATION = False |
632 | 39 | 41 | ||
633 | 40 | # Uncomment following line if you want document-relative URLs when developing | 42 | # Uncomment following line if you want document-relative URLs when developing |
635 | 41 | #RELATIVE_URLS = True | 43 | # RELATIVE_URLS = True |
636 | 42 | 44 | ||
637 | 43 | TAGS_SAVE_AS = '' | 45 | TAGS_SAVE_AS = '' |
638 | 44 | TAG_SAVE_AS = '' | 46 | TAG_SAVE_AS = '' |
640 | 45 | THEME = 'themes/phone' | 47 | THEME = TOP_LEVEL_DIR+'static/themes/app/' |
641 | 46 | 48 | ||
643 | 47 | MD_EXTENSIONS = ['local.q-and-a', 'attr_list', 'toc'] | 49 | MD_EXTENSIONS = ['q-and-a', 'attr_list', 'toc'] |
644 | 48 | 50 | ||
645 | 49 | META_TAGS = [ | 51 | META_TAGS = [ |
646 | 50 | '[TOC]', | 52 | '[TOC]', |
647 | 51 | 53 | ||
648 | === renamed file 'edit-here/local/q-and-a.py' => 'internals/q-and-a.py' | |||
649 | === renamed file 'edit-here/run-tests' => 'internals/run-tests' | |||
650 | --- edit-here/run-tests 2015-03-09 10:16:07 +0000 | |||
651 | +++ internals/run-tests 2015-03-19 17:24:01 +0000 | |||
652 | @@ -1,9 +1,12 @@ | |||
653 | 1 | #!/usr/bin/python3 | 1 | #!/usr/bin/python3 |
654 | 2 | 2 | ||
655 | 3 | import os | ||
656 | 3 | import sys | 4 | import sys |
657 | 4 | import unittest | 5 | import unittest |
658 | 5 | 6 | ||
660 | 6 | test_directory = 'tests/' | 7 | from pelicanconf import TOP_LEVEL_DIR |
661 | 8 | |||
662 | 9 | test_directory = os.path.join(TOP_LEVEL_DIR, 'internals/tests') | ||
663 | 7 | test_filename = 'test_*' | 10 | test_filename = 'test_*' |
664 | 8 | if len(sys.argv) > 1: | 11 | if len(sys.argv) > 1: |
665 | 9 | test_filename = sys.argv[1] | 12 | test_filename = sys.argv[1] |
666 | 10 | 13 | ||
667 | === renamed directory 'edit-here/tests' => 'internals/tests' | |||
668 | === modified file 'internals/tests/test_files.py' | |||
669 | --- edit-here/tests/test_files.py 2015-03-11 11:57:50 +0000 | |||
670 | +++ internals/tests/test_files.py 2015-03-19 17:24:01 +0000 | |||
671 | @@ -1,26 +1,31 @@ | |||
672 | 1 | import os | 1 | import os |
673 | 2 | from unittest import TestCase | 2 | from unittest import TestCase |
674 | 3 | 3 | ||
676 | 4 | import translations | 4 | from translations.build import Translations |
677 | 5 | from translations import utils | ||
678 | 5 | 6 | ||
679 | 6 | 7 | ||
680 | 7 | class HelpTestCase(TestCase): | 8 | class HelpTestCase(TestCase): |
681 | 8 | def __init__(self, *args): | 9 | def __init__(self, *args): |
683 | 9 | self.translations = translations.Translations() | 10 | self.translations = Translations() |
684 | 10 | TestCase.__init__(self, *args) | 11 | TestCase.__init__(self, *args) |
685 | 11 | 12 | ||
686 | 12 | def test_doc_files_size_not_0(self): | 13 | def test_doc_files_size_not_0(self): |
687 | 14 | pwd = utils.use_top_level_dir() | ||
688 | 13 | sizes = [os.stat(fn).st_size | 15 | sizes = [os.stat(fn).st_size |
689 | 14 | for fn in self.translations.documents.docs] | 16 | for fn in self.translations.documents.docs] |
690 | 15 | self.assertNotIn(0, sizes) | 17 | self.assertNotIn(0, sizes) |
691 | 18 | os.chdir(pwd) | ||
692 | 16 | 19 | ||
693 | 17 | def test_po_files_size_not_0(self): | 20 | def test_po_files_size_not_0(self): |
694 | 21 | pwd = utils.use_top_level_dir() | ||
695 | 18 | sizes = [os.stat(fn).st_size | 22 | sizes = [os.stat(fn).st_size |
696 | 19 | for fn in self.translations.po.langs] | 23 | for fn in self.translations.po.langs] |
697 | 20 | self.assertNotIn(0, sizes) | 24 | self.assertNotIn(0, sizes) |
698 | 25 | os.chdir(pwd) | ||
699 | 21 | 26 | ||
700 | 22 | def test_markdown_files(self): | 27 | def test_markdown_files(self): |
701 | 23 | fns = self.translations.documents.find_docs() | 28 | fns = self.translations.documents.find_docs() |
703 | 24 | checked_fns = [translations.verify_markdown_file(fn) | 29 | checked_fns = [utils.verify_markdown_file(fn) |
704 | 25 | for fn in fns] | 30 | for fn in fns] |
705 | 26 | self.assertEqual(len(fns), checked_fns.count(True)) | 31 | self.assertEqual(len(fns), checked_fns.count(True)) |
706 | 27 | 32 | ||
707 | === modified file 'internals/tests/test_links.py' | |||
708 | --- edit-here/tests/test_links.py 2015-03-10 17:20:47 +0000 | |||
709 | +++ internals/tests/test_links.py 2015-03-19 17:24:01 +0000 | |||
710 | @@ -7,15 +7,19 @@ | |||
711 | 7 | from unittest import TestCase | 7 | from unittest import TestCase |
712 | 8 | import urllib.parse | 8 | import urllib.parse |
713 | 9 | 9 | ||
714 | 10 | from translations.utils import use_top_level_dir | ||
715 | 11 | |||
716 | 10 | 12 | ||
717 | 11 | def require_build(build): | 13 | def require_build(build): |
718 | 12 | tempdir = tempfile.mkdtemp() | 14 | tempdir = tempfile.mkdtemp() |
719 | 13 | env = {} | 15 | env = {} |
722 | 14 | if build == 'html': | 16 | if build == 'app': |
723 | 15 | env = {'OUTPUTDIR': tempdir} | 17 | env = {'OUTPUTDIR_APP': tempdir} |
724 | 16 | if build == 'web': | 18 | if build == 'web': |
725 | 17 | env = {'OUTPUTDIR_WEB': tempdir} | 19 | env = {'OUTPUTDIR_WEB': tempdir} |
726 | 20 | pwd = use_top_level_dir() | ||
727 | 18 | ret = subprocess.call(['make', '-es', build], env=env) | 21 | ret = subprocess.call(['make', '-es', build], env=env) |
728 | 22 | os.chdir(pwd) | ||
729 | 19 | return (ret, tempdir) | 23 | return (ret, tempdir) |
730 | 20 | 24 | ||
731 | 21 | 25 | ||
732 | 22 | 26 | ||
733 | === modified file 'internals/tests/test_translations.py' | |||
734 | --- edit-here/tests/test_translations.py 2015-03-17 09:24:48 +0000 | |||
735 | +++ internals/tests/test_translations.py 2015-03-19 17:24:01 +0000 | |||
736 | @@ -1,11 +1,11 @@ | |||
737 | 1 | from unittest import TestCase | 1 | from unittest import TestCase |
738 | 2 | 2 | ||
740 | 3 | import translations | 3 | from translations.build import Translations |
741 | 4 | 4 | ||
742 | 5 | 5 | ||
743 | 6 | class HelpTestCase(TestCase): | 6 | class HelpTestCase(TestCase): |
744 | 7 | def __init__(self, *args): | 7 | def __init__(self, *args): |
746 | 8 | self.translations = translations.Translations() | 8 | self.translations = Translations() |
747 | 9 | TestCase.__init__(self, *args) | 9 | TestCase.__init__(self, *args) |
748 | 10 | 10 | ||
749 | 11 | def test_first_line_of_docs_is_title_line(self): | 11 | def test_first_line_of_docs_is_title_line(self): |
750 | 12 | 12 | ||
751 | === added directory 'internals/translations' | |||
752 | === added file 'internals/translations/__init__.py' | |||
753 | === added file 'internals/translations/build.py' | |||
754 | --- internals/translations/build.py 1970-01-01 00:00:00 +0000 | |||
755 | +++ internals/translations/build.py 2015-03-19 17:24:01 +0000 | |||
756 | @@ -0,0 +1,304 @@ | |||
757 | 1 | import codecs | ||
758 | 2 | import glob | ||
759 | 3 | import os | ||
760 | 4 | import re | ||
761 | 5 | import shutil | ||
762 | 6 | import subprocess | ||
763 | 7 | |||
764 | 8 | from translations.utils import ( | ||
765 | 9 | find_bcp47_code, | ||
766 | 10 | full_path, | ||
767 | 11 | normalise_path, | ||
768 | 12 | require, | ||
769 | 13 | use_top_level_dir, | ||
770 | 14 | verify_markdown_file, | ||
771 | 15 | ) | ||
772 | 16 | |||
773 | 17 | from translations.po4a import PO4A | ||
774 | 18 | |||
775 | 19 | try: | ||
776 | 20 | import polib | ||
777 | 21 | except ImportError: | ||
778 | 22 | require('python3-polib') | ||
779 | 23 | |||
780 | 24 | from pelicanconf import ( | ||
781 | 25 | HIDE_FROM_POT, | ||
782 | 26 | META_TAGS, | ||
783 | 27 | PATH, | ||
784 | 28 | TRANSLATIONS_DIR, | ||
785 | 29 | ) | ||
786 | 30 | |||
787 | 31 | |||
788 | 32 | class POFile(object): | ||
789 | 33 | pofile = None | ||
790 | 34 | |||
791 | 35 | def __init__(self, po_fn): | ||
792 | 36 | self.po_fn = po_fn | ||
793 | 37 | self.load() | ||
794 | 38 | |||
795 | 39 | def load(self): | ||
796 | 40 | self.pofile = polib.pofile(full_path(self.po_fn)) | ||
797 | 41 | |||
798 | 42 | def merge(self, pot_file_ob): | ||
799 | 43 | self.pofile.merge(pot_file_ob) | ||
800 | 44 | |||
801 | 45 | def save(self): | ||
802 | 46 | self.pofile.save(full_path(self.po_fn)) | ||
803 | 47 | |||
804 | 48 | def find_in_msgid(self, find_str, translated=True, fuzzy=True, | ||
805 | 49 | untranslated=True): | ||
806 | 50 | entries = [] | ||
807 | 51 | if translated: | ||
808 | 52 | entries += self.pofile.translated_entries() | ||
809 | 53 | if fuzzy: | ||
810 | 54 | entries += self.pofile.fuzzy_entries() | ||
811 | 55 | if untranslated: | ||
812 | 56 | entries += self.pofile.untranslated_entries() | ||
813 | 57 | results = [] | ||
814 | 58 | for entry in entries: | ||
815 | 59 | if find_str in entry.msgid: | ||
816 | 60 | results += [entry] | ||
817 | 61 | return results | ||
818 | 62 | |||
819 | 63 | def hide_attr_list_statements(self): | ||
820 | 64 | entries = [] | ||
821 | 65 | for statement in HIDE_FROM_POT: | ||
822 | 66 | entries.extend(self.find_in_msgid(statement)) | ||
823 | 67 | statements = r'|'.join(HIDE_FROM_POT) | ||
824 | 68 | for entry in entries: | ||
825 | 69 | matches = re.findall(r'(.*?)\s*?(%s)\s*?' % statements, | ||
826 | 70 | entry.msgid) | ||
827 | 71 | # [('How do I update my system?', '!!T')] | ||
828 | 72 | if len(matches) == 1 and len(matches[0]) == 2: | ||
829 | 73 | entry.msgid = matches[0][0] | ||
830 | 74 | entry.comment = matches[0][1] | ||
831 | 75 | if matches[0][1] in entry.msgstr: | ||
832 | 76 | entry.msgstr = entry.msgstr.replace(' %s' % matches[0][1], '') | ||
833 | 77 | self.save() | ||
834 | 78 | |||
835 | 79 | def readd_attr_list_statements(self): | ||
836 | 80 | entries = [] | ||
837 | 81 | for entry_group in [self.pofile.translated_entries(), | ||
838 | 82 | self.pofile.fuzzy_entries(), | ||
839 | 83 | self.pofile.untranslated_entries()]: | ||
840 | 84 | for entry in entry_group: | ||
841 | 85 | for statement in HIDE_FROM_POT: | ||
842 | 86 | if statement in entry.comment: | ||
843 | 87 | entries += [entry] | ||
844 | 88 | for entry in entries: | ||
845 | 89 | if not entry.msgid.endswith(entry.comment): | ||
846 | 90 | entry.msgid += ' %s' % entry.comment | ||
847 | 91 | if entry.msgstr and not entry.msgstr.endswith(entry.comment): | ||
848 | 92 | entry.msgstr += ' %s' % entry.comment | ||
849 | 93 | entry.comment = '' | ||
850 | 94 | self.save() | ||
851 | 95 | |||
852 | 96 | def safeguard_meta_tags(self): | ||
853 | 97 | for tag in META_TAGS: | ||
854 | 98 | for entry in self.find_in_msgid(tag): | ||
855 | 99 | if entry.msgid == tag: | ||
856 | 100 | entry.msgstr = entry.msgid | ||
857 | 101 | self.save() | ||
858 | 102 | |||
859 | 103 | def find_title_lines(self): | ||
860 | 104 | results = [] | ||
861 | 105 | for entry in self.find_in_msgid('Title: '): | ||
862 | 106 | if entry.msgid.startswith('Title: '): | ||
863 | 107 | where = entry.occurrences[0][0] | ||
864 | 108 | first_line = codecs.open(full_path(where), | ||
865 | 109 | encoding='utf-8').readline().strip() | ||
866 | 110 | results += [(entry, first_line)] | ||
867 | 111 | return results | ||
868 | 112 | |||
869 | 113 | def replace_title_lines(self): | ||
870 | 114 | for entry, first_line in self.find_title_lines(): | ||
871 | 115 | if entry.msgid != first_line: | ||
872 | 116 | print('Title line "%s" found, but not on the first line ' | ||
873 | 117 | 'of "%s".' % (entry.msgid, entry.linenum)) | ||
874 | 118 | return False | ||
875 | 119 | entry.msgid = entry.msgid.replace('Title: ', '') | ||
876 | 120 | if self.po_fn.endswith('.po'): | ||
877 | 121 | entry.msgstr = '' | ||
878 | 122 | self.save() | ||
879 | 123 | return True | ||
880 | 124 | |||
881 | 125 | def find_link_in_markdown_message(self, entry): | ||
882 | 126 | link_regex = r'\[.+?\]\(\{filename\}(.+?)\).*?' | ||
883 | 127 | link_msgid = re.findall(link_regex, entry.msgid)[0] | ||
884 | 128 | link_msgstr = list(re.findall(link_regex, entry.msgstr)) | ||
885 | 129 | return (link_msgid, link_msgstr) | ||
886 | 130 | |||
887 | 131 | def rewrite_links(self, documents, bcp47): | ||
888 | 132 | for entry in self.find_in_msgid('{filename}'): | ||
889 | 133 | (link_msgid, link_msgstr) = \ | ||
890 | 134 | self.find_link_in_markdown_message(entry) | ||
891 | 135 | if [doc for doc in documents.docs if doc.endswith(link_msgid)]: | ||
892 | 136 | translated_doc_fn = os.path.basename( | ||
893 | 137 | documents.translated_doc_fn(link_msgid, bcp47)) | ||
894 | 138 | if not link_msgstr: | ||
895 | 139 | entry.msgstr = entry.msgid | ||
896 | 140 | link_msgstr = [link_msgid] | ||
897 | 141 | entry.msgstr = entry.msgstr.replace(link_msgstr[0], | ||
898 | 142 | translated_doc_fn) | ||
899 | 143 | self.save() | ||
900 | 144 | |||
901 | 145 | def find_translated_title_line(self, original_title): | ||
902 | 146 | for entry in self.find_in_msgid(original_title): | ||
903 | 147 | if entry.msgid == original_title: | ||
904 | 148 | if entry.msgstr: | ||
905 | 149 | return entry.msgstr | ||
906 | 150 | return entry.msgid | ||
907 | 151 | |||
908 | 152 | |||
909 | 153 | class PO(object): | ||
910 | 154 | def __init__(self, po4a): | ||
911 | 155 | self.fake_lang_code = 'en_US' | ||
912 | 156 | self.fake_po_fn = normalise_path( | ||
913 | 157 | os.path.join(TRANSLATIONS_DIR, | ||
914 | 158 | '%s.po' % self.fake_lang_code)) | ||
915 | 159 | self.pot_fn = normalise_path(os.path.join(TRANSLATIONS_DIR, | ||
916 | 160 | 'help.pot')) | ||
917 | 161 | self.pot_file_ob = POFile(self.pot_fn) | ||
918 | 162 | self.po4a = po4a | ||
919 | 163 | self.langs = {} | ||
920 | 164 | for po_fn in glob.glob(TRANSLATIONS_DIR+'/*.po'): | ||
921 | 165 | self.add_language(normalise_path(po_fn)) | ||
922 | 166 | |||
923 | 167 | def add_language(self, po_fn): | ||
924 | 168 | gettext_code = os.path.basename(po_fn).split('.po')[0] | ||
925 | 169 | self.langs[po_fn] = { | ||
926 | 170 | 'bcp47': find_bcp47_code(gettext_code), | ||
927 | 171 | 'gettext_code': gettext_code, | ||
928 | 172 | 'pofile': None, | ||
929 | 173 | } | ||
930 | 174 | |||
931 | 175 | def _remove_fake_po_file(self): | ||
932 | 176 | if os.path.exists(self.fake_po_fn): | ||
933 | 177 | os.remove(self.fake_po_fn) | ||
934 | 178 | |||
935 | 179 | def __del__(self): | ||
936 | 180 | self._remove_fake_po_file() | ||
937 | 181 | |||
938 | 182 | def load_pofile(self, po_fn): | ||
939 | 183 | if not self.langs[po_fn]['pofile']: | ||
940 | 184 | self.langs[po_fn]['pofile'] = POFile(po_fn) | ||
941 | 185 | |||
942 | 186 | def gettextize(self, documents): | ||
943 | 187 | if not self.po4a.gettextize(documents.docs, self.pot_fn): | ||
944 | 188 | return False | ||
945 | 189 | self.pot_file_ob.load() | ||
946 | 190 | return True | ||
947 | 191 | |||
948 | 192 | def generate_pot_file(self, documents): | ||
949 | 193 | if not self.gettextize(documents): | ||
950 | 194 | return False | ||
951 | 195 | if not self.pot_file_ob.replace_title_lines(): | ||
952 | 196 | return False | ||
953 | 197 | self.pot_file_ob.hide_attr_list_statements() | ||
954 | 198 | for po_fn in self.langs: | ||
955 | 199 | self.load_pofile(po_fn) | ||
956 | 200 | self.langs[po_fn]['pofile'].merge(self.pot_file_ob.pofile) | ||
957 | 201 | if not self.langs[po_fn]['pofile'].replace_title_lines(): | ||
958 | 202 | return False | ||
959 | 203 | self.langs[po_fn]['pofile'].hide_attr_list_statements() | ||
960 | 204 | return True | ||
961 | 205 | |||
962 | 206 | # we generate a fake translation for en-US which is going to be | ||
963 | 207 | # the default | ||
964 | 208 | def generate_fake_pofile(self): | ||
965 | 209 | pwd = use_top_level_dir() | ||
966 | 210 | self._remove_fake_po_file() | ||
967 | 211 | shutil.copy(self.pot_fn, self.fake_po_fn) | ||
968 | 212 | os.chdir(pwd) | ||
969 | 213 | self.add_language(self.fake_po_fn) | ||
970 | 214 | |||
971 | 215 | def find_translated_title_line(self, original_title, po_fn): | ||
972 | 216 | return self.langs[po_fn]['pofile'].find_translated_title_line( | ||
973 | 217 | original_title) | ||
974 | 218 | |||
975 | 219 | def rewrite_links(self, documents): | ||
976 | 220 | for po_fn in self.langs: | ||
977 | 221 | self.load_pofile(po_fn) | ||
978 | 222 | self.langs[po_fn]['pofile'].rewrite_links( | ||
979 | 223 | documents, self.langs[po_fn]['bcp47']) | ||
980 | 224 | |||
981 | 225 | def safeguard_meta_tags(self): | ||
982 | 226 | for po_fn in self.langs: | ||
983 | 227 | self.load_pofile(po_fn) | ||
984 | 228 | self.langs[po_fn]['pofile'].safeguard_meta_tags() | ||
985 | 229 | |||
986 | 230 | |||
987 | 231 | class Documents(object): | ||
988 | 232 | def __init__(self): | ||
989 | 233 | self.docs = [fn for fn in self.find_docs() | ||
990 | 234 | if verify_markdown_file(fn)] | ||
991 | 235 | |||
992 | 236 | def find_docs(self): | ||
993 | 237 | docs = [] | ||
994 | 238 | for dirpath, dirnames, fns in os.walk(PATH): | ||
995 | 239 | docs += [normalise_path(os.path.join(dirpath, fn)) | ||
996 | 240 | for fn in fns | ||
997 | 241 | if fn.endswith('.md')] | ||
998 | 242 | return docs | ||
999 | 243 | |||
1000 | 244 | def translated_doc_fn(self, fn, bcp47_code): | ||
1001 | 245 | match = [doc for doc in self.docs | ||
1002 | 246 | if os.path.basename(doc) == os.path.basename(fn)] | ||
1003 | 247 | if not match: | ||
1004 | 248 | return None | ||
1005 | 249 | return '%s.%s.md' % (match[0].split('.md')[0], | ||
1006 | 250 | bcp47_code) | ||
1007 | 251 | |||
1008 | 252 | def _call_po4a_translate(self, doc, po_fn, po4a): | ||
1009 | 253 | res = po4a.translate(doc, po_fn) | ||
1010 | 254 | return codecs.decode(res.communicate()[0]) | ||
1011 | 255 | |||
1012 | 256 | def write_translated_markdown(self, po, po4a): | ||
1013 | 257 | for po_fn in po.langs: | ||
1014 | 258 | po.langs[po_fn]['pofile'].readd_attr_list_statements() | ||
1015 | 259 | for doc_fn in self.docs: | ||
1016 | 260 | output = self._call_po4a_translate(doc_fn, po_fn, po4a) | ||
1017 | 261 | title_line = output.split('\n')[0].split('Title: ')[1] | ||
1018 | 262 | translated_title_line = po.find_translated_title_line( | ||
1019 | 263 | title_line, po_fn) | ||
1020 | 264 | output = '\n'.join([line for line in output.split('\n')][1:]) | ||
1021 | 265 | new_path = full_path(self.translated_doc_fn( | ||
1022 | 266 | doc_fn, po.langs[po_fn]['bcp47'])) | ||
1023 | 267 | text = "Title: %s\nDate:\n\n" % (translated_title_line) | ||
1024 | 268 | text += output | ||
1025 | 269 | if os.path.exists(new_path): | ||
1026 | 270 | os.remove(new_path) | ||
1027 | 271 | if not os.path.exists(os.path.dirname(new_path)): | ||
1028 | 272 | os.makedirs(os.path.dirname(new_path)) | ||
1029 | 273 | with open(new_path, 'w', encoding='utf-8') as f: | ||
1030 | 274 | f.write(text) | ||
1031 | 275 | po.langs[po_fn]['pofile'].hide_attr_list_statements() | ||
1032 | 276 | |||
1033 | 277 | |||
1034 | 278 | class Translations(object): | ||
1035 | 279 | def __init__(self): | ||
1036 | 280 | self._cleanup() | ||
1037 | 281 | self.documents = Documents() | ||
1038 | 282 | self.po4a = PO4A() | ||
1039 | 283 | self.po = PO(self.po4a) | ||
1040 | 284 | |||
1041 | 285 | def _cleanup(self): | ||
1042 | 286 | r = subprocess.Popen(['bzr', 'ignored'], stdout=subprocess.PIPE) | ||
1043 | 287 | fns = [full_path(f.split(' ')[0]) | ||
1044 | 288 | for f in codecs.decode(r.communicate()[0]).split('\n') | ||
1045 | 289 | if f.strip() != ''] | ||
1046 | 290 | fns = [f for f in fns if os.path.exists(f)] | ||
1047 | 291 | for f in fns: | ||
1048 | 292 | try: | ||
1049 | 293 | shutil.rmtree(f) | ||
1050 | 294 | except NotADirectoryError: | ||
1051 | 295 | os.remove(f) | ||
1052 | 296 | |||
1053 | 297 | def generate_pot_file(self): | ||
1054 | 298 | return self.po.generate_pot_file(self.documents) | ||
1055 | 299 | |||
1056 | 300 | def generate_translations(self): | ||
1057 | 301 | self.po.generate_fake_pofile() | ||
1058 | 302 | self.po.rewrite_links(self.documents) | ||
1059 | 303 | self.po.safeguard_meta_tags() | ||
1060 | 304 | self.documents.write_translated_markdown(self.po, self.po4a) | ||
1061 | 0 | 305 | ||
1062 | === added file 'internals/translations/po4a.py' | |||
1063 | --- internals/translations/po4a.py 1970-01-01 00:00:00 +0000 | |||
1064 | +++ internals/translations/po4a.py 2015-03-19 17:24:01 +0000 | |||
1065 | @@ -0,0 +1,62 @@ | |||
1066 | 1 | import copy | ||
1067 | 2 | import os | ||
1068 | 3 | import shutil | ||
1069 | 4 | import subprocess | ||
1070 | 5 | |||
1071 | 6 | from translations.utils import ( | ||
1072 | 7 | require, | ||
1073 | 8 | use_top_level_dir, | ||
1074 | 9 | ) | ||
1075 | 10 | |||
1076 | 11 | # This defines how complete we expect translations to be before we | ||
1077 | 12 | # generate HTML from them. Needs to be string. | ||
1078 | 13 | TRANSLATION_COMPLETION_PERCENTAGE = '0' | ||
1079 | 14 | |||
1080 | 15 | |||
1081 | 16 | class PO4A(object): | ||
1082 | 17 | pwd = None | ||
1083 | 18 | |||
1084 | 19 | def __init__(self): | ||
1085 | 20 | self.default_args = [ | ||
1086 | 21 | '-f', 'text', | ||
1087 | 22 | '-o', 'markdown', | ||
1088 | 23 | '-M', 'utf-8', | ||
1089 | 24 | ] | ||
1090 | 25 | if not shutil.which('po4a'): | ||
1091 | 26 | require('po4a') | ||
1092 | 27 | |||
1093 | 28 | def run(self, po4a_command, additional_args, with_output=False, | ||
1094 | 29 | top_level_dir=False): | ||
1095 | 30 | if top_level_dir: | ||
1096 | 31 | self.pwd = use_top_level_dir() | ||
1097 | 32 | args = copy.copy(self.default_args) | ||
1098 | 33 | args += additional_args | ||
1099 | 34 | if with_output: | ||
1100 | 35 | ret = subprocess.Popen([po4a_command]+args, stdout=subprocess.PIPE) | ||
1101 | 36 | else: | ||
1102 | 37 | ret = subprocess.call([po4a_command]+args) | ||
1103 | 38 | if top_level_dir: | ||
1104 | 39 | os.chdir(self.pwd) | ||
1105 | 40 | self.pwd = None | ||
1106 | 41 | return ret | ||
1107 | 42 | |||
1108 | 43 | def gettextize(self, document_fns, pot_file): | ||
1109 | 44 | args = copy.copy(self.default_args) | ||
1110 | 45 | for document_fn in document_fns: | ||
1111 | 46 | args += ['-m', document_fn] | ||
1112 | 47 | args += [ | ||
1113 | 48 | '-p', pot_file, | ||
1114 | 49 | '-L', 'utf-8', | ||
1115 | 50 | ] | ||
1116 | 51 | ret = self.run('po4a-gettextize', args, top_level_dir=True) | ||
1117 | 52 | return (not ret) | ||
1118 | 53 | |||
1119 | 54 | def translate(self, doc, po_fn): | ||
1120 | 55 | args = [ | ||
1121 | 56 | '-k', TRANSLATION_COMPLETION_PERCENTAGE, | ||
1122 | 57 | '-m', doc, | ||
1123 | 58 | '-p', po_fn, | ||
1124 | 59 | '-L', 'utf-8', | ||
1125 | 60 | ] | ||
1126 | 61 | return self.run('po4a-translate', args, with_output=True, | ||
1127 | 62 | top_level_dir=True) | ||
1128 | 0 | 63 | ||
1129 | === renamed file 'edit-here/translations.py' => 'internals/translations/utils.py' | |||
1130 | --- edit-here/translations.py 2015-03-17 09:24:48 +0000 | |||
1131 | +++ internals/translations/utils.py 2015-03-19 17:24:01 +0000 | |||
1132 | @@ -1,10 +1,5 @@ | |||
1133 | 1 | import codecs | 1 | import codecs |
1134 | 2 | import copy | ||
1135 | 3 | import glob | ||
1136 | 4 | import os | 2 | import os |
1137 | 5 | import re | ||
1138 | 6 | import shutil | ||
1139 | 7 | import subprocess | ||
1140 | 8 | import sys | 3 | import sys |
1141 | 9 | import tempfile | 4 | import tempfile |
1142 | 10 | 5 | ||
1143 | @@ -15,11 +10,6 @@ | |||
1144 | 15 | sys.exit(1) | 10 | sys.exit(1) |
1145 | 16 | 11 | ||
1146 | 17 | try: | 12 | try: |
1147 | 18 | import polib | ||
1148 | 19 | except ImportError: | ||
1149 | 20 | require('python3-polib') | ||
1150 | 21 | |||
1151 | 22 | try: | ||
1152 | 23 | import magic | 13 | import magic |
1153 | 24 | except: | 14 | except: |
1154 | 25 | require('python3-magic') | 15 | require('python3-magic') |
1155 | @@ -29,11 +19,10 @@ | |||
1156 | 29 | except: | 19 | except: |
1157 | 30 | require('python3-markdown') | 20 | require('python3-markdown') |
1158 | 31 | 21 | ||
1164 | 32 | from pelicanconf import PATH, MD_EXTENSIONS, META_TAGS, HIDE_FROM_POT | 22 | from pelicanconf import ( |
1165 | 33 | 23 | MD_EXTENSIONS, | |
1166 | 34 | # This defines how complete we expect translations to be before we | 24 | TOP_LEVEL_DIR, |
1167 | 35 | # generate HTML from them. Needs to be string. | 25 | ) |
1163 | 36 | TRANSLATION_COMPLETION_PERCENTAGE = '0' | ||
1168 | 37 | 26 | ||
1169 | 38 | BCP47_OVERRIDES = ( | 27 | BCP47_OVERRIDES = ( |
1170 | 39 | ('zh_CN', 'zh-hans'), | 28 | ('zh_CN', 'zh-hans'), |
1171 | @@ -47,6 +36,20 @@ | |||
1172 | 47 | ] | 36 | ] |
1173 | 48 | 37 | ||
1174 | 49 | 38 | ||
1175 | 39 | def normalise_path(path): | ||
1176 | 40 | return os.path.relpath(path, TOP_LEVEL_DIR) | ||
1177 | 41 | |||
1178 | 42 | |||
1179 | 43 | def full_path(path): | ||
1180 | 44 | return os.path.abspath(os.path.join(TOP_LEVEL_DIR, path)) | ||
1181 | 45 | |||
1182 | 46 | |||
1183 | 47 | def use_top_level_dir(): | ||
1184 | 48 | pwd = os.getcwd() | ||
1185 | 49 | os.chdir(TOP_LEVEL_DIR) | ||
1186 | 50 | return pwd | ||
1187 | 51 | |||
1188 | 52 | |||
1189 | 50 | def _temp_write_markdown(fn): | 53 | def _temp_write_markdown(fn): |
1190 | 51 | (ret, tmp) = tempfile.mkstemp() | 54 | (ret, tmp) = tempfile.mkstemp() |
1191 | 52 | length = 0 | 55 | length = 0 |
1192 | @@ -65,6 +68,7 @@ | |||
1193 | 65 | def verify_markdown_file(fn): | 68 | def verify_markdown_file(fn): |
1194 | 66 | ms = magic.open(magic.MAGIC_NONE) | 69 | ms = magic.open(magic.MAGIC_NONE) |
1195 | 67 | ms.load() | 70 | ms.load() |
1196 | 71 | fn = full_path(fn) | ||
1197 | 68 | if ms.file(fn) not in MD_MAGIC_FILE_TYPES: | 72 | if ms.file(fn) not in MD_MAGIC_FILE_TYPES: |
1198 | 69 | return False | 73 | return False |
1199 | 70 | (ret, length) = _temp_write_markdown(fn) | 74 | (ret, length) = _temp_write_markdown(fn) |
1200 | @@ -78,314 +82,3 @@ | |||
1201 | 78 | return gettext_code.lower().replace('_', '-') | 82 | return gettext_code.lower().replace('_', '-') |
1202 | 79 | return [c[1] for c in BCP47_OVERRIDES | 83 | return [c[1] for c in BCP47_OVERRIDES |
1203 | 80 | if c[0] == gettext_code][0] | 84 | if c[0] == gettext_code][0] |
1204 | 81 | |||
1205 | 82 | |||
1206 | 83 | class PO4A(object): | ||
1207 | 84 | def __init__(self): | ||
1208 | 85 | self.default_args = [ | ||
1209 | 86 | '-f', 'text', | ||
1210 | 87 | '-o', 'markdown', | ||
1211 | 88 | '-M', 'utf-8', | ||
1212 | 89 | ] | ||
1213 | 90 | if not shutil.which('po4a'): | ||
1214 | 91 | require('po4a') | ||
1215 | 92 | |||
1216 | 93 | def run(self, po4a_command, additional_args, with_output=False): | ||
1217 | 94 | args = copy.copy(self.default_args) | ||
1218 | 95 | args += additional_args | ||
1219 | 96 | if with_output: | ||
1220 | 97 | ret = subprocess.Popen([po4a_command]+args, stdout=subprocess.PIPE) | ||
1221 | 98 | else: | ||
1222 | 99 | ret = subprocess.call([po4a_command]+args) | ||
1223 | 100 | return ret | ||
1224 | 101 | |||
1225 | 102 | def gettextize(self, document_fns, pot_file): | ||
1226 | 103 | args = copy.copy(self.default_args) | ||
1227 | 104 | for document_fn in document_fns: | ||
1228 | 105 | args += ['-m', document_fn] | ||
1229 | 106 | args += [ | ||
1230 | 107 | '-p', pot_file, | ||
1231 | 108 | '-L', 'utf-8', | ||
1232 | 109 | ] | ||
1233 | 110 | ret = self.run('po4a-gettextize', args) | ||
1234 | 111 | return (not ret) | ||
1235 | 112 | |||
1236 | 113 | def translate(self, doc, po_fn): | ||
1237 | 114 | args = [ | ||
1238 | 115 | '-k', TRANSLATION_COMPLETION_PERCENTAGE, | ||
1239 | 116 | '-m', doc, | ||
1240 | 117 | '-p', po_fn, | ||
1241 | 118 | '-L', 'utf-8', | ||
1242 | 119 | ] | ||
1243 | 120 | return self.run('po4a-translate', args, with_output=True) | ||
1244 | 121 | |||
1245 | 122 | |||
1246 | 123 | class POFile(object): | ||
1247 | 124 | def __init__(self, po_fn): | ||
1248 | 125 | self.po_fn = po_fn | ||
1249 | 126 | self.pofile = polib.pofile(po_fn) | ||
1250 | 127 | |||
1251 | 128 | def reread(self): | ||
1252 | 129 | self.pofile = polib.pofile(self.po_fn) | ||
1253 | 130 | |||
1254 | 131 | def merge(self, pot_file_ob): | ||
1255 | 132 | self.pofile.merge(pot_file_ob) | ||
1256 | 133 | |||
1257 | 134 | def save(self): | ||
1258 | 135 | self.pofile.save(self.po_fn) | ||
1259 | 136 | |||
1260 | 137 | def find_in_msgid(self, find_str, translated=True, fuzzy=True, | ||
1261 | 138 | untranslated=True): | ||
1262 | 139 | entries = [] | ||
1263 | 140 | if translated: | ||
1264 | 141 | entries += self.pofile.translated_entries() | ||
1265 | 142 | if fuzzy: | ||
1266 | 143 | entries += self.pofile.fuzzy_entries() | ||
1267 | 144 | if untranslated: | ||
1268 | 145 | entries += self.pofile.untranslated_entries() | ||
1269 | 146 | results = [] | ||
1270 | 147 | for entry in entries: | ||
1271 | 148 | if find_str in entry.msgid: | ||
1272 | 149 | results += [entry] | ||
1273 | 150 | return results | ||
1274 | 151 | |||
1275 | 152 | def hide_attr_list_statements(self): | ||
1276 | 153 | entries = [] | ||
1277 | 154 | for statement in HIDE_FROM_POT: | ||
1278 | 155 | entries.extend(self.find_in_msgid(statement)) | ||
1279 | 156 | statements = r'|'.join(HIDE_FROM_POT) | ||
1280 | 157 | for entry in entries: | ||
1281 | 158 | matches = re.findall(r'(.*?)\s*?(%s)\s*?' % statements, | ||
1282 | 159 | entry.msgid) | ||
1283 | 160 | # [('How do I update my system?', '!!T')] | ||
1284 | 161 | if len(matches) == 1 and len(matches[0]) == 2: | ||
1285 | 162 | entry.msgid = matches[0][0] | ||
1286 | 163 | entry.comment = matches[0][1] | ||
1287 | 164 | if matches[0][1] in entry.msgstr: | ||
1288 | 165 | entry.msgstr = entry.msgstr.replace(' %s' % matches[0][1], '') | ||
1289 | 166 | self.save() | ||
1290 | 167 | |||
1291 | 168 | def readd_attr_list_statements(self): | ||
1292 | 169 | entries = [] | ||
1293 | 170 | for entry_group in [self.pofile.translated_entries(), | ||
1294 | 171 | self.pofile.fuzzy_entries(), | ||
1295 | 172 | self.pofile.untranslated_entries()]: | ||
1296 | 173 | for entry in entry_group: | ||
1297 | 174 | for statement in HIDE_FROM_POT: | ||
1298 | 175 | if statement in entry.comment: | ||
1299 | 176 | entries += [entry] | ||
1300 | 177 | for entry in entries: | ||
1301 | 178 | if not entry.msgid.endswith(entry.comment): | ||
1302 | 179 | entry.msgid += ' %s' % entry.comment | ||
1303 | 180 | if entry.msgstr and not entry.msgstr.endswith(entry.comment): | ||
1304 | 181 | entry.msgstr += ' %s' % entry.comment | ||
1305 | 182 | entry.comment = '' | ||
1306 | 183 | self.save() | ||
1307 | 184 | |||
1308 | 185 | def safeguard_meta_tags(self): | ||
1309 | 186 | for tag in META_TAGS: | ||
1310 | 187 | for entry in self.find_in_msgid(tag): | ||
1311 | 188 | if entry.msgid == tag: | ||
1312 | 189 | entry.msgstr = entry.msgid | ||
1313 | 190 | self.save() | ||
1314 | 191 | |||
1315 | 192 | def find_title_lines(self): | ||
1316 | 193 | results = [] | ||
1317 | 194 | for entry in self.find_in_msgid('Title: '): | ||
1318 | 195 | if entry.msgid.startswith('Title: '): | ||
1319 | 196 | where = entry.occurrences[0][0] | ||
1320 | 197 | first_line = codecs.open(where, | ||
1321 | 198 | encoding='utf-8').readline().strip() | ||
1322 | 199 | results += [(entry, first_line)] | ||
1323 | 200 | return results | ||
1324 | 201 | |||
1325 | 202 | def replace_title_lines(self): | ||
1326 | 203 | for entry, first_line in self.find_title_lines(): | ||
1327 | 204 | if entry.msgid != first_line: | ||
1328 | 205 | print('Title line "%s" found, but not on the first line ' | ||
1329 | 206 | 'of "%s".' % (entry.msgid, entry.linenum)) | ||
1330 | 207 | return False | ||
1331 | 208 | entry.msgid = entry.msgid.replace('Title: ', '') | ||
1332 | 209 | if self.po_fn.endswith('.po'): | ||
1333 | 210 | entry.msgstr = '' | ||
1334 | 211 | self.save() | ||
1335 | 212 | return True | ||
1336 | 213 | |||
1337 | 214 | def find_link_in_markdown_message(self, entry): | ||
1338 | 215 | link_regex = r'\[.+?\]\(\{filename\}(.+?)\).*?' | ||
1339 | 216 | link_msgid = re.findall(link_regex, entry.msgid)[0] | ||
1340 | 217 | link_msgstr = list(re.findall(link_regex, entry.msgstr)) | ||
1341 | 218 | return (link_msgid, link_msgstr) | ||
1342 | 219 | |||
1343 | 220 | def rewrite_links(self, documents, bcp47): | ||
1344 | 221 | for entry in self.find_in_msgid('{filename}'): | ||
1345 | 222 | (link_msgid, link_msgstr) = \ | ||
1346 | 223 | self.find_link_in_markdown_message(entry) | ||
1347 | 224 | if [doc for doc in documents.docs if doc.endswith(link_msgid)]: | ||
1348 | 225 | translated_doc_fn = os.path.basename( | ||
1349 | 226 | documents.translated_doc_fn(link_msgid, bcp47)) | ||
1350 | 227 | if not link_msgstr: | ||
1351 | 228 | entry.msgstr = entry.msgid | ||
1352 | 229 | link_msgstr = [link_msgid] | ||
1353 | 230 | entry.msgstr = entry.msgstr.replace(link_msgstr[0], | ||
1354 | 231 | translated_doc_fn) | ||
1355 | 232 | self.save() | ||
1356 | 233 | |||
1357 | 234 | def find_translated_title_line(self, original_title): | ||
1358 | 235 | for entry in self.find_in_msgid(original_title): | ||
1359 | 236 | if entry.msgid == original_title: | ||
1360 | 237 | if entry.msgstr: | ||
1361 | 238 | return entry.msgstr | ||
1362 | 239 | return entry.msgid | ||
1363 | 240 | |||
1364 | 241 | |||
1365 | 242 | class PO(object): | ||
1366 | 243 | def __init__(self, po4a): | ||
1367 | 244 | self.translations_dir = os.path.abspath(os.path.join(PATH, '../po')) | ||
1368 | 245 | self.fake_lang_code = 'en_US' | ||
1369 | 246 | self.fake_po_fn = os.path.join(self.translations_dir, | ||
1370 | 247 | '%s.po' % self.fake_lang_code) | ||
1371 | 248 | self.pot_fn = os.path.join(self.translations_dir, 'help.pot') | ||
1372 | 249 | self.pot_file_ob = POFile(self.pot_fn) | ||
1373 | 250 | self.po4a = po4a | ||
1374 | 251 | self.langs = {} | ||
1375 | 252 | for po_fn in glob.glob(self.translations_dir+'/*.po'): | ||
1376 | 253 | self.add_language(po_fn) | ||
1377 | 254 | |||
1378 | 255 | def add_language(self, po_fn): | ||
1379 | 256 | gettext_code = os.path.basename(po_fn).split('.po')[0] | ||
1380 | 257 | self.langs[po_fn] = { | ||
1381 | 258 | 'bcp47': find_bcp47_code(gettext_code), | ||
1382 | 259 | 'gettext_code': gettext_code, | ||
1383 | 260 | 'pofile': None, | ||
1384 | 261 | } | ||
1385 | 262 | |||
1386 | 263 | def _remove_fake_po_file(self): | ||
1387 | 264 | if os.path.exists(self.fake_po_fn): | ||
1388 | 265 | os.remove(self.fake_po_fn) | ||
1389 | 266 | |||
1390 | 267 | def __del__(self): | ||
1391 | 268 | self._remove_fake_po_file() | ||
1392 | 269 | |||
1393 | 270 | def load_pofile(self, po_fn): | ||
1394 | 271 | if not self.langs[po_fn]['pofile']: | ||
1395 | 272 | self.langs[po_fn]['pofile'] = POFile(po_fn) | ||
1396 | 273 | |||
1397 | 274 | def gettextize(self, documents): | ||
1398 | 275 | if not self.po4a.gettextize(documents.docs, self.pot_fn): | ||
1399 | 276 | return False | ||
1400 | 277 | self.pot_file_ob.reread() | ||
1401 | 278 | return True | ||
1402 | 279 | |||
1403 | 280 | def generate_pot_file(self, documents): | ||
1404 | 281 | if not self.gettextize(documents): | ||
1405 | 282 | return False | ||
1406 | 283 | if not self.pot_file_ob.replace_title_lines(): | ||
1407 | 284 | return False | ||
1408 | 285 | self.pot_file_ob.hide_attr_list_statements() | ||
1409 | 286 | for po_fn in self.langs: | ||
1410 | 287 | self.load_pofile(po_fn) | ||
1411 | 288 | self.langs[po_fn]['pofile'].merge(self.pot_file_ob.pofile) | ||
1412 | 289 | if not self.langs[po_fn]['pofile'].replace_title_lines(): | ||
1413 | 290 | return False | ||
1414 | 291 | self.langs[po_fn]['pofile'].hide_attr_list_statements() | ||
1415 | 292 | return True | ||
1416 | 293 | |||
1417 | 294 | # we generate a fake translation for en-US which is going to be | ||
1418 | 295 | # the default | ||
1419 | 296 | def generate_fake_pofile(self): | ||
1420 | 297 | self._remove_fake_po_file() | ||
1421 | 298 | shutil.copy(self.pot_fn, self.fake_po_fn) | ||
1422 | 299 | self.add_language(self.fake_po_fn) | ||
1423 | 300 | |||
1424 | 301 | def find_translated_title_line(self, original_title, po_fn): | ||
1425 | 302 | return self.langs[po_fn]['pofile'].find_translated_title_line( | ||
1426 | 303 | original_title) | ||
1427 | 304 | |||
1428 | 305 | def rewrite_links(self, documents): | ||
1429 | 306 | for po_fn in self.langs: | ||
1430 | 307 | self.load_pofile(po_fn) | ||
1431 | 308 | self.langs[po_fn]['pofile'].rewrite_links( | ||
1432 | 309 | documents, self.langs[po_fn]['bcp47']) | ||
1433 | 310 | |||
1434 | 311 | def safeguard_meta_tags(self): | ||
1435 | 312 | for po_fn in self.langs: | ||
1436 | 313 | self.load_pofile(po_fn) | ||
1437 | 314 | self.langs[po_fn]['pofile'].safeguard_meta_tags() | ||
1438 | 315 | |||
1439 | 316 | |||
1440 | 317 | class Documents(object): | ||
1441 | 318 | def __init__(self): | ||
1442 | 319 | self.docs = [fn for fn in self.find_docs() | ||
1443 | 320 | if verify_markdown_file(fn)] | ||
1444 | 321 | |||
1445 | 322 | def find_docs(self): | ||
1446 | 323 | docs = [] | ||
1447 | 324 | for dirpath, dirnames, fns in os.walk(PATH): | ||
1448 | 325 | docs += [os.path.relpath(os.path.join(dirpath, fn), | ||
1449 | 326 | os.path.join(PATH, '..')) | ||
1450 | 327 | for fn in fns | ||
1451 | 328 | if fn.endswith('.md')] | ||
1452 | 329 | return docs | ||
1453 | 330 | |||
1454 | 331 | def translated_doc_fn(self, fn, bcp47_code): | ||
1455 | 332 | match = [doc for doc in self.docs | ||
1456 | 333 | if os.path.basename(doc) == os.path.basename(fn)] | ||
1457 | 334 | if not match: | ||
1458 | 335 | return None | ||
1459 | 336 | return '%s.%s.md' % (match[0].split('.md')[0], | ||
1460 | 337 | bcp47_code) | ||
1461 | 338 | |||
1462 | 339 | def _call_po4a_translate(self, doc, po_fn, po4a): | ||
1463 | 340 | res = po4a.translate(doc, po_fn) | ||
1464 | 341 | return codecs.decode(res.communicate()[0]) | ||
1465 | 342 | |||
1466 | 343 | def write_translated_markdown(self, po, po4a): | ||
1467 | 344 | for po_fn in po.langs: | ||
1468 | 345 | po.langs[po_fn]['pofile'].readd_attr_list_statements() | ||
1469 | 346 | for doc_fn in self.docs: | ||
1470 | 347 | output = self._call_po4a_translate(doc_fn, po_fn, po4a) | ||
1471 | 348 | title_line = output.split('\n')[0].split('Title: ')[1] | ||
1472 | 349 | translated_title_line = po.find_translated_title_line( | ||
1473 | 350 | title_line, po_fn) | ||
1474 | 351 | output = '\n'.join([line for line in output.split('\n')][1:]) | ||
1475 | 352 | new_path = self.translated_doc_fn(doc_fn, | ||
1476 | 353 | po.langs[po_fn]['bcp47']) | ||
1477 | 354 | text = "Title: %s\nDate:\n\n" % (translated_title_line) | ||
1478 | 355 | text += output | ||
1479 | 356 | if os.path.exists(new_path): | ||
1480 | 357 | os.remove(new_path) | ||
1481 | 358 | if not os.path.exists(os.path.dirname(new_path)): | ||
1482 | 359 | os.makedirs(os.path.dirname(new_path)) | ||
1483 | 360 | with open(new_path, 'w', encoding='utf-8') as f: | ||
1484 | 361 | f.write(text) | ||
1485 | 362 | po.langs[po_fn]['pofile'].hide_attr_list_statements() | ||
1486 | 363 | |||
1487 | 364 | |||
1488 | 365 | class Translations(object): | ||
1489 | 366 | def __init__(self): | ||
1490 | 367 | self._cleanup() | ||
1491 | 368 | self.documents = Documents() | ||
1492 | 369 | self.po4a = PO4A() | ||
1493 | 370 | self.po = PO(self.po4a) | ||
1494 | 371 | |||
1495 | 372 | def _cleanup(self): | ||
1496 | 373 | r = subprocess.Popen(['bzr', 'ignored'], stdout=subprocess.PIPE) | ||
1497 | 374 | fns = [os.path.join(PATH, '../..', f.split(' ')[0]) | ||
1498 | 375 | for f in codecs.decode(r.communicate()[0]).split('\n') | ||
1499 | 376 | if f.strip() != ''] | ||
1500 | 377 | fns = [f for f in fns if os.path.exists(f)] | ||
1501 | 378 | for f in fns: | ||
1502 | 379 | try: | ||
1503 | 380 | shutil.rmtree(f) | ||
1504 | 381 | except NotADirectoryError: | ||
1505 | 382 | os.remove(f) | ||
1506 | 383 | |||
1507 | 384 | def generate_pot_file(self): | ||
1508 | 385 | return self.po.generate_pot_file(self.documents) | ||
1509 | 386 | |||
1510 | 387 | def generate_translations(self): | ||
1511 | 388 | self.po.generate_fake_pofile() | ||
1512 | 389 | self.po.rewrite_links(self.documents) | ||
1513 | 390 | self.po.safeguard_meta_tags() | ||
1514 | 391 | self.documents.write_translated_markdown(self.po, self.po4a) | ||
1515 | 392 | 85 | ||
1516 | === renamed directory 'edit-here/po' => 'po' | |||
1517 | === modified file 'po/help.pot' | |||
1518 | --- edit-here/po/help.pot 2015-03-19 11:16:04 +0000 | |||
1519 | +++ po/help.pot 2015-03-19 17:24:01 +0000 | |||
1520 | @@ -2,12 +2,12 @@ | |||
1521 | 2 | # Copyright (C) YEAR Free Software Foundation, Inc. | 2 | # Copyright (C) YEAR Free Software Foundation, Inc. |
1522 | 3 | # This file is distributed under the same license as the PACKAGE package. | 3 | # This file is distributed under the same license as the PACKAGE package. |
1523 | 4 | # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR. | 4 | # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR. |
1525 | 5 | # | 5 | # |
1526 | 6 | #, fuzzy | 6 | #, fuzzy |
1527 | 7 | msgid "" | 7 | msgid "" |
1528 | 8 | msgstr "" | 8 | msgstr "" |
1529 | 9 | "Project-Id-Version: PACKAGE VERSION\n" | 9 | "Project-Id-Version: PACKAGE VERSION\n" |
1531 | 10 | "POT-Creation-Date: 2015-03-19 12:15+0100\n" | 10 | "POT-Creation-Date: 2015-03-19 15:41+0100\n" |
1532 | 11 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" | 11 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" |
1533 | 12 | "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" | 12 | "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" |
1534 | 13 | "Language-Team: LANGUAGE <LL@li.org>\n" | 13 | "Language-Team: LANGUAGE <LL@li.org>\n" |
1535 | @@ -18,7 +18,7 @@ | |||
1536 | 18 | 18 | ||
1537 | 19 | #. type: Plain text | 19 | #. type: Plain text |
1538 | 20 | #: content/pages/security.md:2 | 20 | #: content/pages/security.md:2 |
1540 | 21 | msgid "Security" | 21 | msgid "Title: Security" |
1541 | 22 | msgstr "" | 22 | msgstr "" |
1542 | 23 | 23 | ||
1543 | 24 | #. type: Plain text | 24 | #. type: Plain text |
1544 | @@ -28,16 +28,14 @@ | |||
1545 | 28 | msgstr "" | 28 | msgstr "" |
1546 | 29 | 29 | ||
1547 | 30 | #. type: Plain text | 30 | #. type: Plain text |
1551 | 31 | #: content/pages/security.md:6 content/pages/basic.md:6 | 31 | #: content/pages/security.md:6 content/pages/basic.md:6 content/pages/settings.md:6 content/pages/ui.md:6 content/pages/apps.md:7 content/pages/scopes.md:6 |
1549 | 32 | #: content/pages/settings.md:6 content/pages/ui.md:6 content/pages/apps.md:7 | ||
1550 | 33 | #: content/pages/scopes.md:6 | ||
1552 | 34 | msgid "[TOC]" | 32 | msgid "[TOC]" |
1553 | 35 | msgstr "" | 33 | msgstr "" |
1554 | 36 | 34 | ||
1556 | 37 | #. !!T | 35 | #. type: Title ### |
1557 | 38 | #: content/pages/security.md:7 | 36 | #: content/pages/security.md:7 |
1558 | 39 | #, no-wrap | 37 | #, no-wrap |
1560 | 40 | msgid "How do I lock the phone?" | 38 | msgid "How do I lock the phone? !!T" |
1561 | 41 | msgstr "" | 39 | msgstr "" |
1562 | 42 | 40 | ||
1563 | 43 | #. type: Plain text | 41 | #. type: Plain text |
1564 | @@ -49,10 +47,10 @@ | |||
1565 | 49 | "& Privacy*, then *Phone Locking* to adjust the *Lock when idle* setting." | 47 | "& Privacy*, then *Phone Locking* to adjust the *Lock when idle* setting." |
1566 | 50 | msgstr "" | 48 | msgstr "" |
1567 | 51 | 49 | ||
1569 | 52 | #. !!T | 50 | #. type: Title ### |
1570 | 53 | #: content/pages/security.md:10 | 51 | #: content/pages/security.md:10 |
1571 | 54 | #, no-wrap | 52 | #, no-wrap |
1573 | 55 | msgid "How do I unlock the phone?" | 53 | msgid "How do I unlock the phone? !!T" |
1574 | 56 | msgstr "" | 54 | msgstr "" |
1575 | 57 | 55 | ||
1576 | 58 | #. type: Plain text | 56 | #. type: Plain text |
1577 | @@ -62,10 +60,10 @@ | |||
1578 | 62 | "enabled, you might be required to enter a pin or passcode." | 60 | "enabled, you might be required to enter a pin or passcode." |
1579 | 63 | msgstr "" | 61 | msgstr "" |
1580 | 64 | 62 | ||
1582 | 65 | #. !!T | 63 | #. type: Title ### |
1583 | 66 | #: content/pages/security.md:13 | 64 | #: content/pages/security.md:13 |
1584 | 67 | #, no-wrap | 65 | #, no-wrap |
1586 | 68 | msgid "How do I unlock the bootloader?" | 66 | msgid "How do I unlock the bootloader? !!T" |
1587 | 69 | msgstr "" | 67 | msgstr "" |
1588 | 70 | 68 | ||
1589 | 71 | #. type: Plain text | 69 | #. type: Plain text |
1590 | @@ -75,10 +73,10 @@ | |||
1591 | 75 | "related tasks, see the [developer site](http://developer.ubuntu.com/)" | 73 | "related tasks, see the [developer site](http://developer.ubuntu.com/)" |
1592 | 76 | msgstr "" | 74 | msgstr "" |
1593 | 77 | 75 | ||
1595 | 78 | #. !!T | 76 | #. type: Title ### |
1596 | 79 | #: content/pages/security.md:16 | 77 | #: content/pages/security.md:16 |
1597 | 80 | #, no-wrap | 78 | #, no-wrap |
1599 | 81 | msgid "How can I change my PIN/Passcode?" | 79 | msgid "How can I change my PIN/Passcode? !!T" |
1600 | 82 | msgstr "" | 80 | msgstr "" |
1601 | 83 | 81 | ||
1602 | 84 | #. type: Plain text | 82 | #. type: Plain text |
1603 | @@ -88,12 +86,12 @@ | |||
1604 | 88 | "*Security & Privacy* to adjust the *Lock when idle* setting." | 86 | "*Security & Privacy* to adjust the *Lock when idle* setting." |
1605 | 89 | msgstr "" | 87 | msgstr "" |
1606 | 90 | 88 | ||
1608 | 91 | #. !!T | 89 | #. type: Title ### |
1609 | 92 | #: content/pages/security.md:19 | 90 | #: content/pages/security.md:19 |
1610 | 93 | #, no-wrap | 91 | #, no-wrap |
1611 | 94 | msgid "" | 92 | msgid "" |
1614 | 95 | "Why do I have to type my PIN when using File Manager & Terminal (not default" | 93 | "Why do I have to type my PIN when using File Manager & Terminal (not default " |
1615 | 96 | " apps)?" | 94 | "apps)? !!T" |
1616 | 97 | msgstr "" | 95 | msgstr "" |
1617 | 98 | 96 | ||
1618 | 99 | #. type: Plain text | 97 | #. type: Plain text |
1619 | @@ -103,11 +101,10 @@ | |||
1620 | 103 | "pin/passcode is required. This is for your phone security." | 101 | "pin/passcode is required. This is for your phone security." |
1621 | 104 | msgstr "" | 102 | msgstr "" |
1622 | 105 | 103 | ||
1624 | 106 | #. !!T | 104 | #. type: Title ### |
1625 | 107 | #: content/pages/security.md:22 | 105 | #: content/pages/security.md:22 |
1626 | 108 | #, no-wrap | 106 | #, no-wrap |
1629 | 109 | msgid "" | 107 | msgid "How can I stop someone using the indicators when the phone is unlocked? !!T" |
1628 | 110 | "How can I stop someone using the indicators when the phone is unlocked?" | ||
1630 | 111 | msgstr "" | 108 | msgstr "" |
1631 | 112 | 109 | ||
1632 | 113 | #. type: Plain text | 110 | #. type: Plain text |
1633 | @@ -119,10 +116,10 @@ | |||
1634 | 119 | "settings* option." | 116 | "settings* option." |
1635 | 120 | msgstr "" | 117 | msgstr "" |
1636 | 121 | 118 | ||
1638 | 122 | #. !!T | 119 | #. type: Title ### |
1639 | 123 | #: content/pages/security.md:25 | 120 | #: content/pages/security.md:25 |
1640 | 124 | #, no-wrap | 121 | #, no-wrap |
1642 | 125 | msgid "I forgot my password or passcode. How can I unlock the phone?" | 122 | msgid "I forgot my password or passcode. How can I unlock the phone? !!T" |
1643 | 126 | msgstr "" | 123 | msgstr "" |
1644 | 127 | 124 | ||
1645 | 128 | #. type: Plain text | 125 | #. type: Plain text |
1646 | @@ -132,7 +129,7 @@ | |||
1647 | 132 | 129 | ||
1648 | 133 | #. type: Plain text | 130 | #. type: Plain text |
1649 | 134 | #: content/pages/basic.md:2 | 131 | #: content/pages/basic.md:2 |
1651 | 135 | msgid "Basic tasks" | 132 | msgid "Title: Basic tasks" |
1652 | 136 | msgstr "" | 133 | msgstr "" |
1653 | 137 | 134 | ||
1654 | 138 | #. type: Plain text | 135 | #. type: Plain text |
1655 | @@ -141,37 +138,37 @@ | |||
1656 | 141 | msgid "*If you are wondering how to perform basic tasks, look here.*\n" | 138 | msgid "*If you are wondering how to perform basic tasks, look here.*\n" |
1657 | 142 | msgstr "" | 139 | msgstr "" |
1658 | 143 | 140 | ||
1660 | 144 | #. !!T | 141 | #. type: Title ### |
1661 | 145 | #: content/pages/basic.md:7 | 142 | #: content/pages/basic.md:7 |
1662 | 146 | #, no-wrap | 143 | #, no-wrap |
1664 | 147 | msgid "How do I play music?" | 144 | msgid "How do I play music? !!T" |
1665 | 148 | msgstr "" | 145 | msgstr "" |
1666 | 149 | 146 | ||
1667 | 150 | #. type: Plain text | 147 | #. type: Plain text |
1668 | 151 | #: content/pages/basic.md:9 | 148 | #: content/pages/basic.md:9 |
1669 | 152 | msgid "" | 149 | msgid "" |
1670 | 153 | "The music app let's you play music copied to the device. In addition, " | 150 | "The music app let's you play music copied to the device. In addition, " |
1673 | 154 | "[scopes]({filename}scopes.md) such as 7digital and Grooveshark can also play" | 151 | "[scopes]({filename}scopes.md) such as 7digital and Grooveshark can also play " |
1674 | 155 | " music." | 152 | "music." |
1675 | 156 | msgstr "" | 153 | msgstr "" |
1676 | 157 | 154 | ||
1678 | 158 | #. !!T | 155 | #. type: Title ### |
1679 | 159 | #: content/pages/basic.md:10 | 156 | #: content/pages/basic.md:10 |
1680 | 160 | #, no-wrap | 157 | #, no-wrap |
1682 | 161 | msgid "How do I play videos?" | 158 | msgid "How do I play videos? !!T" |
1683 | 162 | msgstr "" | 159 | msgstr "" |
1684 | 163 | 160 | ||
1685 | 164 | #. type: Plain text | 161 | #. type: Plain text |
1686 | 165 | #: content/pages/basic.md:12 | 162 | #: content/pages/basic.md:12 |
1687 | 166 | msgid "" | 163 | msgid "" |
1690 | 167 | "The media player application will play videos copied to the device. You will" | 164 | "The media player application will play videos copied to the device. You will " |
1691 | 168 | " also find applications like youtube that give you streaming options." | 165 | "also find applications like youtube that give you streaming options." |
1692 | 169 | msgstr "" | 166 | msgstr "" |
1693 | 170 | 167 | ||
1695 | 171 | #. !!T | 168 | #. type: Title ### |
1696 | 172 | #: content/pages/basic.md:13 | 169 | #: content/pages/basic.md:13 |
1697 | 173 | #, no-wrap | 170 | #, no-wrap |
1699 | 174 | msgid "How do I take photos?" | 171 | msgid "How do I take photos? !!T" |
1700 | 175 | msgstr "" | 172 | msgstr "" |
1701 | 176 | 173 | ||
1702 | 177 | #. type: Plain text | 174 | #. type: Plain text |
1703 | @@ -181,38 +178,38 @@ | |||
1704 | 181 | "has both a front and rear camera, you can toggle which camera to use." | 178 | "has both a front and rear camera, you can toggle which camera to use." |
1705 | 182 | msgstr "" | 179 | msgstr "" |
1706 | 183 | 180 | ||
1708 | 184 | #. !!T | 181 | #. type: Title ### |
1709 | 185 | #: content/pages/basic.md:16 | 182 | #: content/pages/basic.md:16 |
1710 | 186 | #, no-wrap | 183 | #, no-wrap |
1712 | 187 | msgid "How do I take a screenshot?" | 184 | msgid "How do I take a screenshot? !!T" |
1713 | 188 | msgstr "" | 185 | msgstr "" |
1714 | 189 | 186 | ||
1715 | 190 | #. type: Plain text | 187 | #. type: Plain text |
1716 | 191 | #: content/pages/basic.md:18 | 188 | #: content/pages/basic.md:18 |
1717 | 192 | msgid "" | 189 | msgid "" |
1718 | 193 | "Press the Volume Up and Volume Down buttons at the same time until you see " | 190 | "Press the Volume Up and Volume Down buttons at the same time until you see " |
1721 | 194 | "the screen flashing. The screenshot will capture what is on your screen, and" | 191 | "the screen flashing. The screenshot will capture what is on your screen, and " |
1722 | 195 | " you can see the resulting picture in the Gallery app or the Photos scope." | 192 | "you can see the resulting picture in the Gallery app or the Photos scope." |
1723 | 196 | msgstr "" | 193 | msgstr "" |
1724 | 197 | 194 | ||
1726 | 198 | #. !!T | 195 | #. type: Title ### |
1727 | 199 | #: content/pages/basic.md:19 | 196 | #: content/pages/basic.md:19 |
1728 | 200 | #, no-wrap | 197 | #, no-wrap |
1730 | 201 | msgid "How do I see pictures I’ve taken?" | 198 | msgid "How do I see pictures I’ve taken? !!T" |
1731 | 202 | msgstr "" | 199 | msgstr "" |
1732 | 203 | 200 | ||
1733 | 204 | #. type: Plain text | 201 | #. type: Plain text |
1734 | 205 | #: content/pages/basic.md:21 | 202 | #: content/pages/basic.md:21 |
1735 | 206 | msgid "" | 203 | msgid "" |
1738 | 207 | "If you've just taken a picture, you can see it easily by swiping to the left" | 204 | "If you've just taken a picture, you can see it easily by swiping to the left " |
1739 | 208 | " from the right edge inside the camera app. Alternatively, use the gallery " | 205 | "from the right edge inside the camera app. Alternatively, use the gallery " |
1740 | 209 | "app to find the picture." | 206 | "app to find the picture." |
1741 | 210 | msgstr "" | 207 | msgstr "" |
1742 | 211 | 208 | ||
1744 | 212 | #. !!T | 209 | #. type: Title ### |
1745 | 213 | #: content/pages/basic.md:22 | 210 | #: content/pages/basic.md:22 |
1746 | 214 | #, no-wrap | 211 | #, no-wrap |
1748 | 215 | msgid "How do I record videos?" | 212 | msgid "How do I record videos? !!T" |
1749 | 216 | msgstr "" | 213 | msgstr "" |
1750 | 217 | 214 | ||
1751 | 218 | #. type: Plain text | 215 | #. type: Plain text |
1752 | @@ -222,10 +219,10 @@ | |||
1753 | 222 | "has both a front and rear camera, you can toggle which camera to use." | 219 | "has both a front and rear camera, you can toggle which camera to use." |
1754 | 223 | msgstr "" | 220 | msgstr "" |
1755 | 224 | 221 | ||
1757 | 225 | #. !!T | 222 | #. type: Title ### |
1758 | 226 | #: content/pages/basic.md:25 | 223 | #: content/pages/basic.md:25 |
1759 | 227 | #, no-wrap | 224 | #, no-wrap |
1761 | 228 | msgid "How can I send a text?" | 225 | msgid "How can I send a text? !!T" |
1762 | 229 | msgstr "" | 226 | msgstr "" |
1763 | 230 | 227 | ||
1764 | 231 | #. type: Plain text | 228 | #. type: Plain text |
1765 | @@ -235,10 +232,10 @@ | |||
1766 | 235 | "messages." | 232 | "messages." |
1767 | 236 | msgstr "" | 233 | msgstr "" |
1768 | 237 | 234 | ||
1770 | 238 | #. !!T | 235 | #. type: Title ### |
1771 | 239 | #: content/pages/basic.md:28 | 236 | #: content/pages/basic.md:28 |
1772 | 240 | #, no-wrap | 237 | #, no-wrap |
1774 | 241 | msgid "How do I make a call?" | 238 | msgid "How do I make a call? !!T" |
1775 | 242 | msgstr "" | 239 | msgstr "" |
1776 | 243 | 240 | ||
1777 | 244 | #. type: Plain text | 241 | #. type: Plain text |
1778 | @@ -248,10 +245,10 @@ | |||
1779 | 248 | "number." | 245 | "number." |
1780 | 249 | msgstr "" | 246 | msgstr "" |
1781 | 250 | 247 | ||
1783 | 251 | #. !!T | 248 | #. type: Title ### |
1784 | 252 | #: content/pages/basic.md:31 | 249 | #: content/pages/basic.md:31 |
1785 | 253 | #, no-wrap | 250 | #, no-wrap |
1787 | 254 | msgid "How do I check recently made/missed calls?" | 251 | msgid "How do I check recently made/missed calls? !!T" |
1788 | 255 | msgstr "" | 252 | msgstr "" |
1789 | 256 | 253 | ||
1790 | 257 | #. type: Plain text | 254 | #. type: Plain text |
1791 | @@ -263,7 +260,7 @@ | |||
1792 | 263 | 260 | ||
1793 | 264 | #. type: Plain text | 261 | #. type: Plain text |
1794 | 265 | #: content/pages/faq.md:2 | 262 | #: content/pages/faq.md:2 |
1796 | 266 | msgid "Get your questions answered." | 263 | msgid "Title: Get your questions answered." |
1797 | 267 | msgstr "" | 264 | msgstr "" |
1798 | 268 | 265 | ||
1799 | 269 | #. type: Plain text | 266 | #. type: Plain text |
1800 | @@ -311,7 +308,7 @@ | |||
1801 | 311 | 308 | ||
1802 | 312 | #. type: Plain text | 309 | #. type: Plain text |
1803 | 313 | #: content/pages/index.md:2 | 310 | #: content/pages/index.md:2 |
1805 | 314 | msgid "Welcome" | 311 | msgid "Title: Welcome" |
1806 | 315 | msgstr "" | 312 | msgstr "" |
1807 | 316 | 313 | ||
1808 | 317 | #. type: Plain text | 314 | #. type: Plain text |
1809 | @@ -348,7 +345,7 @@ | |||
1810 | 348 | 345 | ||
1811 | 349 | #. type: Plain text | 346 | #. type: Plain text |
1812 | 350 | #: content/pages/settings.md:2 | 347 | #: content/pages/settings.md:2 |
1814 | 351 | msgid "Settings" | 348 | msgid "Title: Settings" |
1815 | 352 | msgstr "" | 349 | msgstr "" |
1816 | 353 | 350 | ||
1817 | 354 | #. type: Plain text | 351 | #. type: Plain text |
1818 | @@ -357,10 +354,10 @@ | |||
1819 | 357 | msgid "*How do I change my phone settings?*\n" | 354 | msgid "*How do I change my phone settings?*\n" |
1820 | 358 | msgstr "" | 355 | msgstr "" |
1821 | 359 | 356 | ||
1823 | 360 | #. !!T | 357 | #. type: Title ### |
1824 | 361 | #: content/pages/settings.md:7 | 358 | #: content/pages/settings.md:7 |
1825 | 362 | #, no-wrap | 359 | #, no-wrap |
1827 | 363 | msgid "How do I update my system?" | 360 | msgid "How do I update my system? !!T" |
1828 | 364 | msgstr "" | 361 | msgstr "" |
1829 | 365 | 362 | ||
1830 | 366 | #. type: Plain text | 363 | #. type: Plain text |
1831 | @@ -368,14 +365,14 @@ | |||
1832 | 368 | msgid "" | 365 | msgid "" |
1833 | 369 | "Your device will prompt you when an update is ready. A notification will " | 366 | "Your device will prompt you when an update is ready. A notification will " |
1834 | 370 | "appear informing you of the new update. If you wish, you can manually check " | 367 | "appear informing you of the new update. If you wish, you can manually check " |
1837 | 371 | "and perform an update yourself. Open the *System Settings* application. " | 368 | "and perform an update yourself. Open the *System Settings* " |
1838 | 372 | "Select *Update*, and then click the check for updates button." | 369 | "application. Select *Update*, and then click the check for updates button." |
1839 | 373 | msgstr "" | 370 | msgstr "" |
1840 | 374 | 371 | ||
1842 | 375 | #. !!T | 372 | #. type: Title ### |
1843 | 376 | #: content/pages/settings.md:10 | 373 | #: content/pages/settings.md:10 |
1844 | 377 | #, no-wrap | 374 | #, no-wrap |
1846 | 378 | msgid "How do I set the time / language?" | 375 | msgid "How do I set the time / language? !!T" |
1847 | 379 | msgstr "" | 376 | msgstr "" |
1848 | 380 | 377 | ||
1849 | 381 | #. type: Plain text | 378 | #. type: Plain text |
1850 | @@ -386,23 +383,24 @@ | |||
1851 | 386 | "" | 383 | "" |
1852 | 387 | msgstr "" | 384 | msgstr "" |
1853 | 388 | 385 | ||
1855 | 389 | #. !!T | 386 | #. type: Title ### |
1856 | 390 | #: content/pages/settings.md:14 | 387 | #: content/pages/settings.md:14 |
1857 | 391 | #, no-wrap | 388 | #, no-wrap |
1859 | 392 | msgid "How can I change my wallpaper/background?" | 389 | msgid "How can I change my wallpaper/background? !!T" |
1860 | 393 | msgstr "" | 390 | msgstr "" |
1861 | 394 | 391 | ||
1862 | 395 | #. type: Plain text | 392 | #. type: Plain text |
1863 | 396 | #: content/pages/settings.md:16 | 393 | #: content/pages/settings.md:16 |
1864 | 397 | msgid "" | 394 | msgid "" |
1867 | 398 | "Open the *System Settings* application. Select the *Background* option. " | 395 | "Open the *System Settings* application. Select the *Background* " |
1868 | 399 | "Press the *Add Image* button and choice your image to set as a background." | 396 | "option. Press the *Add Image* button and choice your image to set as a " |
1869 | 397 | "background." | ||
1870 | 400 | msgstr "" | 398 | msgstr "" |
1871 | 401 | 399 | ||
1873 | 402 | #. !!T | 400 | #. type: Title ### |
1874 | 403 | #: content/pages/settings.md:17 | 401 | #: content/pages/settings.md:17 |
1875 | 404 | #, no-wrap | 402 | #, no-wrap |
1877 | 405 | msgid "How do I keep the screen on?" | 403 | msgid "How do I keep the screen on? !!T" |
1878 | 406 | msgstr "" | 404 | msgstr "" |
1879 | 407 | 405 | ||
1880 | 408 | #. type: Plain text | 406 | #. type: Plain text |
1881 | @@ -412,26 +410,26 @@ | |||
1882 | 412 | "option. Select the *Lock Phone* option, and then *Lock when idle*. St" | 410 | "option. Select the *Lock Phone* option, and then *Lock when idle*. St" |
1883 | 413 | msgstr "" | 411 | msgstr "" |
1884 | 414 | 412 | ||
1886 | 415 | #. !!T | 413 | #. type: Title ### |
1887 | 416 | #: content/pages/settings.md:20 | 414 | #: content/pages/settings.md:20 |
1888 | 417 | #, no-wrap | 415 | #, no-wrap |
1890 | 418 | msgid "How do I set up my accounts?" | 416 | msgid "How do I set up my accounts? !!T" |
1891 | 419 | msgstr "" | 417 | msgstr "" |
1892 | 420 | 418 | ||
1893 | 421 | #. type: Plain text | 419 | #. type: Plain text |
1894 | 422 | #: content/pages/settings.md:22 | 420 | #: content/pages/settings.md:22 |
1895 | 423 | msgid "" | 421 | msgid "" |
1898 | 424 | "You can set up some of your accounts from the scopes. Today scope allows you" | 422 | "You can set up some of your accounts from the scopes. Today scope allows you " |
1899 | 425 | " to configure your Google and Fitbit account, while the Pictures scope lets " | 423 | "to configure your Google and Fitbit account, while the Pictures scope lets " |
1900 | 426 | "you configure your flickr, Facebook and Instagram account. You can manage " | 424 | "you configure your flickr, Facebook and Instagram account. You can manage " |
1901 | 427 | "all your accounts (including social media, email, etc) from the *System " | 425 | "all your accounts (including social media, email, etc) from the *System " |
1902 | 428 | "Settings* app, under *Personal*, *Accounts*." | 426 | "Settings* app, under *Personal*, *Accounts*." |
1903 | 429 | msgstr "" | 427 | msgstr "" |
1904 | 430 | 428 | ||
1906 | 431 | #. !!T | 429 | #. type: Title ### |
1907 | 432 | #: content/pages/settings.md:23 | 430 | #: content/pages/settings.md:23 |
1908 | 433 | #, no-wrap | 431 | #, no-wrap |
1910 | 434 | msgid "How do I configure my notifications?" | 432 | msgid "How do I configure my notifications? !!T" |
1911 | 435 | msgstr "" | 433 | msgstr "" |
1912 | 436 | 434 | ||
1913 | 437 | #. type: Plain text | 435 | #. type: Plain text |
1914 | @@ -443,10 +441,10 @@ | |||
1915 | 443 | "notifications from any application on your device." | 441 | "notifications from any application on your device." |
1916 | 444 | msgstr "" | 442 | msgstr "" |
1917 | 445 | 443 | ||
1919 | 446 | #. !!T | 444 | #. type: Title ### |
1920 | 447 | #: content/pages/settings.md:26 | 445 | #: content/pages/settings.md:26 |
1921 | 448 | #, no-wrap | 446 | #, no-wrap |
1923 | 449 | msgid "How do I change the ringtone for calls and texts?" | 447 | msgid "How do I change the ringtone for calls and texts? !!T" |
1924 | 450 | msgstr "" | 448 | msgstr "" |
1925 | 451 | 449 | ||
1926 | 452 | #. type: Plain text | 450 | #. type: Plain text |
1927 | @@ -460,36 +458,36 @@ | |||
1928 | 460 | 458 | ||
1929 | 461 | #. type: Plain text | 459 | #. type: Plain text |
1930 | 462 | #: content/pages/ui.md:2 | 460 | #: content/pages/ui.md:2 |
1932 | 463 | msgid "User Interface" | 461 | msgid "Title: User Interface" |
1933 | 464 | msgstr "" | 462 | msgstr "" |
1934 | 465 | 463 | ||
1935 | 466 | #. type: Plain text | 464 | #. type: Plain text |
1936 | 467 | #: content/pages/ui.md:4 | 465 | #: content/pages/ui.md:4 |
1937 | 468 | #, no-wrap | 466 | #, no-wrap |
1938 | 469 | msgid "" | 467 | msgid "" |
1941 | 470 | "*Are you wondering about the dash, scopes, swiping? You've come to the right" | 468 | "*Are you wondering about the dash, scopes, swiping? You've come to the right " |
1942 | 471 | " place!*\n" | 469 | "place!*\n" |
1943 | 472 | msgstr "" | 470 | msgstr "" |
1944 | 473 | 471 | ||
1946 | 474 | #. !!T | 472 | #. type: Title ### |
1947 | 475 | #: content/pages/ui.md:7 | 473 | #: content/pages/ui.md:7 |
1948 | 476 | #, no-wrap | 474 | #, no-wrap |
1950 | 477 | msgid "What is the dash?" | 475 | msgid "What is the dash? !!T" |
1951 | 478 | msgstr "" | 476 | msgstr "" |
1952 | 479 | 477 | ||
1953 | 480 | #. type: Plain text | 478 | #. type: Plain text |
1954 | 481 | #: content/pages/ui.md:9 | 479 | #: content/pages/ui.md:9 |
1955 | 482 | msgid "" | 480 | msgid "" |
1958 | 483 | "The dash contains a list of applications installed on the device, along with" | 481 | "The dash contains a list of applications installed on the device, along with " |
1959 | 484 | " presenting the scopes and store. The dash is the first thing you see when " | 482 | "presenting the scopes and store. The dash is the first thing you see when " |
1960 | 485 | "booting the phone. You can switch to it again at any time by swiping left " | 483 | "booting the phone. You can switch to it again at any time by swiping left " |
1961 | 486 | "from the right screen edge." | 484 | "from the right screen edge." |
1962 | 487 | msgstr "" | 485 | msgstr "" |
1963 | 488 | 486 | ||
1965 | 489 | #. !!T | 487 | #. type: Title ### |
1966 | 490 | #: content/pages/ui.md:10 | 488 | #: content/pages/ui.md:10 |
1967 | 491 | #, no-wrap | 489 | #, no-wrap |
1969 | 492 | msgid "What is the launcher?" | 490 | msgid "What is the launcher? !!T" |
1970 | 493 | msgstr "" | 491 | msgstr "" |
1971 | 494 | 492 | ||
1972 | 495 | #. type: Plain text | 493 | #. type: Plain text |
1973 | @@ -499,10 +497,10 @@ | |||
1974 | 499 | "the launcher at any time by swiping right from the left screen edge." | 497 | "the launcher at any time by swiping right from the left screen edge." |
1975 | 500 | msgstr "" | 498 | msgstr "" |
1976 | 501 | 499 | ||
1978 | 502 | #. !!T | 500 | #. type: Title ### |
1979 | 503 | #: content/pages/ui.md:13 | 501 | #: content/pages/ui.md:13 |
1980 | 504 | #, no-wrap | 502 | #, no-wrap |
1982 | 505 | msgid "How can I customize the launcher?" | 503 | msgid "How can I customize the launcher? !!T" |
1983 | 506 | msgstr "" | 504 | msgstr "" |
1984 | 507 | 505 | ||
1985 | 508 | #. type: Plain text | 506 | #. type: Plain text |
1986 | @@ -515,69 +513,68 @@ | |||
1987 | 515 | "in." | 513 | "in." |
1988 | 516 | msgstr "" | 514 | msgstr "" |
1989 | 517 | 515 | ||
1991 | 518 | #. !!T | 516 | #. type: Title ### |
1992 | 519 | #: content/pages/ui.md:17 | 517 | #: content/pages/ui.md:17 |
1993 | 520 | #, no-wrap | 518 | #, no-wrap |
1995 | 521 | msgid "What are the indicators?" | 519 | msgid "What are the indicators? !!T" |
1996 | 522 | msgstr "" | 520 | msgstr "" |
1997 | 523 | 521 | ||
1998 | 524 | #. type: Plain text | 522 | #. type: Plain text |
1999 | 525 | #: content/pages/ui.md:19 | 523 | #: content/pages/ui.md:19 |
2000 | 526 | msgid "" | 524 | msgid "" |
2003 | 527 | "Indicators convey quick useful information about your device, like the time," | 525 | "Indicators convey quick useful information about your device, like the time, " |
2004 | 528 | " data connection, location, sound, and notifications. You can access the " | 526 | "data connection, location, sound, and notifications. You can access the " |
2005 | 529 | "indicators at any time by swiping down from the top screen edge." | 527 | "indicators at any time by swiping down from the top screen edge." |
2006 | 530 | msgstr "" | 528 | msgstr "" |
2007 | 531 | 529 | ||
2009 | 532 | #. !!T | 530 | #. type: Title ### |
2010 | 533 | #: content/pages/ui.md:20 | 531 | #: content/pages/ui.md:20 |
2011 | 534 | #, no-wrap | 532 | #, no-wrap |
2013 | 535 | msgid "How do I switch applications?" | 533 | msgid "How do I switch applications? !!T" |
2014 | 536 | msgstr "" | 534 | msgstr "" |
2015 | 537 | 535 | ||
2016 | 538 | #. type: Plain text | 536 | #. type: Plain text |
2017 | 539 | #: content/pages/ui.md:23 | 537 | #: content/pages/ui.md:23 |
2018 | 540 | msgid "" | 538 | msgid "" |
2019 | 541 | "To switch applications, slide your finger left from the right edge of the " | 539 | "To switch applications, slide your finger left from the right edge of the " |
2024 | 542 | "screen. If you slide quickly you will cycle through each application. " | 540 | "screen. If you slide quickly you will cycle through each " |
2025 | 543 | "However, if you slide more slowly, an application switcher will appear " | 541 | "application. However, if you slide more slowly, an application switcher will " |
2026 | 544 | "allowing you to select the application you wish to switch to, including the " | 542 | "appear allowing you to select the application you wish to switch to, " |
2027 | 545 | "dash." | 543 | "including the dash." |
2028 | 546 | msgstr "" | 544 | msgstr "" |
2029 | 547 | 545 | ||
2031 | 548 | #. !!T | 546 | #. type: Title ### |
2032 | 549 | #: content/pages/ui.md:24 | 547 | #: content/pages/ui.md:24 |
2033 | 550 | #, no-wrap | 548 | #, no-wrap |
2035 | 551 | msgid "How do I close applications?" | 549 | msgid "How do I close applications? !!T" |
2036 | 552 | msgstr "" | 550 | msgstr "" |
2037 | 553 | 551 | ||
2038 | 554 | #. type: Plain text | 552 | #. type: Plain text |
2039 | 555 | #: content/pages/ui.md:26 | 553 | #: content/pages/ui.md:26 |
2040 | 556 | msgid "" | 554 | msgid "" |
2045 | 557 | "To close an application, slide your finger *slowly* left from the right edge" | 555 | "To close an application, slide your finger *slowly* left from the right edge " |
2046 | 558 | " of the screen. An application switcher will appear. Place your finger on " | 556 | "of the screen. An application switcher will appear. Place your finger on the " |
2047 | 559 | "the application preview you wish to close and swipe up or down. The " | 557 | "application preview you wish to close and swipe up or down. The application " |
2048 | 560 | "application will disappear." | 558 | "will disappear." |
2049 | 561 | msgstr "" | 559 | msgstr "" |
2050 | 562 | 560 | ||
2052 | 563 | #. !!T | 561 | #. type: Title ### |
2053 | 564 | #: content/pages/ui.md:27 | 562 | #: content/pages/ui.md:27 |
2054 | 565 | #, no-wrap | 563 | #, no-wrap |
2056 | 566 | msgid "How can I copy and paste?" | 564 | msgid "How can I copy and paste? !!T" |
2057 | 567 | msgstr "" | 565 | msgstr "" |
2058 | 568 | 566 | ||
2059 | 569 | #. type: Plain text | 567 | #. type: Plain text |
2060 | 570 | #: content/pages/ui.md:29 | 568 | #: content/pages/ui.md:29 |
2061 | 571 | msgid "" | 569 | msgid "" |
2064 | 572 | "For text that can be copied and pasted, press and hold the text in question." | 570 | "For text that can be copied and pasted, press and hold the text in " |
2065 | 573 | " A menu will appear allowing you to cut, copy and paste." | 571 | "question. A menu will appear allowing you to cut, copy and paste." |
2066 | 574 | msgstr "" | 572 | msgstr "" |
2067 | 575 | 573 | ||
2069 | 576 | #. !!T | 574 | #. type: Title ### |
2070 | 577 | #: content/pages/ui.md:30 | 575 | #: content/pages/ui.md:30 |
2071 | 578 | #, no-wrap | 576 | #, no-wrap |
2074 | 579 | msgid "" | 577 | msgid "What are the small characters on the keyboard and how can I select them? !!T" |
2073 | 580 | "What are the small characters on the keyboard and how can I select them?" | ||
2075 | 581 | msgstr "" | 578 | msgstr "" |
2076 | 582 | 579 | ||
2077 | 583 | #. type: Plain text | 580 | #. type: Plain text |
2078 | @@ -588,10 +585,10 @@ | |||
2079 | 588 | "numbers and accented characters. Give it a try!" | 585 | "numbers and accented characters. Give it a try!" |
2080 | 589 | msgstr "" | 586 | msgstr "" |
2081 | 590 | 587 | ||
2083 | 591 | #. !!T | 588 | #. type: Title ### |
2084 | 592 | #: content/pages/ui.md:33 | 589 | #: content/pages/ui.md:33 |
2085 | 593 | #, no-wrap | 590 | #, no-wrap |
2087 | 594 | msgid "The keyboard behaves funny. What can I do about it?" | 591 | msgid "The keyboard behaves funny. What can I do about it? !!T" |
2088 | 595 | msgstr "" | 592 | msgstr "" |
2089 | 596 | 593 | ||
2090 | 597 | #. type: Plain text | 594 | #. type: Plain text |
2091 | @@ -603,10 +600,10 @@ | |||
2092 | 603 | "control of the input." | 600 | "control of the input." |
2093 | 604 | msgstr "" | 601 | msgstr "" |
2094 | 605 | 602 | ||
2096 | 606 | #. !!T | 603 | #. type: Title ### |
2097 | 607 | #: content/pages/ui.md:36 | 604 | #: content/pages/ui.md:36 |
2098 | 608 | #, no-wrap | 605 | #, no-wrap |
2100 | 609 | msgid "How can I add a new keyboard language?" | 606 | msgid "How can I add a new keyboard language? !!T" |
2101 | 610 | msgstr "" | 607 | msgstr "" |
2102 | 611 | 608 | ||
2103 | 612 | #. type: Plain text | 609 | #. type: Plain text |
2104 | @@ -616,14 +613,14 @@ | |||
2105 | 616 | "that might not be available in other keyboards and (if available) use word " | 613 | "that might not be available in other keyboards and (if available) use word " |
2106 | 617 | "prediction and spell checking for that new language. Go to the Settings " | 614 | "prediction and spell checking for that new language. Go to the Settings " |
2107 | 618 | "app, tap on Language/Text and then on Keyboard layouts. From there, you can " | 615 | "app, tap on Language/Text and then on Keyboard layouts. From there, you can " |
2110 | 619 | "then tick the keyboard you want to add and then [use it](#how-can-i-switch-" | 616 | "then tick the keyboard you want to add and then [use " |
2111 | 620 | "between-keyboard-languages)" | 617 | "it](#how-can-i-switch-between-keyboard-languages)" |
2112 | 621 | msgstr "" | 618 | msgstr "" |
2113 | 622 | 619 | ||
2115 | 623 | #. !!T | 620 | #. type: Title ### |
2116 | 624 | #: content/pages/ui.md:40 | 621 | #: content/pages/ui.md:40 |
2117 | 625 | #, no-wrap | 622 | #, no-wrap |
2119 | 626 | msgid "How can I switch between keyboard languages?" | 623 | msgid "How can I switch between keyboard languages? !!T" |
2120 | 627 | msgstr "" | 624 | msgstr "" |
2121 | 628 | 625 | ||
2122 | 629 | #. type: Plain text | 626 | #. type: Plain text |
2123 | @@ -636,42 +633,43 @@ | |||
2124 | 636 | "activated it in the Settings app](#how-can-i-add-a-new-keyboard-language)." | 633 | "activated it in the Settings app](#how-can-i-add-a-new-keyboard-language)." |
2125 | 637 | msgstr "" | 634 | msgstr "" |
2126 | 638 | 635 | ||
2128 | 639 | #. !!T | 636 | #. type: Title ### |
2129 | 640 | #: content/pages/ui.md:44 | 637 | #: content/pages/ui.md:44 |
2130 | 641 | #, no-wrap | 638 | #, no-wrap |
2132 | 642 | msgid "How can I type Emoji icons?" | 639 | msgid "How can I type Emoji icons? !!T" |
2133 | 643 | msgstr "" | 640 | msgstr "" |
2134 | 644 | 641 | ||
2135 | 645 | #. type: Plain text | 642 | #. type: Plain text |
2136 | 646 | #: content/pages/ui.md:47 | 643 | #: content/pages/ui.md:47 |
2137 | 647 | msgid "" | 644 | msgid "" |
2143 | 648 | "[Learn wow to switch between keyboard languages](#how-can-i-switch-between-" | 645 | "[Learn wow to switch between keyboard " |
2144 | 649 | "keyboard-languages) to alternate between the regular keyboard and the Emoji " | 646 | "languages](#how-can-i-switch-between-keyboard-languages) to alternate " |
2145 | 650 | "keyboard. Once done with typing Emojis, you can switch back to the regular " | 647 | "between the regular keyboard and the Emoji keyboard. Once done with typing " |
2146 | 651 | "keyboard. If you can't see the Emoji keyboard, make sure you've [activated " | 648 | "Emojis, you can switch back to the regular keyboard. If you can't see the " |
2147 | 652 | "it in the Settings app](#how-can-i-add-a-new-keyboard-language)." | 649 | "Emoji keyboard, make sure you've [activated it in the Settings " |
2148 | 650 | "app](#how-can-i-add-a-new-keyboard-language)." | ||
2149 | 653 | msgstr "" | 651 | msgstr "" |
2150 | 654 | 652 | ||
2152 | 655 | #. !!T | 653 | #. type: Title ### |
2153 | 656 | #: content/pages/ui.md:48 | 654 | #: content/pages/ui.md:48 |
2154 | 657 | #, no-wrap | 655 | #, no-wrap |
2155 | 658 | msgid "" | 656 | msgid "" |
2158 | 659 | "What is the round circle on the welcome screen for? What does it show? Can I" | 657 | "What is the round circle on the welcome screen for? What does it show? Can I " |
2159 | 660 | " configure it?" | 658 | "configure it? !!T" |
2160 | 661 | msgstr "" | 659 | msgstr "" |
2161 | 662 | 660 | ||
2162 | 663 | #. type: Plain text | 661 | #. type: Plain text |
2163 | 664 | #: content/pages/ui.md:49 | 662 | #: content/pages/ui.md:49 |
2164 | 665 | msgid "" | 663 | msgid "" |
2167 | 666 | "The round circle is the infographic. It hows you recent phone activity, like" | 664 | "The round circle is the infographic. It hows you recent phone activity, like " |
2168 | 667 | " the number of messages received or the number of songs played. You can " | 665 | "the number of messages received or the number of songs played. You can " |
2169 | 668 | "disable it by launching the *Settings* app, navigating to *Security and " | 666 | "disable it by launching the *Settings* app, navigating to *Security and " |
2170 | 669 | "privacy* and unticking *Stats on Welcome screen*." | 667 | "privacy* and unticking *Stats on Welcome screen*." |
2171 | 670 | msgstr "" | 668 | msgstr "" |
2172 | 671 | 669 | ||
2173 | 672 | #. type: Plain text | 670 | #. type: Plain text |
2174 | 673 | #: content/pages/apps.md:2 | 671 | #: content/pages/apps.md:2 |
2176 | 674 | msgid "Apps" | 672 | msgid "Title: Apps" |
2177 | 675 | msgstr "" | 673 | msgstr "" |
2178 | 676 | 674 | ||
2179 | 677 | #. type: Plain text | 675 | #. type: Plain text |
2180 | @@ -688,24 +686,24 @@ | |||
2181 | 688 | msgid "The Store" | 686 | msgid "The Store" |
2182 | 689 | msgstr "" | 687 | msgstr "" |
2183 | 690 | 688 | ||
2185 | 691 | #. !!T | 689 | #. type: Title ### |
2186 | 692 | #: content/pages/apps.md:10 | 690 | #: content/pages/apps.md:10 |
2187 | 693 | #, no-wrap | 691 | #, no-wrap |
2189 | 694 | msgid "How do I find and install new scopes and applications?" | 692 | msgid "How do I find and install new scopes and applications? !!T" |
2190 | 695 | msgstr "" | 693 | msgstr "" |
2191 | 696 | 694 | ||
2192 | 697 | #. type: Plain text | 695 | #. type: Plain text |
2193 | 698 | #: content/pages/apps.md:12 | 696 | #: content/pages/apps.md:12 |
2194 | 699 | msgid "" | 697 | msgid "" |
2195 | 700 | "From the Apps scope, you can either tap on the “search” icon on the right " | 698 | "From the Apps scope, you can either tap on the “search” icon on the right " |
2198 | 701 | "and start searching by name, or you can go all the way down in the scope and" | 699 | "and start searching by name, or you can go all the way down in the scope and " |
2199 | 702 | " tap on the Ubuntu Store icon." | 700 | "tap on the Ubuntu Store icon." |
2200 | 703 | msgstr "" | 701 | msgstr "" |
2201 | 704 | 702 | ||
2203 | 705 | #. !!T | 703 | #. type: Title ### |
2204 | 706 | #: content/pages/apps.md:13 | 704 | #: content/pages/apps.md:13 |
2205 | 707 | #, no-wrap | 705 | #, no-wrap |
2207 | 708 | msgid "How can I browse the store from my PC?" | 706 | msgid "How can I browse the store from my PC? !!T" |
2208 | 709 | msgstr "" | 707 | msgstr "" |
2209 | 710 | 708 | ||
2210 | 711 | #. type: Plain text | 709 | #. type: Plain text |
2211 | @@ -716,19 +714,19 @@ | |||
2212 | 716 | "store](https://appstore.bhdouglass.com/apps)." | 714 | "store](https://appstore.bhdouglass.com/apps)." |
2213 | 717 | msgstr "" | 715 | msgstr "" |
2214 | 718 | 716 | ||
2216 | 719 | #. !!T | 717 | #. type: Title ### |
2217 | 720 | #: content/pages/apps.md:16 | 718 | #: content/pages/apps.md:16 |
2218 | 721 | #, no-wrap | 719 | #, no-wrap |
2220 | 722 | msgid "How do I remove scopes and applications?" | 720 | msgid "How do I remove scopes and applications? !!T" |
2221 | 723 | msgstr "" | 721 | msgstr "" |
2222 | 724 | 722 | ||
2223 | 725 | #. type: Plain text | 723 | #. type: Plain text |
2224 | 726 | #: content/pages/apps.md:18 | 724 | #: content/pages/apps.md:18 |
2225 | 727 | msgid "" | 725 | msgid "" |
2230 | 728 | "Search for the scope or application you wish to remove inside the store. " | 726 | "Search for the scope or application you wish to remove inside the " |
2231 | 729 | "Open it and press the *Uninstall* button to remove the application. " | 727 | "store. Open it and press the *Uninstall* button to remove the " |
2232 | 730 | "Alternatively, for applications you can also long-press their icons on the " | 728 | "application. Alternatively, for applications you can also long-press their " |
2233 | 731 | "dash to show their store page and the *Uninstall* button." | 729 | "icons on the dash to show their store page and the *Uninstall* button." |
2234 | 732 | msgstr "" | 730 | msgstr "" |
2235 | 733 | 731 | ||
2236 | 734 | #. type: Title ## | 732 | #. type: Title ## |
2237 | @@ -737,10 +735,10 @@ | |||
2238 | 737 | msgid "Misc" | 735 | msgid "Misc" |
2239 | 738 | msgstr "" | 736 | msgstr "" |
2240 | 739 | 737 | ||
2242 | 740 | #. !!T | 738 | #. type: Title ### |
2243 | 741 | #: content/pages/apps.md:21 | 739 | #: content/pages/apps.md:21 |
2244 | 742 | #, no-wrap | 740 | #, no-wrap |
2246 | 743 | msgid "Do you have Spotify?" | 741 | msgid "Do you have Spotify? !!T" |
2247 | 744 | msgstr "" | 742 | msgstr "" |
2248 | 745 | 743 | ||
2249 | 746 | #. type: Plain text | 744 | #. type: Plain text |
2250 | @@ -750,10 +748,10 @@ | |||
2251 | 750 | "([video](https://www.youtube.com/watch?v=ea90rwK_VuI))." | 748 | "([video](https://www.youtube.com/watch?v=ea90rwK_VuI))." |
2252 | 751 | msgstr "" | 749 | msgstr "" |
2253 | 752 | 750 | ||
2255 | 753 | #. !!T | 751 | #. type: Title ### |
2256 | 754 | #: content/pages/apps.md:25 | 752 | #: content/pages/apps.md:25 |
2257 | 755 | #, no-wrap | 753 | #, no-wrap |
2259 | 756 | msgid "Do you have Google Authenticator?" | 754 | msgid "Do you have Google Authenticator? !!T" |
2260 | 757 | msgstr "" | 755 | msgstr "" |
2261 | 758 | 756 | ||
2262 | 759 | #. type: Plain text | 757 | #. type: Plain text |
2263 | @@ -767,27 +765,27 @@ | |||
2264 | 767 | msgid "Music" | 765 | msgid "Music" |
2265 | 768 | msgstr "" | 766 | msgstr "" |
2266 | 769 | 767 | ||
2268 | 770 | #. !!T | 768 | #. type: Title ### |
2269 | 771 | #: content/pages/apps.md:30 | 769 | #: content/pages/apps.md:30 |
2270 | 772 | #, no-wrap | 770 | #, no-wrap |
2272 | 773 | msgid "How do I add music to my device?" | 771 | msgid "How do I add music to my device? !!T" |
2273 | 774 | msgstr "" | 772 | msgstr "" |
2274 | 775 | 773 | ||
2275 | 776 | #. type: Plain text | 774 | #. type: Plain text |
2276 | 777 | #: content/pages/apps.md:32 | 775 | #: content/pages/apps.md:32 |
2277 | 778 | msgid "" | 776 | msgid "" |
2278 | 779 | "You can add music in multiple ways. If you have pre-existing music files, " | 777 | "You can add music in multiple ways. If you have pre-existing music files, " |
2281 | 780 | "simply connect your phone to your pc via the usb cable. Next, copy the music" | 778 | "simply connect your phone to your pc via the usb cable. Next, copy the music " |
2282 | 781 | " you wish to listen to to the *Music* folder. Your music will appear in the " | 779 | "you wish to listen to to the *Music* folder. Your music will appear in the " |
2283 | 782 | "music app. Alternatively, you can acquire music directly using the device " | 780 | "music app. Alternatively, you can acquire music directly using the device " |
2284 | 783 | "via a scope, such as grooveshark or by downloading via the browser or " | 781 | "via a scope, such as grooveshark or by downloading via the browser or " |
2285 | 784 | "another application." | 782 | "another application." |
2286 | 785 | msgstr "" | 783 | msgstr "" |
2287 | 786 | 784 | ||
2289 | 787 | #. !!T | 785 | #. type: Title ### |
2290 | 788 | #: content/pages/apps.md:33 | 786 | #: content/pages/apps.md:33 |
2291 | 789 | #, no-wrap | 787 | #, no-wrap |
2293 | 790 | msgid "What music formats are supported?" | 788 | msgid "What music formats are supported? !!T" |
2294 | 791 | msgstr "" | 789 | msgstr "" |
2295 | 792 | 790 | ||
2296 | 793 | #. type: Plain text | 791 | #. type: Plain text |
2297 | @@ -795,10 +793,10 @@ | |||
2298 | 795 | msgid "The music app supports OGG, FLAG and MP3 formats." | 793 | msgid "The music app supports OGG, FLAG and MP3 formats." |
2299 | 796 | msgstr "" | 794 | msgstr "" |
2300 | 797 | 795 | ||
2302 | 798 | #. !!T | 796 | #. type: Title ### |
2303 | 799 | #: content/pages/apps.md:36 | 797 | #: content/pages/apps.md:36 |
2304 | 800 | #, no-wrap | 798 | #, no-wrap |
2306 | 801 | msgid "How do I listen to podcasts? !" | 799 | msgid "How do I listen to podcasts? !!!T" |
2307 | 802 | msgstr "" | 800 | msgstr "" |
2308 | 803 | 801 | ||
2309 | 804 | #. type: Plain text | 802 | #. type: Plain text |
2310 | @@ -814,20 +812,20 @@ | |||
2311 | 814 | msgid "Contacts" | 812 | msgid "Contacts" |
2312 | 815 | msgstr "" | 813 | msgstr "" |
2313 | 816 | 814 | ||
2315 | 817 | #. !!T | 815 | #. type: Title ### |
2316 | 818 | #: content/pages/apps.md:41 | 816 | #: content/pages/apps.md:41 |
2317 | 819 | #, no-wrap | 817 | #, no-wrap |
2319 | 820 | msgid "How can I sync my Google contacts to my device?" | 818 | msgid "How can I sync my Google contacts to my device? !!T" |
2320 | 821 | msgstr "" | 819 | msgstr "" |
2321 | 822 | 820 | ||
2322 | 823 | #. type: Plain text | 821 | #. type: Plain text |
2323 | 824 | #: content/pages/apps.md:43 | 822 | #: content/pages/apps.md:43 |
2324 | 825 | msgid "" | 823 | msgid "" |
2330 | 826 | "The first time you open the Contacts app you’ll be asked if you want to sync" | 824 | "The first time you open the Contacts app you’ll be asked if you want to sync " |
2331 | 827 | " contacts with your Google account. If you have answered “no” but change " | 825 | "contacts with your Google account. If you have answered “no” but change your " |
2332 | 828 | "your mind later, you can do so by going to the Today scope, and setting up " | 826 | "mind later, you can do so by going to the Today scope, and setting up your " |
2333 | 829 | "your Google account there. After that you can sync your contacts (and, if " | 827 | "Google account there. After that you can sync your contacts (and, if you " |
2334 | 830 | "you want, calendar events as well)." | 828 | "want, calendar events as well)." |
2335 | 831 | msgstr "" | 829 | msgstr "" |
2336 | 832 | 830 | ||
2337 | 833 | #. type: Title ## | 831 | #. type: Title ## |
2338 | @@ -836,10 +834,10 @@ | |||
2339 | 836 | msgid "Gallery" | 834 | msgid "Gallery" |
2340 | 837 | msgstr "" | 835 | msgstr "" |
2341 | 838 | 836 | ||
2343 | 839 | #. !!T | 837 | #. type: Title ### |
2344 | 840 | #: content/pages/apps.md:46 | 838 | #: content/pages/apps.md:46 |
2345 | 841 | #, no-wrap | 839 | #, no-wrap |
2347 | 842 | msgid "How can I share photos?" | 840 | msgid "How can I share photos? !!T" |
2348 | 843 | msgstr "" | 841 | msgstr "" |
2349 | 844 | 842 | ||
2350 | 845 | #. type: Plain text | 843 | #. type: Plain text |
2351 | @@ -851,19 +849,19 @@ | |||
2352 | 851 | "you wish to share your photo." | 849 | "you wish to share your photo." |
2353 | 852 | msgstr "" | 850 | msgstr "" |
2354 | 853 | 851 | ||
2356 | 854 | #. !!T | 852 | #. type: Title ### |
2357 | 855 | #: content/pages/apps.md:49 | 853 | #: content/pages/apps.md:49 |
2358 | 856 | #, no-wrap | 854 | #, no-wrap |
2360 | 857 | msgid "How can I share videos?" | 855 | msgid "How can I share videos? !!T" |
2361 | 858 | msgstr "" | 856 | msgstr "" |
2362 | 859 | 857 | ||
2363 | 860 | #. type: Plain text | 858 | #. type: Plain text |
2364 | 861 | #: content/pages/apps.md:51 | 859 | #: content/pages/apps.md:51 |
2365 | 862 | msgid "" | 860 | msgid "" |
2370 | 863 | "If you've just recorded a video, share it easily by swiping to the left from" | 861 | "If you've just recorded a video, share it easily by swiping to the left from " |
2371 | 864 | " the right edge inside the Camera app. Alternatively, use the Gallery app to" | 862 | "the right edge inside the Camera app. Alternatively, use the Gallery app to " |
2372 | 865 | " find the video. Once loaded, select *Share* from the menu and choose how " | 863 | "find the video. Once loaded, select *Share* from the menu and choose how you " |
2373 | 866 | "you wish to share your video." | 864 | "wish to share your video." |
2374 | 867 | msgstr "" | 865 | msgstr "" |
2375 | 868 | 866 | ||
2376 | 869 | #. type: Title ## | 867 | #. type: Title ## |
2377 | @@ -872,10 +870,10 @@ | |||
2378 | 872 | msgid "Camera" | 870 | msgid "Camera" |
2379 | 873 | msgstr "" | 871 | msgstr "" |
2380 | 874 | 872 | ||
2382 | 875 | #. !!T | 873 | #. type: Title ### |
2383 | 876 | #: content/pages/apps.md:54 | 874 | #: content/pages/apps.md:54 |
2384 | 877 | #, no-wrap | 875 | #, no-wrap |
2386 | 878 | msgid "How can I take a picture?" | 876 | msgid "How can I take a picture? !!T" |
2387 | 879 | msgstr "" | 877 | msgstr "" |
2388 | 880 | 878 | ||
2389 | 881 | #. type: Plain text | 879 | #. type: Plain text |
2390 | @@ -885,31 +883,31 @@ | |||
2391 | 885 | "bottom edge of the phone for additional options. Enjoy taking your picture!" | 883 | "bottom edge of the phone for additional options. Enjoy taking your picture!" |
2392 | 886 | msgstr "" | 884 | msgstr "" |
2393 | 887 | 885 | ||
2395 | 888 | #. !!T | 886 | #. type: Title ### |
2396 | 889 | #: content/pages/apps.md:57 | 887 | #: content/pages/apps.md:57 |
2397 | 890 | #, no-wrap | 888 | #, no-wrap |
2399 | 891 | msgid "How can I crop / rotate a picture?" | 889 | msgid "How can I crop / rotate a picture? !!T" |
2400 | 892 | msgstr "" | 890 | msgstr "" |
2401 | 893 | 891 | ||
2402 | 894 | #. type: Plain text | 892 | #. type: Plain text |
2403 | 895 | #: content/pages/apps.md:59 | 893 | #: content/pages/apps.md:59 |
2404 | 896 | msgid "" | 894 | msgid "" |
2407 | 897 | "Use the gallery app to select your picture. Select the *Edit* button next to" | 895 | "Use the gallery app to select your picture. Select the *Edit* button next to " |
2408 | 898 | " the menu. Inside you'll find options to crop and rotate your picture." | 896 | "the menu. Inside you'll find options to crop and rotate your picture." |
2409 | 899 | msgstr "" | 897 | msgstr "" |
2410 | 900 | 898 | ||
2412 | 901 | #. !!T | 899 | #. type: Title ### |
2413 | 902 | #: content/pages/apps.md:60 | 900 | #: content/pages/apps.md:60 |
2414 | 903 | #, no-wrap | 901 | #, no-wrap |
2416 | 904 | msgid "How can I record video?" | 902 | msgid "How can I record video? !!T" |
2417 | 905 | msgstr "" | 903 | msgstr "" |
2418 | 906 | 904 | ||
2419 | 907 | #. type: Plain text | 905 | #. type: Plain text |
2420 | 908 | #: content/pages/apps.md:62 | 906 | #: content/pages/apps.md:62 |
2421 | 909 | msgid "" | 907 | msgid "" |
2425 | 910 | "Select the Camera app from the launcher or apps scope. Select the video icon" | 908 | "Select the Camera app from the launcher or apps scope. Select the video icon " |
2426 | 911 | " on the bottom of the screen. Swipe up from the bottom edge of the phone for" | 909 | "on the bottom of the screen. Swipe up from the bottom edge of the phone for " |
2427 | 912 | " additional options. Enjoy taking your video!" | 910 | "additional options. Enjoy taking your video!" |
2428 | 913 | msgstr "" | 911 | msgstr "" |
2429 | 914 | 912 | ||
2430 | 915 | #. type: Title ## | 913 | #. type: Title ## |
2431 | @@ -918,10 +916,10 @@ | |||
2432 | 918 | msgid "Clock" | 916 | msgid "Clock" |
2433 | 919 | msgstr "" | 917 | msgstr "" |
2434 | 920 | 918 | ||
2436 | 921 | #. !!T | 919 | #. type: Title ### |
2437 | 922 | #: content/pages/apps.md:65 | 920 | #: content/pages/apps.md:65 |
2438 | 923 | #, no-wrap | 921 | #, no-wrap |
2440 | 924 | msgid "How do I set an alarm?" | 922 | msgid "How do I set an alarm? !!T" |
2441 | 925 | msgstr "" | 923 | msgstr "" |
2442 | 926 | 924 | ||
2443 | 927 | #. type: Plain text | 925 | #. type: Plain text |
2444 | @@ -943,10 +941,10 @@ | |||
2445 | 943 | msgid "HERE Maps" | 941 | msgid "HERE Maps" |
2446 | 944 | msgstr "" | 942 | msgstr "" |
2447 | 945 | 943 | ||
2449 | 946 | #. !!T | 944 | #. type: Title ### |
2450 | 947 | #: content/pages/apps.md:71 | 945 | #: content/pages/apps.md:71 |
2451 | 948 | #, no-wrap | 946 | #, no-wrap |
2453 | 949 | msgid "How can I get directions?" | 947 | msgid "How can I get directions? !!T" |
2454 | 950 | msgstr "" | 948 | msgstr "" |
2455 | 951 | 949 | ||
2456 | 952 | #. type: Plain text | 950 | #. type: Plain text |
2457 | @@ -956,10 +954,10 @@ | |||
2458 | 956 | "*Directions*. Enter your destination and tap the *Get Directions* button." | 954 | "*Directions*. Enter your destination and tap the *Get Directions* button." |
2459 | 957 | msgstr "" | 955 | msgstr "" |
2460 | 958 | 956 | ||
2462 | 959 | #. !!T | 957 | #. type: Title ### |
2463 | 960 | #: content/pages/apps.md:74 | 958 | #: content/pages/apps.md:74 |
2464 | 961 | #, no-wrap | 959 | #, no-wrap |
2466 | 962 | msgid "Can I navigate offline?" | 960 | msgid "Can I navigate offline? !!T" |
2467 | 963 | msgstr "" | 961 | msgstr "" |
2468 | 964 | 962 | ||
2469 | 965 | #. type: Plain text | 963 | #. type: Plain text |
2470 | @@ -967,10 +965,10 @@ | |||
2471 | 967 | msgid "Unfortunately navigation requires an active connection." | 965 | msgid "Unfortunately navigation requires an active connection." |
2472 | 968 | msgstr "" | 966 | msgstr "" |
2473 | 969 | 967 | ||
2475 | 970 | #. !!T | 968 | #. type: Title ### |
2476 | 971 | #: content/pages/apps.md:77 | 969 | #: content/pages/apps.md:77 |
2477 | 972 | #, no-wrap | 970 | #, no-wrap |
2479 | 973 | msgid "Can I view the map offline?" | 971 | msgid "Can I view the map offline? !!T" |
2480 | 974 | msgstr "" | 972 | msgstr "" |
2481 | 975 | 973 | ||
2482 | 976 | #. type: Plain text | 974 | #. type: Plain text |
2483 | @@ -982,7 +980,7 @@ | |||
2484 | 982 | 980 | ||
2485 | 983 | #. type: Plain text | 981 | #. type: Plain text |
2486 | 984 | #: content/pages/get-in-touch.md:2 | 982 | #: content/pages/get-in-touch.md:2 |
2488 | 985 | msgid "Get in touch" | 983 | msgid "Title: Get in touch" |
2489 | 986 | msgstr "" | 984 | msgstr "" |
2490 | 987 | 985 | ||
2491 | 988 | #. type: Plain text | 986 | #. type: Plain text |
2492 | @@ -1007,7 +1005,7 @@ | |||
2493 | 1007 | 1005 | ||
2494 | 1008 | #. type: Plain text | 1006 | #. type: Plain text |
2495 | 1009 | #: content/pages/scopes.md:2 | 1007 | #: content/pages/scopes.md:2 |
2497 | 1010 | msgid "Scopes" | 1008 | msgid "Title: Scopes" |
2498 | 1011 | msgstr "" | 1009 | msgstr "" |
2499 | 1012 | 1010 | ||
2500 | 1013 | #. type: Plain text | 1011 | #. type: Plain text |
2501 | @@ -1016,25 +1014,25 @@ | |||
2502 | 1016 | msgid "*Curious about scopes?*\n" | 1014 | msgid "*Curious about scopes?*\n" |
2503 | 1017 | msgstr "" | 1015 | msgstr "" |
2504 | 1018 | 1016 | ||
2506 | 1019 | #. !!T | 1017 | #. type: Title ### |
2507 | 1020 | #: content/pages/scopes.md:7 | 1018 | #: content/pages/scopes.md:7 |
2508 | 1021 | #, no-wrap | 1019 | #, no-wrap |
2510 | 1022 | msgid "How do favorites work?" | 1020 | msgid "How do favorites work? !!T" |
2511 | 1023 | msgstr "" | 1021 | msgstr "" |
2512 | 1024 | 1022 | ||
2513 | 1025 | #. type: Plain text | 1023 | #. type: Plain text |
2514 | 1026 | #: content/pages/scopes.md:9 | 1024 | #: content/pages/scopes.md:9 |
2515 | 1027 | msgid "" | 1025 | msgid "" |
2520 | 1028 | "Swipe up from the bottom edge of the dash to reveal a scopes manager. " | 1026 | "Swipe up from the bottom edge of the dash to reveal a scopes " |
2521 | 1029 | "Favorite scopes you wish to appear on your dash by selecting them. Selecting" | 1027 | "manager. Favorite scopes you wish to appear on your dash by selecting " |
2522 | 1030 | " them again will remove the favorite. All favorited scopes will appear on " | 1028 | "them. Selecting them again will remove the favorite. All favorited scopes " |
2523 | 1031 | "your dash." | 1029 | "will appear on your dash." |
2524 | 1032 | msgstr "" | 1030 | msgstr "" |
2525 | 1033 | 1031 | ||
2527 | 1034 | #. !!T | 1032 | #. type: Title ### |
2528 | 1035 | #: content/pages/scopes.md:10 | 1033 | #: content/pages/scopes.md:10 |
2529 | 1036 | #, no-wrap | 1034 | #, no-wrap |
2531 | 1037 | msgid "How do I add new scopes?" | 1035 | msgid "How do I add new scopes? !!T" |
2532 | 1038 | msgstr "" | 1036 | msgstr "" |
2533 | 1039 | 1037 | ||
2534 | 1040 | #. type: Plain text | 1038 | #. type: Plain text |
2535 | @@ -1046,10 +1044,10 @@ | |||
2536 | 1046 | "store button in the upper right to look for it in the ubuntu store." | 1044 | "store button in the upper right to look for it in the ubuntu store." |
2537 | 1047 | msgstr "" | 1045 | msgstr "" |
2538 | 1048 | 1046 | ||
2540 | 1049 | #. !!T | 1047 | #. type: Title ### |
2541 | 1050 | #: content/pages/scopes.md:13 | 1048 | #: content/pages/scopes.md:13 |
2542 | 1051 | #, no-wrap | 1049 | #, no-wrap |
2544 | 1052 | msgid "How do I remove a scope?" | 1050 | msgid "How do I remove a scope? !!T" |
2545 | 1053 | msgstr "" | 1051 | msgstr "" |
2546 | 1054 | 1052 | ||
2547 | 1055 | #. type: Plain text | 1053 | #. type: Plain text |
2548 | 1056 | 1054 | ||
2549 | === added directory 'static' | |||
2550 | === renamed directory 'app' => 'static/app' | |||
2551 | === renamed file 'edit-here/index.html' => 'static/index.html' | |||
2552 | === renamed directory 'edit-here/themes' => 'static/themes' | |||
2553 | === renamed directory 'edit-here/themes/phone' => 'static/themes/app' |
Directory structure above needs to be added to the FAQ. Also update the other sections, including testings and generating content.