كيفية التحقق من صحة معاملات اللغة في عناوين URL في React Router v7

التعامل مع رموز اللغة غير المدعومة بشكل سلس

المشكلة

عندما تصبح معرّفات اللغة جزءًا من بنية عنوان URL، فإنها تتحول إلى مدخلات مستخدم يمكن أن تحتوي على أي قيمة عشوائية. قد يكتب المستخدم يدويًا /xx/about أو /gibberish/contact أو أي رمز لغة غير صالح آخر في شريط العنوان. بدون التحقق من الصحة، يجب على التطبيق أن يقرر كيفية التعامل مع هذه المدخلات غير الصالحة. السماح للغات غير الصالحة بالمتابعة يمكن أن يؤدي إلى ترجمات مفقودة أو تنسيق معطل أو أخطاء وقت التشغيل عندما تحاول مكتبات i18n تحميل بيانات لغة غير موجودة. التراجع الصامت إلى لغة افتراضية دون إبلاغ المستخدم يخلق ارتباكًا حول اللغة التي يشاهدونها. عرض حدود الأخطاء أو صفحات فارغة يترك المستخدمين عالقين بدون مسار واضح للمضي قدمًا.

يتفاقم التحدي بسبب حقيقة أن التحقق من صحة اللغة يجب أن يحدث في وقت مبكر من دورة حياة الطلب، قبل عرض المكونات وقبل تحميل بيانات الترجمة. إذا حدث التحقق متأخرًا جدًا، فقد يكون التطبيق قد حاول بالفعل جلب موارد خاصة باللغة أو قام بتهيئة موفري i18n بتكوين غير صالح، مما يهدر الموارد ويحتمل أن يتسبب في فشل متتالي.

الحل

تحقق من صحة معامل اللغة في محمّل المسار قبل عرض الصفحة. تعمل المحمّلات في React Router قبل تثبيت المكونات، مما يجعلها المكان المثالي للتحقق مما إذا كانت اللغة المطلوبة موجودة في قائمة اللغات المدعومة في تطبيقك. إذا كانت اللغة صالحة، اسمح للطلب بالمتابعة بشكل طبيعي. إذا كانت اللغة غير صالحة، أعد توجيه المستخدم فورًا إلى بديل آمن—إما نفس المسار بلغة افتراضية صالحة، أو صفحة مخصصة غير موجودة توضح المشكلة.

يمنع هذا النهج وصول اللغات غير الصالحة إلى مكوناتك ومزودي i18n. من خلال إرجاع استجابة إعادة توجيه من المحمل، فإنك تستفيد من نظام التنقل المدمج في React Router للتعامل مع الخطأ بشكل سلس. تحدث إعادة التوجيه على الخادم أثناء SSR أو على العميل أثناء التنقل، مما يضمن سلوكاً متسقاً عبر استراتيجيات العرض. يتلقى المستخدمون ملاحظات فورية من خلال تغيير عنوان URL، ويتجنب تطبيقك محاولة تحميل الموارد للغات غير موجودة.

الخطوات

1. حدد اللغات المدعومة

أنشئ قائمة برموز اللغات الصالحة التي يدعمها تطبيقك. تعمل هذه القائمة كمصدر الحقيقة للتحقق من الصحة.

export const SUPPORTED_LOCALES = ["en", "es", "fr", "de", "ja"] as const;

export type SupportedLocale = (typeof SUPPORTED_LOCALES)[number];

export function isValidLocale(locale: string): locale is SupportedLocale {
  return SUPPORTED_LOCALES.includes(locale as SupportedLocale);
}

توفر هذه الدالة المساعدة التحقق الآمن من النوع ويمكن إعادة استخدامها عبر المحملات وأجزاء أخرى من تطبيقك.

2. أضف التحقق إلى محمل المسار المسبوق باللغة

في وحدة المسار لصفحاتك المسبوقة باللغة، قم بتصدير محمل يتحقق من معامل اللغة ويعيد التوجيه إذا كان غير صالح.

import type { Route } from "./+types/page";
import { redirect } from "react-router";
import { isValidLocale } from "~/i18n/locales";

