SWC plugin
Is your feature request related to a problem? Please describe. I want to switch our projects from Babel to SWC, a Rust-based JS transpiler. SWC is used by Next.js unless you opt-out from it because you rely on Babel.
Describe proposed solution SWC has a brand new plugin system, which should be able to handle the transformations needed by Lingui's macros.
This is not yet stable / well documented, but some plugins are available here: https://github.com/swc-project/plugins (for Styled Components for example).
Those plugins are compiled to WASM, so they can be written in several languages (including AssemblyScript, a Typescript derivative compiling to WASM)
A first step would probably be to continue using Babel for extracting and use this plugin only to transform macros, but in the long term being able to use SWC for extracting could bring a much faster extract and heavily reduce dependencies needed for extraction (swc + swc-lingui and thats pretty much all).
Describe alternatives you've considered
- Porting Babel macros to a SWC plugin: not sure this can easily be done, this has been discussed a bit in https://github.com/kentcdodds/babel-plugin-macros/issues/144 but this would probably be a new project. Also
babel-macrosis frozen as the author no longer uses it - Using another transpiling tool, like
esbuild: there is no support foresbuild+ Lingui either (andesbuilddoes not have a plugin support) - Switching away from Lingui: I really like Lingui, I would prefer not :)
- Keep using Babel:
swc(and other modern compilers) are much faster, it would really be better to be able to switch away!
Additional context Add any other context or screenshots about the feature request here.
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.
This issue is getting a lot of π, dont close it!
Best post here, swc, ts-jest, both aren't supported by lingui, and our code base growing, our unit test gets poor performances, and the question to remove lingui become more and more obvious.
This is a must for big projects, otherwise lingui will never stay long in any on them.
Any update on this one?
Nothing yet we can't do right now, there's no way we can replicate the macro's thing on swc (at least as far as I know, not a rust expert here) But of course, any help or info will be highly appreciated :)
Our project which is used by millions of users now remove lingui translation for nothing, twice time lost. My advice, don't use macro and even more experimental lingui for your app.
Hi! Is there any progress or any plans for the future?
We have codebase build on Lingui but right now we are upgrading to React 18 + Next 13 and we don't want to stay on Babel.
For now we are okay with @lingui/react syntax but we would like to use lingui extract but its also not possible without Babel.
Is there any chance? π
Yes I'm already moving pieces together thanks to a similar project that implemented Macros on SWC, I'm already working on something suitable for Lingui... I'll open-source it soon.
@semoal do you mean that youβve started implementing a plugin in rust for swc?
I can help at least testing it and maybe writing it. I guess that you need writing a visitor, right?
Iam also ready and happy to test in on our codebase π₯
Hey! I found some solution that enables Babel for Lingui export but Next.js app can be still SWC-only.
You can just add custom Babel configuration into .linguirc file and remove your Babel configuration file in app.
More info in documentation (https://lingui.js.org/ref/conf.html#id3) For me works just Next.js babel configuration:
"extractBabelOptions": {
"presets": ["next/babel"]
}
@sitole Are you using @lingui/macro?
I've tried but I'm getting some error in the @lingui/macro stack

@sitole Are you using
@lingui/macro?I've tried but I'm getting some error in the
@lingui/macrostack
Yea, this is just for non-macro users. But you can still use <Trans> from @lingui/react also with i18n from @lingui/core. But Sam just debugging possible issues. It's not solution ready for production. π
For example with this and Babel configuration i'm not able to export TypeScript translations for example
const propertyName = i18n._(`super-code-in-typescript-code`)
This would help us a lot. Anyway how can I help?
Well, don't know how it's going by @semoal. I've tried to contact him but no luck. So I started my own development of SWC plugin. It's now on very early stages and it's moving quite slow because i'm not a professional rust developer (but i do have a lot of expertise in writing transforms and working with AST so it helps)
Currently, i have only this tests passing:
test!(
Default::default(),
|_| TransformVisitor,
substitution_in_tpl_literal,
// input
r#"
t`Refresh inbox`
t`Refresh ${foo} inbox ${bar}`
t`Refresh ${foo.bar} inbox ${bar}`
t`Refresh ${expr()}`
"#,
// output after transform
r#"
i18n._("Refresh inbox", {})
i18n._("Refresh {foo} inbox {bar}", {foo, bar})
i18n._("Refresh {0} inbox {bar}", {0: foo.bar, bar})
i18n._("Refresh {0}", {0: expr()})
"#
);
test!(
Default::default(),
|_| TransformVisitor,
custom_i18n_passed,
// input
r#"
t(custom_i18n)`Refresh inbox`
t(custom_i18n)`Refresh ${foo} inbox ${bar}`
t(custom_i18n)`Refresh ${foo.bar} inbox ${bar}`
t(custom_i18n)`Refresh ${expr()}`
"#,
// output after transform
r#"
custom_i18n._("Refresh inbox", {})
custom_i18n._("Refresh {foo} inbox {bar}", {foo, bar})
custom_i18n._("Refresh {0} inbox {bar}", {0: foo.bar, bar})
custom_i18n._("Refresh {0}", {0: expr()})
"#
);
Which is personally for me is a big WIN because showing potential and that rest is just a matter of time.
If someone has more experience in rust, and would like to help, i'm looking for a rust mentorship or at least code review of what i've written.
Very nice work @thekip! Do you have open a pull request somewhere, or any versioned source code that you can share? I'm willing to help if I can.
This is amazing!
Could you share a repository? I think SWC author / Vercel employees might want to help you on this as this is one of the most wanted plugins for Next.js users
https://github.com/thekip/swc-lingui-plugin
As mentioned this is not production ready and very far even from 0.0.1 stage.
Future plans:
I'm going to implement a bare minimum of a macro (JSX transformation is a must). Probably not all edge cases or event functionalities would work in that, so only the most useful and important (JSX transformation, plural) and then will ask help from community to implement the rest.
I would recommend you to keep a list of things to do in the README, so people can help implementing some of them. Also split in into what you want for a first version, and what is next.
You should also open some issues on topics you are not sure you handled correctly, questions to be answeredβ¦
I dont give any promises as I am very busy atm but I will watch the repo and have a look asap!
@renchap good suggestions. I've added a list of tasks to the repo, and will create couple issues with questions which i don't know how to solve properly.
Meanwhile, i've implemented basic transformation for ICU calls (plurals, select, etc), it's still far from first launch but implementation went much quicker than before.
Started implementing JSX transformation and extended list of tasks in readme.
Also checked original macro's test suite, well... there so much cases...
The current macro implementation allows the user to use too much different syntax. From my point of view it's better to keep implementation simple and provide users with opinionated way how to write code instead of implementing all possible ways how user can write the code (the js with jsx is so flexible)
For example, user can write:
<Trans>{'My Text'}</Trans>
Instead of simple:
<Trans>My Text</Trans>
This is supported in current babel macro. From AST point of view this children prop is an expression, not a JSXString, so to support it in Rust implement should have a complicated heuristic to parse what inside expression and should we use it as message or not.
Latest updates. I've implemented jsx transformations with interpolations and imports replacement, so now this test passes:
import { Trans } from "@lingui/macro";
const t = <Trans>
Hello <strong>World!</strong><br />
<p>
My name is <a href="/about">{" "}
<em>{name}</em></a>
</p>
</Trans>
// // β β β β β β
import { Trans } from "@lingui/core";
const t = <Trans id={"Hello <0>World!</0><1/><2>My name is <3> <4>{name}</4></3></2>"} values={{
name: name,
}} components={{
0: <strong />,
1: <br />,
2: <p />,
3: <a href="/about" />,
4: <em />
}} />;
It was the hardest part, so rest is should be much simpler.
For those how are tracking progress on the plugin. I've completely stuck trying to implementing recursive parsing of ICU / Trans expressions in the JSX. I'm stuck implementing usual simple data-structures in Rust's needed to implement this recursive feature. Rust is very different when it comes to data structures and some popular approaches just don't work, and i could not come how to make it in Rust-way.
By recursive JSX i mean something like this:
<Trans>
Hello <strong>World!</strong><br />
You have <Plural value={5} one={"# message"} few={"# messages"}>
</Trans>
ping @hywan I know that you master Rust like nobody, maybe can you point @thekip to some solution? :thinking:
I moved on from the dead point. Reorganized code to do stuff in two phases - collecting, then emitting. This way, rust allowed me to do so. Have no idea how fast implementation would be, i'm more concentrated on greening as many tests from original test suite as possible.
So this
<Trans>
Hello <strong>World!</strong><br />
You have <Plural value={5} one={"# message"} few={"# messages"}>
</Trans>
And even this
<Trans>
Hello <strong>World!</strong><br />
You have <Plural value={5} one={<Trans># message</Trans>} few={<Trans># messages</Trans>}>
</Trans>
With any level of depth is supported.
Now i'm focused on refactoring t macro to make it work the same way as JSX
Hey :-),
How can I help?
@Hywan It would be great if you could do a code review and give some advices on https://github.com/thekip/swc-lingui-plugin
I'm new to rust so all suggestions would be appreciated.
Meanwhile, you guys can review this ticket related to macro
https://github.com/lingui/js-lingui/issues/1324 https://github.com/lingui/js-lingui/issues/1323 https://github.com/lingui/js-lingui/issues/1320
You might have more experience with the lib and prove me wrong.
Created a first pre-release. Tested a plugin on my mid-size project and it works.
https://github.com/thekip/swc-lingui-plugin/releases/tag/0.0.1
When we agreed on details with @andrii-bodnar i will move the repo under lingui organization and make a proper npm release within @lingui npm scope. As for now, don't want to mess up things and create another trashy package on npm.
@Martin005 why a "breaking change" label appeared here? This plugin doesn't introduce any breaking changes.