NSLocalizedString มีอะไรเทียบเท่าใน Swift


228

มีค่าเทียบเท่าของ Swift NSLocalizedString(...)หรือไม่? ในObjective-Cเรามักจะใช้:

NSString *string = NSLocalizedString(@"key", @"comment");

ฉันจะประสบความสำเร็จในสวิฟท์ได้อย่างไร ฉันพบฟังก์ชั่น:

func NSLocalizedString(
    key: String,
    tableName: String? = default,
    bundle: NSBundle = default,
    value: String = default,
    #comment: String) -> String

อย่างไรก็ตามมันยาวมากและไม่สะดวกเลย


2
ดีที่สุดคือการสร้างโค้ดขนาดสั้นลง: NSLocalizedString ("", comment: "") ... ฉันชอบโซลูชันส่วนขยาย แต่ปัญหาคือ genstrings จะไม่จับสตริงเหล่านี้ลงในไฟล์การแปล
Matej Ukmar

3
ใน Swift 3 คุณสามารถใช้NSLocalizedString("Cancel", comment: "Cancel button title")ประโยชน์จากค่าเริ่มต้น ฉันคิดว่าสะดวกดี
LShi

นี่เป็นบทความที่ดีมากเกี่ยวกับการแปล (ส่วนขยายสตริงตารางสตริงที่แตกต่างกันและแม้กระทั่งหลายฝ่าย): medium.com/@marcosantadev/…
LightMan

นี้เป็นบทความที่ดีมากเกี่ยวกับการแปลในสวิฟท์สำหรับสถาปัตยกรรมที่แข็งแกร่งmedium.com/@mendibarouk/...
Mendy

คำตอบ:


373

ฉันใช้วิธีแก้ปัญหาต่อไป:

1) สร้างส่วนขยาย:

extension String {
    var localized: String {
        return NSLocalizedString(self, tableName: nil, bundle: Bundle.main, value: "", comment: "")
    }
}

2) ในไฟล์Localizable.strings :

"Hi" = "Привет";

3) ตัวอย่างการใช้งาน:

myLabel.text = "Hi".localized

สนุก! ;)

--upd: -

สำหรับกรณีที่มีความคิดเห็นคุณสามารถใช้วิธีนี้:

1) การขยาย:

extension String {
    func localized(withComment:String) -> String {
        return NSLocalizedString(self, tableName: nil, bundle: Bundle.main, value: "", comment: withComment)
    }
}

2) ในไฟล์. strings:

/* with !!! */
"Hi" = "Привет!!!";

3) ใช้:

myLabel.text = "Hi".localized(withComment: "with !!!")

92
ปัญหาเดียวของเรื่องนี้คือคุณจะไม่สามารถใช้genstringsยูทิลิตีเพื่อสร้างไฟล์. strings ของคุณ
เน็ด

9
นั่นเป็นความคิดที่ดีมาก! ฉันยังทำมันเล็กน้อยอย่างชาญฉลาดโดยการเปลี่ยนไปfunc localized(comment: String = "") -> Stringจึงจะมีขนาดเล็กและมีความคิดเห็นที่ไม่จำเป็น :)
กุย Moura

2
มีความคิดว่าจะใช้genstringsกับสิ่งนี้อย่างไร?
คริส

48
ทุกคนตื่นเต้นมากกับคำตอบนี้ แต่ปัญหาใหญ่ (สำหรับโครงการที่จริงจังกับหลายภาษา) คือสิ่งนี้ทำให้การจัดการข้อความที่แปลของคุณgenstringsยุ่งเหยิงอย่างสิ้นเชิงเพราะใช้งานได้กับสตริงตัวอักษรที่ส่งผ่านไปยัง NSLocalizedString เท่านั้น ด้วยวิธีแก้ปัญหาที่ชาญฉลาดนี้คุณจะสูญเสียความสามารถในการอัปเดตไฟล์. strings ของคุณโดยใช้genstringsเครื่องมือและอย่างน้อยสำหรับฉันนั่นหมายความว่าฉันจะไม่สามารถใช้วิธีการที่เรียบง่ายนี้ได้
Erik van der Neut

14
ผมพบว่าวิธีการแก้ปัญหาที่ดีนี้ดำเนินการในgithub.com/marmelroy/Localize-Swift ปัญหาของการจัดหมวดหมู่ได้รับการแก้ไขโดยสคริปต์ python ที่กำหนดเองซึ่งรวมถึงผู้แต่ง ฉันไม่ใช่ผู้เขียน
Tomek Cejner

279

สิ่งที่NSLocalizedStringมีอยู่ในโลกของสวิฟท์

