เว็บไซต์หลายภาษาแนวปฏิบัติที่ดีที่สุด


179

ฉันดิ้นรนกับคำถามนี้มาหลายเดือนแล้ว แต่ฉันไม่ได้อยู่ในสถานการณ์ที่ฉันต้องสำรวจตัวเลือกทั้งหมดก่อนหน้านี้ ตอนนี้ฉันรู้สึกว่าถึงเวลาที่ต้องทำความรู้จักกับความเป็นไปได้และสร้างความชอบส่วนตัวของฉันเองเพื่อใช้ในโครงการที่กำลังจะมาถึง

ก่อนอื่นให้ฉันเขียนสถานการณ์ที่ฉันกำลังมองหา

ฉันกำลังจะอัพเกรด / พัฒนาระบบการจัดการเนื้อหาซึ่งฉันได้ใช้มาระยะหนึ่งแล้ว อย่างไรก็ตามฉันรู้สึกว่าหลายภาษาเป็นการพัฒนาระบบนี้อย่างยอดเยี่ยม ก่อนที่ฉันจะไม่ใช้เฟรมเวิร์กใด ๆ แต่ฉันจะใช้ Laraval4 สำหรับโครงการที่กำลังจะมาถึง Laravel ดูเหมือนจะเป็นทางเลือกที่ดีที่สุดในการเขียนโค้ด PHP Sidenote: Laraval4 should be no factor in your answer. ฉันกำลังมองหาวิธีการแปลทั่วไปที่เป็นแพลตฟอร์ม / กรอบงานอิสระ

สิ่งที่ควรแปล

เนื่องจากระบบที่ฉันกำลังมองหาต้องการที่จะเป็นมิตรกับผู้ใช้มากที่สุดวิธีการจัดการการแปลควรอยู่ภายใน CMS ไม่จำเป็นต้องเริ่มการเชื่อมต่อ FTP เพื่อแก้ไขไฟล์การแปลหรือเทมเพลตการแยกวิเคราะห์ html / php ใด ๆ

นอกจากนี้ฉันกำลังมองหาวิธีที่ง่ายที่สุดในการแปลหลายตารางฐานข้อมูลบางทีโดยไม่จำเป็นต้องสร้างตารางเพิ่มเติม

ฉันเกิดอะไรขึ้นกับตัวเอง

ขณะที่ฉันค้นหาอ่านและลองทำสิ่งต่าง ๆ ด้วยตัวเองแล้ว มีตัวเลือกสองสามอย่างที่ฉันมี แต่ฉันก็ยังไม่รู้สึกว่าฉันได้วิธีการฝึกฝนที่ดีที่สุดสำหรับสิ่งที่ฉันต้องการจริงๆ ตอนนี้นี่คือสิ่งที่ฉันคิด แต่วิธีนี้ก็มีผลข้างเคียงด้วย

  1. แม่แบบ PHP Parsed : ระบบแม่แบบควรจะแยกวิเคราะห์โดย PHP วิธีนี้ฉันสามารถแทรกพารามิเตอร์ที่แปลแล้วลงใน HTML ได้โดยไม่ต้องเปิดเทมเพลตและแก้ไขมัน นอกจากนั้น PHP แยกแม่แบบให้ฉันมีความสามารถที่จะมี 1 แม่แบบสำหรับเว็บไซต์ที่สมบูรณ์แทนการมีโฟลเดอร์ย่อยสำหรับแต่ละภาษา (ที่ฉันเคยมีมาก่อน) วิธีการเข้าถึงเป้าหมายนี้อาจเป็นได้ทั้ง Smarty, TemplatePower, Blade ของ Laravel หรือตัวแยกวิเคราะห์เทมเพลตอื่น ๆ อย่างที่ฉันบอกไปว่าสิ่งนี้ควรเป็นอิสระต่อโซลูชันที่เป็นลายลักษณ์อักษร
  2. ฐานข้อมูลขับเคลื่อน : บางทีฉันไม่จำเป็นต้องพูดถึงเรื่องนี้อีกครั้ง แต่การแก้ปัญหาควรเป็นฐานข้อมูลขับเคลื่อน CMS มีวัตถุประสงค์เพื่อมุ่งเน้นเชิงวัตถุและ MVC ดังนั้นฉันจะต้องคิดถึงโครงสร้างข้อมูลเชิงตรรกะสำหรับสตริง ในฐานะที่เป็นแม่แบบของฉันจะมีโครงสร้าง: แม่แบบ / ควบคุม / View.php Controller.View.parameterบางทีโครงสร้างนี้จะทำให้ความรู้สึกมากที่สุด: ตารางฐานข้อมูลจะมีข้อมูลเหล่านี้เป็นเวลานานกับvalueฟิลด์ ภายในแม่แบบที่เราสามารถใช้วิธีการเรียงลำดับบางอย่างเช่นและพารามิเตอร์ที่มีecho __('Controller.View.welcome', array('name', 'Joshua')) Welcome, :nameดังนั้นผลลัพธ์ที่Welcome, Joshuaได้ นี่เป็นวิธีที่ดีในการทำเช่นนี้เพราะพารามิเตอร์เช่น: ชื่อนั้นง่ายต่อการเข้าใจโดยบรรณาธิการ
  3. โหลดฐานข้อมูลต่ำ : แน่นอนว่าระบบดังกล่าวจะทำให้โหลดฐานข้อมูลโหลดหากสตริงเหล่านี้จะถูกโหลดในระหว่างการเดินทาง ดังนั้นฉันจะต้องมีระบบแคชที่แสดงผลไฟล์ภาษาอีกครั้งทันทีที่มีการแก้ไข / บันทึกในสภาพแวดล้อมการบริหาร เนื่องจากไฟล์ถูกสร้างขึ้นจึงจำเป็นต้องมีโครงร่างระบบไฟล์ที่ดี ฉันเดาว่าเราสามารถไปด้วยlanguages/en_EN/Controller/View.phpหรือ. ini สิ่งที่เหมาะกับคุณที่สุด บางที. ini จะถูกแยกวิเคราะห์เร็วขึ้นในที่สุด fould format parameter=value; นี้ควรมีข้อมูลในส่วน ฉันเดาว่านี่เป็นวิธีที่ดีที่สุดในการทำเช่นนี้เนื่องจากแต่ละมุมมองที่แสดงผลสามารถรวมเป็นไฟล์ภาษาของตัวเองหากมีอยู่ ควรโหลดพารามิเตอร์ภาษาไปยังมุมมองเฉพาะและไม่อยู่ในขอบเขตส่วนกลางเพื่อป้องกันไม่ให้พารามิเตอร์เขียนทับกัน
  4. การแปลตารางฐานข้อมูล : อันที่จริงสิ่งนี้เป็นสิ่งที่ฉันกังวลมากที่สุด ฉันกำลังมองหาวิธีสร้างการแปลของข่าว / หน้า / ฯลฯ เร็วที่สุดเท่าที่จะทำได้. การมีสองตารางสำหรับแต่ละโมดูล (ตัวอย่างNewsและNews_translations) เป็นตัวเลือก แต่มันรู้สึกอยากทำงานมากเพื่อให้ได้ระบบที่ดี หนึ่งในสิ่งที่ฉันมาด้วยจะขึ้นอยู่กับdata versioningระบบที่ผมเขียน: มีหนึ่งชื่อตารางฐานข้อมูลTranslationsตารางนี้มีการผสมผสานเอกลักษณ์ของlanguage, tablenameและprimarykey. ตัวอย่างเช่น: en_En / News / 1 (อ้างอิงจากรายการข่าวภาษาอังกฤษที่มี ID = 1) แต่มีข้อเสียอย่างใหญ่หลวง 2 ประการสำหรับวิธีนี้: อันดับแรกของตารางทั้งหมดมีแนวโน้มที่จะได้รับข้อมูลจำนวนมากในฐานข้อมูลและอย่างที่สองมันเป็นงานที่ต้องใช้การตั้งค่านี้เพื่อค้นหาตาราง ตัวอย่างเช่นการค้นหาบุ้ง SEO ของรายการจะเป็นการค้นหาข้อความแบบเต็มซึ่งเป็นใบ้สวย แต่ในทางกลับกัน: มันเป็นวิธีที่รวดเร็วในการสร้างเนื้อหาที่แปลได้ในทุก ๆ โต๊ะอย่างรวดเร็ว แต่ฉันไม่เชื่อว่าโปรนี้จะช่วยลดน้ำหนักของคอน
  5. Front-end Work : Front-end ก็ต้องมีความคิดด้วยเช่นกัน แน่นอนว่าเราจะเก็บภาษาที่มีอยู่ในฐานข้อมูลและ (de) ใช้งานภาษาที่เราต้องการ วิธีนี้สคริปต์สามารถสร้างรายการแบบหล่นลงเพื่อเลือกภาษาและส่วนหลังสามารถตัดสินใจได้โดยอัตโนมัติว่าการแปลใดที่สามารถทำได้โดยใช้ CMS ภาษาที่เลือก (เช่น en_EN) จะถูกนำมาใช้เมื่อรับไฟล์ภาษาสำหรับการดูหรือเพื่อให้ได้การแปลที่ถูกต้องสำหรับรายการเนื้อหาในเว็บไซต์

ดังนั้นพวกเขาอยู่ที่นั่น ความคิดของฉันจนถึงตอนนี้ พวกเขายังไม่ได้รวมตัวเลือกการแปลสำหรับวันที่ ฯลฯ แต่เนื่องจากเซิร์ฟเวอร์ของฉันรองรับ PHP5.3.2 + ตัวเลือกที่ดีที่สุดคือการใช้ส่วนขยาย intl ตามที่อธิบายไว้ที่นี่: http://devzone.zend.com/1500/internationalization-in -php-53 / - แต่นี่จะใช้ในสนามกีฬาแห่งการพัฒนาในภายหลัง สำหรับตอนนี้ปัญหาหลักคือทำอย่างไรถึงจะมีแนวปฏิบัติที่ดีที่สุดในการแปลเนื้อหาในเว็บไซต์

นอกจากทุกสิ่งที่ฉันอธิบายไว้ที่นี่ฉันยังมีอีกสิ่งหนึ่งที่ฉันยังไม่ได้ตัดสินใจมันดูเหมือนคำถามง่าย ๆ แต่จริงๆแล้วมันทำให้ฉันปวดหัว:

แปล URL หรือไม่ เราควรทำสิ่งนี้หรือไม่? และในทางใด

ดังนั้น .. ถ้าฉันมี URL นี้http://www.domain.com/about-usและภาษาอังกฤษเป็นภาษาเริ่มต้นของฉัน ควรแปล URL นี้เป็นภาษาของhttp://www.domain.com/over-onsฉันเมื่อฉันเลือกดัชท์เป็นภาษาของฉันหรือไม่ /aboutหรือเราควรจะไปถนนง่ายและก็เปลี่ยนเนื้อหาของที่มองเห็นหน้า สิ่งสุดท้ายดูเหมือนจะเป็นตัวเลือกที่ไม่ถูกต้องเพราะจะสร้าง URL เดียวกันหลายเวอร์ชันการทำดัชนีเนื้อหานี้จะล้มเหลวอย่างถูกวิธี

ตัวเลือกอื่นกำลังใช้http://www.domain.com/nl/about-usแทน สิ่งนี้จะสร้าง URL ที่ไม่ซ้ำกันสำหรับแต่ละเนื้อหา นอกจากนี้สิ่งนี้จะง่ายกว่าในการไปใช้ภาษาอื่นตัวอย่างเช่นhttp://www.domain.com/en/about-usและ URL ที่ให้ไว้นั้นง่ายต่อการเข้าใจสำหรับทั้งผู้เข้าชม Google และคน เมื่อใช้ตัวเลือกนี้เราจะทำอย่างไรกับภาษาเริ่มต้น ภาษาเริ่มต้นควรลบภาษาที่เลือกตามค่าเริ่มต้นหรือไม่ ดังนั้นการเปลี่ยนเส้นทางhttp://www.domain.com/en/about-usไปที่http://www.domain.com/about-us... ในสายตาของฉันนี่เป็นทางออกที่ดีที่สุดเพราะเมื่อตั้งค่า CMS สำหรับภาษาเดียวเท่านั้นไม่จำเป็นต้องมีการระบุภาษานี้ใน URL

