Weird hidden characters in exported XLIFF file - ios

Whenever I try to 'Export For Internationalization', the exported file contains a weird hidden character, making it unparsable for XLIFF editors. The problem seemed to be in the original .string files, somehow the weird character was inserted in those files. I have since deleted the weird character but whenever I export it still sneaks into the xliff file. I tried cleaning and rebuilding the project, restarting Xcode... none of that seems to work.
Is Xcode somehow using a cached version of the 'bad' .strings file containing the bad character?
Using Xcode
If I try Editor > Export For Localization
I get:
/usr/bin/xmllint exited with status 1
Using Terminal
When I run it from Terminal like so:
xcodebuild -exportLocalizations -localizationPath
/Users/Kymer/Downloads/Wolf -project Wolf.xcodeproj -exportLanguage fr
I get the following errors:
parser error : attributes construct error
parser error : Couldn't find end of Start Tag trans-unit
parser error : PCDATA invalid Char value 19
parser error : PCDATA invalid Char value 19
parser error : Opening and ending tag mismatch
parser error : invalid character in attribute value
parser error : attributes construct error
parser error : Couldn't find end of Start Tag
parser error : PCDATA invalid Char value
/Uxcodebuild: error: /usr/bin/xmllint exited with status 1
In both cases the exported xliff file contains the weird hidden character upon inspection with Sublime Text:
If I manually remove the bad characters the file is perfectly readable by xliff-editors but that's not a good long-term solution of course.

I found the problem: when exporting to an XLIFF file Xcode doesn't look at your .string files, it is all generated from the project itself (i.e. it looks at all NSLocalizedString calls and your storyboards). Which makes sense. I found the weird hidden character in one of my code files. Removing it from the source file fixed the export issue.
Easiest XLIFF workflow
I'll also mention this for future reference: the easiest way to add a new language to your project is to first use the command line:
cd to the your project and run:
xcodebuild -exportLocalizations -localizationPath <path> -project <projectname>.xcodeproj -exportLanguage <language code>
That creates a new XLIFF file and will correctly set the target language in the file (source language will be your base language). A translator can now easily add all necessary translations. Afterwards you can import the translated XLIFF file back into Xcode (select target and Editor > Import localizations). Xcode will then generate all necessary .string files.
Updating existing language: If you add new UI elements and want to update an existing localization language, you can simply export an existing localization (select target and Editor > Export for localization). That XLIFF file will contain all previous translations together with the new strings. A translator simply has to fill in the 'blank' lines. There's no need to touch the .string files yourself, because managing that manually is a pain (especially with the crazy Storyboard ID's).

Related

Xcode 10, could not decode input file using specific encoding

I am working on an iOS app. It is working fine in Xcode 9.4.1, but when I build it in Xcode 10 it gives me following error:
I tried the solution given in the following post by changing the encoding, but it didn't work. I tried it by both Reinterpret and Convert
still the same error:
It's working fine on Xcode 9.4.1
Find your Localizable.strings in a Terminal and execute:
$ iconv -f UTF-16LE -t UTF-8 Localizable.strings > LocalizableNew.strings
Then check LocalizableNews.string
and if there is no errors just replace files
$ mv LocalizableNew.strings Localizable.strings
I have similar error once i open my project in Xcode 10.4 and open it agian in Xcode 10.1.
I solved it by selecting my all Localizable.strings file and change there text encoding to UTF-16(In my case error was related to UTF-16 you can change it to UTF-8)
So changing the text encoding to UTF-16 or UTF-8 will works.
It sounds like the file is corrupted, probably with parts of it encoded in UTF-8 and parts of it encoded in 8859-5. From its name, I would suspect this is a Cyrillic localization (perhaps Russian), and the file was probably edited using an editor that didn't correctly maintain encoding or use UTF-8 (the most common cause of that is editing on Windows).
You'll need to open the file, probably in an external editor that can handle random encodings like vim or Sublime Text, and fix any corruption. Exactly how to do that depends on the nature of the corruption.
You need to set correct Text Encoding in the File Inspector. The default is UTF-8.
If you want to fix the problem without UI, you need to look for the XCode project definition (generally YOURPROJECT.xcodeproj/project.pbxproj), then find the reference to the file causing an issue.
You should find something like this (from Adium, in this case)
D182F1B611DFF23700E33AE2 /* sk */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = sk; path = sk.lproj/schema.strings; sourceTree = "<group>"; };
fileEncoding = 10 is UTF-16; 4 is UTF-8, which is currently the default, so you can either set it to that value explicitely, or simply remove the fileEncoding bit altogether.
I got this error message when I forgot to put semicolons at the end of the line to separate the individual translations.

