Skip to content

Commit

Permalink
feat(react): native support react RSC (#1914)
Browse files Browse the repository at this point in the history
  • Loading branch information
timofei-iatsenko authored Apr 12, 2024
1 parent d185981 commit 0e94f2a
Show file tree
Hide file tree
Showing 5 changed files with 99 additions and 4 deletions.
6 changes: 3 additions & 3 deletions packages/core/src/i18n.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ export type MissingMessageEvent = {

type MissingHandler = string | ((locale: string, id: string) => string)

type setupI18nProps = {
export type I18nProps = {
locale?: Locale
locales?: Locales
messages?: AllMessages
Expand Down Expand Up @@ -86,7 +86,7 @@ export class I18n extends EventEmitter<Events> {
private _messages: AllMessages = {}
private _missing?: MissingHandler

constructor(params: setupI18nProps) {
constructor(params: I18nProps) {
super()

if (params.missing != null) this._missing = params.missing
Expand Down Expand Up @@ -266,7 +266,7 @@ export class I18n extends EventEmitter<Events> {
}
}

function setupI18n(params: setupI18nProps = {}): I18n {
function setupI18n(params: I18nProps = {}): I18n {
return new I18n(params)
}

Expand Down
2 changes: 2 additions & 0 deletions packages/react/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,12 @@
".": {
"require": {
"types": "./dist/index.d.cts",
"react-server": "./dist/index-rsc.cjs",
"default": "./dist/index.cjs"
},
"import": {
"types": "./dist/index.d.mts",
"react-server": "./dist/index-rsc.mjs",
"default": "./dist/index.mjs"
}
},
Expand Down
15 changes: 15 additions & 0 deletions packages/react/src/TransRsc.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { TransProps, TransNoContext } from "./TransNoContext"
import React from "react"
import { getI18n } from "./server"

export function TransRsc(
props: TransProps
): React.ReactElement<any, any> | null {
const i18n = getI18n()
if (!i18n) {
throw new Error(
"You tried to use `Trans` in Server Component, but i18n instance for RSC hasn't been setup.\nMake sure to call `setI18n` in root of your page"
)
}
return <TransNoContext {...props} lingui={{ i18n }} />
}
24 changes: 24 additions & 0 deletions packages/react/src/index-rsc.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
export type {
TransProps,
TransRenderProps,
TransRenderCallbackOrComponent,
} from "./TransNoContext"

import type { I18nContext } from "./I18nProvider"
import { getI18n } from "./server"

export { TransRsc as Trans } from "./TransRsc"

export function useLingui(): I18nContext {
const i18n = getI18n()
if (!i18n) {
throw new Error(
"You tried to use `useLingui` in Server Component, but i18n instance for RSC hasn't been setup.\nMake sure to call `setI18n` in root of your page"
)
}

return {
i18n,
_: i18n.t.bind(i18n),
}
}
56 changes: 55 additions & 1 deletion packages/react/src/server.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,67 @@
/// <reference types="react/canary" />
/**
* This is an entry point for React Server Components (RSC)
*
* The RSC uses a static analysis to find any non-valid function calls in the import graph.
* That means this entry point and it's children should not have any Provider/Context calls.
*/
export { TransNoContext } from "./TransNoContext"

export type {
TransProps,
TransRenderProps,
TransRenderCallbackOrComponent,
} from "./TransNoContext"

import type { I18n } from "@lingui/core"
import React from "react"

let cache: (() => { current: I18n | null }) | null = null

const getLinguiCache = () => {
// make lazy initialization of React.cache
// so it will not execute when module just imported
if (!cache) {
cache = React.cache((): { current: I18n | null } => ({
current: null,
}))
}

return cache()
}

/**
* Set Lingui's i18n instance for later use in RSC Components
*
* Example:
*
* ```js
* import { setupI18n } from "@lingui/core";
*
* const i18n = setupI18n({
* locale,
* messages: { [locale]: messages },
* })
*
* setI18n(i18n);
* ```
*/
export function setI18n(i18n: I18n) {
getLinguiCache().current = i18n
}

/**
* Get Lingui's i18n instance saved for RSC
*
* ```js
* export function generateMetadata() {
* const i18n = getI18n()
*
* return {
* title: t(i18n)`Translation Demo`,
* }
* }
* ```
*/
export function getI18n(): I18n | null {
return getLinguiCache().current
}

0 comments on commit 0e94f2a

Please sign in to comment.