☝️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 of getServerSideProps is typed as string | string[], but the NewPageProps interface expects lang to be strictly a string.

  • In Next.js Pages Router, context.params.lang can be a string or string[] (if the dynamic route supports multiple segments, e.g., pages/[...lang].tsx), but your NewPageProps interface and page logic assume lang is always a string.

This is likely caused by:

  1. The dynamic route [lang] being interpreted by TypeScript as potentially returning string | string[] for context.params.lang.

  2. The NewPageProps interface not aligning with the return type of getServerSideProps.

Let’s fix the error by:

  • Ensuring lang is always a string in getServerSideProps.

  • 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[]) using Array.isArray. If it is, take the first element (lang[0]). If not, use the string value or fall back to fallbackLng.

    • This ensures lang is always a string, resolving the string | string[] type mismatch.

  • Type Safety:

    • The NewPageProps interface defines lang: string, which matches the lang prop returned by getServerSideProps.

  • Validation:

    • Validates lang against languages (['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 single lang 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 a string[], 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 to fallbackLng to prevent TypeError: 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

  1. Clear Cache:

    • Run rm -rf .next and restart the server (npm run dev).

  2. Test Navigation:

    • Visit http://localhost:3000/en/new-page:

      • Expect <h1> and <ClientComponent> to show Hello 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 show Xin chào Việt Nam.

  3. 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.

  4. 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, not pages/[...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 a string in getServerSideProps.

  • /en/new-page and /vi/new-page render correctly with ClientComponent.

  • 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?