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
|