และตัวเลือกที่สามคือการรวมกันจากทั้งสองตัวเลือก: ใช้ "language-identification-less" -URL ( http://www.domain.com/about-us) สำหรับภาษาหลัก และใช้ URL ที่มีตัวบุ้ง SEO ที่แปลสำหรับภาษาย่อย: http://www.domain.com/nl/over-ons&http://www.domain.com/de/uber-uns

ฉันหวังว่าคำถามของฉันจะทำให้คุณหัวร้าวพวกเขาก็จะระเบิดแน่นอน! มันช่วยฉันได้แล้วที่จะทำสิ่งต่าง ๆ เป็นคำถามที่นี่ ให้โอกาสฉันในการตรวจสอบวิธีการที่ฉันใช้มาก่อนและความคิดที่ฉันมีสำหรับ CMS ที่กำลังจะมาถึง

ฉันขอขอบคุณที่สละเวลาอ่านข้อความกลุ่มนี้!

// Edit #1:

ฉันลืมพูดถึง: ฟังก์ชั่น __ () เป็นนามแฝงในการแปลสตริงที่กำหนด ภายในวิธีนี้จะเห็นได้ชัดว่าควรมีวิธีการเรียงกลับบางส่วนที่โหลดข้อความเริ่มต้นเมื่อยังไม่มีการแปล หากการแปลหายไปก็ควรจะแทรกหรือแปลไฟล์ใหม่


3
สิ่งที่เกี่ยวกับ gettext php.net/manual/en/book.gettext.php
Francois Bourgeois

คำตอบ:


115

หลักฐานของหัวข้อ

มีสามด้านที่แตกต่างในไซต์ที่พูดได้หลายภาษา:

  • การแปลอินเตอร์เฟซ
  • เนื้อหา
  • การกำหนดเส้นทาง URL

ในขณะที่พวกเขาทั้งหมดเชื่อมต่อกันในรูปแบบที่แตกต่างกันจากมุมมอง CMS พวกเขาจัดการโดยใช้องค์ประกอบ UI ที่แตกต่างกันและเก็บไว้แตกต่างกัน ดูเหมือนว่าคุณจะมีความมั่นใจในการนำไปใช้และความเข้าใจในสองข้อแรก คำถามคือเกี่ยวกับด้านหลัง - "การแปล URL หรือไม่เราควรทำสิ่งนี้หรือไม่และในทางใด"

URL สามารถทำอะไรได้บ้าง

สิ่งที่สำคัญมากคือไม่ได้รับแฟนซีกับIDN แทนที่จะชอบการถอดเสียง (เช่น: การถอดเสียงและการถอดอักษร) ในขณะที่ IDN แรกดูเหมือนว่าตัวเลือกที่ทำงานได้สำหรับ URL ระหว่างประเทศ แต่จริงๆแล้วมันไม่ทำงานตามที่โฆษณาไว้ด้วยเหตุผลสองประการ:

  • เบราว์เซอร์บางตัวจะเปลี่ยนเป็นตัวอักษรที่ไม่ใช่ ASCII เหมือน'ч'หรือ'ž'เป็น'%D1%87'และ'%C5%BE'
  • หากผู้ใช้มีธีมที่กำหนดเองแบบอักษรของธีมน่าจะไม่มีสัญลักษณ์สำหรับตัวอักษรเหล่านั้น

จริง ๆ แล้วฉันพยายามใช้วิธี IDN เมื่อไม่กี่ปีที่ผ่านมาในโปรเจค Yii (เฟรมเวิร์กที่แย่มาก IMHO) ฉันพบปัญหาดังกล่าวข้างต้นทั้งสองก่อนที่จะคัดลอกโซลูชันนั้น นอกจากนี้ฉันสงสัยว่ามันอาจเป็นเวกเตอร์การโจมตี

ตัวเลือกที่มี ... ตามที่ฉันเห็น

โดยทั่วไปคุณมีสองทางเลือกซึ่งอาจแยกออกเป็น:

  • http://site.tld/[:query]: โดย[:query]กำหนดทั้งภาษาและตัวเลือกเนื้อหา

  • http://site.tld/[:language]/[:query]: [:language]ส่วนใดของ URL จะกำหนดตัวเลือกของภาษาและ[:query]ใช้เพื่อระบุเนื้อหาเท่านั้น

ข้อความค้นหาคือΑและΩ ..

http://site.tld/[:query]ช่วยบอกว่าคุณเลือก

ในกรณีนี้คุณมีแหล่งที่มาหลักของภาษาหนึ่ง: เนื้อหาของ[:query]ส่วน; และสองแหล่งเพิ่มเติม:

  • ความคุ้มค่า $_COOKIE['lang']สำหรับเบราว์เซอร์นั้น ๆ
  • รายการภาษาในส่วนหัวHTTP Accept-Language (1) , (2)

ขั้นแรกคุณต้องจับคู่แบบสอบถามกับหนึ่งในรูปแบบการกำหนดเส้นทางที่กำหนด (ถ้าคุณเลือกคือ Laravel จากนั้นอ่านที่นี่ ) ในการจับคู่รูปแบบที่ประสบความสำเร็จคุณจะต้องค้นหาภาษา

คุณจะต้องผ่านทุกส่วนของลวดลาย ค้นหาการแปลที่เป็นไปได้สำหรับกลุ่มเหล่านั้นทั้งหมดและกำหนดภาษาที่ใช้ แหล่งที่มาเพิ่มเติมสองแหล่ง (คุกกี้และส่วนหัว) จะถูกใช้เพื่อแก้ไขข้อขัดแย้งในการกำหนดเส้นทางเมื่อ (ไม่ "ถ้า") เกิดขึ้น

ยกตัวอย่างเช่น: http://site.tld/blog/novinka.

นั่นเป็นทับศัพท์ว่าในภาษาอังกฤษหมายถึงประมาณ"блог, новинка""blog", "latest"

ตามที่คุณสามารถสังเกตเห็นได้ในภาษารัสเซีย "блог" จะถูกถอดเสียงเป็น "บล็อก" ซึ่งหมายความว่าในส่วนแรกของ[:query]คุณ (ในสถานการณ์กรณีที่ดีที่สุด ) จะจบลงด้วย['en', 'ru']รายการภาษาที่เป็นไปได้ จากนั้นคุณจะแบ่งกลุ่มถัดไป - "novinka" นั่นอาจมีเพียงภาษาเดียวในรายการความเป็นไปได้:['ru']ที่อาจมีเพียงภาษาเดียวในรายชื่อของความเป็นไปได้:

เมื่อรายการมีหนึ่งรายการคุณจะพบภาษาได้สำเร็จ

แต่ถ้าคุณจบลงด้วย 2 (ตัวอย่าง: รัสเซียและยูเครน) หรือความเป็นไปได้มากกว่า .. หรือความเป็นไปได้ 0 อย่างแล้วแต่กรณี คุณจะต้องใช้คุกกี้และ / หรือส่วนหัวเพื่อค้นหาตัวเลือกที่ถูกต้อง

และหากทั้งหมดอื่นล้มเหลวคุณเลือกภาษาเริ่มต้นของเว็บไซต์

ภาษาเป็นพารามิเตอร์

ทางเลือกคือใช้ URL ที่สามารถกำหนดเป็น http://site.tld/[:language]/[:query]ที่สามารถกำหนดเป็น ในกรณีนี้เมื่อแปลแบบสอบถามคุณไม่จำเป็นต้องเดาภาษาเพราะ ณ จุดนี้คุณรู้แล้วว่าจะใช้อะไร

นอกจากนี้ยังมีแหล่งที่มาของภาษาที่สอง: ค่าคุกกี้ แต่ที่นี่ไม่มีประเด็นที่จะยุ่งกับส่วนหัว Accept-Language เนื่องจากคุณไม่ได้จัดการกับภาษาที่เป็นไปได้จำนวนมากในกรณีที่ "cold start" (เมื่อผู้ใช้เปิดไซต์ครั้งแรกด้วยแบบสอบถามที่กำหนดเอง)

แต่คุณมี 3 ตัวเลือกที่เรียบง่ายและจัดลำดับความสำคัญดังนี้:

  1. ถ้า[:language]มีการตั้งค่าเซ็กเมนต์ใช้มัน
  2. หาก$_COOKIE['lang']ตั้งไว้ให้ใช้
  3. ใช้ภาษาเริ่มต้น

เมื่อคุณมีภาษาคุณเพียงพยายามแปลแบบสอบถามและหากการแปลล้มเหลวให้ใช้ "ค่าเริ่มต้น" สำหรับส่วนนั้น ๆ (ขึ้นอยู่กับผลการกำหนดเส้นทาง)

นี่ไม่ใช่ตัวเลือกที่สามใช่ไหม

ใช่ในทางเทคนิคคุณสามารถรวมทั้งสองวิธีเข้าด้วยกัน แต่นั่นจะทำให้กระบวนการยุ่งยากและรองรับผู้ที่ต้องการเปลี่ยน URL http://site.tld/en/newsเป็นhttp://site.tld/de/newsและคาดว่าหน้าข่าวจะเปลี่ยนเป็นภาษาเยอรมันด้วยตนเอง

แต่แม้กระทั่งกรณีนี้อาจลดลงได้โดยใช้ค่าคุกกี้ (ซึ่งจะมีข้อมูลเกี่ยวกับตัวเลือกภาษาก่อนหน้า) เพื่อนำไปใช้ด้วยเวทมนตร์และความหวังน้อยลง

วิธีการใช้งานแบบไหน?

ในขณะที่คุณอาจเดาได้แล้วฉันจะแนะนำhttp://site.tld/[:language]/[:query]เป็นตัวเลือกที่เหมาะสมกว่า

นอกจากนี้ในสถานการณ์จริงคุณจะต้องมีส่วนที่ 3 ใน URL: "title" เช่นเดียวกับชื่อของผลิตภัณฑ์ในร้านค้าออนไลน์หรือหัวเรื่องของบทความในเว็บไซต์ข่าว

ตัวอย่าง: http://site.tld/en/news/article/121415/EU-as-global-reserve-currency

ในกรณีนี้'/news/article/121415'จะเป็นแบบสอบถามและ'EU-as-global-reserve-currency'เป็นชื่อ เพียงเพื่อวัตถุประสงค์ SEO

สามารถทำได้ใน Laravel หรือไม่?

ค่อนข้างดี แต่ไม่ใช่โดยค่าเริ่มต้น

ฉันไม่คุ้นเคยกับมันมากนัก แต่จากสิ่งที่ฉันได้เห็น Laravel ใช้กลไกการจัดเส้นทางตามรูปแบบที่เรียบง่าย ในการใช้ URL หลายภาษาคุณอาจต้องขยายคลาสหลัก (es)เนื่องจากการกำหนดเส้นทางหลายภาษาจำเป็นต้องเข้าถึงหน่วยเก็บข้อมูลในรูปแบบต่างๆ (ฐานข้อมูลแคชและ / หรือไฟล์การกำหนดค่า)

มันเป็นเส้นทาง เกิดอะไรขึ้น

จากผลลัพธ์ทั้งหมดคุณจะได้ข้อมูลที่มีค่าสองชิ้น: ภาษาปัจจุบันและเซ็กเมนต์ของคิวรีที่แปล ค่าเหล่านี้สามารถใช้ในการส่งไปยังคลาสซึ่งจะให้ผลลัพธ์

โดยทั่วไปแล้ว URL ต่อไปนี้: http://site.tld/ru/blog/novinka(หรือรุ่นที่ไม่มี'/ru') จะกลายเป็นสิ่งที่ต้องการ

$parameters = [
   'language' => 'ru',
   'classname' => 'blog',
   'method' => 'latest',
];

สิ่งที่คุณเพิ่งใช้สำหรับเยี่ยงอย่าง:

$instance = new {$parameter['classname']};
$instance->{'get'.$parameters['method']}( $parameters );

.. หรือรูปแบบบางส่วนของมันขึ้นอยู่กับการใช้งานเฉพาะ


1
ขอบคุณสำหรับความเข้าใจด้านอื่น! คิดมากมาก! ฉันคิดว่าจะมีพารามิเตอร์ภาษาใน URL เช่นกัน นี่เป็นวิธีที่ดีที่สุดในการระบุภาษาเฉพาะไม่เพียง แต่สำหรับผู้ใช้ แต่เพื่อวัตถุประสงค์ด้าน SEO เช่นกัน ในกรณีที่ผู้ใช้เปลี่ยน / en / ข่าวเป็น / de / ข่าวความคิดของฉันคือการเปลี่ยนเส้นทาง 301 (ถาวร) ไปที่ / de / nachrichten เพียงเพื่อให้แน่ใจว่าแต่ละภาษามี URL ที่ไม่ซ้ำกันต่อหน้า (อีกครั้งเพื่อจุดประสงค์ด้าน SEO)
Joshua - Pendo

มันยากขึ้นเรื่อย ๆ ในการเลือกคำตอบที่ดีที่สุดขณะนี้มีคำตอบประมาณ 3/4 คำที่สมควรได้รับอย่างน้อยส่วนหนึ่งของความโปรดปราน รวมพวกเขากลายเป็นคำตอบที่มั่นคงกับทุกสิ่งที่ฉันต้องการเคลียร์ด้วยกัน :)
Joshua - Pendo

