Package: django-tradukoj
TradukojTradujok is a django db-based translation system with IETF's BCP 47 standard support
and django-rest-framework serializers.Tradukoj can be integrated with common js i18n libs thanks to his JSON tree generation feature.This app is maintained and internally used by Develatio Technologies.RequierementsPython: >= 3.6Django: >= 2.1, 2.2FeaturesNamespaces to separate projects or big sections of your appJSON tree generation with your translationsdjango-rest-framework urls/views/serializersdjango-rest-framework fields to be used in serializersFallback translation for drf fieldsTranslate model fieldsLanguaje detection endpointRTL - LTR supportPO files Import/ExportPublic/Private translations isolationQuick startInstallpip install django-tradukojAdd "tradukoj" to INSTALLED_APPS:INSTALLED_APPS = {
    ...
    'tradukoj'
}Include the tradukoj URLconf in urls.py:url(r'^tradukoj/', include('tradukoj.urls'))Runpython manage.py migrateto create db records.Translate model fieldsUsage ofOneToOneTradukojField:from tradukoj.fields import OneToOneTradukojField

class MyModel(models.Model):
    name = OneToOneTradukojField(null=True, blank=True, verbose_name='Name')Handle translationsAutomatic key generation:>>> from tradukoj.models import TranslationKey
>>> from myapp.models import MyModel

>>> instance = MyModel()
>>> # This creates a translatable key with automatically generated name
>>> instance.name = TranslationKey(init_namespace='mynamespace', public=True)
>>> instance.name
<TranslationKey: mynamespace.myapp_mymodel_name_0def4d78080144cdbc96302842935192>

>>> # This will add a translation for es-ES to instance.name
>>> # A record with 'es-ES' into tradukoj.models.BCP47 will be created if it
>>> # does not exist.
>>> instance.name.translate('es-ES', 'mi nombre')

>>> # Access translations:
>>> instance.name.translations.all()
<QuerySet [<Translation: (es-ES - español (Spanish)) mynamespace.myapp_mymodel_name_0def4d78080144cdbc96302842935192 => mi nombre>]>

>>> instance.name.translations.get(bcp47__langtag='es-ES')
<Translation: (es-ES - español (Spanish)) test.medicalscience_specialties_name_0def4d78080144cdbc96302842935192 => mi nombre>

