کیبورد کُردی

متن‌باز · ورودی متن کُردی · v0.4.0

کیبورد کُردی

تایپ کُردی در هر جای وب

کیبورد مجازی قابل‌نصب برای سورانی (خط عربی) و کرمانجی (خط لاتین). یک ویجت، همراه Unicode یکپارچه و صحیح.

npm install kurdish-keyboard
خط
۲
چیدمان
۳
وابستگی زمان اجرا
۰
codepoint تأییدشده
۵۵

کرمانجی، QWERTY با ردیف اضافه

چیدمان QWERTY با یک ردیف اضافه برای ç ê î ş û، چپ‌به‌راست.

سورانی

چیدمان برگرفته از داده‌های Unicode CLDR، راست‌به‌چپ.

چرا

یک حرف، چند کد

متن کُردی در وب دهه‌هاست که با کدگذاری‌های متفاوت درگیر است. حرف‌هایی که یکسان به نظر می‌رسند کدهای متفاوتی دارند: ك و ک، ي و ی، ه و ە. این تفاوت بی‌صدا جست‌وجو، مرتب‌سازی و غلط‌یابی را گمراه می‌کند. این کیبورد فقط کدپوینت‌های یکپارچهٔ استاندارد ادارهٔ فناوری اطلاعات KRG را تایپ می‌کند که با پایگاه دادهٔ نویسه‌های Unicode (UCD) بازبینی شده‌اند. بدین صورت هر متنی که با آن تایپ شود از پایه درست و صحیح است.

نمایش زنده

این اسکرین‌شات نیست. این خودِ ویجت است.

کیبوردهای این صفحه عکس نیستند. همان پکیج واقعی‌اند که با متغیرهای CSS تم داده شده‌اند. یک چیدمان را انتخاب کرده و تایپ کنید. ⇧ لایهٔ دوم را نشان می‌دهد و چیدمان‌های لاتین ⇪ Caps Lock هم دارند. با فلش ها بین کلیدها جابه‌جا شوید. screen-readerها هم می‌توانند با آن کار کنند.

امکانات

هر آنچه یک ورودی کُردی نیاز دارد

  1. Unicode یکپارچه

    از پایه صحیح.

    ویجت فقط می‌تواند کدپوینت‌هایی را تایپ کند که با استاندارد KRG و UCD بازبینی شده‌اند: همیشه ک نه ك، همیشه ی نه ي، و ە به‌عنوان، همان مصوتی که هست. متنِ ازهم‌گسیخته به خاطره تبدیل می شود!

  2. دو خط، سه چیدمان

    راست‌به‌چپ و چپ‌به‌راست، یک انجین.

    یک چیدمان سورانی ساخته‌شده از داده‌های Unicode CLDR، به‌علاوهٔ دو چیدمان رایج کرمانجی. با setLayout() به‌صورت آنی تغییر دهید؛ جهت، لایه‌ها و Caps Lock خودبه‌خود همراه آن عوض می‌شوند.

  3. ساخته‌شده برای موبایل

    خودِ ویجت، کیبورد است.

    برای هر فیلد، inputmode="none" کیبورد گوشی را مخفی نگه می‌دارد تا پنل چسبان یا شناور کار تایپ را انجام دهد.دیوایس های لمسی بر پایهٔ یک capability query تا ۴۴ پیکسل بزرگ می‌شوند، بدون شناسایی user-agent.

  4. پشتیبانی Rich text

    contenteditable و ویرایشگرها.

    contenteditable ساده بدون هیچ تنظیمی کار می‌کند. ویرایشگرهای فریم‌ورکی مانند Lexical، ProseMirror و Slate با یک آداپتور کوچکِ درج، وصل می‌شوند.

  5. دسترس‌پذیر به‌صورت پیش‌فرض

    tabindex چرخان، نام‌های گفتاری.

    ناویگیشن صفحه‌کلید مطابق WAI-ARIA، فوکوس مرئی و labelهای screen-reader به انگلیسی یا کُردی، قابل تغییر در زمان اجرا(runtime).

  6. نرمال‌ساز متن قدیمی

    پاک‌سازی دهه‌ها ك و ي.

    normalizeLegacy() سورانیِ قدیمی و ازهم‌گسیخته را به کدپوینت‌های یکپارچه تبدیل می‌کند، با همان فایل داده‌ای که خود کیبورد هم استفاده می‌کند. به DOM نیازی نیست، در نتیجه در Node و ابزارهای خط فرمان هم اجرا می‌شود.