ฉันยอมรับคำตอบของคุณเพื่อให้ตัวแทนอย่างน้อยที่สุดสำหรับคำตอบโดยละเอียดที่คุณให้ไว้ในการแปล URL ชื่นชมอย่างมาก! อย่างไรก็ตามความโปรดปรานนั้นมอบให้กับบุคคลที่อยู่ด้านล่างของคุณเนื่องจากเขาตอบทุกแง่มุมของคำถามของฉันในแบบที่เป็นอิสระ
Joshua - Pendo

52

การนำ i18n ไปใช้โดยไม่กระทบต่อประสิทธิภาพโดยใช้ตัวประมวลผลล่วงหน้าตามที่ Thomas Bley แนะนำ

ที่ทำงานเมื่อเร็ว ๆ นี้เราได้ทำการติดตั้ง i18n บนคุณสมบัติของเราสองสามอย่างและสิ่งหนึ่งที่เราพยายามอย่างต่อเนื่องคือประสิทธิภาพของการรับมือกับการแปลแบบทันทีทันใดจากนั้นฉันก็ค้นพบโพสต์บล็อกยอดเยี่ยมนี้โดย Thomas Bleyซึ่งเป็นแรงบันดาลใจให้กับวิธีการที่เราใช้ i18n เพื่อรองรับปริมาณการใช้งานที่มากและปัญหาด้านประสิทธิภาพที่น้อยที่สุด

แทนที่จะเรียกฟังก์ชั่นสำหรับการดำเนินการแปลทุกอย่างซึ่งเรารู้ว่าใน PHP มีราคาแพงเรากำหนดไฟล์ฐานของเราพร้อมตัวยึดตำแหน่งจากนั้นใช้ตัวประมวลผลล่วงหน้าเพื่อแคชไฟล์เหล่านั้น (เราเก็บเวลาการแก้ไขไฟล์เพื่อให้แน่ใจว่า เนื้อหาล่าสุดตลอดเวลา)

แท็กการแปล

Thomas ใช้{tr}และ{/tr}แท็กเพื่อกำหนดตำแหน่งที่การแปลเริ่มต้นและสิ้นสุด เนื่องจากความจริงที่ว่าเรากำลังใช้กิ่งที่เราไม่ต้องการที่จะใช้{ให้เกิดความสับสนหลีกเลี่ยงเพื่อให้เราใช้[%tr%]และ[%/tr%]แทน โดยพื้นฐานแล้วจะมีลักษณะเช่นนี้:

`return [%tr%]formatted_value[%/tr%];`

โปรดทราบว่าโทมัสแนะนำให้ใช้ภาษาอังกฤษพื้นฐานในไฟล์ เราไม่ทำเช่นนี้เพราะเราไม่ต้องการแก้ไขไฟล์แปลทั้งหมดหากเราเปลี่ยนค่าเป็นภาษาอังกฤษ

ไฟล์ INI

จากนั้นเราสร้างไฟล์ INI สำหรับแต่ละภาษาในรูปแบบplaceholder = translated:

// lang/fr.ini
formatted_value = number_format($value * Model_Exchange::getEurRate(), 2, ',', ' ') . '€'

// lang/en_gb.ini
formatted_value = '£' . number_format($value * Model_Exchange::getStgRate())

// lang/en_us.ini
formatted_value = '$' . number_format($value)

มันจะเล็กน้อยเพื่อช่วยให้ผู้ใช้ในการปรับเปลี่ยนเหล่านี้ภายใน CMS เพียงได้รับ keypairs โดยpreg_splitใน\nหรือ=และทำให้ CMS ที่สามารถเขียนไฟล์ INI

ส่วนประกอบตัวประมวลผลล่วงหน้า

โดยพื้นฐานแล้วโทมัสแนะนำให้ใช้ฟังก์ชั่น 'คอมไพเลอร์' (แต่ตามความจริงแล้วมันเป็นตัวประมวลผลล่วงหน้า) เช่นนี้เพื่อใช้ไฟล์แปลของคุณและสร้างไฟล์ PHP แบบคงที่บนดิสก์ ด้วยวิธีนี้เราจะแคชไฟล์ที่แปลของเราเป็นหลักแทนที่จะเรียกใช้ฟังก์ชั่นการแปลสำหรับทุกสตริงในไฟล์:

// This function was written by Thomas Bley, not by me
function translate($file) {
  $cache_file = 'cache/'.LANG.'_'.basename($file).'_'.filemtime($file).'.php';
  // (re)build translation?
  if (!file_exists($cache_file)) {
    $lang_file = 'lang/'.LANG.'.ini';
    $lang_file_php = 'cache/'.LANG.'_'.filemtime($lang_file).'.php';

    // convert .ini file into .php file
    if (!file_exists($lang_file_php)) {
      file_put_contents($lang_file_php, '<?php $strings='.
        var_export(parse_ini_file($lang_file), true).';', LOCK_EX);
    }
    // translate .php into localized .php file
    $tr = function($match) use (&$lang_file_php) {
      static $strings = null;
      if ($strings===null) require($lang_file_php);
      return isset($strings[ $match[1] ]) ? $strings[ $match[1] ] : $match[1];
    };
    // replace all {t}abc{/t} by tr()
    file_put_contents($cache_file, preg_replace_callback(
      '/\[%tr%\](.*?)\[%\/tr%\]/', $tr, file_get_contents($file)), LOCK_EX);
  }
  return $cache_file;
}

หมายเหตุ: ฉันไม่ได้ตรวจสอบว่า regex ทำงานได้ฉันไม่ได้คัดลอกมาจากเซิร์ฟเวอร์ บริษัท ของเรา แต่คุณสามารถดูวิธีการทำงาน

วิธีการโทร

อีกตัวอย่างนี้มาจาก Thomas Bley ไม่ใช่จากฉัน:

// instead of
require("core/example.php");
echo (new example())->now();

// we write
define('LANG', 'en_us');
require(translate('core/example.php'));
echo (new example())->now();

เราเก็บภาษาไว้ในคุกกี้ (หรือตัวแปรเซสชั่นหากเราไม่สามารถรับคุกกี้ได้) จากนั้นเรียกดูมันในทุกคำขอ คุณสามารถรวมสิ่งนี้กับ$_GETพารามิเตอร์ที่เป็นทางเลือกเพื่อแทนที่ภาษา แต่ฉันไม่แนะนำให้ใช้โดเมนย่อยต่อภาษาหรือหน้าต่อภาษาเพราะจะทำให้ยากต่อการดูว่าหน้าใดที่เป็นที่นิยมและจะลดค่าของขาเข้า ลิงก์ที่คุณจะทำให้พวกเขาแพร่กระจายได้ยากขึ้น

ทำไมต้องใช้วิธีนี้

เราชอบวิธีการประมวลผลล่วงหน้าด้วยเหตุผลสามประการ:

  1. ประสิทธิภาพที่เพิ่มขึ้นอย่างมหาศาลจากการไม่เรียกใช้ฟังก์ชั่นทั้งหมดสำหรับเนื้อหาที่ไม่ค่อยมีการเปลี่ยนแปลง (ด้วยระบบนี้ผู้เข้าชม 100k ในภาษาฝรั่งเศสจะยังคงทำงานแทนการแปลเพียงครั้งเดียวเท่านั้น)
  2. มันไม่เพิ่มการโหลดใด ๆ ลงในฐานข้อมูลของเราเนื่องจากมันใช้ไฟล์แบบเรียบง่ายและเป็นโซลูชัน PHP ที่บริสุทธิ์
  3. ความสามารถในการใช้นิพจน์ PHP ในการแปลของเรา

การรับเนื้อหาฐานข้อมูลที่แปลแล้ว

เราเพิ่งเพิ่มคอลัมน์สำหรับเนื้อหาในฐานข้อมูลของเราที่เรียกว่าlanguageจากนั้นเราใช้วิธีการเข้าถึงสำหรับLANGค่าคงที่ที่เรากำหนดไว้ก่อนหน้านี้ดังนั้นการเรียก SQL ของเรา (โดยใช้ ZF1, เศร้า) มีลักษณะดังนี้:

$query = select()->from($this->_name)
                 ->where('language = ?', User::getLang())
                 ->where('id       = ?', $articleId)
                 ->limit(1);

บทความของเรามีคีย์หลักแบบผสมidและlanguageบทความ54สามารถมีได้ในทุกภาษา LANGค่าเริ่มต้นของเราen_USหากไม่ได้ระบุไว้

URL Slug Translation

ฉันจะรวมสองสิ่งที่นี่หนึ่งคือฟังก์ชั่นใน bootstrap ของคุณซึ่งยอมรับ$_GETพารามิเตอร์สำหรับภาษาและแทนที่ตัวแปรคุกกี้และอื่น ๆ คือเส้นทางที่ยอมรับหลายตัวบุ้ง จากนั้นคุณสามารถทำสิ่งนี้ในเส้นทางของคุณ:

"/wilkommen" => "/welcome/lang/de"
... etc ...

สิ่งเหล่านี้สามารถเก็บไว้ในไฟล์แบนซึ่งสามารถเขียนได้อย่างง่ายดายจากแผงผู้ดูแลระบบของคุณ JSON หรือ XML อาจจัดเตรียมโครงสร้างที่ดีสำหรับการสนับสนุน

หมายเหตุเกี่ยวกับตัวเลือกอื่นไม่กี่

การแปล On-The-Fly บนพื้นฐาน PHP

ฉันไม่เห็นว่าข้อเสนอเหล่านี้มีประโยชน์ใด ๆ มากกว่าการแปลที่ดำเนินการล่วงหน้า

การแปลโดยใช้ Front-end

ฉันพบว่าสิ่งเหล่านี้น่าสนใจมานาน แต่มีข้อแม้อยู่บ้าง ตัวอย่างเช่นคุณต้องให้ผู้ใช้สามารถดูรายการวลีทั้งหมดในเว็บไซต์ของคุณที่คุณวางแผนที่จะแปลซึ่งอาจเป็นปัญหาได้หากมีพื้นที่ของเว็บไซต์ที่คุณซ่อนอยู่หรือไม่อนุญาตให้เข้าถึง

คุณต้องสมมติว่าผู้ใช้ของคุณทุกคนเต็มใจและสามารถใช้ Javascript บนเว็บไซต์ของคุณ แต่จากสถิติของฉันผู้ใช้ราว 2.5% ของเรากำลังทำงานอยู่โดยที่ไม่ต้องใช้มัน (หรือใช้ Noscript เพื่อบล็อกเว็บไซต์ของเราจากการใช้งาน) .

การแปลที่ขับเคลื่อนด้วยฐานข้อมูล

ความเร็วในการเชื่อมต่อฐานข้อมูลของ PHP นั้นไม่มีอะไรจะเขียนถึงบ้านและนี่เป็นการเพิ่มค่าใช้จ่ายในการเรียกฟังก์ชั่นให้กับทุกวลีที่ต้องการแปล ปัญหาด้านประสิทธิภาพและความสามารถในการปรับขยายได้ดูเหมือนจะครอบงำด้วยวิธีการนี้


ฉันเห็นว่าฉันสับสนคุณกับ "การแปลส่วนหน้า" สิ่งที่ฉันหมายถึงคือวิธีแยกวิเคราะห์สตริงที่แปลบนหน้าจอ ฉันไม่ได้มองหาวิธีที่จะแปลมันในด้านลูกค้า! สิ่งที่ฉันหมายถึงเป็นวิธีที่ง่ายที่สุดในการสลับภาษาในส่วนหน้า แต่ก็เห็นได้ชัดว่าใช้คุกกี้หรือการตั้งค่าของผู้ใช้ :)
Joshua - Pendo