>>> instance.name.translations.get(bcp47__langtag='es-ES').str_translation()
'mi nombre'Custom key name:If you dont specify a key for translation, a random one will be generated. To
specify a key, you should pass it as argument toTranslationKey:>>> instance.name = TranslationKey(init_namespace='test', text='A translatable string', public=True)
>>> instance.name
<TranslationKey: test.A translatable string>
>>> instance.name.save()
>>> instance.name.translate('es-ES', 'Una cadena traducible')
>>> instance.name.translations.all()
<QuerySet [<Translation: (es-ES - español (Spanish)) test.A translatable string => Una cadena traducible>]>JSON tree of translationsIf you are planning to use json tree feature of tradukoj, remember that a dot in
key are handled as subobject.TranslationKey(init_namespace='project', text='home.title', public=True)
TranslationKey(init_namespace='project', text='home.subtitle', public=True)results into this json object:{
    es-ES: {
        project: {
            home: {
                title: 'Bienvenido',
                subtitle: 'Secciones'
            }
        }
    },
    en-US: {
        project: {
            home: {
                title: 'Welcome',
                subtitle: 'Sections'
            }
        }
    }
}API REST EndpointsLanguaje detection:YOUR_API_URL/tradukoj/bestlangtag/Asking fromes-ES;en(spanish-Spain,english) a web with onlyes-MX(spanish-Mexico) lang:[
    {
        "langtag": "es-MX",
        "score":92,
        "accept_lang": "es-es"
    },
    {
        "langtag": "es-MX",
        "score": 92,
        "accept_lang":"es"
    },
    {
        "langtag":"en",
        "score":100,
        "accept_lang":"en"
    }
]Available langs:YOUR_API_URL/tradukoj/available/{
    "count": 28,
    "next": null,
    "previous": null,
    "results": [
        {"langtag":"en-EN","name":"English (English)","best_match_score":92,"default":false,"direction":0},
        {"langtag":"es-MX","name":"español (Spanish)","best_match_score":92,"default":false,"direction":0},
        {"langtag":"en-US","name":"English (English)","best_match_score":98,"default":false,"direction":0},
        {"langtag":"ar-LB","name":"العربية (Arabic)","best_match_score":0,"default":false,"direction":1},
        {"langtag":"fr-FR","name":"français (French)","best_match_score":0,"default":false,"direction":0},
        {"langtag":"pt-PT","name":"português (Portuguese)","best_match_score":0,"default":false,"direction":0},
        {"langtag":"ru-RU","name":"русский (Russian)","best_match_score":0,"default":false,"direction":0},
        {"langtag":"de-DE","name":"Deutsch (German)","best_match_score":0,"default":false,"direction":0},
        {"langtag":"it-IT","name":"italiano (Italian)","best_match_score":0,"default":false,"direction":0},
        {"langtag":"hu-HU","name":"magyar (Hungarian)","best_match_score":0,"default":false,"direction":0},
        {"langtag":"cs-CZ","name":"čeština (Czech)","best_match_score":0,"default":false,"direction":0},
        {"langtag":"zh-CN","name":"中文 (Chinese)","best_match_score":0,"default":false,"direction":0},
        {"langtag":"vi-VN","name":"Tiếng Việt (Vietnamese)","best_match_score":0,"default":false,"direction":0},
        {"langtag":"hi-IN","name":"हिन्दी (Hindi)","best_match_score":0,"default":false,"direction":0},
        {"langtag":"uk_UA","name":"українська (Ukrainian)","best_match_score":0,"default":false,"direction":0},
        {"langtag":"hy_AM","name":"հայերեն (Armenian)","best_match_score":0,"default":false,"direction":0},
        {"langtag":"be_BY","name":"беларуская (Belarusian)","best_match_score":0,"default":false,"direction":0},
        {"langtag":"bg_BG","name":"български (Bulgarian)","best_match_score":0,"default":false,"direction":0},
        {"langtag":"km_KH","name":"ខ្មែរ (Khmer)","best_match_score":84,"default":false,"direction":0},
        {"langtag":"et_EE","name":"eesti (Estonian)","best_match_score":0,"default":false,"direction":0},
        {"langtag":"lv_LV","name":"latviešu (Latvian)","best_match_score":0,"default":false,"direction":0},
        {"langtag":"pl_PL","name":"polski (Polish)","best_match_score":0,"default":false,"direction":0},
        {"langtag":"ro_RO","name":"română (Romanian)","best_match_score":0,"default":false,"direction":0},
        {"langtag":"uz_Latn","name":"o‘zbek (Uzbek)","best_match_score":0,"default":false,"direction":0},
        {"langtag":"tr_TR","name":"Türkçe (Turkish)","best_match_score":0,"default":false,"direction":0},
        {"langtag":"th_TH","name":"ไทย (Thai)","best_match_score":0,"default":false,"direction":0},
        {"langtag":"zh_TW","name":"中文 (Chinese)","best_match_score":0,"default":false,"direction":0},
        {"langtag":"az_AZ","name":"azərbaycan (Azerbaijani)","best_match_score":0,"default":false,"direction":0},
    ]
}Get all public translations list:YOUR_API_URL/tradukoj/public/Get all private translations list:YOUR_API_URL/tradukoj/private/Get plain JSON of all public translations:YOUR_API_URL/tradukoj/public/plain/Get plain JSON of all private translations:YOUR_API_URL/tradukoj/private/plain/Get filtered public translations:YOUR_API_URL/tradukoj/public/plain/?bcp47__langtag=es&key__namespace__text=mynamespaceGet filtered private translations:YOUR_API_URL/tradukoj/public/plain/?bcp47__langtag=es&key__namespace__text=mynamespaceDjango Rest Framework, Tradukoj Field.To handle a model translatable field into your drf serializers,TradukojSerializedFieldfrom tradukoj.drf_fields import TradukojSerializedField

class MySerializer(serializers.ModelSerializer):
    name = TradukojSerializedField(read_only=True, fallback_langtag='en-US')

    class Meta:
        model = MyModel
        exclude = ('id', )This will give you a JSON like:{
    name: {
        'fallback': 'Welcome',
        'text': 'mykey',
        'translations': {
            'es-ES': 'Bienvenido',
            'en-US': 'Welcome'
        }
    }
}Command line toolsGenerate .po fileusage: manage.py generate_pofile [-h] --langtag LANGTAG --output OUTPUT
                                 --namespace NAMESPACE --reference_langtag
                                 REFERENCE_LANGTAG [--version] [-v {0,1,2,3}]
                                 [--settings SETTINGS]
                                 [--pythonpath PYTHONPATH] [--traceback]
                                 [--no-color] [--force-color]

Generate a .po file of langtag

optional arguments:
  -h, --help            show this help message and exit
  --langtag LANGTAG     tag used in BCP47
  --output OUTPUT       output file
  --namespace NAMESPACE
                        namespace of translations
  --reference_langtag REFERENCE_LANGTAG
                        translated text used in comments to help translator
                        team
  --version             show program's version number and exit
  -v {0,1,2,3}, --verbosity {0,1,2,3}
                        Verbosity level; 0=minimal output, 1=normal output,
                        2=verbose output, 3=very verbose output
  --settings SETTINGS   The Python path to a settings module, e.g.
                        "myproject.settings.main". If this isn't provided, the
                        DJANGO_SETTINGS_MODULE environment variable will be
                        used.
  --pythonpath PYTHONPATH
                        A directory to add to the Python path, e.g.
                        "/home/djangoprojects/myproject".
  --traceback           Raise on CommandError exceptions
  --no-color            Don't colorize the command output.
  --force-color         Force colorization of the command output.Generate a .po file ofes-ESand puten-UStranslations as a commentary of
