Polyconseil/vue-gettext

Translations in attributes

hworld opened this issue ยท 16 comments

Hey there! I didn't see in the documentation an answer to this. I'm working on switching away from angular and therefore angular-gettext. With that lib you can translate attributes of html elements like so: <div title="{{ 'Translate this' | translate }}">.

Is the solution for vue-gettext to instead call into the vm methods and use a computed property instead or is a similar filter possible? I know that in vue2 filters are a bit discouraged now, though.

kemar commented

Nope you can't do this in that way because interpolation in attributes has been deprecated in Vue.js 2.

However, you can use an inline expression or a computed property:

<template>
  <div>

    <!-- Inline expression. -->
    <p :title="$gettext('Translate this')">Foo bar.</p>

    <!-- Computed property. -->
    <p :title="myTitle">Foo bar.</p>

  </div>
</template>

<script>
export default {
  computed: {
    myTitle () {
      return this.$gettext('Translate this')
    },
  },
}
</script>

Ah, I didn't even think of the fact that I can use "$gettext" within the template like that. That's perfect. Thanks so much!

The computed property example works but the inline expression is not working for me. The make file command is not capturing it as a translation.

vue-gettext 2.0.8
Mac Os Sierra 10.12.3
gettext 0.19.8.1
easygettext 1.2.2

@kemar Do you have an example of the inline expression working? If so I'll dig deeper into what I might be doing wrong.

kemar commented

@trainiac yes you're right: the Makefile target does not extract translations for inline expressions.

I'll need to have a depper look at gettext-extract or xgettext to see what would be the best way to fix this.

Meanwhile you can use a computed property to circumvent the problem.

The only thing I could think of to solve this problem was to compile all templates using the vue template compiler and then crawling over that build folder to extract the strings. =\

kemar commented

I have no easy solution for this problem yet.

gettext-extract (as it is currently) will only works with custom tags or attributes.

And xgettext will not parse the non JavaScript part of single-file components with a .vue extension.

The most likely "good enough" solution would be to implement something like this and feeding xgettext with the results of a e.g. a sed command. That could raise other problems like loosing the context of the files etc.

In my project I use this method:
a.html

<input type="text" :placeholder="'Email address' | translate"/>

Then

gettext-extract --startDelimiter "" --endDelimiter "" --output a.pot a.html

It grabs my sting to *.pot file

And my filter:

Vue.filter('translate', value => {
  return !value
    ? ''
    : Vue.prototype.$gettext(value.toString())
})

Will it work?

@mazavr it work, man

Ran into this issue as well, FWIW the solution mentioned here by @mazavr does work, however the extraction is really slow this way.

I ended up using a mix of vue-gettext and babel-vue-extractor, and then using msgcat to concatenate the two.


With a babel.cfg file like:

[babelvueextractor.extract.extract_vue: **.vue]

And the running something like this

gettext-extract --quiet --attribute v-translate --output translations-gettext.po <source files>
pybabel extract -F babel.cfg --keywords '$gettext' -o translations-babel.po <source files>
msgcat translations-gettext translations-babel.po -o translations.po

Using @alexkiro method I use this bash file to extract and make translations
generate_translations.sh

#!/usr/bin/env bash


NODE_BINDIR=../node_modules/.bin
INPUT_FILES=./assets/js
TEMP_BASE=./base.pot
TEMP_EXTRA=./extra.pot
TEMP_MERGED=./merged.pot
OUTPUT_DIR=./assets/langs

export PATH="$NODE_BINDIR:$PATH"

LOCALES=(en_US es_ES re_RE)

GETTEXT_SOURCES=`find $INPUT_FILES -name '*.jade' -o  -name '*.html' -o    -name '*.js' -o    -name '*.vue'`


gettext-extract --quiet --attribute v-translate  --output $TEMP_BASE   $GETTEXT_SOURCES
pybabel extract -F babel.cfg --keywords '$gettext' -o  $TEMP_EXTRA  $GETTEXT_SOURCES
msgcat ./base.pot ./extra.pot -o $TEMP_MERGED

LOCALE_FILES=""
for lang in "${LOCALES[@]}"
do
   :
   PO_FILE=$OUTPUT_DIR/locale/$lang/LC_MESSAGES/app.po
   LOCALE_FILES=" $LOCALE_FILES $OUTPUT_DIR/locale/$lang/LC_MESSAGES/app.po "

   mkdir -p  `dirname $PO_FILE`

   if [  -f  $PO_FILE ]; then
   msgmerge --lang=$lang --update $PO_FILE $TEMP_MERGED
  else
    msginit --no-translator --locale=$lang --input=$TEMP_MERGED --output-file=$PO_FILE
	msgattrib --no-wrap --no-obsolete -o $PO_FILE $PO_FILE;
  fi

done

mkdir -p $OUTPUT_DIR
gettext-compile --output $OUTPUT_DIR/translations.json $LOCALE_FILES

rm $TEMP_BASE $TEMP_EXTRA $TEMP_MERGED

for attributes like:

<info-box :title="$gettext('Staff')"></info-box>

In my experience, https://github.com/Polyconseil/easygettext extracts everything now, including Vue templates, ES7 code, and so on. msgmerge etc is not necessary anymore and should be removed from the docs. I'll do it asap.

the current version of easygettext don't extract attributes like

<my-component :my-attribute="$gettext('my translatable string')">[...]</my-component>

that seems to me like a nice addition to easygettext :) Do you think you could come up with a pull request ? Would seem way cleaner than a complex pybabel + GNU gettext + easygettext utilities, don't you think ?

Given that easygettext works with | translate filter syntax and translate, I have adopted this as the best solution here. While using an expression with attr=":$gettext('Text')" silently "works" in that the translation compiles, easygettext doesn't see this type of text and so that is a dead end.

In using | translate filter syntax, I found I needed interpolation support so I tweaked my Vue.filter this way:

Vue.filter('translate', function(value, params) {
  return !value
    ? ''
    : (params
      ? Vue.prototype.$gettextInterpolate(value.toString(), params)
      : Vue.prototype.$gettext(value.toString()))
});

This allows me to create attribute expressions like this:

<smc-okcancel-dialog :title="'Change User: %{user.usename}' | translate({user})" 

Which I find super helpful and awesome. I propose this just be added to vue-gettext and documented as the solution for this problem, since it is requires no additional work to support attributes.

kemar commented

@kwesterfeld The part 'Change User: %{user.usename}' is still not being extracted when used in a vue file. Did you manage to get it working? Can you sumbit a PR with a working example?