פלטפורמות ורב-לשוניות

איך WIZZO CMS מייצג דומיין ושפה בטבלה אחת, CRM_platforms: בחירת הפלטפורמה בכל בקשה, מזהה השפה בטבלאות התוכן, מילים, כיוון, ערכת נושא, מטמון ופאנל הניהול.

⏱ 11 דק' קריאה 2003 מילים ערוך דף זה ב-GitHub

ב-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 ו-langIdCONFIG::init
domainהדומיין הראשי, בלי פרוטוקול. ממנו נגזר CONFIG::$site_urlCONFIG::init, ‏COOKIES, ‏PAGE::load
allowed_domainsדומיינים נוספים שמגישים את אותה פלטפורמה, בפורמט |a||b|CONFIG::init, ‏PAGE::load, ‏COOKIES, ‏admin_login
nameשם לתצוגה: כפתורי השפה בפאנל ומסך הכניסהForm, ערכת admin_login
directionrtl או 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 ול-keywordsSEO::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']:

  1. התאמה מדויקת של SERVER_NAME לעמודה domain.
  2. אם אין, מעבר על כל הפלטפורמות: התאמה ל-domain או לאחד מ-allowed_domains (פענוח באמצעות MISC::bgl_explode). בהתאמה כזו CONFIG::$env_url נקבע ל-protocol://SERVER_NAME/.
  3. אם עדיין אין, נבחרת הפלטפורמה הראשונה במערך (array_values($platforms)[0]), בלי לוג.

אחרי הבחירה: CONFIG::$site_url הוא protocol://<domain>/ של הפלטפורמה, ‏CONFIG::$original_site_url הוא protocol://HTTP_HOST/, ו-CONFIG::$lang ו-MISC::$GLOBALS["lang"] מקבלים את ה-id.

דומיין לא רשום מגיע ל-&quot;URL ERROR&quot;, ולא לפלטפורמה ריקה

בצד הלקוח, 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
defthemePAGE::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 = 5parentId = 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() רצה בכל בקשת ניהול:

  1. טוענת את כל הפלטפורמות, ממוינות לפי ord, לתוך MISC::$GLOBALS["langs"].
  2. אם $_GET["lang"] הוא מזהה קיים, שומרת אותו בעוגייה CRM_admin_lang (ערך מקודד עם MISC::encode).
  3. אם אין עוגייה, שומרת את הפלטפורמה הראשונה.
  4. מציבה MISC::$GLOBALS["admin_lang_data"] (שורת הפלטפורמה) ו-MISC::$GLOBALS["admin_lang_id"].
  5. מעדכנת 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.
חבילת ה-SEO כן מבדילה בין פלטפורמות

המחלקה SeoPlatform מוסיפה עמודת platform לטבלאות של חבילת ה-SEO ומסננת לפיה, רק באתר שיש בו יותר מפלטפורמה אחת (באתר עם פלטפורמה יחידה השאילתות נשארות זהות). הפלטפורמה הראשונה לפי ord נחשבת ראשית. ראו חבילת ה-SEO ו-SEO למפתחי אתר.

הוספת שפה: צעדים#

  1. מוסיפים פלטפורמה בפאנל {admin}/platforms (שם, דומיין, דומיינים נוספים, כיוון, ערכת ברירת מחדל ושדות ה-SEO). הפאנל מנקה את מטמון platforms בעצמו. אם הוספתם שורה בשאילתה, נקו את המטמון (cache_engine::remove("platforms")).
  2. מפנים את הדומיין החדש לשרת ומוודאים שהוא רשום ב-domain או ב-allowed_domains (אחרת תתקבל URL ERROR).
  3. יוצרים שורות _content עם ה-id החדש כ-langId לכל טבלה רב-לשונית: עמודים, מודולים (modules_content) ומילים (langs_words_content).
  4. אם בערכת הנושא יש טקסטים קבועים, מוודאים שהם עוברים דרך LANGS::get_word ולא כתובים קשיח.
  5. בודקים בדומיין החדש: כיוון העמוד, ערכת הנושא, וש-<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לתקן בערכה או בפילטר פלט
כניסה לפאנל ריקה עם TypeErrorCRM_platforms ריקהלהוסיף שורה ראשונה

ראו גם#

מצאתם טעות או חוסר? תקנו את הדף או פתחו Issue בריפו. התיעוד נכתב מתוך הקוד של ליבה 5.0.115.