What tools support editing project.pbxproj files?

I want to edit project.pbxproj straight up using command line (for CI server script)
what tools can allow me to do this?
I used to use PlistBuddy to edit the output Info.plist; however, what i really want to do is to edit this user defined field, which is used in multiple places, and i really don't want to have to hunt that down in every plist location
project.pbxproj is an old-style ASCII property list file, too. So you can use /usr/libexec/PlistBuddy to edit it.
Print some User-Defined key's value like this,
# Get the key A83311AA20DA4A80004B8C0E in your project.pbxproj
# LZD_NOTIFICATION_SERVICE_BUNDLE_ID is defined by me,
# Replace key paths with your own.
/usr/libexec/PlistBuddy -c 'print :objects:A83311AA20DA4A80004B8C0E:buildSettings:LZD_NOTIFICATION_SERVICE_BUNDLE_ID' LAAppAdapter.xcodeproj/project.pbxproj
Set its value like this,
/usr/libexec/PlistBuddy -c 'set :objects:A83311AA20DA4A80004B8C0E:buildSettings:LZD_NOTIFICATION_SERVICE_BUNDLE_ID com.dawnsong.notification-service' LAAppAdapter.xcodeproj/project.pbxproj
UPDATE
PlistBuddy will automatically convert project.pbxproj into a xml-format plist file since macOS Catalina (or some earlier version). It's better to move the setting item into xcconfig file instead since xcconfig is much smaller and simpler than project.pbxproj and not easy to make mistakes when editing with perl script.
I know this has been answered for a while, but since the original question is about tools supporting the manipulation of .pbxproj files, and many other people may be looking for the same information, here's how I do it. It took me quite a while to figure this out because I was very unfamiliar with Xcode when I started attempting this, so I hope this saves others the hours of grief I had to put in.
You can use the plutil command to transform the .pbxproj file from the legacy .plist format into an XML or JSON format you will be able to manipulate more easily. I'm using JSON. To do so, just run:
plutil -convert json project.pbxproj
This will convert the format of project.pbxproj, but be aware that -contrary to common sense- the output won't be another file with a JSON extention such as project.json. What will happen is that project.pbxproj will be converted to JSON format, but retain it's cryptic .pbxproj extension. So even though the file's format has been changed, Xcode will still pick it up and use it in its new JSON format.
Then you can change project.pbxproj with ease using any JSON manipulation tool of your choosing. I'm using Groovy's JsonSlurper class in a Groovy script.
Note I also explored the XML option, but I found the project.pbxproj file in XML format to be cumbersome to parse. The elements are not properly nested to allow for traversing the tree with ease. It's plagued with:
<key>someKey</key>
<dict>
<!--More elements which provide configuration for the key above-->
</dict>
So it's positional in nature. You have to look for the key element corresponding to the setting you want to manipulate and then jump to the dict element just after it. Which means you have to mount the children of each XML element into an array, in order to index them.
Here are 3 open-source tools which implement .pbxproj file editing:
https://github.com/CocoaPods/Xcodeproj (Ruby based)
https://github.com/apache/cordova-node-xcode (NodeJS based)
https://github.com/kronenthaler/mod-pbxproj (Python based)
Personally, I made the best experience with the NodeJS based tool. So far it has covered all our needs reliably.
In the following is listed an example javascript file update-project.js which sets the developer team ID, app entitlements, adds a GoogleService-Info.plist file to the project and checks it as part of the build target. Take it as an inspiration and adapt the scripts and its paths to your needs:
const fs = require('fs')
const xcode = require('xcode')
if (process.argv.length !== 3) {
console.error("Please pass the development team ID as the first argument")
process.exit(1)
}
const developmentTeamId = process.argv[2]
const path = 'ios/App/App.xcodeproj/project.pbxproj'
const project = xcode.project(path)
project.parse(error => {
const targetKey = project.findTargetKey('App')
const appGroupKey = project.findPBXGroupKey({path: 'App'})
project.addBuildProperty('CODE_SIGN_ENTITLEMENTS', 'App/App.entitlements')
project.addBuildProperty('DEVELOPMENT_TEAM', developmentTeamId)
project.addFile('App.entitlements', appGroupKey)
project.removeFile('GoogleService-Info.plist', appGroupKey)
const f = project.addFile('GoogleService-Info.plist', appGroupKey, {target: targetKey})
f.uuid = project.generateUuid()
project.addToPbxBuildFileSection(f)
project.addToPbxResourcesBuildPhase(f)
fs.writeFileSync(path, project.writeSync())
})
Above script can be executed with
yarn run update-project <arguments...>
given that update-project is registered in package.json:
{
...,
"scripts": {
...
"update-project": "node update-project.js"
},
...
}

