Permalink
Browse files

Add internationalization support.

- depend on Babel (for locale support)
- mark most obvious string to be translated (some with plural forms)
- add "locale" setting to user that sets `request._LOCALE_` (what locale/language to use)
- add support for syncing translation to transifex https://www.transifex.com/projects/p/substanced/resource/master/
- fix some issues that "extract_messages" was choking on while parsing xml
- change logout icon from 'pencil' to 'off'
- add "_" translationstring function to util module
- add initial translation for Slovenian (72%)
- add initial translation for German (1%)
- document internalization workflow into HACKING.txt
- add flake8 ignored error hints to setup.cfg according to pylons codestyle
  • Loading branch information...
1 parent d0076e1 commit da1c601bf0e79c48dc81fa7238d4e914a3ab0250 @domenkozar domenkozar committed Nov 13, 2013
Showing with 2,234 additions and 266 deletions.
  1. +8 −0 .tx/config
  2. +31 −0 HACKING.txt
  3. +24 −0 setup.cfg
  4. +7 −0 setup.py
  5. +1 −0 substanced/__init__.py
  6. +8 −7 substanced/audit/templates/auditing.pt
  7. +2 −1 substanced/audit/views.py
  8. +2 −1 substanced/catalog/views/indexing.py
  9. +6 −5 substanced/catalog/views/templates/catalog.pt
  10. +7 −7 substanced/catalog/views/templates/index.pt
  11. +5 −5 substanced/catalog/views/templates/indexing.pt
  12. +4 −4 substanced/catalog/views/templates/search.pt
  13. +24 −24 substanced/db/templates/db.pt
  14. +12 −12 substanced/db/templates/db_show_evolve.pt
  15. +3 −1 substanced/db/views.py
  16. +1 −0 substanced/file/templates/fileupload.pt
  17. +6 −4 substanced/folder/templates/contents.pt
  18. +6 −6 substanced/folder/templates/rename.pt
  19. +21 −14 substanced/folder/tests/test_views.py
  20. +62 −71 substanced/folder/views.py
  21. BIN substanced/locale/de/LC_MESSAGES/substanced.mo
  22. +609 −0 substanced/locale/de/LC_MESSAGES/substanced.po
  23. BIN substanced/locale/sl/LC_MESSAGES/substanced.mo
  24. +621 −0 substanced/locale/sl/LC_MESSAGES/substanced.po
  25. +607 −0 substanced/locale/substanced.pot
  26. +5 −4 substanced/locking/views.py
  27. +5 −5 substanced/objectmap/templates/integrityerror.pt
  28. +3 −3 substanced/objectmap/templates/referenced.pt
  29. +29 −1 substanced/principal/__init__.py
  30. +4 −0 substanced/principal/templates/resetpassword_email.pt
  31. +2 −2 substanced/principal/views.py
  32. +3 −3 substanced/property/templates/propertysheets.pt
  33. +3 −2 substanced/property/views.py
  34. +2 −0 substanced/root/__init__.py
  35. +3 −2 substanced/sdi/__init__.py
  36. +8 −7 substanced/sdi/views/acl.py
  37. +24 −16 substanced/sdi/views/templates/acl.pt
  38. +7 −7 substanced/sdi/views/templates/batching.pt
  39. +13 −12 substanced/sdi/views/templates/forbidden.pt
  40. +1 −1 substanced/sdi/views/templates/form.pt
  41. +6 −6 substanced/sdi/views/templates/login.pt
  42. +9 −8 substanced/sdi/views/templates/master.pt
  43. +14 −14 substanced/sdi/views/templates/undo.pt
  44. +1 −1 substanced/sdi/views/templates/undobutton.pt
  45. +4 −3 substanced/sdi/views/undo.py
  46. +4 −0 substanced/util/__init__.py
  47. +7 −7 substanced/workflow/templates/workflow.pt
