4

In my Django project I use Vue + vue-gettext for i18n.

To make translatable strings I have the following code in my my .vue files:

<translate>Hello %{name}</translate>

<a href="..." v-translate>Click here</a>

(using translate tags and v-translate attributes)

Is there a way to configure manage.py makemessages to parse this code as well? By default it does not look into .vue files or parse this i18n code format.

Tom
  • 688
  • 13
  • 29
Djangonaut
  • 5,511
  • 7
  • 40
  • 53

2 Answers2

2

Basically I ended up having a Makfile - that extracts messages from vue files and then merges it with django

# On OSX the PATH variable isn't exported unless "SHELL" is also set, see: http://stackoverflow.com/a/25506676
SHELL = /bin/bash
NODE_BINDIR = ./node_modules/.bin
export PATH := $(NODE_BINDIR):$(PATH)

INPUT_DIR = static/js
# Where to write the files generated by this makefile.
OUTPUT_DIR = .

# Available locales for the app.
LOCALES = en nl fr

# Name of the generated .po files for each available locale.
LOCALE_FILES ?= $(patsubst %,$(OUTPUT_DIR)/locale/%/LC_MESSAGES/app.po,$(LOCALES))

GETTEXT_HTML_SOURCES = $(shell find $(INPUT_DIR) -name '*.vue' -o -name '*.html' 2> /dev/null)
GETTEXT_JS_SOURCES = $(shell find $(INPUT_DIR) -name '*.vue' -o -name '*.js')

# Makefile Targets
.PHONY: clean makemessages

clean:
    rm -f /tmp/template.pot

makemessages: clean django_makemessages /tmp/template.pot

django_makemessages:
    ./manage.py makemessages $(patsubst %,-l %,$(LOCALES))

# Create a main .pot template, then generate .po files for each available language.
# Thanx to Systematic: https://github.com/Polyconseil/systematic/blob/866d5a/mk/main.mk#L167-L183
/tmp/template.pot: $(GETTEXT_HTML_SOURCES)
# `dir` is a Makefile built-in expansion function which extracts the directory-part of `$@`.
# `$@` is a Makefile automatic variable: the file name of the target of the rule.
# => `mkdir -p /tmp/`
    mkdir -p $(dir $@)
    which gettext-extract
# Extract gettext strings from templates files and create a POT dictionary template.
    gettext-extract --attribute v-translate --quiet --output $@ $(GETTEXT_HTML_SOURCES)
# Extract gettext strings from JavaScript files.
    xgettext --language=JavaScript --keyword=npgettext:1c,2,3 \
        --from-code=utf-8 --join-existing --no-wrap \
        --package-name=$(shell node -e "console.log(require('./package.json').name);") \
        --package-version=$(shell node -e "console.log(require('./package.json').version);") \
        --output $@ $(GETTEXT_JS_SOURCES)
# Generate .po files for each available language.
    @for lang in $(LOCALES); do \
        export PO_FILE=$(OUTPUT_DIR)/locale/$$lang/LC_MESSAGES/djangojs.po; \
        echo "msgmerge --update $$PO_FILE $@"; \
        mkdir -p $$(dirname $$PO_FILE); \
        [ -f $$PO_FILE ] && msgmerge --lang=$$lang --update --backup=off $$PO_FILE $@ || msginit --no-translator --locale=$$lang --input=$@ --output-file=$$PO_FILE; \
        msgattrib --no-wrap --no-obsolete -o $$PO_FILE $$PO_FILE; \
    done;
Djangonaut
  • 5,511
  • 7
  • 40
  • 53
0

I don't use vue-gettext since Django provides the JavaScriptCatalog view but I still faced the same issues with makemessages not picking up translation string in templates.

Using Django 4, Vue 3, and gettext 0.21 I made my own makemessages management command that overrides the Django's built-in one

from django.core.management.commands import makemessages


class Command(makemessages.Command):
    """
    Overrides the default `makemessages` to add my own configuration & extend the command to recognize `interpolate` keyword in JS source files
    """
    def handle(self, *args, **kwargs):
        # Disable writing of the string's source file location in the generate po file
        kwargs['no_location'] = True
        # Ignore node_modules by default
        kwargs['ignore_patterns'] = ['node_modules/*']
        if kwargs['domain'] == 'djangojs':
            # Set the default extensions
            kwargs['extensions'] = kwargs['extensions'] or []
            kwargs['extensions'].append('.js')
            kwargs['extensions'].append('.vue')
            self.xgettext_options.append('--keyword=interpolate')
            # Set language to C so xgettext can pick up translations in Vue templates
            self.xgettext_options.append('--language=C')
        return super().handle(*args, **kwargs)

With default JavaScriptCatalog setup I have a js module that I import whenever I want to translate something

// Exports Django's `JavaScriptCatalog` functions dynamically
const djangoTranslationFns = [
  'gettext',
  'ngettext',
  'get_format',
  'gettext_noop',
  'pgettext',
  'npgettext',
  'pluralidx',
];
const exports = {};
if (Object.keys(window).includes(djangoTranslationFns[0])) {
  // If one of the functions are defined then it's safe to asumme they all are
  djangoTranslationFns.forEach((fn) => { exports[fn] = window[fn]; });
  // Override the interpolate function so it uses `gettext` by default
  exports.interpolate = (fmt, obj, named) => window.interpolate(window.gettext(fmt), obj, named);
}

export default exports;

Then in the template

<script setup>
import i18n from '@/i18n';
</script>

<template>
  <h1>{{ i18n.gettext("Welcome") }}</h1>
</template>
Ammar Alammar
  • 313
  • 5
  • 15