Skip to content

Commit

Permalink
feat: improve next.js experience (#3335)
Browse files Browse the repository at this point in the history
  • Loading branch information
stepan662 committed Apr 29, 2024
1 parent 5286551 commit 8f61da5
Show file tree
Hide file tree
Showing 7 changed files with 46 additions and 12 deletions.
1 change: 1 addition & 0 deletions packages/core/src/Controller/Controller.ts
Expand Up @@ -208,6 +208,7 @@ export function Controller({ options }: StateServiceProps) {
getTranslationNs: getTranslationNs,
getDefaultAndFallbackNs: getDefaultAndFallbackNs,
findPositions: pluginService.findPositions,
getRequiredRecords: getRequiredRecords,
async changeLanguage(language: string) {
if (
state.getPendingLanguage() === language &&
Expand Down
5 changes: 5 additions & 0 deletions packages/core/src/TolgeeCore.ts
Expand Up @@ -127,6 +127,11 @@ function createTolgee(options: TolgeeOptions) {
*/
isLoaded: controller.isLoaded,

/**
* Returns records needed for instance to be `loaded`
*/
getRequiredRecords: controller.getRequiredRecords,

/**
* @return `true` if tolgee is loading initial data (triggered by `run`).
*/
Expand Down
2 changes: 1 addition & 1 deletion packages/react/src/createServerInstance.tsx
Expand Up @@ -20,7 +20,7 @@ export const createServerInstance = ({
const tolgee = await createTolgee(locale);
await tolgee.run();
return tolgee;
});
}) as (locale: string) => Promise<TolgeeInstance>;

const getTolgee = async () => {
const locale = getLocale();
Expand Down
40 changes: 29 additions & 11 deletions packages/react/src/useTolgeeSSR.ts
Expand Up @@ -34,26 +34,44 @@ export function useTolgeeSSR(
language?: string,
staticData?: TolgeeStaticData | undefined
) {
const initialInstance = useMemo(
() => getTolgeeWithDeactivatedWrapper(tolgeeInstance),
[]
const [noWrappingTolgee] = useState(() =>
getTolgeeWithDeactivatedWrapper(tolgeeInstance)
);

const [tolgee, setTolgee] = useState(initialInstance);
const [initialRender, setInitialRender] = useState(true);

useEffect(() => {
setTolgee(tolgeeInstance);
setInitialRender(false);
}, []);

useMemo(() => {
// we have to prepare tolgee before rendering children
// so translations are available right away
// events emitting must be off, to not trigger re-render while rendering
tolgee.setEmitterActive(false);
tolgee.addStaticData(staticData);
tolgee.changeLanguage(language!);
tolgee.setEmitterActive(true);
}, [language, staticData, tolgee]);
tolgeeInstance.setEmitterActive(false);
tolgeeInstance.addStaticData(staticData);
tolgeeInstance.changeLanguage(language!);
tolgeeInstance.setEmitterActive(true);
}, [language, staticData, tolgeeInstance]);

return tolgee;
useState(() => {
// running this function only on first render
if (!tolgeeInstance.isLoaded()) {
// warning user, that static data provided are not sufficient
// for proper SSR render
const missingRecords = tolgeeInstance
.getRequiredRecords(language)
.map(({ namespace, language }) =>
namespace ? `${namespace}:${language}` : language
)
.filter((key) => !staticData?.[key]);

// eslint-disable-next-line no-console
console.warn(
`Tolgee: Missing records in "staticData" for proper SSR functionality: ${missingRecords.map((key) => `"${key}"`).join(', ')}`
);
}
});

return initialRender ? noWrappingTolgee : tolgeeInstance;
}
Expand Up @@ -82,6 +82,7 @@ export function ElementRegistry(
}

return Object.freeze({
isRestricted: isRestricted,
register(element: Element, node: Node, nodeMeta: NodeMeta) {
if (isRestricted(element)) {
return;
Expand Down
7 changes: 7 additions & 0 deletions packages/web/src/package/observers/general/GeneralObserver.ts
Expand Up @@ -54,7 +54,14 @@ export function GeneralObserver() {

function handleNodes(nodes: Array<Text | Attr>) {
for (const textNode of nodes) {
const parent = textNode.parentElement;

if (parent && elementRegistry.isRestricted(parent)) {
continue;
}

const oldTextContent = getNodeText(textNode);

const result = oldTextContent ? wrapper.unwrap(oldTextContent) : null;
if (result) {
const { text, keys } = result;
Expand Down
2 changes: 2 additions & 0 deletions testapps/next-app/src/app/[locale]/layout.tsx
Expand Up @@ -16,6 +16,8 @@ export default async function LocaleLayout({
notFound();
}

// it's important you provide all data which are needed for initial render
// so current locale and also fallback locales + necessary namespaces
const locales = await getStaticData(['en', locale]);

return (
Expand Down

0 comments on commit 8f61da5

Please sign in to comment.