mac-cain13 / R.swift

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

Add xcstrings support #886

Open fthdgn opened 6 months ago

fthdgn commented 6 months ago

Updated version of #865. I couldn't update source branch of previous PR bacause it has references on my own apps.

Difference from #865


I did a simple development for xcstrings support.

It is backward compatible for for .xcstrings files converted from .strings files. It means the same R.string codes will be generated.

However, it is not backward compatible for .xcstrings files converted from .stringsdict files, because of named arguments.

About named arguments

My implementation strips names of substitutions from generated arguments. I found using them problematic on some cases.

In my opinion, named arguments does not worth the implementation. Xcode generates substituons only if there are more than one pluralable parameter on new xcstrings files. The most of the string values will not be have any information to generate named arguments.

Problematic case 1

Some key information is lost on the xcstring convertion of stringsdict files. Original stringsdict content.

    <key>x_users</key>
    <dict>
        <key>NSStringLocalizedFormatKey</key>
        <string>%#@users@</string>
        <key>users</key>
        <dict>
            <key>NSStringFormatSpecTypeKey</key>
            <string>NSStringPluralRuleType</string>
            <key>NSStringFormatValueTypeKey</key>
            <string>d</string>
            <key>one</key>
            <string>%d user</string>
            <key>other</key>
            <string>%d users</string>
        </dict>
    </dict>

Generated xcstrings content

{
  "x_users" : {
    "localizations" : {
      "en" : {
        "variations" : {
          "plural" : {
            "one" : {"stringUnit" : {"value" : "%d user"}},
            "other" : {"stringUnit" : {"value" : "%d users"}
}}}}}}}

R.swift creates .x_users(users: Int) for stringsdict file. However, xcstrings file does not have the argument name information any more.

If string value of the original content is <string>Add %#@users@</string>, generated xcstrings would keep the substitution information.

Problematic case 2

{
  "example": {
    "localizations": {
      "en": {
        "substitutions": {
          "device_iphone": {
            "argNum": 1,
            "formatSpecifier": "lld",
            "variations": {
              "plural": {
                "one": {"stringUnit": {"value": "%arg iPhone"}},
                "other": {"stringUnit": {"value": "%arg iPhones"}}
              }}},
          "device_mac": {
            "argNum": 1,
            "formatSpecifier": "lld",
            "variations": {
              "plural": {
                "one": {"stringUnit": {"value": "%arg Mac"}},
                "other": {"stringUnit": {"value": "%arg Macs"}}
              }}},
          "input_iphone": {
            "argNum": 2,
            "formatSpecifier": "lld",
            "variations": {
              "plural": {
                "one": {"stringUnit": {"value": "%arg touch"}},
                "other": {"stringUnit": {"value": "%arg touches"}}
              }}},
          "input_mac": {
            "argNum": 2,
            "formatSpecifier": "lld",
            "variations": {
              "plural": {
                "one": {"stringUnit": {"value": "%arg key"}},
                "other": {"stringUnit": {"value": "%arg keys"}}
              }}},
        "variations": {
          "device": {
            "iphone": {
              "stringUnit": {
                "value": "%#@device_iphone@ and %#@input_iphone@"
              }},
            "mac": {
              "stringUnit": {
                "value": "%#@device_mac@ and %#@input_mac@"
              }}}}}}}}

This strings value is "1 iPhone and 10 touches" on iPhones and "1 Mac and 10 keys" on Mac. There are 2 substitutions for each device variation. Which signature should we use? .example(device_iphone: Int, input_iphone: Int) .example(device_mac: Int, inputmac: Int) .example( arg1: Int, _ arg2: Int) .example(device Int, input: Int) //With some extra coding to detect shared parts of the names.

About algorithm

The alghorithm tries to convert localization of source language to single string with basic format parameters, then it uses FormatPart.formatParts(formatString:)` on this string to extract parameters.

This convertion works like that:

Substitutions replacement works like that:

Example 1

  "account": {
    "localizations": {
      "en": {
        "stringUnit": {"value": "Account"}
      }}}

Example 2

  "x_sets": {
    "localizations": {
      "en": {
        "variations": {
          "plural": {
            "one": {"stringUnit": {"value": "a set"}},
            "other": {"stringUnit": {"value": "%d sets"}}
          }}}}}

Example 3

{
  "example": {
    "localizations": {
      "en": {
        "stringUnit": {
          "value": "%#@books@ and %#@pens@"
        },
        "substitutions": {
          "books": {
            "argNum": 1,
            "formatSpecifier": "lld",
            "variations": {
              "plural": {
                "one": {"stringUnit": {"value": "%arg book"}},
                "other": {"stringUnit": {"value": "%arg books"}}
              }}},
          "pens": {
            "argNum": 2,
            "formatSpecifier": "lld",
            "variations": {
              "plural": {
                "one": {"stringUnit": {"value": "%arg pen"}},
                "other": {"stringUnit": {"value": "%arg pens"}}
              }}}}}}}}
aiKrice commented 6 months ago

Awesome !

aiKrice commented 6 months ago

@mac-cain13 👍

tlacan commented 6 months ago

waiting for it ^^

Flatout73 commented 5 months ago

Let's merge it

deekayd commented 5 months ago

When are we gonna see that PR in action?

sushant-here commented 1 month ago

I am keen to use this on a new app that im starting to work on :)... Lets merge!

aiKrice commented 3 weeks ago

I think R.swift will die. I already moved on and removed it on my project. What I did:

Lutzifer commented 3 weeks ago

@aiKrice

Came here because of the feeling that Swiftgen is not really updated any more and seems to die.

The inconsistency build errors all disappeared after running

 `defaults write com.apple.dt.XCBuild BuildDescriptionInMemoryCacheSize 0` 

source: https://mastodon.social/@NeoNacho/112560574952551066

mzying2013 commented 1 week ago

Pod library version 7.6.0 has not merged the above pull request. Please work on it. Thank you!