PK C,Ȅ django-slim-0.7/searchindex.jsSearch.setIndex({objects:{"slim.models.__init__.Slim":{available_translations_exclude_current_admin:[0,3,1,""],available_translations_admin:[0,3,1,""],original_translation:[0,1,1,""],get_original_translation:[0,3,1,""],is_multilingual:[0,1,1,""],available_translations:[0,3,1,""],get_translation_for:[0,3,1,""],translation_admin:[0,3,1,""],get_redirect_to_target:[0,3,1,""]},"slim.admin":{SlimAdmin:[0,4,1,""]},"slim.models.__init__":{SlimBaseModel:[0,4,1,""],Slim:[0,4,1,""]},"slim.models.fields":{SimpleLanguageField:[0,4,1,""],LanguageField:[0,4,1,""]},"slim.models":{fields:[0,0,1,""],decorators:[0,0,1,""],"__init__":[0,0,1,""]},slim:{admin:[0,0,1,""],translations:[0,0,1,""],utils:[0,0,1,""],helpers:[0,0,1,""],conf:[0,0,1,""]},"slim.models.__init__.SlimBaseModel.Meta":{"abstract":[0,1,1,""]},"slim.models.fields.LanguageField":{contribute_to_class:[0,3,1,""],validate:[0,3,1,""],formfield:[0,3,1,""]},"slim.admin.SlimAdmin":{language_field:[0,1,1,""],auto_add_list_view:[0,1,1,""],collapse_slim_fieldset:[0,1,1,""],media:[0,1,1,""],declared_fieldsets:[0,1,1,""],queryset:[0,3,1,""],get_readonly_fields:[0,3,1,""],get_list_display:[0,3,1,""],get_list_filter:[0,3,1,""],list_view_primary_only:[0,1,1,""],auto_add_edit_view:[0,1,1,""]},"slim.models.__init__.SlimBaseModel":{Meta:[0,4,1,""]},"slim.helpers":{get_language_from_request:[0,2,1,""],get_languages:[0,2,1,""],admin_change_url:[0,2,1,""],smart_resolve:[0,2,1,""],get_languages_dict:[0,2,1,""],get_languages_keys:[0,2,1,""],admin_add_url:[0,2,1,""],get_default_language:[0,2,1,""]},"slim.templatetags":{slim_tags:[0,0,1,""]},"slim.conf":{get_setting:[0,2,1,""]},"slim.models.decorators":{prepend_language:[0,2,1,""],auto_prepend_language:[0,2,1,""],localeurl_prepend_language:[0,2,1,""]},"slim.templatetags.slim_tags":{multiling_is_enabled:[0,2,1,""],get_translated_object_for:[0,2,1,""],slim_language_name:[0,2,1,""],set_language:[0,2,1,""],get_translated_objects_for:[0,2,1,""]},"slim.utils":{locale_url_is_installed:[0,2,1,""]},"slim.translations":{is_primary_language:[0,2,1,""],short_language_code:[0,2,1,""]},"slim.models.fields.SimpleLanguageField":{formfield:[0,3,1,""]}},terms:{all:0,code:0,forget:0,queri:0,barseghyan:0,fooitemadmin:0,follow:0,multiling_is_en:0,lgpl:0,get_languag:0,articl:0,introduc:0,sourc:0,fieldset:0,string:0,fals:0,list_view_primary_onli:0,util:0,fall:0,brows:0,list:0,iter:0,"try":0,item:0,quick:0,language_field:0,admin_change_url:0,pass:0,append:0,index:0,compar:0,section:0,abl:0,find:0,current:0,version:0,languagefield:0,"new":0,method:0,bodi:0,let:0,path:0,sinc:0,valu:0,box:0,search:0,orm:0,wheither:0,pick:0,chang:0,prepend_languag:0,extra:0,modul:0,"boolean":0,org:0,instal:0,armenian_foo:0,middlewar:0,from:0,would:0,doubl:0,regist:0,more:0,flat:0,preciou:0,accept:0,obj:0,simplelanguagefield:0,none:0,work:0,uniqu:0,dutch_foo:0,kwarg:0,can:0,slim_language_nam:0,admin_add_url:0,def:0,overrid:0,slim:0,templat:0,tag:0,alwai:0,end:0,contribute_to_class:0,url_titl:0,instead:0,simpl:0,is_multilingu:0,collaps:0,earlier:0,language_nam:0,mai:0,"short":0,ani:0,django:0,issu:0,"switch":0,environ:0,indic:0,egg:0,order:0,offici:0,templatetag:0,"_default_manag":0,paramet:0,melthod:0,collapse_slim_fieldset:0,wget:0,mail:0,main:0,might:0,"return":0,thei:0,python:0,var_nam:0,front:0,now:0,ipsum:0,name:0,edit:0,revers:0,easili:0,token:0,fulli:0,available_translations_admin:0,meta:0,get_translated_objects_for:0,our:0,happen:0,extract:0,out:0,variabl:0,shown:0,localeurl:0,dutch:0,integr:0,differ:0,fixm:0,base:0,get_redirect_to_target:0,slim_tag:0,lingual:0,filter:0,get_languages_dict:0,app_label:0,get_original_transl:0,charfield:0,assign:0,first:0,origin:0,auto_add_list_view:0,template_nam:0,instruct:0,alreadi:0,stabl:0,installed_app:0,primari:0,gpl:0,given:0,licens:0,max_length:0,urlresolv:0,store:0,option:0,get_absolute_url:0,specifi:0,github:0,textfield:0,than:0,target:0,tree:0,project:0,str:0,minut:0,ugettext:0,get_language_from_request:0,queryset:0,argument:0,packag:0,have:0,tabl:0,need:0,get_languages_kei:0,self:0,object_id:0,note:0,also:0,contact:0,which:0,noth:0,sure:0,shall:0,usernam:0,get_readonly_field:0,previou:0,"class":0,url:0,request:0,doe:0,latest:0,fooitem:0,available_translations_exclude_current_admin:0,syntax:0,hack:0,anywai:0,redirect:0,onli:0,execut:0,available_transl:0,dict:0,local:0,onlt:0,get:0,pypi:0,localeurlmiddlewar:0,requir:0,enabl:0,slimadmin:0,admin_sit:0,contain:0,grab:0,where:0,view:0,set:0,contrib_to_class:0,accord:0,model_inst:0,see:0,result:0,arg:0,best:0,gmail:0,auto_prepend_languag:0,review:0,"import":0,approach:0,modeladmin:0,attribut:0,altern:0,get_set:0,barseghyanartur:0,smart_resolv:0,kei:0,extens:0,armenian:0,set_languag:0,behaviour:0,admin:0,equal:0,formfield:0,auto_add_edit_view:0,context:0,mani:0,com:0,is_primary_languag:0,simpli:0,slugfield:0,short_language_cod:0,get_list_filt:0,respect:0,assum:0,creat:0,translation_of:0,extra_path:0,quickli:0,mind:0,locale_url_is_instal:0,func:0,i18n:0,those:0,"case":0,multi:0,raw:0,properti:0,defin:0,error:0,helper:0,demo:0,site:0,pip:0,itself:0,translated_articl:0,conf:0,module_nam:0,"__init__":0,httprequest:0,decor:0,welcom:0,author:0,media:0,make:0,same:0,html:0,document:0,http:0,closest:0,moment:0,rais:0,user:0,get_translated_object_for:0,implement:0,kept:0,thu:0,exampl:0,thi:0,english:0,model:0,multilinug:0,left:0,just:0,declared_fieldset:0,verbose_nam:0,rest:0,slimbasemodel:0,gettext:0,yet:0,languag:0,mix:0,lang_cod:0,add:0,other:0,save:0,app:0,smart:0,advis:0,get_default_languag:0,piec:0,chmod:0,password:0,resolv:0,either:0,list_displai:0,page:0,right:0,some:0,back:0,foo:0,refer:0,core:0,object:0,run:0,imaginari:0,usag:0,step:0,prerequisit:0,original_transl:0,slug:0,src:0,inter:0,actual:0,translated_object:0,tranlat:0,own:0,within:0,automat:0,been:0,artur:0,contrib:0,get_list_displai:0,your:0,git:0,wai:0,support:0,why:0,avail:0,reli:0,get_translation_for:0,includ:0,"var":0,fork:0,properli:0,form:0,tupl:0,bitbucket:0,localeurl_prepend_languag:0,link:0,translat:0,russian:0,"true":0,"default":0,record:0,below:0,foreignkei:0,otherwis:0,evalu:0,"int":0,"abstract":0,parser:0,repres:0,exist:0,translation_admin:0,check:0,multil:0,titl:0,when:0,detail:0,virtual:0,prepend:0,field:0,valid:0,bool:0,test:0,you:0,middleware_class:0,lorem:0,debian:0,global_set:0,directori:0,descript:0},objtypes:{"0":"py:module","1":"py:attribute","2":"py:function","3":"py:method","4":"py:class"},titles:["Package"],objnames:{"0":["py","module","Python module"],"1":["py","attribute","Python attribute"],"2":["py","function","Python function"],"3":["py","method","Python method"],"4":["py","class","Python class"]},filenames:["index"]})PK Cg django-slim-0.7/py-modindex.html
django-slim
Simple implementation of multi-lingual models for Django. Django-admin integration works out of the box. Supports django-localeurl integration.
Note, that Django 1.5 is required. Earlier versions are not supported.
$ pip install django-slim
$ pip install -e hg+https://bitbucket.org/barseghyanartur/django-slim@stable#egg=django-slim
$ pip install -e git+https://github.com/barseghyanartur/django-slim/@stable#egg=django-slim
An extensive example project is available at https://github.com/barseghyanartur/django-slim/tree/stable/example directory.
In order to be able to quickly evaluate the django-slim, a demo app (with a quick installer) has been created (Debian only). Follow the instructions below for having the demo running within a minute.
Grab the latest django-slim-example-app-install.sh
Create a new- or switch to existing- virtual environement, assign execute rights to the installer and run the django-slim-example-app-install.sh.
$ chmod +x django-slim-example-app-install.sh
$ ./django-slim-example-app-install.sh
Go to the front/back -end and test the app.
Let’s now step-by-step review our imaginary example app.
Add slim to installed apps.
>>> INSTALLED_APPS = (
>>> # ...
>>> 'slim',
>>> # ...
>>> )
Add languages.
>>> LANGUAGES = (
>>> ('en', gettext("English")), # Main language!
>>> ('am', gettext("Armenian")),
>>> ('nl', gettext("Dutch")),
>>> ('ru', gettext("Russian")),
>>> )
>>> from django.db import models
>>>
>>> from slim import LanguageField, Slim
>>>
>>> class FooItem(models.Model, Slim):
>>> title = models.CharField(_("Title"), max_length=100)
>>> slug = models.SlugField(unique=True, verbose_name=_("Slug"))
>>> body = models.TextField(_("Body"))
>>> language = LanguageField()
>>> from django.contrib import admin
>>>
>>> from slim.admin import SlimAdmin
>>>
>>> class FooItemAdmin(SlimAdmin):
>>> list_display = ('title',)
>>> fieldsets = (
>>> (None, {
>>> 'fields': ('title', 'slug', 'body')
>>> }),
>>> )
>>>
>>> admin.site.register(FooItem, FooItemAdmin)
We assume that language code is kept in the request object (django-localeurl behaviour, which you’re advised to use).
>>> from slim import get_language_from_request
>>>
>>> from example.models import FooItem
>>>
>>> def browse(request, template_name='foo/browse.html'):
>>> language = get_language_from_request(request)
>>> queryset = FooItem._default_manager.filter(language=language)
>>>
>>> # The rest of the code
>>> from example.models import FooItem
>>> foo = FooItem._default_manager.all()[0]
<FooItem: Lorem ipsum>
Let’s assume, we have such record and it has been translated to Armenian (am) and Dutch (nl). Original translation is named Lorem ipsum. Other translations have the language code appended to the title.
>>> armenian_foo = foo.get_translation_for('am')
<FooItem: Lorem ipsum AM>
>>> dutch_foo = foo.get_translation_for('nl')
<FooItem: Lorem ipsum NL>
If we have a translated object, we can always get the main translation.
>>> armenian_foo.original_translation == foo
True
All available translations for foo:
>>> foo.available_translations.all()
[<FooItem: Lorem ipsum AM>, <FooItem: Lorem ipsum NL>]
All available translations for Armenian foo.
>>> armenian_foo.available_translations.all()
[<FooItem: Lorem ipsum>, <FooItem: Lorem ipsum NL>]
See https://bitbucket.org/barseghyanartur/django-slim/src (example) directory for a working example.
List view:
Edit view for main language:
Edit view for translated item:
django-localeurl integration is fully supported for Python 2.6.* and 2.7.* and installs automatically when installing django-slim. If you are using Python 3, install a forked version of django-localeurl (since official version does not yet have support for Python 3).
Forked version from bitbucket:
$ pip install -e hg+https://bitbucket.org/barseghyanartur/django-localeurl@stable#egg=localeurl
Use slim.models.decorators.auto_prepend_language decorator in order to have it working.
Example (have in mind our FooItem model.
>>> from django.core.urlresolvers import reverse
>>>
>>> from slim.models.decorators import auto_prepend_language
>>>
>>> class FooItem(models.Model):
>>> # Some other code; have in mind previous pieces.
>>> @auto_prepend_language
>>> def get_absolute_url(self):
>>> kwargs = {'slug': self.slug}
>>> return reverse('foo.detail', kwargs=kwargs)
Do not forget to add the LocaleURLMiddleware to the MIDDLEWARE_CLASSES (as first).
>>> MIDDLEWARE_CLASSES = (
>>> 'localeurl.middleware.LocaleURLMiddleware',
>>> # The rest...
>>> )
Also, add localeurl to INSTALLED_APPS.
>>> INSTALLED_APPS = (
>>> # Some apps...
>>> 'localeurl',
>>> # Some more apps...
>>> )
Bases: object
Add this class to all your multi-lingual Django models, where you use slim.models.fields.LanguageField. Alternatively, you may use the slim.models.SlimBaseModel.
Returns available translations.
Return interable: | |
---|---|
At this moment a list of objects. |
Gets a HTML with all available translation URLs for current object if available. For admin use.
Return str: |
---|
Same as available_translations_admin but does not include itself to the list.
Return str: |
---|
Gets original translation of current object.
Return obj: | Object of the same class as the one queried. |
---|
Find an acceptable redirect target. If this is a local link, then try to find the page this redirect references and translate it according to the user’s language. This way, one can easily implement a localized “/”-url to welcome page redirection.
Get translation article in given language.
Parameters: | language (str) – Which shall be one of the languages specified in LANGUAGES in settings.py. |
---|---|
Return obj: | Either object of the same class as or None if no translations are available for the given language. |
Simple flat to use on objects to find our wheither they are multilinugal or not
Return bool: | Always returns boolean True |
---|
Bases: django.db.models.fields.CharField
LanguageField model. Stores language string in a CharField field.
Using contrib_to_class melthod adds translation_of field, which is simply a ForeignKey to the same class.
Validating the field.
We shall make sure that there are double translations for the same language for the same object. That’s why, in case if model is not yet saved (translated_object does not yet have a primary key), we check if there are already translations of the same object in the language we specify now.
Otherwise, if model_instance already has a primary key, we anyway try to get a translated_object and compare it with our model_instance. In case if translated_object exists and not equal to our model_instance we raise an error.
NOTE: This has nothing to do with unique fields in the original model_instance. Make sure you have properly specified all unique attributes with respect to LanguageField` of your original ``model_instance if you need those records to be unique.
Prepends the language from the model to the path resolved.
Prepends the language from the model to the path resolved when django-localeurl package is used.
Prepends the language from the model to the path resolved.
Bases: django.contrib.admin.options.ModelAdmin
SlimAdmin.
list_view_primary_only - if set to True, onlt primary language items would be shown in the list view. Default value is False.
language_field - name of the language field defined in your model. Default value language.
auto_add_edit_view - if set to True, extra fields for language editing are added to the list view. Do NOT set this value to False!
collapse_slim_fieldset if set to True, the language fieldset is shown collapsed.
Gets language from HttpRequest
Parameters: |
|
---|---|
Return str: |
Gets an admin change URL for the object given.
Parameters: |
|
---|---|
Return str: |
Gets an admin edit URL for the object given.
Parameters: |
|
---|---|
Return str: |
Resolves variable from context in a smart way. First trying to resolve from context and when result is None checks if variable is not None and returns just variable when not. Otherwise returns None.
Parameters: |
|
---|---|
Return mixed: |
Gets translated object for the object given.
Gets translations available for the given object.
Sets current language code.
FIXME: This is actually a hack.
Checks if multiling shall be enabled (in templates). Simply, if LANGUAGES tuple contains more than one language, we return boolean True; otherwise - boolean False.
Not all languages are available in Django yet. It might happen that support for your own precious language is not yet available in Django and many apps rely on languages list defined in django.conf.global_settings module.
Thus, to have translations for your own language available, the following approach is introduced: - Pick the language code closest to your language but name it differently: ((‘ar’, ugettext(‘Armenian’)),). - Instead of using Django’s language_name filter (of i18n module) use slim_language_name filter just the
same way.
This filter would get your item tranlation based on your project translations.
Parameters: | lang_code (str) – |
---|---|
Return str: |
GPL 2.0/LGPL 2.1
For any issues contact me at the e-mail given in the Author section.
Artur Barseghyan <artur.barseghyan@gmail.com>
__title__ = 'slim.utils'
__version__ = '0.7'
__build__ = 0x000007
__author__ = 'Artur Barseghyan <artur.barseghyan@gmail.com>'
__all__ = ('locale_url_is_installed',)
from django.conf import settings
from slim.settings import USE_LOCALEURL
[docs]def locale_url_is_installed():
"""
Checks if localeurl is installed in the Django project.
:return bool:
"""
if USE_LOCALEURL is True and 'localeurl' in settings.INSTALLED_APPS and \
'localeurl.middleware.LocaleURLMiddleware' in settings.MIDDLEWARE_CLASSES:
return True
return False
__title__ = 'slim.admin'
__version__ = '0.7'
__build__ = 0x000007
__author__ = 'Artur Barseghyan <artur.barseghyan@gmail.com>'
__all__ = ('SlimAdmin',)
from django.utils.translation import ugettext_lazy as _
from django.contrib import admin
from slim.helpers import default_language
[docs]class SlimAdmin(admin.ModelAdmin):
"""
SlimAdmin.
``list_view_primary_only`` - if set to True, onlt primary language items would be shown in the list
view. Default value is False.
``language_field`` - name of the language field defined in your model. Default value `language`.
``auto_add_edit_view`` - if set to True, extra fields for language editing are added to the list view.
Do NOT set this value to False!
``collapse_slim_fieldset`` if set to True, the language fieldset is shown collapsed.
"""
# If set to True, only primary language objects are shown in the list view.
list_view_primary_only = False
# Language field in your model
language_field = 'language'
# If set to True, languages fields are added to the edit view (as a fieldset)
auto_add_edit_view = True
# If set to True, languages fieldset is auto added to the list view
auto_add_list_view = True
# If set to True, the fieldset is shown collapsed.
collapse_slim_fieldset = True
[docs] def queryset(self, *args, **kwargs):
# For faster admin load we use ``prefetch_related``. Note, that this doesn't work on Django < 1.5.
queryset = super(SlimAdmin, self).queryset(*args, **kwargs) \
.prefetch_related('translations') \
.select_related('translation_of')
if self.list_view_primary_only is True:
f = {self.language_field: default_language}
queryset = queryset.filter(**f)
return queryset
[docs] def get_list_display(self, *args, **kwargs):
list_display = super(SlimAdmin, self).get_list_display(*args, **kwargs)
if self.auto_add_list_view:
list_display = list(list_display)
list_display.extend(
(self.language_field, 'available_translations_admin')
)
return list_display
[docs] def get_readonly_fields(self, *args, **kwargs):
readonly_fields = super(SlimAdmin, self).get_readonly_fields(*args, **kwargs)
if self.auto_add_list_view:
readonly_fields = list(readonly_fields)
readonly_fields.extend(
('available_translations_exclude_current_admin',)
)
return readonly_fields
[docs] def get_list_filter(self, *args, **kwargs):
list_filter = super(SlimAdmin, self).get_list_filter(*args, **kwargs)
if list_filter is None:
return [self.language_field]
else:
list_filter = list(list_filter)
list_filter.append(self.language_field)
return list_filter
def _declared_fieldsets(self, *args, **kwargs):
declared_fieldsets = super(SlimAdmin, self)._declared_fieldsets(*args, **kwargs)
if self.auto_add_edit_view:
declared_fieldsets = list(declared_fieldsets)
declared_fieldsets.append(
(_("Translations"), {
'classes': ('collapse' if self.collapse_slim_fieldset else '',),
'fields': (self.language_field, 'translation_of', 'available_translations_exclude_current_admin',)
}),
)
return declared_fieldsets
declared_fieldsets = property(_declared_fieldsets)
__title__ = 'slim.translations'
__version__ = '0.7'
__build__ = 0x000007
__author__ = 'Artur Barseghyan <artur.barseghyan@gmail.com>'
__all__ = ('short_language_code', 'is_primary_language')
from django.utils import translation
from slim.helpers import default_language
[docs]def short_language_code(code=None):
"""
Extracts the short language code from its argument (or return the default language code).
:param str code:
:return str:
from django.conf import settings
>>> short_language_code('de')
'de'
>>> short_language_code('de-at')
'de'
"""
if code is None:
code = translation.get_language()
pos = code.find('-')
if pos > -1:
return code[:pos]
return code
[docs]def is_primary_language(language=None):
"""
Returns true if current or passed language is the primary language for this site.
(The primary language is defined as the first language in settings.LANGUAGES.)
:param str language:
:return bool:
"""
if not language:
language = translation.get_language()
return language == default_language
__title__ = 'slim.helpers'
__version__ = '0.7'
__build__ = 0x000007
__author__ = 'Artur Barseghyan <artur.barseghyan@gmail.com>'
__all__ = ('get_default_language', 'default_language', 'get_languages', 'get_languages_keys', \
'get_language_from_request', 'get_languages_dict', 'admin_change_url', 'admin_add_url', 'smart_resolve')
from django.conf import settings
from django.core.urlresolvers import reverse
from django.utils.translation import get_language_info
from slim.settings import USE_LOCAL_LANGUAGE_NAMES
[docs]def get_default_language():
"""
Gets default language.
:return str:
"""
return settings.LANGUAGES[0][0]
default_language = get_default_language()
[docs]def get_languages():
"""
Gets available languages.
:return iterable:
"""
if not USE_LOCAL_LANGUAGE_NAMES:
return settings.LANGUAGES
else:
languages = []
for lang_code, lang_name in settings.LANGUAGES:
try:
lang_name = get_language_info(lang_code)['name_local']
except Exception as e:
pass
languages.append((lang_code, lang_name))
return languages
[docs]def get_languages_keys():
"""
Returns just languages keys.
:return list:
"""
return [key for key, name in get_languages()]
[docs]def get_languages_dict():
"""
Returns just languages dict.
:return dict:
"""
return dict(get_languages())
[docs]def get_language_from_request(request, default=default_language):
"""
Gets language from HttpRequest
:param django.http.HttpRequest:
:param str default:
:return str:
"""
if hasattr(request, 'LANGUAGE_CODE') and request.LANGUAGE_CODE:
return request.LANGUAGE_CODE
else:
return default
[docs]def admin_change_url(app_label, module_name, object_id, extra_path='', url_title=None):
"""
Gets an admin change URL for the object given.
:param str app_label:
:param str module_name:
:param int object_id:
:param str extra_path:
:param str url_title: If given, an HTML a tag is returned with `url_title` as the tag title. If left to None
just the URL string is returned.
:return str:
"""
try:
url = reverse('admin:%s_%s_change' %(app_label, module_name), args=[object_id]) + extra_path
if url_title:
return u'<a href="%s">%s</a>' %(url, url_title)
else:
return url
except:
return None
[docs]def admin_add_url(app_label, module_name, extra_path='', url_title=None):
"""
Gets an admin edit URL for the object given.
:param str app_label:
:param str module_name:
:param str extra_path:
:param str url_title: If given, an HTML a tag is returned with `url_title` as the tag title. If left to None
just the URL string is returned.
:return str:
"""
try:
url = reverse('admin:%s_%s_add' %(app_label, module_name)) + extra_path
if url_title:
return u'<a href="%s">%s</a>' %(url, url_title)
else:
return url
except:
return None
[docs]def smart_resolve(var, context):
"""
Resolves variable from context in a smart way. First trying to resolve from context
and when result is None checks if variable is not None and returns just variable
when not. Otherwise returns None.
:param str var:
:param Context context:
:return mixed:
"""
if var is None:
return None
ret_val = None
try:
ret_val = var.resolve(context, True)
except:
ret_val = var
if ret_val is None:
ret_val = var
return ret_val
__title__ = 'slim.conf'
__version__ = '0.7'
__build__ = 0x000007
__author__ = 'Artur Barseghyan <artur.barseghyan@gmail.com>'
__all__ = ('get_setting',)
from django.conf import settings
from slim import defaults
[docs]def get_setting(setting, override=None):
"""
Get a setting from ``slim`` conf module, falling back to the default.
If override is not None, it will be used instead of the setting.
"""
if override is not None:
return override
if hasattr(settings, 'SLIM_%s' % setting):
return getattr(settings, 'SLIM_%s' % setting)
else:
return getattr(defaults, setting)
__title__ = 'slim.templatetags.slim_tags'
__version__ = '0.7'
__build__ = 0x000007
__author__ = 'Artur Barseghyan <artur.barseghyan@gmail.com>'
__all__ = ('get_translated_object_for', 'get_translated_objects_for', 'set_language', 'multiling_is_enabled',
'slim_language_name')
import re
from django.utils.translation import ugettext_lazy as _
from django import template
from django.template import Library
from django.utils import translation
from slim.helpers import smart_resolve, default_language, get_language_from_request, get_languages_keys
from slim.helpers import get_languages_dict
register = Library()
class GetTranslatedObjectForNode(template.Node):
"""
Node for template tag ``get_translated_object_for``.
"""
def __init__(self, obj, as_var, language=None):
"""
:param obj: Object to translate.
:param string as_var: Desired variable name in the template context.
:param str language: Language code. Must be one of those defined in ``LANGUAGES``
of "settigns.py"
:raise template.TemplateSyntaxError: If invalid syntax or in case if HttpRequest
can't be retrieved from template context, while ``language`` attribute is not
specified.
"""
self.as_var = as_var
self.obj = obj
self.language = language
def render(self, context):
ret_val = None
obj = smart_resolve(self.obj, context)
try:
is_multilingual = obj.is_multilingual
except AttributeError as e:
is_multilingual = False
raise template.TemplateSyntaxError(
_("Invalid usage of ``get_translated_object_for``. Translated object shall be multilingual.")
)
try:
request = context['request']
except:
request = None
if language is None and request is None:
raise template.TemplateSyntaxError(
_("Invalid usage of ``get_translated_object_for``. Can't retrieve ``HttpRequest`` object from "
"template context, while ``language`` attribute is not specified.")
)
if language is None:
language = get_language_from_request(request)
else:
language = smart_resolve(self.language, context)
if language:
language = str(language)
else:
language = default_language
ret_val = obj.get_translation_for(language)
if self.as_var:
context[self.as_var] = ret_val
return ''
return ret_val
@register.tag
[docs]def get_translated_object_for(parser, token):
"""
Gets translated object for the object given.
Syntax::
{% get_translated_object_for [object] language=[language] as [var_name] %}
Example usage::
{% get_translated_object_for article as translated_article %}
{% get_translated_object_for article language=ru as translated_article %}
"""
bits = token.contents.split()
if 'as' != bits[-2]:
raise template.TemplateSyntaxError(
_("Invalid syntax for %s. You must specify a name for translated object." % bits[0])
)
as_var = bits[-1]
try:
obj = parser.compile_filter(bits[1])
except:
template.TemplateSyntaxError(_("Invalid syntax for %s. You must provide an object to translate." % bits[0]))
m = re.search(r'language=(\w+)', ' '.join(bits[2:-2]))
if m:
language = parser.compile_filter(m.groups()[0])
else:
language = None
return GetTranslatedObjectForNode(obj=obj, as_var=as_var, language=language)
class GetTranslatedObjectsForNode(template.Node):
"""
Node for template tag ``get_translated_objects_for``.
"""
def __init__(self, obj, as_var):
"""
:param obj: Object to get translations for.
:param str as_var: Desired variable name in the template context.
:raise template.TemplateSyntaxError: If invalid syntax or in case if HttpRequest
can't be retrieved from template context, while ``language`` attribute is not
specified.
"""
self.as_var = as_var
self.obj = obj
def render(self, context):
ret_val = None
obj = smart_resolve(self.obj, context)
try:
is_multilingual = obj.is_multilingual
except AttributeError as e:
is_multilingual = False
raise template.TemplateSyntaxError(
_("Invalid usage of get_translated_object_for. Translated object shall be multilingual.")
)
ret_val = obj.available_translations()
if self.as_var:
context[self.as_var] = ret_val
return ''
return ret_val
@register.tag
[docs]def get_translated_objects_for(parser, token):
"""
Gets translations available for the given object.
Syntax::
{% get_translated_objects_for [object] as [var_name] %}
Example usage::
{% get_translated_objects_for article as translated_article %}
"""
bits = token.contents.split()
if 'as' != bits[-2]:
raise template.TemplateSyntaxError(
_("Invalid syntax for %s. You must specify a name for translated object." % bits[0])
)
as_var = bits[-1]
try:
obj = parser.compile_filter(bits[1])
except:
template.TemplateSyntaxError(_("Invalid syntax for %s. You must provide an object to translate." % bits[0]))
return GetTranslatedObjectsForNode(obj=obj, as_var=as_var)
class SetLanguageNode(template.Node):
"""
Node for ``set_language`` tag.
"""
def __init__(self, language=None):
self.language = language
def render(self, context):
# Try to get request.LANGUAGE_CODE. If fail, use default one.
request = context['request']
language = get_language_from_request(request, default=None)
if not language:
language = smart_resolve(self.language, context)
if not language in get_languages_keys():
language = default_language
translation.activate(language)
return ''
@register.tag
[docs]def set_language(parser, token):
"""
Sets current language code.
FIXME: This is actually a hack.
Syntax::
{% set_language [language] %}
Example::
{% set_language ru %}
"""
bits = token.contents.split()
if 2 < len(bits):
raise template.TemplateSyntaxError("'%s' tag takes one argument at most" % bits[0])
elif 2 == len(bits):
language = parser.compile_filter(bits[1])
else:
language = None
return SetLanguageNode(language=language)
class MultilinIsEnabledNode(template.Node):
"""
Node for ``multiling_is_enabled`` tag.
"""
def __init__(self, as_var=None):
self.as_var = as_var
def render(self, context):
# Try to get request.LANGUAGE_CODE. If fail, use default one.
if len(get_languages_keys()) > 1:
ret_val = True
else:
ret_val = False
if self.as_var:
context[self.as_var] = ret_val
return ''
return ret_val
@register.tag
[docs]def multiling_is_enabled(parser, token):
"""
Checks if multiling shall be enabled (in templates). Simply, if LANGUAGES tuple contains more than one
language, we return boolean True; otherwise - boolean False.
Syntax::
{% multiling_is_enabled as [var_name] %}
Example::
{% multiling_is_enabled as multiling_is_enabled %}
"""
bits = token.contents.split()
if not len(bits) in (1, 3):
raise template.TemplateSyntaxError("'%s' tag takes five arguments at most" % bits[0])
if 3 == len(bits):
if 'as' != bits[-2]:
raise template.TemplateSyntaxError(
_("Invalid syntax for %s. You must specify a name for translated object." % bits[0])
)
as_var = bits[-1]
else:
as_var = None
return MultilinIsEnabledNode(as_var=as_var)
@register.filter
[docs]def slim_language_name(lang_code):
"""
Not all languages are available in Django yet. It might happen that support for your own precious language is
not yet available in Django and many apps rely on languages list defined in `django.conf.global_settings` module.
Thus, to have translations for your own language available, the following approach is introduced:
- Pick the language code closest to your language but name it differently: (('ar', ugettext('Armenian')),).
- Instead of using Django's `language_name` filter (of `i18n` module) use `slim_language_name` filter just the
same way.
This filter would get your item tranlation based on your project translations.
:param str lang_code:
:return str:
"""
return _(get_languages_dict()[lang_code])
@register.filter
def slim_language_local_name(lang_code):
"""
Localised language name.
:param str lang_code:
:return str:
"""
try:
return translation.get_language_info(lang_code)['name_local']
except:
return lang_code
__title__ = 'slim.models.decorators'
__version__ = '0.7'
__build__ = 0x000007
__author__ = 'Artur Barseghyan <artur.barseghyan@gmail.com>'
__all__ = ('prepend_language', 'localeurl_prepend_language', 'auto_prepend_language')
from slim.utils import locale_url_is_installed
[docs]def prepend_language(func, language_field='language'):
"""
Prepends the language from the model to the path resolved.
"""
def inner(self, *args, **kwargs):
return "/%s%s" % (getattr(self, language_field), func(self, *args, **kwargs))
return inner
try:
from localeurl.templatetags.localeurl_tags import chlocale
def localeurl_prepend_language(func, language_field='language'):
"""
Prepends the language from the model to the path resolved when `django-localeurl` package is used.
"""
def inner(self, *args, **kwargs):
return chlocale(func(self, *args, **kwargs), getattr(self, language_field))
return inner
# If `localeurl` is available gets the `localeurl` based decorator. Otherwise, gets `prepend_language`
# based decorator.
if locale_url_is_installed():
auto_prepend_language = localeurl_prepend_language
else:
auto_prepend_language = prepend_language
except ImportError as e:
from slim.exceptions import LocaleurlImportError
[docs] def localeurl_prepend_language(func, language_field=None):
raise LocaleurlImportError(
"You should have localeurl installed in order to use "
"`slim.models.localeurl_prepend_language` decorator."
)
# Fallback
auto_prepend_language = prepend_language
__title__ = 'slim.models.__init__'
__version__ = '0.7'
__build__ = 0x000007
__author__ = 'Artur Barseghyan <artur.barseghyan@gmail.com>'
__all__ = ('Slim', 'SlimBaseModel')
from six import PY2
from django.db import models
from django.utils.translation import ugettext_lazy as _
from slim import get_languages, default_language, get_languages_keys
from slim.translations import is_primary_language
from slim.helpers import admin_change_url, admin_add_url
[docs]class Slim(object):
"""
Add this class to all your multi-lingual Django models, where you use ``slim.models.fields.LanguageField``.
Alternatively, you may use the ``slim.models.SlimBaseModel``.
"""
@property
[docs] def is_multilingual(self):
"""
Simple flat to use on objects to find our wheither they are multilinugal
or not
:return bool: Always returns boolean True
"""
return True
[docs] def get_redirect_to_target(self, request):
"""
Find an acceptable redirect target. If this is a local link, then try
to find the page this redirect references and translate it according
to the user's language. This way, one can easily implement a localized
"/"-url to welcome page redirection.
"""
target = self.redirect_to
if target and target.find('//') == -1: # Not an offsite link http://bla/blubb
try:
page = cls.objects.page_for_path(target)
page = page.get_translation(getattr(request, 'LANGUAGE_CODE', None))
target = page.get_absolute_url()
except cls.DoesNotExist:
pass
return target
[docs] def available_translations(self):
"""
Returns available translations.
:return interable: At this moment a list of objects.
"""
if not self.id: # New, unsaved pages have no translations
return []
if is_primary_language(self.language):
return self.translations.all()
elif self.translation_of:
return [self.translation_of] + list(self.translation_of.translations.exclude(
language=self.language))
else:
return []
[docs] def get_original_translation(self, *args, **kwargs):
"""
Gets original translation of current object.
:return obj: Object of the same class as the one queried.
"""
if is_primary_language(self.language):
return self
return self.translation_of
[docs] def translation_admin(self, *args, **kwargs):
"""
Gets a HTML with URL to the original translation of available. For admin use.
:return str:
"""
if self.translation_of:
if PY2:
url_title = unicode(self.translation_of)
else:
url_title = self.translation_of
return admin_change_url(
self._meta.app_label,
self._meta.module_name,
self.translation_of.id,
url_title = url_title
)
return ''
translation_admin.allow_tags = True
translation_admin.short_description = _('Translation of')
def _available_translations_admin(self, include_self=True):
"""
Gets a HTML with all available translation URLs for current object if available. For admin use.
:return str:
"""
try:
original_translation = self.original_translation
available_translations = list(self.available_translations())
languages_keys = get_languages_keys()
languages = dict(get_languages())
if include_self:
available_translations.append(self)
output = []
# Processing all available translations. Adding edit links.
if available_translations:
for translation in available_translations:
if PY2:
url_title = unicode(languages[translation.language])
else:
url_title = languages[translation.language]
output.append(
admin_change_url(
translation._meta.app_label,
translation._meta.module_name,
translation.id,
url_title = url_title
)
)
languages_keys.remove(translation.language)
if self.pk and self.language in languages_keys:
languages_keys.remove(self.language)
# For all languages that are still available (original object has no translations for)
for language in languages_keys:
url = admin_add_url(
self._meta.app_label,
self._meta.module_name,
'?translation_of=%s&language=%s' % (str(original_translation.id), language)
)
if PY2:
name = unicode(languages[language])
else:
name = languages[language]
output.append(u'<a href="%(url)s" style="color:#baa">%(name)s</a>' % {'url': url, 'name': name})
return u' | '.join(output)
except Exception as e:
return u''
[docs] def available_translations_admin(self, *args, **kwargs):
"""
Gets a HTML with all available translation URLs for current object if available. For admin use.
:return str:
"""
return self._available_translations_admin(include_self=True, *args, **kwargs)
available_translations_admin.allow_tags = True
available_translations_admin.short_description = _('Translations')
[docs] def available_translations_exclude_current_admin(self, *args, **kwargs):
"""
Same as `available_translations_admin` but does not include itself to the list.
:return str:
"""
return self._available_translations_admin(include_self=False, *args, **kwargs)
available_translations_exclude_current_admin.allow_tags = True
available_translations_exclude_current_admin.short_description = _('Translations')
@property
[docs] def original_translation(self):
"""
Property for ``get_original_translation`` method.
:return obj: Object of the same class as the one queried.
"""
return self.get_original_translation()
[docs] def get_translation_for(self, language):
"""
Get translation article in given language.
:param str language: Which shall be one of the languages specified in ``LANGUAGES``
in `settings.py`.
:return obj: Either object of the same class as or None if no translations are
available for the given ``language``.
"""
if not language in get_languages_keys():
return None
if str(self.language) == str(language):
return self
if str(self.original_translation.language) == str(language):
return self.original_translation
try:
return self.original_translation.translations.get(language=language)
except Exception as e:
return None
__title__ = 'slim.models.fields'
__version__ = '0.7'
__build__ = 0x000007
__author__ = 'Artur Barseghyan <artur.barseghyan@gmail.com>'
__all__ = ('LanguageField', 'SimpleLanguageField')
from six import PY2
from django.db import models
from django.core import exceptions
from django.utils.translation import ugettext_lazy as _
from slim import get_languages, default_language, get_languages_keys
from slim.settings import ENABLE_MONKEY_PATCHING
from slim.monkey_patches import monkeypatch_method, monkeypatch_property
from slim.helpers import admin_change_url, admin_add_url
[docs]class LanguageField(models.CharField):
"""
LanguageField model. Stores language string in a ``CharField`` field.
Using `contrib_to_class` melthod adds `translation_of` field, which is simply a ``ForeignKey``
to the same class.
"""
__metaclass__ = models.SubfieldBase
def __init__(self, *args, **kwargs):
"""
Create new field. Argument ``populate`` will be sent as-is to the form field.
"""
defaults = {
'verbose_name': _('Language'),
'populate': None,
'max_length': 10,
'choices': get_languages(),
'default': default_language
}
defaults.update(kwargs)
self.populate = defaults.pop('populate', None)
super(LanguageField, self).__init__(*args, **defaults)
[docs] def formfield(self, **kwargs):
"""
Returns best form field to represent this model field
"""
defaults = {
'form_class': LanguageField,
'populate': self.populate,
}
defaults.update(kwargs)
return super(models.CharField, self).formfield(**defaults)
[docs] def validate(self, value, model_instance):
"""
Validating the field.
We shall make sure that there are double translations for the same language for the same object. That's
why, in case if model is not yet saved (``translated_object`` does not yet have a primary key), we check
if there are already translations of the same object in the language we specify now.
Otherwise, if ``model_instance`` already has a primary key, we anyway try to get a ``translated_object``
and compare it with our ``model_instance``. In case if ``translated_object`` exists and not equal to our
``model_instance`` we raise an error.
NOTE: This has nothing to do with unique fields in the original ``model_instance``. Make sure you have
properly specified all unique attributes with respect to ``LanguageField` of your original
``model_instance`` if you need those records to be unique.
"""
if model_instance.original_translation:
translated_object = model_instance.original_translation.get_translation_for(value)
else:
translated_object = None
if not model_instance.pk:
if translated_object and translated_object.pk:
raise exceptions.ValidationError("Translation in language %s for this object already exists." % value)
else:
if translated_object and translated_object.pk and model_instance != translated_object:
raise exceptions.ValidationError("Translation in language %s for this object already exists." % value)
super(LanguageField, self).validate(value, model_instance)
[docs] def contribute_to_class(self, cls, name):
"""
Language field consists of more than one database record. We have ``lanaguage`` (CharField)
and ``translation_of`` (ForeignKey to ``cls``) in order to identify translated and
primary objects.
We have a set of very useful methods implemented in order to get translations easily.
"""
self.name = name
self.translation_of = models.ForeignKey(cls, blank=True, null=True, verbose_name=_('Translation of'), \
related_name='translations', \
limit_choices_to={'language': default_language}, \
help_text=_('Leave this empty for entries in the primary language.'))
cls.add_to_class('translation_of', self.translation_of)
super(LanguageField, self).contribute_to_class(cls, name)
if ENABLE_MONKEY_PATCHING:
@monkeypatch_property(cls)
def is_multilingual(self):
"""
Simple flat to use on objects to find our wheither they are multilinugal
or not
:return bool: Always returns boolean True
"""
return True
@monkeypatch_method(cls)
def get_redirect_to_target(self, request):
"""
Find an acceptable redirect target. If this is a local link, then try
to find the page this redirect references and translate it according
to the user's language. This way, one can easily implement a localized
"/"-url to welcome page redirection.
"""
target = self.redirect_to
if target and target.find('//') == -1: # Not an offsite link http://bla/blubb
try:
page = cls.objects.page_for_path(target)
page = page.get_translation(getattr(request, 'LANGUAGE_CODE', None))
target = page.get_absolute_url()
except cls.DoesNotExist:
pass
return target
@monkeypatch_method(cls)
def available_translations(self):
"""
Returns available translations.
:return interable: At this moment a list of objects.
"""
if not self.id: # New, unsaved pages have no translations
return []
if is_primary_language(self.language):
return self.translations.all()
elif self.translation_of:
return [self.translation_of] + list(self.translation_of.translations.exclude(
language=self.language))
else:
return []
@monkeypatch_method(cls)
def get_original_translation(self, *args, **kwargs):
"""
Gets original translation of current object.
:return obj: Object of the same class as the one queried.
"""
if is_primary_language(self.language):
return self
return self.translation_of
@monkeypatch_method(cls)
def translation_admin(self, *args, **kwargs):
"""
Gets a HTML with URL to the original translation of available. For admin use.
:return str:
"""
if self.translation_of:
if PY2:
url_title = unicode(self.translation_of)
else:
url_title = self.translation_of
return admin_change_url(
self._meta.app_label,
self._meta.module_name,
self.translation_of.id,
url_title = url_title
)
return ''
translation_admin.allow_tags = True
translation_admin.short_description = _('Translation of')
@monkeypatch_method(cls)
def _available_translations_admin(self, include_self=True):
"""
Gets a HTML with all available translation URLs for current object if available. For admin use.
:return str:
"""
try:
original_translation = self.original_translation
available_translations = list(self.available_translations())
languages_keys = get_languages_keys()
languages = dict(get_languages())
if include_self:
available_translations.append(self)
output = []
# Processing all available translations. Adding edit links.
if available_translations:
for translation in available_translations:
if PY2:
url_title = unicode(languages[translation.language])
else:
url_title = languages[translation.language]
output.append(
admin_change_url(
translation._meta.app_label,
translation._meta.module_name,
translation.id,
url_title = url_title
)
)
languages_keys.remove(translation.language)
if self.pk and self.language in languages_keys:
languages_keys.remove(self.language)
# For all languages that are still available (original object has no translations for)
for language in languages_keys:
url = admin_add_url(
self._meta.app_label,
self._meta.module_name,
'?translation_of=%s&language=%s' % (str(original_translation.id), language)
)
if PY2:
name = unicode(languages[language])
else:
name = languages[language]
output.append(u'<a href="%(url)s" style="color:#baa">%(name)s</a>' % {'url': url, 'name': name})
return ' | '.join(output)
except Exception as e:
return ''
@monkeypatch_method(cls)
def available_translations_admin(self, *args, **kwargs):
"""
Gets a HTML with all available translation URLs for current object if available. For admin use.
:return str:
"""
return self._available_translations_admin(include_self=True, *args, **kwargs)
available_translations_admin.allow_tags = True
available_translations_admin.short_description = _('Translations')
@monkeypatch_method(cls)
def available_translations_exclude_current_admin(self, *args, **kwargs):
"""
Same as `available_translations_admin` but does not include itself to the list.
:return str:
"""
return self._available_translations_admin(include_self=False, *args, **kwargs)
available_translations_exclude_current_admin.allow_tags = True
available_translations_exclude_current_admin.short_description = _('Translations')
@monkeypatch_property(cls)
def original_translation(self):
"""
Property for ``get_original_translation`` method.
:return obj: Object of the same class as the one queried.
"""
return self.get_original_translation()
@monkeypatch_method(cls)
def get_translation_for(self, language):
"""
Get translation article in given language.
:param str language: Which shall be one of the languages specified in ``LANGUAGES``
in `settings.py`.
:return obj: Either object of the same class as or None if no translations are
available for the given ``language``.
"""
if not language in get_languages_keys():
return None
if str(self.language) == str(language):
return self
if str(self.original_translation.language) == str(language):
return self.original_translation
try:
return self.original_translation.translations.get(language=language)
except Exception as e:
return None
[docs]class SimpleLanguageField(models.CharField):
"""
SimpleLanguageField model. Stores language string in a ``CharField`` field.
"""
def __init__(self, *args, **kwargs):
"""
Create new field. Argument ``populate`` will be sent as-is to the form field.
"""
defaults = {
'verbose_name': _('Language'),
'populate': None,
'max_length': 10,
'choices': get_languages(),
'default': default_language
}
defaults.update(kwargs)
self.populate = defaults.pop('populate', None)
super(SimpleLanguageField, self).__init__(*args, **defaults)
[docs] def formfield(self, **kwargs):
"""
Returns best form field to represent this model field
"""
defaults = {
'form_class': SimpleLanguageField,
'populate': self.populate,
}
defaults.update(kwargs)
return super(models.CharField, self).formfield(**defaults)
# Add schema's for South
try:
from south.modelsinspector import add_introspection_rules
add_introspection_rules(
rules=[((LanguageField,), [], {'populate': ('populate', {'default':None}),})], \
patterns=['slim.models\.fields\.LanguageField', 'slim.models\.fields\.SimpleLanguageField']
)
except ImportError:
pass