pyramid-localize 0.1.0__py2.py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,62 @@
1
+ # Copyright (c) 2013-2014 by pyramid_localize authors and contributors <see AUTHORS file>
2
+ #
3
+ # This module is part of pyramid_localize and is released under
4
+ # the MIT License (MIT): http://opensource.org/licenses/MIT
5
+ """pyramid_localize configuration module."""
6
+
7
+ try: # pragma: no cover
8
+ import babel
9
+ except ImportError: # pragma: no cover
10
+ babel = False
11
+
12
+ from tzf.pyramid_yml import config_defaults
13
+
14
+ from pyramid_localize.request import (
15
+ locale, database_locales, locales, locale_id
16
+ )
17
+
18
+ __version__ = '0.1.0'
19
+
20
+
21
+ def includeme(configurator):
22
+ """pyramid_localize configuration method."""
23
+ # let's check if we have any configuration, or not
24
+ if babel:
25
+ configurator.scan('pyramid_localize.subscribers.i18n')
26
+ configuration = configurator.registry.get('config', {}).get('localize')
27
+ if configuration:
28
+ configurator.include('pyramid_mako')
29
+ # once user allowed for localization, lets set up default values!
30
+ config_defaults(configurator, 'pyramid_localize:config')
31
+
32
+ configurator.set_locale_negotiator('pyramid_localize.negotiator.locale_negotiator')
33
+ translation_dirs = configuration.translation.dirs
34
+ # if it's not a list, lets make it a list.
35
+ # This is to allow creating both single, and list-like config entry
36
+ if not isinstance(translation_dirs, list):
37
+ translation_dirs = [translation_dirs]
38
+
39
+ # let's add destination folder to the list
40
+ if 'destination' in configuration.translation:
41
+ translation_dirs.append(configuration.translation.destination)
42
+
43
+ configurator.add_translation_dirs(*translation_dirs)
44
+ # let scan all subscribers
45
+ configurator.scan('pyramid_localize.views')
46
+
47
+ configurator.add_route(name='localize:index', pattern='catalogue')
48
+ configurator.add_route(name='localize:update', pattern='catalogue/update')
49
+ configurator.add_route(name='localize:compile', pattern='catalogue/compile')
50
+ configurator.add_route(name='localize:reload', pattern='catalogue/reload')
51
+
52
+ # getting requests methods
53
+ configurator.add_request_method(locale, name='locale', reify=True)
54
+ configurator.add_request_method(database_locales, name='_database_locales', reify=True)
55
+ configurator.add_request_method(locales, name='locales')
56
+ configurator.add_request_method(locale_id, name='locale_id', reify=True)
57
+
58
+ # if configured
59
+ return
60
+
61
+ # including fake subscribers
62
+ configurator.scan('pyramid_localize.subscribers.fake')
@@ -0,0 +1,13 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ # Copyright (c) 2013-2014 by pyramid_localize authors and contributors <see AUTHORS file>
4
+ #
5
+ # This module is part of pyramid_localize and is released under
6
+ # the MIT License (MIT): http://opensource.org/licenses/MIT
7
+ localize:
8
+ pybabel: pybabel
9
+ locales: # available and default locale for your app
10
+ available: [en, de, pl]
11
+ default: en
12
+ translation:
13
+ dirs: []
@@ -0,0 +1,37 @@
1
+ # Copyright (c) 2013-2014 by pyramid_localize authors and contributors <see AUTHORS file>
2
+ #
3
+ # This module is part of pyramid_localize and is released under
4
+ # the MIT License (MIT): http://opensource.org/licenses/MIT
5
+ """Language model."""
6
+
7
+
8
+ from sqlalchemy import Column
9
+ from sqlalchemy import Sequence
10
+ from sqlalchemy import Integer
11
+ from sqlalchemy import String
12
+ from sqlalchemy import Unicode
13
+ from sqlalchemy import DateTime
14
+ from sqlalchemy import func
15
+
16
+ from pyramid_basemodel import Base
17
+
18
+
19
+ class Language(Base):
20
+
21
+ """Language table model definition."""
22
+
23
+ __tablename__ = 'languages'
24
+
25
+ id = Column(Integer, Sequence(__tablename__ + '_sq'), primary_key=True)
26
+ updated_at = Column(DateTime, default=func.now(), onupdate=func.now(), nullable=False)
27
+ name = Column(Unicode(45), nullable=False)
28
+ native_name = Column(Unicode(45), nullable=False)
29
+ language_code = Column(String(2), unique=True, nullable=False) # ISO 639-1 (Alpha2)
30
+
31
+ def __unicode__(self): # pragma: no cover
32
+ """Language to unicode conversion."""
33
+ return self.name
34
+
35
+ def __str__(self): # pragma: no cover
36
+ """Language to string conversion."""
37
+ return self.name.encode('utf8')
@@ -0,0 +1,41 @@
1
+ # Copyright (c) 2013-2014 by pyramid_localize authors and contributors <see AUTHORS file>
2
+ #
3
+ # This module is part of pyramid_localize and is released under
4
+ # the MIT License (MIT): http://opensource.org/licenses/MIT
5
+ """Locale negotiator."""
6
+
7
+
8
+ def locale_negotiator(request):
9
+ """
10
+ Locale negotiator.
11
+
12
+ It sets best suited locale variable for given user:
13
+
14
+ 1. Check for presence and value of **request._LOCALE_** value
15
+ 2. Then tries the address url, if the first part has locale indicator.
16
+ 3. It checks cookies, for value set here
17
+ 4. Tries to best match accepted language for browser user is visiting
18
+ website with
19
+ 5. Defaults to **localize.locales.default** configuration setting value
20
+
21
+ :param pyramid.request.Request request: a request object
22
+ :returns: locale name
23
+ :rtype: str
24
+ """
25
+ available_languages = request.registry['config'].localize.locales.available
26
+ locale = request.registry['config'].localize.locales.default
27
+ # We do not have a matchdict present at the moment, lets get our own split
28
+ # (request.path is always a /, so we'll get two elements)
29
+ route_elements = request.path.split('/')
30
+ if hasattr(request, '_LOCALE_') and request._LOCALE_ in available_languages:
31
+ locale = request._LOCALE_
32
+ # we check if route_element[1] is a locale indicator for path
33
+ elif len(route_elements[1]) == 2 and route_elements[1] in available_languages:
34
+ locale = route_elements[1]
35
+ elif request.cookies and '_LOCALE_' in request.cookies and\
36
+ request.cookies['_LOCALE_'] in available_languages:
37
+ locale = request.cookies['_LOCALE_']
38
+ elif request.accept_language:
39
+ locale = request.accept_language.best_match(available_languages)
40
+
41
+ return locale
@@ -0,0 +1,124 @@
1
+ # Copyright (c) 2013-2014 by pyramid_localize authors and contributors <see AUTHORS file>
2
+ #
3
+ # This module is part of pyramid_localize and is released under
4
+ # the MIT License (MIT): http://opensource.org/licenses/MIT
5
+ """Request related code."""
6
+
7
+ import warnings
8
+
9
+ from pyramid.compat import text_type
10
+ import pyramid_basemodel
11
+
12
+ from pyramid_localize.models import Language
13
+
14
+
15
+ class LocalizeRequestMixin(object):
16
+
17
+ """Mixin adding overwriting Request methods."""
18
+
19
+ def default_locale(self, **kw):
20
+ """
21
+ Set up default locale for path kwargs.
22
+
23
+ Can be used in custom route_url overwrites.
24
+
25
+ :param kwargs kw: list of route parts
26
+
27
+ :returns: kw
28
+ """
29
+ if '__LOCALE__' not in kw \
30
+ or kw['__LOCALE__'] not in self.registry['config'].localize.locales.available:
31
+ kw['__LOCALE__'] = self.locale_name
32
+
33
+ return kw
34
+
35
+ def route_url(self, route_name, *elements, **kw):
36
+ """
37
+ Overwrite original route_url to handle default locale within route.
38
+
39
+ .. note:: see :meth:`pyramid.request.Request.route_url`
40
+ """
41
+ return super(LocalizeRequestMixin, self).route_url(
42
+ route_name, *elements, **self.default_locale(**kw)
43
+ )
44
+
45
+
46
+ def locale(request):
47
+ """
48
+ Return locale name.
49
+
50
+ .. warning::
51
+
52
+ Since pyramid 1.5 this function is obsolete and will be deprecated
53
+ in future versions of pyramid_localize
54
+
55
+ When called for the first time, it ask environment for language code,
56
+ which is later available as a pure property
57
+ overriding this method
58
+
59
+ :returns: language code needed for translations
60
+ :rtype: string
61
+ """
62
+ warnings.warn(
63
+ 'request.locale is deprecated as of pyramid_localize 0.1. '
64
+ 'Please use request.locale_name delivered by Pyramid 1.5.',
65
+ DeprecationWarning,
66
+ 2
67
+ )
68
+ return request.locale_name
69
+
70
+
71
+ def locale_id(request):
72
+ """
73
+ Return database id of a current locale name.
74
+
75
+ :returns: database id of a language code needed for translations
76
+ :rtype: int
77
+ """
78
+ if request.locale_name not in request._database_locales:
79
+ _create_locale(request.locale_name, request)
80
+
81
+ return request._database_locales[request.locale_name].id
82
+
83
+
84
+ def database_locales(request):
85
+ """
86
+ Return list of all database locales available.
87
+
88
+ :returns: dictionary of Language objects language_code: Language
89
+ :rtype: dict
90
+ """
91
+ locales = {}
92
+ for language in pyramid_basemodel.Session.query(Language).all():
93
+ locales[language.language_code] = language
94
+
95
+ return locales
96
+
97
+
98
+ def locales(request, config=False):
99
+ """
100
+ Return a list of locales.
101
+
102
+ :param bool config: Whether to restrict list with config
103
+
104
+ :returns: dictionary of Language objects language_code: Language
105
+ :rtype: dict
106
+ """
107
+ if config:
108
+ locales = {}
109
+ for locale in request.registry['config'].localize.locales.available:
110
+ if locale not in request._database_locales:
111
+ _create_locale(locale, request)
112
+ locales[locale] = request._database_locales[locale]
113
+
114
+ return locales
115
+
116
+ return request._database_locales
117
+
118
+
119
+ def _create_locale(locale, request):
120
+ new_locale = Language(name=text_type(locale),
121
+ native_name=text_type(locale),
122
+ language_code=text_type(locale))
123
+ pyramid_basemodel.Session.add(new_locale)
124
+ request._database_locales = database_locales(request)
@@ -0,0 +1,58 @@
1
+ # Translations template for pyramid_localize.
2
+ # Copyright (C) 2013 ORGANIZATION
3
+ # This file is distributed under the same license as the pyramid_localize
4
+ # project.
5
+ # FIRST AUTHOR <EMAIL@ADDRESS>, 2013.
6
+ #
7
+ #, fuzzy
8
+ msgid ""
9
+ msgstr ""
10
+ "Project-Id-Version: pyramid_localize 0.0.1\n"
11
+ "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
12
+ "POT-Creation-Date: 2013-06-06 22:20+0200\n"
13
+ "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
14
+ "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
15
+ "Language-Team: LANGUAGE <LL@li.org>\n"
16
+ "MIME-Version: 1.0\n"
17
+ "Content-Type: text/plain; charset=utf-8\n"
18
+ "Content-Transfer-Encoding: 8bit\n"
19
+ "Generated-By: Babel 0.9.6\n"
20
+
21
+ #: pyramid_localize/resources/templates/_list.mako:5
22
+ msgid "Language"
23
+ msgstr ""
24
+
25
+ #: pyramid_localize/resources/templates/_list.mako:6
26
+ msgid "Translation domain"
27
+ msgstr ""
28
+
29
+ #: pyramid_localize/resources/templates/_list.mako:7
30
+ #: pyramid_localize/resources/templates/_list.mako:8
31
+ #: pyramid_localize/resources/templates/_list.mako:9
32
+ msgid "${type} modification time"
33
+ msgstr ""
34
+
35
+ #: pyramid_localize/resources/templates/index.mako:4
36
+ msgid "Translations"
37
+ msgstr ""
38
+
39
+ #: pyramid_localize/resources/templates/index.mako:6
40
+ msgid "Update catalog"
41
+ msgstr ""
42
+
43
+ #: pyramid_localize/resources/templates/index.mako:7
44
+ msgid "Compile catalog"
45
+ msgstr ""
46
+
47
+ #: pyramid_localize/resources/templates/index.mako:8
48
+ msgid "Reload catalog"
49
+ msgstr ""
50
+
51
+ #: pyramid_localize/resources/templates/layout.mako:3
52
+ msgid "Welcome, to pyramid_localize!"
53
+ msgstr ""
54
+
55
+ #: pyramid_localize/views/catalog.py:166
56
+ msgid "Localizators has been reloaded"
57
+ msgstr ""
58
+
@@ -0,0 +1,33 @@
1
+ <%doc>
2
+ Copyright (c) 2013-2014 by pyramid_localize authors and contributors <see AUTHORS file>
3
+
4
+ This module is part of pyramid_localize and is released under
5
+ the MIT License (MIT): http://opensource.org/licenses/MIT
6
+ </%doc>
7
+ <table class="table table-hover">
8
+ <thead>
9
+ <tr>
10
+ <th>${_('Language', domain='pyramid_localize')}</th>
11
+ <th>${_('Translation domain', domain='pyramid_localize')}</th>
12
+ <th>${_('${type} modification time', domain='pyramid_localize', mapping={'type': 'pot'})}</th>
13
+ <th>${_('${type} modification time', domain='pyramid_localize', mapping={'type': 'po'})}</th>
14
+ <th>${_('${type} modification time', domain='pyramid_localize', mapping={'type': 'mo'})}</th>
15
+ </tr>
16
+ </thead>
17
+ <tbody>
18
+ % for language in translations:
19
+ <tr>
20
+ <td rowspan="${len(translations[language]) if len(translations[language]) > 1 else 1}">${language}</td>
21
+ % for domain in translations[language]:
22
+ <td>${domain}</td>
23
+ <td>${translations[language][domain]['pot']}</td>
24
+ <td>${translations[language][domain]['po']}</td>
25
+ <td>${translations[language][domain]['mo']}</td>
26
+ % if len(translations[language]) > 1:
27
+ </tr><tr>
28
+ % endif
29
+ % endfor
30
+ </tr>
31
+ % endfor
32
+ </tbody>
33
+ </table>
@@ -0,0 +1,19 @@
1
+ <%doc>
2
+ Copyright (c) 2013-2014 by pyramid_localize authors and contributors <see AUTHORS file>
3
+
4
+ This module is part of pyramid_localize and is released under
5
+ the MIT License (MIT): http://opensource.org/licenses/MIT
6
+ </%doc>
7
+ <%inherit file="pyramid_localize:resources/templates/layout.mako" />
8
+ <div class="row">
9
+ <div class="span9">
10
+ <h1>${_('Translations', domain='pyramid_localize')}</h1>
11
+ <p>
12
+ <a href="${request.route_path('localize:update')}" class="btn">${_('Update catalog', domain='pyramid_localize')}</a>
13
+ <a href="${request.route_path('localize:compile')}" class="btn">${_('Compile catalog', domain='pyramid_localize')}</a>
14
+ <a href="${request.route_path('localize:reload')}" class="btn">${_('Reload catalog', domain='pyramid_localize')}</a>
15
+ </p>
16
+ <%include file="_list.mako" />
17
+
18
+ </div>
19
+ </div>
@@ -0,0 +1,12 @@
1
+ <%doc>
2
+ Copyright (c) 2013-2014 by pyramid_localize authors and contributors <see AUTHORS file>
3
+
4
+ This module is part of pyramid_localize and is released under
5
+ the MIT License (MIT): http://opensource.org/licenses/MIT
6
+ </%doc>
7
+ <html>
8
+ <body>
9
+ <h1>${_('Welcome, to pyramid_localize!', domain='pyramid_localize')}</h1>
10
+ ${next.body()}
11
+ </body>
12
+ </html>
@@ -0,0 +1,5 @@
1
+ # Copyright (c) 2013-2014 by pyramid_localize authors and contributors <see AUTHORS file>
2
+ #
3
+ # This module is part of pyramid_localize and is released under
4
+ # the MIT License (MIT): http://opensource.org/licenses/MIT
5
+ """Routing related modules."""
@@ -0,0 +1,18 @@
1
+ # Copyright (c) 2013-2014 by pyramid_localize authors and contributors <see AUTHORS file>
2
+ #
3
+ # This module is part of pyramid_localize and is released under
4
+ # the MIT License (MIT): http://opensource.org/licenses/MIT
5
+ """Localize route predicate."""
6
+
7
+
8
+ def language(field):
9
+ """Create language predicate for given url match field."""
10
+ def predicate(info, request):
11
+ """Check whether language is one of the defaults."""
12
+ if field in info['match'] and\
13
+ info['match'][field] in request.registry['config'].localize.locales.available:
14
+ return True
15
+ return False
16
+ return predicate
17
+
18
+ language.__text__ = 'language predicate, to determine allowed languages in route'
@@ -0,0 +1,5 @@
1
+ # Copyright (c) 2013-2014 by pyramid_localize authors and contributors <see AUTHORS file>
2
+ #
3
+ # This module is part of pyramid_localize and is released under
4
+ # the MIT License (MIT): http://opensource.org/licenses/MIT
5
+ """Event subscribers, fake and real."""
@@ -0,0 +1,27 @@
1
+ # Copyright (c) 2013-2014 by pyramid_localize authors and contributors <see AUTHORS file>
2
+ #
3
+ # This module is part of pyramid_localize and is released under
4
+ # the MIT License (MIT): http://opensource.org/licenses/MIT
5
+ """Subscribers adding mocked translation methods to render context, and request."""
6
+
7
+ from pyramid.events import subscriber
8
+ from pyramid.events import BeforeRender
9
+ from pyramid.events import NewRequest
10
+
11
+ from pyramid_localize.tools import dummy_autotranslate
12
+
13
+
14
+ @subscriber(BeforeRender)
15
+ def global_renderer(event):
16
+ """BeforeRender subscriber, adds fake localizer, and translation methods to context."""
17
+ request = event['request']
18
+ try:
19
+ event['_'] = request._
20
+ except AttributeError:
21
+ event['_'] = dummy_autotranslate
22
+
23
+
24
+ @subscriber(NewRequest)
25
+ def add_localizer(event):
26
+ """NewRequest subscriber, adds fake localizer and translation methods to request."""
27
+ event.request._ = dummy_autotranslate
@@ -0,0 +1,27 @@
1
+ # Copyright (c) 2013-2014 by pyramid_localize authors and contributors <see AUTHORS file>
2
+ #
3
+ # This module is part of pyramid_localize and is released under
4
+ # the MIT License (MIT): http://opensource.org/licenses/MIT
5
+ """i18n subscribers."""
6
+
7
+ from pyramid.events import subscriber
8
+ from pyramid.events import BeforeRender
9
+ from pyramid.events import NewRequest
10
+
11
+ from pyramid_localize.tools import set_localizer
12
+
13
+
14
+ @subscriber(BeforeRender)
15
+ def global_renderer(event):
16
+ """BeforeRender subscriber, adds localizer, and translation methods to context."""
17
+ request = event['request']
18
+ set_localizer(request)
19
+
20
+ event['_'] = request._
21
+ event['localizer'] = request.localizer
22
+
23
+
24
+ @subscriber(NewRequest)
25
+ def add_localizer(event):
26
+ """NewRequest subscriber, adds localizer and translation methods to request."""
27
+ set_localizer(event.request)
@@ -0,0 +1,100 @@
1
+ # Copyright (c) 2013-2014 by pyramid_localize authors and contributors <see AUTHORS file>
2
+ #
3
+ # This module is part of pyramid_localize and is released under
4
+ # the MIT License (MIT): http://opensource.org/licenses/MIT
5
+ """methods in this module are tools, thank to which pyramid_localize works most of its magic."""
6
+
7
+ import sys
8
+ import os
9
+ import logging
10
+
11
+ from translationstring import _interp_regex
12
+ from pyramid.i18n import make_localizer
13
+ from pyramid.i18n import TranslationString
14
+ from pyramid.asset import resolve_asset_spec
15
+ from pyramid.path import package_path
16
+ from pyramid.interfaces import ILocalizer
17
+ from pyramid.interfaces import ITranslationDirectories
18
+ from pyramid.compat import text_type
19
+
20
+ logger = logging.getLogger(__name__)
21
+
22
+
23
+ def set_localizer(request, reset=False):
24
+ """
25
+ Set localizer and auto_translate methods for request.
26
+
27
+ :param pyramid.request.Request request: request object
28
+ :param bool reset: flag that directs resetting localizer within app
29
+ """
30
+ if reset:
31
+
32
+ for locale in request.registry['config'].localize.locales.available:
33
+ logger.debug('Resetting {0} localizator'.format(locale))
34
+ tdirs = request.registry.queryUtility(ITranslationDirectories,
35
+ default=[])
36
+ localizer = make_localizer(locale, tdirs)
37
+ request.registry.registerUtility(localizer, ILocalizer,
38
+ name=locale)
39
+
40
+ def auto_translate(*args, **kwargs):
41
+ # lets pass default domain, so we don't have to determine it with
42
+ # each _() function in apps.
43
+ if len(args) <= 1 and 'domain' not in kwargs:
44
+ kwargs['domain'] = request.registry['config'].localize.domain
45
+
46
+ # unlike in examples we use TranslationString, to make sure we always
47
+ # use appropriate domain
48
+ return request.localizer.translate(TranslationString(*args, **kwargs))
49
+
50
+ request._ = auto_translate
51
+
52
+
53
+ def destination_path(request):
54
+ """
55
+ Return absolute path of the translation destination.
56
+
57
+ :param pyramid.request.Request request: a request object
58
+
59
+ :returns: A combined translation destination path
60
+ :rtype: str
61
+ """
62
+ package_name, filename = resolve_asset_spec(
63
+ request.registry['config'].localize.translation.destination
64
+ )
65
+
66
+ if package_name is None: # absolute filename
67
+ directory = filename
68
+ else:
69
+ __import__(package_name)
70
+ package = sys.modules[package_name]
71
+ directory = os.path.join(package_path(package), filename)
72
+ return directory
73
+
74
+
75
+ def dummy_autotranslate(msgid, domain=None, default=None, mapping=None):
76
+ """
77
+ Method that simulate autotranslate.
78
+
79
+ :param str msgid: Message or message id
80
+ :param str domain: Translation domain
81
+ :param str default: Default message
82
+ :param dict mapping: Mapping dictionary for message variables
83
+
84
+ :returns: *translated* string
85
+ :rtype: str
86
+ """
87
+ # Try to return defaults first:
88
+ tstr = None
89
+ if default:
90
+ tstr = default
91
+ else:
92
+ tstr = msgid
93
+
94
+ if mapping and tstr:
95
+ def replace(match):
96
+ whole, param1, param2 = match.groups()
97
+ return text_type(mapping.get(param1 or param2, whole))
98
+ tstr = _interp_regex.sub(replace, tstr)
99
+
100
+ return tstr
@@ -0,0 +1,5 @@
1
+ # Copyright (c) 2013-2014 by pyramid_localize authors and contributors <see AUTHORS file>
2
+ #
3
+ # This module is part of pyramid_localize and is released under
4
+ # the MIT License (MIT): http://opensource.org/licenses/MIT
5
+ """View submodules."""