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

feat: add full support of Vue.js #1925

Open
wants to merge 5 commits into
base: next
Choose a base branch
from

Conversation

JSteunou
Copy link
Contributor

@JSteunou JSteunou commented Apr 29, 2024

add Vue.js support with components, plugins, extractor and compiler

Description

Title is kinda catchy because in reality there is still a lot to do, but this PR brings at least a full Vue.js experience that just works

Types of changes

  • Bugfix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Documentation update
  • Examples update

Fixes # (issue)

Checklist

  • I have read the CONTRIBUTING and CODE_OF_CONDUCT docs
  • I have added tests that prove my fix is effective or that my feature works
  • I have added the necessary documentation (if appropriate)

To this checklist I will add


For more context this is the README of my original repo https://github.com/JSteunou/vue-lingui

Why?

#1730

How to use lingui in vue SFC?

in the setup part

You can use any of the current @lingui/macro helper it will just work as long as you follow the vite configuration instruction below.

Of course vanilla JS with @lingui/core also works fine

in the template part

You will have to use either <Trans> or vt from lingui-vue. Both mimic the actuel lingui macros <Trans> and t but with limited functionnalities. (see What still need to be done?)

<script setup lang="ts">
import { t } from '@lingui/macro'
import { Trans, vt } from 'lingui-vue'

const hello = t`Hello world`
</script>

<template>
  <h1>{{ hello }}</h1>
  <p><Trans>You can use lingui in vuejs!</Trans></p>
  <img src="" :alt="vt`and in prop`" />
</template>

How to configure your vuejs / vite project?

lingui config

This is mostly as the doc says except you have to use this new extractor.

import { vueExtractor } from 'lingui-vue/extractor'

vite config

This is what a really basic config will look like:

import { lingui } from '@lingui/vite-plugin'
import { babelMacros, transformer } from 'lingui-vue/compiler'
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [
    vue({
      template: {
        compilerOptions: {
          nodeTransforms: [transformer],
        },
      },
    }),
    lingui(),
    babelMacros(),
  ],
})
  • The transformer will do some magic inside your template sections
  • The lingui plugin is nothing more than the vite plugin / loader for catalogs
  • The babelMacros plugin will make lingui macros work with vite

How it works?

See #1730 (comment)

Or to wrap things up with some detailed examples

In extraction phase

<Trans> will be find in the SFC AST by the vue extractor for lingui and will be transformed to a thing that the lingui default extractor already knows and will handle.

<script setup lang="ts">
import { Trans } from 'lingui-vue'
const name = 'John'
</script>

<template>
<Trans context="demo">Hello <strong>{{ name }}</strong></Trans>
</template>

// ↓ ↓ ↓ ↓ ↓ ↓

<script setup lang="ts">
import { Trans } from 'lingui-vue'
const name = 'John'
</script>

<template>
{{ i18n._({message: 'Hello <0>{name}</0>', id='mk8bSG', context='demo'}) }}
</template>

vt will be find in the SFC AST the same way and also transformed.

Notice the change of name to not collide with existing t macro that can be used in the same module.

<script setup lang="ts">
import { vt } from 'lingui-vue'
</script>

<template>
<img src="" :alt="vt`img description`" />
</template>

// ↓ ↓ ↓ ↓ ↓ ↓

<script setup lang="ts">
import { vt } from 'lingui-vue'
</script>

<template>
<img src="" :alt="i18n._({message: 'img description', id='mk8bSG'})" />
</template>

In build & runtime phase

Again <Trans> will be find in the SFC AST but this time by the vite / vue transformer and will be transformed to a more complexe form that just works and allows to use variables and inner tags / components without worring about this complex syntax.

<script setup lang="ts">
import { Trans } from 'lingui-vue'
const name = 'John'
</script>

<template>
<Trans context="demo">Hello <strong>{{ name }}</strong></Trans>
</template>

// ↓ ↓ ↓ ↓ ↓ ↓

<script setup lang="ts">
import { Trans } from 'lingui-vue'
const name = 'John'
</script>

<template>
<Trans context="demo" id="mk8bSG" message="Hello <0>{name}</0>" :values="{name: name}">
  <template v-slot:0="{children}">
    <strong><component :is="children"></component></strong>
  </template>
