Xliff export and \n

I have the following in my English strings file:

"BackupFailReason"="A database integrity failure occurred.\n\nThe backup cannot be performed.";


When I export it with xcodeBuild I get:

      <trans-unit id="BackupFailReason">
        <source>A database integrity failure occurred.

The backup cannot be performed.</source>
      </trans-unit>


When this gets localized, and then given back to me, and then reimported in xcode I get:

/ (No Commment) */
"BackupFailReason" = "En database integrasjonsfeil oppsto.

Sikkerhetskopieringen kan ikke gjennomføres.";


This obviously breaks localization. What is the best way to handle this? I don't mind going through my English strings and changing them - but I don't want to lose my multi-line strings.

Replies

Please file a bug report, if you haven't already. Include the information provided here, and also which version of Xcode that you're using. Thanks!

OK I can do that…but is there no recourse until then? I'm at ship stage…as in I was supposed to ship today and that's when I discovered that some of our localization files were broken…and now I'm trying to figure out the right choice and right now I'm kind of swearing at my tools.

Radar; 22981479

To be fair, this is something I should've noticed during QA and it shouldn't be your fault that we're at ship time and this is failing, well sort of, but we ignored localization issues to some degree for awhile because the word on the forums and the street was that localizations just broke on iOS 9 and we were kind of waiting on an iOS 9 fix.

It wasn't until I dug around more before I saw this.