اصول

چگونه ساخته شده است

  1. استاندارد به‌جای اختراع!

    کدپوینت‌ها از استاندارد یکپارچهٔ کیبورد KRG می‌آیند و چیدمان سورانی از Unicode CLDR. بدین صورت با استفاده از این استانداردها، هیچ‌چیز به صورت دستی ساخته نمی‌شود.

  2. داده به‌جای کد

    چیدمان‌ها فایل‌های JSON هستند که با یک اسکیما اعتبارسنجی می‌شوند. انجین هرگز به نام چیدمان نگاه نمی‌کند. چیدمان تازه یعنی دادهٔ تازه، نه کد تازه.

  3. خودِ پلتفرم کافی است

    setRangeText برای تایپ، inputmode برای گوشی‌ها، متغیرهای CSS برای تم. بدون وابستگی در زمان اجرا، ESM و CJS، چیدمان‌های tree-shakable.

  4. تأییدشده، نه فرض‌شده

    هر کدپوینت حرف‌به‌حرف با منابع دست‌اول بازبینی شده است. پیش از هر انتشار، یک مشخصهٔ اجرایی پکیج منتشرشده را راستی‌آزمایی می‌کند.

راهنما

راهنمای یکپارچه‌سازی

هر آنچه برای افزودن کیبورد به سایتتان لازم دارید. هر نمونه کنار کدش به‌صورت زنده اجرا می‌شود.

شروع سریع

وصل کنید، تایپ کنید، جدا کنید

برای هر چیدمان یک import و یک فراخوانی. پنل پس از فیلد ظاهر می‌شود. هر کلید در جای مکان‌نما تایپ می‌کند، متن انتخاب‌شده را جایگزین می‌کند و فوکوس را در فیلد نگه می‌دارد. kb.detach() پنل را برمی‌دارد و همهٔ side effectها را برمی‌گرداند.

import { KurdishKeyboard } from "kurdish-keyboard";
import "kurdish-keyboard/styles.css"; // optional, recommended
import { soraniLayout } from "kurdish-keyboard/layouts/sorani";

const kb = KurdishKeyboard.attachTo(document.querySelector("input"), {
  layout: soraniLayout,
});
// later: kb.detach()

زنده

گزینهنوعپیش‌فرضکارکرد
layoutLayoutپنل حداقلیچیدمانی که نمایش داده می‌شود. برای هر خط یکی را import کنید، یا چیدمان خودتان را بدهید.
adapterInsertionAdapterداخلیدرج اختصاصی شما برای ویرایشگرهای فریم‌ورکی. بخش «Rich text» را در ادامه ببینید.
suppressNativebooleanfalseinputmode="none" را تنظیم می‌کند تا گوشی‌ها کیبورد خودشان را پنهان نگه دارند.
position'inline' | 'docked' | 'floating''inline'جای پنل. بخش «موبایل» را در ادامه ببینید.
labelLanguage'en' | 'ku''en'زبان labelهای screen-reader (Shift، Space، …).

چیدمان‌ها

سه چیدمان، تغییر آنی