</Trans>
</template>

vt will not be transformed and will be used as is in runtime. This is possible only thanks to the recent feature that lingui introduced in 4.60 about ids hence the strong requirement.

What still need to be done?

but will be done later... (any contributions are welcome)

  • support messages in <Trans>
    • initial support
    • support inner tags (unlimited depth)
    • support simple values
    • initial support of reactive values
    • better support reactive values (still some issue in development HMR mode)
    • support complexe variables with positional placeholder ({user.name} => {0})
    • support custom id
    • support custom message
    • support context
    • support comments
  • support messages as prop during extraction
    • add helper / macro vt
    • support values
    • support interpolation prop (like ternary)
    • support vt in component child interpolation
    • support vt in Trans inner tag attributes
    • support context
    • support i18n instance
    • support custom id
    • support comments
  • add Plural
  • add SelectOrdinal
  • add Select
  • add formaters

Copy link

vercel bot commented Apr 29, 2024

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
js-lingui ✅ Ready (Inspect) Visit Preview 💬 Add feedback May 22, 2024 8:38pm

Copy link

github-actions bot commented Apr 29, 2024

size-limit report 📦

Path Size
./packages/core/dist/index.mjs 2.86 KB (0%)
./packages/detect-locale/dist/index.mjs 723 B (0%)
./packages/react/dist/index.mjs 1.65 KB (0%)
./packages/remote-loader/dist/index.mjs 7.26 KB (0%)