func NSLocalizedString(
    key: String,
    tableName: String? = default,
    bundle: NSBundle = default,
    value: String = default,
    #comment: String) -> String

เครื่องหมายtableName, bundleและvalueพารามิเตอร์ถูกทำเครื่องหมายด้วยdefaultคำสำคัญซึ่งหมายความว่าเราสามารถละเว้นพารามิเตอร์เหล่านี้ขณะเรียกใช้ฟังก์ชัน ในกรณีนี้จะใช้ค่าเริ่มต้น

สิ่งนี้นำไปสู่ข้อสรุปว่าการเรียกใช้เมธอดนั้นสามารถทำให้:

NSLocalizedString("key", comment: "comment")

Swift 5 - ไม่มีการเปลี่ยนแปลงยังคงทำงานเช่นนั้น


44
ความแตกต่างเพียงอย่างเดียวที่ความคิดเห็นไม่สามารถเป็นศูนย์ได้และการเติมข้อความอัตโนมัตินั้นไม่ง่ายสำหรับรุ่นสั้น
Marcin

1
นี่ไม่ทำงานอีกต่อไปฉันได้รับข้อผิดพลาดบอกว่ามีการใช้อาร์กิวเมนต์ไม่เพียงพอ
แอป 4 U

2
ไม่ว่าข้างต้นถูกต้องใน Xcode 6.3, Swift 1.2 พร้อมการเปลี่ยนแปลงเฉพาะจากวัตถุประสงค์ -c ความคิดเห็น (ตามที่ Marcin ระบุไว้) ไม่สามารถเป็นศูนย์ได้ แต่สามารถเป็น "" (ว่าง)
Neil

2
ความคิดเห็นที่ไม่มี / ว่างเปล่าทำให้การย้ายสตริงในภายหลังในไฟล์สตริงทำได้ยาก ถ้าไม่มีอะไรเพิ่มชื่อคลาส / ไฟล์ที่ใช้เป็นความคิดเห็น
Johan

นี่คือคำตอบที่ถูกต้อง เมื่อ Apple อัปเดตเป็น Swift แล้ว Xcode จะสามารถแปลง API นี้เป็น Swift API ใหม่โดยอัตโนมัติและจะไม่มีอะไรแตก แม้กระทั่งในเมนู Refractor ของ Xcode ในปัจจุบัน (v 11.4.1) มีWrap in NSLocalizedStringตัวเลือกที่ทำให้สิ่งต่าง ๆ ง่ายขึ้นเพียงแค่ไฮไลต์ข้อความคลิกขวาและเลือกรายการเมนู
อีธานอัลเลน

28

รูปแบบของคำตอบที่มีอยู่:

สวิฟท์ 5.1:

extension String {

    func localized(withComment comment: String? = nil) -> String {
        return NSLocalizedString(self, comment: comment ?? "")
    }

}

จากนั้นคุณสามารถใช้โดยมีหรือไม่มีความคิดเห็น:

"Goodbye".localized()
"Hello".localized(withComment: "Simple greeting")

โปรดทราบว่าgenstringsจะไม่ทำงานกับโซลูชันนี้


14

โดยใช้วิธีนี้เป็นไปได้ในการสร้างการใช้งานที่แตกต่างกันสำหรับประเภทที่แตกต่างกัน (เช่น Int หรือคลาสที่กำหนดเองเช่น CurrencyUnit, ... ) นอกจากนี้ยังเป็นไปได้ที่จะสแกนหาวิธีการนี้ด้วยการใช้ยูทิลิตี้การผสม เพียงเพิ่มการตั้งค่ารูทีนไปยังคำสั่ง

genstrings MyCoolApp/Views/SomeView.swift -s localize -o .

ส่วนขยาย:

import UIKit

extension String {
    public static func localize(key: String, comment: String) -> String {
        return NSLocalizedString(key, comment: comment)
    }
}

การใช้งาน:

String.localize("foo.bar", comment: "Foo Bar Comment :)")

คำตอบนี้น่าอัศจรรย์และควรได้รับการสนับสนุนมากขึ้น! นี่เป็นวิธีที่ง่ายที่สุดที่ฉันเคยพบมาหากคุณต้องการหลีกเลี่ยงการนำเข้าไปในห้องสมุดอื่น นี่เป็นวิธีแก้ปัญหาดั้งเดิมที่ดี
cgossain

6

รุ่น Swift 3:) ...

import Foundation

extension String {
    var localized: String {
        return NSLocalizedString(self, tableName: nil, bundle: Bundle.main, value: "", comment: "")
    }
}

6

