أداة الترجمة
بطاقة ترجمة جنبًا إلى جنب، مع محددات اللغة وزر التبديل والنسخ وعداد الأحرف وحالة التحميل. مدركة لـ RTL ومنسّقة برموز shadcn.
التثبيت
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")}
/>
);
}الخصائص
| الخاصية | النوع | مطلوب | الوصف |
|---|---|---|---|
locales | Locale[] | لا | اللغات المحلية لمحددات اللغة في كل لوحة؛ احذفها لعرض رقائق ثابتة |
sourceLocale | string | لا | رمز BCP 47 للغة المحلية المصدر (مثل "en")؛ مرّره للتحكم بها |
defaultSourceLocale | string | لا | اللغة المحلية المصدر الأولية عند عدم التحكم (القيمة الافتراضية: locales[0]، وإلا "en") |
onSourceLocaleChange | (code: string) => void | لا | دالة استدعاء عند تغيّر اللغة المحلية المصدر |
targetLocale | string | لا | رمز BCP 47 للغة المحلية الهدف (مثل "fr"، "ar")؛ مرّره للتحكم بها |
defaultTargetLocale | string | لا | اللغة المحلية الهدف الأولية عند عدم التحكم (القيمة الافتراضية: locales[1]، وإلا "en") |
onTargetLocaleChange | (code: string) => void | لا | دالة استدعاء عند تغيّر اللغة المحلية الهدف |
sourceText | string | لا | نص المصدر — مرّره للتحكم في لوحة المصدر |
defaultSourceText | string | لا | نص المصدر الأولي عند عدم التحكم |
onSourceChange | (text: string) => void | لا | دالة استدعاء عند تغيّر نص المصدر؛ تجعل اللوحة قابلة للتحرير |
targetText | string | لا | نص الهدف — مرّره للتحكم في لوحة الهدف (للقراءة فقط بدون onTargetChange) |
defaultTargetText | string | لا | نص الهدف الأولي عند عدم التحكم |
onTargetChange | (text: string) => void | لا | دالة استدعاء عند تغيّر نص الهدف |
onSwap | () => void | لا | تُستدعى بعد أن يتبادل زر التبديل اللغات المحلية والنصوص |
sourcePlaceholder | string | لا | نص توضيحي للوحة المصدر |
targetPlaceholder | string | لا | نص توضيحي للوحة الهدف |
sourceLabel | string | لا | تسمية إمكانية الوصول للوحة المصدر (القيمة الافتراضية: "Source text") |
targetLabel | string | لا | تسمية إمكانية الوصول للوحة الهدف (القيمة الافتراضية: "Target text") |
readOnlySource | boolean | لا | أبقِ لوحة المصدر للقراءة فقط حتى مع onSourceChange |
showCount | boolean | لا | أظهر عداد أحرف المصدر (القيمة الافتراضية: true) |
maxLength | number | لا | حد الأحرف للوحتين معًا؛ يعرض العداد n / max |
showClear | boolean | لا | أظهر زر مسح أسفل المصدر القابل للتحرير (القيمة الافتراضية: true) |
showCopy | boolean | لا | أظهر زر النسخ إلى الحافظة أسفل الهدف (القيمة الافتراضية: true) |
onTargetCopy | (text: string) => void | لا | تُستدعى بنص الهدف بعد نسخه |
labels | Partial<TranslationWidgetLabels> | لا | تجاوز نصوص الواجهة الإنجليزية المدمجة (انظر أدناه) |
loading | boolean | لا | أظهر هيكل تحميل في لوحة الهدف وسمه بـ aria-busy |
classNames | TranslationWidgetClassNames | لا | تجاوزات الأصناف لكل جزء: root، header، pane، textarea، footer، swap، chip |
dir | "ltr" | "rtl" | لا | تجاوز اتجاه التخطيط المكتشف تلقائيًا |
className | string | لا | أصناف 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>= 18react-dom>= 18tailwindcss>= 3
حقل إدخال الهاتف
حقل إدخال أرقام الهواتف الدولية مع محدّد الدولة وأعلام الدول الرمزية والبحث عن رمز الاتصال. يدعم 50 دولة مع تنسيق وتحقق مدمجين.
واجهات TypeScript
واجهات TypeScript الأساسية التي تصدّرها مكوّنات Babelize Elements: الخصائص، وأنواع اللغات المحلية، وخيارات المنسّق، لكتابة مكوّنات i18n الخاصة بك في React.