هر چیدمان نقطهٔ ورود جداگانه‌ای دارد، بدین صورت سایتی که فقط سورانی دارد هرگز دادهٔ کرمانجی را در باندل نمی‌آورد. setLayout() به‌صورت آنی جابه‌جا می‌کند: پنل دوباره ساخته شده، جهت بین راست‌به‌چپ و چپ‌به‌راست عوض می‌شود و Shift و Caps Lock به حالت اول برمی‌گردند. چیدمان‌های لاتین ⇪ Caps Lock دارند؛ Shift همه‌جا برای یک کلید کار می‌کند.

چیدمان خودتان: چیدمان دادهٔ plain است. نوع Layout export شده است و JSON با اسکیمای منتشرشده اعتبارسنجی می‌شود. کلیدها رشته تایپ می‌کنند، نه نویسه: Shift+و در CLDR برابر وو است. جدول کدپوینت‌های تأییدشده با نام kurdish-keyboard/codepoints.json همراه پکیج آمده است. فیلدهای راست‌به‌چپ: پنل جهت خودش را تنظیم می‌کند، اما dir فیلد با شماست. برای فیلدهای سورانی از dir="rtl" یا dir="auto" استفاده کنید؛ اگر فراموش کردید، ویجت یک log در کنسول می‌نویسد.

import { soraniLayout } from "kurdish-keyboard/layouts/sorani";
import { kurmanjiPhonetic } from "kurdish-keyboard/layouts/kurmanji-phonetic";
import { kurmanjiOfficial } from "kurdish-keyboard/layouts/kurmanji-official";

kb.setLayout(kurmanjiPhonetic); // live swap, LTR
kb.setLayout(soraniLayout); // back to RTL

زنده

Rich text

contenteditable و آداپتور خودتان

پنل بدون هیچ تنظیمی به المنت های contenteditable وصل می‌شود. تایپ از طریق کامندهای ویرایشی خودِ مرورگر انجام می‌شود، در نتیجه مکان‌نما، انتخاب و رویدادهای input مثل ضربه‌های واقعی کلید رفتار می‌کنند. یک قاعده: کلیدها فقط وقتی کار می‌کنند که انتخاب درون المنت وصل‌شده باشد. اگر مکان‌نما جای دیگری باشد، کلید کاری نکرده و هرگز در فیلد اشتباه تایپ نمی‌کند.

ویرایشگرهای فریم‌ورکی مانند Lexical، ProseMirror و Slate عمداً ویرایش‌های بیرونی را برمی‌گردانند. برای آن‌ها می توانید یک adapter بدهید. insertChar و deleteBack الزامی‌اند؛ insertLineBreak و insertParagraph اختیاری‌اند. پنل فقط برای عملیاتی که آداپتور دارد کلید را نشان می‌دهد: بدون insertParagraph، کلید ¶ هم نیست. آداپتورهای داخلی هم اکسپورت شده‌اند (valueAdapter، contentEditableAdapter)،در نتیجه اگر بخواهید یکی از آن‌ها را درون آداپتور خودتان به کار ببرید، امکان پذیر است.

// Plain contenteditable: nothing new to learn
KurdishKeyboard.attachTo(document.querySelector("[contenteditable]"), {
  layout: soraniLayout,
});

// Lexical (e.g. Payload CMS): supply an editor-native adapter
import {
  $getSelection,
  DELETE_CHARACTER_COMMAND,
  INSERT_LINE_BREAK_COMMAND,
  INSERT_PARAGRAPH_COMMAND,
} from "lexical";

KurdishKeyboard.attachTo(editorRootElement, {
  layout: soraniLayout,
  adapter: {
    insertChar: (text) =>
      editor.update(() => $getSelection()?.insertText(text)),
    deleteBack: () => editor.dispatchCommand(DELETE_CHARACTER_COMMAND, true),
    insertLineBreak: () =>
      editor.dispatchCommand(INSERT_LINE_BREAK_COMMAND, false),
    insertParagraph: () =>
      editor.dispatchCommand(INSERT_PARAGRAPH_COMMAND, undefined),
  },
});

زنده

قسمت قابل ویرایش (سورانی)

موبایل