export async function loader({ params }: Route.LoaderArgs) {
  const { locale } = params;

  if (!locale || !isValidLocale(locale)) {
    return redirect("/en/not-found");
  }

  return { locale };
}

export default function Page({ loaderData }: Route.ComponentProps) {
  return (
    <div>
      <h1>Content in {loaderData.locale}</h1>
    </div>
  );
}

يستخرج المحمل اللغة من عنوان URL، ويتحقق من صحتها، ويعيد التوجيه إلى بديل آمن في حالة فشل التحقق. إذا كانت اللغة صالحة، فإنه يرجع بيانات يمكن للمكونات استخدامها.

3. قم بتكوين مساراتك بمعاملات اللغة

في ملف routes.ts الخاص بك، حدد المسارات التي تتضمن اللغة كجزء ديناميكي.

import { type RouteConfig, route } from "@react-router/dev/routes";

export default [
  route(":locale/about", "./routes/about.tsx"),
  route(":locale/contact", "./routes/contact.tsx"),
  route(":locale/not-found", "./routes/not-found.tsx"),
] satisfies RouteConfig;

سيستدعي كل مسار يحتوي على معامل :locale المحمل الخاص به، حيث يحدث التحقق قبل عرض المكون.

4. أنشئ صفحة غير موجودة للغات غير الصالحة

أنشئ صفحة مخصصة توضح أن اللغة غير موجودة وتقدم خيارات التنقل.

import { Link } from "react-router";
import { SUPPORTED_LOCALES } from "~/i18n/locales";

export default function NotFound() {
  return (
    <div>
      <h1>Language Not Found</h1>
      <p>The requested language is not supported.</p>
      <nav>
        <p>Choose a language:</p>
        <ul>
          {SUPPORTED_LOCALES.map((locale) => (
            <li key={locale}>
              <Link to={`/${locale}`}>{locale.toUpperCase()}</Link>
            </li>
          ))}
        </ul>
      </nav>
    </div>
  );
}

توفر هذه الصفحة ملاحظات واضحة وخطوات تالية قابلة للتنفيذ، مما يساعد المستخدمين على التعافي من الخطأ دون مغادرة تطبيقك.

5. إضافة مسار شامل للمسارات غير الصالحة تمامًا

بالنسبة لعناوين URL التي لا تطابق أي نمط مسار محدد، أضف مسار splat في نهاية تكوين المسار الخاص بك.

import { type RouteConfig, route } from "@react-router/dev/routes";

export default [
  route(":locale/about", "./routes/about.tsx"),
  route(":locale/contact", "./routes/contact.tsx"),
  route(":locale/not-found", "./routes/not-found.tsx"),
  route("*", "./routes/catch-all.tsx"),
] satisfies RouteConfig;

يطابق مسار splat أي مسار لا يطابق المسارات السابقة، مما يسمح لك بمعالجة عناوين URL المشوهة تمامًا بشكل منفصل عن رموز اللغة غير الصالحة.

6. إعادة التوجيه اختياريًا إلى لغة افتراضية بدلاً من صفحة غير موجودة

إذا كنت تفضل تصحيح اللغات غير الصالحة بصمت بدلاً من عرض خطأ، فقم بإعادة التوجيه إلى نفس المسار مع لغة افتراضية.

import type { Route } from "./+types/page";
import { redirect } from "react-router";
import { isValidLocale } from "~/i18n/locales";

const DEFAULT_LOCALE = "en";

export async function loader({ params, request }: Route.LoaderArgs) {
  const { locale } = params;

  if (!locale || !isValidLocale(locale)) {
    const url = new URL(request.url);
    const newPath = url.pathname.replace(/^\/[^/]+/, `/${DEFAULT_LOCALE}`);
    return redirect(newPath);
  }

  return { locale };
}

يحافظ هذا النهج على بقية مسار URL مع استبدال جزء اللغة غير الصالح فقط، مما يوفر تجربة مستخدم أكثر سلاسة عندما تكون اللغة هي المشكلة الوحيدة.