Skip to content

Commit

Permalink
Fix NSLocalizedString and Add Localization Tests (#2824)
Browse files Browse the repository at this point in the history
* Fix NSLocalizedString and add new tests

* Address CR Feedback.

* Forgot to change to StringByAppendingPath

* Nevermind. ASSERT_OBJCEQ does indeed work.

* Address feedback.

* Address feedback.
  • Loading branch information
MSFTFox authored Dec 16, 2017
1 parent e1fae88 commit 22fb297
Show file tree
Hide file tree
Showing 14 changed files with 357 additions and 10 deletions.
63 changes: 63 additions & 0 deletions Frameworks/CoreFoundation/PlugIn.subproj/CFBundle_Locale.c
Original file line number Diff line number Diff line change
Expand Up @@ -531,6 +531,10 @@ CF_PRIVATE CFArrayRef _CFBundleCopyUserLanguages() {
static CFArrayRef _CFBundleUserLanguages = NULL;
static dispatch_once_t once = 0;
dispatch_once(&once, ^{
// WINOBJC: __CFAppleLanguages does not exist on Windows
#if DEPLOYMENT_TARGET_WINDOWS
_CFBundleUserLanguages = CFLocaleCopyPreferredLanguages();
#else
CFArrayRef preferencesArray = NULL;
if (__CFAppleLanguages) {
CFDataRef data;
Expand All @@ -555,6 +559,7 @@ CF_PRIVATE CFArrayRef _CFBundleCopyUserLanguages() {
_CFBundleUserLanguages = NULL;
}
if (preferencesArray) CFRelease(preferencesArray);
#endif
});

if (_CFBundleUserLanguages) {
Expand Down Expand Up @@ -663,8 +668,66 @@ static CFStringRef _CFBundleCopyLanguageFoundInLocalizations(CFArrayRef localiza
return NULL;
}

// WINOBJC: Helper functions for workaround in _CFBundleCreateMutableArrayOfFallbackLanguages
static CFStringRef _copyStringTruncated(CFStringRef localization, CFRange cutoff) {
return CFStringCreateWithSubstring(NULL, localization, CFRangeMake(0, cutoff.location));
}

static CFStringRef _copyStringWithUnderscores(CFStringRef localization) {
CFMutableStringRef underscoredString = CFStringCreateMutableCopy(NULL, 0, localization);
CFStringFindAndReplace(underscoredString, CFSTR("-"), CFSTR("_"), CFRangeMake(0, CFStringGetLength(underscoredString)), 0);
return underscoredString;
}

// Given a list of localizations (e.g., provided as argument to API, or present as .lproj directories), return a mutable array of localizations in preferred order. Returns NULL if nothing is found.
static CFMutableArrayRef _CFBundleCreateMutableArrayOfFallbackLanguages(CFArrayRef availableLocalizations, CFArrayRef preferredLocalizations) {
// WINOBJC: The API that performs the work described below does not exist in our 3rd party libraries. ualoc_ is an Apple ICU addition.
#if DEPLOYMENT_TARGET_WINDOWS
// Here we need to intersect the preferred languages with the available localizations
// We know the user languages are in preferred order, add to this list in this order
// Prefer the full language locale, attempt to convert any hyphens to underscores as
// Windows language settings are retrieved with hyphens while underscores are commonly used for localization.
// Finally, attempt to truncate any underscores from the language to find a base localization.
// For example, an english locale will appear as "en-US" and a German locale will appear as "de-DE".
// A localization for "en-US" may be set up as "en-US", "en_US", or even just "en".

CFMutableArrayRef resultArray = CFArrayCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeArrayCallBacks);

for (CFIndex i = 0, preferredCount = CFArrayGetCount(preferredLocalizations); i < preferredCount; i++) {
CFStringRef preferredLocalization = (CFStringRef)CFArrayGetValueAtIndex(preferredLocalizations, i);
for (CFIndex j = 0, availableCount = CFArrayGetCount(availableLocalizations); j < availableCount; j++) {
CFStringRef availableLocalization = (CFStringRef)CFArrayGetValueAtIndex(availableLocalizations, j);
if(CFStringCompare(preferredLocalization, availableLocalization, 0) == kCFCompareEqualTo) {
CFArrayAppendValue(resultArray, preferredLocalization);
}
CFRange hyphenation;
if (CFStringFindWithOptions(preferredLocalization, CFSTR("-"), CFRangeMake(0, CFStringGetLength(preferredLocalization)), kCFCompareCaseInsensitive, &hyphenation) == true) {
CFStringRef underscoreNotationLocalization = _copyStringWithUnderscores(preferredLocalization);
if (CFStringCompare(underscoreNotationLocalization, availableLocalization, 0) == kCFCompareEqualTo) {
CFArrayAppendValue(resultArray, underscoreNotationLocalization);
}

CFStringRef truncatedLocalization = _copyStringTruncated(underscoreNotationLocalization, hyphenation);
if (CFStringCompare(truncatedLocalization, availableLocalization, 0) == kCFCompareEqualTo) {
CFArrayAppendValue(resultArray, truncatedLocalization);
}

CFRelease(underscoreNotationLocalization);
CFRelease(truncatedLocalization);
} else if (CFStringFindWithOptions(preferredLocalization, CFSTR("_"), CFRangeMake(0, CFStringGetLength(preferredLocalization)), kCFCompareCaseInsensitive, &hyphenation) == true) {
CFStringRef truncatedLocalization = _copyStringTruncated(preferredLocalization, hyphenation);
if (CFStringCompare(truncatedLocalization, availableLocalization, 0) == kCFCompareEqualTo) {
CFArrayAppendValue(resultArray, truncatedLocalization);
}
CFRelease(truncatedLocalization);
}
}
}
if (CFArrayGetCount(resultArray) > 0) {
return resultArray;
}
return NULL;
#endif
// stringPointers must be the length of list
char * (^makeBuffer)(CFArrayRef, char **) = ^(CFArrayRef list, char *stringPointers[]) {
#if !__HAS_APPLE_ICU__
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -237,10 +237,11 @@
<ClangCompile Include="$(StarboardBasePath)\tests\unittests\Foundation\WindowsOnly\NSRecursiveLockInternalTests.mm" />
<ClangCompile Include="$(StarboardBasePath)\tests\unittests\Foundation\WindowsOnly\NSPointerArrayInternalTests.mm" />
<ClangCompile Include="$(StarboardBasePath)\tests\unittests\Foundation\WindowsOnly\ArchivalInternalTests.mm" />
<ClangCompile Include="$(StarboardBasePath)\tests\unittests\Foundation\WindowsOnly\BundleInternalTests.mm" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="$(StarboardBasePath)\tests\unittests\Foundation\RuntimeTestHelpers.h" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<Import Project="$(StarboardBasePath)\common\winobjc.packagereference.override.targets" Condition="Exists('$(StarboardBasePath)\common\winobjc.packagereference.override.targets')" />
</Project>
</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -27,5 +27,6 @@
<ClangCompile Include="$(StarboardBasePath)\Frameworks\Foundation\NSString+HSTRING.mm" />
<ClangCompile Include="$(StarboardBasePath)\tests\unittests\Foundation\WindowsOnly\ArchivalInternalTests.mm" />
<ClangCompile Include="$(StarboardBasePath)\tests\unittests\Foundation\WindowsOnly\FoundationInternalTests.mm" />
<ClangCompile Include="$(StarboardBasePath)\tests\unittests\Foundation\WindowsOnly\BundleInternalTests.mm" />
</ItemGroup>
</Project>
</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
"a" = "z";
"b" = "y";
"c" = "x";
"d" = "w";
"e" = "v";
"f" = "u";
"g" = "t";
"h" = "s";
"i" = "r";
"j" = "q";
"Hello World" = "English: Hello World";
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<?xml version="1.0"?>
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|ARM">
Expand Down Expand Up @@ -212,6 +212,7 @@
<ClInclude Include="..\..\WOCCatalog\GeocodingViewController.h" />
<ClInclude Include="..\..\WOCCatalog\GesturesViewController.h" />
<ClInclude Include="..\..\WOCCatalog\ImagesViewController.h" />
<ClInclude Include="..\..\WOCCatalog\LocalizationViewController.h" />
<ClInclude Include="..\..\WOCCatalog\MainViewController.h" />
<ClInclude Include="..\..\WOCCatalog\MenuTableViewController.h" />
<ClInclude Include="..\..\WOCCatalog\OpenGLES11Controller.h" />
Expand Down Expand Up @@ -248,6 +249,7 @@
<ClangCompile Include="..\..\WOCCatalog\GeocodingViewController.m" />
<ClangCompile Include="..\..\WOCCatalog\GesturesViewController.m" />
<ClangCompile Include="..\..\WOCCatalog\ImagesViewController.m" />
<ClangCompile Include="..\..\WOCCatalog\LocalizationViewController.m" />
<ClangCompile Include="..\..\WOCCatalog\MainViewController.m" />
<ClangCompile Include="..\..\WOCCatalog\MenuTableViewController.m" />
<ClangCompile Include="..\..\WOCCatalog\OpenGLES11Controller.m" />
Expand All @@ -274,6 +276,26 @@
<VariableFile Condition="'$(Configuration)'=='Debug'">WOCCatalog-Debug-xcvars.txt</VariableFile>
<VariableFile Condition="'$(Configuration)'=='Release'">WOCCatalog-Release-xcvars.txt</VariableFile>
</SBInfoPlistCopy>
<SBResourceCopy Include="Base.lproj\Localizable.strings">
<FileType>Document</FileType>
<VariantDir>Base.lproj</VariantDir>
</SBResourceCopy>
<SBResourceCopy Include="de.lproj\Localizable.strings">
<FileType>Document</FileType>
<VariantDir>de.lproj</VariantDir>
</SBResourceCopy>
<SBResourceCopy Include="zh.lproj\Localizable.strings">
<FileType>Document</FileType>
<VariantDir>zh.lproj</VariantDir>
</SBResourceCopy>
<SBResourceCopy Include="zh-Hant-TW.lproj\Localizable.strings">
<FileType>Document</FileType>
<VariantDir>zh-Hant-TW.lproj</VariantDir>
</SBResourceCopy>
<SBResourceCopy Include="es_MX.lproj\Localizable.strings">
<FileType>Document</FileType>
<VariantDir>es_MX.lproj</VariantDir>
</SBResourceCopy>
<SBResourceCopy Include="..\..\WOCCatalog\Images\blueButton.png" />
<SBResourceCopy Include="..\..\WOCCatalog\Images\photo1.jpg" />
<SBResourceCopy Include="..\..\WOCCatalog\Images\photo10.jpg" />
Expand Down Expand Up @@ -303,7 +325,6 @@
<ItemGroup />
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets" />

<!-- Begin NuGet Section -->
<PropertyGroup>
<TargetFramework>uap10.0</TargetFramework>
Expand All @@ -316,6 +337,5 @@
<PackageReference Include="WinObjC.Logging" Version="*" />
</ItemGroup>
<!-- End NuGet Section -->

<Import Project="$(MSBuildThisFileDirectory)\..\..\..\..\common\winobjc.packagereference.override.targets" Condition="Exists('$(MSBuildThisFileDirectory)\..\..\..\..\common\winobjc.packagereference.override.targets')"/>
</Project>
<Import Project="$(MSBuildThisFileDirectory)\..\..\..\..\common\winobjc.packagereference.override.targets" Condition="Exists('$(MSBuildThisFileDirectory)\..\..\..\..\common\winobjc.packagereference.override.targets')" />
</Project>
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<?xml version="1.0"?>
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Common">
Expand Down Expand Up @@ -99,6 +99,9 @@
<ClInclude Include="..\..\WOCCatalog\ImagesViewController.h">
<Filter>WOCCatalog</Filter>
</ClInclude>
<ClInclude Include="..\..\WOCCatalog\LocalizationViewController.h">
<Filter>WOCCatalog</Filter>
</ClInclude>
<ClInclude Include="..\..\WOCCatalog\MainViewController.h">
<Filter>WOCCatalog</Filter>
</ClInclude>
Expand Down Expand Up @@ -207,6 +210,9 @@
<ClangCompile Include="..\..\WOCCatalog\ImagesViewController.m">
<Filter>WOCCatalog</Filter>
</ClangCompile>
<ClangCompile Include="..\..\WOCCatalog\LocalizationViewController.m">
<Filter>WOCCatalog</Filter>
</ClangCompile>
<ClangCompile Include="..\..\WOCCatalog\MainViewController.m">
<Filter>WOCCatalog</Filter>
</ClangCompile>
Expand Down Expand Up @@ -332,4 +338,21 @@
<Filter>WOCCatalog</Filter>
</XibCompile>
</ItemGroup>
</Project>
<ItemGroup>
<ApplicationDefinition Include="App.xaml" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="App.xaml.cpp" />
<ClCompile Include="pch.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="pch.h" />
<ClInclude Include="App.xaml.h" />
<Filter>WOCCatalog</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<SBResourceCopy Include="Base.lproj\Localizable.strings" />
<SBResourceCopy Include="de.lproj\Localizable.strings" />
</ItemGroup>
</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
"a" = "1";
"b" = "2";
"c" = "3";
"d" = "4";
"e" = "5";
"f" = "6";
"g" = "7";
"h" = "8";
"i" = "9";
"j" = "10";
"Hello World" = "German: Hallo Welt";
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
"a" = "z";
"b" = "x";
"c" = "c";
"d" = "v";
"e" = "b";
"f" = "n";
"g" = "m";
"h" = ",";
"i" = ".";
"j" = "/";
"Hello World" = "Spanish _: Hola Mundo";
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
"a" = "q";
"b" = "w";
"c" = "e";
"d" = "r";
"e" = "t";
"f" = "y";
"g" = "u";
"h" = "i";
"i" = "o";
"j" = "p";
"Hello World" = "zh-Hant-TW: 世界您好";
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
"a" = "a";
"b" = "s";
"c" = "d";
"d" = "f";
"e" = "g";
"f" = "h";
"g" = "j";
"h" = "k";
"i" = "l";
"j" = ";";
"Hello World" = "zh-Hans-CN truncated: 世界您好";
21 changes: 21 additions & 0 deletions samples/WOCCatalog/WOCCatalog/LocalizationViewController.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
//******************************************************************************
//
// Copyright (c) Microsoft. All rights reserved.
//
// This code is licensed under the MIT License (MIT).
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
//******************************************************************************

#import <UIKit/UIKit.h>
#import <Foundation/Foundation.h>

@interface LocalizationViewController : UITableViewController <UITextFieldDelegate>
@end
82 changes: 82 additions & 0 deletions samples/WOCCatalog/WOCCatalog/LocalizationViewController.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
//******************************************************************************
//
// Copyright (c) Microsoft. All rights reserved.
//
// This code is licensed under the MIT License (MIT).
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
//******************************************************************************

#import "LocalizationViewController.h"

static const CGFloat c_originX = 5;
static const CGFloat c_originY = 8;
static const CGFloat c_width = 260;
static const CGFloat c_height = 50;

@implementation LocalizationViewController {
@private
UITableViewCell* _cell;
UITextField* _copyTextField;
UILabel* _pasteTextLabel;
}
- (void)viewDidLoad {
[super viewDidLoad];
self.tableView.allowsSelection = YES;
}

- (NSInteger)tableView:(UITableView*)tableView numberOfRowsInSection:(NSInteger)section {
return 2;
}

- (CGFloat)tableView:(UITableView*)tableView heightForRowAtIndexPath:(NSIndexPath*)indexPath {
return 70;
}

- (UITableViewCell*)tableView:(UITableView*)tableView cellForRowAtIndexPath:(NSIndexPath*)indexPath {
_cell = [tableView dequeueReusableCellWithIdentifier:@"MenuCell"];
if (nil == _cell) {
_cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"MenuCell"];
_cell.selectionStyle = UITableViewCellSelectionStyleNone;
}

CGRect frame = CGRectMake(c_originX, c_originY, c_width, c_height);

if (indexPath.row == 0) {
_copyTextField = [[UITextField alloc] initWithFrame:frame];
_copyTextField.textColor = [UIColor blackColor];
_copyTextField.font = [UIFont systemFontOfSize:17.0];
_copyTextField.text = @"Hello World";
[_copyTextField addTarget:self action:@selector(onCopyTextChanged:) forControlEvents:UIControlEventEditingChanged];

_cell.accessoryView = _copyTextField;
_cell.textLabel.text = @"Enter Text";
} else if (indexPath.row == 1) {
_pasteTextLabel = [[UILabel alloc] initWithFrame:frame];
_pasteTextLabel.textColor = [UIColor blackColor];
_pasteTextLabel.font = [UIFont systemFontOfSize:17.0];
_pasteTextLabel.textAlignment = NSTextAlignmentLeft;
_pasteTextLabel.text = NSLocalizedString(_copyTextField.text, nil);
_cell.accessoryView = _pasteTextLabel;
_cell.textLabel.text = @"Localized Text";
}
return _cell;
}

- (void)viewDidLayoutSubviews {
[(UIScrollView*)self.view setContentSize:[self.view.subviews[0] bounds].size];
}

- (void)onCopyTextChanged:(UITextField*)textField {
_pasteTextLabel.text = NSLocalizedString(textField.text, nil);
[[self tableView] setNeedsLayout];
}

@end
Loading

0 comments on commit 22fb297

Please sign in to comment.