diff --git a/.gitignore b/.gitignore
index b648ad00b9..eaa9e5574d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -40,6 +40,7 @@ docs/_build
# Local static and media file storage (only when running in development mode)
inventree_media
inventree_static
+static_i18n
# Local config file
config.yaml
diff --git a/InvenTree/InvenTree/management/commands/prerender.py b/InvenTree/InvenTree/management/commands/prerender.py
new file mode 100644
index 0000000000..b80f35d421
--- /dev/null
+++ b/InvenTree/InvenTree/management/commands/prerender.py
@@ -0,0 +1,63 @@
+"""
+Custom management command to prerender files
+"""
+
+import django
+from django.core.management.base import BaseCommand
+from django.conf import settings
+from django.template.loader import render_to_string
+from django.utils.module_loading import import_string
+from django.http.request import HttpRequest
+from django.utils.translation import override as lang_over
+
+import time
+import os
+
+
+def render_file(file_name, source, target, locales, ctx):
+ """ renders a file into all provided locales """
+ for locale in locales:
+ target_file = os.path.join(target, locale + '.' + file_name)
+ with open(target_file, 'w') as localised_file:
+ with lang_over(locale):
+ renderd = render_to_string(os.path.join(source, file_name), ctx)
+ localised_file.write(renderd)
+
+
+class Command(BaseCommand):
+ """
+ django command to prerender files
+ """
+
+ def handle(self, *args, **kwargs):
+ # static directorys
+ LC_DIR = settings.LOCALE_PATHS[0]
+ SOURCE_DIR = settings.STATICFILES_I18_SRC
+ TARTGET_DIR = settings.STATICFILES_I18_TRG
+
+ # ensure static directory exists
+ if not os.path.exists(TARTGET_DIR):
+ os.mkdir(TARTGET_DIR)
+
+ # collect locales
+ locales = {}
+ for locale in os.listdir(LC_DIR):
+ path = os.path.join(LC_DIR, locale)
+ if os.path.exists(path) and os.path.isdir(path):
+ locales[locale] = locale
+
+ # render!
+ request = HttpRequest()
+ ctx = {}
+ processors = tuple(import_string(path) for path in settings.STATFILES_I18_PROCESORS)
+ for processor in processors:
+ ctx.update(processor(request))
+
+ for file in os.listdir(SOURCE_DIR, ):
+ path = os.path.join(SOURCE_DIR, file)
+ if os.path.exists(path) and os.path.isfile(path):
+ print(f"render {file}")
+ render_file(file, SOURCE_DIR, TARTGET_DIR, locales, ctx)
+ else:
+ raise NotImplementedError('Using multi-level directories is not implemented at this point') # TODO multilevel dir if needed
+ print(f"rendered all files in {SOURCE_DIR}")
diff --git a/InvenTree/InvenTree/settings.py b/InvenTree/InvenTree/settings.py
index b1256dccee..c4cc8e961a 100644
--- a/InvenTree/InvenTree/settings.py
+++ b/InvenTree/InvenTree/settings.py
@@ -190,6 +190,17 @@ STATICFILES_DIRS = [
os.path.join(BASE_DIR, 'InvenTree', 'static'),
]
+# Translated Template settings
+STATICFILES_I18_PREFIX = 'i18n'
+STATICFILES_I18_SRC = os.path.join(BASE_DIR, 'templates', 'js')
+STATICFILES_I18_TRG = STATICFILES_DIRS[0] + '_' + STATICFILES_I18_PREFIX
+STATICFILES_DIRS.append(STATICFILES_I18_TRG)
+STATICFILES_I18_TRG = os.path.join(STATICFILES_I18_TRG, STATICFILES_I18_PREFIX)
+
+STATFILES_I18_PROCESORS = [
+ 'InvenTree.context.status_codes',
+]
+
# Color Themes Directory
STATIC_COLOR_THEMES_DIR = os.path.join(STATIC_ROOT, 'css', 'color-themes')
diff --git a/InvenTree/part/templatetags/inventree_extras.py b/InvenTree/part/templatetags/inventree_extras.py
index cf56e01fbf..a92d95c766 100644
--- a/InvenTree/part/templatetags/inventree_extras.py
+++ b/InvenTree/part/templatetags/inventree_extras.py
@@ -6,6 +6,7 @@ import os
from django import template
from django.urls import reverse
from django.utils.safestring import mark_safe
+from django.templatetags.static import StaticNode
from InvenTree import version, settings
import InvenTree.helpers
@@ -180,3 +181,31 @@ def object_link(url_name, pk, ref):
ref_url = reverse(url_name, kwargs={'pk': pk})
return mark_safe('{}'.format(ref_url, ref))
+
+
+class I18nStaticNode(StaticNode):
+ """
+ custom StaticNode
+ replaces a variable named *lng* in the path with the current language
+ """
+ def render(self, context):
+ self.path.var = self.path.var.format(lng=context.request.LANGUAGE_CODE)
+ ret = super().render(context)
+ return ret
+
+
+@register.tag('i18n_static')
+def do_i18n_static(parser, token):
+ """
+ Overrides normal static, adds language - lookup for prerenderd files #1485
+
+ usage (like static):
+ {% i18n_static path [as varname] %}
+ """
+ bits = token.split_contents()
+ loc_name = settings.STATICFILES_I18_PREFIX
+
+ # change path to called ressource
+ bits[1] = f"'{loc_name}/{{lng}}.{bits[1][1:-1]}'"
+ token.contents = ' '.join(bits)
+ return I18nStaticNode.handle_token(parser, token)
diff --git a/InvenTree/templates/base.html b/InvenTree/templates/base.html
index 8ccf691ef5..970d9f1016 100644
--- a/InvenTree/templates/base.html
+++ b/InvenTree/templates/base.html
@@ -143,20 +143,21 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/tasks.py b/tasks.py
index 895f3183ce..d775b943a0 100644
--- a/tasks.py
+++ b/tasks.py
@@ -154,6 +154,7 @@ def static(c):
as per Django requirements.
"""
+ manage(c, "prerender")
manage(c, "collectstatic --no-input")