โอ้และด้วย Database-Driven ฉันมุ่งเน้นไปที่วิธีการจัดการการแปลทั้งหมดดังนั้นทางออกในอุดมคติของฉันคือ back-end ที่เขียนการแปลไปยังฐานข้อมูลตามด้วยฟังก์ชั่นที่สร้างส่วนประกอบการประมวลผลล่วงหน้าซึ่งสร้าง PHP ไฟล์. Why?: ง่าย .. ฉันไม่ต้องการที่จะรำคาญกับการเปลี่ยนแปลงเล็ก ๆ น้อย ๆ ของข้อความผู้ใช้ควรสามารถทำได้ด้วยตนเองโดยไม่ต้องใช้โปรแกรมแก้ไขโค้ดและ / หรือโปรแกรม ftp :)
Joshua - Pendo

@PENDO ฉันรู้ว่าคุณไม่ได้หมายถึงการแปลส่วนหน้านั่นเป็นความคิดเห็นที่คลุมถูกผู้ใช้ที่แนะนำกรอบการแปลส่วนหน้าโดยใช้ JS ;)
ทันทีทันใดความปรารถนา

@PENDO ฉันเห็นด้วยฉันจะใช้ส่วนแบ็คเอนด์ตามที่คุณแนะนำ แต่แทนที่จะเป็นฐานข้อมูลที่ฉันใช้ไฟล์แฟลตเพื่อเหตุผลด้านประสิทธิภาพ แน่นอนข้อเสนอแนะหลักที่นี่คือการแสดงผลแม่แบบล่วงหน้าเมื่อมีการเปลี่ยนแปลงดังนั้นคุณสามารถแทนที่.INIไฟล์ด้วยตารางฐานข้อมูล 3 คอลัมน์ด้วยplaceholder , ,replacement languageที่สำคัญ Compound บนและplaceholder languageจากนั้นมีอีก 2 คอลัมน์ด้วยtempfile(พา ธ ไปยังเทมเพลต) และmodified(DATETIME)
ทันทีทันใดความปรารถนา

1
@PENDO ขอบคุณ ฉันได้สำรอง 250 และฉันวางแผนที่จะให้รางวัลเป็น teresko ใน 24 ชั่วโมงเมื่อเว็บไซต์ให้ฉันเมื่อคุณเลือกทั้งสองคำตอบที่ถูกต้องและฉันคิดว่าการแยกจะแสดงถึงความตั้งใจของคุณได้ดีที่สุด
Glitch Desire

15

ฉันขอแนะนำให้คุณไม่ประดิษฐ์วงล้อและใช้ตัวย่อภาษา gettext และ ISO คุณเคยเห็นวิธีการนำ i18n / l10n มาใช้ใน CMS หรือเฟรมเวิร์กยอดนิยมหรือไม่?

การใช้ gettext คุณจะมีเครื่องมือที่มีประสิทธิภาพซึ่งหลายกรณีถูกนำไปใช้แล้วเช่นรูปแบบพหูพจน์ ในภาษาอังกฤษคุณมีเพียง 2 ตัวเลือก: เอกพจน์และพหูพจน์ แต่ในภาษารัสเซียมี 3 รูปแบบและไม่ง่ายเหมือนภาษาอังกฤษ

นักแปลหลายคนมีประสบการณ์ในการทำงานกับ gettext แล้ว

ดูที่CakePHPหรือDrupal Drupalเปิดใช้งานทั้งสองภาษา CakePHP เป็นตัวอย่างของการแปลอินเตอร์เฟสและ Drupal เป็นตัวอย่างของการแปลเนื้อหา

สำหรับ l10n ที่ใช้ฐานข้อมูลไม่ได้เกิดขึ้นเลย มันจะเป็นข้อสงสัยมากมาย วิธีการมาตรฐานคือการรับข้อมูล l10n ทั้งหมดในหน่วยความจำในระยะเริ่มต้น (หรือระหว่างการเรียกใช้ฟังก์ชัน i10n เป็นครั้งแรกหากคุณต้องการโหลดแบบขี้เกียจ) มันสามารถอ่านได้จากไฟล์. po หรือจากฐานข้อมูลทั้งหมดในครั้งเดียว และมากกว่าแค่อ่านสตริงที่ร้องขอจากอาร์เรย์

หากคุณต้องการใช้เครื่องมือออนไลน์เพื่อแปลส่วนต่อประสานคุณสามารถมีข้อมูลทั้งหมดในฐานข้อมูลได้ แต่ยังคงบันทึกข้อมูลทั้งหมดลงในไฟล์เพื่อทำงานกับมัน เพื่อลดปริมาณข้อมูลในหน่วยความจำคุณสามารถแบ่งข้อความ / สตริงที่แปลแล้วทั้งหมดออกเป็นกลุ่มและโหลดได้เฉพาะกลุ่มที่คุณต้องการหากเป็นไปได้

ดังนั้นคุณจึงถูกต้องใน # 3 ของคุณ โดยมีข้อยกเว้นหนึ่งข้อ: โดยปกติแล้วจะเป็นไฟล์ขนาดใหญ่หนึ่งไฟล์ไม่ใช่ไฟล์ต่อคอนโทรลเลอร์ เพราะเป็นการดีที่สุดสำหรับประสิทธิภาพในการเปิดไฟล์เดียว คุณอาจรู้ว่าบางเว็บที่มีโหลดสูงรวบรวมโค้ด PHP ทั้งหมดไว้ในไฟล์เดียวเพื่อหลีกเลี่ยงการทำงานของไฟล์เมื่อมี / ต้องการเรียกใช้

เกี่ยวกับ URL Google แนะนำให้ใช้การแปลทางอ้อม :

เพื่อระบุเนื้อหาภาษาฝรั่งเศสอย่างชัดเจน: http://example.ca/fr/vélo-de-montagne.html

ฉันคิดว่าคุณต้องเปลี่ยนเส้นทางผู้ใช้ไปยังคำนำหน้าภาษาเริ่มต้นเช่น http://examlpe.com/about-usจะเปลี่ยนเส้นทางไปที่http://examlpe.com/en/about-us แต่ถ้าเว็บไซต์ของคุณใช้ภาษาเดียว ไม่ต้องการคำนำหน้าเลย

ชำระเงิน: http://www.audiomicro.com/trailer-hit-impact-psychodrama-sound-effects-836925 http://nl.audiomicro.com/aanhangwagen-hit-effect-psychodrama-geluidseffecten-836925 http: / /de.audiomicro.com/anhanger-hit-auswirkungen-psychodrama-sound-effekte-836925

การแปลเนื้อหาเป็นงานที่ยากขึ้น ฉันคิดว่ามันจะมีความแตกต่างบางอย่างกับเนื้อหาประเภทต่าง ๆ เช่นบทความรายการเมนู ฯลฯ แต่ใน # 4 คุณคิดถูกแล้ว ดูใน Drupal เพื่อรับแนวคิดเพิ่มเติม มันมี DB schema ที่ชัดเจนเพียงพอและอินเทอร์เฟซที่ดีเพียงพอสำหรับการแปล เช่นเดียวกับคุณสร้างบทความและเลือกภาษาสำหรับมัน และคุณสามารถแปลเป็นภาษาอื่นได้ในภายหลัง

อินเตอร์เฟซการแปล Drupal

ฉันคิดว่ามันไม่ได้มีปัญหากับทาก URL คุณสามารถสร้างตารางแยกต่างหากสำหรับทากและมันจะเป็นการตัดสินใจที่ถูกต้อง การใช้ดัชนีที่ถูกต้องไม่ใช่ปัญหาในการสืบค้นตารางแม้จะมีข้อมูลจำนวนมาก และมันก็ไม่ใช่การค้นหาข้อความแบบเต็ม แต่การจับคู่สตริงถ้าจะใช้ประเภทข้อมูล varchar สำหรับกระสุนและคุณสามารถมีดัชนีในฟิลด์นั้นได้เช่นกัน

ป.ล. ขออภัยภาษาอังกฤษของฉันยังห่างไกลจากความสมบูรณ์แบบ


ขอบคุณสำหรับเวลาที่ตอบคำถามของฉัน ภาษาอังกฤษของคุณดีพอที่ฉันจะเข้าใจ! ฉันจะ +1 คุณแล้วสำหรับความพยายามของคุณ!
Joshua - Pendo

Yaroslav ขอขอบคุณสำหรับคำตอบของคุณ อย่างไรก็ตามฉันไปพร้อมคำตอบอีก 2 คำตอบว่าจะมีความสมบูรณ์มากขึ้นและอธิบายวิธีการที่ใช้หลังโค้ดแทนการชี้ให้เห็นว่ามันมีอยู่แล้ว
Joshua - Pendo

2
ไม่มีปัญหา. แน่นอนว่าคำตอบนั้นสมบูรณ์และน่าสนใจสำหรับฉันเช่นกัน แต่ฉันหวังว่าคุณจะได้ประโยชน์จากคำตอบของฉันเช่นกัน
Yaroslav

12

ขึ้นอยู่กับว่าเว็บไซต์ของคุณมีเนื้อหามากน้อยเพียงใด ตอนแรกฉันใช้ฐานข้อมูลเช่นเดียวกับคนอื่น ๆ ที่นี่ แต่อาจใช้เวลานานในการเขียนสคริปต์การทำงานทั้งหมดของฐานข้อมูล ฉันไม่ได้บอกว่านี่เป็นวิธีการในอุดมคติและโดยเฉพาะอย่างยิ่งถ้าคุณมีข้อความจำนวนมาก แต่ถ้าคุณต้องการที่จะทำมันอย่างรวดเร็วโดยไม่ต้องใช้ฐานข้อมูลวิธีนี้สามารถใช้ได้แม้ว่าคุณจะไม่อนุญาตให้ผู้ใช้ป้อนข้อมูล ซึ่งจะใช้เป็นไฟล์แปล แต่ถ้าคุณเพิ่มคำแปลด้วยตัวเองมันจะทำงาน:

สมมติว่าคุณมีข้อความนี้:

Welcome!

คุณสามารถป้อนข้อมูลนี้ในฐานข้อมูลพร้อมคำแปล แต่คุณสามารถทำสิ่งนี้ได้:

$welcome = array(
"English"=>"Welcome!",
"German"=>"Willkommen!",
"French"=>"Bienvenue!",
"Turkish"=>"Hoşgeldiniz!",
"Russian"=>"Добро пожаловать!",
"Dutch"=>"Welkom!",
"Swedish"=>"Välkommen!",
"Basque"=>"Ongietorri!",
"Spanish"=>"Bienvenito!"
"Welsh"=>"Croeso!");

ตอนนี้หากเว็บไซต์ของคุณใช้คุกกี้คุณมีสิ่งนี้เป็นตัวอย่าง:

$_COOKIE['language'];

เพื่อให้ง่ายขึ้นลองแปลงเป็นรหัสที่สามารถใช้งานได้ง่าย:

$language=$_COOKIE['language'];

หากภาษาคุกกี้ของคุณเป็นภาษาเวลส์และคุณมีรหัสชิ้นนี้:

echo $welcome[$language];

ผลลัพธ์ของสิ่งนี้จะเป็น:

Croeso!

หากคุณต้องการเพิ่มการแปลจำนวนมากสำหรับเว็บไซต์ของคุณและฐานข้อมูลมีการใช้งานมากเกินไปการใช้อาเรย์อาจเป็นทางออกที่ดีที่สุด


1
นี่ไม่ใช่ที่ใกล้คำตอบที่ฉันขอ นอกจากแทนที่จะให้ทุกภาษาพร้อมใช้งานในแต่ละหน้าคุณควรสร้างไฟล์lang.en.phpที่รวมอยู่และใช้$lang['welcome']ซึ่งประกาศไว้ในแต่ละไฟล์
Joshua - Pendo

7

ฉันจะแนะนำให้คุณไม่ต้องพึ่งพาฐานข้อมูลสำหรับการแปลมันอาจเป็นงานที่ยุ่งเหยิงจริงๆและอาจเป็นปัญหาร้ายแรงในกรณีที่มีการเข้ารหัสข้อมูล

ฉันประสบปัญหาคล้ายกันเมื่อก่อนแล้วและเขียนชั้นเรียนต่อไปนี้เพื่อแก้ปัญหาของฉัน

วัตถุ: Locale \ Locale

