Package: django-international-sites
Django InternationalAn app to allow a single Django application to serve multiple domains and country sites and adjust the content based on that. Similar to the Django Sites Framework, but without theSitehardcoded in the settings of the app. Instead it uses aCountrySiteobject (similar toSite) which can be set dynamically in the middleware based on the request domain, session, url parameter or visitor location.Installpip install django-international-sitesAdd "international" toINSTALLED_APPSinsettings.pyRunpython manage.py migrate internationalAddCountrySiteobjects in admin/admin/international/countrysite/Settings# Fallback country codeDEFAULT_COUNTRY_CODE="NL"# Optional: Set the below if you want the middleware to set the current site based# on the location/country deduced from the visitor IP# When using international.middleware.InternationalSiteMiddleware obtain# geoip license key (for free) at xx and set path were geoip2 country library is# to be installed hereGEOIP_PATH=os.path.join("geoip")GEOIP_LICENSE="asecretkeybymaxmind"# Map domains uniquely to a single country code (optional)UNIQUE_DOMAINS={"example.nl":"nl","example.co.uk":"uk"}# Directory for site icons to be displayed in admin (optional)SITE_ICON_DIR="static/site_icons/"Request middlewareHow is the country code detected from the request?If a unique domain name set insettings.UNIQUE_DOMAINS(e.g. example.nl), use country data of related country codeIf country code is forced as url parameter (i.e. example.com/c=fr), use that country codeIf a cookie with location preference is used, use that country codeCheck location based on visitor IP address, use that country codeIf nothing could be detected, use default country codeAdd the middleware to settingsafterthe DjangoLocaleMiddleware:MIDDLEWARE=[...'django.middleware.locale.LocaleMiddleware','international.middleware.InternationalSiteMiddleware','vinaigrette.middleware.VinaigretteAdminLanguageMiddleware']This makes the currentCountrySiteobject available through the request object. E.g., in views:defindex(request):country_site=request.country_siteModelsAll models in a project can be made international, i.e. associated to countries and/or languages, by inheriting theInternationalModelbase class.# models.pyfrominternational.modelsimportInternationalModelclassProduct(InternationalModel):name=models.CharField(max_length=50)def__str__(self):returnself.nameThis will add thecountry_sitesandobject_languagefield and extend the model managers.To filter all models associated with CountrySitenlor with language "en":# Get all items associated to a country codeproducts=Product.objects.by_country("nl")# Get all items associated to a language codeproducts=Product.objects.by_language("en")# Get all items associated to either a country code or language codeproducts=Product.by_country_or_language(country_code="nl",language_code="en")LanguageWhen using in combination with Django'si18n translation, add theInternationalSiteMiddlewarebefore the DjangoLocaleMiddlewarein your project's settings.If you want to force using theCountrySite.default_languagelanguage for a given CountrySite, setFORCE_COUNTRY_LANGUAGEto True. This will make sure that for e.g. the German country site,i18nwill use the German language that has been associated to the CountrySite.# settings.pyFORCE_COUNTRY_LANGUAGE=TrueCountry detection endpointTheinternational.views.get_country_from_requestis included that will return a JSON response with the detected visitor location based on their IP address when the MaxMind GeoIP2 library is installed. To use it, includeinternational.urlsin your projecturls.py. This will include thelocalize/endpoint that only allows GET requests, with example response:{
    "country": "NL",    # country code or null
    "detected": true    # false when the country could not be detected from the visitor IP
}International SitemapUse the International extension to the Django Sites Sitemap to create dynamic sitemaps based on the current request domain rather than a single fixed site domain. First, usethe Django Sitemaps like usualbut instead of using the out-of-the-boxdjango.contrib.sites.sitemaps.viewsimport the same views frominternational.sitemaps.views, this will change the domain of the urls shown in the sitemap to that of the current request CountrySite instead of the hardcoded Site domain (which can only be one per application).frominternational.sitemapsimportviewsasinternational_sitemap_views# Register sitemap viewsurlpatterns+=(path(r"sitemap.xml",international_sitemap_views.index,{"sitemaps":sitemap_sections}),path("sitemap-<section>.xml",international_sitemap_views.sitemap,{"sitemaps":sitemap_sections},name="international.sitemaps.views.sitemap",),)In order to limit/adjust the items shown in a sitemap based on the current request domain/current CountrySite. Inherit theInternationalSitemapextension in your Sitemap class (this is available for models that useInternationalModel), this wel make the country_code of the current request available inside the class methods. For example, here the blog post items shown in the sitemap are filtered by country code:frominternational.sitemapsimportInternationalSitemapclassBlogSitemap(InternationalSitemap):changefreq="weekly"priority=0.5defitems(self):returnPost.objects.by_country(self.country_code)deflastmod(self,obj):returnobj.published_dateAdmin MixinsInternationalModelAdminMixinFor models inheriting the InternationalModel classTranslatedFieldsModelAdminMixinFor models using Vinaigrette translated fields - not InternationalModelExtend model admin's with fields that usedjango-vinaigretteto add translations (usinggettextinstead of adding more fields in the db). In order to use this mixin the translated fields must be registerred to Vinaigrette in the app's config like below. Specifically, thetranslated_fieldsdictionary must be available in the AppConfig.classTestappConfig(AppConfig):default_auto_field='django.db.models.BigAutoField'name='testapp'translated_fields={'Certificate':['description']}defready(self):formodelnameinself.translated_fields.keys():model=self.get_model(modelname)# Register fields to translatevinaigrette.register(model,self.translated_fields[modelname])This will show thetranslated fieldindicator with all fields that can be translated in the translation files. It will also add the current translations to the bottom of the admin page (note: these are for reference and cannot be edited through the admin since they do not come from the database but the translation files)