File: internationalization.rst

package info (click to toggle)
python-sqlalchemy-utils 0.41.2-1
  • links: PTS, VCS
  • area: main
  • in suites: experimental
  • size: 1,252 kB
  • sloc: python: 13,566; makefile: 141
file content (167 lines) | stat: -rw-r--r-- 4,472 bytes parent folder | download | duplicates (4)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
Internationalization
====================

SQLAlchemy-Utils provides a way for modeling translatable models. Model is
translatable if one or more of its columns can be displayed in various languages.

.. note::

    The implementation is currently highly PostgreSQL specific since it needs
    a dict-compatible column type (PostgreSQL HSTORE and JSON are such types).
    If you want database-agnostic way of modeling i18n see `SQLAlchemy-i18n`_.


TranslationHybrid vs SQLAlchemy-i18n
------------------------------------

Compared to SQLAlchemy-i18n the TranslationHybrid has the following pros and cons:

* Usually faster since no joins are needed for fetching the data
* Less magic
* Easier to understand data model
* Only PostgreSQL supported for now


Quickstart
----------

Let's say we have an Article model with translatable name and content. First we
need to define the TranslationHybrid.

::

    from sqlalchemy_utils import TranslationHybrid


    # For testing purposes we define this as simple function which returns
    # locale 'fi'. Usually you would define this function as something that
    # returns the user's current locale.
    def get_locale():
        return 'fi'


    translation_hybrid = TranslationHybrid(
        current_locale=get_locale,
        default_locale='en'
    )


Then we can define the model.::


    from sqlalchemy import *
    from sqlalchemy.dialects.postgresql import HSTORE


    class Article(Base):
        __tablename__ = 'article'

        id = Column(Integer, primary_key=True)
        name_translations = Column(HSTORE)
        content_translations = Column(HSTORE)

        name = translation_hybrid(name_translations)
        content = translation_hybrid(content_translations)


Now we can start using our translatable model. By assigning things to
translatable hybrids you are assigning them to the locale returned by the
`current_locale`.
::


    article = Article(name='Joku artikkeli')
    article.name_translations['fi']  # Joku artikkeli
    article.name  # Joku artikkeli


If you access the hybrid with a locale that doesn't exist the hybrid tries to
fetch a the locale returned by `default_locale`.
::

    article = Article(name_translations={'en': 'Some article'})
    article.name  # Some article
    article.name_translations['fi'] = 'Joku artikkeli'
    article.name  # Joku artikkeli


Translation hybrids can also be used as expressions.
::

    session.query(Article).filter(Article.name_translations['en'] == 'Some article')


By default if no value is found for either current or default locale the
translation hybrid returns `None`. You can customize this value with `default_value` parameter
of translation_hybrid. In the following example we make translation hybrid fallback to empty string instead of `None`.

::

    translation_hybrid = TranslationHybrid(
        current_locale=get_locale,
        default_locale='en',
        default_value=''
    )


    class Article(Base):
        __tablename__ = 'article'

        id = Column(Integer, primary_key=True)
        name_translations = Column(HSTORE)

        name = translation_hybrid(name_translations, default)


    Article().name  # ''


Dynamic locales
---------------

Sometimes locales need to be dynamic. The following example illustrates how to setup
dynamic locales. You can pass a callable of either 0, 1 or 2 args as a constructor parameter for TranslationHybrid.

The first argument should be the associated object and second parameter the name of the translations attribute.


::

    translation_hybrid = TranslationHybrid(
        current_locale=get_locale,
        default_locale=lambda obj: obj.locale,
    )


    class Article(Base):
        __tablename__ = 'article'

        id = Column(Integer, primary_key=True)
        name_translations = Column(HSTORE)

        name = translation_hybrid(name_translations, default)
        locale = Column(String)


    article = Article(name_translations={'en': 'Some article'})
    article.locale = 'en'
    session.add(article)
    session.commit()

    article.name  # Some article (even if current locale is other than 'en')


The locales can also be attribute dependent so you can set up translation hybrid in a way that
it is guaranteed to return a translation.

::

    translation_hybrid.default_locale = lambda obj, attr: sorted(getattr(obj, attr).keys())[0]


    article.name  # Some article




.. _SQLAlchemy-i18n: https://github.com/kvesteri/sqlalchemy-i18n