خودتان کیبورد باشید!

suppressNative مقدار inputmode="none" را تنظیم می‌کند؛ روش استاندارد برای اینکه به گوشی بگویید «این صفحه، کیبورد خودش را دارد». مستلزم آن، تصحیح خودکار گوشی برای آن فیلد است، در نتیجه برای هر فیلد جداگانه و اختیاری است. docked پنل را به پایین صفحه می‌چسباند و فیلد را در دید نگه می‌دارد؛ floating آن را نزدیک گوشه نگه می‌دارد. در اسکرین های لمسی، اهداف لمسی تا ۴۴ پیکسل بزرگ می‌شوند.

KurdishKeyboard.attachTo(field, {
  layout: soraniLayout,
  suppressNative: true,
  position: "docked", // or "floating"
});

زنده

روی کامپیوتر پنل ثابت را می‌بینید. روی گوشی، inputmode="none" کیبورد خود گوشی را هم کنار نگه می‌دارد.

تم

متغیرهای CSS، بدون CSS-in-JS

فایل CSS را یک بار import کنید، سپس متغیرهای --kkb-* را روی پنل یا هر والد آن تنظیم کنید. فایل CSS اختیاری است؛ ویجت بدون آن هم کار می‌کند. دو کیبورد زیر فقط در متغیرهای CSS با هم فرق دارند.

متغیرهای داخلی --_* از بقیه پیروی می کنند. فقط متغیرهای عمومی را تغییر دهید.

.theme-night {
  --kkb-key-bg: #1e293b;
  --kkb-key-color: #e2e8f0;
  --kkb-key-border: #334155;
  --kkb-pressed-bg: #334155;
  --kkb-focus-color: #38bdf8;
  --kkb-surface-bg: #1e293b;
}

پیش‌فرض

تم تاریک، فقط با --kkb-*

متغیرپیش‌فرضکاربرد
--kkb-gap4pxفاصلهٔ بین کلیدها و ردیف‌ها
--kkb-key-size2.2rem · 44px در صفحه‌های لمسیکمترین عرض و ارتفاع کلید
--kkb-key-font-size1.1rem · 1.3rem در صفحه‌های لمسیاندازهٔ نوشتهٔ کلید
--kkb-key-bgButtonFaceپس‌زمینهٔ کلید
--kkb-key-colorButtonTextرنگ نوشتهٔ کلید
--kkb-key-borderButtonText 40%رنگ حاشیهٔ کلید
--kkb-key-radius4pxشعاع گوشهٔ کلید
--kkb-pressed-bg#cbdceeپس‌زمینهٔ Shift و Caps Lock فعال
--kkb-focus-color#1a56a0حلقهٔ فوکوس صفحه‌کلید
--kkb-surface-bgCanvasپس‌زمینهٔ پنل چسبان یا شناور

دسترس‌پذیری

ناوبری با صفحه‌کلید، نام‌های گفتاری

پنل یک role="group" برچسب‌دار از دکمه‌های دیفالت است که از الگوی roving tabindex در WAI-ARIA پیروی می‌کند. پنل فقط یک توقف Tab است. کلیدهای جهت‌نما در جهت دیداری بین کلیدها حرکت می‌کنند، در نتیجه در پنل‌های راست‌به‌چپ وارونه‌اند. Home و End به ابتدا و انتهای ردیف می‌پرند؛ Enter و Space کلید را می‌فشارند. کلیدهایی که نویسهٔ نادیدنی یا بعبارتی پنهان تایپ می‌کنند نام دارند (Space، ZWNJ، ⇧ Shift، ⇪ Caps Lock). فوکوس پس از رندر دوباره سر جایش می‌ماند و :focus-visible یک خط outline با کنتراست دست‌کم ۳:۱ نشان می‌دهد.

// at attach time…
KurdishKeyboard.attachTo(field, {
  layout: soraniLayout,
  labelLanguage: "ku",
});

