หมายเหตุ: กรอบการใช้งาน Ruby I18n จะให้คุณมีเครื่องมือที่จำเป็นสำหรับการระบุภาษาสากล / การแปลภาษาของแอปพลิเคชัน Rails ของคุณ คุณยังสามารถใช้แพ็กเกจต่าง ๆ ที่มีอยู่เพิ่มเติมเพื่อเพิ่มฟังก์ชันหรือคุณสมบัติเพิ่มเติม ดู แพ็กเกจ rails-i18n เพื่อข้อมูลเพิ่มเติม
1 วิธีการทำงานของ I18n ใน Ruby on Rails
การระบุภาษาสากลเป็นปัญหาที่ซับซ้อน ภาษาธรรมชาติแตกต่างกันในหลายๆ ด้าน (เช่นกฎการเปลี่ยนรูปพจนานุกรม) ซึ่งทำให้ยากที่จะให้เครื่องมือสำหรับแก้ปัญหาทั้งหมดได้พร้อมกัน ด้วยเหตุนี้ API I18n ของ Rails มุ่งเน้นที่:
- การให้การสนับสนุนสำหรับภาษาอังกฤษและภาษาที่คล้ายกันอื่นๆ ในตัว
- การทำให้ง่ายต่อการปรับแต่งและขยายความสามารถทั้งหมดสำหรับภาษาอื่นๆ
เป็นส่วนหนึ่งของการแก้ปัญหานี้ สตริงแบบสถิต ในกรอบการทำงานของ Rails - เช่นข้อความการตรวจสอบ Active Record, รูปแบบเวลาและวันที่ - ได้รับการระบุภาษาสากล การ localization ของแอปพลิเคชัน Rails หมายถึงการกำหนดค่าที่แปลสำหรับสตริงเหล่านี้ในภาษาที่ต้องการ
ในการทำให้เป็นภาษาท้องถิ่น จัดเก็บ และอัปเดต เนื้อหา ในแอปพลิเคชันของคุณ (เช่นการแปลบล็อกโพสต์) ดูส่วน การแปลเนื้อหาของโมเดล
1.1 โครงสร้างโดยรวมของไลบรารี
ดังนั้น แพ็กเกจ Ruby I18n ถูกแบ่งออกเป็นสองส่วน:
- ส่วน API สาธารณะของกรอบการทำงาน I18n - โมดูล Ruby ที่มีเมธอดสาธารณะที่กำหนดวิธีการทำงานของไลบรารี
- ส่วน backend เริ่มต้น (ที่มีชื่อ Simple backend) ซึ่งดำเนินการดังนี้
เป็นผู้ใช้คุณควรเข้าถึงเฉพาะเมธอดสาธารณะบนโมดูล I18n เท่านั้น แต่มันก็เป็นประโยชน์ที่จะทราบถึงความสามารถของ backend หมายเหตุ: สามารถแทนที่ Simple backend ที่จัดส่งมาด้วย backend ที่มีกำลังมากกว่าซึ่งจะเก็บข้อมูลการแปลในฐานข้อมูลที่เกี่ยวข้อง, พจนานุกรม GetText หรือคล้ายกัน ดูในส่วน การใช้ backend ที่แตกต่างกัน ด้านล่าง
1.2 สาธิต API สาธิต
เมธอดที่สำคัญที่สุดของ I18n API คือ:
translate # ค้นหาการแปลข้อความ
localize # แปลงวัตถุวันที่และเวลาเป็นรูปแบบท้องถิ่น
เมธอดเหล่านี้มีชื่อย่อว่า #t และ #l ดังนั้นคุณสามารถใช้งานได้ดังนี้:
I18n.t 'store.title'
I18n.l Time.now
ยังมี attribute readers และ writers สำหรับแอตทริบิวต์ต่อไปนี้:
load_path # ประกาศไฟล์แปลกำหนดเอง
locale # รับและตั้งค่า locale ปัจจุบัน
default_locale # รับและตั้งค่า locale เริ่มต้น
available_locales # รับ locale ที่อนุญาตให้ใช้งานได้สำหรับแอปพลิเคชัน
enforce_available_locales # บังคับการอนุญาต locale (true หรือ false)
exception_handler # ใช้ exception_handler ที่แตกต่างกัน
backend # ใช้ backend ที่แตกต่างกัน
ดังนั้นเรามานำเสนอวิธีการนำระบบรายการระหว่างประเทศเข้ากับแอปพลิเคชัน Rails ที่ง่ายที่สุดในบทถัดไป!
ตั้งค่าแอปพลิเคชัน Rails สำหรับการนำระบบรายการระหว่างประเทศเข้าใช้งาน
มีขั้นตอนเพียงไม่กี่ขั้นตอนเพื่อเริ่มต้นใช้งานระบบรายการระหว่างประเทศสำหรับแอปพลิเคชัน Rails
1.3 กำหนดค่าโมดูล I18n
ตามหลักการ convention over configuration ระบบรายการระหว่างประเทศของ Rails จะให้สตริงแปลเริ่มต้นที่เหมาะสม หากต้องการสตริงแปลที่แตกต่างกันสามารถแทนที่ได้
Rails จะเพิ่มไฟล์ .rb
และ .yml
ทั้งหมดจากไดเรกทอรี config/locales
เข้าสู่ ที่อยู่โหลดรายการแปล โดยอัตโนมัติ
โลเคลที่เริ่มต้น en.yml
ในไดเรกทอรีนี้มีคู่ตัวอย่างของสตริงแปล:
en:
hello: "Hello world"
นั่นหมายความว่าในโลเคล :en
คีย์ hello จะแมปไปยังสตริง Hello world สตริงทุกตัวภายใน Rails ถูกนำเข้าระบบรายการระหว่างประเทศในลักษณะนี้ ดูตัวอย่างข้อความการตรวจสอบ Active Model ในไฟล์ activemodel/lib/active_model/locale/en.yml
หรือรูปแบบเวลาและวันที่ในไฟล์ activesupport/lib/active_support/locale/en.yml
คุณสามารถใช้ YAML หรือ Hash ของ Ruby มาเก็บรายการแปลใน backend เริ่มต้น (Simple)
ไลบรารี I18n จะใช้ ภาษาอังกฤษ เป็น locale เริ่มต้น กล่าวคือหากไม่ได้ตั้งค่า locale ที่แตกต่างกัน :en
จะถูกใช้สำหรับการค้นหาการแปล
หมายเหตุ: ไลบรารี i18n ใช้วิธีการที่เหมาะสมสำหรับกุญแจ locale (หลังจาก การสนทนาบางส่วน) ซึ่งรวมเฉพาะส่วน locale ("ภาษา") เช่น :en
, :pl
ไม่มีส่วน region เช่น :"en-US"
หรือ :"en-GB"
ซึ่งใช้เป็นแบ่ง "ภาษา" และ "การตั้งค่าภูมิภาค" หรือ "สำเนาภาษา" แบบดั้งเดิม แอปพลิเคชันระหว่างประเทศหลายแอปพลิเคชันใช้เฉพาะองค์ประกอบ "ภาษา" ของ locale เช่น :cs
, :th
, หรือ :es
(สำหรับภาษาเช็ก, ไทย และสเปน) อย่างไรก็ตาม มีความแตกต่างทางภูมิภาคภายในกลุ่มภาษาต่าง ๆ ที่อาจเป็นสิ่งสำคัญ เช่น ในโลเคล :"en-US"
คุณจะมีสัญลักษณ์สกุลเงินเป็น $ ในขณะที่ใน :"en-GB"
คุณจะมี £ ไม่มีอะไรหยุดคุณจากการแยกการตั้งค่าภูมิภาคและอื่น ๆ ในทางนี้: คุณเพียงแต่ต้องให้ locale "ภาษาอังกฤษ - สหราชอาณาจักร" เต็มรูปแบบในพจนานุกรม :"en-GB"
คุณสามารถเปลี่ยนภาษาเริ่มต้นและกำหนดพาธการโหลดแปลได้ใน config/application.rb
ดังนี้:
config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}')]
config.i18n.default_locale = :de
จะต้องระบุพาธการโหลดก่อนที่จะค้นหาแปลภาษาใด ๆ เพื่อเปลี่ยนภาษาเริ่มต้นจากตัวกำหนดใน config/application.rb
:
# config/initializers/locale.rb
# สถานที่ที่ไลบรารี I18n ควรค้นหาไฟล์แปล
I18n.load_path += Dir[Rails.root.join('lib', 'locale', '*.{rb,yml}')]
# ภาษาที่อนุญาตให้ใช้สำหรับแอปพลิเคชัน
I18n.available_locales = [:en, :pt]
# ตั้งค่าภาษาเริ่มต้นเป็นอย่างอื่นนอกเหนือจาก :en
I18n.default_locale = :pt
โปรดทราบว่าการเพิ่มโดยตรงไปยัง I18n.load_path
แทนที่การกำหนดค่า I18n ที่กำหนดไว้ในแอปพลิเคชัน จะไม่ แทนที่แปลจากแพ็คเกจภายนอก
1.4 การจัดการภาษาในแต่ละคำขอ
แอปพลิเคชันที่ใช้งานในหลายภาษาอาจต้องการการสนับสนุนสำหรับภาษาหลายภาษา ในการดำเนินการนี้ ภาษาที่ต้องการจะต้องถูกตั้งค่าตั้งแต่เริ่มต้นของแต่ละคำขอเพื่อให้สายอักขระทั้งหมดถูกแปลเป็นภาษาที่ต้องการในระหว่างการดำเนินการของคำขอนั้น
ภาษาเริ่มต้นถูกใช้สำหรับแปลภาษาทั้งหมดยกเว้นใช้ I18n.locale=
หรือ I18n.with_locale
I18n.locale
อาจรั่วไหลเข้าสู่คำขอถัดไปที่ให้บริการโดยเธรด/กระบวนการเดียวกันหากไม่ได้ตั้งค่าไว้เสมอในทุก ๆ คอนโทรลเลอร์ ตัวอย่างเช่นการดำเนินการ I18n.locale =:es
ในคำขอ POST หนึ่งจะมีผลต่อคำขอที่ต่อมาที่ไม่ได้ตั้งค่าภาษา แต่เฉพาะในกระบวนการเธรด/กระบวนการนั้นเท่านั้น ดังนั้น แทนที่ I18n.locale =
คุณสามารถใช้ I18n.with_locale
ซึ่งไม่มีปัญหาการรั่วไหลนี้
สามารถตั้งค่าภาษาได้ใน around_action
ใน ApplicationController
:
around_action :switch_locale
def switch_locale(&action)
locale = params[:locale] || I18n.default_locale
I18n.with_locale(locale, &action)
end
ตัวอย่างนี้แสดงให้เห็นการใช้พารามิเตอร์คิวรี URL เพื่อตั้งค่าภาษา (เช่น http://example.com/books?locale=pt
) ด้วยวิธีนี้ http://localhost:3000?locale=pt
จะแสดงผลเป็นภาษาโปรตุเกส ในขณะที่ http://localhost:3000?locale=de
จะโหลดแปลงเยอรมัน
สามารถตั้งค่าภาษาได้โดยใช้วิธีหนึ่งในหลาย ๆ วิธีต่าง ๆ
1.4.1 ตั้งค่าภาษาจากชื่อโดเมน
หนึ่งในตัวเลือกที่คุณมีคือการตั้งค่าภาษาจากชื่อโดเมนที่แอปพลิเคชันของคุณทำงาน ตัวอย่างเช่น เราต้องการให้ www.example.com
โหลดภาษาอังกฤษ (หรือภาษาเริ่มต้น) และ www.example.es
โหลดภาษาสเปน ดังนั้น ชื่อโดเมนระดับสูง ถูกใช้สำหรับการตั้งค่าภาษา มีข้อดีหลายประการ:
- ภาษาเป็นส่วนหนึ่งที่ ชัดเจน ของ URL
- ผู้คนเข้าใจได้ง่ายว่าเนื้อหาจะแสดงเป็นภาษาใด
- มันง่ายมากที่จะดำเนินการใน Rails
- เครื่องมือค้นหาดูเหมือนชอบเนื้อหาในภาษาต่าง ๆ อยู่ในโดเมนที่แตกต่างกันและเชื่อมโยงกัน
คุณสามารถดำเนินการดังนี้ใน ApplicationController
ของคุณ:
around_action :switch_locale
def switch_locale(&action)
locale = extract_locale_from_tld || I18n.default_locale
I18n.with_locale(locale, &action)
end
# รับภาษาจากชื่อโดเมนระดับสูงหรือส่งคืน +nil+ หากไม่มีภาษาดังกล่าว
# คุณต้องใส่บางอย่างเช่น:
# 127.0.0.1 application.com
# 127.0.0.1 application.it
# 127.0.0.1 application.pl
# ในไฟล์ /etc/hosts เพื่อลองใช้งานในเครื่องที่เป็นท้องถิ่น
def extract_locale_from_tld
parsed_locale = request.host.split('.').last
I18n.available_locales.map(&:to_s).include?(parsed_locale) ? parsed_locale : nil
end
เรายังสามารถตั้งค่าภาษาจาก subdomain ได้ในวิธีที่คล้ายกัน: ```ruby
รับรหัสภาษาจาก subdomain ของคำขอ (เช่น http://it.application.local:3000)
คุณต้องเพิ่มบางอย่างเช่น:
127.0.0.1 gr.application.local
ในไฟล์ /etc/hosts เพื่อทดสอบในเครื่อง localhost
def extract_locale_from_subdomain parsed_locale = request.subdomains.first I18n.available_locales.map(&:to_s).include?(parsed_locale) ? parsed_locale : nil end ```
หากแอปพลิเคชันของคุณมีเมนูสลับภาษา คุณจะต้องมีสิ่งที่คล้ายกันกับนี้:
link_to("Deutsch", "#{APP_CONFIG[:deutsch_website_url]}#{request.env['PATH_INFO']}")
โดยสมมติว่าคุณตั้งค่า APP_CONFIG[:deutsch_website_url]
เป็นค่าใดค่าหนึ่ง เช่น http://www.application.de
.
วิธีนี้มีข้อดีที่กล่าวมาแล้ว อย่างไรก็ตาม คุณอาจไม่สามารถหรืออาจไม่ต้องการให้มีการแปลงภาษา ("เวอร์ชันภาษา") ต่างกันบนโดเมนที่แตกต่างกันได้ วิธีที่ชัดเจนที่สุดคือการรวมรหัสภาษาในพารามิเตอร์ URL (หรือเส้นทางคำขอ)
1.4.2 การตั้งค่าภาษาจากพารามิเตอร์ URL
วิธีที่ส่วนใหญ่ใช้ในการตั้งค่า (และส่งต่อ) ภาษาคือการรวมมันในพารามิเตอร์ URL เช่นเราทำใน I18n.with_locale(params[:locale], &action)
around_action ในตัวอย่างแรก เราต้องการให้ URL ดูเช่น www.example.com/books?locale=ja
หรือ www.example.com/ja/books
ในกรณีนี้
วิธีนี้มีชุดข้อดีเกือบเหมือนกับการตั้งค่าภาษาจากชื่อโดเมน: คือเป็น RESTful และสอดคล้องกับเว็บโลกออนไลน์ทั้งหมด แต่จะต้องใช้งานเพิ่มเติมเล็กน้อย
การรับรหัสภาษาจาก params
และตั้งค่าตามนั้นไม่ยาก แต่การรวมมันในทุก URL และดังนั้น ส่งผ่านคำขอ จะเป็นเรื่องยาก การรวมตัวเลือกชัดเจนในทุก URL เช่น link_to(books_url(locale: I18n.locale))
จะเป็นงานที่น่าเบื่อและอาจเป็นไปไม่ได้
Rails มีโครงสร้างพื้นฐานสำหรับ "การจัดการการตัดสินใจเกี่ยวกับ URL" ใน ApplicationController#default_url_options
ซึ่งเป็นประโยชน์อย่างมากในสถานการณ์นี้: มันช่วยให้เราสามารถตั้งค่า "ค่าเริ่มต้น" สำหรับ url_for
และเมธอดช่วยที่ขึ้นอยู่กับมัน (โดยการดำเนินการ/แทนที่ default_url_options
)
เราสามารถรวมสิ่งที่คล้ายกันกับนี้ใน ApplicationController
ได้ดังนี้:
# app/controllers/application_controller.rb
def default_url_options
{ locale: I18n.locale }
end
เมื่อใช้เมธอดช่วยที่ขึ้นอยู่กับ url_for
(เช่นเมธอดช่วยสำหรับเส้นทางที่มีชื่อเช่น root_path
หรือ root_url
เส้นทางทรัพยากรเช่น books_path
หรือ books_url
เป็นต้น) จะ รวมรหัสภาษาในสตริงคิวรีสต์ ดังนี้: http://localhost:3001/?locale=ja
คุณอาจพอใจกับสิ่งนี้ แต่มันจะมีผลต่อความอ่าน URL เมื่อรหัสภาษา "ติดอยู่" ที่ส่วนท้ายของทุก URL ในแอปพลิเคชันของคุณ นอกจากนี้ จากมุมมองทางสถาปัตยกรรม ภาษาท้องถิ่นมักจะอยู่ในระดับที่สูงกว่าส่วนอื่น ๆ ของโดเมนแอปพลิเคชัน: และ URL ควรสะท้อนสิ่งนี้
คุณอาจต้องการให้ URL ดูเช่นนี้: http://www.example.com/en/books
(ภาษาอังกฤษ) และ http://www.example.com/nl/books
(ภาษาดัตช์) สามารถทำได้ด้วยวิธี "การแทนที่ default_url_options
" จากข้างต้น: คุณเพียงแค่ตั้งค่าเส้นทางของคุณด้วย scope
:
# config/routes.rb
scope "/:locale" do
resources :books
end
ตอนนี้เมื่อคุณเรียกใช้เมธอด books_path
คุณควรได้ "/en/books"
(สำหรับภาษาเริ่มต้น) URL เช่น http://localhost:3001/nl/books
ควรโหลดภาษาดัตช์ และเมื่อเรียกใช้ books_path
ต่อมาคุณควรได้ "/nl/books"
(เนื่องจากมีการเปลี่ยนแปลงภาษา)
คำเตือน. เนื่องจากค่าที่ส่งกลับจาก default_url_options
ถูกแคชต่อคำขอ ลิงก์ในตัวเลือกภาษาไม่สามารถสร้างขึ้นได้โดยเรียกใช้เมธอดช่วยในลูปที่ตั้งค่า I18n.locale
ในแต่ละรอบ แทนที่นั่ง I18n.locale
ไว้เดิมและส่งตัวเลือก :locale
แบบชัดเจนให้กับเมธอดช่วย หรือแก้ไข request.original_fullpath
หากคุณไม่ต้องการบังคับให้ใช้ locale ในเส้นทางของคุณ คุณสามารถใช้ scope เส้นทางทางเลือก (ที่ระบุโดยวงเล็บ) ได้ดังนี้:
# config/routes.rb
scope "(:locale)", locale: /en|nl/ do
resources :books
end
ด้วยวิธีนี้ คุณจะไม่ได้รับข้อผิดพลาดในการเข้าถึงทรัพยากรของคุณ เช่น http://localhost:3001/books
โดยไม่มี locale นี่เป็นประโยชน์เมื่อคุณต้องการใช้ locale เริ่มต้นเมื่อไม่ได้ระบุ
แน่นอนว่า คุณต้องดูแลเฉพาะ URL ราก (ที่เรียกว่า "homepage" หรือ "dashboard") ของแอปพลิเคชันของคุณ URL เช่น http://localhost:3001/nl
จะไม่ทำงานโดยอัตโนมัติ เนื่องจากการประกาศ root to: "dashboard#index"
ใน routes.rb
ของคุณไม่พิจารณา locale (และถูกต้อง: มีเพียง URL เดียว)
คุณอาจต้องแมป URL เช่นเหล่านี้:
# config/routes.rb
get '/:locale' => 'dashboard#index'
คุณต้องดูแลเฉพาะเรื่องของเส้นทางของคุณ ลำดับ, เพื่อให้การประกาศเส้นทางนี้ไม่ "กิน" เส้นทางอื่น ๆ (คุณอาจต้องเพิ่มโดยตรงก่อนการประกาศ root :to
)
หมายเหตุ: คุณควรดูที่ gem ต่าง ๆ ที่ทำให้ง่ายต่อการทำงานกับเส้นทาง: routing_filter, route_translator.
1.4.3 ตั้งค่า Locale จากการตั้งค่าของผู้ใช้
แอปพลิเคชันที่มีผู้ใช้ที่ได้รับการรับรองอาจอนุญาตให้ผู้ใช้ตั้งค่าลักษณะท้องถิ่นผ่านอินเตอร์เฟซของแอปพลิเคชัน ด้วยวิธีนี้ การตั้งค่าลักษณะท้องถิ่นที่ผู้ใช้เลือกจะถูกบันทึกในฐานข้อมูลและใช้ในการตั้งค่าลักษณะท้องถิ่นสำหรับคำขอที่รับรองโดยผู้ใช้นั้น
around_action :switch_locale
def switch_locale(&action)
locale = current_user.try(:locale) || I18n.default_locale
I18n.with_locale(locale, &action)
end
1.4.4 เลือก Locale ที่นำมาคาดเดา
เมื่อไม่ได้ตั้งค่า locale โดยชัดเจนสำหรับคำขอ (เช่นผ่านวิธีด้านบน) แอปพลิเคชันควรพยายามคาดเดา locale ที่ต้องการ
1.4.4.1 การคาดเดา Locale จากส่วนหัวภาษา
ส่วนหัว HTTP Accept-Language
ระบุภาษาที่ต้องการสำหรับการตอบสนองของคำขอ บราวเซอร์ ตั้งค่าค่าส่วนหัวนี้ตามการตั้งค่าภาษาของผู้ใช้ ทำให้เป็นตัวเลือกแรกที่ดีเมื่อคาดเดา locale
การใช้ส่วนหัว Accept-Language
อย่างง่ายจะเป็นดังนี้:
def switch_locale(&action)
logger.debug "* Accept-Language: #{request.env['HTTP_ACCEPT_LANGUAGE']}"
locale = extract_locale_from_accept_language_header
logger.debug "* Locale set to '#{locale}'"
I18n.with_locale(locale, &action)
end
private
def extract_locale_from_accept_language_header
request.env['HTTP_ACCEPT_LANGUAGE'].scan(/^[a-z]{2}/).first
end
ในการปฏิบัติจริง จำเป็นต้องใช้โค้ดที่เสถียรมากขึ้นเพื่อทำให้สามารถทำได้เป็นประจำ ไลบรารี http_accept_language ของ Iain Hecker หรือ middleware Rack locale ของ Ryan Tomayko จะช่วยแก้ปัญหานี้
1.4.4.2 การคาดเดา Locale จาก IP Geolocation
ที่อยู่ IP ของไคลเอนต์ที่ทำคำขอสามารถใช้ในการคาดเดาภูมิภาคของไคลเอนต์และดังนั้น locale ของพวกเขา บริการเช่น GeoLite2 Country หรือ gem เช่น geocoder สามารถใช้ในการดำเนินการนี้ได้
โดยทั่วไป วิธีนี้น้อยเสียกว่าการใช้ส่วนหัวภาษาและไม่แนะนำสำหรับแอปพลิเคชันเว็บที่มากที่สุด
1.4.5 การเก็บ Locale จากเซสชันหรือคุกกี้
คำเตือน: คุณอาจจะมีความใจจะเก็บ locale ที่เลือกไว้ใน เซสชัน หรือ คุกกี้ อย่างไรก็ตาม อย่าทำเช่นนี้ locale ควรเป็นโปร่งใสและเป็นส่วนหนึ่งของ URL นี่จะทำให้คุณไม่ทำลายความคาดหวังพื้นฐานของผู้คนเกี่ยวกับเว็บเอง: หากคุณส่ง URL ไปยังเพื่อน พวกเขาควรเห็นหน้าเว็บและเนื้อหาเดียวกันกับคุณ คำที่เรียบง่ายสำหรับสิ่งนี้คือคุณกำลังเป็น RESTful อ่านเพิ่มเติมเกี่ยวกับการใช้วิธีการ RESTful ใน บทความของ Stefan Tilkov บางครั้งอาจมีข้อยกเว้นในกฎนี้และจะถูกพูดถึงด้านล่าง
2 การนำเสนอและการใช้งานในระดับสากล
ดี! ตอนนี้คุณได้เริ่มต้นการสนับสนุน I18n สำหรับแอปพลิเคชัน Ruby on Rails ของคุณและบอกให้มันใช้ locale ใด ๆ และวิธีการเก็บรักษามันระหว่างคำขอ
ถัดไปเราต้อง นำเสนอให้แอปพลิเคชันของเราสามารถใช้งานได้ในระดับสากล โดยการแยกส่วนที่เกี่ยวข้องกับแต่ละ locale ในท้ายที่สุด เราต้อง ให้แปลเอกสาร โดยให้แปลส่วนที่เราได้แยกออกมา
โดยใช้ตัวอย่างต่อไปนี้:
# config/routes.rb
Rails.application.routes.draw do
root to: "home#index"
end
# app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
around_action :switch_locale
def switch_locale(&action)
locale = params[:locale] || I18n.default_locale
I18n.with_locale(locale, &action)
end
end
# app/controllers/home_controller.rb
class HomeController < ApplicationController
def index
flash[:notice] = "Hello Flash"
end
end
<!-- app/views/home/index.html.erb -->
<h1>Hello World</h1>
<p><%= flash[:notice] %></p>
2.1 การแยกส่วนของรหัสที่ใช้งานได้ในระดับสากล
ในรหัสของเรา มีสตริงสองตัวที่เขียนเป็นภาษาอังกฤษที่จะถูกแสดงในการตอบสนองของเรา ("Hello Flash" และ "Hello World") เพื่อที่จะทำให้สามารถใช้งานได้ในระดับสากล สตริงเหล่านี้จำเป็นต้องถูกแทนที่ด้วยการเรียกใช้ #t
ของ Rails พร้อมกับคีย์ที่เหมาะสมสำหรับแต่ละสตริง:
# app/controllers/home_controller.rb
class HomeController < ApplicationController
def index
flash[:notice] = t(:hello_flash)
end
end
<!-- app/views/home/index.html.erb -->
<h1><%= t :hello_world %></h1>
<p><%= flash[:notice] %></p>
ตอนนี้ เมื่อมีการแสดงผลวิวนี้ มันจะแสดงข้อความข้อผิดพลาดที่บอกว่าข้อความแปลสำหรับคีย์ :hello_world
และ :hello_flash
หายไป
หมายเหตุ: Rails เพิ่มเมธอด t
(translate
) เข้าไปในวิวของคุณเพื่อให้คุณไม่ต้องพิมพ์ I18n.t
ตลอดเวลา นอกจากนี้ เมธอดช่วยในการตรวจจับการแปลที่หายไปและห่อหุ้มข้อความข้อผิดพลาดที่ได้รับเป็น <span class="translation_missing">
.
2.2 การให้บริการแปลสำหรับสตริงที่ใช้งานได้ในระดับสากล
เพิ่มการแปลที่หายไปลงในไฟล์พจนานุกรมแปล:
# config/locales/en.yml
en:
hello_world: Hello world!
hello_flash: Hello flash!
# config/locales/pirate.yml
pirate:
hello_world: Ahoy World
hello_flash: Ahoy Flash
เนื่องจาก default_locale
ไม่เปลี่ยนแปลง การแปลใช้ locale :en
และการตอบสนองจะแสดงสตริงภาษาอังกฤษ:
หาก locale ถูกตั้งค่าผ่าน URL เป็น locale ของโจรสลัด (http://localhost:3000?locale=pirate
) การตอบสนองจะแสดงสตริงของโจรสลัด:
หมายเหตุ: คุณต้องรีสตาร์ทเซิร์ฟเวอร์เมื่อคุณเพิ่มไฟล์ locale ใหม่
คุณสามารถใช้ไฟล์ YAML (.yml
) หรือไฟล์ Ruby (.rb
) เพื่อเก็บแปลงานของคุณใน SimpleStore ได้ ไฟล์ YAML เป็นตัวเลือกที่นิยมในความคิดของนักพัฒนา Rails อย่างไรก็ตาม มันมีข้อเสียหนึ่ง คือ YAML มีความไวต่อช่องว่างและอักขระพิเศษ ดังนั้นแอปพลิเคชันอาจจะไม่โหลดพจนานุกรมของคุณได้อย่างถูกต้อง ไฟล์ Ruby จะทำให้แอปพลิเคชันของคุณล้มเหลวในคำขอแรก ดังนั้นคุณสามารถหาวิธีแก้ไขได้ง่าย (หากคุณพบปัญหา "ปัญหาแปลก ๆ" ในพจนานุกรม YAML ลองใส่ส่วนที่เกี่ยวข้องของพจนานุกรมของคุณลงในไฟล์ Ruby)
หากแปลงานของคุณถูกเก็บไว้ในไฟล์ YAML คุณต้องใช้การหนีตัวอักษรบางตัว ดังต่อไปนี้:
- true, on, yes
- false, off, no
ตัวอย่าง:
# config/locales/en.yml
en:
success:
'true': 'True!'
'on': 'On!'
'false': 'False!'
failure:
true: 'True!'
off: 'Off!'
false: 'False!'
I18n.t 'success.true' # => 'True!'
I18n.t 'success.on' # => 'On!'
I18n.t 'success.false' # => 'False!'
I18n.t 'failure.false' # => Translation Missing
I18n.t 'failure.off' # => Translation Missing
I18n.t 'failure.true' # => Translation Missing
2.3 การส่งตัวแปรไปยังการแปล
หนึ่งในปัจจัยสำคัญในการทำให้แอปพลิเคชันสามารถรองรับการใช้งานในหลายภาษาได้อย่างสำเร็จคือการหลีกเลี่ยงการสร้างคาดการณ์ที่ไม่ถูกต้องเกี่ยวกับกฏไวยากรณ์เมื่อทำการแปลโค้ดในภาษาท้องถิ่น กฏไวยากรณ์ที่ดูเป็นพื้นฐานในภาษาหนึ่งอาจจะไม่เป็นจริงในภาษาอื่น
การสร้างคาดการณ์ที่ไม่ถูกต้องแสดงในตัวอย่างต่อไปนี้ ที่ทำการสร้างคาดการณ์เกี่ยวกับลำดับของส่วนต่าง ๆ ของการแปล โปรดทราบว่า Rails มีฟังก์ชันช่วยเหลือ number_to_currency
เพื่อจัดการกรณีดังกล่าว
<!-- app/views/products/show.html.erb -->
<%= "#{t('currency')}#{@product.price}" %>
# config/locales/en.yml
en:
currency: "$"
# config/locales/es.yml
es:
currency: "€"
หากราคาสินค้าเป็น 10 แล้วการแปลที่ถูกต้องสำหรับภาษาสเปนคือ "10 €" แทนที่จะเป็น "€10" แต่การสร้างคาดการณ์ไม่สามารถให้ได้
เพื่อสร้างคาดการณ์ที่ถูกต้อง แพ็กเกจ I18n มาพร้อมกับคุณสมบัติที่เรียกว่าตัวแปร interpolation ซึ่งช่วยให้คุณสามารถใช้ตัวแปรในการกำหนดค่าแปลและส่งค่าสำหรับตัวแปรเหล่านี้ไปยังเมธอดแปล
ตัวอย่างการสร้างคาดการณ์ที่ถูกต้องแสดงดังนี้:
<!-- app/views/products/show.html.erb -->
<%= t('product_price', price: @product.price) %>
# config/locales/en.yml
en:
product_price: "$%{price}"
# config/locales/es.yml
es:
product_price: "%{price} €"
การตัดสินใจเกี่ยวกับไวยากรณ์และเครื่องหมายวรรคตอนทั้งหมดถูกตัดสินในการกำหนดเอง ดังนั้นการสร้างคาดการณ์สามารถให้แปลได้อย่างถูกต้อง
หมายเหตุ: คำสงวน default
และ scope
ไม่สามารถใช้เป็นชื่อตัวแปรได้ หากใช้งานจะเกิดข้อยกเว้น I18n::ReservedInterpolationKey
หากการแปลคาดหวังว่าจะมีตัวแปร interpolation แต่ไม่ได้รับการส่งไปยัง #translate
จะเกิดข้อยกเว้น I18n::MissingInterpolationArgument
2.4 เพิ่มรูปแบบวันที่/เวลา
โอเค! ตอนนี้เรามาเพิ่มการแสดงเวลาในหน้ามุมมอง เพื่อให้เราสามารถสาธิตฟีเจอร์การแปล วันที่/เวลา ได้ด้วย ในการแปลรูปแบบเวลาคุณสามารถส่งวัตถุเวลาไปยัง I18n.l
หรือ (แนะนำ) ใช้ช่วยเหลือของ Rails #l
คุณสามารถเลือกรูปแบบโดยส่งตัวเลือก :format
- โดยค่าเริ่มต้นจะใช้รูปแบบ :default
<!-- app/views/home/index.html.erb -->
<h1><%= t :hello_world %></h1>
<p><%= flash[:notice] %></p>
<p><%= l Time.now, format: :short %></p>
และในไฟล์แปลภาษาโจรสำหรับเราเพิ่มรูปแบบเวลา (มันอยู่ในค่าเริ่มต้นของ Rails สำหรับภาษาอังกฤษแล้ว):
# config/locales/pirate.yml
pirate:
time:
formats:
short: "arrrround %H'ish"
ดังนั้นนั่นคือ:
เคล็ดลับ: ขณะนี้คุณอาจจะต้องเพิ่มรูปแบบวันที่/เวลาเพิ่มเติมเพื่อให้เครื่องมือ I18n ทำงานตามที่คาดหวัง (อย่างน้อยสำหรับภาษา 'pirate') แน่นอนว่ามีโอกาสที่ดีว่ามีคนทำงานทั้งหมดแล้วโดยการ แปลค่าเริ่มต้นของ Rails สำหรับภาษาของคุณ ดูที่ rails-i18n repository at GitHub เพื่อดูเอกสารของไฟล์แปลภาษาต่าง ๆ ตอนที่คุณวางไฟล์เหล่านั้นในไดเรกทอรี config/locales/
พวกเขาจะพร้อมใช้งานโดยอัตโนมัติ
2.5 กฎการเปลี่ยนรูปของภาษาอื่น ๆ
Rails ช่วยให้คุณสามารถกำหนดกฎการเปลี่ยนรูป (เช่นกฎสำหรับการกำหนดรูปของคำพหูพจน์และคำกริยาในรูปสกุลเดียวกัน) สำหรับภาษาอื่นนอกเหนือจากภาษาอังกฤษ ใน config/initializers/inflections.rb
คุณสามารถกำหนดกฎเหล่านี้สำหรับภาษาหลาย ๆ ภาษา ไฟล์เริ่มต้นมีตัวอย่างเริ่มต้นสำหรับการระบุกฎเพิ่มเติมสำหรับภาษาอังกฤษ คุณสามารถทำตามรูปแบบนั้นสำหรับภาษาอื่น ๆ ตามที่คุณเห็นสมควร
2.6 มุมมองท้องถิ่น
เราสมมติว่าคุณมี BooksController ในแอปพลิเคชันของคุณ การกระทำ index จะแสดงเนื้อหาในเทมเพลต app/views/books/index.html.erb
เมื่อคุณวาง localized variant ของเทมเพลตนี้: index.es.html.erb
ในไดเรกทอรีเดียวกัน Rails จะแสดงเนื้อหาในเทมเพลตนี้เมื่อตั้งค่า locale เป็น :es
เมื่อตั้งค่า locale เป็น default locale จะใช้เทมเพลต index.html.erb
ทั่วไป (เวอร์ชันข้างหน้าของ Rails อาจนำเสนอการ localiztion automagic นี้ไปยังส่วนทรัพยากรใน public
เป็นต้น)
คุณสามารถใช้คุณสมบัตินี้ได้ เช่น เมื่อทำงานกับเนื้อหาสถิตที่มีจำนวนมากซึ่งจะเป็นซับซ้อนในการวางใน YAML หรือ Ruby dictionaries อย่างไรก็ตาม โปรดจำไว้ว่าการเปลี่ยนแปลงใด ๆ ที่คุณต้องการทำในภายหลังกับเทมเพลตจะต้องถูกแพร่กระจายไปยังทั้งหมด
2.7 การจัดระเบียบไฟล์ Locale
เมื่อคุณใช้ SimpleStore เริ่มต้นที่จัดส่งกับไลบรารี i18n พจนานุกรมจะถูกเก็บไว้ในไฟล์ข้อความธรรมดาบนดิสก์ การวางแปลสำหรับส่วนทั้งหมดของแอปพลิเคชันของคุณในไฟล์เดียวต่อภาษาอาจยากต่อการจัดการ คุณสามารถเก็บไฟล์เหล่านี้ในโครงสร้างที่มีความหมายสำหรับคุณ
ตัวอย่างเช่น ไดเรกทอรี config/locales
ของคุณอาจมีลักษณะดังนี้:
|-defaults
|---es.yml
|---en.yml
|-models
|---book
|-----es.yml
|-----en.yml
|-views
|---defaults
|-----es.yml
|-----en.yml
|---books
|-----es.yml
|-----en.yml
|---users
|-----es.yml
|-----en.yml
|---navigation
|-----es.yml
|-----en.yml
ด้วยวิธีนี้คุณสามารถแยกชื่อโมเดลและแอตทริบิวต์ของโมเดลจากข้อความภายในวิว และทั้งหมดนี้จาก "ค่าเริ่มต้น" (เช่น รูปแบบวันที่และเวลา) ร้ายอาจารย์อื่น ๆ สำหรับไลบรารี i18n อาจให้วิธีการแยกแยะที่แตกต่างกัน
หมายเหตุ: กลไกการโหลด locale เริ่มต้นใน Rails ไม่โหลดไฟล์ locale ในพจนานุกรมที่ซ้อนกัน เช่นเรามีที่นี่ ดังนั้นเพื่อให้สามารถทำงานได้เราต้องบอก Rails ให้มองหาไกด์เพิ่มเติม:
# config/application.rb
config.i18n.load_path += Dir[Rails.root.join('config', 'locales', '**', '*.{rb,yml}')]
3 ภาพรวมของคุณลักษณะ API ของ I18n
คุณควรมีความเข้าใจที่ดีในการใช้ไลบรารี i18n และรู้วิธีการนำมาใช้ในแอปพลิเคชัน Rails พื้นฐาน ในบทต่อไป เราจะพิจารณาคุณลักษณะของมันอย่างละเอียดขึ้น
บทนี้จะแสดงตัวอย่างโดยใช้วิธี I18n.translate
และ translate
view helper method (โดยทำความเข้าใจถึงคุณลักษณะเพิ่มเติมที่ให้โดยเมธอดช่วยเหลือในการมองเห็น)
ครอบคลุมคุณลักษณะเช่นเหล่านี้:
- การค้นหาแปลภาษา
- การแทรกข้อมูลในแปลภาษา
- การกำหนดพหูพจน์ในแปลภาษา
- การใช้แปลภาษา HTML ที่ปลอดภัย (เฉพาะเมธอดช่วยเหลือในการมองเห็นเท่านั้น)
- การให้ค่าวันที่เป็นภาษาท้องถิ่น ตัวเลข สกุลเงิน เป็นต้น
3.1 การค้นหาแปลภาษา
3.1.1 การค้นหาพื้นฐาน ขอบเขต และคีย์ที่ซ้อนกัน
แปลภาษาจะถูกค้นหาโดยใช้คีย์ซึ่งสามารถเป็นทั้งสัญลักษณ์และสตริง ดังนั้นการเรียกใช้เหล่านี้เทียบเท่ากัน:
I18n.t :message
I18n.t 'message'
เมธอด translate
ยังรับตัวเลือก :scope
ซึ่งสามารถมีคีย์เพิ่มเติมหนึ่งหรือมากกว่าที่จะใช้ในการระบุ "เนมสเปซ" หรือขอบเขตสำหรับคีย์แปลภาษา:
I18n.t :record_invalid, scope: [:activerecord, :errors, :messages]
นี้จะค้นหาข้อความ :record_invalid
ในข้อความข้อผิดพลาดของ Active Record
นอกจากนี้ ทั้งคีย์และขอบเขตสามารถระบุเป็นคีย์ที่แยกกันด้วยจุดได้ดังนี้:
I18n.translate "activerecord.errors.messages.record_invalid"
ดังนั้นการเรียกใช้เหล่านี้เทียบเท่ากัน:
ruby
I18n.t 'activerecord.errors.messages.record_invalid'
I18n.t 'errors.messages.record_invalid', scope: :activerecord
I18n.t :record_invalid, scope: 'activerecord.errors.messages'
I18n.t :record_invalid, scope: [:activerecord, :errors, :messages]
3.1.2 ค่าเริ่มต้น
เมื่อมีตัวเลือก :default
ถูกกำหนดไว้ ค่าของมันจะถูกส่งกลับหากไม่พบการแปล:
I18n.t :missing, default: 'ไม่มีที่นี่'
# => 'ไม่มีที่นี่'
หากค่า :default
เป็นสัญลักษณ์ จะถูกใช้เป็นคีย์และถูกแปลงค่า สามารถกำหนดค่าเริ่มต้นหลายค่าได้ ค่าแรกที่ได้ผลลัพธ์จะถูกส่งกลับ
ตัวอย่างเช่น ต่อไปนี้จะพยายามแปลคีย์ :missing
และแปลคีย์ :also_missing
หากทั้งสองไม่ได้ผลลัพธ์ จะส่งกลับสตริง "ไม่มีที่นี่":
I18n.t :missing, default: [:also_missing, 'ไม่มีที่นี่']
# => 'ไม่มีที่นี่'
3.1.3 การค้นหาแบบกลุ่มและเนมสเปซ
เพื่อค้นหาแปลหลายรายการในครั้งเดียว สามารถส่งอาร์เรย์ของคีย์ได้:
I18n.t [:odd, :even], scope: 'errors.messages'
# => ["ต้องเป็นเลขคี่", "ต้องเป็นเลขคู่"]
นอกจากนี้ คีย์สามารถแปลงเป็นแฮช (hash) ที่แบ่งกลุ่มแปลได้ (อาจมีการซ้อนกัน) ตัวอย่างเช่น สามารถรับข้อความข้อผิดพลาดทั้งหมดของ Active Record ในรูปแบบแฮชได้ดังนี้:
I18n.t 'errors.messages'
# => {:inclusion=>"ไม่ได้รับอยู่ในรายการ", :exclusion=> ... }
หากต้องการทำตัวแทนที่ในแฮชของแปลภายใน คุณต้องส่ง deep_interpolation: true
เป็นพารามิเตอร์ หากคุณมีพจนานุกรมต่อไปนี้:
en:
welcome:
title: "ยินดีต้อนรับ!"
content: "ยินดีต้อนรับสู่ %{app_name}"
แล้วการทำตัวแทนที่ซ้อนกันจะถูกละเว้นโดยไม่ตั้งค่า:
I18n.t 'welcome', app_name: 'ร้านหนังสือ'
# => {:title=>"ยินดีต้อนรับ!", :content=>"ยินดีต้อนรับสู่ %{app_name}"}
I18n.t 'welcome', deep_interpolation: true, app_name: 'ร้านหนังสือ'
# => {:title=>"ยินดีต้อนรับ!", :content=>"ยินดีต้อนรับสู่ร้านหนังสือ"}
3.1.4 การค้นหาแบบ "Lazy"
Rails มีวิธีการสะดวกในการค้นหาภาษาใน views เมื่อคุณมีพจนานุกรมต่อไปนี้:
es:
books:
index:
title: "ชื่อเรื่อง"
คุณสามารถค้นหาค่า books.index.title
ภายใน เทมเพลต app/views/books/index.html.erb
ได้ดังนี้ (โปรดทราบจุด):
<%= t '.title' %>
หมายเหตุ: การค้นหาแบบ "Lazy" สามารถใช้ได้ในคอนโทรลเลอร์เช่นกัน:
en:
books:
create:
success: สร้างหนังสือแล้ว!
นี้เป็นประโยชน์ในการตั้งค่าข้อความแฟลชเช่น:
class BooksController < ApplicationController
def create
# ...
redirect_to books_url, notice: t('.success')
end
end
3.2 การกำหนดพหูพจน์
ในภาษาหลายภาษา - รวมถึงภาษาอังกฤษ - มีเพียงสองรูปแบบ คือ พหูพจน์และพหูพจน์ สำหรับสตริงที่กำหนด เช่น "1 ข้อความ" และ "2 ข้อความ" ภาษาอื่น ๆ (อาหรับ, ญี่ปุ่น, รัสเซีย และอื่น ๆ) มีกฎไวยากรณ์ที่แตกต่างกันซึ่งมีรูปแบบพหูพจน์เพิ่มเติมหรือน้อยกว่า รูปแบบพหูพจน์ ดังนั้น I18n API มีคุณสมบัติการกำหนดพหูพจน์ที่ยืดหยุ่น
ตัวแปรการตัดสินใจ :count
มีบทบาทพิเศษในที่ว่ามันจะถูกตัดสินใจในการแปลและใช้ในการเลือกพหูพจน์จากการแปลตามกฎพหูพจน์ที่กำหนดในพื้นหลังการตัดสินใจพหูพจน์ ตามค่าเริ่มต้น จะใช้กฎพหูพจน์ภาษาอังกฤษเท่านั้น
I18n.backend.store_translations :en, inbox: {
zero: 'ไม่มีข้อความ', # ไม่จำเป็น
one: '1 ข้อความ',
other: '%{count} ข้อความ'
}
I18n.translate :inbox, count: 2
# => '2 ข้อความ'
I18n.translate :inbox, count: 1
# => '1 ข้อความ'
I18n.translate :inbox, count: 0
# => 'ไม่มีข้อความ'
อัลกอริทึมสำหรับการพหุนามใน :en
มีความง่ายดังนี้:
lookup_key = :zero if count == 0 && entry.has_key?(:zero)
lookup_key ||= count == 1 ? :one : :other
entry[lookup_key]
การแปลที่ระบุว่า :one
ถือเป็นรูปเดียว และ :other
ถูกใช้เป็นรูปพหุนาม หากจำนวนเป็นศูนย์ และมีรายการ :zero
อยู่ จะใช้แทน :other
แทน
หากการค้นหาคีย์ไม่ส่งคืน Hash ที่เหมาะสมสำหรับการพหุนาม จะเกิดข้อยกเว้น I18n::InvalidPluralizationData
3.2.1 กฎเฉพาะของภาษา
แพ็กเกจ I18n มี Pluralization backend ที่ใช้เพื่อเปิดใช้กฎเฉพาะของภาษา รวมกับ Simple backend แล้ว เพิ่มอัลกอริทึมพหุนามของภาษาที่แปลงไว้ใน translation store เป็น i18n.plural.rule
I18n::Backend::Simple.include(I18n::Backend::Pluralization)
I18n.backend.store_translations :pt, i18n: { plural: { rule: lambda { |n| [0, 1].include?(n) ? :one : :other } } }
I18n.backend.store_translations :pt, apples: { one: 'one or none', other: 'more than one' }
I18n.t :apples, count: 0, locale: :pt
# => 'one or none'
หรือใช้แพ็กเสริมแยกออกมา rails-i18n เพื่อให้ได้กฎพหุนามของภาษาที่ครบถ้วนมากขึ้น
3.3 การตั้งค่าและส่งต่อภาษา
ภาษาสามารถตั้งค่าเป็น pseudo-globally ที่ I18n.locale
(ซึ่งใช้ Thread.current
เช่นเดียวกับ Time.zone
เป็นต้น) หรือสามารถส่งผ่านเป็นตัวเลือกให้กับ #translate
และ #localize
ได้
หากไม่มีการส่งภาษา จะใช้ I18n.locale
:
I18n.locale = :de
I18n.t :foo
I18n.l Time.now
การส่งภาษาโดยตรง:
I18n.t :foo, locale: :de
I18n.l Time.now, locale: :de
I18n.locale
มีค่าเริ่มต้นเป็น I18n.default_locale
ซึ่งมีค่าเริ่มต้นเป็น :en
สามารถตั้งค่าภาษาเริ่มต้นได้ดังนี้:
I18n.default_locale = :de
3.4 การใช้งานการแปลภาษาที่ปลอดภัยสำหรับ HTML
คีย์ที่มี '_html' ต่อท้ายและคีย์ที่ชื่อ 'html' ถูกทำเครื่องหมายว่าปลอดภัยสำหรับ HTML โดยเมื่อใช้ในมุมมอง HTML จะไม่ถูกหลีกเลี่ยง
# config/locales/en.yml
en:
welcome: <b>welcome!</b>
hello_html: <b>hello!</b>
title:
html: <b>title!</b>
<!-- app/views/home/index.html.erb -->
<div><%= t('welcome') %></div>
<div><%= raw t('welcome') %></div>
<div><%= t('hello_html') %></div>
<div><%= t('title.html') %></div>
การตัดคำตามต้องการจะถูกทำเครื่องหมายตามที่จำเป็น ตัวอย่างเช่น กำหนดให้:
en:
welcome_html: "<b>Welcome %{username}!</b>"
คุณสามารถส่งชื่อผู้ใช้ที่ถูกตั้งค่าโดยผู้ใช้ได้อย่างปลอดภัย:
<%# นี่เป็นปลอดภัย มันจะถูกตัดคำตามความจำเป็น %>
<%= t('welcome_html', username: @current_user.username) %>
สตริงที่ปลอดภัยอย่างอื่น ๆ จะถูกตัดคำตามต้องการ
หมายเหตุ: การแปลงอัตโนมัติเป็น HTML safe ข้อความแปลเท่านั้นที่สามารถใช้ได้จากเมธอดช่วย translate
(หรือ t
) นี้ สามารถใช้งานได้ในมุมมองและคอนโทรลเลอร์
3.5 การแปลภาษาสำหรับโมเดล Active Record
คุณสามารถใช้เมธอด Model.model_name.human
และ Model.human_attribute_name(attribute)
เพื่อค้นหาแปลภาษาสำหรับโมเดลและชื่อแอตทริบิวต์ของคุณได้อย่างโปร่งใส
ตัวอย่างเช่นเมื่อคุณเพิ่มแปลภาษาต่อไปนี้:
en:
activerecord:
models:
user: Customer
attributes:
user:
login: "Handle"
# จะแปลงแอตทริบิวต์ User "login" เป็น "Handle"
จากนั้น User.model_name.human
จะคืนค่าเป็น "Customer" และ User.human_attribute_name("login")
จะคืนค่าเป็น "Handle"
คุณยังสามารถตั้งค่ารูปพหุนามสำหรับชื่อโมเดลได้อีกด้วย โดยเพิ่มดังนี้:
en:
activerecord:
models:
user:
one: Customer
other: Customers
แล้ว User.model_name.human(count: 2)
จะคืนค่าเป็น "ลูกค้า" ด้วย count: 1
หรือโดยไม่ระบุพารามิเตอร์จะคืนค่าเป็น "ลูกค้า"
ในกรณีที่คุณต้องการเข้าถึงแอตทริบิวต์ที่ซ้อนกันภายในโมเดลที่กำหนด คุณควรซ้อนเหล่านี้ภายใต้ model/attribute
ที่ระดับโมเดลของไฟล์แปลของคุณ:
en:
activerecord:
attributes:
user/role:
admin: "ผู้ดูแลระบบ"
contributor: "ผู้ร่วมสนContributor"
แล้ว User.human_attribute_name("role.admin")
จะคืนค่าเป็น "ผู้ดูแลระบบ"
หมายเหตุ: หากคุณกำลังใช้คลาสที่รวม ActiveModel
และไม่สืบทอดจาก ActiveRecord::Base
ให้แทนที่ activerecord
ด้วย activemodel
ในเส้นทางคีย์ด้านบน
3.5.1 ขอบเขตข้อความข้อผิดพลาด
ข้อความข้อผิดพลาดในการตรวจสอบของ Active Record ยังสามารถแปลงแปลงได้อย่างง่ายดาย Active Record ให้คุณใช้งานเนมสเปซที่คุณสามารถวางแปลงข้อความของคุณได้เพื่อให้ได้ข้อความและการแปลงแตกต่างสำหรับโมเดลที่แตกต่างกัน แอตทริบิวต์ และ/หรือการตรวจสอบที่แตกต่างกัน นอกจากนี้ยังรองรับการสืบทอดตารางเดียวเองอย่างโปร่งใส
นี้จะให้คุณมีวิธีที่มีประสิทธิภาพในการปรับแต่งข้อความข้อผิดพลาดให้เหมาะสมกับความต้องการของแอปพลิเคชันของคุณ
พิจารณาโมเดล User ที่มีการตรวจสอบสำหรับแอตทริบิวต์ชื่อดังนี้:
class User < ApplicationRecord
validates :name, presence: true
end
คีย์สำหรับข้อความข้อผิดพลาดในกรณีนี้คือ :blank
Active Record จะค้นหาคีย์นี้ในเนมสเปซต่อไปนี้:
activerecord.errors.models.[model_name].attributes.[attribute_name]
activerecord.errors.models.[model_name]
activerecord.errors.messages
errors.attributes.[attribute_name]
errors.messages
ดังนั้นในตัวอย่างของเราจะลองค้นหาคีย์ต่อไปนี้ตามลำดับนี้และคืนค่าผลลัพธ์แรก:
activerecord.errors.models.user.attributes.name.blank
activerecord.errors.models.user.blank
activerecord.errors.messages.blank
errors.attributes.name.blank
errors.messages.blank
เมื่อโมเดลของคุณใช้การสืบทอดเพิ่มเติม ข้อความจะถูกค้นหาในลำดับการสืบทอด
ตัวอย่างเช่น คุณอาจมีโมเดล Admin ที่สืบทอดจาก User:
class Admin < User
validates :name, presence: true
end
แล้ว Active Record จะค้นหาข้อความตามลำดับนี้:
activerecord.errors.models.admin.attributes.name.blank
activerecord.errors.models.admin.blank
activerecord.errors.models.user.attributes.name.blank
activerecord.errors.models.user.blank
activerecord.errors.messages.blank
errors.attributes.name.blank
errors.messages.blank
ด้วยวิธีนี้คุณสามารถให้แปลงแปลงพิเศษสำหรับข้อความข้อผิดพลาดต่างๆ ที่จุดต่างๆ ในโมเดลที่สืบทอดและในแอตทริบิวต์ โมเดล หรือขอบเขตเริ่มต้น
3.5.2 การแทรกข้อความข้อผิดพลาด
ชื่อโมเดลที่แปลแล้ว ชื่อแอตทริบิวต์ที่แปลแล้ว และค่าที่แปลแล้วจะมีอยู่เสมอสำหรับการแทรกข้อความเป็น model
attribute
และ value
ตามลำดับ
ดังนั้น เช่น แทนที่ข้อความข้อผิดพลาดเริ่มต้น "cannot be blank"
คุณสามารถใช้ชื่อแอตทริบิวต์เช่นนี้: "กรุณากรอก %{attribute} ของคุณ"
.
count
ถ้ามีอยู่ สามารถใช้สำหรับการกำหนดพหูพจน์:
การตรวจสอบ | พร้อมตัวเลือก | ข้อความ | การแทรกข้อความ |
---|---|---|---|
confirmation | - | :confirmation | attribute |
acceptance | - | :accepted | - |
presence | - | :blank | - |
absence | - | :present | - |
length | :within, :in | :too_short | count |
length | :within, :in | :too_long | count |
length | :is | :wrong_length | count |
length | :minimum | :too_short | count |
length | :maximum | :too_long | count |
uniqueness | - | :taken | - |
format | - | :invalid | - |
inclusion | - | :inclusion | - |
exclusion | - | :exclusion | - |
associated | - | :invalid | - |
non-optional association | - | :required | - |
numericality | - | :not_a_number | - |
numericality | :greater_than | :greater_than | count |
numericality | :greater_than_or_equal_to | :greater_than_or_equal_to | count |
numericality | :equal_to | :equal_to | count |
numericality | :less_than | :less_than | count |
numericality | :less_than_or_equal_to | :less_than_or_equal_to | count |
numericality | :other_than | :other_than | count |
numericality | :only_integer | :not_an_integer | - |
numericality | :in | :in | count |
numericality | :odd | :odd | - |
numericality | :even | :even | - |
comparison | :greater_than | :greater_than | count |
comparison | :greater_than_or_equal_to | :greater_than_or_equal_to | count |
comparison | :equal_to | :equal_to | count |
comparison | :less_than | :less_than | count |
comparison | :less_than_or_equal_to | :less_than_or_equal_to | count |
comparison | :other_than | :other_than | count |
3.6 การแปลสำหรับหัวข้ออีเมล Action Mailer
หากคุณไม่ส่งหัวข้อให้กับเมธอด mail
Action Mailer จะพยายามหาหัวข้อในการแปลของคุณ การค้นหาที่ทำจะใช้รูปแบบ <mailer_scope>.<action_name>.subject
เพื่อสร้างคีย์
# user_mailer.rb
class UserMailer < ActionMailer::Base
def welcome(user)
#...
end
end
en:
user_mailer:
welcome:
subject: "ยินดีต้อนรับสู่ Rails Guides!"
ในการส่งพารามิเตอร์ไปยังการตัดคำใช้เมธอด default_i18n_subject
บนเมลเลอร์
# user_mailer.rb
class UserMailer < ActionMailer::Base
def welcome(user)
mail(to: user.email, subject: default_i18n_subject(user: user.name))
end
end
en:
user_mailer:
welcome:
subject: "%{user}, ยินดีต้อนรับสู่ Rails Guides!"
3.7 ภาพรวมของเมธอดอื่น ๆ ที่มีการให้การสนับสนุน I18n
Rails ใช้สตริงคงที่และการแปลอื่น ๆ เช่นสตริงรูปแบบและข้อมูลรูปแบบอื่น ๆ ในหลายๆ เมธอดช่วยเหลือ นี่คือภาพรวมสั้นๆ
3.7.1 เมธอดช่วยเหลือ Action View
distance_of_time_in_words
แปลและทำให้พหูพจน์ผลลัพธ์และตัดคำตัวเลขวินาที นาที ชั่วโมง และอื่นๆ ดูการแปลที่ datetime.distance_in_wordsdatetime_select
และselect_month
ใช้ชื่อเดือนที่แปลแล้วสำหรับการเติมค่าในแท็ก select ที่ได้รับผลลัพธ์ ดูการแปลที่ date.month_names สำหรับการแปลdatetime_select
ยังค้นหาตัวเลือก order จาก date.order (ยกเว้นว่าคุณจะส่งตัวเลือกไปโดยชัดเจน) ช่วยเลือกวันที่ทั้งหมดแปลคำถามโดยใช้การแปลในขอบเขต datetime.prompts ถ้ามีเมธอด
number_to_currency
,number_with_precision
,number_to_percentage
,number_with_delimiter
, และnumber_to_human_size
ใช้การตั้งค่ารูปแบบตัวเลขที่อยู่ในขอบเขต number
3.7.2 เมธอด Active Model
model_name.human
และhuman_attribute_name
ใช้การแปลสำหรับชื่อโมเดลและชื่อแอตทริบิวต์ถ้ามีในขอบเขต activerecord.models พวกเขายังสนับสนุนการแปลสำหรับชื่อคลาสที่สืบทอด (เช่นสำหรับใช้กับ STI) ตามที่อธิบายไว้ด้านบนใน "ขอบเขตข้อความข้อผิดพลาด"ActiveModel::Errors#generate_message
(ซึ่งใช้ในการตรวจสอบความถูกต้องของ Active Model แต่อาจใช้ด้วยตนเอง) ใช้model_name.human
และhuman_attribute_name
(ดูด้านบน) นอกจากนี้ยังแปลคำผิดพลาดและสนับสนุนการแปลสำหรับชื่อคลาสที่สืบทอดตามที่อธิบายไว้ด้านบนใน "ขอบเขตข้อความข้อผิดพลาด"ActiveModel::Error#full_message
และActiveModel::Errors#full_messages
เติมชื่อแอตทริบิวต์ในข้อความผิดพลาดโดยใช้รูปแบบที่ค้นหาจากerrors.format
(ค่าเริ่มต้น:"%{attribute} %{message}"
) หากต้องการปรับแต่งรูปแบบเริ่มต้นให้เขียนทับในไฟล์โลเคลล์ของแอป หากต้องการปรับแต่งรูปแบบตามโมเดลหรือแอตทริบิวต์ ดูที่config.active_model.i18n_customize_full_message
3.7.3 เมธอด Active Support
Array#to_sentence
ใช้การตั้งค่ารูปแบบตามที่กำหนดในขอบเขต support.array
4 วิธีการเก็บการแปลที่กำหนดเอง
Backend ที่เรียบง่ายที่ส่งพร้อม Active Support ช่วยให้คุณเก็บการแปลได้ทั้งในรูปแบบ Ruby และรูปแบบ YAML.2
ตัวอย่างของแปลที่ให้โดยใช้ Ruby Hash อาจมีดังนี้:
{
pt: {
foo: {
bar: "baz"
}
}
}
ไฟล์ YAML ที่เทียบเท่าจะมีลักษณะดังนี้:
pt:
foo:
bar: baz
เห็นได้ว่าในทั้งสองกรณีคีย์ระดับบนสุดคือ locale :foo
เป็นคีย์ของเนมสเปซและ :bar
เป็นคีย์สำหรับการแปล "baz"
นี่คือตัวอย่าง "จริง" จากไฟล์แปล YAML ของ Active Support en.yml
:
en:
date:
formats:
default: "%Y-%m-%d"
short: "%b %d"
long: "%B %d, %Y"
ดังนั้นการค้นหาที่เหมือนกันต่อไปนี้จะคืนรูปแบบวันที่ :short
"%b %d"
:
ruby
I18n.t 'date.formats.short'
I18n.t 'formats.short', scope: :date
I18n.t :short, scope: 'date.formats'
I18n.t :short, scope: [:date, :formats]
ทั่วไปแล้วเราแนะนำให้ใช้ YAML เป็นรูปแบบในการเก็บข้อมูลแปลภาษา อย่างไรก็ตาม มีกรณีบางกรณีที่คุณต้องการเก็บ Ruby lambdas เป็นส่วนหนึ่งของข้อมูลภาษา ตัวอย่างเช่นสำหรับรูปแบบวันที่พิเศษ
5 ปรับแต่งการตั้งค่า I18n ของคุณ
5.1 การใช้ Backend ที่แตกต่างกัน
ด้วยเหตุผลหลายประการ Backend ที่จัดส่งพร้อมกับ Active Support ทำเพียง "สิ่งที่ง่ายที่สุดที่สามารถทำได้" สำหรับ Ruby on Rails3 ... ซึ่งหมายความว่ามันสามารถทำงานได้เฉพาะภาษาอังกฤษและอาจมีผลข้างเคียงที่เกี่ยวข้องกับภาษาที่คล้ายกันมาก นอกจากนี้ Backend ที่เรียบง่ายสามารถอ่านแปลงานแปลงได้เท่านั้นแต่ไม่สามารถเก็บไว้ในรูปแบบใด ๆ ได้
แต่นั่นไม่ได้หมายความว่าคุณต้องติดอยู่กับข้อจำกัดเหล่านี้ แต่ Ruby I18n gem ทำให้ง่ายมากที่จะแทนที่การดำเนินการ Backend ที่เรียบง่ายด้วยสิ่งอื่นที่เหมาะสมกับความต้องการของคุณ โดยการส่ง backend instance ไปยัง setter I18n.backend=
ตัวอย่างเช่นคุณสามารถแทนที่ Backend ที่เรียบง่ายด้วย Chain backend เพื่อเชื่อมต่อ backend หลายรายการเข้าด้วยกัน ซึ่งเป็นประโยชน์เมื่อคุณต้องการใช้การแปลภาษามาตรฐานกับ Backend ที่เรียบง่าย แต่เก็บการแปลภาษาแอปพลิเคชันที่กำหนดเองในฐานข้อมูลหรือ backend อื่น ๆ
ด้วย Chain backend คุณสามารถใช้ Active Record backend และย้อนกลับไปยัง Simple backend (ค่าเริ่มต้น):
I18n.backend = I18n::Backend::Chain.new(I18n::Backend::ActiveRecord.new, I18n.backend)
5.2 การใช้ Exception Handlers ที่แตกต่างกัน
API ของ I18n กำหนดข้อยกเว้นต่อไปนี้ที่จะถูกเรียกขึ้นโดย backend เมื่อเกิดเงื่อนไขที่ไม่คาดคิด:
ข้อยกเว้น | เหตุผล |
---|---|
I18n::MissingTranslationData |
ไม่พบการแปลสำหรับคีย์ที่ร้องขอ |
I18n::InvalidLocale |
ภาษาที่ตั้งค่าเป็น I18n.locale ไม่ถูกต้อง (เช่น nil ) |
I18n::InvalidPluralizationData |
มีการส่งตัวเลือกนับแต่ข้อมูลแปลภาษาไม่เหมาะสมสำหรับพหูพจน์ |
I18n::MissingInterpolationArgument |
การแปลคาดหวังอาร์กิวเมนต์ที่ยังไม่ได้ถูกส่ง |
I18n::ReservedInterpolationKey |
การแปลมีชื่อตัวแปรอินเตอร์โพเลชันที่สงวนไว้ (เช่น: scope , default ) |
I18n::UnknownFileType |
backend ไม่รู้จักวิธีการจัดการประเภทไฟล์ที่เพิ่มเข้าไปใน I18n.load_path |
5.2.1 การปรับแต่งวิธีการจัดการ I18n::MissingTranslationData
หาก config.i18n.raise_on_missing_translations
เป็น true
ข้อผิดพลาด I18n::MissingTranslationData
จะถูกเรียกขึ้น คุณควรเปิดใช้งานสิ่งนี้ในสภาพแวดล้อมการทดสอบ เพื่อให้คุณสามารถตรวจจับสถานที่ที่ขาดหายไปของการแปลได้
หาก config.i18n.raise_on_missing_translations
เป็น false
(ค่าเริ่มต้นในสภาพแวดล้อมทั้งหมด) ข้อผิดพลาดของข้อยกเว้นจะถูกพิมพ์ออกมา ซึ่งประกอบด้วยคีย์/สโคปที่ขาดหายไปเพื่อให้คุณสามารถแก้ไขโค้ดของคุณ
หากคุณต้องการปรับแต่งพฤติกรรมนี้ได้อีกต่อไป คุณควรตั้งค่า config.i18n.raise_on_missing_translations = false
แล้วดำเนินการสร้าง I18n.exception_handler
ที่กำหนดเอง ตัวจัดการข้อยกเว้นที่กำหนดเองสามารถเป็น proc หรือคลาสที่มีเมธอด call
:
# config/initializers/i18n.rb
module I18n
class RaiseExceptForSpecificKeyExceptionHandler
def call(exception, locale, key, options)
if key == "special.key"
"translation missing!" # return this, don't raise it
elsif exception.is_a?(MissingTranslation)
raise exception.to_exception
else
raise exception
end
end
end
end
I18n.exception_handler = I18n::RaiseExceptForSpecificKeyExceptionHandler.new
นี้จะเรียกข้อยกเว้นทั้งหมดในทางเดียวกันกับตัวจัดการเริ่มต้น ยกเว้นในกรณีของ I18n.t("special.key")
การแปล API I18n ที่อธิบายในคู่มือนี้มีวัตถุประสงค์หลักในการแปลสตริงของอินเตอร์เฟซ หากคุณต้องการแปลเนื้อหาของโมเดล (เช่นบทความบล็อก) คุณจะต้องใช้วิธีการที่แตกต่างเพื่อช่วยในการทำเช่นนี้
มี Gems หลายตัวที่ช่วยในการแปลเนื้อหาดังนี้:
- Mobility: สนับสนุนการเก็บรักษาการแปลในรูปแบบหลายรูปแบบ เช่น ตารางแปลภาษา คอลัมน์ JSON (PostgreSQL) เป็นต้น
- Traco: คอลัมน์ที่สามารถแปลได้เก็บไว้ในตารางโมเดลเอง
6 สรุป
ในจุดนี้คุณควรมีภาพรวมที่ดีเกี่ยวกับวิธีการสนับสนุน I18n ใน Ruby on Rails และพร้อมที่จะเริ่มต้นการแปลโปรเจกต์ของคุณ
7 การมีส่วนร่วมใน Rails I18n
การสนับสนุน I18n ใน Ruby on Rails ถูกนำเสนอในเวอร์ชัน 2.2 และยังคงพัฒนาอย่างต่อเนื่อง โครงการนี้ปฏิบัติตามแบบฉบับการพัฒนา Ruby on Rails ที่ดีโดยพัฒนาแนวทางใน Gems และแอปพลิเคชันจริงก่อน และจากนั้นเลือกเฉพาะคุณสมบัติที่ดีที่สุดและมีประโยชน์ที่สุดสำหรับการรวมเข้ากับคอร์
ดังนั้นเราขอเชิญทุกคนที่จะทดลองใช้ไอเดียและคุณสมบัติใหม่ใน Gems หรือไลบรารีอื่น ๆ และทำให้พวกเขาเป็นที่ใช้งานได้สำหรับชุมชน (อย่าลืมประกาศงานของคุณใน mailing list ของเรา!)
หากคุณพบว่าภาษาของคุณไม่มีในเรื่องตัวอย่างข้อมูลแปล (translations) ของเราสำหรับ Ruby on Rails กรุณา fork รีพอสิทธิ์ และเพิ่มข้อมูลของคุณ แล้วส่ง pull request มาให้เรา
8 ทรัพยากร
- กลุ่ม Google: rails-i18n - กลุ่มสมาชิกทางอีเมลของโครงการ
- GitHub: rails-i18n - ที่เก็บรหัสและติดตามปัญหาสำหรับโครงการ rails-i18n ที่สำคัญที่สุดคุณสามารถค้นหา ตัวอย่างการแปล สำหรับ Rails ที่ควรทำงานสำหรับแอปพลิเคชันของคุณในกรณีส่วนใหญ่
- GitHub: i18n - ที่เก็บรหัสและติดตามปัญหาสำหรับ gem i18n
9 ผู้เขียน
- Sven Fuchs (ผู้เขียนเริ่มต้น)
- Karel Minařík
10 เชิงอรรถ
1 หรืออ้างอิงจาก วิกิพีเดีย: "Internationalization เป็นกระบวนการออกแบบแอปพลิเคชันซอฟต์แวร์ให้สามารถปรับเปลี่ยนได้สำหรับภาษาและภูมิภาคต่าง ๆ โดยไม่ต้องเปลี่ยนแปลงการพัฒนา การแปลงเป็นภาษาในเชิงพื้นที่คือกระบวนการปรับเปลี่ยนซอฟต์แวร์ให้เหมาะสมสำหรับภูมิภาคหรือภาษาที่เฉพาะเจาะจงโดยการเพิ่มคอมโพเนนต์ที่เป็นพื้นที่และการแปลข้อความ"
2 แบ็กเอนด์อื่นอาจอนุญาตหรือต้องการให้ใช้รูปแบบอื่น เช่น แบ็กเอนด์ GetText อาจอนุญาตให้อ่านไฟล์ GetText
3 เหตุผลหนึ่งในเรื่องนี้คือเราไม่ต้องการให้มีการโหลดที่ไม่จำเป็นสำหรับแอปพลิเคชันที่ไม่ต้องการความสามารถในการ I18n ดังนั้นเราต้องรักษาไลบรารี I18n ให้เรียบง่ายที่สุดสำหรับภาษาอังกฤษ อีกเหตุผลหนึ่งคือเป็นเรื่องยากมากที่จะสร้างตัวเลือกที่เหมาะสมสำหรับปัญหาที่เกี่ยวข้องกับ I18n สำหรับภาษาที่มีอยู่ทั้งหมด ดังนั้น การมีวิธีการที่ช่วยให้เราสามารถแทนที่การดำเนินการทั้งหมดได้อย่างง่ายดายก็เหมาะสมอยู่แล้ว นอกจากนี้ยังทำให้ง่ายขึ้นในการทดลองใช้คุณสมบัติและส่วนขยายที่กำหนดเอง
ข้อเสนอแนะ
คุณสามารถช่วยปรับปรุงคุณภาพของคู่มือนี้ได้
กรุณาช่วยเพิ่มเติมหากพบข้อผิดพลาดหรือข้อผิดพลาดทางความจริง เพื่อเริ่มต้นคุณสามารถอ่านส่วน การสนับสนุนเอกสาร ของเราได้
คุณอาจพบเนื้อหาที่ไม่สมบูรณ์หรือเนื้อหาที่ไม่ได้อัปเดต กรุณาเพิ่มเอกสารที่ขาดหายไปสำหรับเนื้อหาหลัก โปรดตรวจสอบ Edge Guides ก่อนเพื่อตรวจสอบ ว่าปัญหาได้รับการแก้ไขหรือไม่ในสาขาหลัก ตรวจสอบ คู่มือแนวทาง Ruby on Rails เพื่อดูรูปแบบและกฎเกณฑ์
หากคุณพบข้อผิดพลาดแต่ไม่สามารถแก้ไขได้เอง กรุณา เปิดปัญหา.
และสุดท้าย การสนทนาใด ๆ เกี่ยวกับ Ruby on Rails เอกสารยินดีต้อนรับที่สุดใน เว็บบอร์ดอย่างเป็นทางการของ Ruby on Rails.