Babelize Elements
الأدوات المصغّرة

أداة الترجمة

بطاقة ترجمة جنبًا إلى جنب، مع محددات اللغة وزر التبديل والنسخ وعداد الأحرف وحالة التحميل. مدركة لـ RTL ومنسّقة برموز shadcn.

72 / 500 characters

التثبيت

npx shadcn@latest add @babelize/translation-widget

الاستخدام

"use client";

import { useState } from "react";
import { TranslationWidget } from "@/components/ui/translation-widget";

export function Translate() {
  const [text, setText] = useState("");

  return (
    <TranslationWidget
      sourceLocale="en"
      targetLocale="fr"
      sourceText="Hello, world!"
      targetText={text} // omit for uncontrolled
      onTargetChange={setText}
    />
  );
}

مع محددات اللغة وزر التبديل:

"use client";

import { useState } from "react";
import { TranslationWidget } from "@/components/ui/translation-widget";

const locales = [{ code: "en" }, { code: "fr" }, { code: "ar" }];

export function Translate() {
  const [source, setSource] = useState("Hello, world!");
  const [target, setTarget] = useState("Bonjour, le monde !");
  const [sourceLocale, setSourceLocale] = useState("en");
  const [targetLocale, setTargetLocale] = useState("fr");

  return (
    <TranslationWidget
      locales={locales}
      sourceLocale={sourceLocale}
      targetLocale={targetLocale}
      onSourceLocaleChange={setSourceLocale}
      onTargetLocaleChange={setTargetLocale}
      sourceText={source}
      onSourceChange={setSource}
      targetText={target}
      onTargetChange={setTarget}
      onSwap={() => console.log("swapped")}
    />
  );
}

الخصائص

الخاصيةالنوعمطلوبالوصف
localesLocale[]لااللغات المحلية لمحددات اللغة في كل لوحة؛ احذفها لعرض رقائق ثابتة
sourceLocalestringلارمز BCP 47 للغة المحلية المصدر (مثل "en")؛ مرّره للتحكم بها
defaultSourceLocalestringلااللغة المحلية المصدر الأولية عند عدم التحكم (القيمة الافتراضية: locales[0]، وإلا "en")
onSourceLocaleChange(code: string) => voidلادالة استدعاء عند تغيّر اللغة المحلية المصدر
targetLocalestringلارمز BCP 47 للغة المحلية الهدف (مثل "fr"، "ar")؛ مرّره للتحكم بها
defaultTargetLocalestringلااللغة المحلية الهدف الأولية عند عدم التحكم (القيمة الافتراضية: locales[1]، وإلا "en")
onTargetLocaleChange(code: string) => voidلادالة استدعاء عند تغيّر اللغة المحلية الهدف
sourceTextstringلانص المصدر — مرّره للتحكم في لوحة المصدر
defaultSourceTextstringلانص المصدر الأولي عند عدم التحكم
onSourceChange(text: string) => voidلادالة استدعاء عند تغيّر نص المصدر؛ تجعل اللوحة قابلة للتحرير
targetTextstringلانص الهدف — مرّره للتحكم في لوحة الهدف (للقراءة فقط بدون onTargetChange)
defaultTargetTextstringلانص الهدف الأولي عند عدم التحكم
onTargetChange(text: string) => voidلادالة استدعاء عند تغيّر نص الهدف
onSwap() => voidلاتُستدعى بعد أن يتبادل زر التبديل اللغات المحلية والنصوص
sourcePlaceholderstringلانص توضيحي للوحة المصدر
targetPlaceholderstringلانص توضيحي للوحة الهدف
sourceLabelstringلاتسمية إمكانية الوصول للوحة المصدر (القيمة الافتراضية: "Source text")
targetLabelstringلاتسمية إمكانية الوصول للوحة الهدف (القيمة الافتراضية: "Target text")
readOnlySourcebooleanلاأبقِ لوحة المصدر للقراءة فقط حتى مع onSourceChange
showCountbooleanلاأظهر عداد أحرف المصدر (القيمة الافتراضية: true)
maxLengthnumberلاحد الأحرف للوحتين معًا؛ يعرض العداد n / max
showClearbooleanلاأظهر زر مسح أسفل المصدر القابل للتحرير (القيمة الافتراضية: true)
showCopybooleanلاأظهر زر النسخ إلى الحافظة أسفل الهدف (القيمة الافتراضية: true)
onTargetCopy(text: string) => voidلاتُستدعى بنص الهدف بعد نسخه
labelsPartial<TranslationWidgetLabels>لاتجاوز نصوص الواجهة الإنجليزية المدمجة (انظر أدناه)
loadingbooleanلاأظهر هيكل تحميل في لوحة الهدف وسمه بـ aria-busy
classNamesTranslationWidgetClassNamesلاتجاوزات الأصناف لكل جزء: root، header، pane، textarea، footer، swap، chip
dir"ltr" | "rtl"لاتجاوز اتجاه التخطيط المكتشف تلقائيًا
classNamestringلاأصناف CSS إضافية

