☝️Toàn bộ giải thích sử dụng chatgpt Triển khai client, server (part 3)
The TypeScript error you're encountering indicates a mismatch between the expected type for getServerSideProps
and the actual type defined in your pages/[lang]/new-page.tsx
. Specifically:
The error occurs because the
lang
prop in the return value ofgetServerSideProps
is typed asstring | string[]
, but theNewPageProps
interface expectslang
to be strictly astring
.In Next.js Pages Router,
context.params.lang
can be astring
orstring[]
(if the dynamic route supports multiple segments, e.g.,pages/[...lang].tsx
), but yourNewPageProps
interface and page logic assumelang
is always astring
.
This is likely caused by:
The dynamic route
[lang]
being interpreted by TypeScript as potentially returningstring | string[]
forcontext.params.lang
.The
NewPageProps
interface not aligning with the return type ofgetServerSideProps
.
Let’s fix the error by:
Ensuring
lang
is always astring
ingetServerSideProps
.Updating the
NewPageProps
interface to match the props returned.Maintaining compatibility with your i18n setup and
ClientComponent
.
Solution
1. Fix pages/[lang]/new-page.tsx
Update pages/[lang]/new-page.tsx
to explicitly handle context.params.lang
as a string
and ensure the NewPageProps
interface matches the props returned by getServerSideProps
.
import ClientComponent from '@/src/components/ClientComponent';
import LanguageSwitcher from '@/src/components/LanguageSwitcher';
import initI18next from '@/src/lib/i18n/server';
import { fallbackLng, languages } from '@/src/lib/i18n/settings';
import { GetServerSideProps } from 'next';
interface NewPageProps {
lang: string;
serverTranslation: string;
}
export default function NewPage({ lang, serverTranslation }: NewPageProps) {
console.log('NewPage Server: lang=', lang, 'serverTranslation=', serverTranslation);
return (
<>
<header className="flex justify-end p-4">
<LanguageSwitcher />
</header>
<ClientComponent lang={lang} />
<h1>{serverTranslation}</h1>
</>
);
}
export const getServerSideProps: GetServerSideProps<NewPageProps> = async (context) => {
// Ensure lang is a string
const langParam = Array.isArray(context.params?.lang)
? context.params.lang[0] // Take first element if array
: context.params?.lang || fallbackLng;
const lang = languages.includes(langParam) ? langParam : fallbackLng;
const i18n = await initI18next(lang, 'common');
const t = i18n.getFixedT(lang, 'common');
const serverTranslation = t('hello');
console.log('NewPage getServerSideProps: lang=', lang, 't(hello)=', serverTranslation);
return {
props: {
lang,
serverTranslation,
},
};
};
Changes:
Handle
langParam
:Check if
context.params?.lang
is an array (string[]
) usingArray.isArray
. If it is, take the first element (lang[0]
). If not, use the string value or fall back tofallbackLng
.This ensures
lang
is always astring
, resolving thestring | string[]
type mismatch.
Type Safety:
The
NewPageProps
interface defineslang: string
, which matches thelang
prop returned bygetServerSideProps
.
Validation:
Validates
lang
againstlanguages
(['en', 'vi']
) to ensure only supported languages are used.
Logging:
Kept logging to debug
lang
and translations.
2. Verify Route Structure
Ensure the file is correctly placed in pages/[lang]/new-page.tsx
(not pages/[...lang]/new-page.tsx
):
pages/[lang]/new-page.tsx
expects a singlelang
segment (e.g.,/en/new-page
,/vi/new-page
).If you accidentally used
pages/[...lang]/new-page.tsx
(catch-all route),context.params.lang
would be astring[]
, causing the type error. Rename the folder to[lang]
if needed.
Check:
Confirm the file path:
pages/[lang]/new-page.tsx
.Run
ls pages
to verify the directory structure.
3. Verify ClientComponent
Ensure src/components/ClientComponent.tsx
handles lang
correctly and is synchronous:
'use client';
import { useEffect, useState } from 'react';
import i18n from '@/src/lib/i18n/client';
import { useTranslation } from 'react-i18next';
import { fallbackLng } from '@/src/lib/i18n/settings';
export default function ClientComponent({ lang = fallbackLng }: { lang?: string }) {
const { t } = useTranslation('common');
const [isLanguageReady, setIsLanguageReady] = useState(i18n.language === lang);
useEffect(() => {
console.log('ClientComponent: lang=', lang, 'i18n.language=', i18n.language);
if (i18n.language !== lang) {
i18n.services.languageDetector?.cache?.clear?.();
i18n.changeLanguage(lang, (err) => {
if (err) console.error(`Failed to change language to ${lang}`, err);
i18n.loadNamespaces('common', (err) => {
if (err) console.error(`Failed to load namespace 'common' for ${lang}`, err);
setIsLanguageReady(true);
});
});
} else {
setIsLanguageReady(true);
}
}, [lang]);
console.log('ClientComponent rendering: t(hello)=', t('hello'));
return isLanguageReady ? <p>{t('hello')}</p> : null;
}
Checks:
Synchronous: Not
async
, avoiding the async Client Component error.Fallback
lang
: Defaults tofallbackLng
to preventTypeError: Cannot read properties of undefined (reading 'lang')
.Language Sync: Ensures translations are correct.
4. Verify client.ts
Ensure src/lib/i18n/client.ts
is unchanged and supports client-side translations:
'use client';
import i18n from 'i18next';
import { initReactI18next } from 'react-i18next';
import LanguageDetector from 'i18next-browser-languagedetector';
import resourcesToBackend from 'i18next-resources-to-backend';
import { getOptions } from './settings';
if (!i18n.isInitialized) {
i18n
.use(initReactI18next)
.use(LanguageDetector)
.use(
resourcesToBackend(async (lang: string, namespace: string) => {
try {
console.log(`Loading translation: ${lang}/${namespace}`);
const module = await import(`@/src/locales/${lang}/${namespace}.json`);
return module.default;
} catch (error) {
console.error(`Failed to load translation for ${lang}/${namespace}`, error);
return {};
}
})
)
.init({
...getOptions(),
detection: {
order: ['path'],
caches: [],
lookupFromPathIndex: 0,
},
ns: ['common'],
defaultNS: 'common',
});
i18n.on('languageChanged', (lng) => {
console.log('Language changed to:', lng, 'Resources:', i18n.services.resourceStore.data);
});
}
export default i18n;
5. Verify LanguageSwitcher
Ensure src/components/LanguageSwitcher.tsx
works in the Pages Router:
'use client';
import { usePathname, useRouter } from 'next/navigation';
import { useTransition } from 'react';
const locales = ['en', 'vi'];
export default function LanguageSwitcher() {
const router = useRouter();
const pathname = usePathname();
const [isPending, startTransition] = useTransition();
const currentLang = pathname ? pathname.split('/')[1] : 'en';
const otherLang = locales.find((lng) => lng !== currentLang) || 'en';
const switchLang = () => {
if (!pathname) return;
const segments = pathname.split('/');
segments[1] = otherLang;
const newPath = segments.join('/');
console.log('LanguageSwitcher: Switching to:', newPath);
startTransition(() => {
router.push(newPath, { scroll: false });
});
};
return (
<button
onClick={switchLang}
disabled={isPending}
className="px-4 py-2 rounded border bg-gray-100 hover:bg-gray-200 text-sm"
>
{otherLang.toUpperCase()}
</button>
);
}
6. Handle Turbopack HMR
If Turbopack HMR issues persist (from your previous errors), disable Turbopack:
Update next.config.js
:
/** @type {import('next').NextConfig} */
const nextConfig = {
experimental: {
turbo: {
enabled: false,
},
},
};
export default nextConfig;
Alternative: Preload translations in src/lib/i18n/client.ts
(as shown previously) to avoid dynamic import issues.
7. Test the Page
Clear Cache:
Run
rm -rf .next
and restart the server (npm run dev
).
Test Navigation:
Visit
http://localhost:3000/en/new-page
:Expect
<h1>
and<ClientComponent>
to showHello Eng
.Check logs:
NewPage getServerSideProps: lang= en t(hello)= Hello Eng NewPage Server: lang= en serverTranslation= Hello Eng ClientComponent: lang= en i18n.language= en ClientComponent rendering: t(hello)= Hello Eng
Visit
http://localhost:3000/vi/new-page
:Expect
<h1>
and<ClientComponent>
to showXin chào Việt Nam
.
Test Language Switching:
Start at
/en/new-page
, switch to/vi/new-page
, then back to/en/new-page
.Verify no TypeScript errors or runtime issues.
Test Production Build:
Run
npm run build && npm run start
.
8. Debugging Tips
If the TypeScript error persists or other issues arise:
Check Route Name:
Ensure the file is
pages/[lang]/new-page.tsx
, notpages/[...lang]/new-page.tsx
.
Log
context.params
:Add logging in
getServerSideProps
:console.log('NewPage getServerSideProps: context.params=', context.params);
Test Static
lang
:Temporarily hardcode
lang
:export default function NewPage() { return ( <> <LanguageSwitcher /> <ClientComponent lang="vi" /> </> ); }
Check TypeScript Config:
Ensure
tsconfig.json
is configured for Next.js:{ "compilerOptions": { "target": "es5", "lib": ["dom", "dom.iterable", "esnext"], "allowJs": true, "skipLibCheck": true, "strict": true, "forceConsistentCasingInFileNames": true, "noEmit": true, "esModuleInterop": true, "module": "esnext", "moduleResolution": "node", "resolveJsonModule": true, "isolatedModules": true, "jsx": "preserve", "incremental": true }, "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"], "exclude": ["node_modules"] }
Disable Turbopack:
If HMR issues occur, disable Turbopack as shown above.
9. Expected Outcome
The TypeScript error is resolved by ensuring
lang
is astring
ingetServerSideProps
./en/new-page
and/vi/new-page
render correctly withClientComponent
.Language switching works without errors.
10. If Issues Persist
Please provide:
Full console logs and error stack trace.
Confirmation that the file is
pages/[lang]/new-page.tsx
.The exact URL where the issue occurs (e.g.,
/en/new-page
).The Next.js version (
next --version
).Contents of
tsconfig.json
if TypeScript issues persist.
Let me know how it goes or if you need further clarification!
Last updated
Was this helpful?