ที่จริงแล้วคุณสามารถใช้สองขั้นตอนในการแปลข้อความของคุณในโครงการ Swift:

1) เฟสแรกใช้วิธีเก่าเพื่อสร้างสตริงที่แปลได้ทั้งหมดของคุณ:

NSLocalisedString("Text to translate", comment: "Comment to comment")

1.1) จากนั้นคุณควรใช้ genstrings เพื่อสร้าง Localizable.strings:

$ genstrings *swift

2) หลังจากนั้นคุณควรใช้คำตอบนี้

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

NSLocalizedString\((.*)\, comment:\ \"\"\) 

และแทนที่ด้วย

$1.localized

หรือ (ถ้าคุณมีความคิดเห็น)

NSLocalizedString\((.*)\, comment:\ (.*)\)

และแทนที่ด้วย

$1.localizedWithComment(comment: $2)

คุณมีอิสระในการเล่นกับ regex และชุดค่าผสมส่วนขยายต่าง ๆ ตามที่คุณต้องการ วิธีทั่วไปคือการแยกกระบวนการทั้งหมดในสองขั้นตอน หวังว่าจะช่วย


1
ขอโทษฉันไม่ได้รับคำตอบมากมายที่นี่ ประโยชน์ของวิธีการใช้NSLocalizedString("Cancel", comment: "Cancel button title")คืออะไร
LShi

1
@LShi บางคนบ่นว่ามันNSLocalizedStringดูน้อยกว่า Swiftier มากกว่าที่ควรจะเป็น String.localizedในทางกลับกันมีลักษณะ Swifty มากขึ้น แต่คุณไม่สามารถใช้gesntringsยูทิลิตี้ด้วยซึ่งเป็นที่นิยมใช้ในการทำงานของคุณให้เป็นสากล ประเด็นของฉันคือการผสมผสานทั้งสองวิธีเข้าด้วยกันเป็นเรื่องง่าย ดังนั้นส่วนใหญ่มันเป็นคำถามของการอ่าน
GYFK

เกิดอะไรขึ้นถ้าคุณต้องทำรอบของผู้อื่นgenstrings? คุณเปลี่ยนกลับทั้งหมด.localizedโดยNSLocalizedString?
Cristik

5

สร้างวิธีการช่วยเหลือขนาดเล็กสำหรับกรณีที่ "ความคิดเห็น" ถูกละเว้นเสมอ รหัสน้อยง่ายต่อการอ่าน:

public func NSLocalizedString(key: String) -> String {
    return NSLocalizedString(key, comment: "")
}

เพียงวางไว้ที่ใดก็ได้ (นอกชั้นเรียน) และ Xcode จะพบวิธีการทั่วโลกนี้


12
นี่คือการปฏิบัติที่ไม่ดี แนะนำความคิดเห็นและเป็นประโยชน์เว้นแต่คุณจะทำการแปลทั้งหมดด้วยตัวเอง
เยเรมีย์

แม้ว่าคุณจะกำลังแปลความคิดเห็นของตัวเองความคิดเห็นก็จะเป็นประโยชน์โดยเฉพาะในโครงการขนาดใหญ่
ชิม

4

น่าจะเป็นวิธีที่ดีที่สุดคือคนนี้ที่นี่

fileprivate func NSLocalizedString(_ key: String) -> String {
    return NSLocalizedString(key, comment: "")
}

และ

import Foundation
extension String {
    static let Hello = NSLocalizedString("Hello")
    static let ThisApplicationIsCreated = NSLocalizedString("This application is created by the swifting.io team")
    static let OpsNoFeature = NSLocalizedString("Ops! It looks like this feature haven't been implemented yet :(!")
}

จากนั้นคุณสามารถใช้มันได้เช่นนี้

let message: String = .ThisApplicationIsCreated
print(message)

สำหรับฉันนี่คือสิ่งที่ดีที่สุดเพราะ

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

3
สิ่งที่ควรทราบคือสตริงที่กำหนดในวิธีที่อธิบายไว้เป็นสตริงคงที่ ควรเปิดแอพใหม่หลังจากเปลี่ยนภาษาในแอปตั้งค่า iOS ถ้าไม่ใช่ให้เปิดใหม่อีกครั้งด้วยตัวเองเพื่อดูการเปลี่ยนแปลง นอกจากนี้ยังสามารถมีค่าใช้จ่ายหน่วยความจำเนื่องจากเราเริ่มต้นสตริงทั้งหมดในครั้งเดียวไม่ได้ในเวลาที่พวกเขาต้องการ
iDevAmit

2
ฉันคิดว่ามันจะดีกว่าถ้าใช้คุณสมบัติที่คำนวณได้ที่นี่เช่นนี้static var Hello: String = { return NSLocalizedString("Hello") }
ศิลปะแห่งความฝัน

ลงเนื่องจากไม่ได้ปฏิบัติตามแนวทางการตั้งชื่อ
Cristik

3

เมื่อคุณพัฒนา SDK คุณต้องมีการดำเนินการพิเศษ

1) สร้างLocalizable.stringsตามปกติใน YourLocalizeDemoSDK