<?php

  namespace Locale;

  class Locale{

// Following array stolen from Zend Framework
public $country_to_locale = array(
    'AD' => 'ca_AD',
    'AE' => 'ar_AE',
    'AF' => 'fa_AF',
    'AG' => 'en_AG',
    'AI' => 'en_AI',
    'AL' => 'sq_AL',
    'AM' => 'hy_AM',
    'AN' => 'pap_AN',
    'AO' => 'pt_AO',
    'AQ' => 'und_AQ',
    'AR' => 'es_AR',
    'AS' => 'sm_AS',
    'AT' => 'de_AT',
    'AU' => 'en_AU',
    'AW' => 'nl_AW',
    'AX' => 'sv_AX',
    'AZ' => 'az_Latn_AZ',
    'BA' => 'bs_BA',
    'BB' => 'en_BB',
    'BD' => 'bn_BD',
    'BE' => 'nl_BE',
    'BF' => 'mos_BF',
    'BG' => 'bg_BG',
    'BH' => 'ar_BH',
    'BI' => 'rn_BI',
    'BJ' => 'fr_BJ',
    'BL' => 'fr_BL',
    'BM' => 'en_BM',
    'BN' => 'ms_BN',
    'BO' => 'es_BO',
    'BR' => 'pt_BR',
    'BS' => 'en_BS',
    'BT' => 'dz_BT',
    'BV' => 'und_BV',
    'BW' => 'en_BW',
    'BY' => 'be_BY',
    'BZ' => 'en_BZ',
    'CA' => 'en_CA',
    'CC' => 'ms_CC',
    'CD' => 'sw_CD',
    'CF' => 'fr_CF',
    'CG' => 'fr_CG',
    'CH' => 'de_CH',
    'CI' => 'fr_CI',
    'CK' => 'en_CK',
    'CL' => 'es_CL',
    'CM' => 'fr_CM',
    'CN' => 'zh_Hans_CN',
    'CO' => 'es_CO',
    'CR' => 'es_CR',
    'CU' => 'es_CU',
    'CV' => 'kea_CV',
    'CX' => 'en_CX',
    'CY' => 'el_CY',
    'CZ' => 'cs_CZ',
    'DE' => 'de_DE',
    'DJ' => 'aa_DJ',
    'DK' => 'da_DK',
    'DM' => 'en_DM',
    'DO' => 'es_DO',
    'DZ' => 'ar_DZ',
    'EC' => 'es_EC',
    'EE' => 'et_EE',
    'EG' => 'ar_EG',
    'EH' => 'ar_EH',
    'ER' => 'ti_ER',
    'ES' => 'es_ES',
    'ET' => 'en_ET',
    'FI' => 'fi_FI',
    'FJ' => 'hi_FJ',
    'FK' => 'en_FK',
    'FM' => 'chk_FM',
    'FO' => 'fo_FO',
    'FR' => 'fr_FR',
    'GA' => 'fr_GA',
    'GB' => 'en_GB',
    'GD' => 'en_GD',
    'GE' => 'ka_GE',
    'GF' => 'fr_GF',
    'GG' => 'en_GG',
    'GH' => 'ak_GH',
    'GI' => 'en_GI',
    'GL' => 'iu_GL',
    'GM' => 'en_GM',
    'GN' => 'fr_GN',
    'GP' => 'fr_GP',
    'GQ' => 'fan_GQ',
    'GR' => 'el_GR',
    'GS' => 'und_GS',
    'GT' => 'es_GT',
    'GU' => 'en_GU',
    'GW' => 'pt_GW',
    'GY' => 'en_GY',
    'HK' => 'zh_Hant_HK',
    'HM' => 'und_HM',
    'HN' => 'es_HN',
    'HR' => 'hr_HR',
    'HT' => 'ht_HT',
    'HU' => 'hu_HU',
    'ID' => 'id_ID',
    'IE' => 'en_IE',
    'IL' => 'he_IL',
    'IM' => 'en_IM',
    'IN' => 'hi_IN',
    'IO' => 'und_IO',
    'IQ' => 'ar_IQ',
    'IR' => 'fa_IR',
    'IS' => 'is_IS',
    'IT' => 'it_IT',
    'JE' => 'en_JE',
    'JM' => 'en_JM',
    'JO' => 'ar_JO',
    'JP' => 'ja_JP',
    'KE' => 'en_KE',
    'KG' => 'ky_Cyrl_KG',
    'KH' => 'km_KH',
    'KI' => 'en_KI',
    'KM' => 'ar_KM',
    'KN' => 'en_KN',
    'KP' => 'ko_KP',
    'KR' => 'ko_KR',
    'KW' => 'ar_KW',
    'KY' => 'en_KY',
    'KZ' => 'ru_KZ',
    'LA' => 'lo_LA',
    'LB' => 'ar_LB',
    'LC' => 'en_LC',
    'LI' => 'de_LI',
    'LK' => 'si_LK',
    'LR' => 'en_LR',
    'LS' => 'st_LS',
    'LT' => 'lt_LT',
    'LU' => 'fr_LU',
    'LV' => 'lv_LV',
    'LY' => 'ar_LY',
    'MA' => 'ar_MA',
    'MC' => 'fr_MC',
    'MD' => 'ro_MD',
    'ME' => 'sr_Latn_ME',
    'MF' => 'fr_MF',
    'MG' => 'mg_MG',
    'MH' => 'mh_MH',
    'MK' => 'mk_MK',
    'ML' => 'bm_ML',
    'MM' => 'my_MM',
    'MN' => 'mn_Cyrl_MN',
    'MO' => 'zh_Hant_MO',
    'MP' => 'en_MP',
    'MQ' => 'fr_MQ',
    'MR' => 'ar_MR',
    'MS' => 'en_MS',
    'MT' => 'mt_MT',
    'MU' => 'mfe_MU',
    'MV' => 'dv_MV',
    'MW' => 'ny_MW',
    'MX' => 'es_MX',
    'MY' => 'ms_MY',
    'MZ' => 'pt_MZ',
    'NA' => 'kj_NA',
    'NC' => 'fr_NC',
    'NE' => 'ha_Latn_NE',
    'NF' => 'en_NF',
    'NG' => 'en_NG',
    'NI' => 'es_NI',
    'NL' => 'nl_NL',
    'NO' => 'nb_NO',
    'NP' => 'ne_NP',
    'NR' => 'en_NR',
    'NU' => 'niu_NU',
    'NZ' => 'en_NZ',
    'OM' => 'ar_OM',
    'PA' => 'es_PA',
    'PE' => 'es_PE',
    'PF' => 'fr_PF',
    'PG' => 'tpi_PG',
    'PH' => 'fil_PH',
    'PK' => 'ur_PK',
    'PL' => 'pl_PL',
    'PM' => 'fr_PM',
    'PN' => 'en_PN',
    'PR' => 'es_PR',
    'PS' => 'ar_PS',
    'PT' => 'pt_PT',
    'PW' => 'pau_PW',
    'PY' => 'gn_PY',
    'QA' => 'ar_QA',
    'RE' => 'fr_RE',
    'RO' => 'ro_RO',
    'RS' => 'sr_Cyrl_RS',
    'RU' => 'ru_RU',
    'RW' => 'rw_RW',
    'SA' => 'ar_SA',
    'SB' => 'en_SB',
    'SC' => 'crs_SC',
    'SD' => 'ar_SD',
    'SE' => 'sv_SE',
    'SG' => 'en_SG',
    'SH' => 'en_SH',
    'SI' => 'sl_SI',
    'SJ' => 'nb_SJ',
    'SK' => 'sk_SK',
    'SL' => 'kri_SL',
    'SM' => 'it_SM',
    'SN' => 'fr_SN',
    'SO' => 'sw_SO',
    'SR' => 'srn_SR',
    'ST' => 'pt_ST',
    'SV' => 'es_SV',
    'SY' => 'ar_SY',
    'SZ' => 'en_SZ',
    'TC' => 'en_TC',
    'TD' => 'fr_TD',
    'TF' => 'und_TF',
    'TG' => 'fr_TG',
    'TH' => 'th_TH',
    'TJ' => 'tg_Cyrl_TJ',
    'TK' => 'tkl_TK',
    'TL' => 'pt_TL',
    'TM' => 'tk_TM',
    'TN' => 'ar_TN',
    'TO' => 'to_TO',
    'TR' => 'tr_TR',
    'TT' => 'en_TT',
    'TV' => 'tvl_TV',
    'TW' => 'zh_Hant_TW',
    'TZ' => 'sw_TZ',
    'UA' => 'uk_UA',
    'UG' => 'sw_UG',
    'UM' => 'en_UM',
    'US' => 'en_US',
    'UY' => 'es_UY',
    'UZ' => 'uz_Cyrl_UZ',
    'VA' => 'it_VA',
    'VC' => 'en_VC',
    'VE' => 'es_VE',
    'VG' => 'en_VG',
    'VI' => 'en_VI',
    'VN' => 'vn_VN',
    'VU' => 'bi_VU',
    'WF' => 'wls_WF',
    'WS' => 'sm_WS',
    'YE' => 'ar_YE',
    'YT' => 'swb_YT',
    'ZA' => 'en_ZA',
    'ZM' => 'en_ZM',
    'ZW' => 'sn_ZW'
);

/**
 * Store the transaltion for specific languages
 *
 * @var array
 */
protected $translation = array();

/**
 * Current locale
 *
 * @var string
 */
protected $locale;

/**
 * Default locale
 *
 * @var string
 */
protected $default_locale;

/**
 *
 * @var string
 */
protected $locale_dir;

/**
 * Construct.
 *
 *
 * @param string $locale_dir            
 */
public function __construct($locale_dir)
{
    $this->locale_dir = $locale_dir;
}

/**
 * Set the user define localte
 *
 * @param string $locale            
 */
public function setLocale($locale = null)
{
    $this->locale = $locale;

    return $this;
}

/**
 * Get the user define locale
 *
 * @return string
 */
public function getLocale()
{
    return $this->locale;
}

/**
 * Get the Default locale
 *
 * @return string
 */
public function getDefaultLocale()
{
    return $this->default_locale;
}

/**
 * Set the default locale
 *
 * @param string $locale            
 */
public function setDefaultLocale($locale)
{
    $this->default_locale = $locale;

    return $this;
}

/**
 * Determine if transltion exist or translation key exist
 *
 * @param string $locale            
 * @param string $key            
 * @return boolean
 */
public function hasTranslation($locale, $key = null)
{
    if (null == $key && isset($this->translation[$locale])) {
        return true;
    } elseif (isset($this->translation[$locale][$key])) {
        return true;
    }

    return false;
}

/**
 * Get the transltion for required locale or transtion for key
 *
 * @param string $locale            
 * @param string $key            
 * @return array
 */
public function getTranslation($locale, $key = null)
{
    if (null == $key && $this->hasTranslation($locale)) {
        return $this->translation[$locale];
    } elseif ($this->hasTranslation($locale, $key)) {
        return $this->translation[$locale][$key];
    }

    return array();
}

/**
 * Set the transtion for required locale
 *
 * @param string $locale
 *            Language code
 * @param string $trans
 *            translations array
 */
public function setTranslation($locale, $trans = array())
{
    $this->translation[$locale] = $trans;
}

/**
 * Remove transltions for required locale
 *
 * @param string $locale            
 */
public function removeTranslation($locale = null)
{
    if (null === $locale) {
        unset($this->translation);
    } else {
        unset($this->translation[$locale]);
    }
}

/**
 * Initialize locale
 *
 * @param string $locale            
 */
public function init($locale = null, $default_locale = null)
{
    // check if previously set locale exist or not
    $this->init_locale();
    if ($this->locale != null) {
        return;
    }

    if ($locale == null || (! preg_match('#^[a-z]+_[a-zA-Z_]+$#', $locale) && ! preg_match('#^[a-z]+_[a-zA-Z]+_[a-zA-Z_]+$#', $locale))) {
        $this->detectLocale();
    } else {
        $this->locale = $locale;
    }

    $this->init_locale();
}

/**
 * Attempt to autodetect locale
 *
 * @return void
 */
private function detectLocale()
{
    $locale = false;

    // GeoIP
    if (function_exists('geoip_country_code_by_name') && isset($_SERVER['REMOTE_ADDR'])) {

        $country = geoip_country_code_by_name($_SERVER['REMOTE_ADDR']);

        if ($country) {

            $locale = isset($this->country_to_locale[$country]) ? $this->country_to_locale[$country] : false;
        }
    }

    // Try detecting locale from browser headers
    if (! $locale) {

        if (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {

            $languages = explode(',', $_SERVER['HTTP_ACCEPT_LANGUAGE']);

            foreach ($languages as $lang) {

                $lang = str_replace('-', '_', trim($lang));

                if (strpos($lang, '_') === false) {

                    if (isset($this->country_to_locale[strtoupper($lang)])) {

                        $locale = $this->country_to_locale[strtoupper($lang)];
                    }
                } else {

                    $lang = explode('_', $lang);

                    if (count($lang) == 3) {
                        // language_Encoding_COUNTRY
                        $this->locale = strtolower($lang[0]) . ucfirst($lang[1]) . strtoupper($lang[2]);
                    } else {
                        // language_COUNTRY
                        $this->locale = strtolower($lang[0]) . strtoupper($lang[1]);
                    }

                    return;
                }
            }
        }
    }

    // Resort to default locale specified in config file
    if (! $locale) {
        $this->locale = $this->default_locale;
    }
}

/**
 * Check if config for selected locale exists
 *
 * @return void
 */
private function init_locale()
{
    if (! file_exists(sprintf('%s/%s.php', $this->locale_dir, $this->locale))) {
        $this->locale = $this->default_locale;
    }
}

/**
 * Load a Transtion into array
 *
 * @return void
 */
private function loadTranslation($locale = null, $force = false)
{
    if ($locale == null)
        $locale = $this->locale;

    if (! $this->hasTranslation($locale)) {
        $this->setTranslation($locale, include (sprintf('%s/%s.php', $this->locale_dir, $locale)));
    }
}

/**
 * Translate a key
 *
 * @param
 *            string Key to be translated
 * @param
 *            string optional arguments
 * @return string
 */
public function translate($key)
{
    $this->init();
    $this->loadTranslation($this->locale);

    if (! $this->hasTranslation($this->locale, $key)) {

        if ($this->locale !== $this->default_locale) {

            $this->loadTranslation($this->default_locale);

            if ($this->hasTranslation($this->default_locale, $key)) {

                $translation = $this->getTranslation($this->default_locale, $key);
            } else {
                // return key as it is or log error here
                return $key;
            }
        } else {
            return $key;
        }
    } else {
        $translation = $this->getTranslation($this->locale, $key);
    }
    // Replace arguments
    if (false !== strpos($translation, '{a:')) {
        $replace = array();
        $args = func_get_args();
        for ($i = 1, $max = count($args); $i < $max; $i ++) {
            $replace['{a:' . $i . '}'] = $args[$i];
        }
        // interpolate replacement values into the messsage then return
        return strtr($translation, $replace);
    }

    return $translation;
  }
}

การใช้

 <?php
    ## /locale/en.php

    return array(
       'name' => 'Hello {a:1}'
       'name_full' => 'Hello {a:1} {a:2}'
   );

$locale = new Locale(__DIR__ . '/locale');
$locale->setLocale('en');// load en.php from locale dir
//want to work with auto detection comment $locale->setLocale('en');

echo $locale->translate('name', 'Foo');
echo $locale->translate('name', 'Foo', 'Bar');

มันทำงานอย่างไร

{a:1} ถูกแทนที่ด้วยอาร์กิวเมนต์ที่ 1 ส่งผ่านไปยังวิธีการ Locale::translate('key_name','arg1') {a:2}ถูกแทนที่ด้วยอาร์กิวเมนต์ที่ 2 ที่ส่งผ่านไปยังวิธีการLocale::translate('key_name','arg1','arg2')

วิธีการตรวจจับทำงาน

  • โดยค่าเริ่มต้นหากgeoipมีการติดตั้งแล้วมันจะส่งคืนรหัสประเทศโดยgeoip_country_code_by_nameและถ้า geoip ไม่ได้ติดตั้งทางเลือกที่จะกลับไปที่HTTP_ACCEPT_LANGUAGEส่วนหัว

ฐานข้อมูลจะยุ่งขนาดไหน? เพราะตัวละครที่เป็นไปได้ในภาษาต่าง ๆ ? จนถึงตอนนี้ฉันส่วนใหญ่มีเว็บไซต์ภาษาอังกฤษฝรั่งเศสดัตช์เยอรมันดังนั้นจึงไม่มีปัญหาสำหรับตอนนี้ ขอบคุณสำหรับคำตอบ แต่เนื่องจากเป็นเพียงส่วนหนึ่งของคำตอบจึงไม่ชนะรางวัล
Joshua - Pendo

ฉันเดาว่าคำถามของคุณมีประโยชน์สำหรับคุณเท่านั้นจะมีผู้ชายบางคนที่จะพิจารณาใช้ภาษาอย่างฮินดีไทยจีนและอาหรับ หากคุณใช้ db การutf8_general_ciเปรียบเทียบคือวิธีที่เหมาะสมในการทำ
Shushant

ฉันเห็นด้วยฉันได้ติดตามตัวเองเล็กน้อยที่นั่น ขอบคุณที่ชี้ให้เห็นตัวละครหลายบิตก็มีความสำคัญพอที่จะกล่าวถึงในคำถามนี้ :)
Joshua - Pendo