foreach ($list as $fileElements)
{
  $units = $fileElements->getElementsByTagName('trans-unit');
  foreach ($units as $unit)
  {
  //look for multi-line \n
  foreach ($unit->getElementsByTagName('source') as $source)
  {
  if (strstr($source->nodeValue, PHP_EOL))
  {
  $fixedString = str_replace(PHP_EOL, '\\n', $source->nodeValue);

  $source->nodeValue = $fixedString;
  }
  }
}


That seems to work. Its a little PHP script I already had for working with some xliff stuff. Going to test it and make sure.

I've added my scripts and more info to my radar:

22981479


Specifically, Xcode is exporting the XLIFF just fine. Its the import that's broken. Its not converting new lines in the source element to \n when it constructs the strings file.


I've basically spent the last week writing scripts to make newlines untouchable by xcode and do nothing else.

Alex - please can you elaborate on why the strings files with literal newlines don't work for you?

Sure, but I thought I did that in the original post. Look at the strings file. When I start I have an \n in the line. When I export and then import, its broken up into multiple lines in the strings file. This causes iOS to tell you that the strings file is misformatted and now it will not load the string.


So I start with:

"BackupFailReason"="A database integrity failure occurred.\n\nThe backup cannot be performed.";


and end up with:


"BackupFailReason"="A database integrity failure occurred.

The backup cannot be performed.";


And then when iOS tries to load the strings file, you get:

(lldb) po [[NSBundle mainBundle] localizedStringForKey:@"Event" value:@"" table:@"InformantCommon"] 
2015-10-05 15:49:37.570 Informant[16125:14938518] Unable to load .strings file: CFBundle 0x7fefa2404100 </Users/akac/Library/Developer/CoreSimulator/Devices/9B2E40B5-7E2E-45C5-9C8A-E96044F37E4A/data/Containers/Bundle/Application/53B52216-15FA-4050-8564-69F65E664A1E/Informant.app> (executable, loaded) / InformantCommon: Error Domain=NSCocoaErrorDomain Code=3840 "Unexpected character / at line 1" UserInfo={NSDebugDescription=Unexpected character / at line 1, kCFPropertyListOldStyleParsingError=Error Domain=NSCocoaErrorDomain Code=3840 "Unexpected ';' or '=' after key at line 4801" UserInfo={NSDebugDescription=Unexpected ';' or '=' after key at line 4801}} 
Event

Thanks Alex - please attach the complete strings file to the Radar if possible.

Which one - the exported xliff? Or the original strings file its comign from? Or both?

Aleksei Kac06-Oct-2015 02:52 PM

'en.xliff' and 'InformantCommon.strings' were successfully uploaded.

BTW - on a separate note, with Xcode 7.1 beta 3 (the one that just came out today), I import an xliff file that works under 7.01 - and it gets me to the window where I approve the import and then I get this error (radar 22997699):


XLIFF target-language attribute missing


Basically I'm just seeing XCode's localization tools go downhill big time since Xcode 7. It seems like every release does not fix any of the issues that I talked to you about at WWDC and introduces new ones. I can't tell you how disappointed and discouraging this is. I've put in so many bug reports only to see things get worse.


I tell you, if you sat down with me for a few days and we ran through my project back and forth, you would fix so many issues.


Console:

10/6/15 2:59:51.695 PM Xcode[61177]: Failed to connect (comparisonSplitView) outlet from (DVTComparisonScrollCoordinator) to (DVTComparisonSplitView): missing setter or instance variable

10/6/15 3:00:05.349 PM Xcode[61177]: [default] Failed to updated bookmark for item (null) [063E9DE3-3FFD-42C4-A82D-1D592BCF75F9] - URL:file:///var/folders/3l/y2j2wt7x4_357qnkmcmbgc700000gn/T/IDELocalizationImporter-F1CFA10C-FD48-45D7-8421-2DDA5A7C473C-Compare/Import/en.lproj/InformantCommon.strings.comparison with error Error Domain=NSCocoaErrorDomain Code=260 "The file couldn’t be opened because it doesn’t exist."

10/6/15 3:00:05.349 PM Xcode[61177]: -[SFLListItem synthesizeMissingPropertyValues] Failed to updated bookmark for item (null) [063E9DE3-3FFD-42C4-A82D-1D592BCF75F9] - URL:file:///var/folders/3l/y2j2wt7x4_357qnkmcmbgc700000gn/T/IDELocalizationImporter-F1CFA10C-FD48-45D7-8421-2DDA5A7C473C-Compare/Import/en.lproj/InformantCommon.strings.comparison with error Error Domain=NSCocoaErrorDomain Code=260 "The file couldn’t be opened because it doesn’t exist."

10/6/15 3:00:05.355 PM Xcode[61177]: [default] Failed to updated bookmark for item InformantCommon.strings.comparison [063E9DE3-3FFD-42C4-A82D-1D592BCF75F9] - URL:file:///var/folders/3l/y2j2wt7x4_357qnkmcmbgc700000gn/T/IDELocalizationImporter-F1CFA10C-FD48-45D7-8421-2DDA5A7C473C-Compare/Import/en.lproj/InformantCommon.strings.comparison with error Error Domain=NSCocoaErrorDomain Code=260 "The file couldn’t be opened because it doesn’t exist."

10/6/15 3:00:05.355 PM Xcode[61177]: -[SFLListItem synthesizeMissingPropertyValues] Failed to updated bookmark for item InformantCommon.strings.comparison [063E9DE3-3FFD-42C4-A82D-1D592BCF75F9] - URL:file:///var/folders/3l/y2j2wt7x4_357qnkmcmbgc700000gn/T/IDELocalizationImporter-F1CFA10C-FD48-45D7-8421-2DDA5A7C473C-Compare/Import/en.lproj/InformantCommon.strings.comparison with error Error Domain=NSCocoaErrorDomain Code=260 "The file couldn’t be opened because it doesn’t exist."

10/6/15 3:00:05.368 PM Xcode[61177]: [default] Failed to updated bookmark for item (null) [899B3EF5-5B87-4105-A676-4ABED54C43FD] - URL:file:///var/folders/3l/y2j2wt7x4_357qnkmcmbgc700000gn/T/IDELocalizationImporter-F1CFA10C-FD48-45D7-8421-2DDA5A7C473C-Compare/Export/en.lproj/InformantCommon.strings.comparison with error Error Domain=NSCocoaErrorDomain Code=260 "The file couldn’t be opened because it doesn’t exist."

10/6/15 3:00:05.368 PM Xcode[61177]: -[SFLListItem synthesizeMissingPropertyValues] Failed to updated bookmark for item (null) [899B3EF5-5B87-4105-A676-4ABED54C43FD] - URL:file:///var/folders/3l/y2j2wt7x4_357qnkmcmbgc700000gn/T/IDELocalizationImporter-F1CFA10C-FD48-45D7-8421-2DDA5A7C473C-Compare/Export/en.lproj/InformantCommon.strings.comparison with error Error Domain=NSCocoaErrorDomain Code=260 "The file couldn’t be opened because it doesn’t exist."

10/6/15 3:00:05.373 PM Xcode[61177]: [default] Failed to updated bookmark for item (null) [899B3EF5-5B87-4105-A676-4ABED54C43FD] - URL:file:///var/folders/3l/y2j2wt7x4_357qnkmcmbgc700000gn/T/IDELocalizationImporter-F1CFA10C-FD48-45D7-8421-2DDA5A7C473C-Compare/Export/en.lproj/InformantCommon.strings.comparison with error Error Domain=NSCocoaErrorDomain Code=260 "The file couldn’t be opened because it doesn’t exist."

10/6/15 3:00:05.373 PM Xcode[61177]: -[SFLListItem synthesizeMissingPropertyValues] Failed to updated bookmark for item (null) [899B3EF5-5B87-4105-A676-4ABED54C43FD] - URL:file:///var/folders/3l/y2j2wt7x4_357qnkmcmbgc700000gn/T/IDELocalizationImporter-F1CFA10C-FD48-45D7-8421-2DDA5A7C473C-Compare/Export/en.lproj/InformantCommon.strings.comparison with error Error Domain=NSCocoaErrorDomain Code=260 "The file couldn’t be opened because it doesn’t exist."

10/6/15 3:01:01.289 PM Xcode[61177]: [MT] DVTAssertions: Warning in /Library/Caches/com.apple.xbs/Sources/IDEFrameworks/IDEFrameworks-9077/IDEFoundation/FileManagement/IDEReadOnlyItemsManager.m:112

Details: (nil == parentAttrs) for file path even though it exists (0) <DVTFilePath:0x7ffcd72c1990:'/var/folders/3l/y2j2wt7x4_357qnkmcmbgc700000gn/T/IDELocalizationImporter-E26EC406-E174-4E45-9AF7-2BE5AF27E6D0-Compare/Export/en.lproj'>

Object: <IDEReadOnlyItemsManager>

Method: +readOnlyStatusOfFilePath:

Thread: <NSThread: 0x7ffce3f12fe0>{number = 1, name = main}

Please file a bug at http://bugreport.apple.com with this warning message and any useful information you can provide.

10/6/15 3:01:01.379 PM Xcode[61177]: [default] Failed to updated bookmark for item (null) [EE632A45-2F95-40C2-A19F-5778A867D037] - URL:file:///var/folders/3l/y2j2wt7x4_357qnkmcmbgc700000gn/T/IDELocalizationImporter-E26EC406-E174-4E45-9AF7-2BE5AF27E6D0-Compare/Export/en.lproj/InformantCommon.strings.comparison with error Error Domain=NSCocoaErrorDomain Code=260 "The file couldn’t be opened because it doesn’t exist."

10/6/15 3:01:01.379 PM Xcode[61177]: -[SFLListItem synthesizeMissingPropertyValues] Failed to updated bookmark for item (null) [EE632A45-2F95-40C2-A19F-5778A867D037] - URL:file:///var/folders/3l/y2j2wt7x4_357qnkmcmbgc700000gn/T/IDELocalizationImporter-E26EC406-E174-4E45-9AF7-2BE5AF27E6D0-Compare/Export/en.lproj/InformantCommon.strings.comparison with error Error Domain=NSCocoaErrorDomain Code=260 "The file couldn’t be opened because it doesn’t exist."

10/6/15 3:01:01.386 PM Xcode[61177]: [default] Failed to updated bookmark for item (null) [EE632A45-2F95-40C2-A19F-5778A867D037] - URL:file:///var/folders/3l/y2j2wt7x4_357qnkmcmbgc700000gn/T/IDELocalizationImporter-E26EC406-E174-4E45-9AF7-2BE5AF27E6D0-Compare/Export/en.lproj/InformantCommon.strings.comparison with error Error Domain=NSCocoaErrorDomain Code=260 "The file couldn’t be opened because it doesn’t exist."

10/6/15 3:01:01.386 PM Xcode[61177]: -[SFLListItem synthesizeMissingPropertyValues] Failed to updated bookmark for item (null) [EE632A45-2F95-40C2-A19F-5778A867D037] - URL:file:///var/folders/3l/y2j2wt7x4_357qnkmcmbgc700000gn/T/IDELocalizationImporter-E26EC406-E174-4E45-9AF7-2BE5AF27E6D0-Compare/Export/en.lproj/InformantCommon.strings.comparison with error Error Domain=NSCocoaErrorDomain Code=260 "The file couldn’t be opened because it doesn’t exist."

10/6/15 3:01:01.411 PM Xcode[61177]: [default] Failed to updated bookmark for item (null) [5EC1ED1E-1EBE-42E3-BADE-EF66C2046A82] - URL:file:///var/folders/3l/y2j2wt7x4_357qnkmcmbgc700000gn/T/IDELocalizationImporter-E26EC406-E174-4E45-9AF7-2BE5AF27E6D0-Compare/Import/en.lproj/InformantCommon.strings.comparison with error Error Domain=NSCocoaErrorDomain Code=260 "The file couldn’t be opened because it doesn’t exist."

10/6/15 3:01:01.411 PM Xcode[61177]: -[SFLListItem synthesizeMissingPropertyValues] Failed to updated bookmark for item (null) [5EC1ED1E-1EBE-42E3-BADE-EF66C2046A82] - URL:file:///var/folders/3l/y2j2wt7x4_357qnkmcmbgc700000gn/T/IDELocalizationImporter-E26EC406-E174-4E45-9AF7-2BE5AF27E6D0-Compare/Import/en.lproj/InformantCommon.strings.comparison with error Error Domain=NSCocoaErrorDomain Code=260 "The file couldn’t be opened because it doesn’t exist."

10/6/15 3:01:01.417 PM Xcode[61177]: [default] Failed to updated bookmark for item InformantCommon.strings.comparison [5EC1ED1E-1EBE-42E3-BADE-EF66C2046A82] - URL:file:///var/folders/3l/y2j2wt7x4_357qnkmcmbgc700000gn/T/IDELocalizationImporter-E26EC406-E174-4E45-9AF7-2BE5AF27E6D0-Compare/Import/en.lproj/InformantCommon.strings.comparison with error Error Domain=NSCocoaErrorDomain Code=260 "The file couldn’t be opened because it doesn’t exist."

10/6/15 3:01:01.417 PM Xcode[61177]: -[SFLListItem synthesizeMissingPropertyValues] Failed to updated bookmark for item InformantCommon.strings.comparison [5EC1ED1E-1EBE-42E3-BADE-EF66C2046A82] - URL:file:///var/folders/3l/y2j2wt7x4_357qnkmcmbgc700000gn/T/IDELocalizationImporter-E26EC406-E174-4E45-9AF7-2BE5AF27E6D0-Compare/Import/en.lproj/InformantCommon.strings.comparison with error Error Domain=NSCocoaErrorDomain Code=260 "The file couldn’t be opened because it doesn’t exist."

I think the problem is caused by Xcode not properly handling white space in the XML. Xcode should probably not generate real linefeed characters during export but if it does, it should add the attribute xml:space="preserve". Otherwise any other tool that is importing the XML file will ignore the linefeed, as per XML specification.


Same goed probably for the import, Xcode should obey the xml:space="preserve" attribute, otherwise simply ignore the linefeeds.

You can fix this if you copy/paste the strings in an text editor that can run RegEx and search for:


\n(?=[^\n$"/])


and replace that with


\\n

Lo and behold today I tried Xcode's export/import for the first time and used Xliffie for the external translating. Very convenient but a lot of crashes in XLiffie and Xcode. I got around the \n issue by putting those strings in their own strings file with a unique table name. I only had three. I used NSLocalizedString("fooEnglish", tableName "fooTable", comment:"fooComment"). It seems that "straight-out-of-the-box" Xcode's export/import function ignores uniquely named strings files and only export/imports the Localizable.strings file along with the Storyboard files. This works for me in this case because I am translating into two languages that I happen to know. However, if I was sending this out for professional translation, it would be an additional chore and expense to deal with these exceptions.

It's not hard to test. Just import an xliff with a newline.


How do people get around this bug? Every project with a lot of localize strings must have at least one '/n' included somewhere. Maybe no one uses this import/export feature.