add Vue.js support with components, plugins, extractor and compiler
): TemplateNode {
const loc = child.loc
return {
type: /* NodeTypes.ELEMENT */ 1,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i already asked, but don't remember the answer, why not use NodeTypes enum directly?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had issue with those imported enum in my project, but I'll try again here.

Comment on lines 15 to 26
const result = `import { resolveComponent as _resolveComponent, createVNode as _createVNode, openBlock as _openBlock, createElementBlock as _createElementBlock } from "vue"

export function render(_ctx, _cache) {
const _component_Trans = _resolveComponent("Trans")

return (_openBlock(), _createElementBlock("template", null, [
_createVNode(_component_Trans, {
id: "cr8mms",
message: "This is some random content"
})
]))
}`
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's better to use snapshots (inline or external) to test it, this will make maintaing it in future so much easier, especially when Vue itself will change something in the way how they transpiling.

Here is an example how i did snapshot testing for core macro.

Note that snapshot contains input and output at the same time, for easier error spotting.

@thekip
Copy link
Collaborator

thekip commented Apr 30, 2024

Cool, thanks. I actually don't know what steps should we take to proceed it further.

  1. AFAIK this package will replace @lingui/vue-extractor. So we should decide on what to do with that package
  2. Tutorials, API descriptions, examples

Could you also describe what exactly left to do?

Upd:

I think it worth it to copy the readme from your original repo into this PR description

@andrii-bodnar
Copy link
Contributor

We should probably hide @lingui/vue-extractor from the docs sidebar (and add a deprecation warning to the actual article) and deprecate the npm package in v4. Then it will be removed from the codebase in v5.

I also thought a lot about how to organize the documentation. At least we need to create a new page in the API Reference section. In addition, it would be good to have a separate page in the Tutorials section describing the full path of using the new package in a Vue application. I don't think we need a separate installation article, just include this step in the tutorial (maybe the Installation section will disappear in the future).

I see the Tutorials section as a dedicated place to describe Lingui usage with various frameworks (React, Vue, Next.js, React Native, Svelte, etc.) and think about moving the React common patterns and Explicit vs Generated IDs articles to the Guides section.

@JSteunou
Copy link
Contributor Author

Cool, thanks. I actually don't know what steps should we take to proceed it further.

1. AFAIK this package will replace `@lingui/vue-extractor`. So we should decide on what to do with that package

2. Tutorials, API descriptions, examples

I would advice to deprecate @lingui/vue-extractor in a v4.xx and remove it in v5 with a migration documentation. The new extractor covers the same things + more features

@aseerkt aseerkt mentioned this pull request May 1, 2024
8 tasks
@thekip
Copy link
Collaborator

thekip commented May 6, 2024

My fifty cents:

We can publish it as beta for a while with a description what exactly the drawbacks.

The most important things to be done:

  1. Plural / Select / etc support for both Component and functional usage
  2. Compile time transpiling for vt now it seems that message would be doubled in the code since original message is not dropped.

I'm thinking about how we can reuse existing macro code for vue version.

@andrii-bodnar
Copy link
Contributor

@JSteunou how can we proceed with this PR?

@JSteunou
Copy link
Contributor Author

@JSteunou how can we proceed with this PR?

Sorry guys, not available ATM, but I will take a moment on this next week. Priority on docs, then snapshots.

I think it is better to publish early, feature freeze, and add support for Plural and all other goodies later.

@thekip
Copy link
Collaborator

thekip commented May 20, 2024

@JSteunou how do you use plurals in Vue right now? It seems the only way is manually wright ICU expressions and pass arguments.

Also "Compile time transpiling for vt" is not only about dedoubling messages in the bundle, but also about feature parity. The vt will work diffrently, will not support other macros inline and will not follow the same rules about variable placeholders. So when eventually this function would be updgraded to real t macro it would be a breaking change.

@JSteunou
Copy link
Contributor Author

@JSteunou how do you use plurals in Vue right now? It seems the only way is manually wright ICU expressions and pass arguments.

Nope, the actual way is to use plural in the vue <script setup> part.

Also "Compile time transpiling for vt" is not only about dedoubling messages in the bundle, but also about feature parity. The vt will work diffrently, will not support other macros inline and will not follow the same rules about variable placeholders. So when eventually this function would be updgraded to real t macro it would be a breaking change.

I try to make vt as close as possible as t but being realistic I dont think it will be possible (even <Trans> will not be 100% equal).

plural & t can be used inside <script setup and I could have stopped here, but while playing around me and my teammates realized having t features inside <template> was mandatory, hence vt for a better DX.

@thekip
Copy link
Collaborator

thekip commented May 20, 2024

i think integrating deeply core t macro into the vue templates is possible. The idea which has to be proven is the following:

  1. Using vue-compiler mark vt`Hello` in templates into something like /*__lingui_macro__ */ t`Hello`.
  2. Then operating on the level of the regular babel macro take that expression and transpile as it would export directly from macro package.
  3. Voila, profit.

I believe the vue compiler expose analyzed binding which is passed from setup script to the template, so you can use them to fin instances of t in the code.

@JSteunou
Copy link
Contributor Author

i think integrating deeply core t macro into the vue templates is possible. The idea which has to be proven is the following:

1. Using vue-compiler mark `` vt`Hello` `` in templates into something like `` /*__lingui_macro__ */ t`Hello` ``.

2. Then operating on the level of the regular babel macro take that expression and transpile as it would export directly from macro package.

3. Voila, profit.

I believe the vue compiler expose analyzed binding which is passed from setup script to the template, so you can use them to fin instances of t in the code.

As to be tested, but SFC compiler is so much not documented, it is a pain to find what it does and what can be done safely. :(

If it is possible to go this way, vt could be dropped to use t directly, might be more an evolution than a breaking feature. If timing is prior, could we go with vt first and explore later?

@thekip
Copy link
Collaborator

thekip commented May 21, 2024

I was able to incorporate existing macro code into the vue tranformer, DMed in the discord for more info

image

@thekip
Copy link
Collaborator

thekip commented May 22, 2024

i've incorporated lingui internals into vue transforms, so now:

  1. vt (or t) supporting everything what core macro t supports. Including plural and other methods.
  2. I rewrote Trans transform, so it's also uses Lingui internals partially. It doesn't support Plural yet, but it is not very complex to add it in the future.
  3. There are no separate transform for extractor, the main transform is transpiling Trans and t into message descriptor which original extractor understand out of the box (i found it unnecessary to replicate extractor functionality for vue extractor)

There a still things to do:

  1. More tests for runtime components
  2. Shape and stabilize API, vt vs t, what package should export them, etc
  3. stripNonEssentialProps - didn't find how to get isProd from the compiler, probably we will have to pass it manually to the transform
  4. cleanup runtime part of vt
  5. ensure that 'lazy' translations approach is supported in Trans / vt
  6. add example application

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

Successfully merging this pull request may close these issues.

None yet

4 participants