5

เพียงคำตอบย่อย: ใช้ URL ที่แปลพร้อมตัวระบุภาษาที่อยู่ตรงหน้า: http://www.domain.com/nl/over-ons
Hybride solution มีแนวโน้มที่จะซับซ้อนดังนั้นฉันจะติดกับมัน ทำไม? ทำให้ URL เป็นสิ่งจำเป็นสำหรับ SEO

เกี่ยวกับการแปล db: จำนวนภาษามีการแก้ไขมากหรือน้อย? หรือค่อนข้างคาดเดาไม่ได้และเป็นแบบไดนามิก? หากได้รับการแก้ไขฉันจะเพิ่มคอลัมน์ใหม่ไม่เช่นนั้นจะมีหลายตาราง

แต่โดยทั่วไปทำไมไม่ใช้ Drupal ฉันรู้ว่าทุกคนต้องการสร้าง CMS ของตัวเองเพราะมันเร็วขึ้นเอนกาย ฯลฯ เป็นต้น แต่นั่นเป็นความคิดที่แย่จริงๆ!


1
ขอบคุณสำหรับคำตอบ. เหตุผลที่ฉันไม่ต้องการใช้ Drupal / Joomla นั้นง่าย: ฉันต้องการตรวจสอบให้แน่ใจว่าฉันรู้ทุกอย่างเกี่ยวกับระบบของฉันข้อผิดพลาดที่เป็นไปได้วิธีการสร้างรหัส (และสำคัญ: ไม่ได้สร้างโดย 300 โปรแกรมเมอร์ด้วยกัน) . ฉันมีเหตุผลมากพอที่จะไม่เลือกโอเพ่นซอร์ส นอกจากนั้นฉันต้องการให้ บริษัท ของฉันเป็นปัจจัยสำคัญสำหรับลูกค้าของฉันมันเป็นสิ่งที่ไม่ดีที่พวกเขาสามารถไปที่นักพัฒนาอื่น ๆ และทิ้งฉันไว้ข้างหลังโดยไม่มีอะไรเลย
โจชัว - Pendo

7
ฉันคิดว่าเหตุผลทั้งหมดนี้เป็นข้อโต้แย้งในบทความมากมาย หวังว่าลูกค้าของคุณจะไม่เลือกคุณเพราะคุณมี CMS ที่เป็นกรรมสิทธิ์ซึ่งไม่มีใครดูแลได้ แต่อย่างไรก็ตามนั่นคือการสนทนาที่แตกต่างกันโดยสิ้นเชิง
Remy

1
ฉันเข้าใจประเด็นของคุณ แต่ฉันก็ยังต้องการระบบที่ฉันรู้ทั้งหมดและลึกหนาบางสำหรับและฉันไม่รู้สึกอะไรเลยที่จะพึ่งพาคนอื่น ๆ ที่ทำงานเมื่อฉันใช้ปลั๊กอิน
Joshua - Pendo

1
นอกจากนี้ฉันมักจะบันทึกงานของฉันได้ดีพอเนื่องจากฉันเป็น "ทหารคนหนึ่ง" คนที่ทำงานให้ฉันไม่ควรลำบากในการทำความรู้จักระบบ
Joshua - Pendo

ความคิดที่ไม่ดีคือการเลือก Drupal และแม้แต่ google ก็บอกว่าพวกเขาไม่สนใจว่า url จะถูกแปลหรือไม่ มันจะต้องมีตัวระบุสถานที่
undefinedman

5

ฉันจะไม่พยายามปรับแก้คำตอบที่ได้ให้ไปแล้ว แต่ฉันจะบอกคุณเกี่ยวกับวิธีการที่กรอบ OOP PHP ของฉันจัดการการแปลแทน

ภายในกรอบงานของฉันใช้รหัสเช่น en, fr, es, cn และอื่น ๆ อาเรย์ถือภาษาที่เว็บไซต์รองรับ: อาเรย์ ('en', 'fr', 'es', 'cn') รหัสภาษาจะถูกส่งผ่านทาง $ _GET (lang = fr) และหากไม่ผ่านหรือไม่ถูกต้องมันจะ ถูกตั้งค่าเป็นภาษาแรกในอาร์เรย์ ดังนั้นตลอดเวลาระหว่างการทำงานของโปรแกรมและจากจุดเริ่มต้นมากภาษาปัจจุบันเป็นที่รู้จักกัน

มีประโยชน์ในการทำความเข้าใจชนิดของเนื้อหาที่จำเป็นต้องแปลในแอปพลิเคชันทั่วไป:

1) ข้อความแสดงข้อผิดพลาดจากคลาส (หรือรหัสโพรซีเดอร์) 2) ข้อความแสดงข้อผิดพลาดจากคลาส (หรือรหัสโพรซีเดอร์) 3) เนื้อหาของหน้า (มักเก็บไว้ในฐานข้อมูล) 4) สตริงทั่วทั้งไซต์ (เช่นชื่อเว็บไซต์) 5) สคริปต์ สตริงเฉพาะ

ประเภทแรกเข้าใจง่าย โดยทั่วไปเรากำลังพูดถึงข้อความเช่น "ไม่สามารถเชื่อมต่อกับฐานข้อมูล ... " ข้อความเหล่านี้จะต้องโหลดเมื่อเกิดข้อผิดพลาด คลาสผู้จัดการของฉันรับสายจากคลาสอื่นและใช้ข้อมูลที่ส่งผ่านเป็นพารามิเตอร์ไปที่โฟลเดอร์คลาสที่เกี่ยวข้องและดึงไฟล์ข้อผิดพลาด

ข้อความแสดงข้อผิดพลาดประเภทที่สองนั้นคล้ายกับข้อความที่คุณได้รับเมื่อการตรวจสอบแบบฟอร์มมีข้อผิดพลาด ("คุณไม่สามารถปล่อยให้ ... ว่างเปล่า" หรือ "โปรดเลือกรหัสผ่านที่มีอักขระเกิน 5 ตัว") จำเป็นต้องโหลดสตริงก่อนที่คลาสจะรันฉันรู้ว่ามันคืออะไร

สำหรับเนื้อหาหน้าจริงฉันใช้หนึ่งตารางต่อภาษาแต่ละตารางนำหน้าด้วยรหัสสำหรับภาษา ดังนั้น en_content เป็นตารางที่มีเนื้อหาภาษาอังกฤษ es_content สำหรับสเปน, cn_content สำหรับจีนและ fr_content เป็นภาษาฝรั่งเศส

สตริงประเภทที่สี่มีความเกี่ยวข้องทั่วทั้งเว็บไซต์ของคุณ สิ่งนี้ถูกโหลดผ่านไฟล์การกำหนดค่าที่ใช้รหัสสำหรับภาษานั่นคือ en_lang.php, es_lang.php และอื่น ๆ ในไฟล์ภาษาสากลคุณจะต้องโหลดภาษาที่แปลเช่นอาร์เรย์ ('อังกฤษ', 'จีน', 'สเปน', 'ฝรั่งเศส') ในไฟล์และอาร์เรย์ทั่วโลกในภาษาอังกฤษ ('Anglais', 'Chinois', ' Espagnol ',' Francais ') ในไฟล์ภาษาฝรั่งเศส ดังนั้นเมื่อคุณเติมแบบเลื่อนลงสำหรับการเลือกภาษามันเป็นภาษาที่ถูกต้อง;)

