Cannot localize my Swift 5 framework. Localization import does nothing - swift

I develop a Swift 5 framework that runs on iOS 12 and above for iPhone and iPad.
I added French localization in addition to English, but the project info tab shows there's no localized files for both languages as shown on the image below:
When I run Xcode Editor|Export for localization I get the bundled files (including the fr.xliff file). I’ve edited fr.xliff with Xcode to translate some strings, and imported it back. But nothing changed in the app.
Btw, I can’t see any of the *.strings files in the project files hierarchy, which sounds weird.
[Update]
So I manually added Localizable.Strings file, and added English and French.
When I import xliff files, the Localizable.strings files remains empty. No apparent issue pops up.
When I put the key=value list in the string file, the NSLocalizedString() does not return the right localised version of the string (it always return the key).
Help appreciated.

Found what's wrong: I had to use the full (extended) version of NSLocalizedString() in order to make the function call get the translation from my framework Localizable.strings files (rather than the client application), as follows:
NSLocalizedString("Continue", tableName: tableName, bundle: bundle, comment: "Continue in the button title of UIAladdinOnboardingViewController")
Where:
tableName: a let global that hold the name of my localisable string file (in my case "Aladdin.Localizable"). I did this to avoid potential collision of .strings files (as my framework name is Aladdin);
bundle: a let global in my the framework name space to designates the framework model bundle to pick the string from. I defined it like this: let bundle = Bundle(for: ExtendedAppDelegate.self) where ExtendedAppDelegate is one of my framework custom class.
Side note of importance:
Btw, don’t put your interpolating string as the key parameter of the NSLocalizedString call, otherwise it’s gonna be interpolated at runtime, leading to an interpolated string that won't exist in your string file.
Don’t do that:
let s = NSLocalizedString("{Start your \(introductoryPeriod) free trial.}", tableName: tableName, bundle: bundle, comment: "in price display")
Do this:
let s = String(format: NSLocalizedString("{Start your %# free trial.}", tableName: tableName, bundle: bundle, comment: "{Start your \\(introductoryPeriod) free trial.}"), introductoryPeriod)
With the corresponding Aladdin.Localizable.strings (en):
/* {Start your \(introductoryPeriod) free trial.} (in price display) */
"{Start your %# free trial.}" = "{Start your %# free trial.}";
To generate the strings:
I used the gestrings from the terminal as follows:
$genstrings -o en.lproj/ *.swift
$genstrings -o fr.lproj/ *.swift
And so on for every target languages you have.
Finally, do the translation straight in the string files.

in Localization you can Localize your string directly from Storyboard.
after adding your language..click on Main.storyboard -> go to identity and type...
you can see your localize languages string...Image of your Main.strings -> select them select all Languages..
after that you can see main.strings(French) and main.strings(English) in your Navigation panel...
change your strings on prefer languages like...
Image of how to change your strings in English
Image of how to change your strings in French
now, change Your Simulator Language from Edit schema -> Run -> Your language(French or English).
it's working..

Related

How to localise CL applications in macOS (Xcode)?

I am having trouble localising a command-line application I've created in Xcode. Here's what I did:
Used NSLocalizedString on all strings to be translated
Add base localisation, and the target localisation under Project settings
Add Strings file, check both English, and target language checkboxes
Create strings to be localised via genstring (both English and target language) and localised them as necessary
When I build the application, I still get all the text in English. What could be the issue here? My system is set to my target language, and when I try to add a scheme with the target language, I get the following error:
Error: 2 unexpected arguments: '(language_code)', 'NO'
This error is only present if I use swift-argument-parser for the application. For plain applications, it's still not possible to get the localised version.
Xcode 11.3, macOS10.15.3
Usually when you build a Cocoa application (with NSApplicationMain), the framework will take care of loading the appropriate localized strings from the main bundle. The framework will parse UserDefaults overrides from command line arguments, so you/Xcode can supply locale settings via flags like -AppleLanguages and -AppleLocale .
When you write a command-line utility, by default there is no bundle, no startup logic that performs localization, and no parsing of UserDefaults. Setting the target language from the scheme just adds the arguments (which is why you get an error from swift-argument-parser). However, you can load resources from a bundle yourself.
// Get the language from UserDefaults (to support scheme settings, will also need to parse command line arguments)
let language = UserDefaults.standard.stringArray(forKey: "AppleLanguages")?.first ?? "en"
// In addition to the command-line binary, you'll also need a bundle with the translations somewhere
let path = "/path/to/\(language).lproj"
let lproj = Bundle(path: path)
let localized = lproj?.localizedStringForKey("key", value: nil, table: nil)
Note that Bundle.main for an unbundled command line utility will just be the parent directory of wherever the binary is. So you can also use NSLocalizedString if your lproj files are built/installed in the appropriate place, but this would be unusual for utilities installed to binary directories like /usr/local/bin.

Getting path for resource in Command Line Tool

I'm trying to get the path of a resource in a Command Line Tool in Xcode (8 beta 2). Here's what I've got:
The resource, file.xyz, has been dragged into the project and the target membership matches the main project.
Under Build Phases -> Copy Files, the destination is set to "Resources" and the subpath is empty. "Copy only when installing" is unchecked and file.xyz is listed in the table below.
In my main.swift file, I have the following code:
guard let filePath = Bundle.pathForResource("file",
ofType: "xyz",
inDirectory: "Resources") else{
fatalError("Could not find file")
}
The fatalError is triggered every time. Any ideas? I understand that Command Line Tools don't have an application bundle and that's why the resource is being copied into the Resources folder instead, but my inDirectory argument is a bit of a wild guess… Thanks for reading.
EDIT: My primary goal is to access a resource file (text file, in this case) from a CLT—I'm not attached to this specific way of doing it, so if there's an alternate approach that's great too!
With Xcode 13.2 and Swift 5, I had same issue: I've dragged a JSON file into my Command Line Tool project and was not able to use it directly in my code. As CLT does not seems to have a Bundle, I have found a workaround.
I have created a struct with only a static let property containing my JSON as string:
struct CategoriesJSON {
static let string = """
... my JSON is here as plain text ...
"""
}
Now I can use it by simply call: CategoriesJSON.string.
I know it's a bit ugly, but now my JSON is in my project as I needed.
As a workaround, you can use the absolute path of the source file
For example if on iCloud drive :
let path = "/Users/myName/Library/Mobile
Documents/com~apple~CloudDocs/myDirectory/file.xyz"

Swift NSLocalizedString practical use

I couldn't understand and find any sources that showed me the practical use of NSLocalizedString. Even Apple documentation..
NSLocalizedString(<#key: String#>, tableName: <#String?#>,
bundle: <#NSBundle#>, value: <#String#>, comment: <#String#>)
Ok so lets say I created this in a file, how do I write this for different languages?
Do I create a localized version of the whole file like: en.thisfile.swift , es.thisfile.swift, fr.thisfile.swift?
Or do I create new languages in different bundles or tables?
NSLocalizedString("hello", tableName: "en", value: "hello", comment: "wtf is this anyways?")
NSLocalizedString("hello", tableName: "fr", value: "bonjour", comment: "wtf is this anyways?")
NSLocalizedString("hello", tableName: "es", value: "hola", comment: "wtf is this anyways?")
And then how do I use it in a random file after setting it up?
println("dat nslocalizedstring I created, which will automatically know what to write??")
The table is the name of the file that defines the string, for example, table "ErrorMessages" will define a file called ErrorMessages.strings.
If no table is specified, the file will be the default Localizable.strings. The format of the file is a list of the following:
/* This is displayed when the entity is not found. */
"MSG_object_not_found" = "Object not found. Please, try again.";
Where the first line is the comment, the value on the left side is the key that identifies the string and the value on the right side is the actual localized value.
Usually, you can use the English value as the key (the 2 parameter version of NSLocalizedString). The language name (en, fr, es) is not part of the method call.
All of this is there to make translation simple. Note that most translators won't understand or even see the application, that's why it's important to have a good comment to provide a context for the message. There are examples of funny translation all over the internet which were created because the context was missing and the translators didn't know what they were translating.
Basically, your code will contain the texts in your main language and then you will generate the .strings files from your code using genstring tool (See this for more info). Then you will give the files to your translator, add the translated files to your application (every language in a different folder) and the application will select the correct files at runtime depending on the language selected by the user.

Creating a translator/dictionary in IOS

So Im importing a text file that contains a list of character sets. These sets have a meaning they refer to a status of an object. For example TOMTOM100 means Delivery announced. Ones i import he text file the status is presented in 0-5 labels(depends on how many status updates are available).
At first i wanted to do this with a if statement but quickly realized that it would be to much.
if ((trackTraceStatusone.text = #"TOMTOM100"))
{
trackTraceStatusone.text = #"Delivery announced.";
}
Is there a way to create some kind of translator that automatically translates the status in a readable format?
TOMTOM100 > Delivery announced
TOMTOM101 > Delivery Scanned
and so on.
Sounds like a job for NSLocalizedStringFromTable() or the corresponding NSBundle method -localizedStringForKey:value:table:. This will let you load the string from a .strings file in your bundle, which will look something like this:
"TOMTOM100" = "Delivery Announced";
"TOMTOM101" = "Delivery Scanned";
This will also make it easy to provide different strings for different languages. For more information, see the String Resources section of the Resource Programming Guide.

iOS how to localize files in for a module

I have a class that implements the "Take photo / Choose from library" that we all know and love. It is here https://github.com/fulldecent/FDTake This is included in my other projects via git submodule and that works fine.
Now I need to translate the text in that class to Chinese so it is "拍照 / 选照片" or something like that. Is there a good way to put translations in there so everyone can use them?
Localization is typically handled by the NSLocalizedString(<#key#>, <#comment#>) macro. In the source your replace all hard coded string with the macro. For example:
[self.buttonTitles addObject:#"Hi"]; // hard coded greeting
with
[self.theLabel setText:(NSLocalizedString(#"theKey", #"Hi"))];
Then genstrings (from inside a Terminal) is used to scan the implementations file (*.m) and to write its output to the language project folder (here: en.lproj)
$ genstrings -o en.lproj/ *.m
In the directory en.lproj/ a file called Localizable.strings. Its contents will be:
/* Hi */
"theKey" = "theKey";
The comment /* Hi */ is taken from our source code. The string Hi should be displayed to the (English speaking) user. So we need to edit the string on the right hand side of the equals sign and make it the greeting, = "theKey" has to become = "Hi":
/* Hi */
"theKey" = "Hi!";
So far so good
All this is fine if there are only a few strings or when there is no intend to ever modify the strings. The moment gestrings is run again it will overwrite the modifications and you effectively lose the work done in Localizable.strings.
An idea could be to write the output of genstrings to a different location. But then you will have to manually merge the changes. Once the Localizable.strings file grows it becomes a nightmare trying to keep source code and Localizable.strings in sync. So lets try to avoid that.
A big help comes from using NSLocalizedStringWithDefaultValue(<#key#>, <#tbl#>, <#bundle#>, <#val#>, <#comment#>). This macro will allow to set a default value in the Localizable.strings file and in addition, it will make the need for the initial edit of the value field go away.
Putting it together:
[self.theLabel setText:NSLocalizedStringWithDefaultValue(#"theKey2", #"Localizable", [NSBundle mainBundle], #"Hi!", #"informal greeting"))];
After running the genstrings command as used above there is now a small different in Localizable.strings
/* informal greeting */
"theKey2" = "Hi!";
Apart from the comment now telling the translator that we want an informal greeting, the “Hi!” is already present in the value filed. There is no need to go to the Localizable.strings file, search for the correct line, modify the field form “theKey” to “Hi!”. genstrings did that for us based on the default value supplied with NSLocalizedStringWithDefaultValue.
Add the file Localizable.strings to the xcode project.
Doing the translations
After changing the source code, for a new language from inside the Xcode first add a localization to Localizable.strings. Xcode will generate a copy of Localizable.strings under a subfolder based on the original Localizable.strings.
I personally don't speak Chinese, but German. So if to add German localization my translation would go under de.lproj/Localizable.strings, italian under it.lproj/ and so on.
Edit the new Localizable.strings as needed:
(German)
/* informal greeting */
"theKey2" = "Hallo!";
(Italian)
/* informal greeting */
"theKey2" = "Ciao!";
and then build and run.
********* begin edit
Bundle it up
The above considers a "standard" xcode project. You are asking about cereateing a module, therefore allowing your code to become an addition to a project. I suggest you create a bundle with the localizations. When somebody will include your code into their project the localizations remain separate.
Full documentation about bundles is here.
A project that uses bundles for localizations is QuincyKit (there probably are more, that one was the first that came to mind)
So when placing the localization into a bundle other than the mainBundle the [NSBundle mainBundle] in the line below has to change
[self.theLabel setText:NSLocalizedStringWithDefaultValue(#"theKey2", #"Localizable", [NSBundle mainBundle], #"Hi!", #"informal greeting"))];
Instead of getting the strings from the mainBundle, obtain a reference to your own module. The docs suggest:
NSBundle* myBundle = [NSBundle bundleForClass:[self class]];
So the line becomes:
[self.theLabel setText:NSLocalizedStringWithDefaultValue(#"theKey2", #"Localizable", myBundle, #"Hi!", #"informal greeting"))];
********* end edit
PS: : my original text can be seen here
I think a bundle (as Olaf suggests) would work great, but another way with less overhead to ensure that your localizable string resources don't interfere with another project in the same solution (causing a weird problem to people reusing your component in another localized project) is to change your localizable.strings file name to a unique file name. This means that where you used NSLocalizedString you now need to use its variant NSLocalizedStringFromTable (Apple documentation), where tableName is the same name as your strings file (without the .strings extension). You can define your own macro so that's it's just a straight replacement of NSLocalizedString with, say FDTakeLocalizedString.
A file name collision is much less likely to happen with XIBs files or storyboards than for the localizable.strings file. But in both cases, if you use a prefixed naming convention (say FDTake.strings and FDTake-Main.xib), it will eliminate the risk and can only help.