Research
Security News
Quasar RAT Disguised as an npm Package for Detecting Vulnerabilities in Ethereum Smart Contracts
Socket researchers uncover a malicious npm package posing as a tool for detecting vulnerabilities in Etherium smart contracts.
@patreon/stele
Advanced tools
Stele is a suite of tools for building international applications in modern single page apps. The name comes from the Rosetta Stone, which was a Stele, meaning a giant stone (or wooden) monument, often with some sort of decree. Really, it was the coolest name we could think of that was not taken on NPM.
Currently our only dependency to use Stele is babel. If you are not currently using babel for your build, you can get started here
First install Stele:
$ npm i -D @patreon/stele
Then add it as a plugin to your babel config:
const { plugin } = require('@patreon/stele')
module.exports = {
presets: ['@babel/preset-react'],
plugins: [[plugin, options]],
}
The ecosystem in javascript for internationalization is quite daunting to first look into. At Patreon we had a few main goals for starting our internationalization journey.
Our goal is not to create a new standard to replace all standards in the JS ecosystem. We wanted to solve the problems we were facing at Patreon and share our solution. If you are facing a different set of problems this may not be the right solution for you or your product.
This library is heavily inspired by elm-i18n and i18n-webpack-plugin. This library co-evolved with js-lingui with similar ideas. When babel starts, so does Stele:
[defaultLanguage]-[defaultLocale].json
to your webpack build (coming soon!)[currentLanguage]-[currentLocale].json
Most of your strings will likely exist through React components. Stele makes transitioning these components easy.
Given:
<Text>
{props.food} is just a {props.kind} calzone
</Text>
Let's say that you have a Text
component for rendering
out user facing copy on your website. We need a way to tell the compiler
that this text should be extracted to a JSON file and not just normal layout text.
The way we will do this in react is through a prop on your component, let's call it intl
. In order to get the above string translated in Stele the migration is simple:
<Text intl>
{props.food} is just a {props.kind} calzone
</Text>
This produces the JSON file:
{
"{food} is just a {kind} calzone": "{food} is just a {kind} calzone"
}
Let's say you have a string that has some italic text in it
<Text>
Tom considers himself a <Text italic>foodie</Text>
</Text>
We want this entire string to be translatable, rather than translating "Tom considers himself a" and "foodie" stele instead extracts this as a single string once it is marked for translation:
<Text intl>Tom considers himself a <Text italic>foodie</Text></Text>
// creates json:
{"Tom considers himself a <1>foodie</1>": "Tom considers himself a <1>foodie</1>"}
Stele keeps an internal order for HTML tags when compiling back to the language it cares about. This way in case the order changes, stele can put the right JSX in the correct spots.
Stele tries to support all ICU formats like select
, number
, date
and plural
.
Using these in JSX is done through React components maintained by stele.
<Text>
Wait, now we're on an island? With
{ props.kidCount === 1 ? 'a kid' : 'kids') }
</Text>
Pluralizing this string in Stele takes a little bit more work than our previous examples. The big problem here is that while english only has 2 ways to pluralize something (One or Other), many languages have multiple. To handle this we simply need to use the plural component that Stele provides:
<Text intl intl-description="Phrase with plural check on main page">
Wait, now we're on an island? With
<Plural value={kidCount} one="a kid" other="# kids" />?
</Text>
This produces the ICU message we want to send our translators:
Wait, now we're on an island. With {kidCount, plural, one {a kid} other {# kids}}?
The translators will send back a string in a new language that might have different kinds of plurals, for instance in russian the string sent back might look like:
Подождите, теперь мы на острове. {kidCount, plural, {
=0 {Без детей}
one {C одним ребенком}
few {C # детьми}
many {C # детьми}
}}
When stele is compiling the russian version of the site, we compile that in to JSX for you that looks like:
<Text>
Подождите, теперь мы на острове?
<Plural
value={kidCount}
zero="Без детей"
ones="C одним ребенком"
few="C # детьми"
many="C # детьми"
/>?
</Text>
Let's say you have an input on your page with a placeholder:
<label>What if I get drunk and I talk about Darfur too much?</label>
<input placeholder="Have a practice date" />
Internationalizing the label is easy, however internationalizing the placeholder is just as easy:
<label>
<Text intl>What if I get drunk and I talk about Darfur too much?</Text>
</label>
<input placeholder={__('Have a practice date')} />
Sometimes you might need to include variables in your strings.
const whichWife = `No, my other ex-wife Tammy - Tammy ${whichTammy}.`
Internationalizing this follows standard ICU string rules:
const whichWife = __('No, my other ex-wife Tammy - Tammy {whichTammy}', {
whichTammy: whichTammy,
})
These strings are extracted directly with no manipulations, unlike JSX strings. We call this function the dunder
function as a portmanteau of "double" and "underscore".
While this appears to be a silver bullet, there are few common paradigms in JSX that will not work in Stele. Namely that no run time specific code can be put in our Text
as children and also
that strings are not computed at compile time, like they might in more idiomatic React code.
Strings in JSX must be wholly comprised of Stele components or string literals. Outside of JSX the entire string you want to display to a user must be a string literal.
Text
componentLet's say we had a string that computes a different string based on a variable:
<Text intl>But babe, {isBenWyatt ? 'calzone' : 'pizza'}</Text>
would only produce the message:
"But babe, "
because the above code relies on runtime behavior to create a string. All strings in Stele must be complete sentences, without logic.
Instead you could encompass both strings in their entirety inside of the ternary:
{
isBenWyatt ? (
<Text intl>But babe, calzone</Text>
) : (
<Text intl>But babe, pizza</Text>
)
}
or, if you'd like, you can even use the dunder function:
<Text>{isBenWyatt ? __('But babe, calzone') : __('But babe, pizza')}</Text>
Which is the valid run time version of the previous string.
FAQs
A compile time internationalization library for javascript and webpack
We found that @patreon/stele demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 8 open source maintainers collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Research
Security News
Socket researchers uncover a malicious npm package posing as a tool for detecting vulnerabilities in Etherium smart contracts.
Security News
Research
A supply chain attack on Rspack's npm packages injected cryptomining malware, potentially impacting thousands of developers.
Research
Security News
Socket researchers discovered a malware campaign on npm delivering the Skuld infostealer via typosquatted packages, exposing sensitive data.