Localizable.strings failing to load every other build? - iphone

I have been having a strange problem with the localization system built into Cocoa. I used genstrings to create a localizable.strings file for my project, and the file loads and replaces the strings as expected in my app.
However, it appears to only work every other build. I will build the code with XCode, test it on my device, and it will display the correct strings no problem. The next build, however, will fail to load the strings file (At least, that's what I'm assuming.) This is not random, but predictably every other build. I am doing nothing fancy with the Localizable.strings file.
I have no idea where to even start in diagnosing this issue, and I was wondering if anyone had experience with doing localizations on Cocoa.
I am using NSLocalizedString throughout my code base like so:
NSLocalizedString(#"ReallyNewGame", #"Are you sure you want to start a new game?")
The corresponding entry in my Localizable.strings file:
/* Are you sure you want to start a new game? */
"ReallyNewGame" = "Do you really want to start a new game?";
Here are the relavent parts of my Info.plist:
<key>CFBundleDevelopmentRegion</key>
<string>English</string>
Here is a screenshot of what happens every other build of the app:
Correct:
Incorrect:
I am baffled as to why this happens. I do not do anything manually with the Localizable.strings file and I have cleaned my project several times on XCode. Any pointers in the right direction would be greatly appreciated. If you need any more information, I will attempt to provide it.
Thanks!

I cannot give you a straight out answer but can suggest some ways to proceed. I don't actually have a multi language app now, so most of this is what I've gleaned by reading up (I may have one soon so your issue is of interest to me):
1) Apple has deprecated the user of English.lproj in favor of en.lproj. In any case, its important that if you use "English" as your CFBundleDevelopmentRegion value, that the folder be named "English" and not "en".
2) Your strings file should be UTF16, and noted as such in the file inspector (that right most pane in Xcode)
3) There is a nice previous question that has some graphical pointers on insuring that your localization files are correctly entered in Xcode (so Xcode knows about then, and knows it must process them).
4) Its possible your file has gotten corrupt, the Resource Guide says you can run "plutil -lint Localizable.strings" on it to verify correctness
5) As a side note, a number of people have pointed to Mac App Store App as a nice utility to merge (not overwrite) strings files (as you make additions).
6) If things still look good, then add the following to your AppDelegate "didFinishLaunchingWithOptions" (at the top) when the app first launches:
NSBundle *bundle = [NSBundle bundleForClass:[self class]];
NSLog(#"Strings file: %#", [bundel pathForResource:#"Localizable" ofType:#".strings"]);
NSLog(#"Localizations: %#" [bundle localizations]);
NSLog(#"Local Dict: %#", [bundle localizedInfoDictionary]];
NSLog(#"localizedStringForKey: %#", [bundle localizedStringForKey:#"ReallyNewGame"value:#"WTF???" table:nil];
NSLog(#"Localized String: %#", NSLocalizedString(#"ReallyNewGame", #"Are you sure you want to start a new game?"));
exit(0); // just testing the above for now
Run the app several times. The output should be the same. If its not add a comment to this answer and we can drill down further. If it is the same, well, then something is causing corruption further on in your app.

In case it helps anyone else:
I encountered exactly the same problem: every other build the localizations would not work. I found when I inspected the bundle contents that the Localizable.strings in the en.lproj were corrupt - the file was only 76 bytes long when it should have been 4k. The next build the corruption was gone, then back again, then gone...
It turned out that I had copied over an extra Localizable.strings folder into my project when I had copied a folder from another project. When I deleted the extra Localizable.strings folder everything magically worked. Whew!

This issue occurs when:
you have at least two files */<locale>.lproj/<table_name>.strings, eg two files */en.lproj/Localizable.strings
you support more than one language with your *.strings files
You probably wouldn't create two+ .strings files with the same name. This problem usually occurs when you use external library that has Localizable.strings file(s) in its resources. You probably have Localizable.strings file(s) in your resources - and that's the conflict that XCode fails to resolve.
TL; DR; General tip: if you create a library to be used as a third party code by other developers, instead of creating Localizable.strings file and using NSLocalizedString() in it, create custom-named localizable strings table (e.g. MyLibName.strings) and use NSLocalizedStringFromTable.
Problem example and detailed description:
I've created a "problem-demo" repository: https://github.com/kajot/LocalizedStringsMergingFailure
Specifically with a test that fails every other run: https://github.com/kajot/LocalizedStringsMergingFailure/blob/master/LocalizedStringsMergingFailureTests/LocalizedStringsMergingFailureTests.m
│  ├── KJAppDelegate.h
│   ├── KJAppDelegate.m
│   ├── Localizations1
│   │   ├── de.lproj
│   │   │   └── Localizable.strings
│   │   └── en.lproj
│   │   └── Localizable.strings
│   ├── Localizations2
│   │   ├── de.lproj
│   │   │   └── Localizable.strings
│   │   └── en.lproj
│   │   └── Localizable.strings
Every OTHER build, XCode will create a corrupted Localizable.strings file in the bundle. Solution: do NOT create/add more than one LocalizedString table with the same name to the same target.
LocalizedString table is a set of */<locale>.lproj/<tableName>.strings files. In the example above, there are two tables, each named Localizable (default name for a table).
If a table is called Localizable, you get localized strings from the table by using
`NSLocalizedString(key, optionalComment)`.
Solution:
You can either merge those two tables (concatenate corresponding files with translations from the same languages) or change the name of one of the tables.
Example of the second approach (changed name of one of the tables):
│  ├── KJAppDelegate.h
│   ├── KJAppDelegate.m
│   ├── Localizations1
│   │   ├── de.lproj
│   │   │   └── Localizable.strings
│   │   └── en.lproj
│   │   └── Localizable.strings
│   ├── Localizations2
│   │   ├── de.lproj
│   │   │   └── NewTableName.strings
│   │   └── en.lproj
│   │   └── NewTableName.strings
Now you can get a translation from the new table (NewTableName) by using NSLocalizedStringFromTable(key, #"NewTableName", optionalComment)
and from the "original" table (Localizable) using NSLocalizedString(key, optionalComment).

other thread may helps... Multiple Localizable.strings files in one iOS app
again, maybe you can cleanup and then build the app from xcode...

I had an similar issue regarding my app-icon. I had several targets in my app and all with different app-icons. For some reason one of the app-icons was corrupt and thus all of my targets switched through the app-icons of the other targets. which is pretty odd, because one target should not know of the other targets app-icon, if it is not used for both targets and marked for both targets in xcode.
I solved this issue through deleting the corrupt png and adding it anew to the project.
If you do this with your i18n file it might help, too.
But be sure, to remove not only the reference from xcode but the complete file. Best would be to open the file in some external editor like textwrangler and copy the text to a new file and then use this instead.
Good Luck.

the behaviour, what you've experienced, is totally normal. why?
if you check these lines, the relevant part of original macro definition of the NSLocalizedString:
#define NSLocalizedString(key, comment) \
[[NSBundle mainBundle] localizedStringForKey:(key) value:#"" table:nil]
you could see the comment parameter is deceptive parameter, it is never used. (it is just for the developer who reads the code)
futhermore, if you check the NSBundle class Reference, you could read the following interesting thing about the return value of the -localizedStringForKey:value:table: method:
Return Value
If value is nil or an empty string, and a localized string is not found in the table, returns key.
this is why you get back your key because probably there is no value for this key in your Localizable.string files.
sad news, but it is normal.
the solution would be the following:
NSLocalizedString(#"Are you sure you want to start a new game?", #"");
and in your Localizable.string file:
#"Are you sure you want to start a new game?" = "Do you really want to start a new game?";

I just found something that was killing me!
I had the same issue: Depending on the STRINGS file, sometimes it Works, some times doesn't/
What I found, is that if some line in your .strings file finishes with double semicolon
;;
all the lines below ware ignored!
For example:
The upper image works great: As you can see, the NSLocalizationString commands replaces correctly the strings at the template
But the other image shows that, before the double semicolon, it will ignore everything. It is a complete mess!
No resources compilation error, nothing. Just a nightmare!
Hope it helps someone to don't get insomnia!

Related

typo3 includes files from wrong path

Setup
Typo3 10.4.16 (classic installation: NO composer!)
Structure:
/html/typo3
|
+--- typo3_src
+--- typo3
+--- typo3conf
+--- typo3temp
+--- index.php
Configuration works fine on localhost
Issue
I copied everything onto the remote machine, adjusted database, domain config, sites, ...
Now, if I open the domain in my browser i get the following error:
Warning: require(/.../html/typo3/sysext/core/Configuration/DefaultConfiguration.php): failed to open stream: No such file or directory in /html/typo3/typo3_src-10.4.16/typo3/sysext/core/Classes/Configuration/ConfigurationManager.php on line 92
Of couse, the file cannot be found, because the include path should be /.../html/typo3/typo3/sysext/... (see file structure above).
I can reach the backend and the install tool. Both works fine. No issues there as far as I know.
Why is typo3 including the wrong path here?
What did I miss?
Thank you very much for your help!
The TYPO3 Installation and Upgrade Guide provides details about how the directory structure should look like.
If you don't use a Composer-based installation (Composer is by the way the recommended installation method today), make sure that you create symbolic links to specific target directories. These are the three entries with the -> sign in the following example:
www
├── public
│   ├── fileadmin
│   ├── index.php -> typo3_src/index.php
│   ├── typo3 -> typo3_src/typo3
│   ├── typo3conf
│   ├── typo3_src -> ../typo3_src-10.4.16
│   └── typo3temp
└── typo3_src-10.4.16
...
The public/ directory is typically the document root (e.g. DocumentRoot in an Apache web server configuration). Inside public/ you create the following symbolic links:
typo3_src points to the directory typo3_src-10.4.x in the parent level.
index.php points to the same file name inside typo3_src.
typo3 points to the same file/folder name inside typo3_src.
This officially recommended structure lets you organize all files of the TYPO3 source package separated from other files of your installation. One benefit of this approach is that you can use the Core Update function in TYPO3's Admin Tools to easily update your TYPO3 installation.

How to hide source code of my flutter plugin so that nobody can see the same while using?

We have developed a flutter plugin and we don't want to share the source code with the customer. If they are adding our plugin to their app there are possibilities to get the code from pub cache . So we want to deliver our product without revealing the source code.I just wanted to know if there is a way to implement this.It would be a great help if somebody could help me to do this.
Thank you.
If you wanna use a local package/plugin you needn't share it or publish it on pub.dev. You can create a new folder which would take plugins files, within your project.
├── android
├── ios
├── lib
├── packages
│ └── custom_button
└── test
you can use it directly but if you have created this file like a flutter package style you should implement it in your project's pubspec.yaml.
Like this:
dependencies:
custom_button:
path: packages/custom_button

Share .dart-files between test/ and example/

I'm currently writing a dart package. I have a file data_model.dart which I included in test/test.dart to perform tests based on that file. Now I also want to include it into example/lib/main.dart to use it for my example code. Where should I put my file to not violate any package layout conventions and can use it from both test/ and example/ at the same time?

Bazel build rules for BLAS & LAPACK

I have not found any project/repo providing Bazel build rules for Blas or Lapack.
This is quite unfortunate as these tools are often the primary libraries one must use for project oriented towards numerical computations.
Does such thing already exist somewhere?
No BUILD files yet...
However, a quick fix if you want to use already installed Blas or Lapack libs is to add a line like this one (according to the installed libs on your computer):
build --linkopt="-llapacke -llapack -lblas"
in your bazel.rc file (in the tools/ directory):
YourBazelProject/.
├── ...
├── WORKSPACE
└── tools
└── bazel.rc
I am not aware of BUILD files for these libraries either.
If you do create them, sharing them publicly or trying to submit them upstream (in the library code) will be a great way for others to benefit from your efforts.

How to bundle modules for an offline server with cpanm

I would like to do cpanm SomeModule to install SomeModule together with about 10 dependencies, but the target server has no internet access. I do have a very similar development machine (same Perl environment, same Perl version) where cpanm is able to download its source modules.
After studying the man page of cpanm, I have the feeling that I can create a tarball on the development machine, transfer that to the server, and use it to install the modules in one go.
Unfortunately, I do not seem to find which exact combination it is. Especially, as on the dev machine the modules are already installed, I need to force it to still add all the dependencies to the tarball (excluding core modules of course).
Can someone give the commands for the dev machine and the target machine?
EDIT: this is specifically about cpanm. Of course, if you can say with authority that it is definitely not possible with cpanm, that would be a valid answer as well...
EDIT: The comments and answers so far suggest using pinto or minicpan to create a bundle of CPAN module sources. This works well (especially pinto is quite trivial to use for this). I used pinto now to solve my current problem, but still, Pinto itself has a lot of prerequisite modules (>100 compared to Perl-Core). My hope with this question was that cpanm, which is a standalone, installation-less script, can do it itself (it has extensive options that kind of sound like they could go into that direction). That would be nice for bootstrapping Perl installations without large overheads.
You can download the tars from CPAN or metacpan for all your dependencies manually, then copy them and install one by one in the right order. That's a bit of work for ten modules, but it's not too bad. You can write a script.
But you can also use minicpan to create a local small CPAN that only contains what you need. Those are great to have a local copy of some or all of CPAN, e.g. on a USB drive when you need to install a module while hacking code on a flight. It's essentially a directory full of more directories and tars. You can just pick the things you need, zip it up, move it to your production server, unpack it there and tell cpanm to install from that local CPAN mirror.
You can use Carton to bundle the dependencies locally (on your machine with internet access) and then use either Carton itself to install the bundled distributions, or use cpanm itself and specify the bundle location.
You'll need carton 1.0.32 (to generate the package index) and cpanm 1.7016 (for the --from option) for this to work.
In the root of your distribution, you can do
$ carton install # will install the dependencies in `local`
$ carton bundle # will cache the dependencies in `vendor`
$ tree vendor/
vendor/
└── cache
├── authors
│   └── id
│   └── F
│   └── FO
│   └── FOOBAR
│   ├── Some-Dist-1.337.tar.gz
│   └── Another-Dist-0.001001.tar.gz
└── modules
└── 02packages.details.txt.gz
Later, after transferring this to your other airgapped machine, you can either use carton:
$ carton install --cached
$ carton exec scripts/your-script.pl
or install with cpanm directly
# To emulate carton:
$ cpanm -L local --from "$PWD/vendor/cache" --installdeps --notest --quiet .
# Or to install globally:
$ cpanm --from "$PWD/vendor/cache" .