Can't get the CFBundleIdentifier from dynamically loaded NSBundle - ios

I'm trying to load a bundle dynamically, to be used in place of the mainBundle in few cases.
I managed to load the bundle with some resources, like Localizable.strings and so on.
And when I use localizedStringForKey against that bundle, the right localized string is loaded. This is to say that it works.
Nevertheless, I'd like to get even have a bundle identifier. Thus, I added to the root of the bundle folder the info.plist file, containing the CFBundleIdentifier string.
This, doesn't work. When I try to get the identifier via
[myBundle bundleIdentifier]
I get a null value. I tried to name the file both as
Info.plist
and
MyBundle-Info.plist
where MyBundle is the name of the bundle (the content is stored in MyBundle.bundle). But no luck.
I really don't get what's wrong. Do I have to set other keys in the info plist? Or maybe it's a naming problem? Any help will be more than appreciated.

I ended up looking at the Core Foundation (after all, NSBundle is based on CFBundle) source code and figured out where was the problem. So..
The function in charge of gathering the info.plist content is CFBundleGetInfoDictionary. This is called anytime an information theoretically contained in the plist is requested.
Looking at CFBundle.c, the implementation of CFBundleGetInfoDictionary checks whether the _infoDict field of the bundle is initialized and in case it's not, it initializes it:
if (!bundle->_infoDict) bundle->_infoDict = _CFBundleCopyInfoDictionaryInDirectoryWithVersion(CFGetAllocator(bundle), bundle->_url, bundle->_version);
By calling that function on my CFBundle I didn't have any luck, so I guessed something wrong must have been happening in _CFBundleCopyInfoDictionaryInDirectoryWithVersion.
Looking at the source code, I noticed that, depending on the bundle->_version, a different path is used to search the info plist. The version in this case depends on the dir structure used to setup the bundle. To be exact, my version was 0, because, as specified in the function _CFBundleURLLooksLikeBundleVersion (used during bundle initialization), bundle with the Resources dir are this:
// check for existence of "Resources" or "Contents" or "Support Files"
// but check for the most likely one first
// version 0: old-style "Resources" bundles
// version 1: obsolete "Support Files" bundles
// version 2: modern "Contents" bundles
// version 3: none of the above (see below)
// version 4: not a bundle (for main bundle only)
So, to finish the story, the base URL of the Info.plist is initialized in _CFBundleCopyInfoDictionaryInDirectoryWithVersion based on the version 0
infoURLFromBase = _CFBundleInfoURLFromBase0;
that is defined as:
#define _CFBundleInfoURLFromBase0 CFSTR("Resources/Info.plist")
Soooo... I've put my Info.plist in the Resources dir and not outside and now it works.
I guess I'm somehow an idiot for having done all this trip, given that this stuff is probably written somewhere in the doc, but I couldn't find it :(

Related

Bundle.preferredLocalizations confusion

The 3 preferredLocalizations of Bundle are so confusing that I have so many questions:
Why the 2 preferredLocalizations methods are class methods but keep talking about some unspecified specific bundle as if they are called on some individual Bundle instance?
preferredLocalizations(from:): What exactly are a bundle object and the bundle?
Returns one or more localizations from the specified list that a bundle object would use to locate resources for the current user.
An array of NSString objects, each of which specifies the language ID for a localization that the bundle supports.
preferred​Localizations(from:​for​Preferences:​): What exactly are the specified bundle and the receiver’s bundle?
Returns the localizations that a bundle object would prefer, given the specified bundle and user’s language preferences.
An array of NSString objects, each of which identifies a localization in the receiver’s bundle. These strings are ordered in the array according to the specified preferences and are taken from the strings in the localizations​Array parameter. If none of the user-preferred localizations are available in the bundle, this method returns one of the bundle localizations.
Why preferredLocalizations(from: localizations) does't return the same result as preferred​Localizations(from: localizations, ​for​Preferences:​ nil)? As noted above reading their docs doesn't really help. Sure they look designed this way but actually not:
// Locale.preferredLanguages: ["es-CN", "ja-CN", "zh-Hans-CN", "en-CN", "he-IL"].
let localizations = ["de", "en", "es", "fr", "it"]
print(Bundle.preferredLocalizations(from: localizations)) // ["en"]
print(Bundle.preferredLocalizations(from: localizations, forPreferences: nil)) // ["es"]
The reference documentation isn't that great; Technical Note 2418 explains it better.
Specifically, it says
Note that Bundle.preferredLocalizations(from:) will restrict the results to localizations supported by Bundle.mainBundle(), or the return value of Bundle.mainBundle().localizations(). If you would like to match against a different set of language identifiers, use Bundle.preferredLocalizations(from:forPreferences:) which does not rely on mainBundle’s localizations and instead solely relies on the two arguments passed in.
So,
If you use Bundle.preferredLocalizations(from:), the localizations list you give it is first filtered against the localizations defined for the main bundle (Bundle.main.localizations).
If you use Bundle.preferredLocalizations(from:forPreferences:), it works with the list you give it.
In the example you gave, Bundle.preferredLocalizations(from: localizations) returns ["en"]. This is likely because your app does not have any version of Spanish ("es") defined. If you add a Spanish localization to your project, it should return ["es"], since "es-CN" is listed before "en-CN".

Sylius missing translations

I'm currently busy on a new installation of Sylius but the Dutch translation is just 80% complete which results in a few missing strings like 'sylius.report.no_data' and such.
Now I've discovered the Crowdin and already contributed a bit but now I want to update my own installation first.
I've seen that Sylius uses jms/translation-bundle in the composer so I tried to add the WebUI using:
JMSTranslationBundle_ui:
resource: #JMSTranslationBundle/Controller/
type: annotation
prefix: /_trans
But this gives me following error:
Cannot load resource "#JMSTranslationBundle/Controller/". Make sure the "JMSTranslationBundle" bundle is correctly registered and loaded in the application kernel class. If the bundle is registered, make sure the bundle path "#JMSTranslationBundle/Controller/" is not empty.
Registering the bundle again in AppKernel.php is also not working...
Or should I just edit the yml files?
Just make the translations in the yml and clear cache, that would be enough.
So in your app/Resources/translation/messages.nl.yml or src/Acme/Resources/translation/message.nl.yml translate the needed strings eg.:
sylius:
add_to_cart: Your translation
I think that indeed editing them in my messages.nl.yml in the Resources folder will be the way to go if I need them fast.

How to pass a relative path (relative to main bundle) to iOS app as "argument passed on launch" in Schemes?

I try to pass a relative path, e.g., subFolder/file, as an argument to my app. The subFolder is placed in Copy Bundle Resources and I made sure that the folder is under myApp.app/Contents/.
I added an argument in my Scheme > Run > Arguments > Arguments Passed On Launch, as
subFolder/file.
However, when running the app, the debugger output says:
/var/mobile/applications/feae1664-6f16-4b96-8b8d-05b4531fe6da/myApp.app/myappbin: cannot open subFolder/file: No such file or directory
myappbin is the executable of the app bundle.
How should I specify a relative path like this?
EDIT
the thing is my app really needs to use that path as a runtime variable, for testing purposes. I do know how to refer to the bundled folders/files in mainBundle in ObjC.
You don't need to pass in the path as an argument. You just need to use the NSBundle methods to return the path.
Have a look at
[[NSBundle mainBundle] pathForResouce: ofType:];
And also the documentation for NSBundle,
https://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/NSBundle_Class/Reference/Reference.html

Localizing strings from the Settings.bundle using InAppSettingsKit

I am attempting to use InAppSettingsKit to manage my settings. This uses the Settings.bundle with a .plist file and the .strings files for each of the languages being translated.
I can confirm that the translation of my strings is working properly outside of my application, using the Setting application. But when I am in my application, the translation is not occurring.
I think it comes down to code like this, from the InAppSettingsKit class IASKSettingsReader, with a couple logging statements that I thought my be helpful:
- (NSString*)titleForStringId:(NSString*)stringId {
NSLog(#"%#",[_bundle localizedStringForKey:stringId value:stringId table:self.localizationTable]);
NSLog(#"%#",[_bundle localizedInfoDictionary]);
return [_bundle localizedStringForKey:stringId value:stringId table:self.localizationTable];
}
If I understand correctly, this should be using a table with the name self.localizationTable as the source of the translation. This value is simply "Root". It's not a path to the Root.strings file in the selected language, so I am guessing that the method localizedStringForKey:value:table: must be using some global system reference that points to the correct path.
I have confirmed that the strings file name is "Root.strings" all around, with a capital R, including in the Root.plist file.
[_bundle localizedInfoDictionary] returns (null); It is doing this for two language settings of English and French.
I'm not sure how to debug this. Thanks for any help you can give.
I'm using InAppSettingsKit with localized text with no problems. Two things I can think of that you could check: are your Root.strings files located in the correct subdirectories of Settings.bundle (en.lproj and fr.lproj for English and French?
Is there a "Strings Filename" entry in your Root.plist? It should simply contain a string with value "Root"
It has been quite some time since I resolved this, and at the time, I didn't fully understand it. But, in the interest of closing out this question, I'll point to the following documentation for future reference:
NSBundle Class Reference
which refers to the following:
Resource Programming Guide
In the second document, refer to the section "String REsources -> Loading String Resources Into Your Code"
The solution contains a properly configured Root.strings file, which shows up in the file list like this:

Help-balloons in Grails

I am using the help-balloons plugin
I would like to use it parametrized. I mean, the messages should come from a properties file. In the documentation appears the following:
<g:helpBalloon code="user.name" suffix=".help"/>
In this last example, the code attribute is used to look up the balloon's title within the message bundle and then the suffix is added to the code (producing user.name.help in our example) as the key to be used for looking up the content of the balloon.
My question is:
Where should be located this properties file (message bundle)?
Can I have one message bundle per controller?
Luis
if you looked at the source code for the help balloon tag, it literally uses the grails interationalization code to render the message if given a key. http://fisheye.codehaus.org/browse/grails-plugins/grails-help-balloons/trunk/grails-app/taglib/HelpBalloonTagLib.groovy?r=45243
check out this page http://www.grails.org/doc/1.0.x/guide/10.%20Internationalization.html it tells you where and how to name the file for message bundles.
as for a message bundle per controller, it doesnt seem like you can (at least not apparent from the documentation). but you can hack it by prefixing the message key by the controller name, and thus use the same message bundle file (message.properties_ but still be able to namespace each message.
Where are the Resource bundles:
There is a directory under grails-app called i18n where all the generated resource files are placed, start looking there and see how they are used in the app.
You may be able to just place multiple message files for your controllers in there for organization, just be careful of reusing keys as I'm not sure how that will be handled off hand.
How to access them:
Maybe this will help I hope:
http://www.nabble.com/Organizing-message-bundles-tt16169280.html#a16169280

Resources