Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -556,6 +556,40 @@ if (isServer) {
export { locale, i18n }
```

### Loading translations asynchronously
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

What is use cases for that? SSR?

Why do we use it in React components?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

It's for rendering async server components which aren't updated on the client side. The translation must be available before server sends the rendered html to client.

async function Component(): Promise<React.ReactNode> {
 // ...
}

It's different from pre-rendered client components which are later hydrated with loaded translation.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Nice explanation, let’s put it to the docs.


When rendering content completely on the server without client hydration, i.e. when using React server components, you can create a `loadTranslations` function. It ensures the translations will be loaded before you use them.

```js
import { createI18n, createLoadTranslations } from '@nanostores/i18n'
import { atom } from 'nanostores'

const locale = atom('en')

const i18n = createI18n(locale, {
get(code, components) {
// your fetching logic
}
})
const loadTranslations = createLoadTranslations(i18n)
```

Then you can get translations by passing i18n component into this function:

```jsx
// components/post.jsx
import { i18n, loadTranslations } from '../stores/i18n.js'

export const messages = i18n('post', {
post: 'Post details'
})

async function Post() {
const t = await loadTranslations(messages)
return <span>{t.post}</span>
}
```

### Preprocessors

You can change all messages in your translation by preprocessors.
Expand Down
3 changes: 2 additions & 1 deletion create-i18n/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,10 @@ export function createI18n(locale, opts) {
}

let t = atom()

if (process.env.NODE_ENV !== 'production') {
t.component = componentName
t.base = base
t.component = componentName
if (define.cache[baseLocale][componentName]) {
let isHMR = import.meta && (import.meta.hot || import.meta.webpackHot)
if (isHMR) {
Expand Down
31 changes: 31 additions & 0 deletions create-load-translations/index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import type {
I18n,
Messages,
TranslationLoader,
Translations
} from '../create-i18n/index.js'
import type { LocaleStore } from '../locale-from/index.js'

export interface LoadTranslations {
<Body extends Translations>(messages: Messages<Body>): Promise<Body>
}

/**
* Create a function to asynchronously load translations from i18n components.
*
* ```js
* import { createI18n, localeFrom, createLoadTranslations } from '@nanostores/i18n'
*
* function get(code, components) {
* // your fetching logic
* }
*
* export const locale = localeFrom(…)
* export const i18n = createI18n(locale, { get })
* export const loadTranslations = createLoadTranslations(i18n)
* ```
*
* @param i18n Component definition function.
* @returns Asynchronous translations loader.
*/
export function createLoadTranslations(i18n: I18n): LoadTranslations
12 changes: 12 additions & 0 deletions create-load-translations/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { translationsLoading } from '../translations-loading/index.js'

export function createLoadTranslations(i18n) {
async function loadTranslations(messages) {
void messages.get()
await translationsLoading(i18n)

return messages.get()
}

return loadTranslations
}
25 changes: 25 additions & 0 deletions create-load-translations/index.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { atom } from 'nanostores'
import { deepStrictEqual, equal } from 'node:assert'
import { test } from 'node:test'

import { createI18n, createLoadTranslations } from '../index.js'

test('waits for translations to load', async () => {
let locale = atom('pl')

let i18n = createI18n(locale, {
get() {
return Promise.resolve({
component: { title: 'Tytuł' }
})
}
})
let loadTranslations = createLoadTranslations(i18n)

equal(i18n.loading.get(), false)

let messages = i18n('component', { title: 'Title' })

deepStrictEqual(await loadTranslations(messages), { title: 'Tytuł' })
equal(i18n.loading.get(), false)
})
4 changes: 4 additions & 0 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ export {
Translations,
TranslationsJSON
} from './create-i18n/index.js'
export {
createLoadTranslations,
LoadTranslations
} from './create-load-translations/index.js'
export { eachMessage } from './each-message/index.js'
export { formatter, Formatter } from './formatter/index.js'
export { localeFrom, LocaleStore } from './locale-from/index.js'
Expand Down
1 change: 1 addition & 0 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
export { browser } from './browser/index.js'
export { count } from './count/index.js'
export { createI18n } from './create-i18n/index.js'
export { createLoadTranslations } from './create-load-translations/index.js'
export { eachMessage } from './each-message/index.js'
export { formatter } from './formatter/index.js'
export { localeFrom } from './locale-from/index.js'
Expand Down
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -65,12 +65,12 @@
{
"name": "Minimum",
"import": "{ localeFrom, createI18n }",
"limit": "651 B"
"limit": "665 B"
},
{
"name": "Maximum",
"import": "{ localeFrom, browser, createI18n, params, count, formatter, createProcessor }",
"limit": "1059 B"
"import": "{ localeFrom, browser, createI18n, params, count, formatter, createProcessor, createLoadTranslations }",
"limit": "1121 B"
}
],
"engines": {
Expand Down