Skip to content

Commit

Permalink
Merge pull request #2922 from opral/lorissigrist/parjs-156-show-good-…
Browse files Browse the repository at this point in the history
…and-bad-example-for-server-side-usage-in-docs

Add good & bad examples for `setLanguageTag`
  • Loading branch information
LorisSigrist committed Jun 17, 2024
2 parents dd5a5bd + 2842d93 commit ab0461d
Show file tree
Hide file tree
Showing 2 changed files with 51 additions and 15 deletions.
1 change: 0 additions & 1 deletion inlang/source-code/paraglide/paraglide-js/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,6 @@ After running the compiler import the messages with `import * as m from "./parag

```js
import * as m from "./paraglide/messages.js"
import { setLanguageTag } from "./paraglide/runtime.js"

m.hello() // Hello world!
m.loginHeader({ name: "Samuel" }) // Hello Samuel, please login to continue.
Expand Down
65 changes: 51 additions & 14 deletions inlang/source-code/paraglide/paraglide-js/docs/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@

## Working with the Inlang Message Format

Paraglide is part of the highly modular Inlang Ecosystem which supports many different Message Formats. By default, the [Inlang Message Format](https://inlang.com/m/reootnfj/plugin-inlang-messageFormat) is used.
Paraglide is part of the highly modular Inlang Ecosystem which supports many different Message Formats. By default, the [Inlang Message Format](https://inlang.com/m/reootnfj/plugin-inlang-messageFormat) is used.

It expects messages to be in `messages/{lang}.json` relative to your repo root.

```json
//messages/en.json
{
//the $schema key is automatically ignored
//the $schema key is automatically ignored
"$schema": "https://inlang.com/schema/inlang-message-format",
"hello_world: "Hello World!",
"greeting": "Hello {name}!"
Expand All @@ -18,7 +18,7 @@ It expects messages to be in `messages/{lang}.json` relative to your repo root.

The `messages/{lang}.json` file contains a flat map of message IDs and their translations. You can use curly braces to insert `{parameters}` into translations

**Nesting purposely isn't supported and likely won't be**. Nested messages are way harder to interact with from complementary tools like the [Sherlock IDE Extension](https://inlang.com/m/r7kp499g/app-inlang-ideExtension), the [Parrot Figma Plugin](https://inlang.com/m/gkrpgoir/app-parrot-figmaPlugin), or the [Fink Localization editor](https://inlang.com/m/tdozzpar/app-inlang-finkLocalizationEditor). Intellisense also becomes less helpful since it only shows the messages at the current level, not all messages. Additionally enforcing an organization-style side-steps organization discussions with other contributors.
**Nesting purposely isn't supported and likely won't be**. Nested messages are way harder to interact with from complementary tools like the [Sherlock IDE Extension](https://inlang.com/m/r7kp499g/app-inlang-ideExtension), the [Parrot Figma Plugin](https://inlang.com/m/gkrpgoir/app-parrot-figmaPlugin), or the [Fink Localization editor](https://inlang.com/m/tdozzpar/app-inlang-finkLocalizationEditor). Intellisense also becomes less helpful since it only shows the messages at the current level, not all messages. Additionally enforcing an organization-style side-steps organization discussions with other contributors.

## Using messages in Code

Expand Down Expand Up @@ -49,28 +49,29 @@ For date & currency formatting use the `.toLocaleString` method on the `Date` or
import * as m from "./paraglide/messages.js"
import { languageTag } from "./paraglide/runtime.js"

const todaysDate = new Date();
m.today_is_the({
date: todaysDate.toLocaleString(languageTag())
const todaysDate = new Date()
m.today_is_the({
date: todaysDate.toLocaleString(languageTag()),
})

const price = 100;
const price = 100
m.the_price_is({
price: price.toLocaleString(languageTag(), {
style: "currency",
currency: "EUR",
})
}),
})
```

You can put HTML into the messages. This is useful for links and images.
You can put HTML into the messages. This is useful for links and images.

```json
// messages/en.json
{
"you_must_agree_to_the_tos": "You must agree to the <a href='/en/tos'>Terms of Service</a>."
}
```

```json
// messages/de.json
{
Expand All @@ -80,7 +81,6 @@ You can put HTML into the messages. This is useful for links and images.

There is currently no way to interpolate framework components into messages. If you require components mid-message you will need to create a one-off component for that bit of text.


## Getting a message in a specific language

You can import a message in a specific language from `paraglide/messages/{lang}.js`.
Expand All @@ -99,16 +99,15 @@ const msg = m.hello({ name: "Samuel" }, { languageTag: "de" }) // Hallo Samuel!

### Lazy-Loading

Paraglide discourages lazy-loading translations since it causes a render-fetch waterfall which hurts Web Vitals. Learn more about why lazy-loading is bad & what to do instead in [our blog post on lazy-loading](https://inlang.com/g/mqlyfa7l/guide-lorissigrist-dontlazyload).
Paraglide discourages lazy-loading translations since it causes a render-fetch waterfall which hurts Web Vitals. Learn more about why lazy-loading is bad & what to do instead in [our blog post on lazy-loading](https://inlang.com/g/mqlyfa7l/guide-lorissigrist-dontlazyload).

If you want to do it anyway, lazily import the language-specific message files.
If you want to do it anyway, lazily import the language-specific message files.

```ts
const lazyGerman = await import("./paraglide/messages/de.js")
lazyGerman.hello() // Hallo Welt
```


## Language Management

### Setting the language
Expand All @@ -130,6 +129,44 @@ m.hello() // Hello world!

The [language tag](https://www.inlang.com/m/8y8sxj09/library-inlang-languageTag) is a global function. This means that on the server it is _shared_ accross requests. In order to avoid the langauge from one request being overwritten by another request you need to use _getter function_ that returns the language for the _current request_. A good way to implement this is using [`AsyncLocalStorage`](https://nodejs.org/api/async_context.html).

**⛔️ Bad Example**:

```ts
import { setLanguageTag, sourceLanguageTag } from "./paraglide/runtime.js"

export function onRequest(request, next) {
const langForReq = detectLanguage(request)

// ⛔️ DONT DO THIS
// ⛔️ If multiple requests are handled concurretntly
// ⛔️ later ones will override the language for earlier ones
setLanguageTag(langForReq)

return langStorage(langForReq, async () => await next())
}
```

**✅ Good Example**:

```ts
import { setLanguageTag, sourceLanguageTag } from "./paraglide/runtime.js"
import { AsyncLocalStorage } from "node:async_hooks"

const langStorage = new AsyncLocalStorage()

// ✅ DO THIS
// ✅ when `languageTag` is called inside a route handler
// ✅ this function will return the language for the current request
setLanguageTag(() => {
return langStorage.getValue() ?? sourceLanguageTag
})

export function onRequest(request, next) {
const langForReq = detectLanguage(request)
return langStorage(langForReq, async () => await next())
}
```

If you are using a Framework library like `Paraglide-Next` or `Paraglide-SvelteKit` you do not need to call `setLanguageTag` manually.

### Reacting to language changes
Expand Down Expand Up @@ -205,4 +242,4 @@ If you are using a bundler you should use the corresponding plugin. The plugin w
<doc-link title="Vite Plugin" icon="tabler:brand-vite" href="https://github.com/opral/monorepo/tree/main/inlang/source-code/paraglide/paraglide-vite" description="Go to Github"></doc-link>
<doc-link title="Rollup Plugin" icon="file-icons:rollup" href="https://github.com/opral/monorepo/tree/main/inlang/source-code/paraglide/paraglide-rollup" description="Go to Github"></doc-link>
<doc-link title="Webpack Plugin" icon="mdi:webpack" href="https://github.com/opral/monorepo/tree/main/inlang/source-code/paraglide/paraglide-webpack" description="Go to Github"></doc-link>
</doc-links>
</doc-links>

0 comments on commit ab0461d

Please sign in to comment.