كيفية التحقق من صحة معلمات اللغة في عناوين 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 مع استبدال جزء اللغة غير الصالح فقط، مما يوفر تجربة مستخدم أكثر سلاسة عندما تكون اللغة هي المشكلة الوحيدة.