=== Yaftino ===
Contributors: mhmanavi
Tags: search, persian, arabic, synonyms, woocommerce
Requires at least: 6.8
Tested up to: 7.1
Requires PHP: 8.0
Stable tag: 0.8.6
License: GPLv2 or later
License URI: https://www.gnu.org/licenses/gpl-2.0.html

Improve Persian, English and optional Arabic search with spelling variants, quantities and synonyms using existing forms.

== Description ==

Yaftino improves Persian, English and optional Arabic search using your existing forms, with spelling variants, units and synonyms.

۵۰وات matches 50 W, not 500 W; 5 cm differs from 5 mm.

Persian plurals, close matches, word/model prefixes, Arabic/dashboard matching and statistics start off. Search runs locally.

== Installation ==

1. Install and activate on single-site WordPress.
2. Choose language and features in Yaftino > Settings.
3. Open Prepare search, click the main button and keep it open until ready. You can pause/resume.
4. Use your theme’s search form; add synonyms if needed.

== Frequently Asked Questions ==

= Which fields and WooCommerce searches are supported? =

Site search covers title, excerpt and content, excluding custom fields and category names. Optional dashboard matching preserves WooCommerce Products list ID and SKU/global identifier searches; variation SKUs find their parent. Filters, permissions and explicit ordering remain. Other WooCommerce search tools keep their own behavior.

= How does Arabic matching work? =

Enable Arabic matching and prepare once. Common vowels U+064B-U+0652 are ignored; hamza, madda, distinct Arabic letters and Quranic signs remain. Canonically equivalent forms match. Mixed Persian/Arabic uses this profile, preserving آ/ا. Content stays unchanged. With the option off, Arabic keeps native matching.

= How does keyboard correction work? =

After zero results, retry once using Windows Persian key positions and synonyms. At least two letters are needed. Separate numbers/decimals stay unchanged; attached codes and mixed scripts are excluded. Arabic/English and ordinary dashboard searches skip it.

= Can it correct misspelled words? =

After zero results, correct one unknown 4-24-letter word by one edit or adjacent swap. In Persian and Arabic, three-letter input only allows one missing letter, with another known word of at least three letters in the same result title. Language, types and filters remain. Each title must contain the corrected word. Known words, units, numbers and codes stay exact; ties are skipped. Limits: 5 input parts, 200 characters, 2,048 variants, 32 candidates, one retry. Quotes, exact/sentence requests and dashboard lists skip it; supported live searches can use it. Enable and prepare once; disabling needs no preparation.

= Can I search part of a model code? =

Optional title-code prefixes run after zero results: 7823 can find 7823B, M25 can find M25rQ. Mixed prefixes need 3 characters; numeric prefixes need 4 digits. Not infix or SKU search. Codes contain ASCII letters and digits with optional internal hyphens/underscores. Quantities are protected. One code component, 5 input parts, 16 distinct codes/title and 32 candidate codes are allowed; overflow skips expansion. Full known codes stay exact. Other terms and native filters remain. Enable and prepare once.

= Can it find an unfinished word? =

Enable last-word completion and prepare once. After zero results, lamp p can find lamp park. Only the last word is completed from same-language, published, password-free titles; earlier terms and native filters remain required. One or two letters need another complete non-unit word of at least three letters in the same title; otherwise three letters are needed. Limits: 5 input parts, 200 characters, 32 distinct continuations. Numbers, units and models stay exact. It shares one completion retry with model prefixes; no recursive completions. Quotes, exact/sentence/offset requests and ordinary dashboard lists skip it. Independent of fuzzy matching; preserves the theme’s live-search threshold.

= Which spelling differences need no synonyms? =

Persian/Arabic yeh/kaf variants, digits and supported quantities match automatically. Ordinary Persian matching also folds آ/ا; Arabic mode preserves them. Prepared public content teaches bounded spaced/joined variants from real half-spaces. For example, نرم‌افزار matches نرم افزار. Half-spaces typed in the query also work for that request, even if content only uses spaces. Plain separate words are never joined by guesswork. Spacing exceptions in Settings apply immediately. Conflicting boundaries or synonym groups are not merged.

= Can singular Persian words find plurals? =