أي خاصية أخرى — id، data-*، aria-* — تُمرَّر إلى العنصر الجذري، ويُعاد توجيه المرجع (ref) إليه.

المصدر القابل للتحرير

لوحة المصدر للقراءة فقط افتراضيًا. مرّر onSourceChange لجعلها قابلة للتحرير، أو اجعلها للقراءة فقط بـ readOnlySource حتى مع وجود معالج تغيير:

<TranslationWidget
  sourceLocale="en"
  targetLocale="de"
  sourceText={source}
  onSourceChange={setSource}
  readOnlySource
/>

الهدف الخاضع للتحكم

لوحة الهدف قابلة للتحرير عند عدم التحكم (defaultTargetText) أو عند تمرير targetText وonTargetChange معًا. الهدف الخاضع للتحكم دون معالج يُعرض للقراءة فقط، حتى لا يُفقد النص المكتوب بصمت:

// Read-only: controlled with no way to report edits
<TranslationWidget sourceLocale="en" targetLocale="fr" targetText="Bonjour" />

// Editable
<TranslationWidget
  sourceLocale="en"
  targetLocale="fr"
  targetText={text}
  onTargetChange={setText}
/>

محددات اللغة

مرّر locales لعرض مبدّل اللغة في كل لوحة بدلًا من رقاقة اللغة الثابتة. يحدّث كل محدد اللغة المحلية للوحته، ويتبعه lang وdir في منطقة النص، فتبديل الهدف إلى العربية يجعل تلك اللوحة من اليمين إلى اليسار:

<TranslationWidget
  locales={[{ code: "en" }, { code: "fr" }, { code: "ar" }]}
  defaultSourceLocale="en"
  defaultTargetLocale="fr"
  sourceText={source}
  onSourceChange={setSource}
/>

يمكن أن تكون حالة اللغة المحلية غير خاضعة للتحكم (defaultSourceLocale / defaultTargetLocale) أو خاضعة للتحكم (sourceLocale + onSourceLocaleChange، وبالمثل للهدف). عند حذف locales تحتفظ الأداة برقائق ثابتة مدمجة.

التبديل

يتبادل زر التبديل بين اللوحتين اللغتين المحليتين والنصين معًا — المصدر ↔ الهدف — ويُطلق onSwap. يقع في شريط الرأس بين محددي اللغة، ويدور أيقونته نصف دورة عند كل تبديل. يحمل الزر aria-label="Swap languages" ويكون معطّلًا عندما تكون أي قيمة متبادلة خاضعة للتحكم دون معالج تغيير، لأن التبديل لا يمكن تطبيقه:

<TranslationWidget
  locales={locales}
  sourceLocale={sourceLocale}
  targetLocale={targetLocale}
  onSourceLocaleChange={setSourceLocale}
  onTargetLocaleChange={setTargetLocale}
  sourceText={source}
  onSourceChange={setSource}
  targetText={target}
  onTargetChange={setTarget}
  onSwap={() => analytics.track("languages-swapped")}
/>

شريط الأدوات

تنتهي كل لوحة بشريط أدوات صغير. يعرض المصدر عداد أحرف حي — n / max عند تمرير maxLength، ويتحول إلى اللون المدمّر عند الحد — وزر مسح عندما تكون اللوحة قابلة للتحرير. يحتوي الهدف على زر نسخ يتأكد بعلامة صح ويعلن “تمت نسخ الترجمة إلى الحافظة” لقارئات الشاشة:

<TranslationWidget
  sourceLocale="en"
  targetLocale="fr"
  sourceText={source}
  onSourceChange={setSource}
  maxLength={500}
  onTargetCopy={(text) => analytics.track("translation-copied", { length: text.length })}
/>

أوقف أيًّا منها باستخدام showCount={false} أو showClear={false} أو showCopy={false}.

حالة التحميل

أثناء تنفيذ الترجمة، مرّر loading لاستبدال نص الهدف بهيكل تحميل ينبض. تُوسم منطقة النص بـ aria-busy ويُعطَّل النسخ:

const [loading, setLoading] = useState(false);

async function translate(text: string) {
  setLoading(true);
  setTarget(await api.translate(text, targetLocale));
  setLoading(false);
}

<TranslationWidget loading={loading} /* … */ />;

نصوص الواجهة المترجمة

لعناصر النائبة وتسميات اللوحات خصائص خاصة بها (sourcePlaceholder، sourceLabel وما شابه). أما بقية النصوص المدمجة — أزرار شريط الأدوات، وإعلان النسخ، والكلمة التي تُقرأ بعد عداد الأحرف، ومحددا اللغة — فتأتي من labels. أي مفتاح تتركه يحتفظ بقيمته الإنجليزية الافتراضية.

