Localization with String interpolation in SwiftUI

戏子无情 提交于 2021-02-07 13:57:39

问题


I am trying to localize my SwiftUI Watch app. I don't have any problems with static strings. I use LocalizedKeyStrings in my Text views and add my translations in Localizable.strings files. For example:

Text("history")

in Localizable.strings:

"history" = "Historique";

Result : "Historique"

But I also want to localize stings using interpolation. For example:

Text("startCustom \(format: "%.1f",customDistance)")

In Localizable.strings, I have tried with different syntax:

"startCustom %@" = "Course de %@ km";

or

"startCustom %f" = "Course de %f km";

or

"startCustom %.1f" = "Course de %.1f km";

Nothing works. I don't find any documentation for that ...


回答1:


The following simple just works (tested with Xcode 11.4)

Text(String(format: NSLocalizedString("startCustom %.1f", comment: ""), 
     self.customDistance))

with Localizable.string having

"startCustom %.1f" = "Course de %.1f km";



回答2:


Apparently, a LocalizedStringKey will automatically generate the localization key depending on the type of the values interpolated. For example, if you have the following Texts

Text("title key")
Text("name key \("Club")")
Text("count key \(8)")
Text("price key \(6.25)")

Your Localizable.strings file should look like

"title key" = "Sandwiches";
"name key %@" = "Name: %@";
"count key %lld" = "%lld sandwiches";

// You can change the format specifier in the value, but not in the key.
"price key %lf" = "Price: %.2lf";  

Be careful if you want to support 32-bit systems (iPhone 5 or earlier). In a 32-bit system, Int is Int32, the key of "int32 key \(Int32(8))" is "int32 key %d". You can always convert an integer to Int64 like in "count key \(Int64(8))" to enforce consistent keys across different systems.

Remark 1: For people who want to know how it works. When you use a string literal or an interpolated string such as "count key \(8)" in Text, the compiler will consider the string as a LocalizedStringKey, because Text has an initializer

init(_ key: LocalizedStringKey, tableName: String? = nil, bundle: Bundle? = nil, comment: StaticString? = nil),

and LocalizedStringKey conforms to ExpressibleByStringLiteral and ExpressibleByStringInterpolation and thus can be implicitly initialized from a string literal or a string interpolation.

Remark 2: If you're not sure what the key is, you can get the answer yourself by po a LocalizedStringKey in the debugger like this:

po LocalizedStringKey("count key \(8)")



回答3:


I have built this extension to String to manage localization across my apps. You can simply use it like "history".localized to get the localized string.

To apply replacements, use the method "my string %@".localized(withSubstitutions: "my substitution")

You can slice and dice to use what you want from the extension.

I have also maintained 2 constants let sameInBothLanguages: [String] & let needTranslationsFor: [String] to keep a record if there are strings which shouldn't be localized because they are same in both languages or to be sent out to content team for translations.

extension String {
    var localized: String {
        return localized(from: nil)
    }

    func localized(withSubstitutions substitutions: String...) -> String {
        return String(format: self.localized, arguments: substitutions)
    }

    func localized(from table: String?) -> String {
        let translatedString = getTranslatedString(fromTable: table)

        // No sense looking up the string in Release builds
        #if !DEBUG
            return translatedString
        #endif

        guard Locale.current.languageCode == "en" else {
            return translatedString
        }

        let otherLanguage = "es"

        // We can keep adding to this list temporarily in order to make the app actually run.  Every so often we will give this list to the content team and empty it once we get the translations back.
        let otherLanguageString = getTranslatedString(fromTable: table, inLanguage: otherLanguage)

        if otherLanguageString == self &&
            !sameInBothLanguages.contains(self) &&
            !needTranslationsFor.contains(self) {
            //swiftlint:disable:next no_nslocalizedstring
            assertionFailure("No Spanish version of localized string found for '\(self)'.  Please go to String+SA.swift and add this string to either the 'needTranslationsFor' or 'sameInBothLanguages' array.")
        }

        return translatedString
    }

    private func getTranslatedString(fromTable table: String?, inLanguage language: String) -> String {
        if let path = Bundle.main.path(forResource: language, ofType: "lproj"),
            let otherLanguageBundle = Bundle(path: path) {
            let otherLanguageString = getTranslatedString(fromTable: table, andBundle: otherLanguageBundle)

            return otherLanguageString
        }
        return self
    }

    private func getTranslatedString(fromTable table: String?, andBundle bundle: Bundle = Bundle.main) -> String {
        let translatedString = bundle.localizedString(forKey: self, value: self, table: table)

        return translatedString
    }
}


来源:https://stackoverflow.com/questions/62042521/localization-with-string-interpolation-in-swiftui

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!