I'm trying to fetch translation JSONs from an API for my Next app that uses the /app directory. It all works fine on the server-side, since I can await
the API call and pass the data globally as a "hook".
I've managed to do something similar on the client components using a Context, but there is a delay between the loading of the page and loading the translations. There's also no simple way for me to cache the data, like it's cached on the server side.
有没有办法在服务器端(可能在layout.tsx或其他地方)获取这些数据,并使其可供客户端组件全局访问?类似于它以前使用getInitialProps/getServerSideProps的方式.
以下是我的客户环境:
'use client';
import React, { createContext, useCallback, useEffect, useState } from 'react';
import { getActiveBrand } from '../actions/actions';
import { defaultLocale } from './translationProvider';
interface TranslationContextProps {
translateProvider: (
locale?: string
) => (path: string, templates?: { [key: string]: string }) => string;
}
const TranslationContext = createContext<TranslationContextProps>(
{} as TranslationContextProps
); // ts hack :/
interface AuthProviderProps {
children: React.ReactNode | React.ReactNode[];
}
const TranslationProvider = ({ children }: AuthProviderProps) => {
const [translations, setTranslations] = useState(
null as { [key: string]: any } | null
);
const translateProvider = useCallback(
(locale?: string) => {
const lang = locale || defaultLocale;
const translation = translations ? translations[lang] : {};
const translate = (
path: string,
templates?: { [key: string]: string }
) => {
if (translation !== null) {
const keys = path.split('.');
let value: string;
try {
value = keys.reduce((a, c) => a[c], translation);
if (templates && typeof value === 'string')
Object.keys(templates).forEach(key => {
value = value.replace(`{${key}}`, `${templates[key]}`);
});
} catch (e: any) {
return path;
}
return value;
}
return path;
};
return translate;
},
[translations]
);
const getTranslations = async () => {
const brand = await getActiveBrand();
const languages = {} as { [key: string]: any };
brand.data.languages.forEach(lang => {
languages[lang.language] = JSON.parse(JSON.parse(lang.languageData));
});
setTranslations(languages);
};
useEffect(() => {
getTranslations();
}, []);
return (
<TranslationContext.Provider
value={{
translateProvider,
}}
>
{children}
</TranslationContext.Provider>
);
};
export { TranslationProvider, TranslationContext };
我的服务器端"钩子":
import { getActiveBrand } from '../actions/actions';
export const availableLanguages = ['en', 'de', 'cs', 'sk', 'sl', 'hu'];
export const defaultLocale = 'de';
export async function translationProvider(lang: string) {
const brand = await getActiveBrand();
const languageRemote = brand.data.languages.find(
l => l.language === lang
)?.languageData;
const translation = languageRemote
? JSON.parse(JSON.parse(languageRemote))
: null;
const t = (path: string, templates?: { [key: string]: string }): string => {
if (translation !== null) {
const keys = path.split('.');
let value: string;
try {
value = keys.reduce((a, c) => a[c], translation);
if (templates && typeof value === 'string')
Object.keys(templates).forEach(key => {
value = value.replace(`{${key}}`, `${templates[key]}`);
});
} catch (e: any) {
return path;
}
return value;
}
return path;
};
return t;
}