ב-WIZZO CMS אין מושג נפרד של "שפה". יש פלטפורמה: שורה בטבלה CRM_platforms שמייצגת דומיין אחד, ובו זמנית שפה אחת. מכאן נגזר כל הרב-לשוניות של המערכת: איזה תוכן נטען, אילו מילים מוצגות, באיזה כיוון נכתב העמוד ואיזו ערכת נושא עוטפת אותו. העמוד הזה מתאר את המנגנון כפי שהוא בקוד, כולל מה שהוא לא עושה.
נתיבי הקבצים הם יחסית ל-api/core בריפו, שהיא system/core/ באתר פרוס. {admin} הוא CONFIG::$admin_url.
הרעיון: פלטפורמה היא דומיין והיא גם שפה#
דומיין בבקשה -> שורה ב-CRM_platforms -> CONFIG::$lang = id של השורה
|
langId בכל טבלת *_content <-+
-
CONFIG::$langאינו קוד שפה (he,en) אלא ה-idשל שורת הפלטפורמה. העמודהlangIdבכל טבלת_contentמכילה אותו מספר. - שפה חדשה פירושה פלטפורמה חדשה, וכל פלטפורמה היא דומיין משלה (למשל
example.co.ilו-en.example.co.il). - אין בליבה מנגנון של קידומת שפה בכתובת (
/en/...). ה-ROUTERלא מזהה אותו, ולכן שפה נבחרת רק לפי הדומיין שאליו הגיע המבקר.
הטבלה CRM_platforms#
העמודות הבאות נקראות על ידי הליבה, וכך נראית הרשומה בפאנל {admin}/platforms:
| עמודה | משמעות | מי קורא אותה |
|---|---|---|
id | מזהה הפלטפורמה, והוא גם CONFIG::$lang ו-langId | CONFIG::init |
domain | הדומיין הראשי, בלי פרוטוקול. ממנו נגזר CONFIG::$site_url | CONFIG::init, COOKIES, PAGE::load |
allowed_domains | דומיינים נוספים שמגישים את אותה פלטפורמה, בפורמט |a||b| | CONFIG::init, PAGE::load, COOKIES, admin_login |
name | שם לתצוגה: כפתורי השפה בפאנל ומסך הכניסה | Form, ערכת admin_login |
direction | rtl או ltr: ה-dir של <html>, ה-class של <body>, וכיוון שדות בטפסים | PAGE, panel_table, Form |
ord | סדר הפלטפורמות בפאנל. הראשונה היא ברירת המחדל של הניהול | ADMIN::init_multilang, SeoPlatform |
deftheme | שם התיקייה תחת themes/ שמעטפת את העמוד | PAGE::load, MASK |
seo_title | כותרת האתר, ברירת מחדל ל-<title> | SEO, PAGE |
seo_description, seo_keywords | ברירות מחדל ל-meta description ול-keywords | SEO::tags_summary |
logo, logo_1, flag | מזהי קבצים ב-storage (שדות העלאה בפאנל) | פאנל הפלטפורמות בלבד |
allowads | תיבת סימון "אפשר פרסומות" | נכתבת בפאנל, אף קוד בליבה לא קורא אותה |
הטבלה נטענת בכל בקשה דרך cache_engine::get("platforms", ..., 24*30, false): מטמון גלובלי ל-30 יום, ממופה לפי domain. פאנל {admin}/platforms מנקה אותו בשמירה ובמחיקה (cache_engine::remove("platforms")), אבל עדכון ישיר בבסיס הנתונים לא מנקה. שורה שנוספה ב-SQL תיראה רק אחרי ניקוי המטמון (ראו ניקוי ותחזוקה).
השורה הראשונה בטבלה היא תנאי הכרחי להפעלת האתר. הסכמה המלאה והשורה המינימלית להקמה נמצאות ב-סכמת הטבלאות וב-SQL להקמה.
איך CONFIG::init בוחרת פלטפורמה#
הבחירה מתרחשת בתחילת כל בקשה, לפני כל שאילתה אחרת, ועל סמך $_SERVER['SERVER_NAME']:
- התאמה מדויקת של
SERVER_NAMEלעמודהdomain. - אם אין, מעבר על כל הפלטפורמות: התאמה ל-
domainאו לאחד מ-allowed_domains(פענוח באמצעותMISC::bgl_explode). בהתאמה כזוCONFIG::$env_urlנקבע ל-protocol://SERVER_NAME/. - אם עדיין אין, נבחרת הפלטפורמה הראשונה במערך (
array_values($platforms)[0]), בלי לוג.
אחרי הבחירה: CONFIG::$site_url הוא protocol://<domain>/ של הפלטפורמה, CONFIG::$original_site_url הוא protocol://HTTP_HOST/, ו-CONFIG::$lang ו-MISC::$GLOBALS["lang"] מקבלים את ה-id.
בצד הלקוח, PAGE::load בודקת ש-SERVER_NAME נמצא ב-domain של הפלטפורמה שנבחרה או ב-allowed_domains שלה, ואם לא, מסיימת עם URL ERROR. הבדיקה מתבצעת כש-CONFIG::$ignore_domain_check אינו true, ואינה חלה על /system/minify ועל /system/Tools. דומיין שמופנה לשרת אך לא רשום בטבלה ייפול בבדיקה הזו. צד הניהול אינו עובר אותה, ולכן כניסה לפאנל מדומיין לא רשום עשויה לעבוד ולהציג את הפלטפורמה הראשונה.
אם CRM_platforms ריקה, array_values($platforms)[0] מחזיר null ומתקבלת שגיאת PHP בתחילת הבקשה. חייבת להיות שורה אחת לפחות, ו-domain שלה צריך להיות בדיוק ה-SERVER_NAME (למשל www.example.com). אם ה-.htaccess מפנה www ל-non-www, רשמו את הצורה שאליה מגיעים בסוף.
ב-CLI (קריאות cron) MISC::cli_check מרוקנת את SERVER_NAME ו-HTTP_HOST לפני CONFIG::init, כך שנבחרת הפלטפורמה הראשונה, ואז מגדירה ignore_domain_check = true ומחזירה את שני הערכים לדומיין שלה.
מה נקבע לפי הפלטפורמה#
הפלט: כיוון, ערכת נושא ו-SEO#
| ערך | איפה הוא משפיע |
|---|---|
direction | <html dir="..."> ו-class rtl או ltr על <body> (PAGE::output), וכיוון הטבלה ב-panel_table |
deftheme | PAGE::load קוראת ל-PAGE::set_theme(...) אם הקונטרולר לא קבע ערכה בעצמו, והעמוד נבנה מ-themes/<deftheme>/index.php (המחלקה TEMPLATE_<deftheme>). ראו ערכות נושא |
seo_title, seo_description, seo_keywords | ברירות המחדל ל-SEO::tags_summary(). כותרת פאנל הניהול היא seo_title ואחריו CMS |
name | שם האתר במסך הכניסה לניהול |
<html lang> קבוע ל-he
PAGE::output כותבת <html lang="he" dir="...">: הכיוון נלקח מהפלטפורמה אבל השפה קבועה. באתר באנגלית ה-HTML עדיין מצהיר על עברית, וזה משפיע על קוראי מסך ועל מנועי חיפוש. אין בטבלה עמודת שפה. עד שזה ייפתר בליבה, אם אתם צריכים lang נכון, הוא נקבע בקוד של ערכת הנושא או בפילטר פלט של האתר.תוכן: טבלאות _content ו-langId#
לכל טבלה עם תוכן רב-לשוני יש טבלת תאומה <t>_content. שורת ה-conf (CRM_pages) מחזיקה את הנתונים הקבועים, ושורה אחת לכל שפה בטבלת התוכן (CRM_pages_content) מחזיקה את הטקסטים:
| שורת conf | שורות content |
|---|---|
pages.id = 5 | parentId = 5, langId = 1, title = "שלום" |
parentId = 5, langId = 2, title = "Hello" |
קריאה ב-DB::query("pages", ["id" => 5]) מחברת את שתיהן עם tData.langId = CONFIG::$lang. כלומר באתר, התוכן שנטען הוא תמיד התוכן של הדומיין שהבקשה הגיעה אליו. אפשר לבקש שפה אחרת במפורש בפרמטר הרביעי:
$en = DB::query("pages", ["id" => 5], "", 2)->get_row(); // langId = 2
הפרמטר $lang נכנס ל-SQL כמספר גולמי, ולכן העבירו אליו רק (int).
כתיבה לשפה אחרת נעשית עם editLang של בונה העדכון (ברירת מחדל CONFIG::$lang):
$u = DB::update("pages");
$u->editLang = 2; // כותבים לשפה 2
$u->set_var("title", "Hello", "content");
$u->update(5); // אם אין שורת content ל-langId הזה, היא נוצרת
ב-insert() של טבלה עם _content, העמודה langId נכתבת אוטומטית מ-editLang. ראו טבלאות רב-לשוניות ו-שאילתות.
DB::query
ה-JOIN לטבלת התוכן הוא פנימי. דף שנוצר בעברית בלבד ונקרא מדומיין האנגלית פשוט נעלם מתוצאות השאילתה, בלי שגיאה. בדקו שיש שורת תוכן לכל שפה פעילה, או טפלו בהיעדר שורה בקוד.copyToAllLangs נשען על טבלה ישנה
לבונה העדכון יש דגל copyToAllLangs שמעתיק הוספה לכל השפות, אבל הוא קורא את הרשימה מטבלה בשם <prefix>_langs, שאינה חלק מהסכמה של הליבה (השפות הן platforms). אל תסתמכו עליו.מילים (LANGS)#
המילון נשמר בטבלאות langs_words ו-langs_words_content (עמודות parentId, langId, value). LANGS::init_words() טוענת בכל בקשה את מילות השפה הנוכחית ל-cache_engine תחת "words_lang" . CONFIG::$lang ל-4 שעות, לפי דומיין. LANGS::get_word($key) מחזירה את המילה מהמערך הטעון, ומחרוזת ריקה אם היא חסרה. עם הפרמטר $lang היא שואלת את בסיס הנתונים ישירות ובלי המטמון:
LANGS::get_word("EDIT"); // השפה של הבקשה, מהמטמון
LANGS::get_word("EDIT", 2); // שפה 2, שאילתה ישירה
מילה חסרה בשפה לא נופלת ל"שפת ברירת מחדל". היא ריקה. ראו פרמטרים ומילים ו-PARAMS ו-LANGS.
מטמון לפי דומיין#
תיקיית המטמון של הנתונים והעמודים היא <cache_folder>/<SERVER_NAME>/, כלומר שם השרת של הבקשה, וגם דומיין חלופי מ-allowed_domains מקבל תיקייה משלו. לכן:
| מפתח | רמה | TTL |
|---|---|---|
platforms, params, table_list, table_columns_<t>, minify_files, minify_groups, redirections | גלובלי (שורש המטמון) | 30 יום, 30 יום, 7 ימים, 7 ימים, 30 יום, 30 יום, 7 ימים |
words_lang<id> | לפי דומיין | 4 שעות |
modules_lang<id> | לפי דומיין | 7 ימים |
seoUrl_tbl, scripts | לפי דומיין | ברירת מחדל 24 שעות, ו-48 שעות |
מטמון עמודים מלא (pages, pages_mobile) | לפי דומיין | לפי הגדרה |
לכן cache_engine::remove($key) מוחקת את הקובץ בכל תיקיות הדומיינים, ולא רק בתיקיית הדומיין הנוכחי. פירוט ב-איך המטמון עובד, ב-מטמון עמודים מלא וב-cache_engine.
עוגיות#
COOKIES קובעת את דומיין העוגייה לפי base_domain (CONFIG_USER::$set_base_domain אם הוגדר, אחרת HTTP_HOST). עוגיית ניהול כמו CRM_admin_lang נכתבת עם $base_domain = false, והדומיין שלה נבחר מתוך domain ו-allowed_domains של הפלטפורמה. עוגייה של דומיין אחד אינה נראית בדומיין אחר, ולכן בחירת שפה בפאנל נשמרת לדומיין שבו נעשתה. ראו COOKIES ו-SESSION.
פאנל הניהול ושפות#
בחירת שפת הפאנל#
ADMIN::init_multilang() רצה בכל בקשת ניהול:
- טוענת את כל הפלטפורמות, ממוינות לפי
ord, לתוךMISC::$GLOBALS["langs"]. - אם
$_GET["lang"]הוא מזהה קיים, שומרת אותו בעוגייהCRM_admin_lang(ערך מקודד עםMISC::encode). - אם אין עוגייה, שומרת את הפלטפורמה הראשונה.
- מציבה
MISC::$GLOBALS["admin_lang_data"](שורת הפלטפורמה) ו-MISC::$GLOBALS["admin_lang_id"]. - מעדכנת
last_loginשל המנהל.
admin_lang_id משמש את ערכת הניהול (בורר השפה בראש הפאנל וקישור "פתיחת האתר" אל הדומיין של הפלטפורמה שנבחרה) ואת טופס settings ב-PanelList, שעורך את שורת הפלטפורמה הנבחרת.
LANGS::init_words() רצה לפי CONFIG::$lang, כלומר לפי הדומיין שדרכו נכנסתם לפאנל, ולא לפי admin_lang_id. בחירת שפה בפאנל משנה את שפת העריכה ולא את תוויות הממשק.עריכת תוכן בשפה אחרת#
טופס שנבנה ב-Form קורא $_GET["editLangID"]. כשהוא קיים, הוא קובע את editLang של הטופס ומציב את MISC::$GLOBALS["inputDirection"] לפי direction של אותה פלטפורמה, כדי שהשדות ייכתבו בכיוון הנכון. כפתורי בחירת השפה (editLangBtns) מופיעים רק כשיש יותר מפלטפורמה אחת בטבלה. שדות מסוג conf לא נטענים בעריכה של שפה שאינה שפת הדומיין, כי הם משותפים לכל השפות ולא שייכים לשפה מסוימת. ראו טפסים ו-קטלוג שדות הטופס.
רב-דומיין מול רב-לשוניות#
שני הדברים נראים דומים, והקוד מבדיל ביניהם רק בדרך אחת: האם הדומיין הוא domain של פלטפורמה או רק ב-allowed_domains שלה.
| רצון | איך מגדירים | מה מקבלים |
|---|---|---|
אותו אתר ואותו תוכן בשני דומיינים (example.co.il ו-example.com) | דומיין אחד ב-domain, השני ב-allowed_domains של אותה פלטפורמה | אותה שפה, אותה ערכה, אותו תוכן. מטמון נפרד לכל שם שרת |
| אתר בשתי שפות | שתי שורות ב-CRM_platforms, כל אחת עם domain משלה, ושורות _content בכל שפה | תוכן, מילים, כיוון וערכה לכל שפה, לפי הדומיין |
| אתר אחד עם ערכות נושא שונות בדומיינים שונים | שורה לכל דומיין עם deftheme שונה | כל דומיין מקבל את הערכה שלו |
מה שאין:
- אין החלפת שפה על אותו דומיין, בקידומת נתיב (
/en/) או בעוגייה. ה-ROUTERלא מכיר קידומת כזו. - אין שפת ברירת מחדל שאליה נופלים כשחסר תוכן. תוכן או מילה חסרים נשארים חסרים.
- אין סינון לפי פלטפורמה בטבלאות
seoUrlו-redirectionsבקוד הליבה.ROUTER::get_seo_tblקוראת את כלseoUrl(ובמטמון של הדומיין הנוכחי), ו-redirectionsנקראת כרשימה אחת גלובלית. כלל שהגדרתם חל על כל הדומיינים של האתר. ראו כתובות ידידותיות ו-הפניות 301.
המחלקה SeoPlatform מוסיפה עמודת platform לטבלאות של חבילת ה-SEO ומסננת לפיה, רק באתר שיש בו יותר מפלטפורמה אחת (באתר עם פלטפורמה יחידה השאילתות נשארות זהות). הפלטפורמה הראשונה לפי ord נחשבת ראשית. ראו חבילת ה-SEO ו-SEO למפתחי אתר.
הוספת שפה: צעדים#
- מוסיפים פלטפורמה בפאנל
{admin}/platforms(שם, דומיין, דומיינים נוספים, כיוון, ערכת ברירת מחדל ושדות ה-SEO). הפאנל מנקה את מטמוןplatformsבעצמו. אם הוספתם שורה בשאילתה, נקו את המטמון (cache_engine::remove("platforms")). - מפנים את הדומיין החדש לשרת ומוודאים שהוא רשום ב-
domainאו ב-allowed_domains(אחרת תתקבלURL ERROR). - יוצרים שורות
_contentעם ה-idהחדש כ-langIdלכל טבלה רב-לשונית: עמודים, מודולים (modules_content) ומילים (langs_words_content). - אם בערכת הנושא יש טקסטים קבועים, מוודאים שהם עוברים דרך
LANGS::get_wordולא כתובים קשיח. - בודקים בדומיין החדש: כיוון העמוד, ערכת הנושא, וש-
<html lang>נכון (ראו האזהרה למעלה).
מלכודות נפוצות#
| תסמין | סיבה | פתרון |
|---|---|---|
URL ERROR בדומיין חדש | הדומיין לא ב-domain ולא ב-allowed_domains, או שהמטמון platforms ישן | לרשום את הדומיין ולנקות מטמון |
| דף חסר באתר האנגלי | אין שורת _content עם langId של האנגלית | ליצור את שורת התוכן |
| שינוי בפלטפורמה לא נראה | platforms שמור 30 יום וגלובלי | cache_engine::remove("platforms") או שמירה דרך הפאנל |
| מילה מופיעה ריקה | חסרה שורת langs_words_content לשפה, או שהמטמון words_lang<id> ישן (4 שעות) | להוסיף את המילה ולנקות מטמון |
| טקסט הממשק בפאנל לא עבר לשפה שבחרתי | המילים נקבעות לפי הדומיין, לא לפי CRM_admin_lang | להיכנס לפאנל דרך הדומיין של השפה הרצויה |
<html lang> שגוי | הערך קבוע ל-he ב-PAGE::output | לתקן בערכה או בפילטר פלט |
כניסה לפאנל ריקה עם TypeError | CRM_platforms ריקה | להוסיף שורה ראשונה |
ראו גם#
- ה-Collections:
CONFIG, DB, LANGSו-cache_engineשמופיעים כאן. - מוסכמות ושמות:
_content, |1||2|ושמות מפתחות מטמון. - טבלאות רב-לשוניות ו-פרמטרים ומילים: הצד של הנתונים והמילים.
- איך המטמון עובד: היקף כל מפתח מטמון.