Enable Persian plurals and prepare once. Real half-spaces before final ها/های link گل and گل‌ها/گل‌های, including short bases. Evidence links spaced/joined ها/های siblings. Exact forms rank first per field. No other suffixes or guessed endings; exceptions and conflicting synonyms block links. Quotes, units and other languages keep their rules.

= How do I enter synonyms? =

Enter 2-10 equivalent phrases, one per line; maximum 500 groups. Choose the search language; * means shared across supported languages. Edit an existing group to add another spelling. Alternatives require all words; gaps are allowed. Quotes stay literal; no recursive chains. No preparation after saving.

= Which multilingual sites are supported? =

Tested with Polylang 3.9.1 and WPML 5.0.2, following query locales and custom language codes. Persian locales such as fa_AF can use Persian matching; Urdu and Pashto are not supported search profiles.

Search language differs from dashboard language. All-language queries, untranslated types and WPML language fallback keep native matching. English supports case/punctuation rules, units, synonyms, ranking, statistics and optional close matches. No Persian keyboard rules, translation or stemming; candidates stay language-specific.

= When should I prepare the search data again? =

After enabling Persian plurals, close matches, model prefixes or last-word completion, or changing dashboard matching, Arabic mode or language context. Other switches need no preparation. Normal search stays available until ready. Progress is estimated.

Edits and deletions synchronize automatically; rebuild after imports bypassing WordPress save hooks.

= What are the limits and compatibility boundaries? =

Limits: 200 characters, 8 positive components, 4 expanded positions. Overflow keeps native search and the full input. Quotes retain adjacency in one field and skip synonyms/keyboard correction. WordPress exact=true keeps native matching.

Supported: single-site standard WP_Query, including public WordPress/WooCommerce AJAX with filters enabled, explicit published scope and 1-100 results. Live-search requests are recorded separately from results-page searches when statistics are enabled.

Settings shows other engines; Test search explains handover. Custom SQL/replacement engines, suppressed filters, private/unbounded AJAX, REST/Store API, multisite, TranslatePress and commercial WooCommerce translation add-ons retain their own behavior. Unknown language providers use native matching.

On Multisite, activation is silent; search stays unchanged, with no Yaftino data/jobs created. Plugins screen explains the single-site requirement.

= What happens on deactivation or deletion? =

Deactivation keeps data; reactivation reconciles it. Single-site uninstall removes Yaftino data/events, preserving content. Multisite uninstall preserves existing data.

== Privacy ==

Local tables hold normalized text, half-space spellings and optional source-linked title words and model codes. Title words serve close matches and last-word completion. Tables grow with content. Matching and statistics use no external services or tracking identifiers.

Optional statistics store search phrases, dates, language, content types, request source and aggregate counts, without IP addresses, user IDs or cookies. Search phrases can contain personal information. Retention is up to 90 days while scheduled cleanup runs, with a periodically enforced target of 50,000 daily aggregate rows. Cleanup needs working WordPress cron or manual continuation. Admins can delete selected/all records. Disclose collection in your privacy policy before enabling it.

== Optional feedback by email ==

Feedback never interrupts activation, deactivation or deletion. Admins must check consent (initially unchecked) and click Send feedback by email.

Feedback goes to yaftino.support@gmail.com. It includes the selected reason, message, Yaftino version and optional reply address entered by the administrator. No statistics, content, customer data, IP address or site URL is attached automatically.

Mail headers may include the sender address; providers may retain logs. Yaftino does not store messages or send them to WordPress.org. Only a one-minute rate-limit timestamp is stored until uninstall. The developer's Gmail mailbox is subject to [Google Privacy Policy](https://policies.google.com/privacy) and [Google Terms](https://policies.google.com/terms).

Mail-service acceptance does not guarantee delivery. Do not send secrets, license keys or customer information. Uninstall never sends feedback.

== Screenshots ==

1. Choose matching options and optional search statistics.
2. Prepare search data and track progress.
3. Edit synonym groups, choose their language and manage the paginated list.
4. Test searches inline and inspect matching details.
5. Filter search statistics and delete selected records.

== Changelog ==

= 0.8.6 =
* Match spaced and joined Persian ها/های siblings using real half-space evidence.

See changelog.txt for older changes.

== Third-party resources ==

includes/presentation-forms.json maps Arabic Presentation Forms using Python unicodedata. Unicode data uses the [Unicode License V3](https://www.unicode.org/license.txt); see UNICODE-LICENSE.txt.
