Polyconseil / vue-gettext

Translate your Vue.js applications with gettext.
MIT License
278 stars 55 forks source link

Translations in attributes #9

Open hworld opened 7 years ago

hworld commented 7 years ago

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 7 years ago

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>
hworld commented 7 years ago

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!

trainiac commented 7 years ago

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

trainiac commented 7 years ago

@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 7 years ago

@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.

hworld commented 7 years ago

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 7 years ago

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.

mazavr commented 6 years ago

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?

alansoliditydev commented 6 years ago

@mazavr it work, man

alexkiro commented 5 years ago

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
drzippie commented 5 years ago

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>
vperron commented 5 years ago

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.

drzippie commented 5 years ago

the current version of easygettext don't extract attributes like

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

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 ?

kwesterfeld commented 5 years ago

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 5 years ago

@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?