mac-cain13 / R.swift

Strong typed, autocompleted resources like images, fonts and segues in Swift projects
MIT License
9.5k stars 763 forks source link

Enable usage of C string placeholder: %s #622

Closed schroepf closed 4 years ago

schroepf commented 4 years ago

For cross platform projects it is often important to have shared strings and translations for android and iOS. But using format strings which contain string placeholders cannot be done in a consistent way for both platforms, as swift cannot easily handle the c string placeholder %s.

Using R.swift strings containing these kind of placeholders are generated to take arguments of type: UnsafePointer<unichar>, but I found no way to put a unichar pointer into the function and produce meaningful output. It seems that unichar is the wrong type to use for String(format: format, locale: locale, value1) which seems to work with 8 bit chars, not 16 bit chars.

So I think that changing the generated type for %s placeholder from UnsafePointer<unichar> to UnsafePointer<CChar> would allow us to work with these kind of format string.

I patched R.swift locally with this change and it seemed to work:

Following code in Localizable.strings:

"test_string" = "Test %1$s, %2$s, %3$s";

produces now the following code in R.generated.swift:

      static func test_string(_ value1: UnsafePointer<CChar>, _ value2: UnsafePointer<CChar>, _ value3: UnsafePointer<CChar>, preferredLanguages: [String]? = nil) -> String {
        guard let preferredLanguages = preferredLanguages else {
          let format = NSLocalizedString("test_string", bundle: hostingBundle, comment: "")
          return String(format: format, locale: applicationLocale, value1, value2, value3)
        }

        guard let (locale, bundle) = localeBundle(tableName: "Localizable", preferredLanguages: preferredLanguages) else {
          return "test_string"
        }

        let format = NSLocalizedString("test_string", bundle: bundle, comment: "")
        return String(format: format, locale: locale, value1, value2, value3)
      }

And this can now be used like so:

        let test = "one".withCString { one in
            "two".withCString { two in
                "three".withCString { three in
                    R.string.localizable.test_string(one, two, three)
                }
            }
        }

        print(test)

Output is:

Test one, two, three
schroepf commented 4 years ago

PR to fix this issue: #623

schroepf commented 4 years ago

With an extension like:

extension String {
    var cstring: UnsafePointer<CChar> {
        (self as NSString).utf8String!
    }
}

Usage of the generated function is even easier:

print(R.string.localizable.test_string("one".cstring, "two".cstring, "three".cstring))
tomlokhorst commented 4 years ago

https://github.com/mac-cain13/R.swift/pull/623 is merged