2) สร้างLocalizable.stringsเดียวกันใน YourLocalizeDemo

3) ค้นหาเส้นทาง BundleของคุณYourLocalizeDemoSDK

Swift4 :

// if you use NSLocalizeString in NSObject, you can use it like this
let value = NSLocalizedString("key", tableName: nil, bundle: Bundle(for: type(of: self)), value: "", comment: "")

Bundle(for: type(of: self))ช่วยคุณค้นหาบันเดิลใน YourLocalizeDemoSDK หากคุณใช้Bundle.mainแทนคุณจะได้รับค่าที่ไม่ถูกต้อง (อันที่จริงมันจะเป็นสตริงเดียวกันกับคีย์)

แต่ถ้าคุณต้องการที่จะใช้นามสกุลสตริงดังกล่าวโดยดร OX คุณต้องทำอะไรมากกว่านี้ ส่วนขยายต้นฉบับมีลักษณะเช่นนี้

extension String {
    var localized: String {
        return NSLocalizedString(self, tableName: nil, bundle: Bundle.main, value: "", comment: "")
    }
}

อย่างที่เรารู้เรากำลังพัฒนา SDK Bundle.mainจะได้รับมัดของ YourLocalizeDemo มัด นั่นไม่ใช่สิ่งที่เราต้องการ เราต้องการชุดข้อมูลใน YourLocalizeDemoSDK นี่เป็นเคล็ดลับในการค้นหาอย่างรวดเร็ว

เรียกใช้รหัสด้านล่างในอินสแตนซ์ NSObject ใน YourLocalizeDemoSDK และคุณจะได้รับ URL ของ YourLocalizeDemoSDK

let bundleURLOfSDK = Bundle(for: type(of: self)).bundleURL
let mainBundleURL = Bundle.main.bundleURL

พิมพ์ทั้งสอง url คุณจะพบว่าเราสามารถสร้าง bundleURLofSDK ฐานบน mainBundleURL ในกรณีนี้มันจะเป็น:

let bundle = Bundle(url: Bundle.main.bundleURL.appendingPathComponent("Frameworks").appendingPathComponent("YourLocalizeDemoSDK.framework")) ?? Bundle.main

และนามสกุลของสตริงจะเป็น:

extension String {
    var localized: String {
        let bundle = Bundle(url: Bundle.main.bundleURL.appendingPathComponent("Frameworks").appendingPathComponent("YourLocalizeDemoSDK.framework")) ?? Bundle.main
        return NSLocalizedString(self, tableName: nil, bundle: bundle, value: "", comment: "")
    }
}

หวังว่ามันจะช่วย


2

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

extension String {

    func localizedWith(comment:String) -> String {
        return NSLocalizedString(self, tableName: nil, bundle: Bundle.main, value: "", comment: comment)
    }

}

https://gist.github.com/Maxdw/e9e89af731ae6c6b8d85f5fa60ba848c

มันจะแยกไฟล์ swift ทั้งหมดของคุณและส่งออกสตริงและความคิดเห็นในรหัสของคุณไปยังไฟล์. strings

อาจไม่ใช่วิธีที่ง่ายที่สุดที่จะทำ แต่เป็นไปได้


1

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

struct Constants {
    // Error Messages
    struct ErrorMessages {
        static let unKnownError = NSLocalizedString("Unknown Error", comment: "Unknown Error Occured")
        static let downloadError = NSLocalizedString("Error in Download", comment: "Error in Download")
    }
}

let error = Constants.ErrorMessages.unKnownError

วิธีนี้คุณสามารถจัดระเบียบข้อความและทำให้ Genstrings ทำงานได้

และนี่คือคำสั่ง genstrings ที่ใช้

find ./ -name \*.swift -print0 | xargs -0 genstrings -o .en.lproj

1

มีประโยชน์สำหรับการใช้งานในการทดสอบหน่วย:

นี่เป็นเวอร์ชั่นง่าย ๆ ที่สามารถขยายไปยังกรณีการใช้งานที่แตกต่างกัน (เช่นการใช้ tableNames)

