Locale คือคลาส Foundation ใน iOS และ macOS ที่ห่อหุ้มข้อตกลงทางภาษาและวัฒนธรรมของผู้ใช้: รูปแบบวันที่ รูปแบบตัวเลข สกุลเงิน และหน่วยวัด ตาม Apple Developer Documentation, 2024 Locale กำหนดว่า DateFormatter แสดงเดือนอย่างไร (มกราคม หรือ January) ตัวคั่นทศนิยมในตัวเลข (จุลภาคหรือจุด) และสัญลักษณ์สกุลเงิน (รูเบิล ดอลลาร์ หรือยูโร) แต่ละอินสแตนซ์ของ Locale เชื่อมโยงกับตัวระบุเช่น ru_RU หรือ en_US โดยส่วนแรกคือรหัสภาษา (ISO 639-1) และส่วนที่สองคือรหัสภูมิภาค (ISO 3166-1) แตกต่างจาก TimeZone ตรงที่ Locale ไม่มีผลต่อค่าสัมบูรณ์ของเวลา มีผลเฉพาะการแสดงเป็นสตริงเท่านั้น
ประเด็นสำคัญ
Locale เป็นชนิดค่าใน Swift (NSLocale ใน Objective-C) ที่แสดงถึงชุดของกฎการจัดรูปแบบเฉพาะสำหรับภาษาและภูมิภาคที่กำหนด แตกต่างจาก TimeZone ซึ่งกำหนดออฟเซตสัมบูรณ์ของเวลา Locale กำหนดว่าเวลา ตัวเลข และสกุลเงินปรากฏอย่างไรในการแสดงเป็นสตริง วันที่เดียวกัน 2024-07-21 จะแสดงเป็น “21 กรกฎาคม 2024” สำหรับ ru_RU และ “July 21, 2024” สำหรับ en_US
แต่ละอินสแตนซ์ของ Locale ประกอบด้วยสององค์ประกอบ: ภาษา (กำหนดชื่อเดือน ชื่อวันในสัปดาห์ ลำดับคำ) และภูมิภาค (กำหนดรูปแบบตัวเลข สกุลเงิน ปฏิทิน) การรวมกันขององค์ประกอบเหล่านี้ถูกเข้ารหัสในตัวระบุตามมาตรฐาน BCP 47: ru_RU (ภาษารัสเซีย รัสเซีย), en_US (ภาษาอังกฤษ สหรัฐอเมริกา), de_DE (ภาษาเยอรมัน เยอรมนี)
ตาม Unicode CLDR (2024) จำนวนตำแหน่งที่ตั้งที่รองรับใน iOS มีมากกว่า 700 ชุดภาษา-ภูมิภาค Foundation ใช้ข้อมูลจาก CLDR (Common Locale Data Repository) — คลังข้อมูลการแปลภาษาที่ครอบคลุมมากที่สุดซึ่งดูแลโดย Unicode Consortium ซึ่งรับประกันการจัดรูปแบบที่สอดคล้องกันในอุปกรณ์ Apple ทั้งหมด
DateFormatter ใช้ Locale เพื่อเลือกชื่อเดือนและวันที่ถูกต้อง กำหนดลำดับขององค์ประกอบวันที่ (วัน/เดือน/ปี หรือ เดือน/วัน/ปี) และตัวคั่น โดยไม่ระบุ Locale อย่างชัดเจน DateFormatter จะใช้ตำแหน่งที่ตั้งของอุปกรณ์ — ซึ่งถูกต้องสำหรับ UI แต่อันตรายสำหรับข้อมูลเซิร์ฟเวอร์ที่รูปแบบควรคงที่
| องค์ประกอบ | ru_RU | en_US | de_DE |
|---|---|---|---|
| วันที่ (medium) | 21 กรกฎาคม 2024 | Jul 21, 2024 | 21.07.2024 |
| ตัวเลข (1000.5) | 1 000,5 | 1,000.5 | 1.000,5 |
| สกุลเงิน (100) | 100,00 ₽ | $100.00 | 100,00 € |
| ปฏิทิน | เกรกอเรียน | เกรกอเรียน | เกรกอเรียน |
| ตัวคั่นรายการ | ; | , | ; |
NumberFormatter ใช้ Locale เพื่อกำหนดตัวคั่นทศนิยม (จุลภาคหรือจุด) ตัวคั่นกลุ่ม (ช่องว่าง จุลภาค จุด) และสัญลักษณ์สกุลเงิน การละเว้น Locale เมื่อแยกวิเคราะห์ตัวเลขเป็นหนึ่งในสาเหตุทั่วไปของบั๊กในแอปพลิเคชันระหว่างประเทศ: ตัวเลข “1,5” หมายถึง “หนึ่งครึ่ง” สำหรับ ru_RU แต่สำหรับ en_US ตัวแยกวิเคราะห์ดิจิทัลจะอ่านเป็น “ห้า” หลังจุลภาค
สำคัญ: Calendar ที่สร้างผ่าน Calendar.current สืบทอดตำแหน่งที่ตั้งของอุปกรณ์ Calendar(identifier: .gregorian) ที่ระบุตำแหน่งที่ตั้งอย่างชัดเจนเป็นวิธีการที่แนะนำสำหรับการจัดรูปแบบที่คาดเดาได้ เมื่อทำงานกับวันที่ ISO 8601 ให้ใช้ Locale(identifier: “en_US_POSIX”) เสมอ — ตำแหน่งที่ตั้งพิเศษสำหรับการจัดรูปแบบทางเทคนิคที่ไม่ได้รับผลกระทบจากการตั้งค่าภูมิภาค
ตัวระบุ Locale ประกอบด้วยรหัสภาษา (ISO 639-1 สองอักขระ) และรหัสภูมิภาค (ISO 3166-1 สองอักขระ) คั่นด้วยขีดล่าง ตัวอย่าง: ru_RU, en_US, fr_FR, zh_Hans_CN (ภาษาจีน อักษรย่อ จีน) Foundation ยังรองรับตัวระบุในรูปแบบ BCP 47: ru-RU, en-US ที่ใช้ในมาตรฐานเว็บ
นอกจากตัวระบุแบบเต็มแล้ว Locale สามารถสร้างได้ด้วยภาษาเท่านั้น: Locale(identifier: “ru”) จะคืนค่าตำแหน่งที่ตั้งด้วยภาษารัสเซียและภูมิภาคเริ่มต้นสำหรับภาษานั้น (โดยปกติคือรัสเซีย) ในทำนองเดียวกันสำหรับภาษาอังกฤษ: Locale(identifier: “en”) ใช้ภูมิภาคสหรัฐอเมริกา วิธีการนี้มีประโยชน์สำหรับการตั้งค่าภาษาจัดรูปแบบโดยไม่ผูกติดกับภูมิภาคเฉพาะ
ตำแหน่งที่ตั้งพิเศษ รวมถึง en_US_POSIX — ตำแหน่งที่ตั้งทางเทคนิคสำหรับการจัดรูปแบบวันที่และตัวเลขแบบอัตโนมัติ ซึ่งรับประกันรูปแบบที่เสถียรโดยไม่ขึ้นกับการตั้งค่าผู้ใช้ ตำแหน่งที่ตั้งนี้จำเป็นสำหรับการแยกวิเคราะห์วันที่จาก API เซิร์ฟเวอร์ โดยเฉพาะอย่างยิ่งสำหรับรูปแบบ ISO 8601 โดยใช้ปฏิทินเกรกอเรียน รูปแบบเวลา 24 ชั่วโมง และจุดเป็นตัวคั่นทศนิยม
import Foundation
// ตัวระบุตำแหน่งที่ตั้งที่มีอยู่
let available: [String] = Locale.availableIdentifiers
print("ตำแหน่งที่ตั้งทั้งหมด: \(available.count)")
// กรองตำแหน่งที่ตั้งภาษารัสเซีย
let russianLocales = available.filter { $0.hasPrefix("ru") }
print("ตำแหน่งที่ตั้งภาษารัสเซีย: \(russianLocales)")
// ส่วนประกอบของตำแหน่งที่ตั้ง
let locale = Locale(identifier: "de_DE")
print("ภาษา: \(locale.languageCode ?? "nil")")
print("ภูมิภาค: \(locale.regionCode ?? "nil")")
print("สกุลเงิน: \(locale.currencyCode ?? "nil")")
print("ปฏิทิน: \(locale.calendar.identifier)")
การตรวจสอบตำแหน่งที่ตั้งที่มีอยู่ ผ่าน Locale.availableIdentifiers คืนค่าอาร์เรย์ของตัวระบุทั้งหมดที่รองรับโดย iOS เวอร์ชันปัจจุบัน หากต้องการกรองตามภูมิภาค ให้ใช้ Locale.availableIdentifiers.filter ด้วยการตรวจสอบ regionCode ซึ่งมีประโยชน์สำหรับการสร้าง UI การเลือกภูมิภาคโดยไม่มีรายการแบบตายตัว
Locale.current เป็นวิธีหลักในการรับตำแหน่งที่ตั้งปัจจุบันของอุปกรณ์ที่ผู้ใช้กำหนดในการตั้งค่า iOS (Settings > General > Language & Region) คุณสมบัตินี้จะอัปเดตโดยอัตโนมัติเมื่อเปลี่ยนภาษาหรือภูมิภาคในการตั้งค่าโดยไม่ต้องรีสตาร์ทแอปพลิเคชัน อย่างไรก็ตาม อาจไม่ตรงกับตำแหน่งที่ตั้งที่ต้องการสำหรับการแสดงเนื้อหา: ผู้ใช้อาจตั้งค่าภาษาอินเทอร์เฟซเป็นภาษาอังกฤษ แต่ดูวันที่ในรูปแบบไทย
สำหรับการกำหนดความชอบของผู้ใช้ที่แม่นยำยิ่งขึ้น ให้ใช้ Locale.preferredLanguages — อาร์เรย์ของภาษาที่เรียงลำดับตามลำดับความสำคัญของผู้ใช้ องค์ประกอบแรกคือภาษาอินเทอร์เฟซหลัก รายการนี้สอดคล้องกับการตั้งค่าใน Language & Region รวมถึงการลากภาษาในลำดับที่ต้องการ แอปพลิเคชันการสื่อสาร (แอปส่งข้อความ อีเมลคลายนต์) ควรพิจารณาลำดับนี้เมื่อเลือกภาษาแสดงเนื้อหา
import Foundation
// ตำแหน่งที่ตั้งระบบปัจจุบัน
let current = Locale.current
print("ตำแหน่งที่ตั้งปัจจุบัน: \(current.identifier)")
print("ภาษา: \(current.language?.disjointName ?? "nil")")
// ภาษาที่ผู้ใช้ต้องการ
let preferred = Locale.preferredLanguages
print("ภาษาที่ต้องการ: \(preferred)")
// รับภูมิภาคจากตำแหน่งที่ตั้งปัจจุบัน
if let region = current.regionCode {
let regionLocale = Locale(identifier: "en_\(region)")
let countryName = regionLocale.localizedString(
forRegionCode: region
)
print("ประเทศ: \(countryName ?? region)")
}
// ตรวจสอบรูปแบบ 24 ชั่วโมง
let uses24h = current.uses24hClock(
for: .dateAndTime
)
print("ใช้ 24 ชม.: \(uses24h)")
การแปลภาษาใน UI: เพื่อแสดงชื่อเดือนและวันในภาษาอินเทอร์เฟซ ให้ใช้ Calendar ที่ตั้งค่าตำแหน่งที่ตั้ง Calendar.current.symbols(for: .month) คืนชื่อเดือนในภาษาของตำแหน่งที่ตั้งปัจจุบัน หากต้องการแสดงชื่อประเทศในภาษาของผู้ใช้ ให้ใช้ Locale.current.localizedString(forRegionCode:)
การจัดรูปแบบวันที่ ด้วยการรับรู้ตำแหน่งที่ตั้งเป็นงานสำคัญเมื่อแสดงวันที่ให้ผู้ใช้ DateFormatter ที่ตั้งค่าตำแหน่งที่ตั้งจะเลือกรูปแบบวันที่และเวลาที่ถูกต้องสำหรับภูมิภาคของผู้ใช้โดยอัตโนมัติ สำหรับ dateStyle และ timeStyle ด้วยค่า .short, .medium, .long, .full ตัวจัดรูปแบบจะใช้กฎของตำแหน่งที่ตั้งเพื่อประกอบองค์ประกอบวันที่
import Foundation
let date = Date()
// รูปแบบกับตำแหน่งที่ตั้งต่างๆ
let formatter = DateFormatter()
formatter.dateStyle = .medium
formatter.locale = Locale(identifier: "ru_RU")
print("รัสเซีย: \(formatter.string(from: date))")
formatter.locale = Locale(identifier: "en_US")
print("อังกฤษ: \(formatter.string(from: date))")
formatter.locale = Locale(identifier: "ja_JP")
print("ญี่ปุ่น: \(formatter.string(from: date))")
// การจัดรูปแบบสกุลเงินกับตำแหน่งที่ตั้ง
let numFormatter = NumberFormatter()
numFormatter.numberStyle = .currency
numFormatter.locale = Locale(identifier: "de_DE")
print("สกุลเงินเยอรมัน: \(numFormatter.string(from: 1234.56) ?? "nil")")
numFormatter.locale = Locale(identifier: "en_US")
print("สกุลเงินสหรัฐ: \(numFormatter.string(from: 1234.56) ?? "nil")")
การแยกวิเคราะห์วันที่จาก API เซิร์ฟเวอร์ ควรใช้ Locale(identifier: “en_US_POSIX”) เสมอสำหรับรูปแบบคงที่ เซิร์ฟเวอร์มักจะส่งวันที่ในรูปแบบ ISO 8601 พร้อมชื่อเดือนภาษาอังกฤษ และการใช้ตำแหน่งที่ตั้งปัจจุบันของอุปกรณ์อาจทำให้เกิดข้อผิดพลาดในการแยกวิเคราะห์หากผู้ใช้อยู่ในภูมิภาคที่ไม่ใช่ภาษาอังกฤษ en_US_POSIX รับประกันว่าการแยกวิเคราะห์ไม่ขึ้นอยู่กับการตั้งค่าอุปกรณ์
import Foundation
// การแยกวิเคราะห์วันที่เซิร์ฟเวอร์ที่ถูกต้อง
let isoFormatter = DateFormatter()
isoFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZ"
isoFormatter.locale = Locale(identifier: "en_US_POSIX")
isoFormatter.timeZone = TimeZone(secondsFromGMT: 0)
let serverDate = "2024-07-21T14:30:00+0000"
if let parsed = isoFormatter.date(from: serverDate) {
print("วันที่แยกวิเคราะห์แล้ว: \(parsed)")
}
// ชื่อสกุลเงินที่แปลภาษา
let usLocale = Locale(identifier: "en_US")
let currencyName = usLocale.localizedString(
forCurrencyCode: "RUB"
)
print("รูเบิลรัสเซียในตำแหน่งที่ตั้งสหรัฐ: \(currencyName ?? "nil")")
ความสามารถเพิ่มเติม: Locale ให้คำอธิบายที่แปลภาษาของส่วนประกอบผ่านเมธอด localizedString(forRegionCode:), localizedString(forLanguageCode:), localizedString(forCurrencyCode:) และ localizedString(forCalendarIdentifier:) เมธอดเหล่านี้คืนชื่อในภาษาของตำแหน่งที่ตั้งที่เรียกใช้ ตัวอย่างเช่น Locale(identifier: “ru_RU”).localizedString(forCountryCode: “DE”) จะคืน “เยอรมนี”
การละเว้น Locale เมื่อแยกวิเคราะห์ตัวเลข เป็นข้อผิดพลาดร้ายแรงในแอปพลิเคชันระหว่างประเทศ NumberFormatter ที่ไม่มีตำแหน่งที่ตั้งชัดเจนจะใช้ตำแหน่งที่ตั้งปัจจุบันของอุปกรณ์ หากผู้ใช้ในรัสเซียป้อน “1,5” NumberFormatter.number(from: “1,5”) จะคืนค่า 1.5 อย่างถูกต้อง แต่หากโค้ดเดียวกันทำงานบนอุปกรณ์ที่มีตำแหน่งที่ตั้ง en_US การแยกวิเคราะห์จะคืนค่า nil เนื่องจากสำหรับ en_US ตัวคั่นทศนิยมคือจุด
การขาด en_US_POSIX สำหรับวันที่เซิร์ฟเวอร์ นำไปสู่บั๊กที่ยากจะตรวจจับ DateFormatter ที่มี dateFormat และ locale = Locale.current อาจเสียสำหรับผู้ใช้จากภูมิภาคที่รูปแบบวันที่แตกต่างจากรูปแบบอเมริกัน ตัวอย่างเช่น ในเยอรมนี DateFormatter อาจคาดหวัง “21.07.2024” ในขณะที่เซิร์ฟเวอร์ส่ง “07/21/2024” en_US_POSIX รับประกันรูปแบบคงที่สำหรับการแยกวิเคราะห์อัตโนมัติโดยไม่ขึ้นกับภูมิภาคของผู้ใช้
การเปรียบเทียบสตริงวันที่ แทนการใช้ Date เป็นข้อผิดพลาดทั่วไปอีกประการหนึ่ง นักพัฒนาบางครั้งเปรียบเทียบการแสดงเป็นสตริงของวันที่จากตำแหน่งที่ตั้งต่างๆ ทำให้ได้ผลลัพธ์ที่ไม่ถูกต้อง Locale เปลี่ยนแปลงเฉพาะการแสดงผล ไม่ใช่ค่าสัมบูรณ์ของวันที่ เปรียบเทียบออบเจ็กต์ Date เสมอ ไม่ใช่การแสดงเป็นสตริง หากต้องการเปรียบเทียบองค์ประกอบวันที่ ให้ใช้ Calendar ที่ระบุตำแหน่งที่ตั้งอย่างชัดเจน
ตาม WWDC 2023 ประมาณ 30% ของปัญหาการทำให้เป็นสากลในแอปพลิเคชันเกี่ยวข้องกับการกำหนดค่า Locale ที่ไม่ถูกต้อง Apple แนะนำให้ตั้งค่าตำแหน่งที่ตั้งอย่างชัดเจนสำหรับ DateFormatter และ NumberFormatter เสมอเมื่อทำงานกับข้อมูลเซิร์ฟเวอร์ และใช้ Locale.current เฉพาะสำหรับการแสดง UI เท่านั้น ปฏิบัติง่ายๆ นี้ช่วยขจัดบั๊กส่วนใหญ่ที่เกี่ยวข้องกับการตั้งค่าภูมิภาค
คำถามที่พบบ่อย
Locale เป็นคลาส Foundation ที่แสดงถึงกฎการจัดรูปแบบทางวัฒนธรรมและภาษา: รูปแบบวันที่ รูปแบบตัวเลข สกุลเงิน และหน่วยวัด ใช้ร่วมกับ DateFormatter, NumberFormatter และ Calendar สำหรับการแสดงข้อมูลที่แปลภาษา
Locale กำหนดรูปแบบการแสดง (ภาษา ข้อตกลงภูมิภาค) ในขณะที่ TimeZone กำหนดออฟเซตสัมบูรณ์ของเวลาที่สัมพันธ์กับ UTC Locale ส่งผลต่อการแสดงเป็นสตริง TimeZone ส่งผลต่อค่าตัวเลขของเวลา ทั้งสองใช้ร่วมกันสำหรับการจัดรูปแบบวันที่ที่สมบูรณ์
en_US_POSIX เป็นตำแหน่งที่ตั้งพิเศษสำหรับการจัดรูปแบบทางเทคนิคที่รับประกันรูปแบบที่เสถียรโดยไม่ขึ้นกับการตั้งค่าผู้ใช้ จำเป็นสำหรับการแยกวิเคราะห์วันที่เซิร์ฟเวอร์ (ISO 8601) และการทำงานกับ API ที่รูปแบบต้องสามารถคาดเดาได้
Locale.availableIdentifiers คืนค่าอาร์เรย์ของสตริงพร้อมตัวระบุของตำแหน่งที่ตั้งที่รองรับทั้งหมด หากต้องการกรองตามภาษา ให้ใช้ filter ด้วย hasPrefix หากต้องการรับภูมิภาค ให้ใช้ Locale(identifier:).regionCode
NumberFormatter ใช้ Locale เพื่อกำหนดตัวคั่นทศนิยม (จุลภาคหรือจุด) สัญลักษณ์สกุลเงิน และตัวคั่นกลุ่ม สำหรับรูปแบบคงที่ ให้ตั้งค่าตำแหน่งที่ตั้งเป็น en_US_POSIX หรือตั้งค่าคุณสมบัติของตัวจัดรูปแบบอย่างชัดเจน
สรุป
เราจะพัฒนาแอปพลิเคชันบนมือถือแบบครบวงจร
IT Sectr สร้างแอปพลิเคชัน iOS และ Android สำหรับสตาร์ทอัพและธุรกิจตั้งแต่ปี 2017 เราจะให้คำแนะนำและเสนอวิธีแก้ปัญหาที่ดีที่สุดแก่คุณ
อ่านเพิ่มเติม