// …or live
kb.setLabelLanguage("en");

زنده

کلید ⇧ می‌گوید:

برای رفتن به درون پنل Tab را بزنید، سپس از کلیدهای جهت‌نما یا فلش ها استفاده کنید.

متن قدیمی

پاک‌سازی متن قدیمی

normalizeLegacy() سورانیِ قدیمی و ازهم‌گسیخته (ك و ي عربی، ه به‌جای ە) را به کدپوینت‌های یکپارچه تبدیل می‌کند.در این متد از همان فایل داده‌ای استفاده می‌کند که کیبورد از آن استفاده میکند. به DOM نیازی ندارد، در نتیجه در Node، در workerها و در ابزارهای خط فرمان اجرا می‌شود.

اول این را بخوانید: فرض بر این است که ورودی کُردی است. ك←ک و ي←ی همیشه اعمال می‌شوند، پس نقل‌قول‌های واقعی عربی هم تغییر می‌کنند؛ متن زبان‌های دیگر را خودتان جدا کنید. قاعدهٔ ه←ە فقط به جایگاه نگاه می‌کند: ه در پایان واژه تغییر می‌کند (پیش از ZWNJ هم)؛ ه پیش از یک حرف عربی دیگر، کشیده (tatweel) یا اعراب تغییر نمی‌کند. مصوت‌های قدیمی وسط واژه پیدا نمی‌شوند و هیچ واژه‌نامه‌ای در کار نیست. متن لاتین تغییر نمی‌کند.

import { normalizeLegacy } from "kurdish-keyboard/normalize";

normalizeLegacy("كوردي"); // → "کوردی"
normalizeLegacy("ئێمه"); // → "ئێمە"

زنده

نتیجه

CDN

بدون مرحلهٔ build

باندل اسکریپت یک KurdishKeyboard گلوبال در اختیار می‌گذارد. attachTo آن نام یا مقدار چیدمان‌ها ('sorani'، 'kurmanji-phonetic'، 'kurmanji-official') یا شیءهای Layout را می‌پذیرد؛ مقدارهایی غیر از این ها، خطایی همراه با فهرست مقادیر می‌دهد.هم چنانکه یک لیست از layouts و normalizeLegacy هم دارد. این باندل همهٔ چیدمان‌ها را دارد؛ اگر bundler دارید، از ورودی‌های ماژولی(module) استفاده کنید.

<script src="https://unpkg.com/kurdish-keyboard"></script>
<script>
  KurdishKeyboard.attachTo(document.querySelector("input"), {
    layout: "sorani",
  });
</script>

فریم‌ورک‌ها

React، Vue، Svelte: بدون نیاز به wrapper

هر درج یک رویداد input بصورت bubbling (inputType: 'insertText') روی فیلد می‌فرستد. در نتیجه بایندینگ فریم‌ورک‌ها تغییر را می‌بینند، انگار کاربر خودش تایپ کرده است. هنگام mount وصل کنید و هنگام unmount جدا کنید.

function KurdishInput() {
  const [value, setValue] = useState("");
  const ref = useRef(null);

  useEffect(() => {
    const kb = KurdishKeyboard.attachTo(ref.current, { layout: soraniLayout });
    return () => kb.detach();
  }, []);

  return (
    <input
      dir="rtl"
      ref={ref}
      value={value}
      onChange={(e) => setValue(e.target.value)}
    />
  );
}
<script setup>
const text = ref("");
const field = ref(null);
let kb;

onMounted(() => {
  kb = KurdishKeyboard.attachTo(field.value, { layout: soraniLayout });
});
onUnmounted(() => kb?.detach());
</script>

<template>
  <input dir="rtl" ref="field" v-model="text" />
</template>

<!-- Svelte: bind:value works the same way -->

محدودیت‌ها

چه کارهایی نمی‌تواند بکند، و چرا

این‌ها ruleهای مرورگرند، نه امکاناتی miss شده.