View
@@ -0,0 +1,8 @@
+[main]
+host = https://www.transifex.com
+type = PO
+
+[substanced.master]
+file_filter = substanced/locale/<lang>/LC_MESSAGES/substanced.po
+source_file = substanced/locale/substanced.pot
+source_lang = en
View
@@ -115,6 +115,37 @@ To run coverage tests for a single version of Python:
easiest usage is to simply provide the verbatim name of the test you're
working on.
+Internalization (adding/updating languages)
+-------------------------------------------
+
+Transifex project should be used for translations: https://www.transifex.com/projects/p/substanced/resource/master/
+
+Before you can do anything related to translations, there is small setup:
+
+- Make sure "transifex-client" Python package is installed
+
+To update transifex with new translations:
+
+- Run ``python setup.py extract_messages`` to populate `substanced.pot`
+
+- Run ``python setup.py update_catalog`` to update translation strings in existing .po files
+
+- Run ``tx push -t -s`` to push translation files to transifex to be translated
+
+- Notify translators through transifex
+
+To fetch translated files from transifex:
+
+- Run ``tx pull`` to update .po files with translations
+
+- Run ``python setup.py compile_catalog`` to update .mo files
+
+- Run `git commit -m "Update catalog" substanced/locales` to commit changes (don't forget to submit changes upstream)
+
+To add new language:
+
+- Run ``python setup.py init_catalog -l de``
+
Test Coverage
-------------
View
@@ -11,3 +11,27 @@ cover-erase=1
[aliases]
dev = develop easy_install substanced[testing]
docs = develop easy_install substanced[docs]
+
+[compile_catalog]
+directory = substanced/locale
+domain = substanced
+statistics = true
+
+[extract_messages]
+add_comments = TRANSLATORS:
+output_file = substanced/locale/substanced.pot
+width = 80
+
+[init_catalog]
+domain = substanced
+input_file = substanced/locale/substanced.pot
+output_dir = substanced/locale
+
+[update_catalog]
+domain = substanced
+input_file = substanced/locale/substanced.pot
+output_dir = substanced/locale
+previous = true
+
+[flake8]
+ignore = E302,E261,E231,E123,E301,E226,E262,E225,E303,E125,E251,E201,E202,E128,E122,E701,E203,E222,W293,W291,W391,E121,E126
View
@@ -41,6 +41,7 @@
'zope.deprecation',
'statsd',
'pytz',
+ 'Babel',
]
docs_extras = ['Sphinx', 'repoze.sphinx.autointerface']
@@ -76,6 +77,12 @@
install_requires=install_requires,
tests_require=install_requires,
test_suite="substanced",
+ message_extractors={
+ 'substanced': [
+ ('**.py', 'python', None), # babel extractor supports plurals
+ ('**.pt', 'lingua_xml', None),
+ ],
+ },
entry_points="""
[console_scripts]
sd_evolve = substanced.scripts.evolve:main
@@ -22,6 +22,7 @@ def include(config): # pragma: no cover
config.include('.locking')
config.include('.audit')
config.include('.editable')
+ config.add_translation_dirs('locale/')
def scan(config): # pragma: no cover
""" Perform all ``config.scan`` tasks required for Substance D and the
@@ -1,20 +1,21 @@
-<div metal:use-macro="request.sdiapi.main_template">
+<div metal:use-macro="request.sdiapi.main_template"
+ i18n:domain="substanced">
<div metal:fill-slot="main">
<div tal:condition="log_exists"
metal:use-macro="request.sdiapi.get_macro('substanced.sdi.views:templates/batching.pt', 'batching')"/>
<div class="panel panel-default">
- <div class="panel-heading">Audit events</div>
+ <div class="panel-heading" i18n:translate="">Audit events</div>
<div class="panel-body">
<table border="0"
class="table table-striped table-condensed"
tal:condition="log_exists">
<thead>
<tr>
- <th>Id</th>
- <th>Name</th>
- <th>Time</th>
- <th>Payload</th>
+ <th i18n:translate="">Id</th>
+ <th i18n:translate="">Name</th>
+ <th i18n:translate="">Time</th>
+ <th i18n:translate="">Payload</th>
</tr>
</thead>
<tr tal:repeat="(gen, idx, time, event) batch">
@@ -27,7 +28,7 @@
</td>
</tr>
</table>
- <h2 tal:condition="not log_exists">
+ <h2 tal:condition="not log_exists" i18n:translate="">
Auditing not configured in this system
</h2>
</div>
@@ -9,6 +9,7 @@
Batch,
get_oid,
get_auditlog,
+ _,
)
@view_defaults(
@@ -25,7 +26,7 @@ def __init__(self, context, request):
@mgmt_view(
name='auditing',
- tab_title='Auditing',
+ tab_title=_('Auditing'),
renderer='templates/auditing.pt',
tab_near=RIGHT,
physical_path='/',
@@ -6,6 +6,7 @@
from substanced.util import (
get_oid,
find_catalogs,
+ _,
)
from substanced.sdi import (
@@ -26,7 +27,7 @@ def __init__(self, context, request):
@mgmt_view(
renderer='templates/indexing.pt',
- tab_title='Indexing',
+ tab_title=_('Indexing'),
tab_near=RIGHT, # try not to be the default tab, we're too obscure
)
def show(self):
@@ -1,10 +1,11 @@
-<div metal:use-macro="request.sdiapi.main_template">
+<div metal:use-macro="request.sdiapi.main_template"
+ i18n:domain="substanced">
<div metal:fill-slot="main">
<div class="panel panel-default">
- <div class="panel-heading">Manage catalog &nbsp;
- (<span class="badge badge-primary">${cataloglen}</span>
+ <div class="panel-heading" i18n:translate="">Manage catalog &nbsp;
+ (<span class="badge badge-primary" i18n:name="length">${cataloglen}</span>
items in this catalog)
</div>
<div class="panel-body">
@@ -16,7 +17,7 @@
<div class="col-md-12">
</div>
<div class="row" style="margin-bottom: 20px;">
- <div class="col-md-10">
+ <div class="col-md-10" i18n:translate="">
Reindex all objects in all indexes.
</div>
<div class="col-md-2">
@@ -25,7 +26,7 @@
</div>
</div>
<div class="row">
- <div class="col-md-10">
+ <div class="col-md-10" i18n:translate="">
Update all index definitions
</div>
<div class="col-md-2">
@@ -1,14 +1,14 @@
-<div metal:use-macro="request.sdiapi.main_template">
+<div metal:use-macro="request.sdiapi.main_template" i18n:domain="substanced">
<div metal:fill-slot="main">
<form action="./manage_index" method="POST">
<div class="panel panel-default">
- <div class="panel-heading">Manage Index</div>
+ <div class="panel-heading" i18n:translate="">Manage Index</div>
<div class="panel-body">
<div class="container">
<div class="row">
- <div class="col-md-10">
+ <div class="col-md-10" i18n:translate="">
Index type
</div>
<div class="col-md-2">
@@ -17,7 +17,7 @@
</div>
<div class="row">
- <div class="col-md-10">
+ <div class="col-md-10" i18n:translate="">
Number of indexed items
</div>
<div class="col-md-2">
@@ -26,21 +26,21 @@
</div>
<div class="row">
- <div class="col-md-10">
+ <div class="col-md-10" i18n:translate="">
Number of not-indexed items
</div>
<div class="col-md-2">
${not_indexed}
</div>
</div>
<div class="row">
- <div class="col-md-10">
+ <div class="col-md-10" i18n:translate="">
Reindex this index
</div>
<div class="col-md-2">
<input type="hidden" value="${request.session.get_csrf_token()}"
name="csrf_token"/>
- <input type="submit" class="btn btn-primary"
+ <input type="submit" class="btn btn-primary" i18n:attributes="value"
value="Reindex" name="reindex"/>
</div>
</div>
@@ -1,20 +1,20 @@
-<div metal:use-macro="request.sdiapi.main_template">
+<div metal:use-macro="request.sdiapi.main_template" i18n:domain="substanced">
<div metal:fill-slot="main">
<div tal:repeat="(catalog,indexes) catalogs" style="margin-bottom: 20px">
<div class="panel panel-default">
- <div class="panel-heading">
+ <div class="panel-heading" i18n:translate="">
Catalog service at "${request.resource_path(catalog)}"
</div>
<div class="panel-body">
<table class="table table-striped">
<thead>
<tr>
- <th>Index name</th>
- <th>Value</th>
+ <th i18n:translate="">Index name</th>
+ <th i18n:translate="">Value</th>
</tr>
</thead>
@@ -29,7 +29,7 @@
<form action="@@indexing" method="post" onsubmit="sdi.loading_indicator_on()">
<input type="hidden" name="csrf_token"
value="${request.session.get_csrf_token()}"/>
- <input type="submit" name="form.reindex" class="btn btn-primary" value="Reindex This Object"/>
+ <input type="submit" name="form.reindex" class="btn btn-primary" value="Reindex This Object" i18n:attributes="value"/>
</form>
</div>
@@ -1,4 +1,4 @@
-<div metal:use-macro="request.sdiapi.main_template">
+<div metal:use-macro="request.sdiapi.main_template" i18n:domain="substanced">
<metal:head-more fill-slot="head-more">
<!-- CSS -->
@@ -19,13 +19,13 @@
<h1>${view.title|None}</h1>
<div id="form" tal:content="structure form"/>
- <h3 tal:condition="searchresults|None">Search Results</h3>
+ <h3 tal:condition="searchresults|None" i18n:translate="">Search Results</h3>
<table tal:condition="searchresults|None" class="table table-striped">
<thead>
<tr>
- <th width="15%">Object Id</th>
- <th width="85%">Repr</th>
+ <th width="15%" i18n:translate="">Object Id</th>
+ <th width="85%" i18n:translate="">Repr</th>
</tr>
</thead>
Oops, something went wrong.

0 comments on commit da1c601

Please sign in to comment.