kopia lustrzana https://github.com/OpenDroneMap/WebODM
Extract ODM strings, moar translation work
rodzic
27379a6b2d
commit
2aa1c12970
|
@ -61,7 +61,7 @@ class ThemeModelForm(forms.ModelForm):
|
|||
label=_("HTML (after header)"),
|
||||
required=False,
|
||||
widget=CodeMirrorEditor(options={'mode': 'xml', 'lineNumbers': True}))
|
||||
html_after_body = forms.CharField(help_text=_("HTML that will be displayed after the </body> tag"),
|
||||
html_after_body = forms.CharField(help_text=_("HTML that will be displayed after the body tag"),
|
||||
label=_("HTML (after body)"),
|
||||
required=False,
|
||||
widget=CodeMirrorEditor(options={'mode': 'xml', 'lineNumbers': True}))
|
||||
|
|
|
@ -4,78 +4,79 @@
|
|||
|
||||
import re
|
||||
from functools import lru_cache
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
algos = {
|
||||
'NDVI': {
|
||||
'expr': '(N - R) / (N + R)',
|
||||
'help': 'Normalized Difference Vegetation Index shows the amount of green vegetation.'
|
||||
'help': _('Normalized Difference Vegetation Index shows the amount of green vegetation.')
|
||||
},
|
||||
'NDVI (Blue)': {
|
||||
'expr': '(N - B) / (N + B)',
|
||||
'help': 'Normalized Difference Vegetation Index shows the amount of green vegetation.'
|
||||
'help': _('Normalized Difference Vegetation Index shows the amount of green vegetation.')
|
||||
},
|
||||
'ENDVI':{
|
||||
'expr': '((N + G) - (2 * B)) / ((N + G) + (2 * B))',
|
||||
'help': 'Enhanced Normalized Difference Vegetation Index is like NDVI, but uses Blue and Green bands instead of only Red to isolate plant health.'
|
||||
'help': _('Enhanced Normalized Difference Vegetation Index is like NDVI, but uses Blue and Green bands instead of only Red to isolate plant health.')
|
||||
},
|
||||
'VARI': {
|
||||
'expr': '(G - R) / (G + R - B)',
|
||||
'help': 'Visual Atmospheric Resistance Index shows the areas of vegetation.',
|
||||
'help': _('Visual Atmospheric Resistance Index shows the areas of vegetation.'),
|
||||
'range': (-1, 1)
|
||||
},
|
||||
'EXG': {
|
||||
'expr': '(2 * G) - (R + B)',
|
||||
'help': 'Excess Green Index emphasizes the greenness of leafy crops such as potatoes.',
|
||||
'help': _('Excess Green Index emphasizes the greenness of leafy crops such as potatoes.',)
|
||||
},
|
||||
'BAI': {
|
||||
'expr': '1.0 / (((0.1 - R) ** 2) + ((0.06 - N) ** 2))',
|
||||
'help': 'Burn Area Index hightlights burned land in the red to near-infrared spectrum.'
|
||||
'help': _('Burn Area Index hightlights burned land in the red to near-infrared spectrum.')
|
||||
},
|
||||
'GLI': {
|
||||
'expr': '((G * 2) - R - B) / ((G * 2) + R + B)',
|
||||
'help': 'Green Leaf Index shows greens leaves and stems.',
|
||||
'help': _('Green Leaf Index shows greens leaves and stems.'),
|
||||
'range': (-1, 1)
|
||||
},
|
||||
'GNDVI':{
|
||||
'expr': '(N - G) / (N + G)',
|
||||
'help': 'Green Normalized Difference Vegetation Index is similar to NDVI, but measures the green spectrum instead of red.'
|
||||
'help': _('Green Normalized Difference Vegetation Index is similar to NDVI, but measures the green spectrum instead of red.')
|
||||
},
|
||||
'GRVI':{
|
||||
'expr': 'N / G',
|
||||
'help': 'Green Ratio Vegetation Index is sensitive to photosynthetic rates in forests.'
|
||||
'help': _('Green Ratio Vegetation Index is sensitive to photosynthetic rates in forests.')
|
||||
},
|
||||
'SAVI':{
|
||||
'expr': '(1.5 * (N - R)) / (N + R + 0.5)',
|
||||
'help': 'Soil Adjusted Vegetation Index is similar to NDVI but attempts to remove the effects of soil areas using an adjustment factor (0.5).'
|
||||
'help': _('Soil Adjusted Vegetation Index is similar to NDVI but attempts to remove the effects of soil areas using an adjustment factor (0.5).')
|
||||
},
|
||||
'MNLI':{
|
||||
'expr': '((N ** 2 - R) * 1.5) / (N ** 2 + R + 0.5)',
|
||||
'help': 'Modified Non-Linear Index improves the Non-Linear Index algorithm to account for soil areas.'
|
||||
'help': _('Modified Non-Linear Index improves the Non-Linear Index algorithm to account for soil areas.')
|
||||
},
|
||||
'MSR': {
|
||||
'expr': '((N / R) - 1) / (sqrt(N / R) + 1)',
|
||||
'help': 'Modified Simple Ratio is an improvement of the Simple Ratio (SR) index to be more sensitive to vegetation.'
|
||||
'help': _('Modified Simple Ratio is an improvement of the Simple Ratio (SR) index to be more sensitive to vegetation.')
|
||||
},
|
||||
'RDVI': {
|
||||
'expr': '(N - R) / sqrt(N + R)',
|
||||
'help': 'Renormalized Difference Vegetation Index uses the difference between near-IR and red, plus NDVI to show areas of healthy vegetation.'
|
||||
'help': _('Renormalized Difference Vegetation Index uses the difference between near-IR and red, plus NDVI to show areas of healthy vegetation.')
|
||||
},
|
||||
'TDVI': {
|
||||
'expr': '1.5 * ((N - R) / sqrt(N ** 2 + R + 0.5))',
|
||||
'help': 'Transformed Difference Vegetation Index highlights vegetation cover in urban environments.'
|
||||
'help': _('Transformed Difference Vegetation Index highlights vegetation cover in urban environments.')
|
||||
},
|
||||
'OSAVI': {
|
||||
'expr': '(N - R) / (N + R + 0.16)',
|
||||
'help': 'Optimized Soil Adjusted Vegetation Index is based on SAVI, but tends to work better in areas with little vegetation where soil is visible.'
|
||||
'help': _('Optimized Soil Adjusted Vegetation Index is based on SAVI, but tends to work better in areas with little vegetation where soil is visible.')
|
||||
},
|
||||
'LAI': {
|
||||
'expr': '3.618 * (2.5 * (N - R) / (N + 6*R - 7.5*B + 1)) * 0.118',
|
||||
'help': 'Leaf Area Index estimates foliage areas and predicts crop yields.',
|
||||
'help': _('Leaf Area Index estimates foliage areas and predicts crop yields.'),
|
||||
'range': (-1, 1)
|
||||
},
|
||||
'EVI': {
|
||||
'expr': '2.5 * (N - R) / (N + 6*R - 7.5*B + 1)',
|
||||
'help': 'Enhanced Vegetation Index is useful in areas where NDVI might saturate, by using blue wavelengths to correct soil signals.',
|
||||
'help': _('Enhanced Vegetation Index is useful in areas where NDVI might saturate, by using blue wavelengths to correct soil signals.'),
|
||||
'range': (-1, 1)
|
||||
},
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@ from nodeodm.models import ProcessingNode
|
|||
from worker import tasks as worker_tasks
|
||||
from .common import get_and_check_project
|
||||
from app.security import path_traversal_check
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
|
||||
def flatten_files(request_files):
|
||||
|
@ -175,7 +176,7 @@ class TaskViewSet(viewsets.ViewSet):
|
|||
task.images_count = models.ImageUpload.objects.filter(task=task).count()
|
||||
|
||||
if task.images_count < 2:
|
||||
raise exceptions.ValidationError(detail="You need to upload at least 2 images before commit")
|
||||
raise exceptions.ValidationError(detail=_("You need to upload at least 2 images before commit"))
|
||||
|
||||
task.save()
|
||||
worker_tasks.process_task.delay(task.id)
|
||||
|
@ -197,7 +198,7 @@ class TaskViewSet(viewsets.ViewSet):
|
|||
files = flatten_files(request.FILES)
|
||||
|
||||
if len(files) == 0:
|
||||
raise exceptions.ValidationError(detail="No files uploaded")
|
||||
raise exceptions.ValidationError(detail=_("No files uploaded"))
|
||||
|
||||
with transaction.atomic():
|
||||
for image in files:
|
||||
|
@ -220,7 +221,7 @@ class TaskViewSet(viewsets.ViewSet):
|
|||
files = flatten_files(request.FILES)
|
||||
|
||||
if len(files) <= 1:
|
||||
raise exceptions.ValidationError(detail="Cannot create task, you need at least 2 images")
|
||||
raise exceptions.ValidationError(detail=_("Cannot create task, you need at least 2 images"))
|
||||
|
||||
with transaction.atomic():
|
||||
task = models.Task.objects.create(project=project,
|
||||
|
@ -325,10 +326,10 @@ class TaskDownloads(TaskNestedView):
|
|||
try:
|
||||
asset_path = task.get_asset_download_path(asset)
|
||||
except FileNotFoundError:
|
||||
raise exceptions.NotFound("Asset does not exist")
|
||||
raise exceptions.NotFound(_("Asset does not exist"))
|
||||
|
||||
if not os.path.exists(asset_path):
|
||||
raise exceptions.NotFound("Asset does not exist")
|
||||
raise exceptions.NotFound(_("Asset does not exist"))
|
||||
|
||||
return download_file_response(request, asset_path, 'attachment')
|
||||
|
||||
|
@ -347,10 +348,10 @@ class TaskAssets(TaskNestedView):
|
|||
try:
|
||||
asset_path = path_traversal_check(task.assets_path(unsafe_asset_path), task.assets_path(""))
|
||||
except SuspiciousFileOperation:
|
||||
raise exceptions.NotFound("Asset does not exist")
|
||||
raise exceptions.NotFound(_("Asset does not exist"))
|
||||
|
||||
if (not os.path.exists(asset_path)) or os.path.isdir(asset_path):
|
||||
raise exceptions.NotFound("Asset does not exist")
|
||||
raise exceptions.NotFound(_("Asset does not exist"))
|
||||
|
||||
return download_file_response(request, asset_path, 'inline')
|
||||
|
||||
|
@ -366,13 +367,13 @@ class TaskAssetsImport(APIView):
|
|||
|
||||
files = flatten_files(request.FILES)
|
||||
import_url = request.data.get('url', None)
|
||||
task_name = request.data.get('name', 'Imported Task')
|
||||
task_name = request.data.get('name', _('Imported Task'))
|
||||
|
||||
if not import_url and len(files) != 1:
|
||||
raise exceptions.ValidationError(detail="Cannot create task, you need to upload 1 file")
|
||||
raise exceptions.ValidationError(detail=_("Cannot create task, you need to upload 1 file"))
|
||||
|
||||
if import_url and len(files) > 0:
|
||||
raise exceptions.ValidationError(detail="Cannot create task, either specify a URL or upload 1 file.")
|
||||
raise exceptions.ValidationError(detail=_("Cannot create task, either specify a URL or upload 1 file."))
|
||||
|
||||
with transaction.atomic():
|
||||
task = models.Task.objects.create(project=project,
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
from django.core.management.commands.makemessages import Command as MMCommand
|
||||
|
||||
# https://medium.com/@hugosousa/hacking-djangos-makemessages-for-better-translations-matching-in-jsx-components-1174b57a13b1
|
||||
class Command(MMCommand):
|
||||
"""
|
||||
This is a wrapper for the makemessages command and
|
||||
it is used to force makemessages call xgettext with the language
|
||||
provided as input
|
||||
The solution is really hacky and takes advantage of the fact
|
||||
that in makemessages TranslatableFile process()
|
||||
the options in command.xgettext_options are appended to the end
|
||||
of the xgettext command.
|
||||
"""
|
||||
def add_arguments(self, parser):
|
||||
parser.add_argument(
|
||||
'--language',
|
||||
'-lang',
|
||||
default='Python',
|
||||
dest='language',
|
||||
help='Language to be used by xgettext'
|
||||
)
|
||||
|
||||
super(Command, self).add_arguments(parser)
|
||||
def handle(self, *args, **options):
|
||||
language = options.get('language')
|
||||
self.xgettext_options.append('--language={lang}'.format(
|
||||
lang=language
|
||||
))
|
||||
super(Command, self).handle(*args, **options)
|
|
@ -8,7 +8,6 @@ from django.db.models import signals
|
|||
from django.dispatch import receiver
|
||||
from imagekit.models import ImageSpecField
|
||||
from imagekit.processors import ResizeToFit
|
||||
from django.utils.translation import gettext
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from webodm import settings
|
||||
|
@ -71,7 +70,7 @@ class Setting(models.Model):
|
|||
super(Setting, self).save(*args, **kwargs)
|
||||
|
||||
def __str__(self):
|
||||
return gettext("Application")
|
||||
return str(_("Application"))
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("Settings")
|
||||
|
|
|
@ -0,0 +1,84 @@
|
|||
#!/usr/bin/python3
|
||||
|
||||
import argparse, os, urllib.request, ast, sys
|
||||
from io import StringIO
|
||||
|
||||
parser = argparse.ArgumentParser(description='Extract ODM strings.')
|
||||
parser.add_argument('input', type=str,
|
||||
help='URL to ODM\'s config.py')
|
||||
parser.add_argument('output', type=str,
|
||||
help='Where to write resulting translation file')
|
||||
args = parser.parse_args()
|
||||
|
||||
url = args.input
|
||||
outfile = args.output
|
||||
|
||||
strings = []
|
||||
print("Fetching %s ..." % url)
|
||||
res = urllib.request.urlopen(url)
|
||||
config_file = res.read().decode('utf-8')
|
||||
# config_file = open("test.py").read()
|
||||
|
||||
options = {}
|
||||
class ArgumentParserStub(argparse.ArgumentParser):
|
||||
def add_argument(self, *args, **kwargs):
|
||||
argparse.ArgumentParser.add_argument(self, *args, **kwargs)
|
||||
options[args[0]] = {}
|
||||
for name, value in kwargs.items():
|
||||
options[args[0]][str(name)] = str(value)
|
||||
|
||||
def add_mutually_exclusive_group(self):
|
||||
return ArgumentParserStub()
|
||||
|
||||
# Voodoo! :)
|
||||
# ( parse AST, extract "def config()" function, set module to only
|
||||
# contain that function, execute module in current scope,
|
||||
# run config function)
|
||||
root = ast.parse(config_file)
|
||||
new_body = []
|
||||
for stmt in root.body:
|
||||
# Assignments
|
||||
if hasattr(stmt, 'targets'):
|
||||
new_body.append(stmt)
|
||||
|
||||
# Functions
|
||||
elif hasattr(stmt, 'name'):
|
||||
new_body.append(stmt)
|
||||
|
||||
root.body = new_body
|
||||
exec(compile(root, filename="<ast>", mode="exec"))
|
||||
|
||||
|
||||
# Misc variables needed to get config to run
|
||||
__version__ = '?'
|
||||
class context:
|
||||
root_path = ''
|
||||
num_cores = 4
|
||||
class io:
|
||||
def path_or_json_string_to_dict(s):
|
||||
return s
|
||||
def path_or_json_string(s):
|
||||
return s
|
||||
class log:
|
||||
def ODM_ERROR(s):
|
||||
pass
|
||||
|
||||
config(["--project-path", "/bogus", "name"], parser=ArgumentParserStub())
|
||||
for opt in options:
|
||||
h = options[opt].get('help')
|
||||
if h:
|
||||
h = h.replace("\n", "")
|
||||
strings.append(h)
|
||||
|
||||
strings = list(set(strings))
|
||||
print("Found %s ODM strings" % len(strings))
|
||||
if len(strings) > 0:
|
||||
with open(outfile, "w") as f:
|
||||
f.write("// Auto-generated with extract_odm_strings.py, do not edit!\n\n")
|
||||
|
||||
for s in strings:
|
||||
f.write("_(\"%s\");\n" % s.replace("\"", "\\\""))
|
||||
|
||||
print("Wrote %s" % outfile)
|
||||
else:
|
||||
print("No strings found")
|
|
@ -1,9 +1,10 @@
|
|||
{% extends "app/base.html" %}
|
||||
{% load settings %}
|
||||
{% load settings i18n %}
|
||||
|
||||
{% block page-wrapper %}
|
||||
<div style="text-align: center;">
|
||||
<h3>404 Page Not Found</h3>
|
||||
<h5>Are you sure the address is correct?</h5>
|
||||
<h3>{% trans '404 Page Not Found' %}</h3>
|
||||
<h5>{% trans 'Are you sure the address is correct?' %}</h5>
|
||||
<img src="/static/app/img/404.png" alt="404"/>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
{% extends "app/base.html" %}
|
||||
{% load settings %}
|
||||
{% load settings i18n %}
|
||||
{% block page-wrapper %}
|
||||
<div style="text-align: center;">
|
||||
<h3>500 Internal Server Error</h3>
|
||||
<h5>Something happened. The server logs contain more information.</h5>
|
||||
<h3>{% trans '500 Internal Server Error' %}</h3>
|
||||
<h5>{% trans 'Something happened. The server logs contain more information.' %}</h5>
|
||||
<img src="/static/app/img/500.png" alt="500"/>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -84,12 +84,14 @@
|
|||
<div id='stars2'></div>
|
||||
<div id='stars3'></div>
|
||||
|
||||
{% load i18n %}
|
||||
|
||||
<div class="text-center about-text">
|
||||
<h2 class="theme-secondary">WebODM {{ version }}</h2>
|
||||
|
||||
<div style="margin-bottom: 16px; margin-top: 16px;"><i class="fab fa-github"></i> <a target="_blank" href="https://github.com/OpenDroneMap/WebODM">https://github.com/OpenDroneMap/WebODM</a></div>
|
||||
|
||||
<div style="font-size: 90%; opacity: 0.9;" class="theme-secondary">This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. See <a href="https://raw.githubusercontent.com/OpenDroneMap/WebODM/master/LICENSE.md">the GNU Affero General Public License</a> for more details.</div>
|
||||
<div style="font-size: 90%; opacity: 0.9;" class="theme-secondary">{% blocktrans with link_start="<a href='https://raw.githubusercontent.com/OpenDroneMap/WebODM/master/LICENSE.md'>" link_end='</a>' %}This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. See {{link_start}}the GNU Affero General Public License{{link_end}} for more details.{% endblocktrans %}</div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
|
|
|
@ -43,7 +43,7 @@ def dashboard(request):
|
|||
if Project.objects.count() == 0:
|
||||
Project.objects.create(owner=request.user, name=_("First Project"))
|
||||
|
||||
return render(request, 'app/dashboard.html', {'title': 'Dashboard',
|
||||
return render(request, 'app/dashboard.html', {'title': _('Dashboard'),
|
||||
'no_processingnodes': no_processingnodes,
|
||||
'no_tasks': no_tasks
|
||||
})
|
||||
|
@ -144,7 +144,7 @@ def welcome(request):
|
|||
|
||||
return render(request, 'app/welcome.html',
|
||||
{
|
||||
'title': 'Welcome',
|
||||
'title': _('Welcome'),
|
||||
'firstuserform': fuf
|
||||
})
|
||||
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
from __future__ import unicode_literals
|
||||
|
||||
from django.apps import AppConfig
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
class NodeodmConfig(AppConfig):
|
||||
name = 'nodeodm'
|
||||
verbose_name = 'Node Management'
|
||||
verbose_name = _('Node Management')
|
||||
|
|
|
@ -18,19 +18,23 @@ from datetime import timedelta
|
|||
|
||||
|
||||
OFFLINE_MINUTES = 5 # Number of minutes a node hasn't been seen before it should be considered offline
|
||||
# TODO
|
||||
|
||||
class ProcessingNode(models.Model):
|
||||
hostname = models.CharField(max_length=255, help_text="Hostname or IP address where the node is located (can be an internal hostname as well). If you are using Docker, this is never 127.0.0.1 or localhost. Find the IP address of your host machine by running ifconfig on Linux or by checking your network settings.")
|
||||
port = models.PositiveIntegerField(help_text="Port that connects to the node's API")
|
||||
api_version = models.CharField(max_length=32, null=True, help_text="API version used by the node")
|
||||
last_refreshed = models.DateTimeField(null=True, help_text="When was the information about this node last retrieved?")
|
||||
queue_count = models.PositiveIntegerField(default=0, help_text="Number of tasks currently being processed by this node (as reported by the node itself)")
|
||||
available_options = fields.JSONField(default=dict, help_text="Description of the options that can be used for processing")
|
||||
token = models.CharField(max_length=1024, blank=True, default="", help_text="Token to use for authentication. If the node doesn't have authentication, you can leave this field blank.")
|
||||
max_images = models.PositiveIntegerField(help_text="Maximum number of images accepted by this node.", blank=True, null=True)
|
||||
engine_version = models.CharField(max_length=32, null=True, help_text="Engine version used by the node.")
|
||||
label = models.CharField(max_length=255, default="", blank=True, help_text="Optional label for this node. When set, this label will be shown instead of the hostname:port name.")
|
||||
engine = models.CharField(max_length=255, null=True, help_text="Engine used by the node.")
|
||||
hostname = models.CharField(verbose_name=_("Hostname"), max_length=255, help_text=_("Hostname or IP address where the node is located (can be an internal hostname as well). If you are using Docker, this is never 127.0.0.1 or localhost. Find the IP address of your host machine by running ifconfig on Linux or by checking your network settings."))
|
||||
port = models.PositiveIntegerField(verbose_name=_("Port"), help_text=_("Port that connects to the node's API"))
|
||||
api_version = models.CharField(verbose_name=_("API Version"), max_length=32, null=True, help_text=_("API version used by the node"))
|
||||
last_refreshed = models.DateTimeField(verbose_name=_("Last Refreshed"), null=True, help_text=_("When was the information about this node last retrieved?"))
|
||||
queue_count = models.PositiveIntegerField(verbose_name=_("Queue Count"), default=0, help_text=_("Number of tasks currently being processed by this node (as reported by the node itself)"))
|
||||
available_options = fields.JSONField(verbose_name=_("Available Options"), default=dict, help_text=_("Description of the options that can be used for processing"))
|
||||
token = models.CharField(verbose_name=_("Token"), max_length=1024, blank=True, default="", help_text=_("Token to use for authentication. If the node doesn't have authentication, you can leave this field blank."))
|
||||
max_images = models.PositiveIntegerField(verbose_name=_("Max Images"), help_text=_("Maximum number of images accepted by this node."), blank=True, null=True)
|
||||
engine_version = models.CharField(verbose_name=_("Engine Version"), max_length=32, null=True, help_text=_("Engine version used by the node."))
|
||||
label = models.CharField(verbose_name=_("Label"), max_length=255, default="", blank=True, help_text=_("Optional label for this node. When set, this label will be shown instead of the hostname:port name."))
|
||||
engine = models.CharField(verbose_name=_("Engine"), max_length=255, null=True, help_text=_("Engine used by the node."))
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("Processing Node")
|
||||
verbose_name_plural = _("Processing Nodes")
|
||||
|
||||
def __str__(self):
|
||||
if self.label != "":
|
||||
|
|
|
@ -13,6 +13,7 @@ if [[ "$1" == "extract" ]]; then
|
|||
mkdir -p locale
|
||||
|
||||
python3 app/scripts/extract_potree_strings.py app/static/app/js/vendor/potree/build/potree/resources/lang/en/translation.json app/static/app/js/translations/potree_autogenerated.js
|
||||
python3 app/scripts/extract_odm_strings.py https://raw.githubusercontent.com/OpenDroneMap/ODM/master/opendm/config.py app/static/app/js/translations/odm_autogenerated.js
|
||||
|
||||
django-admin makemessages --keep-pot $locale_param --ignore=build --ignore=app/templates/app/registration/*
|
||||
python manage.py makemessages_djangojs --keep-pot $locale_param -d djangojs --extension jsx --extension js --ignore=build --ignore app/static/app/js/vendor --ignore app/static/app/bundles --ignore node_modules --language Python
|
||||
|
|
Ładowanie…
Reference in New Issue