نمی‌تواند در iframeهای سایت‌های دیگر تایپ کند
سیاست same-origin هر نوع دسترسی به صفحه‌هایی را که از origin دیگری جاسازی شده‌اند مسدود می‌کند.
نمی‌تواند ضربهٔ کلید واقعی بسازد
رویدادهای صفحه‌کلیدی که یک اسکریپت می‌سازد isTrusted: false دارند و مرورگرها اثرشان را نادیده می‌گیرند. پس ویجت مقدار فیلد را مستقیم با setRangeText() تغییر می‌دهد. این روش همه‌جا کار می‌کند، در فیلدهای رمز عبور هم.
suppressNative تصحیح خودکار را خاموش می‌کند
inputmode="none" کل کیبورد گوشی را برای آن فیلد کنار می‌گذارد، تصحیح خودکار را هم. سازوکار همین است، پس برای هر فیلد جداگانه و اختیاری است.
ویرایشگرهای فریم‌ورکی به آداپتور نیاز دارند
Lexical، ProseMirror و Slate خودشان DOM را در اختیار دارند و ویرایش‌های بیرونی را برمی‌گردانند. درج در سطح مرورگر نمی‌تواند آن‌ها را به‌طور امن هدایت کند؛ گزینهٔ adapter برای همین‌هاست. contenteditable ساده بدون هیچ تنظیمی کار می‌کند.
پشتیبانی مرورگرها
مرورگرهای به‌روزشوندهٔ حدود سه سال اخیر، به‌علاوهٔ Safari 15.4+. یک کمبود کوچک: حاشیهٔ پیش‌فرض کلیدها از color-mix() استفاده می‌کند (Safari 16.2+). مرورگرهای قدیمی‌تر کلیدها را بدون حاشیه نشان می‌دهند، مگر اینکه خودتان --kkb-key-border را تنظیم کنید. هیچ polyfillی همراه بسته نیست.

دریافت

رایگان، همان‌طور که یک کیبورد بایستی باشد

رایگان

مجوز MIT · بدون پلن، بدون ردیابی، بدون قفل‌شدن

npm install kurdish-keyboard

نسخهٔ 0.4.0 روی npm، یا نصب از مخزن GitHub.

کد در GitHub

پرسش‌ها

پاسخ، پیش از پرسش

کدام مرورگرها پشتیبانی می‌شوند؟
مرورگرهای به‌روزشوندهٔ سه سال اخیر، به‌علاوهٔ Safari 15.4+. همهٔ APIهای مرورگر که ویجت به کار می‌برد به‌طور گسترده در دسترس‌اند، پس هیچ polyfillی همراه بسته نیست.
با React، Vue یا Svelte کار می‌کند؟
بله. ویجت پس از هر درج یک رویداد input بصورت bubbling می‌فرستد، پس بایندینگ فریم‌ورک‌ها بدون wrapper تغییرها را می‌بینند.
در هر فیلدی می‌تواند تایپ کند؟
در هر input، textarea یا عنصر contenteditable در همان صفحه، و در ویرایشگرهای Rich text از راه یک آداپتور. iframeهای سایت‌های دیگر به‌دلیل امنیت مرورگر بسته‌اند.
چرا کاف عربی را تایپ نمی‌کند؟
در متن کُردی، ك فقط شبیه ک است و بی‌صدا جست‌وجو و مرتب‌سازی را می‌شکند. پس ویجت همیشه ک را تایپ می‌کند. حروف واقعی عربی برای تایپ نام‌های عربی روی لایهٔ Shift سورانی هستند.
می‌توانم چیدمان خودم را بسازم؟
بله. چیدمان‌ها JSON ساده‌اند و با اسکیمای منتشرشده اعتبارسنجی می‌شوند. انجین هر چیدمان معتبری را همان‌طور که هست نشان می‌دهد، پس چیدمان‌های community افزودنی‌اند، نه فورک.