ในที่สุดคุณก็มีสตริงเฉพาะสคริปต์ ดังนั้นถ้าคุณเขียนแอปพลิเคชันการทำอาหารมันอาจเป็น "เตาอบของคุณไม่ร้อนพอ"

ในรอบแอปพลิเคชันของฉันไฟล์ภาษาสากลจะถูกโหลดก่อน ในนั้นคุณจะพบไม่เพียง แต่สตริงทั่วโลก (เช่น "เว็บไซต์ของแจ็ค") แต่ยังมีการตั้งค่าสำหรับบางคลาส โดยพื้นฐานแล้วอะไรก็ตามที่เป็นภาษาหรือวัฒนธรรมขึ้นอยู่กับ บางส่วนของสตริงในนั้นมีมาสก์สำหรับวันที่ (MMDDYYYY หรือ DDMMYYYY) หรือรหัสภาษา ISO ในไฟล์ภาษาหลักฉันรวมสตริงสำหรับแต่ละคลาสเนื่องจากมีจำนวนน้อยมาก

ไฟล์ภาษาที่สองและสุดท้ายที่อ่านจากดิสก์คือไฟล์ภาษาสคริปต์ lang_en_home_welcome.php เป็นไฟล์ภาษาสำหรับโฮม / สคริปต์ต้อนรับ สคริปต์ถูกกำหนดโดยโหมด (โฮม) และการกระทำ (ยินดีต้อนรับ) แต่ละสคริปต์มีโฟลเดอร์ของตนเองพร้อมไฟล์กำหนดค่าและ lang

สคริปต์ดึงเนื้อหาจากฐานข้อมูลการตั้งชื่อตารางเนื้อหาตามที่อธิบายไว้ข้างต้น

หากมีข้อผิดพลาดเกิดขึ้นผู้จัดการจะทราบว่าจะรับไฟล์ข้อผิดพลาดที่ขึ้นกับภาษาได้ที่ไหน ไฟล์ดังกล่าวจะถูกโหลดในกรณีที่เกิดข้อผิดพลาดเท่านั้น

ดังนั้นข้อสรุปที่ชัดเจน คิดเกี่ยวกับปัญหาการแปลก่อนที่จะเริ่มพัฒนาแอปพลิเคชันหรือกรอบงาน คุณต้องมีขั้นตอนการพัฒนาที่รวมเอาการแปลไว้ด้วย ด้วยกรอบการทำงานของฉันฉันพัฒนาทั้งเว็บไซต์เป็นภาษาอังกฤษแล้วแปลไฟล์ที่เกี่ยวข้องทั้งหมด

เพียงแค่คำศัพท์สุดท้ายอย่างรวดเร็วเกี่ยวกับวิธีการใช้งานสตริงการแปล เฟรมเวิร์กของฉันมีโกลบอลเดียวคือ $ manager ซึ่งรันบริการที่มีให้กับบริการอื่น ๆ ตัวอย่างเช่นบริการฟอร์มได้รับบริการ html และใช้เพื่อเขียน html หนึ่งในบริการในระบบของฉันคือบริการแปล $ translator-> set ($ service, $ code, $ string) ตั้งค่าสตริงสำหรับภาษาปัจจุบัน ไฟล์ภาษาเป็นรายการคำสั่งดังกล่าว $ translator-> get ($ service, $ code) ดึงสตริงการแปล รหัส $ สามารถเป็นตัวเลขได้เช่น 1 หรือสตริงเช่น 'no_connection' ไม่สามารถขัดแย้งกันระหว่างบริการต่างๆเนื่องจากแต่ละบริการมีเนมสเปซของตนเองในพื้นที่ข้อมูลของนักแปล

ฉันโพสต์ที่นี่ด้วยความหวังว่ามันจะช่วยให้ใครบางคนทำงานใหม่ในการสร้างวงล้อเหมือนฉันต้องทำเมื่อไม่กี่ปีที่ผ่านมา


4

ฉันเคยเจอปัญหานี้มาซักพักแล้วก่อนจะเริ่มใช้งานเฟรมเวิร์กSymfony

  1. เพียงแค่ใช้ฟังก์ชั่น __ () ซึ่งมีพารามิเตอร์ pageId (หรือ objectId, objectTable อธิบายไว้ใน # 2) ภาษาเป้าหมายและพารามิเตอร์ทางเลือกของภาษาทางเลือก (ค่าเริ่มต้น) สามารถตั้งค่าภาษาเริ่มต้นในการกำหนดค่าส่วนกลางบางอย่างเพื่อให้สามารถเปลี่ยนได้ง่ายขึ้นในภายหลัง

  2. สำหรับการจัดเก็บเนื้อหาในฐานข้อมูลฉันใช้โครงสร้างต่อไปนี้: (pageId, ภาษา, เนื้อหา, ตัวแปร)

    • pageId จะเป็น FK สำหรับหน้าของคุณที่คุณต้องการแปล หากคุณมีวัตถุอื่น ๆ เช่นข่าวสารแกลเลอรีหรืออะไรก็ตามเพียงแค่แยกออกเป็น 2 ฟิลด์ objectId, objectTable

    • ภาษา - เห็นได้ชัดว่ามันจะเก็บสตริงภาษา ISO EN_en, LT_lt, EN_us ฯลฯ

    • เนื้อหา - ข้อความที่คุณต้องการแปลพร้อมกับไวด์การ์ดสำหรับการแทนที่ตัวแปร ตัวอย่าง "Hello mr. %% ชื่อ %% ยอดเงินในบัญชีของคุณคือ %% balance %%"

    • ตัวแปร - ตัวแปรที่เข้ารหัส json PHP จัดเตรียมฟังก์ชันเพื่อแยกวิเคราะห์สิ่งเหล่านี้อย่างรวดเร็ว ตัวอย่าง "ชื่อ: Laurynas ยอดคงเหลือ: 15.23"

    • คุณพูดถึงสนามกระสุนยัง คุณสามารถเพิ่มลงในตารางนี้ได้อย่างอิสระเพื่อให้สามารถค้นหาได้อย่างรวดเร็ว

  3. การโทรฐานข้อมูลของคุณจะต้องลดลงให้น้อยที่สุดด้วยการแคชการแปล จะต้องเก็บไว้ในอาร์เรย์ PHP เพราะเป็นโครงสร้างที่เร็วที่สุดในภาษา PHP คุณจะทำให้การแคชนี้ขึ้นอยู่กับคุณอย่างไร จากประสบการณ์ของฉันคุณควรมีโฟลเดอร์สำหรับแต่ละภาษาที่รองรับและอาร์เรย์สำหรับแต่ละหน้ารหัส ควรสร้างแคชใหม่หลังจากที่คุณอัปเดตการแปล ควรสร้างใหม่อาร์เรย์ที่เปลี่ยนแปลงเท่านั้น

  4. ฉันคิดว่าฉันตอบว่าใน # 2

  5. ความคิดของคุณมีเหตุผลอย่างสมบูรณ์ อันนี้ค่อนข้างเรียบง่ายและฉันคิดว่าจะไม่ทำให้คุณมีปัญหาใด ๆ

ควรแปล URL โดยใช้ทากที่เก็บไว้ในตารางการแปล

คำพูดสุดท้าย

มันเป็นการดีเสมอที่จะทำการวิจัยแนวปฏิบัติที่ดีที่สุด แต่อย่าคิดค้นล้อใหม่ เพียงใช้และใช้ส่วนประกอบจากกรอบที่รู้จักกันดีและใช้พวกเขา

จะดูที่องค์ประกอบแปล Symfony มันอาจเป็นฐานรหัสที่ดีสำหรับคุณ


ขอบคุณสำหรับความคิดเห็นเช่นกัน +1 สำหรับเวลาของคุณ Laravel (ในกรณีของฉัน) ใช้บางส่วนของ Symfony ถ้าฉันไม่เข้าใจผิด ฉันเริ่มต้นคำถามนี้ (และความโปรดปราน) เพื่อรับข้อมูลเชิงลึกในวิธีที่คนอื่นทำการแปลฉันเริ่มเชื่อว่ามีแนวทางปฏิบัติที่ดีที่สุดมากมายที่นั่น :-)
Joshua - Pendo

1

ฉันถามคำถามที่เกี่ยวข้องกับตัวเองซ้ำแล้วซ้ำอีกแล้วหายไปในภาษาทางการ ... แต่เพื่อช่วยคุณออกไปเล็กน้อยฉันต้องการแบ่งปันสิ่งที่ค้นพบ:

ฉันแนะนำให้ดูที่ CMS ขั้นสูง

Typo3สำหรับPHP (ฉันรู้ว่ามีหลายสิ่ง แต่นั่นคือสิ่งที่ฉันคิดว่าเป็นผู้ใหญ่ที่สุด)

Plone ใน Python

หากคุณพบว่าเว็บในปี 2013 ควรทำงานแตกต่างจากนั้นเริ่มจากศูนย์ นั่นหมายถึงการรวมทีมของคนที่มีทักษะสูง / มีประสบการณ์เพื่อสร้าง CMS ใหม่ อาจเป็นไปได้ว่าคุณอยากจะดูโพลิเมอร์เพื่อจุดประสงค์นั้น

ถ้าเป็นเรื่องของการเข้ารหัสและเว็บไซต์หลายภาษา / การสนับสนุนภาษาพื้นเมืองฉันคิดว่าโปรแกรมเมอร์ทุกคนควรมีเงื่อนงำเกี่ยวกับยูนิโค้ด ถ้าคุณไม่รู้ยูนิโค้ดคุณก็จะทำให้ข้อมูลของคุณยุ่งเหยิงไปหมด อย่าใช้รหัส ISO หลายพันรายการ พวกเขาจะช่วยให้คุณประหยัดหน่วยความจำบางส่วนเท่านั้น แต่คุณสามารถทำทุกอย่างได้อย่างแท้จริงด้วย UTF-8 แม้แต่เก็บตัวอักษรจีน แต่สำหรับสิ่งที่คุณต้องจัดเก็บทั้ง 2 หรือ 4 ไบต์อักขระที่ทำให้มันเป็น utf-16 หรือ utf-32

ถ้ามันเกี่ยวกับการเข้ารหัส URL คุณควรผสมการเข้ารหัสอีกครั้งและระวังว่าอย่างน้อยสำหรับชื่อโดเมนจะมีกฎที่กำหนดโดยล็อบบี้ต่าง ๆ ที่ให้แอปพลิเคชั่นเช่นเบราว์เซอร์ เช่นโดเมนอาจคล้ายกันมากเช่น:

ьankofamerica.comหรือ bankofamerica.com samesamebutdifferent;)

แน่นอนคุณต้องการระบบไฟล์เพื่อทำงานกับการเข้ารหัสทั้งหมด ข้อดีอีกอย่างของยูนิโค้ดโดยใช้ระบบไฟล์ utf-8

หากเกี่ยวกับการแปลให้นึกถึงโครงสร้างของเอกสาร เช่นหนังสือหรือบทความ คุณมีdocbookข้อกำหนดที่จะเข้าใจเกี่ยวกับโครงสร้างเหล่านั้น แต่ใน HTML มันเป็นเพียงเกี่ยวกับบล็อกเนื้อหา ดังนั้นคุณจึงต้องการให้มีการแปลในระดับนั้นรวมถึงในระดับหน้าเว็บหรือระดับโดเมน ดังนั้นหากไม่มีบล็อกอยู่ที่นั่นหากหน้าเว็บไม่มีอยู่คุณจะถูกเปลี่ยนเส้นทางไปยังระดับการนำทางด้านบน หากโดเมนควรแตกต่างไปจากเดิมอย่างสิ้นเชิงในโครงสร้างการนำทางดังนั้นโครงสร้างที่แตกต่างในการจัดการ สิ่งนี้สามารถทำได้ด้วย Typo3

ถ้าเป็นเรื่องของเฟรมเวิร์กที่เป็นผู้ใหญ่ที่สุดที่ฉันรู้จักทำสิ่งทั่วไปเช่น MVC (buzzword ฉันเกลียดจริง ๆ ! เช่น "ประสิทธิภาพ" หากคุณต้องการขายบางอย่างใช้คำพูดและ featurerich และคุณขาย ... อะไร นรก) คือ Zendเป็น มันได้รับการพิสูจน์แล้วว่าเป็นสิ่งที่ดีที่จะนำมาตรฐานไปใช้กับผู้เขียนโค้ด php วุ่นวาย แต่ typo3 ยังมี Framework นอกเหนือจาก CMS เมื่อเร็ว ๆ นี้มันได้รับการพัฒนาขึ้นใหม่และเรียกว่า flow3 ในขณะนี้ กรอบของหลักสูตรครอบคลุมเนื้อหานามธรรมการสร้างเทมเพลตและแนวคิดสำหรับการแคช แต่มีจุดแข็งของแต่ละบุคคล

