Update translation for iOS internationalization - ios

To extract the string "marked" with NSLocalizedString in my iOS app, I use the following terminal command:
find . -name \*.m | xargs genstrings -o ro.lproj
This will basically extract all the marked strings in all the .m files and put them in the Localizable.strings file inside my project romanian's folder.
My problem is that I don't manage to get it to update the strings file. If I change/add some strings it will overwrite my old strings file.
I've look to the arguments the genstrings accepts, looked on the man page, and I don't seem to find the option to update the file instead of overwriting it.
How do I update the translation file ?

genstrings doesn't do updates. If it did you'd still be stuck with the problem of what to do to manage updates to the localized files (which require the attention of a translator to make changes to their translations, particularly for edited source strings, so they need to see the differences in the source text).
There are tools designed for this. For example the Localization Suite desktop app (http://www.loc-suite.org/) and the Gengo String web app (http://gengo.com/string/about/)

You could:
Use the -a flag which will append the strings to the existing file
Or, choose a different location for the output and use a file merge tool to collect the results.
Or use Version control to merge back previous versions.

Genstrings does not update, its a one way one time deal.
I use the Linguan tool see http://www.peerassembly.com/linguan.html
its not expensive and works great with xcode 5. You just point
it to your xcode project and it will extract all strings from your
code (simular to genstrings but without the commandline stuff) and
build the .strings files. When you update your code, you ask Linguan
to scan again and it will nicely tell you what the new strings are.
You can also do other stuff like stats, export, import...

Related

How to check which localization strings are really used in iOS Application?

We have a legacy application for iOS translated to two languages. After checking the localization files we realized, that there are a lot of strings that are not really used in the application, though, we cannot be sure which ones.
Is there some way (maybe some utility) that can check objective-c project and localization files and check which strings re really in use and which are not so we can delete the from the localization files?
Thanks
You could use genstrings to generate a new strings file from your project and then use one of the string-files comparison/merge tools to find the differences.

Is there a good way to externalize user-facing strings out of objective C files?

I have read a few tutorials about iOS's tools and processes for i18n/l10n, and am unpleasantly surprised with what I'm seeing. It seems there isn't a solid way of externalizing user-facing strings out of objective C files. Am I missing something?
Description of the problem:
There is a tendency to place English strings directly into the .m file, and the Apple documentation seems to encourage this. While this is also possible in Android, as least in Android there is a clear distinction between externalized and non-externalized strings. With iOS, on the other hand, code that calls for a string tends to look like this:
NSLocalizedString(#"There was an error loading the image.", nil)
In this case, "There was an error loading the image." is the key for this string resource. Therefore if I want to make another reference to the same string in some other place we have to again write code like this:
NSLocalizedString(#"There was an error loading the image.", nil)
But now I have to make sure that I spelled these two strings the same and there is no compile time check to help me confirm that. I could write a helper function called createErrorString, but that's not fun. And I could replace "There was an error loading the image." with a more sensible key like "ERROR_IMAGE_LOAD", but that does not seem to be a common practice, and Apple seems to discourage this sensible behavior. Here is what their documentation states:
"A common convention when developing applications is to use a key name
that equals the value in the language used to develop the
application."
It looks like Apple is recommending that you put the full English string in your source code. So I'll have to try to convince my colleagues to go against Apple's guidance.
Now that I've got all of these user-facing English strings (or keys) in the source code, Apple includes a tool called genstrings that parses the .m files, and spits out a Localizable.strings file that I can then send out for translation. This might work if you are only going to get your app localized one time, but in our company localization is an ongoing iterative process. Look at what the Apple documentation recommends:
"For subsequent runs, it is a good idea to save a copy of your current
strings files before running genstrings. You can then diff the new and
old versions to determine which strings were added to (or changed in)
your project. You can then use this information to update any already
localized versions of your strings files, rather than replacing those
files and localizing them again."
That doesn't seem very good. In Android and Windows8, you internationalize your source tree one time, and from that moment on your externalized strings are owned in the xml files where they belong; in iOS they are owned in source code (sort of) and then tabulated into some intermediate file (or is it?) by some crazy tool. Is the Localizable.strings file an intermediate file or should it be committed into git - we are still debating this at my company.
(And from what I can tell, this is only the beginning. In xib-land, which is where 90% of your user-facing strings live, there also seems to be an inefficient mechanism for l10n. Wil Shipley's article describes this at length.)
Does anyone have any suggestions on a good way to externalize strings in iOS? My main question concerns objective-C strings, but answers pertaining to xib files would also be much appreciated. Thanks!
I found the recommendation to name the key like the english string strange, too.
I name the keys, value e.g "Menu1SettingsTitle" = "Settings".
I dont need genstrings tool, just externalize manually.
And no, the string files is not an intermediate step, they should be in git.
However with that approach i noticed three drawbacks:
1) I detected duplicate names, but that can be moved to a common section for strings like "cancel, delete"
2) If you forget to put a string into that language file, it cannot be found and then the key is displayed, which looks very strange, of course. Otherwise with apples reccomendation, If the key is the english word, it looks "only english" but not worse.
3) The translation process is easier if english is always left, instead of "Menu1SettingsTitle". to solve that i put a comment above, but dont know if the translation service would be happy with that.
After a couple of hours searching I decided to use two different approaches: one for the storyboards and one for the text inside the .m files. The result are two files Localizable.strings for the Objective C text and the internationalized storyboard.
The update_storyboard_strings.sh can automatically extract translatable strings from storyboards and update strings files. The source code (by mikezang) can be found at:
http://forums.macrumors.com/showpost.php?p=16060008&postcount=4
For the objective C NLS I use another script around the xcode-tools by Frédéric Sagnes:
https://github.com/ndfred/xcode-tools
I had to call it for each language in order to get the desired results:
python scripts/xcode-tools/update_strings.py --import=MyProject/Base.lproj/Localizable.strings MyProject/Base.lproj/Localizable.strings
python scripts/xcode-tools/update_strings.py --import=MyProject/de.lproj/Localizable.strings MyProject/de.lproj/Localizable.strings
Now put both in one script and add it to your Xcode project as a last build phase.

How to customize carrier name in iOS 6 simulator?

How can I customize the default Carrier name to something else, e.g. AT&T?
There is a way to change the carrier name without using any objective c code or image editor app: https://github.com/toursprung/iOS-Screenshot-Automator/blob/master/changeCarrierName
This solution works great with all versions of iOS (including iOS 6 and 7)
The key point is
sudo plutil -convert xml1 SpringBoard.strings
We don't necessarily have to convert it back to binary code since Mac OS accepts both formats.
After you converted your file to XML, you can easily edit the file with vim or any other editor of your choice.
UPDATE:
I uploaded a detailed description on how to update the carrier name: https://github.com/toursprung/iOS-Screenshot-Automator/blob/master/changeCarrierName
Copy and paste into terminal, replacing DERP with your carrier string:
find -E /Applications/Xcode.app -iregex '.*/en(glish)?.lproj/SpringBoard.strings' -exec sudo /usr/libexec/PlistBuddy -c 'Set :SIMULATOR_CARRIER_STRING DERP' {} \;
Finds relevant files and modifies them in place. No copying files, converting binary to xml, or editing strings in other applications.
You can use this hack, just compile the XCDFakeCarrier.m file in your project and change the FakeCarrier constant to what you want.
There is a perfect app for this called Status Magic that I was beta testing. Just trying to find a link for it...
You import a screenshot and it removes all the carrier specific text from it and replaces it with a standard status bar and you can edit the time, carrier info, etc...
Means you don't have to use Photoshop or anything.
Will be back with a link...
LINK
https://itunes.apple.com/uk/app/status-magic/id547920381?mt=12
EDIT Just found out that the app is still in review fro the Mac App Store. Should be out soon. It really is perfect for what you are wanting to do though. And really easy to use.
I don't know if that's possible with iOS Simulator (If it's even possible you have to dig deep :) ). The best way is to take the screenshot (Command+S), and change the image in Adobe Photoshop, or some image processing program like that.

How does incremental localization work?

I'm trying to build my first localized application. I have all the strings in code translated using NSLocalizedString (for use with genstrings tool). Now I'm bumping into ibtool. How does incremental localization work? Regarding to the manual page, I should write something like this:
$ ibtool --previous-file path/to/prev.xib \
--incremental-file path/to/inc.xib --localize-incremental \
--write path/to/new.xib mod.xib
Where do I get the incremental file? To my understanding if I'm using the version control (git/svn), the "old" file is at few commits ago, the incremental file is the diff and path/to/new.xib is newly produced xib file. mod.nib is a mystery to me. Can anyone explain me how this works? Also - how do I start the localization of a xib if no previous versions are available (i.e. doing not incremental, but initial localization)?
I think their choice of terminology, particularly for --incremental-file, is causing confusion. The idea is that you have an old version of your xib in two languages (source and target) and that you have since changed it in your source-language and want to update the target-language version to match.
Let's take an example. You previously had home.xib in English (source language) and got someone to translate it to French (target language). You've since developed a new feature and you now have an updated version of home.xib in English in which you added a UILabel and a UITextField and moved things around. The command you showed can help you get an updated version of home.xib in French so that it has the new UILabel and UITextField and that things are moved around like in English. Note that any textual content that you set in your new UILabel and UITextField will be added in English and will then need to be translated in the French xib (but you can automate this by adding --import-strings-file and providing the translations in one more file).
So if we map the command you showed to this example:
--previous-file path/to/prev.xib specifies the old English xib
--incremental-file path/to/inc.xib specifies the old French xib
--write path/to/new.xib specifies the new French xib that will be created
mod.xib specifies the new English xib
For your other question regarding how you start the process, really it depends how you will localize your xibs. You'll obviously create the new language versions of the xibs (in XCode, you just add a language to the language list of the xib and the localized xibs are created automatically). And then if you localize them in Interface Builder yourself, then you'll simply make the relevant changes (translation of text and any necessary resizing) in the localized xibs. Or you could extract the text in the xibs into .strings files, get them translated, and inject them into the relevant language version of these xibs. For this, again you will use ibtool but with --generate-strings-file for the extract phase and with --import-strings-file for the inject phase.
I wrote a script for git projects which automates the steps necessary (as described in the answer above) to migrate a change to a different language.
Usage:
migrate_changes.sh <target_language> <xib file without ending>
Example:
After you've committed your changes to the english xib file, run the script at the root of your resource folder.
migrate_changes.sh de MyViewController
Source:
#!/bin/sh
LANG_FROM='en'
LANG_TO=$1
XIB_FILE=$2
FROM_FILE=${LANG_FROM}.lproj/${XIB_FILE}.xib
PREV_FILE=${LANG_FROM}.lproj/${XIB_FILE}_old.xib
TO_FILE=${LANG_TO}.lproj/${XIB_FILE}.xib
# checkout old version of xib file
git show `git log -2 --format="%H" $FROM_FILE | tail -n 1`:./$FROM_FILE > $PREV_FILE
# merge changes
ibtool --previous-file $PREV_FILE --incremental-file $TO_FILE --localize-incremental --write $TO_FILE $FROM_FILE
# remove previous version
rm $PREV_FILE

How to use Git properly with Xcode?

I have been an iphone developer for a while, and I have recently been including git in my workflow. I have used git settings found on http://shanesbrain.net/2008/7/9/using-xcode-with-git for my workflow so far.
Those settings tell git to exclude *.pbxproj from merges? Is there a real reason for doing this? For example, when I add a file to the project and push to origin, my fellow developers will not have that file added to their xcode project when they pull. Then if one of them builds a release this file may not be included. Shouldn't I just let git handle the merges for the project file? Why or why not this file should be in merges and how to properly handle the situation when files are added to the project?
I have worked on iPhone applications full time since the SDK launch, most of that time spent working on teams with multiple developers.
The truth is that it's way more harmful to disallow merging of that .pbxproj file than it is helpful. As you say, when you add a file unless other people get that file, they have to also add it to their project - in an application of any size, that sucks and it also takes away a huge benefit of source code control in that you cannot really revert to a complete earlier project state just through git.
The .pbxproj file is simply a property list (similar to XML). From experience, just about the ONLY merge conflict you were ever get is if two people have added files at the same time. The solution in 99% of the merge conflict cases is to keep both sides of the merge, which for git at least simply involves removing any >>>>, <<<<, and ==== lines. In fact this is so common that I have created a simple shell script to fix a .pbxproj file in a merge state from git, I run this from within the project directory (at the Classes level):
#!/bin/sh
projectfile=`find -d . -name 'project.pbxproj'`
projectdir=`echo *.xcodeproj`
projectfile="${projectdir}/project.pbxproj"
tempfile="${projectdir}/project.pbxproj.out"
savefile="${projectdir}/project.pbxproj.mergesave"
cat $projectfile | grep -v "<<<<<<< HEAD" | grep -v "=======" | grep -v "^>>>>>>> " > $tempfile
cp $projectfile $savefile
mv $tempfile $projectfile
Worst case if it fails (you ask XCode to load the project and it fails to load), you simply delete the .pbxproj file, check out the master from git, and re-add your files. But I've never had that happen in many months of use with this script, again working full time on iPhone applications with several other developers.
Another option (pointed out in comments below) that you can try using in place of the script, is to add this line to a .gitattributes file:
*.pbxproj text -crlf -diff -merge=union
Then git will always take both sides of a merge for the .pbxproject files, having the same effect as the script I provided only without any extra work.
Lastly, here is my complete .gitignore file, showing what I do have it set to ignore as there are a few things you don't want - in my case really just emacs remnants and the whole build directory:
# xcode noise
build/*
*.pbxuser
*.mode1v3
*~
# old skool
.svn
# osx noise
.DS_Store
profile
This works for me in Xcode 4.6 and Git 1.7.5.
Add and commit .gitattributes file with this:
*.pbxproj binary merge=union
I've tested this with another team member and works great.
Taken from:
http://robots.thoughtbot.com/post/33796217972/xcode-and-git-bridging-the-gap
Frankly, the existing answers are misleading.
If you never delete or rename files, then using the merge=union strategy, which just combines the differences in different commits directly, is a good idea.
However, in the real world, we do need to delete or rename files sometimes. Merging the differences without any modification would make a lot of problems under these situations, and these problems usually lead to the "Workspace Integrity - Couldn't load project" issue, which makes you even not able to run the project.
The best solution I got so far:
1) Design the project well and add all the needed files at the beginning, so you would seldom need to change the project.pbxproj.
2) Make your features tiny. Don't do too many things in a branch.
3) For any reason, if you need to modify the file structure and get conflicts in project.pbxproj, use your favorite text editor to solve them manually. As you make your tasks tiny, the conflicts might be easy to solve.
The short answer is that even if you don't include that line in .gitattributes, you may not be able to easily merge two modified versions of a .pbxproj. It's better for git to treat it as a binary.
See here for details: Git and pbxproj
Update: Even though the git book still agrees with this answer, I no longer do. I version control my .pbxproj just like any other non-binary source file.
I did create a Python script that can handle merge conflicts in XCode Project files.
If you want to try it, you can check it out here: https://github.com/simonwagner/mergepbx
You will have to install it as a merge driver, so it gets called automatically when you have a merge conflict in your project file (the README.md will tell you how to do that).
It should work much better than using merge=union as mergepbx understands the semantics of your project file and therefore will resolve the conflict correctly.
However the project is still alpha, don't expect it to understand every project file that is out there.

Resources