+4

[Swift] Localization with UI (XIBs and Storyboards)

Lập trình viên iOS luôn gặp khó khăn với việc làm đa ngôn ngữ trên UI (XIBs hoặc Storyboards).

Cách làm thông thường là sẽ tạo 1 connect đến variable trong file .swift: @IBOutlet weak var cancelButton: UIButton! Rồi sau đó sẽ gọi localization: self.cancelButton = NSLocalizedString("Cancel", comment: "")

Hôm nay mình sẽ giới thiệu đến các bạn 1 cách tiếp cận khác.

Chúng ta có 2 việc cần làm ở đây:

1. Đầu tiên mình sẽ định nghĩa 1 protocol như sau:

public protocol Localizable {
    func localize()
}

Tạo 1 extension cho protocol vừa tạo nhằm kết nối với giá trị đã được localization:

public extension Localizable {
    
    public func localize(_ string: String?) -> String? {
        guard let term = string, term.hasPrefix("@") else {
            return string
        }
        guard !term.hasPrefix("@@") else {
            return term.substring(from: term.index(after: term.startIndex))
        }
        return NSLocalizedString(term.substring(from: term.index(after: term.startIndex)), comment: "")
    }
    
    public func localize(_ string: String?, _ setter: (String?) -> Void) {
        setter(localize(string))
    }

    public func localize(_ getter: (UIControlState) -> String?, _ setter: (String?, UIControlState) -> Void) {
        setter(localize(getter(.normal)), .normal)
        setter(localize(getter(.selected)), .selected)
        setter(localize(getter(.highlighted)), .highlighted)
        setter(localize(getter(.disabled)), .disabled)
    }
}

Mình sẽ định nghĩa các localization bắt đầu với ký tự "@" như code ở trên. Nếu bạn muốn bỏ localization thì chỉ cần sử dụng "@@". Đọc code chắc cũng đã hiểu nhỉ? 😄 OK, như vậy là đã xong việc thứ nhất. Mình sẽ bắt đầu luôn công việc thứ 2.

2. Thêm các định nghĩa localization cho các components thông dụng như UIView, UIButton,UILabel,UIViewController...

extension UIView: Localizable {
    public func localize() {
        subviews.forEach { $0.localize() }
    }
}

public extension UILabel {
    public override func localize() {
        super.localize()
        localize(text) { text = $0 }
    }
}

public extension UIButton {
    public override func localize() {
        super.localize()
        localize(title(for:), setTitle(_:for:))
    }
}

extension UIBarItem: Localizable {
    public func localize() {
        localize(title) { title = $0 }
    }
}

public extension UIBarButtonItem {
    public override func localize() {
        super.localize()
        customView?.localize()
    }
}

extension UINavigationItem: Localizable {
    public func localize() {
        localize(title) { title = $0 }
        localize(prompt) { prompt = $0 }
        titleView?.localize()
        leftBarButtonItems?.forEach { $0.localize() }
        rightBarButtonItems?.forEach { $0.localize() }
    }
}

extension UIViewController: Localizable {
    public func localize() {
        localize(title) { title = $0 }
        navigationItem.localize()
        tabBarItem?.localize()
        view.localize()
    }
}

Đã xong cả 2 việc, giờ mình sẽ demo cho các bạn hiểu hơn. Project mình tạo sẽ có 2 file localizable(Enlish và Japanese) như sau:

Tiếp theo mình sẽ kéo 1 UIButton vào 1 file XIB trong 1 Storyboard: Mình sẽ thay title của button bằng localize string vừa định nghĩa ở trên với prefix là "@" như đề cập ở trên: Và khi đổi ngôn ngữ máy từ En sang Jp và ngược lại các bạn sẽ có kết quả là title của button tự động localize theo setting bạn vừa cài đặt.

Tổng kết

Đây là một trong nhiều cách để làm đa ngôn ngữ trực tiếp trên UI. Mình mong rằng qua bài viết này, các bạn có thể có thêm lựa chọn để làm việc tốt hơn. Nếu có đóng góp gì vui lòng comment nhé. Tks!


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí