I want to display in a watch complication localized text that contains an argument set at runtime, e.g. „Available: 3“, where "3" should be set at runtime.
On iOS, this is easy: One defines a localized format string and insets into this format the actual parameter, like:
let str = String.init(format: NSLocalizedString("TEST", comment:"Test"), 3)
where the Localizable.strings file contains an entry
"TEST" = "Available: %i";
In watchOS 3, if one wants to update a complication, one can use
func getCurrentTimelineEntry(for complication: CLKComplication, withHandler handler: #escaping (CLKComplicationTimelineEntry?) -> Void)
There, if the right complication type is provided, one could choose a complication template, and set some text, e.g. using:
let modularLargeTemplate = CLKComplicationTemplateModularLargeStandardBody()
modularLargeTemplate.headerImageProvider = CLKImageProvider.init(onePieceImage: UIImage(named: "Complication/Modular")!)
modularLargeTemplate.headerTextProvider = CLKSimpleTextProvider.localizableTextProvider(withStringsFileTextKey: „TEST“)
where a file ckcomplication.strings contains e.g. an entry
"TEST" = "Available"
In this case, the complication would display „Available“.
The question is, how do I add an actual value, e.g. „3“, to the displayed text?
One can do it exactly like in iOS:
let str = String.init(format: NSLocalizedString("TEST", comment:" "), 3)
modularLargeTemplate.body1TextProvider = CLKSimpleTextProvider(text: str, shortText: str)
where the Localizable.strings file contains an entry
"TEST" = "Available: %i";
This works, if Localizable.strings is target of the iOS app, and the watch extension.
However, I don’t know how to do the same with CLKSimpleTextProvider.localizableTextProvider(withStringsFileTextKey: „TEST“).
Actually, I cannot imagine why a localizableTextProvider exists at all, if it cannot be used together with run time arguments (apparently), and does not provide any advantage (apparently) compared to the solution above.
Related
I was doing localization of an app in which I face an issue regarding dialects of a country's language. My main question is, is there any provision of adding custom language.
Eg:
Suppose there are two languages:
PL for Poland
UK for Ukraine
I need to support pl-uk i.e Poland Ukraini
Adding a pl-UK.lproj would have made sense if this dialect could be chosen from the system preferences, which is not the case. If you have a local setting, I'm afraid there's no other solution than managing the localisations yourself - and it won't work for Interface Builder files.
The simplest is to store all the pl-UK differences in a separate file (it can be a .strings that you store into the pl.lproj folder (that you localise in Polish Polish - to respect the semantics of the system). Then in a custom function, you load those strings:
func localize(_ string : String, comment: comment) {
guard !isUkrainianPolish else {
return NSLocalizedString(string, comment: comment)
}
// retrieve the cache and check if a key with string exists
if let url = Bundle.main.url(forResource: "localizable_pl_UK" /* or any other name*/, withExtension: "strings", subdirectory: nil, localization:"pl"),
let data = try? Data(contentsOf: url),
let plist = (try? PropertyListSerialization.propertyList(from: data, options: [], format: nil)) as? [String:String] {
// cache the dictionary where you want
return plist[string] ?? NSLocalizedString(string, comment: comment)
}
}
Depending on the organisation of your code, you can implement the function in a singleton or the class that handle localizations.
I am creating an app that for now I would like to offer in English and German. I checked the Base localization mark in the project configuration and added German. I left English as the development language.
Then I created a file Translation.plist which basically consists of dictionaries that I call categories, e.g. I have one dictionary for button texts, label texts etc. Each of the category dictionaries again consists of dictionaries that contain two Strings: value and comment. Translation.plist is localized via XCode. The folders Base.lproj, en.lproj and de.lproj exist and contain a copy of the plist-file as expected.
Then I created a class Translator.swift that is supposed to load the Translation.plist file as an NSDictionary depending on the user's preferred locale. The code looks like this:
func relevantDictionary(category: String) -> NSDictionary {
let preferredLocale = Bundle.main.preferredLocalizations.first ?? "Base"
NSLog("User's preferred locale is \(preferredLocale)")
guard let url = Bundle.main.url(forResource: "Translation", withExtension: "plist") else {
fatalError("Could not find Translation.plist")
}
NSLog("Using \(url.absoluteURL) for translation")
guard let root = NSDictionary(contentsOf: url) else {
fatalError("Could not find dictionary for category (locale=\(preferredLocale)")
}
guard let relevant = root.value(forKey: category) as? NSDictionary else {
fatalError("Could not create dictionary from Translation.plist")
}
return relevant
}
Then I created a String extension that uses the Translator as follows:
func localize(category: String) -> String {
return Translator.instance.translate(category: category, string: self)
}
With this I call the Translator by "yes".localize("button"). In English I would expect "Yes", in German I would expect "Ja". The log says the following:
2017-07-05 08:45:24.728 myApp[13598:35048360] User's preferred locale is de_DE
2017-07-05 08:45:24.728 myApp[13598:35048360] Using file:///Users/me/Library/Developer/CoreSimulator/Devices/A39D3318-943D-4EFE-BB97-5C2218279132/data/Containers/Bundle/Application/4614E696-B52E-4C30-BBE8-3C76F6392413/myApp.app/Base.lproj/Translation.plist for translation
I wonder why this happens and what I have missed. I would have expected that de.lproj/Translation.plist is loaded instead of Base.lproj/Translation.plist.
Any help is highly appreciated.
You can do it with single .plist file. You don't need to create different .plist files for it.
Firstly, Add English and German countries with locale in Project -> info
https://i.stack.imgur.com/M4QIY.png
Once, you added countries with locale in Project -> info then add localizable.strings file in your bundle.
https://i.stack.imgur.com/lnjgL.png
At the end, just add country's locale in your language support class.
NSArray* languages = #[#"en", #"de"];`enter code here`
NSString *current = [languages objectAtIndex:0];
[self setLanguage:current];
Hope it would help you.
ios does not let the containing app and the contained extensions to share a common container, so UserDefaults is the proposed solution.
I have tried using UserDefaults with sirikit intent handler assuming the handler behaves as an extension as follows :
inside IntentHandler.swift
let shared = UserDefaults(suiteName:XXXXXXXX.group...)
shared?.set("saved value 1", forKey: "key1")
shared?.set("saved value 2", forKey: "key2")
shared?.set("saved value 3", forKey: "key3")
inside ViewController.swift in viewDidLoad
let shared = UserDefaults(suiteName:XXXXXXXX.group...)
if let temp1 = shared?.string(forKey:"key1")
{
contentLabel.text = temp1
}
if let value = shared?.string(forKey: "key2")
{
valueLabel.text = value
}
if let key = shared?.string(forKey: "key3")
{
keyLabel.text = key
}
i can see the strings corresponding to key1 and key2 on my ipad screen but not for key3, peppering the code with synchronizes does not help.
here are my questions :
1) are sirikit handlers different from other extensions? if yes how to pass data to my app? if not am i using UserDefaults incorrectly?
2) is there a better way to handle IPC between the app and its extensions where i just need to pass simple string messages between them.
using swift 3.0 and xcode 8.2.1
Check that you have the App Group enabled for all targets you want to access the group from. Check in project -> your target -> capabilities under "App Groups".
There's something called MMWomhole. It will definitely do the work.
I'm trying to find several different data types including Dates, Addresses, Phone numbers, and Links. I'm already able to find them but I want to be able to format them by underlining and changing their color. This is my code so far.
func detectData() {
let text = self.textView.text
let types: NSTextCheckingType = .Date | .Address | .PhoneNumber | .Link
var error: NSError?
let detector = NSDataDetector(types: types.rawValue, error: &error)
var dataMatches: NSArray = [detector!.matchesInString(text, options: nil, range: NSMakeRange(0, (text as NSString).length))]
for match in dataMatches {
I was thinking I should first get each result out of the loop then
1) turn them into strings 2)format them.
First question. How will I put my formatted string back into my UITextView at the same place?
Second question. I'm thinking about creating a switch like so
switch match {
case match == NSTextCheckingType.date
but now that I have a specific type of NSTextCheckingType, what do I have to do to make them have the functionality I want? (e.g. call a phone number, open up maps for an address, create a event for a date)
To do what Notes does you just need to set the dataDetectorTypes property on your text view. That's all! No NSDataDetector involved.
Is it possible to retrieve strings for a specific locale programmatically regardless of what locale the phone is set to? For example, users may be running the phone in English, but I want to retrieve French strings instead without changing the OS locale setting.
Note: This is not a duplicate of the above question. I do not want to override the current setting within my app, I merely want to have the ability to retrieve language values of whatever locale I wish programatically. My app contents may be displaying English text, but I want a specific component of my app to display a different language instead.
I solved this by extending String with this method. You can get localized string for any locale you have in your app this way.
extension String {
func localized(forLanguageCode lanCode: String) -> String {
guard
let bundlePath = Bundle.main.path(forResource: lanCode, ofType: "lproj"),
let bundle = Bundle(path: bundlePath)
else { return "" }
return NSLocalizedString(
self,
bundle: bundle,
value: " ",
comment: ""
)
}
}
Example (get localized string for ukrainian language when system language is english):
"settings_choose_language".localized(forLanguageCode: "uk")