ถ้ามันเกี่ยวกับแคช ... นั่นอาจซับซ้อน / หลายชั้น ใน PHP คุณจะนึกถึง accellerator, opcode, แต่ยังเป็น html, httpd, mysql, xml, css, js ... แคชทุกชนิด แน่นอนว่าบางส่วนควรแคชและส่วนแบบไดนามิกเช่นคำตอบบล็อกไม่ควร บางคนควรได้รับการร้องขอผ่าน AJAX ด้วย URL ที่สร้างขึ้น JSON, hashbangsเป็นต้น

จากนั้นคุณต้องการให้มีองค์ประกอบเล็ก ๆ น้อย ๆ ในเว็บไซต์ของคุณที่จะเข้าถึงหรือจัดการโดยผู้ใช้บางรายดังนั้นแนวคิดที่มีบทบาทสำคัญ

นอกจากนี้คุณต้องการสร้างสถิติอาจมีการกระจายระบบ / facebook ของ facebooks และซอฟต์แวร์ใด ๆ ที่จะสร้างขึ้นบนสุดของคุณใน cms ด้านบน ... ดังนั้นคุณต้องมีฐานข้อมูลประเภทต่าง ๆ ในหน่วยความจำ, bigdata, xml,ใด ๆ .

ฉันคิดว่าเพียงพอแล้วสำหรับตอนนี้ หากคุณไม่เคยได้ยิน typo3 / plone หรือกรอบที่กล่าวถึงคุณมีเพียงพอที่จะศึกษา บนเส้นทางนั้นคุณจะพบคำตอบมากมายสำหรับคำถามที่คุณยังไม่ได้ถาม

ถ้าคุณคิดว่าให้สร้าง CMS ใหม่เพราะ 2013 และ php กำลังจะตายต่อไปคุณยินดีที่จะเข้าร่วมกลุ่มนักพัฒนากลุ่มอื่นหวังว่าจะไม่หลงทาง

โชคดี!

และ btw ในอนาคตผู้คนจะไม่มีเว็บไซต์ใด ๆ อีกต่อไป? และเราทุกคนจะอยู่ใน google +? ฉันหวังว่านักพัฒนาจะมีความคิดสร้างสรรค์มากขึ้นและทำสิ่งที่มีประโยชน์ (เพื่อไม่ให้หลอมรวมโดย borgle)

//// แก้ไข /// เพียงคิดเล็กน้อยสำหรับแอปพลิเคชันปัจจุบันของคุณ:

หากคุณมี php mysql CMS และคุณต้องการที่จะฝังการสนับสนุนแบบหลายภาษา คุณสามารถใช้ตารางของคุณกับคอลัมน์ aditional สำหรับภาษาใด ๆ หรือแทรกการแปลด้วยรหัสวัตถุและรหัสภาษาในตารางเดียวกันหรือสร้างตารางที่เหมือนกันสำหรับภาษาใด ๆ และแทรกวัตถุที่นั่นแล้วทำการเลือกสหภาพถ้าคุณต้องการ เพื่อให้พวกเขาทั้งหมดแสดง สำหรับฐานข้อมูลให้ใช้ utf8 ci ทั่วไปและแน่นอนในการใช้ข้อความ / การเข้ารหัส utf8 ด้านหน้า / แบ็กเอนด์ ฉันใช้ส่วนเส้นทาง URL สำหรับ URL ในแบบที่คุณอธิบายไว้แล้ว

domain.org/en/about คุณสามารถแม็พ lang ID กับตารางเนื้อหาของคุณ อย่างไรก็ตามคุณต้องมีแผนที่ของพารามิเตอร์สำหรับ URL ของคุณดังนั้นคุณจึงต้องการกำหนดพารามิเตอร์ที่จะแมปจาก Pathegment ใน URL ของคุณเช่น

domain.org/en/about/employees/IT/administrators/

การกำหนดค่าการค้นหา

PageId | URL

1 | /about/employees/../ ..

1 | /../about/employees../../

แม็พพารามิเตอร์กับ url pathegment ""

$parameterlist[lang] = array(0=>"nl",1=>"en"); // default nl if 0
$parameterlist[branch] = array(1=>"IT",2=>"DESIGN"); // default nl if 0
$parameterlist[employertype] = array(1=>"admin",1=>"engineer"); //could be a sql result 

$websiteconfig[]=$userwhatever;
$websiteconfig[]=$parameterlist;
$someparameterlist[] = array("branch"=>$someid);
$someparameterlist[] = array("employertype"=>$someid);
function getURL($someparameterlist){ 
// todo foreach someparameter lookup pathsegment 
return path;
}

ต่อพูดว่าได้รับการคุ้มครองแล้วในโพสต์บน

และเพื่อที่จะไม่ลืมคุณจะต้อง "เขียน" url ไปยังไฟล์ php ที่คุณสร้างซึ่งโดยส่วนใหญ่แล้วจะเป็น index.php


ขอบคุณสำหรับความคิดเห็นมีสิ่งที่ฉันควรคิดอย่างแน่นอนที่สุด ฉันใช้การเข้ารหัส utf8 มาสองสามปีแล้วฉันเคยต่อสู้กับตัวละครครั้งเดียว ;-) ในอีกด้านหนึ่งประเภทของ CMS / Framework นั้นไม่ได้เป็นปัจจัยในการตอบคำถามของคุณเพราะฉันกำลังมองหา วิธีการแพลตฟอร์มอิสระราวกับว่าเราได้รับการเข้ารหัสตั้งแต่เริ่มต้น
Joshua - Pendo

ถ้าคุณต้องการโค้ดจากศูนย์ฉันขอแนะนำให้ดูที่ Dartlang และโพลิเมอร์ เนื่องจาก dartlang ทำงานในเบราว์เซอร์และมีการสนับสนุน 32 และ 64 บิตและสามารถใช้เพื่อวัตถุประสงค์ส่วนใหญ่ในเซิร์ฟเวอร์และมีคอมไพเลอร์ dart2js มันคุ้มค่าที่จะศึกษา หากผู้คนพูดถึงความเป็นอิสระของแพลตฟอร์มพวกเขาคิดเกี่ยวกับจาวา ... เรารู้ว่ามันหมายถึงอะไร Buildprocess ... ฉันคิดว่าฉันจะใช้ JSON เพื่อการแลกเปลี่ยน สร้างเว็บไซต์ฝั่งไคลเอ็นต์กับ hashbangs และฝั่งเซิร์ฟเวอร์ .. ทำทุกอย่างที่คุณต้องการเพื่อให้มั่นใจในการทำงานร่วมกัน
ดร. ดามา

databaselayout และ logic generation เป็นภารกิจหลัก ไม่มีใครจะทำอย่างนั้นกับคุณที่นี่ ... แต่ความคิดของตัวเองเป็นสิ่งที่นับ เนื่องจากฉันไม่สนใจเกี่ยวกับล็อบบี้ แต่เพื่อให้งานเสร็จฉันหวังว่าคุณจะสามารถสร้างแบบจำลองและแบ่งปันบางสิ่งได้ ตอนนี้ฉันกำลังทำงานที่คล้ายกันอยู่ แต่ฉันยังคงวางแผนอยู่ ฉันกำลังพิจารณา Typo3 เป็นแบ็กเอนด์และสร้างโครงสร้างลูกค้าใหม่ รูปแบบหลายภาษาได้รับการแก้ไขในแบ็กเอนด์และจะแบ่งปันข้อมูลในรูปแบบเฉพาะสำหรับเครื่องมือค้นหา / เว็บเซอร์ อย่างไรก็ตามมันเป็นงานที่ต้องคำนึงถึงและสร้างอย่างต่อเนื่อง
ดร. ดามา

-1

งานฐานข้อมูล:

สร้างตารางภาษา 'languages':

ทุ่ง:

language_id(primary and auto increamented)

language_name

created_at

created_by

updated_at

updated_by

สร้างตารางในฐานข้อมูล 'เนื้อหา':

ทุ่ง:

content_id(primary and auto incremented)

main_content

header_content

footer_content

leftsidebar_content

rightsidebar_content

language_id(foreign key: referenced to languages table)

created_at

created_by

updated_at

updated_by

ส่วนหน้าทำงาน:

เมื่อผู้ใช้เลือกภาษาใด ๆ จากดรอปดาวน์หรือพื้นที่ใด ๆ จากนั้นให้บันทึก id ภาษาที่เลือกในเซสชันเช่น,

$_SESSION['language']=1;

ตอนนี้ดึงข้อมูลจาก 'เนื้อหา' ตารางฐานข้อมูลตามรหัสภาษาที่เก็บไว้ในเซสชั่น

รายละเอียดอาจพบได้ที่นี่ http://skillrow.com/multilingual-website-in-php-2/


1
นี่เป็นวิธีในการรวมภาษาอย่างง่ายจากนั้นจึงจำเป็นคุณลองอ่านโพสต์แบบเต็มและคำตอบที่ได้รับหรือไม่?
Joshua - Pendo

-2

ในฐานะที่เป็นคนที่อาศัยอยู่ในควิเบกที่เว็บไซต์เกือบทั้งหมดเป็นภาษาฝรั่งเศสและภาษาอังกฤษ ... ฉันได้ลองหลายอย่างถ้าไม่ปลั๊กอินหลายภาษามากที่สุดสำหรับ WP ... หนึ่งในโซลูชั่นที่มีประโยชน์เพียงอย่างเดียวที่ทำงานได้กับเว็บไซต์ของฉันทั้งหมดคือ mQtranslate ... ฉันมีชีวิตอยู่และตายไปกับมัน!

https://wordpress.org/plugins/mqtranslate/


1
ใช่แล้ว WP ไม่ใช่ปัจจัยของคำถาม นี่อาจเป็นความคิดเห็นได้เช่นกัน
Joshua - Pendo

-3

WORDPRESS + MULTI-LANGUAGE SITE BASIS(ปลั๊กอิน) เกี่ยวกับอะไร เว็บไซต์จะมีโครงสร้าง:

  • example.com/ eng / category1 / ....
  • example.com/ eng / my-page ....
  • example.com/ rus / category1 / ....
  • example.com/ rus / my-page ....

ปลั๊กอินให้ส่วนต่อประสานสำหรับการแปลวลีทั้งหมดด้วยตรรกะอย่างง่าย:

(ENG) my_title - "Hello user"
(SPA) my_title - "Holla usuario"

จากนั้นจะสามารถส่งออก:
echo translate('my_title', LNG); // LNG is auto-detected

ป.ล. ตรวจสอบว่าปลั๊กอินยังคงทำงานอยู่หรือไม่


3
และไม่ใช่ "Holla userio" ในภาษาสเปนคือ "Hola Usuario"
bheatcoker

1
ผู้ใช้ Lol Holla นั่นเป็นเรื่องตลก!
spekdrum

สำหรับเหตุผลที่ฉันไม่รู้ภาษาสเปน (เพิ่งใช้ตัวอย่าง) รีบรีบลงคะแนน !! :)
T.Todua

-5

ตัวเลือกที่เรียบง่ายจริงๆที่ทำงานกับเว็บไซต์ใด ๆ ที่คุณสามารถอัปโหลด Javascript ได้คือwww.multilingualizer.com

มันช่วยให้คุณใส่ข้อความทั้งหมดสำหรับทุกภาษาลงในหน้าเดียวแล้วซ่อนภาษาที่ผู้ใช้ไม่จำเป็นต้องดู ทำได้ดี.


ระวัง SEO จะแย่มาก! ยิ่งคุณโหลดเนื้อหาทั้งหมดในขณะที่คุณเพียงแค่ต้องเป็นส่วนหนึ่งของมันซึ่งเป็นการปฏิบัติที่ไม่ดีจริงๆ
Hafenkranich

สิ่งแปลก ๆ ที่เว็บไซต์เป็นภาษาอังกฤษเท่านั้น ... ทำไมพวกเขาถึงไม่ใช้วิธีแก้ปัญหา
eduardo.lopes
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.