Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

xgettext fails with some .vue files [was: makefile ignore strings] #28

Open
thujone25 opened this issue Jun 22, 2017 · 19 comments
Open

Comments

@thujone25
Copy link

thujone25 commented Jun 22, 2017

Hi! I have a Vue component file which are ignored by Makefile. What do I make wrong?
The text appears in the view but doesn't appear in .po files after make makemessages

screenshot from 2017-06-22 17-01-53

@thujone25
Copy link
Author

I've found out that everything works good if I remove line №7 or №8
But I can't understand why.

@kemar
Copy link
Contributor

kemar commented Jun 22, 2017

Which one is line 7? (it's difficult to help you when the only thing I have is a screenshot)

@thujone25
Copy link
Author

thujone25 commented Jun 22, 2017

<template>
  <form class="auth-form"
        :class="{'auth-form--password-pages': (forgotPass || newPass)}">
    <h1 class="auth-form__title">
      <span>{{ headerText }}</span>
    </h1>
    <special-error class="auth-form__errors-block"></special-error>
    <p class="auth-form__forgot-pass-text" v-if="forgotPass">{{ text3 }}</p>
    <slot name="emailField"></slot>
    <slot name="passwordField"></slot>
    <slot name="submitBtn"></slot>
    <em class="auth-form__password-text"
        v-if="newPass"></em>
    <router-link :to="{name: 'SignIn'}" 
                 v-if="forgotPass"
                 class="tt-link tt-link--destroy-action auth-form__forgot-pass-link">{{ text4 }}</router-link>
    <div class="auth-form__remember-forgot-cont" v-if="rememberSection">
      <div class="auth-form__remember-forgot-section">
        <slot name="rememberCheckbox"></slot>
      </div>
      <div class="auth-form__remember-forgot-section auth-form__remember-forgot-section--link">
        <router-link class="tt-link"
                     :to="{name: 'RestorePassword'}">{{ text1 }}</router-link>
      </div>
    </div>
    <social-links :hr-text="text2"
                  v-if="socialLinks"></social-links>
  </form>
</template>

<script>
  export default {
    props: [
      'headerText',
      'paddingRight',
      'rememberSection',
      'socialLinks',
      'forgotPass',
      'newPass'
    ],
    computed: {
      text1() {return this.$gettext('Forgot your password?');},
      text2() {return this.$gettext('or join in with');},
      text3() {return this.$gettext('Enter the e-mail address associated with your account, and we will send you a link to reset your password.');},
      text4() {return this.$gettext('Cancel');},
      text5() {return this.$gettext('8 characters minimum');},
    }
  }
</script>

Lines:

<special-error class="auth-form__errors-block"></special-error>
    <p class="auth-form__forgot-pass-text" v-if="forgotPass">{{ text3 }}</p>

@kemar
Copy link
Contributor

kemar commented Jun 22, 2017

Ok, now I can reproduce and confirm your issue.

I think that xgettext fails during extraction (here). I don't know exactly why but I think this is related to the way xgettext is parsing your .vue file.

By default xgettext cannot extracts strings directly from .vue files. I used a trick in order to make it think it's parsing a JavaScript file.

However, I can see that it's not working for you :'(

Unfortunately I have no easy solution for this problem yet. The ideal solution could be to code a custom extractor for .vue files (see also #9) but I don't have much time yet to implement it.

@kemar kemar changed the title makefile ignore strings xgettext fails with some .vue files [was: makefile ignore strings] Jun 22, 2017
@thujone25
Copy link
Author

Hi. I've spotted that wrapping template markup with parentheses solves the problem.
Also it allows us to use Inline expression. from #9
Unfortunately I've not found a solution to use Inline expression for props (<component :prop-name="$gettext('some text')"></component>) yet.
I hope that this small trick with parentheses can help.
So instead of

<template>
  <translate>text 1</translate>
  <p v-translate>text 2</p>
  <p>{{ text3 }}</p>
</template>

<script>
  export default {
    computed: {
      text3() {return this.$gettext('Text 3');}
    }
  }
</script>

we should wrap <template>

(<template>
  <translate>text 1</translate>
  <p v-translate>text 2</p>
  <!-- Inline expression. -->
  <p>{{ $gettext('Text 3') }}</p>
</template>)

It's hack so some testing is needed.

@GUI
Copy link

GUI commented Jan 3, 2018

As one other potential option for xgettext parsing of Vue files, I've created gettext-vue which integrates with xgettext-template to do some pretty basic parsing of Vue files.

The parsing is just done with some basic regexes (based on gettext-ejs) that might not handle more complicated parsing situations, but it seems to work well for our use-case. The parsing doesn't take any context into account, so it's just looking for any function calls named $t, $gettext, or $ngettext by default (although those keywords can be configured with xgettext-template). But this simplistic approach does mean it picks up references in both the <template> and <script> sections of Vue files, and it should also pick up the various syntaxes in the templates (eg, {{ $t('Hello') }} or <div v-bind:placeholder="$t('World')">).

So while this might not cover all parsing situations, hopefully it might be useful to others. And if we can improve the parsing for other situations, I'm open to pull requests or feedback.

@kemar
Copy link
Contributor

kemar commented Jan 9, 2018

@GUI thanx for letting us know. This is really interesting.

@bart-1990
Copy link

bart-1990 commented Jan 15, 2018

I did find one bug in the xgettext parsing of .vue files. It has something to do with the amount of '/' the file contains.

Suppose you have the following test.vue file:

<template>
	<div>
		<div></div>
	</div>
</template>

<script>
	export default {
		computed: {
			zoomLabel() {
				return this.$gettext('Zoom');
			}
		}
</script>

Running the command
xgettext --language=JavaScript --keyword=npgettext:1c,2,3 --from-code=utf-8 --join-existing --no-wrap --output translations.pot test.vue will not extract the Zoom string in the translations.pot file.

However when you change the file to one of the following 2 templates, it will work!

<template>
	<div></div>
</template>

<script>
	export default {
		computed: {
			zoomLabel() {
				return this.$gettext('Zoom');
			}
		}
</script>

--> 2 / now because of the removal of one <div>

<template>
	<div>
		<div></div>
	</div>
</template>

<script>
	import foo from './foo';

	export default {
		computed: {
			zoomLabel() {
				return this.$gettext('Zoom');
			}
		}
</script>

--> 4 / now because of the import statement

Do mind I'm not counting the / in </script>.

@thujone25 If I counted well, your template also contains an odd number of /, so I think adding an element would probably resolve the issue, not only removing line 7 or 8. Of course it's not an elegant solution to add extra elements but I don't know anything about xgettext parsers.

@thujone25
Copy link
Author

@bart-1990 actually I wrap every template with () and I've not faced with problems yet. So I think that () better than counting /

@bart-1990
Copy link

bart-1990 commented Feb 21, 2018

It's still a bug that needs solving. :) Wrapping all my templates in () is not something I'm particularly fond of but indeed it helps.

@narrowtux
Copy link
Contributor

For webpack users that want to use "$gettext" etc in slots: I've created a webpack loader that plugs behind vue-loader, gets the compiled vue source code, runs GNU xgettext on that source code and emits a pot file for each vue component file. You can also use this for other files that are transpiled to javascript.

https://gist.github.com/narrowtux/6b745e9346db97cec7f122f5bdf9bba4

@lajosbencz
Copy link

lajosbencz commented Dec 14, 2018

Putting the <script> tag at the top of vue files solved the missing msgid problem for me.

@pieterjandesmedt
Copy link

pieterjandesmedt commented Nov 23, 2020

Is anyone still looking at this? The repo doesn't seem to be maintained anymore? I tried all solutions in this thread (wrapping in (), adding a /, moving the script tag, gettext-vue), but none of them seem to work. Is there a better solution than using vue-gettext right now?

@kemar
Copy link
Contributor

kemar commented Nov 24, 2020

Sorry @pieterjandesmedt I don't have time anymore to contribute to vue-gettext. In fact I haven't worked with Vue.js in over three years. I'm barely merging PR and keeping deps up to date.

@mitar
Copy link

mitar commented Nov 24, 2020

@kemar Still thank you for doing that!

@pieterjandesmedt
Copy link

@kemar thank you for your great work! I used (and still use) vue-gettext a lot. If you want, I can help you maintain it.

I made a workaround with two oneliners in the makefile:

perl -ne 'print if(/<script>/../<\/script>/)' $(GETTEXT_HTML_SOURCES) | sed 's|<script>||' | sed 's|</script>||' > vue-scripts.tmp.js

This concatenates all javascript of the single file components into one file (named vue-scripts.tmp.js)

sed -n '/\<template\>/,/\<\/template\>/p' $(GETTEXT_HTML_SOURCES) |  perl -ne 'print if (/gettext\(/../\)/)' | sed -E 's|.*([n$$]+)(gettext\(.*\)).*|\1\2|g' > vue-scripts2.tmp.js

This extracts all gettext(-lookalikes from the <template>, so also those in the html attributes into vue-scripts2.tmp.js

Then I add these to files into the xgettext extraction like so:

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) vue-scripts.tmp.js vue-scripts2.tmp.js

It's not perfect (e.g. it doesn't detect npgettext in attributes) but it does the trick for me now.

@kemar
Copy link
Contributor

kemar commented Nov 27, 2020

@pieterjandesmedt thx :)

@vperron I'm not able to maintain vue-gettext anymore, what do you think about adding maintainers or transferring ownership?

@vperron
Copy link
Contributor

vperron commented Nov 27, 2020

I'm 100% open to adding external maintainers, as many as possible actually. As long as you've successfully added a PR you should be able to get the maintaining rights. I'll look into it.

@vperron
Copy link
Contributor

vperron commented Nov 27, 2020

@pieterjandesmedt @mitar @GUI @bart-1990 could you please summarize very quickly what's teh topic at hand, I'm not sure anymore what's the issue here :)
I'd recommend to use https://github.com/Polyconseil/easygettext which has been really well maintained and supports everything from Vue 3 templates to Typescript and so on, bu maybe I'm missing the point.

Also, if anyone is up for getting upgraded and get write rights to the repo and help maintain it, feel free !

@kemar thanks a lot for all the good stuff on this for so long !

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

9 participants