Xcode + xcodebuild complains about multiline string literal when exporting for localization

I have something like
NSLocalizedString(#"key",
#"comments in "
#"multiple lines")
in one of my files and when I attempt to export the project that contains the file via
xcodebuild -exportLocalizations -localizationPath <dirpath> -project <projectname> [[-exportLanguage <targetlanguage>]]
I get an error message like this:
Bad entry in file <file_name>.m (line = 35): Argument is not a literal string.
In my experiment, this issue goes away when I make the comment a single line literal.
Has anyone experienced a similar problem and knows more about what is going on here? Xcode's other tool for localization, genstrings, seems to handle this fine.

Xcode "no such file or directory" if filename contains $$ sign?

Using xcode 6 and including files with names like Some$$Class.h and Some$$Class.m leads to problems. Xcode shows to error:
clang: error: no such file or directory: '/Users/test/Some$ClassX.m'
clang: error: no input files
How can I force Xcode to handle files with $$in its name correctly?
There is a very! dirty hack.
If you look to the error message, you can see that the build process of Xcode replaces the $$ of Some$$Class.m with a single $. (Obviously there is no problem with Some$$Class.h) It is an escape sequence.
Some$$Class.m -> Some$Class.m
Therefore you can use Some$$$$Class.m to get Some$$Class.m.
Simply add an (empty) File with the name Some$$$$Class.m to your project to show Xcode that it exists. You have to do this once.
Generate your Some$$Class.m as you did as many times as you want.
When building Xcode will believe that it compiles and links Some$$$$Class.m, but in fact compiles and builds Some$$Class.m.
But you should really, really avoid these names. If the files are generated automatically it should be possible to rename them automatically.

Xcode: Localizable.strings: Conversion of string failed. The string is empty. copystrings failed with exit code 1

I use a Localizable.strings file and replaces the strings in my app with NSLocalizedString(#"KEY",#" COMMENT").
I replaced up to now a lot of strings and it worked well. I have added some more strings and now I have got the following error message :
CopyStringsFile
build/Debug-iphoneos/Australia.app/en.lproj/Localizable.strings
en.lproj/Localizable.strings cd
/Users/regisandre/Desktop/XCode/AUSTRALIAENINT
setenv ICONV /usr/bin/iconv setenv
PATH
"/Developer/Platforms/iPhoneOS.platform/Developer/usr/bin:/Developer/usr/bin:/usr/bin:/bin:/usr/sbin:/sbin"
/Developer/Library/Xcode/Plug-ins/CoreBuildTasks.xcplugin/Contents/Resources/copystrings
--validate --inputencoding utf-8 --outputencoding binary en.lproj/Localizable.strings --outdir
/Users/regisandre/Desktop/XCode/AUSTRALIAENINT/build/Debug-iphoneos/Australia.app/en.lproj
en.lproj/Localizable.strings:
Conversion of string failed. The
string is empty.
en.lproj/Localizable.strings:
Conversion of string failed. The
string is empty. Command
/Developer/Library/Xcode/Plug-ins/CoreBuildTasks.xcplugin/Contents/Resources/copystrings
failed with exit code 1
Update:
You are right ! It was a question of UTF-8/UTF-16 encoding. Once the file generated with genstrings, it is necessary to avoid copy/paste in the strings file from other files (even from Xcode) as it induces some encoding issues.
Once the file generated with Xcode, all the modifications have to be done directly in the file by "direct typing with the keayboard" ; no copy/paste ! (except if you are sure that the format you copy/paste is correct but it seems not so easy to know)
Check if it's UTF-8 encoding when it has special symbols in it.

Resources