Problems with genstrings in Swift app

I’m running El Capitan 10.11.5 and xCode 7.3.1


I am localizing a Swift application for iOS. Due to the possibility of multiple occurances of the same string in different contexts I don’t want to use the typical form of NSLocalizedString that only takes a key and a comment. For example:


let hello = NSLocalizedString("Hello", comment: "greeting on first view controller”)


I prefer to use the form that allows specification of the a key distring from the value. For example:


let bundle = NSBundle.mainBundle() let hello = NSLocalizedString("initialview.hello", tableName: "Localized", bundle: bundle, value: "Hello", comment: "greeting on first view controller”)


I then run


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


to generate my strings file. The genstrings command emits the follow error:


Bad entry in file .//LocalizationTest/ViewController.swift (line = 29): Argument is not a literal string.


Searching the usual places (like stackoverflow) indicates that this problem has existed for a while, multiple radars have been filed, and there has been no resolution.


What are my options? If I decide to go with the simplified form of NSLocalizedString and to replace redundant strings with identifiers when necessary (like I did in second example above) then how do I subsequently edit the Localized.strings file to update the keyto replace the redundant instance? Is there another way in Swift to use something other than the literal string as the key?


Thanks,

Cliff

Accepted Answer

AFAIK it's always been true that genstrings has accepted only the 2-parameter syntax for NSLocalizedString. After all, it's a purely lexical parser, not a syntactic one. (It predates the clang parser library by a long time.)


However, in modern Xcode projects you shouldn't be using genstrings at all. Instead, the workflow is to export (and later re-import) LIFF files. In this workflow, Xcode is responsible for finding the localizable strings, and I assume it handles all the variants of NSLocalizedString properly (though I haven't tried it to find out for sure).


Since you're using Swift, you can do even better than this, because you can wrap localizable strings in a a struct or enum to make them type-safe (that is, you prevent passing or producing an arbitrary string, but use typed values instead), and that also nicely solves the difficulty of having string values that are identical but semantically different. Apple is now doing this with globals strings like notification names. I think this is discussed in the 2016 WWDC Swift API video, so I suggest you take a look.

Yes, thanks. I have also learned that if for some reason one wants to run genstrings by hand a functional equivilant is to execute

xcrun extractLocStrings

Cliff

I've been working with this in Xcode 7.3.1 recently and I can confirm that the export process does support the different variants for NSLocalizedString, including tableName and value.

When the XLIFF export / import process is done, it will create the correct string tables inside the Supporting Files directory.


However, I'm still not clear on how to create strings for the development language. The exporter will only export for language which are NOT in the development language. In the past, you could use genstrings to create them, but now genstrings doesn't fully support swift, I don't see an easy way to keep the development language strings updated from the source of truth in the code.


I asked the question on stackoverflow here:

http://stackoverflow.com/questions/39240465/xcode-localization-how-to-create-localized-strings-for-development-language


Regarding using Swift enums, I had high hopes for this too. But the export process is still calling similar tooling under the hood (I believe it's xcodebuild -exportLocalizations) , which is still just a lexical parser. So we're still stuck with the awful NSLocalizedString syntax.


The best I've been able to do is to keep all the NSLocalizedStrings in a single enum file, per string table, and defining them as constants.


enum MyStrings {
    static let something = NSLocalizedString("something.1", tableName: "MyStrings", value: "Hello world!", comment: "lorem ipsum comment 1")
    static let somethingElse = NSLocalizedString("something.2", tableName: "MyStrings", value: "Hello again!", comment: "lorem ipsum comment 2")
}

Looking at your stackoverflow question, I think you just Did It Wrong™. You should have created a Chinese localization and exported that. Instead, you exported the development language, and your translator changed your English strings to Chinese strings that were re-imported into the development language, which was English. It was still regarded as English, even though all the words were now Chinese.


>> I'm still not clear on how to create strings for the development language. The exporter will only export for language which are NOT in the development language.


Do you have base internationalization turned on? That moves string definitions out of the story board and into a "base" folder. However, it's still a bit murky what this means in terms of localization. A localization is a set of strings that replace the base strings at run time, when running on system set to a different language. The development language strings are the strings being replaced, so they're not localizable as such. At least, that seems to be the rationale behind this.

I'm actually hoping that I've done it wrong and missed something, because it seems this would be a huge gap in the Xliff export/import workflow...


>> your translator changed your English strings to Chinese strings that were re-imported into the development language, which was English. It was still regarded as English, even though all the words were now Chinese

That's not the case. I've edited my stackoverflow post to be more clear now.

I started with NO strings files at all, just my Swift source code, which has `NSLocalizedString` macros.

When you do the first export, it will generate an Xliff file which does NOT contain any `<target>` tags, which means the translation is blank.

When translators use tools to translate, it creates those `<target>` tags.

When you re-import it, Xcode creates the strings files for you, for that language that the translator has specified.


I believe the problem stems from the fact that the system will attempt to use the first language which your app supports, which is also in the user's preferred languages list in the settings.

E.g. the user has English, and then Chinese in their preferred languages order.

If you only include chinese strings, but not english string, it will use chinese.

On further testing, it seems that as soon as you include an entry in a strings file for for one localization, it will fallback to that one (even if it's NOT one of the user's preferred languages). So the default value in the macro seems to only be used when it can't be found in ANY strings file.

This means I MUST have english strings files, so I need to generate them somehow.

But it seems that XLIFF workflow doesn't allow it.


I've also put on my SO post, that in the documentation for Separating User-Facing Text from Your Code, it says "Alternatively, you can generate the development language strings files from NSLocalizedString macros directly, as described in Creating Strings Files for User-Facing Text in Your Code." , which tells you to use

genstrings

This is pretty much what I'm after.

But genstrings doesn't work properly for Swift anymore.

So what's the new way?


(btw, I'm not using Storyboard/Xib localization, it's too much of a mess for translators)

I think I've worked it out


The problem was that the app didn't think that my development language was supported, so it did further fallback, and ended up picking a language that my app was localized for (in this case, chinese).


By adding a file localized for english, and ensuring there is an en.lproj folder in the bundle, it infers my app is localized for english, and will correctly use the default values in the development language.


This means I don't need a seperate copy of the english strings files.


This link was helpful:


How does iOS determine the language for my app

https://developer.apple.com/library/ios/qa/qa1828/_index.html

Problems with genstrings in Swift app
 
 
Q