everymsgid:python manage.py generate_pofile --langtag es-ES --output ./pofile.po --namespace mynamespace --reference_langtag en-USDestroy keys not in a .po file:This will destroy those keys not in a .po file and related translations.usage: manage.py destroy_dbkeys_not_in_pofile [-h] --pofile POFILE --namespace
                                              NAMESPACE [--safe] [--version]
                                              [-v {0,1,2,3}]
                                              [--settings SETTINGS]
                                              [--pythonpath PYTHONPATH]
                                              [--traceback] [--no-color]
                                              [--force-color]

Remove those translations key in DB that does not exists in a given pofile

optional arguments:
  -h, --help            show this help message and exit
  --pofile POFILE       The pofile
  --namespace NAMESPACE
                        The namespace
  --safe                Show those keys that should be deleted but do not
                        commit delete command on db
  --version             show program's version number and exit
  -v {0,1,2,3}, --verbosity {0,1,2,3}
                        Verbosity level; 0=minimal output, 1=normal output,
                        2=verbose output, 3=very verbose output
  --settings SETTINGS   The Python path to a settings module, e.g.
                        "myproject.settings.main". If this isn't provided, the
                        DJANGO_SETTINGS_MODULE environment variable will be
                        used.
  --pythonpath PYTHONPATH
                        A directory to add to the Python path, e.g.
                        "/home/djangoprojects/myproject".
  --traceback           Raise on CommandError exceptions
  --no-color            Don't colorize the command output.
  --force-color         Force colorization of the command output.Show incompatible keys when use JSON tree generation featureIf you are planning to use JSON tree generation, some keys will be incompatible
with this scheme:namespace.section.title = 'test'namespace.section = 'test'to detect those incompatibility, useshow_incompatible_tree_keycommand:usage: manage.py show_incompatible_tree_key [-h] [--pofile POFILE] [--version]
                                            [-v {0,1,2,3}]
                                            [--settings SETTINGS]
                                            [--pythonpath PYTHONPATH]
                                            [--traceback] [--no-color]
                                            [--force-color]

Show incompatible Translation Key for plain tree generation

optional arguments:
  -h, --help            show this help message and exit
  --pofile POFILE       Test pofile instead database
  --version             show program's version number and exit
  -v {0,1,2,3}, --verbosity {0,1,2,3}
                        Verbosity level; 0=minimal output, 1=normal output,
                        2=verbose output, 3=very verbose output
  --settings SETTINGS   The Python path to a settings module, e.g.
                        "myproject.settings.main". If this isn't provided, the
                        DJANGO_SETTINGS_MODULE environment variable will be
                        used.
  --pythonpath PYTHONPATH
                        A directory to add to the Python path, e.g.
                        "/home/djangoprojects/myproject".
  --traceback           Raise on CommandError exceptions
  --no-color            Don't colorize the command output.
  --force-color         Force colorization of the command output.Load .po filesImportingtradukoj.adminmodels into your admin, will give you some features
like importing .po files.from tradukoj import models


class TranslationKeyAdmin(admin.ModelAdmin):
    search_fields = ('text',)

class TranslationAdmin(admin.ModelAdmin):
    search_fields = ('key__text',)


admin.site.register(models.TranslationKey, TranslationKeyAdmin)
admin.site.register(models.GetTextFile)
admin.site.register(models.Namespace)
admin.site.register(models.Translation, TranslationAdmin)
admin.site.register(models.BCP47)Vue.js: Translate fields POCAssuming that you are storing your current languaje tag instore.state.i18n.active_langtag,
use this POC vuejs code:create aplugins/tradukoj-translate.jswith this content:import store from "../store";

const TradukojTranslatable = {
  install(Vue) {
    Vue.mixin({
      methods: {
        $tradukojTranslate(translatable) {
          if (!translatable) {
            return "";
          }

          if (
            translatable.translations &&
            translatable.translations[store.state.i18n.active_langtag]
          ) {
            return translatable.translations[
              store.state.i18n.active_langtag
            ];
          }

          if (translatable.fallback) {
            return translatable.fallback;
          }

          return "";
        }
      }
    });
  }
};
export default TradukojTranslatable;Register globally:import TradukojTranslatable from "@/plugins/tradukoj-translatable";
Vue.use(TradukojTranslatable);Use in component:<template>
  <div>
    <p>{{ $tradukojTranslate(mymodel.name) }}</p>
  </div>
</template>
[...]Once you change the value ofstore.state.i18n.active_langtag, the translations will be
automatically updated to current selected lang.