public func NSLocalizedString(key: String, referenceClass: AnyClass, comment: String = "") -> String 
{
    let bundle = NSBundle(forClass: referenceClass)
    return NSLocalizedString(key, tableName:nil, bundle: bundle, comment: comment)
}

ใช้มันแบบนี้:

NSLocalizedString("YOUR-KEY", referenceClass: self)

หรือเช่นนี้กับความคิดเห็น:

NSLocalizedString("YOUR-KEY", referenceClass: self, comment: "usage description")

1
เป็นการปฏิบัติที่ไม่ดีที่จะแสดงความคิดเห็น
โฮเซ

@ Joséขอบคุณสำหรับความคิดเห็นของคุณ รหัสมีความหมายว่าเป็นแนวคิดไม่ใช่เทมเพลตสำหรับการคัดลอกและวาง แต่ฉันได้เพิ่มตัวเลือกในการเพิ่มความคิดเห็นหากคุณต้องการ;)
GatoCurioso

1

นี่คือการปรับปรุงวิธีการ ".localized" เริ่มต้นด้วยการเพิ่มนามสกุลของชั้นเรียนซึ่งจะช่วยให้คุณมีสตริงที่คุณตั้งโปรแกรม:

extension String {
    func localized (bundle: Bundle = .main, tableName: String = "Localizable") -> String {
        return NSLocalizedString(self, tableName: tableName, value: "\(self)", comment: "")
    }
}

ตัวอย่างใช้สำหรับสตริงที่คุณตั้งค่าโดยทางโปรแกรม:

  override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)

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

class BasicLabel: UILabel {
    //initWithFrame to init view from code
    override init(frame: CGRect) {
      super.init(frame: frame)
      setupView()
    }

    //initWithCode to init view from xib or storyboard
    required init?(coder aDecoder: NSCoder) {
      super.init(coder: aDecoder)
      setupView()
    }

    //common func to init our view
    private func setupView() {
        let storyboardText = self.text
        text = storyboardText?.localized()
    }
}

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

คุณสามารถทำเช่นเดียวกันสำหรับ UIButton:

class BasicBtn: UIButton {
    //initWithFrame to init view from code
    override init(frame: CGRect) {
      super.init(frame: frame)
      setupView()
    }

    //initWithCode to init view from xib or storyboard
    required init?(coder aDecoder: NSCoder) {
      super.init(coder: aDecoder)
      setupView()
    }

    //common func to init our view
    private func setupView() {
        let storyboardText = self.titleLabel?.text
        let lclTxt = storyboardText?.localized()
        setTitle(lclTxt, for: .normal)
    }
}

0

เมื่อคุณแปลพูดจากภาษาอังกฤษโดยที่วลีเหมือนกันเป็นภาษาอื่นที่แตกต่างกัน (เนื่องจากเพศการผันคำกริยาหรือการเสื่อม) รูปแบบ NSString ที่ง่ายที่สุดใน Swift ที่ใช้งานได้ในทุกกรณีคืออาร์กิวเมนต์สามข้อ . ยกตัวอย่างเช่นวลีภาษาอังกฤษ "ก่อนหน้านี้" จะแปลแตกต่างกันไปรัสเซียสำหรับกรณีของ "น้ำหนัก" ( "предыдущ ий был") และสำหรับ "เอว" ( "предыдущ ая был а ")

ในกรณีนี้คุณต้องใช้การแปลสองแบบสำหรับแหล่งข้อมูลหนึ่ง (ในแง่ของเครื่องมือ XLIFF ที่แนะนำใน WWDC 2018) คุณไม่สามารถทำได้ด้วยสองอาร์กิวเมนต์ NSLocalizedString โดยที่ "previous was" จะเหมือนกันทั้งสำหรับ "key" และการแปลภาษาอังกฤษ (เช่นสำหรับค่า) วิธีเดียวคือใช้รูปแบบอาร์กิวเมนต์สามแบบ

NSLocalizedString("previousWasFeminine", value: "previous was", comment: "previousWasFeminine")

NSLocalizedString("previousWasMasculine", value: "previous was", comment: "previousWasMasculine")

โดยที่ keys ("previousWasFeminine" และ "previousWasMasculine") นั้นแตกต่างกัน

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


-1

รองรับหลายภาษาด้วยภาษาเริ่มต้น:

extension String {
func localized() -> String {
       let defaultLanguage = "en"
       let path = Bundle.main.path(forResource: defaultLanguage, ofType: "lproj")
       let bundle = Bundle(path: path!)

       return NSLocalizedString(self, tableName: nil, bundle: bundle!, value: "", comment: "")
    }
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.