<TranslationWidget
  locales={locales}
  defaultSourceLocale="fr"
  defaultTargetLocale="en"
  onSourceChange={(text) => console.log(text)}
  sourcePlaceholder="Texte source"
  sourceLabel="Texte source"
  labels={{
    swap: "Inverser les langues",
    copy: "Copier la traduction",
    copied: "Copié",
    copiedStatus: "Traduction copiée dans le presse-papiers",
    clearSource: "Effacer le texte source",
    characters: (count) => (count === 1 ? "caractère" : "caractères"),
    languageSwitcher: {
      current: (name) => `Langue actuelle : ${name}`,
      searchPlaceholder: "Rechercher des langues…",
      emptyMessage: "Aucune langue trouvée.",
    },
  }}
/>
المفتاحالافتراضييُستخدم في
swap"Swap languages"تسمية إمكانية الوصول لزر التبديل
copy"Copy translation"تسمية إمكانية الوصول لزر النسخ
copied"Copied"تسمية زر النسخ مباشرة بعد النسخ
copiedStatus"Translation copied to clipboard"إعلان قارئ الشاشة بعد النسخ
clearSource"Clear source text"تسمية إمكانية الوصول لزر المسح
characters(count) => "characters"ما يُقرأ بعد عداد الأحرف؛ يأخذ العدد صيغة الجمع
languageSwitcherالإعدادات الافتراضية لـ LanguageSwitcherمحددا اللغة كلاهما (Partial<LanguageSwitcherLabels>)

التخصيص

يقبل كل جزء أصنافًا إضافية عبر classNames، تُدمج بعد الإعدادات الافتراضية بواسطة cn، فتتغلب أصنافك:

<TranslationWidget
  classNames={{
    root: "rounded-2xl shadow-lg",
    header: "bg-muted/50",
    textarea: "font-serif text-lg",
    swap: "bg-primary text-primary-foreground",
  }}
/>

دعم RTL

عندما تكون اللغة المحلية الهدف من اللغات التي تُكتب من اليمين إلى اليسار (ar، he، fa، ur، …) أو تحمل وسم نص فرعيًا من نوع RTL (az-Arab)، يتدفق نص لوحة الهدف من اليمين إلى اليسار. ولا يتغيّر ترتيب اللوحتين أبدًا، فالمصدر أولًا والهدف ثانيًا، ولكل لوحة dir وlang خاصان بها، فيُقرأ زوج مختلط من LTR إلى RTL بشكل صحيح على الجانبين. مرّر dir لتحديد اتجاه الأداة صراحةً.

<TranslationWidget sourceLocale="en" targetLocale="ar" sourceText="Hello, world!" />

الميزات

  • لوحتان جنبًا إلى جنب — المصدر والهدف في بطاقة واحدة، وتتكدّسان على الشاشات الضيقة
  • محددات اللغة — مرّر locales لمبدّل لغة في كل لوحة؛ وإلا فرقائق ثابتة
  • زر التبديل — يتبادل اللغتين المحليتين والنصين، مع onSwap وتسمية إمكانية وصول
  • هدف قابل للتحرير — غير خاضع للتحكم، أو خاضع للتحكم مع targetText + onTargetChange (الهدف الخاضع للتحكم دون معالج يُعرض للقراءة فقط حتى لا يُفقد النص المكتوب بصمت)
  • مدرك لـ RTL — يتبع dir وlang لكل لوحة كل لغة محلية، ويبقى ترتيب اللوحات ثابتًا
  • شريط أدوات — عداد أحرف مع maxLength، ومسح، ونسخ إلى الحافظة
  • حالة التحميل — لوحة هدف بهيكل تحميل مع aria-busy
  • قابل للوصول — حقول نصية مسمّاة، وسمات lang، وحلقات تركيز مرئية، وإعلان نسخ حي
  • دعم لوحة المفاتيح — Tab للتنقل بين اللوحتين، واكتب للترجمة
  • قابل للتنسيق — رموز shadcn (bg-card، border، muted، ring)، فيتبع سمة موقعك والوضع الداكن
  • قابل للتخصيص — classNames لكل جزء
  • حركة — أيقونة تبديل بحركة نابضة وتأكيد للنسخ، مع احترام إعدادات تقليل الحركة
  • خاضع للتحكم / غير خاضع للتحكم — يعمل مع targetText أو بدونه، وكذلك اللغات المحلية

التصميم

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

الاعتماديات

يستخدم هذا المكوّن:

  • clsx وtailwind-merge (مُضمّنة كاعتماديات)
  • lucide-react للأيقونات، وmotion لحركات التبديل والنسخ
  • مكوّن مبدّل اللغة (يُضاف تلقائيًا عبر تبعيات السجل)

الاعتماديات النظيرة:

  • react >= 18
  • react-dom >= 18
  • tailwindcss >= 3

في هذه الصفحة