aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--AppKit/GTMUILocalizer.h83
-rw-r--r--AppKit/GTMUILocalizer.m218
-rw-r--r--AppKit/GTMUILocalizerTest.h37
-rw-r--r--AppKit/GTMUILocalizerTest.m98
-rw-r--r--AppKit/GTMUILocalizerTestView.xib481
-rw-r--r--AppKit/GTMUILocalizerTestWindow.xib695
-rw-r--r--AppKit/TestData/GTMUILocalizerMenuState.gtmUTState31
-rw-r--r--AppKit/TestData/GTMUILocalizerView1State.gtmUTState60
-rw-r--r--AppKit/TestData/GTMUILocalizerView2State.gtmUTState40
-rw-r--r--AppKit/TestData/GTMUILocalizerWindow1State.gtmUTState147
-rw-r--r--AppKit/TestData/GTMUILocalizerWindow2State.gtmUTState21
-rw-r--r--AppKit/TestData/Resources/English.lproj/Localizable.stringsbin0 -> 2122 bytes
-rw-r--r--GTM.xcodeproj/project.pbxproj74
-rw-r--r--ReleaseNotes.txt10
-rw-r--r--UnitTesting/GTMAppKit+UnitTesting.h6
-rw-r--r--UnitTesting/GTMAppKit+UnitTesting.m50
-rw-r--r--UnitTesting/GTMUIUnitTestingHarness/English.lproj/Localizable.stringsbin0 -> 1372 bytes
-rw-r--r--UnitTesting/GTMUIUnitTestingHarness/English.lproj/MainMenu.xib77
-rw-r--r--UnitTesting/TestData/GTMUnitTestingTestApp.gtmUTState26
-rw-r--r--UnitTesting/TestData/GTMUnitTestingWindow.gtmUTState20
20 files changed, 2155 insertions, 19 deletions
diff --git a/AppKit/GTMUILocalizer.h b/AppKit/GTMUILocalizer.h
new file mode 100644
index 0000000..d484cb2
--- /dev/null
+++ b/AppKit/GTMUILocalizer.h
@@ -0,0 +1,83 @@
+//
+// GTMUILocalizer.h
+//
+// Copyright 2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy
+// of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+//
+
+#import <Cocoa/Cocoa.h>
+
+// A class for localizing nibs by doing simple string replacement.
+// To use this, make an instance of GTMUILocalizer in your nib. Connect the
+// owner_ outlet of the your instance to the File Owner of the nib. It expects
+// the owner_ outlet to be an instance or subclass of NSWindowController,
+// NSViewController or NSApplication. Using the bundle of the nib it will then
+// localize any items in the NSWindowController's window and subviews, or the
+// NSViewController's view and subviews or the NSApplication's main menu
+// and dockmenu at when awakeFromNib is called on the GTMUILocalizer instance.
+// You can optionally hook up otherObjectToLocalize_ and
+// yetAnotherObjectToLocalize_ and those will also be localized. Strings in the
+// nib that you want localized must start with ^ (shift-6). The strings will
+// be looked up in the Localizable.strings table without the caret as the
+// key.
+// Things that will be localized are:
+// - Titles (for menus, buttons, windows, menuitems, tabViewItem)
+// - stringValue (for labels)
+// - tooltips
+// - accessibility help
+// - accessibility description
+//
+// As an example if I wanted to localize a button with the word "Print" on
+// it, I would put it in a window controlled by a NSWindowController that was
+// the owner of the nib. I would set it's title to be "^Print". I would then
+// create an instance of GTMUILocalizer and set it's owner_ to be the owner
+// of the nib.
+// In my Localizable.strings file in my fr.lproj directory for the bundle
+// I would put "Print" = "Imprimer";
+// Then when my app launched in French I would get a button labeled
+// "Imprimer". Note that GTMUILocalizer is only for strings, and doesn't
+// resize, move or change text alignment on any of the things it modifies.
+// If you absolutely need a caret at the beginning of the string
+// post-localization, you can put "Foo" = "^Foo"; in your strings file and
+// it will work.
+// Your nib could be located in a variety of places depending on what you want
+// to do. I would recommend having your "master" nib directly in Resources.
+// If for some reason you needed to do some custom localization of the
+// nib you could copy the master nib into your specific locale folder, and
+// then you only need to adjust the items in the nib that you need to
+// customize. You can leave the strings in the "^Foo" convention and they
+// will localize properly. This keeps the differences between the nibs down
+// to the bare essentials.
+//
+@interface GTMUILocalizer : NSObject {
+ @private
+ IBOutlet id owner_;
+ IBOutlet id otherObjectToLocalize_;
+ IBOutlet id yetAnotherObjectToLocalize_;
+ NSBundle *bundle_;
+}
+- (id)initWithBundle:(NSBundle *)bundle;
+- (void)localizeObject:(id)object recursively:(BOOL)recursive;
+- (void)localizeWindow:(NSWindow *)window recursively:(BOOL)recursive;
+- (void)localizeView:(NSView *)view recursively:(BOOL)recursive;
+- (void)localizeMenu:(NSMenu *)menu recursively:(BOOL)recursive;
+
+// A method for subclasses to override in case you have a different
+// way to go about getting localized strings.
+// If |string| does not start with ^ you should return nil.
+// If |string| is nil, you should return nil
+- (NSString *)localizedStringForString:(NSString *)string;
+// Allows subclasses to override how the bundle is picked up
+- (NSBundle *)bundleForOwner:(id)owner;
+@end
diff --git a/AppKit/GTMUILocalizer.m b/AppKit/GTMUILocalizer.m
new file mode 100644
index 0000000..cfeb401
--- /dev/null
+++ b/AppKit/GTMUILocalizer.m
@@ -0,0 +1,218 @@
+//
+// GTMUILocalizer.m
+//
+// Copyright 2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy
+// of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+//
+
+#import "GTMDefines.h"
+#import "GTMUILocalizer.h"
+
+@implementation GTMUILocalizer
+- (id)initWithBundle:(NSBundle *)bundle {
+ if ((self = [super init])) {
+ bundle_ = [bundle retain];
+ }
+ return self;
+}
+
+- (void)dealloc {
+ [bundle_ release];
+ [super dealloc];
+}
+
+- (void)awakeFromNib {
+ if (owner_) {
+ NSBundle *newBundle = [self bundleForOwner:owner_];
+ bundle_ = [newBundle retain];
+ [self localizeObject:owner_ recursively:YES];
+ [self localizeObject:otherObjectToLocalize_ recursively:YES];
+ [self localizeObject:yetAnotherObjectToLocalize_ recursively:YES];
+ } else {
+ _GTMDevLog(@"Expected an owner_ set for %@", self);
+ }
+}
+
+- (NSBundle *)bundleForOwner:(id)owner {
+ NSBundle *newBundle = nil;
+ if (owner) {
+ Class class = NSClassFromString(@"NSViewController");
+ if ([owner isKindOfClass:class]) {
+ newBundle = [owner performSelector:@selector(nibBundle)];
+ } else {
+ class = [NSWindowController class];
+ if ([owner isKindOfClass:class]
+ && ![owner isMemberOfClass:class]) {
+ newBundle = [NSBundle bundleForClass:[owner class]];
+ }
+ }
+ if (!newBundle) {
+ newBundle = [NSBundle mainBundle];
+ }
+ }
+ return newBundle;
+}
+
+- (NSString *)localizedStringForString:(NSString *)string {
+ NSString *localized = nil;
+ if (bundle_ && [string hasPrefix:@"^"]) {
+ NSString *notFoundValue = @"__GTM_NOT_FOUND__";
+ NSString *key = [string substringFromIndex:1];
+ localized = [bundle_ localizedStringForKey:key
+ value:notFoundValue
+ table:nil];
+ if ([localized isEqualToString:notFoundValue]) {
+ localized = nil;
+ }
+ }
+ return localized;
+}
+
+- (void)localizeObject:(id)object recursively:(BOOL)recursive {
+ if (object) {
+ if ([object isKindOfClass:NSClassFromString(@"NSViewController")]) {
+ NSView *view = [object view];
+ [self localizeView:view recursively:recursive];
+ } else if ([object isKindOfClass:[NSWindowController class]]) {
+ NSWindow *window = [object window];
+ [self localizeWindow:window recursively:recursive];
+ } else if ([object isKindOfClass:[NSMenu class]]) {
+ [self localizeMenu:(NSMenu *)object recursively:recursive];
+ } else if ([object isKindOfClass:[NSWindow class]]) {
+ [self localizeWindow:(NSWindow *)object recursively:recursive];
+ } else if ([object isKindOfClass:[NSView class]]) {
+ [self localizeView:(NSView *)object recursively:recursive];
+ } else if ([object isKindOfClass:[NSApplication class]]) {
+ // Do the main menu
+ NSMenu *menu = [object mainMenu];
+ [self localizeMenu:menu recursively:recursive];
+ }
+ }
+}
+
+- (void)localizeWindow:(NSWindow *)window recursively:(BOOL)recursive {
+ NSString *title = [window title];
+ NSString *localizedTitle = [self localizedStringForString:title];
+ if (localizedTitle) {
+ [window setTitle:localizedTitle];
+ }
+ if (recursive) {
+ NSView *content = [window contentView];
+ [self localizeView:content recursively:recursive];
+ }
+}
+
+- (void)localizeView:(NSView *)view recursively:(BOOL)recursive {
+ if (view) {
+ // First do tooltips
+ NSString *toolTip = [view toolTip];
+ if (toolTip) {
+ NSString *localizedToolTip = [self localizedStringForString:toolTip];
+ if (localizedToolTip) {
+ [view setToolTip:localizedToolTip];
+ }
+ }
+
+ // Then do accessibility stuff
+ NSArray *supportedAttrs = [view accessibilityAttributeNames];
+ if ([supportedAttrs containsObject:NSAccessibilityHelpAttribute]) {
+ NSString *accessibilityHelp
+ = [view accessibilityAttributeValue:NSAccessibilityHelpAttribute];
+ if (accessibilityHelp) {
+ NSString *localizedAccessibilityHelp
+ = [self localizedStringForString:accessibilityHelp];
+ if (localizedAccessibilityHelp) {
+ [view accessibilitySetValue:localizedAccessibilityHelp
+ forAttribute:NSAccessibilityHelpAttribute];
+ }
+ }
+ }
+
+ if ([supportedAttrs containsObject:NSAccessibilityDescriptionAttribute]) {
+ NSString *accessibilityDesc
+ = [view accessibilityAttributeValue:NSAccessibilityDescriptionAttribute];
+ if (accessibilityDesc) {
+ NSString *localizedAccessibilityDesc
+ = [self localizedStringForString:accessibilityDesc];
+ if (localizedAccessibilityDesc) {
+ [view accessibilitySetValue:localizedAccessibilityDesc
+ forAttribute:NSAccessibilityDescriptionAttribute];
+ }
+ }
+ }
+
+ // Then do titles
+ if ([view isKindOfClass:[NSTextField class]]) {
+ NSString *title = [(NSTextField *)view stringValue];
+ NSString *localizedTitle = [self localizedStringForString:title];
+ if (localizedTitle) {
+ [(NSTextField *)view setStringValue:localizedTitle];
+ }
+ } else if ([view respondsToSelector:@selector(title)]
+ && [view respondsToSelector:@selector(setTitle:)]) {
+ NSString *title = [view performSelector:@selector(title)];
+ if (title) {
+ NSString *localizedTitle = [self localizedStringForString:title];
+ if (localizedTitle) {
+ [view performSelector:@selector(setTitle:) withObject:localizedTitle];
+ }
+ }
+ } else if ([view respondsToSelector:@selector(tabViewItems)]) {
+ NSArray *items = [view performSelector:@selector(tabViewItems)];
+ NSEnumerator *itemEnum = [items objectEnumerator];
+ NSTabViewItem *item = nil;
+ while ((item = [itemEnum nextObject])) {
+ NSString *label = [item label];
+ NSString *localizedLabel = [self localizedStringForString:label];
+ if (localizedLabel) {
+ [item setLabel:localizedLabel];
+ }
+ if (recursive) {
+ [self localizeView:[item view] recursively:recursive];
+ }
+ }
+ }
+ [self localizeMenu:[view menu] recursively:recursive];
+ if (recursive) {
+ NSArray *subviews = [view subviews];
+ NSView *subview = nil;
+ GTM_FOREACH_OBJECT(subview, subviews) {
+ [self localizeView:subview recursively:recursive];
+ }
+ }
+ }
+}
+
+- (void)localizeMenu:(NSMenu *)menu recursively:(BOOL)recursive {
+ if (menu) {
+ NSString *title = [menu title];
+ NSString *localizedTitle = [self localizedStringForString:title];
+ if (localizedTitle) {
+ [menu setTitle:localizedTitle];
+ }
+ NSArray *menuItems = [menu itemArray];
+ NSMenuItem *menuItem = nil;
+ GTM_FOREACH_OBJECT(menuItem, menuItems) {
+ title = [menuItem title];
+ localizedTitle = [self localizedStringForString:title];
+ if (localizedTitle) {
+ [menuItem setTitle:localizedTitle];
+ }
+ if (recursive) {
+ [self localizeMenu:[menuItem submenu] recursively:recursive];
+ }
+ }
+ }
+}
+@end
diff --git a/AppKit/GTMUILocalizerTest.h b/AppKit/GTMUILocalizerTest.h
new file mode 100644
index 0000000..c3d1f36
--- /dev/null
+++ b/AppKit/GTMUILocalizerTest.h
@@ -0,0 +1,37 @@
+//
+// GTMUILocalizerTest.h
+//
+// Copyright 2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy
+// of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+//
+
+#import <Cocoa/Cocoa.h>
+#import "GTMDefines.h"
+
+@interface GTMUILocalizerTestWindowController : NSWindowController {
+ IBOutlet NSWindow *otherWindow_;
+ IBOutlet NSMenu *otherMenu_;
+}
+- (NSWindow *)otherWindow;
+- (NSMenu *)otherMenu;
+@end
+
+#if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5
+@interface GTMUILocalizerTestViewController : NSViewController {
+ IBOutlet NSView *otherView_;
+}
+- (NSView *)otherView;
+@end
+#endif // MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5
+
diff --git a/AppKit/GTMUILocalizerTest.m b/AppKit/GTMUILocalizerTest.m
new file mode 100644
index 0000000..fb92b52
--- /dev/null
+++ b/AppKit/GTMUILocalizerTest.m
@@ -0,0 +1,98 @@
+//
+// GTMUILocalizerTest.m
+//
+// Copyright 2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy
+// of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+//
+
+#import "GTMSenTestCase.h"
+#import "GTMUILocalizerTest.h"
+#import "GTMNSObject+UnitTesting.h"
+#import "GTMUILocalizer.h"
+
+@interface GTMUILocalizerTest : GTMTestCase
+@end
+
+@implementation GTMUILocalizerTest
+- (void)testWindowLocalization {
+ GTMUILocalizerTestWindowController *controller
+ = [[GTMUILocalizerTestWindowController alloc] init];
+ NSWindow *window = [controller window];
+ STAssertNotNil(window, nil);
+ GTMAssertObjectStateEqualToStateNamed(window,
+ @"GTMUILocalizerWindow1State", nil);
+
+ NSBundle *bundle = [NSBundle bundleForClass:[self class]];
+ GTMUILocalizer *localizer = [[GTMUILocalizer alloc] initWithBundle:bundle];
+ window = [controller otherWindow];
+ STAssertNotNil(window, nil);
+ [localizer localizeObject:window recursively:YES];
+ GTMAssertObjectStateEqualToStateNamed(window,
+ @"GTMUILocalizerWindow2State", nil);
+ NSMenu *menu = [controller otherMenu];
+ STAssertNotNil(menu, nil);
+ [localizer localizeObject:menu recursively:YES];
+ GTMAssertObjectStateEqualToStateNamed(menu,
+ @"GTMUILocalizerMenuState", nil);
+ [localizer release];
+ [controller release];
+}
+
+#if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5
+- (void)testViewLocalization {
+ GTMUILocalizerTestViewController *controller
+ = [[GTMUILocalizerTestViewController alloc] init];
+ NSView *view = [controller view];
+ STAssertNotNil(view, nil);
+ GTMAssertObjectStateEqualToStateNamed(view,
+ @"GTMUILocalizerView1State", nil);
+
+ NSBundle *bundle = [NSBundle bundleForClass:[self class]];
+ GTMUILocalizer *localizer = [[GTMUILocalizer alloc] initWithBundle:bundle];
+ view = [controller otherView];
+ STAssertNotNil(view, nil);
+ [localizer localizeObject:view recursively:YES];
+ GTMAssertObjectStateEqualToStateNamed(view, @"GTMUILocalizerView2State", nil);
+ [localizer release];
+ [controller release];
+}
+#endif // MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5
+@end
+
+@implementation GTMUILocalizerTestWindowController
+- (id)init {
+ return [self initWithWindowNibName:@"GTMUILocalizerTestWindow"];
+}
+
+- (NSWindow *)otherWindow {
+ return otherWindow_;
+}
+
+- (NSMenu *)otherMenu {
+ return otherMenu_;
+}
+@end
+
+#if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5
+@implementation GTMUILocalizerTestViewController
+- (id)init {
+ NSBundle *bundle = [NSBundle bundleForClass:[self class]];
+ return [self initWithNibName:@"GTMUILocalizerTestView" bundle:bundle];
+}
+
+- (NSView *)otherView {
+ return otherView_;
+}
+@end
+#endif
diff --git a/AppKit/GTMUILocalizerTestView.xib b/AppKit/GTMUILocalizerTestView.xib
new file mode 100644
index 0000000..d1a9126
--- /dev/null
+++ b/AppKit/GTMUILocalizerTestView.xib
@@ -0,0 +1,481 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<archive type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="7.03">
+ <data>
+ <int key="IBDocument.SystemTarget">1050</int>
+ <string key="IBDocument.SystemVersion">9G55</string>
+ <string key="IBDocument.InterfaceBuilderVersion">677</string>
+ <string key="IBDocument.AppKitVersion">949.43</string>
+ <string key="IBDocument.HIToolboxVersion">353.00</string>
+ <object class="NSMutableArray" key="IBDocument.EditedObjectIDs">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <integer value="1"/>
+ <integer value="3"/>
+ </object>
+ <object class="NSArray" key="IBDocument.PluginDependencies">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ </object>
+ <object class="NSMutableDictionary" key="IBDocument.Metadata">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSArray" key="dict.sortedKeys">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ </object>
+ <object class="NSMutableArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ </object>
+ </object>
+ <object class="NSMutableArray" key="IBDocument.RootObjects" id="1000">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSCustomObject" id="1001">
+ <string key="NSClassName">GTMUILocalizerTestViewController</string>
+ </object>
+ <object class="NSCustomObject" id="1003">
+ <string key="NSClassName">FirstResponder</string>
+ </object>
+ <object class="NSCustomObject" id="1004">
+ <string key="NSClassName">NSApplication</string>
+ </object>
+ <object class="NSCustomView" id="1005">
+ <reference key="NSNextResponder"/>
+ <int key="NSvFlags">268</int>
+ <object class="NSMutableArray" key="NSSubviews">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSButton" id="68586906">
+ <reference key="NSNextResponder" ref="1005"/>
+ <int key="NSvFlags">268</int>
+ <string key="NSFrame">{{179, 180}, {96, 32}}</string>
+ <reference key="NSSuperview" ref="1005"/>
+ <bool key="NSEnabled">YES</bool>
+ <object class="NSButtonCell" key="NSCell" id="53479329">
+ <int key="NSCellFlags">67239424</int>
+ <int key="NSCellFlags2">134217728</int>
+ <string key="NSContents">^Button</string>
+ <object class="NSFont" key="NSSupport" id="887836340">
+ <string key="NSName">LucidaGrande</string>
+ <double key="NSSize">1.300000e+01</double>
+ <int key="NSfFlags">1044</int>
+ </object>
+ <reference key="NSControlView" ref="68586906"/>
+ <int key="NSButtonFlags">-2038284033</int>
+ <int key="NSButtonFlags2">129</int>
+ <string key="NSAlternateContents"/>
+ <string key="NSKeyEquivalent"/>
+ <int key="NSPeriodicDelay">200</int>
+ <int key="NSPeriodicInterval">25</int>
+ </object>
+ </object>
+ <object class="NSTextField" id="326674882">
+ <reference key="NSNextResponder" ref="1005"/>
+ <int key="NSvFlags">268</int>
+ <string key="NSFrame">{{295, 132}, {129, 17}}</string>
+ <reference key="NSSuperview" ref="1005"/>
+ <bool key="NSEnabled">YES</bool>
+ <object class="NSTextFieldCell" key="NSCell" id="1000174622">
+ <int key="NSCellFlags">68288064</int>
+ <int key="NSCellFlags2">272630784</int>
+ <string key="NSContents">^Polly want a caret?</string>
+ <reference key="NSSupport" ref="887836340"/>
+ <reference key="NSControlView" ref="326674882"/>
+ <object class="NSColor" key="NSBackgroundColor">
+ <int key="NSColorSpace">6</int>
+ <string key="NSCatalogName">System</string>
+ <string key="NSColorName">controlColor</string>
+ <object class="NSColor" key="NSColor">
+ <int key="NSColorSpace">3</int>
+ <bytes key="NSWhite">MC42NjY2NjY2OQA</bytes>
+ </object>
+ </object>
+ <object class="NSColor" key="NSTextColor">
+ <int key="NSColorSpace">6</int>
+ <string key="NSCatalogName">System</string>
+ <string key="NSColorName">controlTextColor</string>
+ <object class="NSColor" key="NSColor">
+ <int key="NSColorSpace">3</int>
+ <bytes key="NSWhite">MAA</bytes>
+ </object>
+ </object>
+ </object>
+ </object>
+ </object>
+ <string key="NSFrameSize">{480, 272}</string>
+ <reference key="NSSuperview"/>
+ <string key="NSClassName">NSView</string>
+ </object>
+ <object class="NSCustomView" id="1006142900">
+ <reference key="NSNextResponder"/>
+ <int key="NSvFlags">268</int>
+ <object class="NSMutableArray" key="NSSubviews">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSButton" id="707802764">
+ <reference key="NSNextResponder" ref="1006142900"/>
+ <int key="NSvFlags">268</int>
+ <string key="NSFrame">{{48, 35}, {96, 32}}</string>
+ <reference key="NSSuperview" ref="1006142900"/>
+ <bool key="NSEnabled">YES</bool>
+ <object class="NSButtonCell" key="NSCell" id="1059575151">
+ <int key="NSCellFlags">67239424</int>
+ <int key="NSCellFlags2">134217728</int>
+ <string key="NSContents">^Button</string>
+ <reference key="NSSupport" ref="887836340"/>
+ <reference key="NSControlView" ref="707802764"/>
+ <int key="NSButtonFlags">-2038284033</int>
+ <int key="NSButtonFlags2">129</int>
+ <string key="NSAlternateContents"/>
+ <string key="NSKeyEquivalent"/>
+ <int key="NSPeriodicDelay">200</int>
+ <int key="NSPeriodicInterval">25</int>
+ </object>
+ </object>
+ </object>
+ <string key="NSFrameSize">{163, 96}</string>
+ <reference key="NSSuperview"/>
+ <string key="NSClassName">NSView</string>
+ </object>
+ <object class="NSCustomObject" id="10941456">
+ <string key="NSClassName">GTMUILocalizer</string>
+ </object>
+ </object>
+ <object class="IBObjectContainer" key="IBDocument.Objects">
+ <object class="NSMutableArray" key="connectionRecords">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="IBConnectionRecord">
+ <object class="IBOutletConnection" key="connection">
+ <string key="label">view</string>
+ <reference key="source" ref="1001"/>
+ <reference key="destination" ref="1005"/>
+ </object>
+ <int key="connectionID">11</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBOutletConnection" key="connection">
+ <string key="label">otherView_</string>
+ <reference key="source" ref="1001"/>
+ <reference key="destination" ref="1006142900"/>
+ </object>
+ <int key="connectionID">12</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBOutletConnection" key="connection">
+ <string key="label">owner_</string>
+ <reference key="source" ref="10941456"/>
+ <reference key="destination" ref="1001"/>
+ </object>
+ <int key="connectionID">14</int>
+ </object>
+ </object>
+ <object class="IBMutableOrderedSet" key="objectRecords">
+ <object class="NSArray" key="orderedObjects">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="IBObjectRecord">
+ <int key="objectID">0</int>
+ <object class="NSArray" key="object" id="1002">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ </object>
+ <reference key="children" ref="1000"/>
+ <nil key="parent"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">-2</int>
+ <reference key="object" ref="1001"/>
+ <reference key="parent" ref="1002"/>
+ <string type="base64-UTF8" key="objectName">RmlsZSdzIE93bmVyA</string>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">-1</int>
+ <reference key="object" ref="1003"/>
+ <reference key="parent" ref="1002"/>
+ <string key="objectName">First Responder</string>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">-3</int>
+ <reference key="object" ref="1004"/>
+ <reference key="parent" ref="1002"/>
+ <string key="objectName">Application</string>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">1</int>
+ <reference key="object" ref="1005"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="68586906"/>
+ <reference ref="326674882"/>
+ </object>
+ <reference key="parent" ref="1002"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">3</int>
+ <reference key="object" ref="1006142900"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="707802764"/>
+ </object>
+ <reference key="parent" ref="1002"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">5</int>
+ <reference key="object" ref="707802764"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="1059575151"/>
+ </object>
+ <reference key="parent" ref="1006142900"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">6</int>
+ <reference key="object" ref="1059575151"/>
+ <reference key="parent" ref="707802764"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">7</int>
+ <reference key="object" ref="68586906"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="53479329"/>
+ </object>
+ <reference key="parent" ref="1005"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">8</int>
+ <reference key="object" ref="53479329"/>
+ <reference key="parent" ref="68586906"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">13</int>
+ <reference key="object" ref="10941456"/>
+ <reference key="parent" ref="1002"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">15</int>
+ <reference key="object" ref="326674882"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="1000174622"/>
+ </object>
+ <reference key="parent" ref="1005"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">16</int>
+ <reference key="object" ref="1000174622"/>
+ <reference key="parent" ref="326674882"/>
+ </object>
+ </object>
+ </object>
+ <object class="NSMutableDictionary" key="flattenedProperties">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSMutableArray" key="dict.sortedKeys">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>-1.IBPluginDependency</string>
+ <string>-2.IBPluginDependency</string>
+ <string>-3.IBPluginDependency</string>
+ <string>1.IBEditorWindowLastContentRect</string>
+ <string>1.IBPluginDependency</string>
+ <string>1.WindowOrigin</string>
+ <string>1.editorWindowContentRectSynchronizationRect</string>
+ <string>13.IBPluginDependency</string>
+ <string>15.IBPluginDependency</string>
+ <string>16.IBPluginDependency</string>
+ <string>3.IBEditorWindowLastContentRect</string>
+ <string>3.IBPluginDependency</string>
+ <string>5.IBAttributePlaceholdersKey</string>
+ <string>5.IBPluginDependency</string>
+ <string>6.IBPluginDependency</string>
+ <string>7.IBPluginDependency</string>
+ <string>8.IBPluginDependency</string>
+ </object>
+ <object class="NSMutableArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>{{180, 852}, {480, 272}}</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>{628, 654}</string>
+ <string>{{357, 416}, {480, 272}}</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>{{715, 1101}, {163, 96}}</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <object class="NSMutableDictionary">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSMutableArray" key="dict.sortedKeys">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>AccessibilityDescription</string>
+ <string>AccessibilityHelp</string>
+ <string>ToolTip</string>
+ </object>
+ <object class="NSMutableArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="IBAccessibilityAttribute">
+ <string key="name">AccessibilityDescription</string>
+ <reference key="object" ref="707802764"/>
+ <string key="accessibilityValue">^AccessibilityDescription</string>
+ </object>
+ <object class="IBAccessibilityAttribute">
+ <string key="name">AccessibilityHelp</string>
+ <reference key="object" ref="707802764"/>
+ <string key="accessibilityValue">^AccessibilityHelp</string>
+ </object>
+ <object class="IBToolTipAttribute">
+ <string key="name">ToolTip</string>
+ <reference key="object" ref="707802764"/>
+ <string key="toolTip">^ToolTip</string>
+ </object>
+ </object>
+ </object>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ </object>
+ </object>
+ <object class="NSMutableDictionary" key="unlocalizedProperties">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSArray" key="dict.sortedKeys">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ </object>
+ <object class="NSMutableArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ </object>
+ </object>
+ <nil key="activeLocalization"/>
+ <object class="NSMutableDictionary" key="localizations">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSArray" key="dict.sortedKeys">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ </object>
+ <object class="NSMutableArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ </object>
+ </object>
+ <nil key="sourceID"/>
+ <int key="maxID">16</int>
+ </object>
+ <object class="IBClassDescriber" key="IBDocument.Classes">
+ <object class="NSMutableArray" key="referencedPartialClassDescriptions">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="IBPartialClassDescription">
+ <string key="className">GTMUILocalizer</string>
+ <string key="superclassName">NSObject</string>
+ <object class="NSMutableDictionary" key="outlets">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSMutableArray" key="dict.sortedKeys">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>otherObjectToLocalize_</string>
+ <string>owner_</string>
+ <string>yetAnotherObjectToLocalize_</string>
+ </object>
+ <object class="NSMutableArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>id</string>
+ <string>id</string>
+ <string>id</string>
+ </object>
+ </object>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">AppKit/GTMUILocalizer.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">GTMUILocalizerTestViewController</string>
+ <string key="superclassName">NSViewController</string>
+ <object class="NSMutableDictionary" key="outlets">
+ <string key="NS.key.0">otherView_</string>
+ <string key="NS.object.0">NSView</string>
+ </object>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">AppKit/GTMUILocalizerTest.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSApplication</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier" id="81743917">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">UnitTesting/GTMAppKit+UnitTesting.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSCell</string>
+ <reference key="sourceIdentifier" ref="81743917"/>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSControl</string>
+ <reference key="sourceIdentifier" ref="81743917"/>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSMenu</string>
+ <reference key="sourceIdentifier" ref="81743917"/>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">AppKit/GTMCarbonEvent.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">AppKit/GTMDelegatingTableColumn.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">Foundation/GTMHTTPServer.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">Foundation/GTMNSAppleEventDescriptor+Foundation.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">Foundation/GTMNSObject+KeyValueObserving.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">UnitTesting/GTMCALayer+UnitTesting.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">UnitTesting/GTMNSObject+BindingUnitTesting.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">UnitTesting/GTMNSObject+UnitTesting.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSTextField</string>
+ <reference key="sourceIdentifier" ref="81743917"/>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSView</string>
+ <reference key="sourceIdentifier" ref="81743917"/>
+ </object>
+ </object>
+ </object>
+ <int key="IBDocument.localizationMode">0</int>
+ <string key="IBDocument.LastKnownRelativeProjectPath">../GTM.xcodeproj</string>
+ <int key="IBDocument.defaultPropertyAccessControl">3</int>
+ </data>
+</archive>
diff --git a/AppKit/GTMUILocalizerTestWindow.xib b/AppKit/GTMUILocalizerTestWindow.xib
new file mode 100644
index 0000000..9584b76
--- /dev/null
+++ b/AppKit/GTMUILocalizerTestWindow.xib
@@ -0,0 +1,695 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<archive type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="7.03">
+ <data>
+ <int key="IBDocument.SystemTarget">1050</int>
+ <string key="IBDocument.SystemVersion">9G55</string>
+ <string key="IBDocument.InterfaceBuilderVersion">677</string>
+ <string key="IBDocument.AppKitVersion">949.43</string>
+ <string key="IBDocument.HIToolboxVersion">353.00</string>
+ <object class="NSMutableArray" key="IBDocument.EditedObjectIDs">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <integer value="22"/>
+ </object>
+ <object class="NSArray" key="IBDocument.PluginDependencies">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ </object>
+ <object class="NSMutableDictionary" key="IBDocument.Metadata">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSArray" key="dict.sortedKeys">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ </object>
+ <object class="NSMutableArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ </object>
+ </object>
+ <object class="NSMutableArray" key="IBDocument.RootObjects" id="1000">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSCustomObject" id="1001">
+ <string key="NSClassName">GTMUILocalizerTestWindowController</string>
+ </object>
+ <object class="NSCustomObject" id="1003">
+ <string key="NSClassName">FirstResponder</string>
+ </object>
+ <object class="NSCustomObject" id="1004">
+ <string key="NSClassName">NSApplication</string>
+ </object>
+ <object class="NSWindowTemplate" id="1005">
+ <int key="NSWindowStyleMask">15</int>
+ <int key="NSWindowBacking">2</int>
+ <string key="NSWindowRect">{{196, 240}, {480, 270}}</string>
+ <int key="NSWTFlags">536870912</int>
+ <string key="NSWindowTitle">Window</string>
+ <string key="NSWindowClass">NSWindow</string>
+ <nil key="NSViewClass"/>
+ <string key="NSWindowContentMaxSize">{3.40282e+38, 3.40282e+38}</string>
+ <object class="NSView" key="NSWindowView" id="1006">
+ <nil key="NSNextResponder"/>
+ <int key="NSvFlags">256</int>
+ <object class="NSMutableArray" key="NSSubviews">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSButton" id="934296854">
+ <reference key="NSNextResponder" ref="1006"/>
+ <int key="NSvFlags">268</int>
+ <string key="NSFrame">{{30, 205}, {100, 32}}</string>
+ <reference key="NSSuperview" ref="1006"/>
+ <bool key="NSEnabled">YES</bool>
+ <object class="NSButtonCell" key="NSCell" id="768899742">
+ <int key="NSCellFlags">67239424</int>
+ <int key="NSCellFlags2">134217728</int>
+ <string key="NSContents">^Button1</string>
+ <object class="NSFont" key="NSSupport" id="168773824">
+ <string key="NSName">LucidaGrande</string>
+ <double key="NSSize">1.300000e+01</double>
+ <int key="NSfFlags">1044</int>
+ </object>
+ <reference key="NSControlView" ref="934296854"/>
+ <int key="NSButtonFlags">-2038284033</int>
+ <int key="NSButtonFlags2">129</int>
+ <string key="NSAlternateContents"/>
+ <string key="NSKeyEquivalent"/>
+ <int key="NSPeriodicDelay">200</int>
+ <int key="NSPeriodicInterval">25</int>
+ </object>
+ </object>
+ <object class="NSButton" id="657027262">
+ <reference key="NSNextResponder" ref="1006"/>
+ <int key="NSvFlags">268</int>
+ <string key="NSFrame">{{30, 173}, {100, 32}}</string>
+ <reference key="NSSuperview" ref="1006"/>
+ <bool key="NSEnabled">YES</bool>
+ <object class="NSButtonCell" key="NSCell" id="105732693">
+ <int key="NSCellFlags">67239424</int>
+ <int key="NSCellFlags2">134217728</int>
+ <string key="NSContents">^Button2</string>
+ <reference key="NSSupport" ref="168773824"/>
+ <reference key="NSControlView" ref="657027262"/>
+ <int key="NSButtonFlags">-2038284033</int>
+ <int key="NSButtonFlags2">129</int>
+ <string key="NSAlternateContents"/>
+ <string key="NSKeyEquivalent"/>
+ <int key="NSPeriodicDelay">200</int>
+ <int key="NSPeriodicInterval">25</int>
+ </object>
+ </object>
+ <object class="NSTabView" id="1054080138">
+ <reference key="NSNextResponder" ref="1006"/>
+ <int key="NSvFlags">12</int>
+ <string key="NSFrame">{{161, 115}, {177, 124}}</string>
+ <reference key="NSSuperview" ref="1006"/>
+ <object class="NSMutableArray" key="NSTabViewItems">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSTabViewItem" id="567210703">
+ <string key="NSIdentifier">1</string>
+ <object class="NSView" key="NSView" id="837485648">
+ <nil key="NSNextResponder"/>
+ <int key="NSvFlags">256</int>
+ <string key="NSFrame">{{10, 33}, {157, 78}}</string>
+ </object>
+ <string key="NSLabel">^Tab1</string>
+ <object class="NSColor" key="NSColor" id="1000162704">
+ <int key="NSColorSpace">6</int>
+ <string key="NSCatalogName">System</string>
+ <string key="NSColorName">controlColor</string>
+ <object class="NSColor" key="NSColor">
+ <int key="NSColorSpace">3</int>
+ <bytes key="NSWhite">MC42NjY2NjY2OQA</bytes>
+ </object>
+ </object>
+ <reference key="NSTabView" ref="1054080138"/>
+ </object>
+ <object class="NSTabViewItem" id="786693096">
+ <string key="NSIdentifier">2</string>
+ <object class="NSView" key="NSView" id="75519688">
+ <reference key="NSNextResponder" ref="1054080138"/>
+ <int key="NSvFlags">256</int>
+ <string key="NSFrame">{{10, 33}, {157, 78}}</string>
+ <reference key="NSSuperview" ref="1054080138"/>
+ </object>
+ <string key="NSLabel">^Tab2</string>
+ <reference key="NSColor" ref="1000162704"/>
+ <reference key="NSTabView" ref="1054080138"/>
+ </object>
+ </object>
+ <reference key="NSSelectedTabViewItem" ref="786693096"/>
+ <reference key="NSFont" ref="168773824"/>
+ <int key="NSTvFlags">0</int>
+ <bool key="NSAllowTruncatedLabels">YES</bool>
+ <bool key="NSDrawsBackground">YES</bool>
+ <object class="NSMutableArray" key="NSSubviews">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="75519688"/>
+ </object>
+ </object>
+ <object class="NSTextField" id="39833761">
+ <reference key="NSNextResponder" ref="1006"/>
+ <int key="NSvFlags">268</int>
+ <string key="NSFrame">{{71, 61}, {74, 17}}</string>
+ <reference key="NSSuperview" ref="1006"/>
+ <bool key="NSEnabled">YES</bool>
+ <object class="NSTextFieldCell" key="NSCell" id="569396622">
+ <int key="NSCellFlags">68288064</int>
+ <int key="NSCellFlags2">272630784</int>
+ <string key="NSContents">^Label1</string>
+ <reference key="NSSupport" ref="168773824"/>
+ <reference key="NSControlView" ref="39833761"/>
+ <reference key="NSBackgroundColor" ref="1000162704"/>
+ <object class="NSColor" key="NSTextColor" id="50010148">
+ <int key="NSColorSpace">6</int>
+ <string key="NSCatalogName">System</string>
+ <string key="NSColorName">controlTextColor</string>
+ <object class="NSColor" key="NSColor">
+ <int key="NSColorSpace">3</int>
+ <bytes key="NSWhite">MAA</bytes>
+ </object>
+ </object>
+ </object>
+ </object>
+ <object class="NSTextField" id="18987080">
+ <reference key="NSNextResponder" ref="1006"/>
+ <int key="NSvFlags">268</int>
+ <string key="NSFrame">{{153, 66}, {72, 17}}</string>
+ <reference key="NSSuperview" ref="1006"/>
+ <bool key="NSEnabled">YES</bool>
+ <object class="NSTextFieldCell" key="NSCell" id="851209037">
+ <int key="NSCellFlags">68288064</int>
+ <int key="NSCellFlags2">272630784</int>
+ <string key="NSContents">^Label2</string>
+ <reference key="NSSupport" ref="168773824"/>
+ <reference key="NSControlView" ref="18987080"/>
+ <reference key="NSBackgroundColor" ref="1000162704"/>
+ <reference key="NSTextColor" ref="50010148"/>
+ </object>
+ </object>
+ </object>
+ <string key="NSFrameSize">{480, 270}</string>
+ </object>
+ <string key="NSScreenRect">{{0, 0}, {1680, 1028}}</string>
+ <string key="NSMaxSize">{3.40282e+38, 3.40282e+38}</string>
+ </object>
+ <object class="NSWindowTemplate" id="655734033">
+ <int key="NSWindowStyleMask">15</int>
+ <int key="NSWindowBacking">2</int>
+ <string key="NSWindowRect">{{196, 240}, {480, 270}}</string>
+ <int key="NSWTFlags">603979776</int>
+ <string key="NSWindowTitle">^WindowTest</string>
+ <string key="NSWindowClass">NSWindow</string>
+ <nil key="NSViewClass"/>
+ <string key="NSWindowContentMaxSize">{3.40282e+38, 3.40282e+38}</string>
+ <object class="NSView" key="NSWindowView" id="769884725">
+ <reference key="NSNextResponder"/>
+ <int key="NSvFlags">256</int>
+ <string key="NSFrameSize">{480, 270}</string>
+ <reference key="NSSuperview"/>
+ </object>
+ <string key="NSScreenRect">{{0, 0}, {2560, 1578}}</string>
+ <string key="NSMaxSize">{3.40282e+38, 3.40282e+38}</string>
+ </object>
+ <object class="NSMenu" id="526087585">
+ <string key="NSTitle">^MenuTest</string>
+ <object class="NSMutableArray" key="NSMenuItems">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSMenuItem" id="383489140">
+ <reference key="NSMenu" ref="526087585"/>
+ <string key="NSTitle">^MenuItemTest</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <object class="NSCustomResource" key="NSOnImage">
+ <string key="NSClassName">NSImage</string>
+ <string key="NSResourceName">NSMenuCheckmark</string>
+ </object>
+ <object class="NSCustomResource" key="NSMixedImage">
+ <string key="NSClassName">NSImage</string>
+ <string key="NSResourceName">NSMenuMixedState</string>
+ </object>
+ </object>
+ </object>
+ </object>
+ <object class="NSCustomObject" id="92471218">
+ <string key="NSClassName">GTMUILocalizer</string>
+ </object>
+ </object>
+ <object class="IBObjectContainer" key="IBDocument.Objects">
+ <object class="NSMutableArray" key="connectionRecords">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="IBConnectionRecord">
+ <object class="IBOutletConnection" key="connection">
+ <string key="label">window</string>
+ <reference key="source" ref="1001"/>
+ <reference key="destination" ref="1005"/>
+ </object>
+ <int key="connectionID">3</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBOutletConnection" key="connection">
+ <string key="label">owner_</string>
+ <reference key="source" ref="92471218"/>
+ <reference key="destination" ref="1001"/>
+ </object>
+ <int key="connectionID">32</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBOutletConnection" key="connection">
+ <string key="label">otherWindow_</string>
+ <reference key="source" ref="1001"/>
+ <reference key="destination" ref="655734033"/>
+ </object>
+ <int key="connectionID">33</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBOutletConnection" key="connection">
+ <string key="label">otherMenu_</string>
+ <reference key="source" ref="1001"/>
+ <reference key="destination" ref="526087585"/>
+ </object>
+ <int key="connectionID">34</int>
+ </object>
+ </object>
+ <object class="IBMutableOrderedSet" key="objectRecords">
+ <object class="NSArray" key="orderedObjects">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="IBObjectRecord">
+ <int key="objectID">0</int>
+ <object class="NSArray" key="object" id="1002">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ </object>
+ <reference key="children" ref="1000"/>
+ <nil key="parent"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">-2</int>
+ <reference key="object" ref="1001"/>
+ <reference key="parent" ref="1002"/>
+ <string type="base64-UTF8" key="objectName">RmlsZSdzIE93bmVyA</string>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">-1</int>
+ <reference key="object" ref="1003"/>
+ <reference key="parent" ref="1002"/>
+ <string key="objectName">First Responder</string>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">-3</int>
+ <reference key="object" ref="1004"/>
+ <reference key="parent" ref="1002"/>
+ <string key="objectName">Application</string>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">1</int>
+ <reference key="object" ref="1005"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="1006"/>
+ </object>
+ <reference key="parent" ref="1002"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">2</int>
+ <reference key="object" ref="1006"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="934296854"/>
+ <reference ref="657027262"/>
+ <reference ref="1054080138"/>
+ <reference ref="18987080"/>
+ <reference ref="39833761"/>
+ </object>
+ <reference key="parent" ref="1005"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">4</int>
+ <reference key="object" ref="934296854"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="768899742"/>
+ </object>
+ <reference key="parent" ref="1006"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">5</int>
+ <reference key="object" ref="768899742"/>
+ <reference key="parent" ref="934296854"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">6</int>
+ <reference key="object" ref="657027262"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="105732693"/>
+ </object>
+ <reference key="parent" ref="1006"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">7</int>
+ <reference key="object" ref="105732693"/>
+ <reference key="parent" ref="657027262"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">8</int>
+ <reference key="object" ref="1054080138"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="567210703"/>
+ <reference ref="786693096"/>
+ </object>
+ <reference key="parent" ref="1006"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">9</int>
+ <reference key="object" ref="567210703"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="837485648"/>
+ </object>
+ <reference key="parent" ref="1054080138"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">10</int>
+ <reference key="object" ref="786693096"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="75519688"/>
+ </object>
+ <reference key="parent" ref="1054080138"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">11</int>
+ <reference key="object" ref="75519688"/>
+ <reference key="parent" ref="786693096"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">12</int>
+ <reference key="object" ref="837485648"/>
+ <reference key="parent" ref="567210703"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">13</int>
+ <reference key="object" ref="39833761"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="569396622"/>
+ </object>
+ <reference key="parent" ref="1006"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">14</int>
+ <reference key="object" ref="569396622"/>
+ <reference key="parent" ref="39833761"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">15</int>
+ <reference key="object" ref="18987080"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="851209037"/>
+ </object>
+ <reference key="parent" ref="1006"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">16</int>
+ <reference key="object" ref="851209037"/>
+ <reference key="parent" ref="18987080"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">22</int>
+ <reference key="object" ref="655734033"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="769884725"/>
+ </object>
+ <reference key="parent" ref="1002"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">23</int>
+ <reference key="object" ref="769884725"/>
+ <reference key="parent" ref="655734033"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">24</int>
+ <reference key="object" ref="526087585"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="383489140"/>
+ </object>
+ <reference key="parent" ref="1002"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">25</int>
+ <reference key="object" ref="383489140"/>
+ <reference key="parent" ref="526087585"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">30</int>
+ <reference key="object" ref="92471218"/>
+ <reference key="parent" ref="1002"/>
+ </object>
+ </object>
+ </object>
+ <object class="NSMutableDictionary" key="flattenedProperties">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSMutableArray" key="dict.sortedKeys">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>-1.IBPluginDependency</string>
+ <string>-2.IBPluginDependency</string>
+ <string>-3.IBPluginDependency</string>
+ <string>1.IBEditorWindowLastContentRect</string>
+ <string>1.IBPluginDependency</string>
+ <string>1.IBWindowTemplateEditedContentRect</string>
+ <string>1.NSWindowTemplate.visibleAtLaunch</string>
+ <string>1.WindowOrigin</string>
+ <string>1.editorWindowContentRectSynchronizationRect</string>
+ <string>10.IBPluginDependency</string>
+ <string>11.IBPluginDependency</string>
+ <string>12.IBPluginDependency</string>
+ <string>13.IBPluginDependency</string>
+ <string>14.IBPluginDependency</string>
+ <string>15.IBPluginDependency</string>
+ <string>16.IBPluginDependency</string>
+ <string>2.IBPluginDependency</string>
+ <string>22.IBEditorWindowLastContentRect</string>
+ <string>22.IBPluginDependency</string>
+ <string>22.IBWindowTemplateEditedContentRect</string>
+ <string>22.NSWindowTemplate.visibleAtLaunch</string>
+ <string>23.IBPluginDependency</string>
+ <string>24.IBEditorWindowLastContentRect</string>
+ <string>24.IBPluginDependency</string>
+ <string>25.IBPluginDependency</string>
+ <string>30.IBPluginDependency</string>
+ <string>4.IBPluginDependency</string>
+ <string>5.IBPluginDependency</string>
+ <string>6.IBPluginDependency</string>
+ <string>7.IBPluginDependency</string>
+ <string>8.IBPluginDependency</string>
+ <string>9.IBPluginDependency</string>
+ </object>
+ <object class="NSMutableArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>{{174, 930}, {480, 270}}</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>{{174, 930}, {480, 270}}</string>
+ <boolean value="NO" id="6"/>
+ <string>{196, 240}</string>
+ <string>{{357, 418}, {480, 270}}</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>{{533, 1286}, {480, 270}}</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>{{533, 1286}, {480, 270}}</string>
+ <reference ref="6"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>{{126, 673}, {161, 23}}</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ </object>
+ </object>
+ <object class="NSMutableDictionary" key="unlocalizedProperties">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSArray" key="dict.sortedKeys">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ </object>
+ <object class="NSMutableArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ </object>
+ </object>
+ <nil key="activeLocalization"/>
+ <object class="NSMutableDictionary" key="localizations">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSArray" key="dict.sortedKeys">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ </object>
+ <object class="NSMutableArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ </object>
+ </object>
+ <nil key="sourceID"/>
+ <int key="maxID">34</int>
+ </object>
+ <object class="IBClassDescriber" key="IBDocument.Classes">
+ <object class="NSMutableArray" key="referencedPartialClassDescriptions">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="IBPartialClassDescription">
+ <string key="className">GTMUILocalizer</string>
+ <string key="superclassName">NSObject</string>
+ <object class="NSMutableDictionary" key="outlets">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSMutableArray" key="dict.sortedKeys">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>otherObjectToLocalize_</string>
+ <string>owner_</string>
+ <string>yetAnotherObjectToLocalize_</string>
+ </object>
+ <object class="NSMutableArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>id</string>
+ <string>id</string>
+ <string>id</string>
+ </object>
+ </object>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">AppKit/GTMUILocalizer.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">GTMUILocalizerTestWindowController</string>
+ <string key="superclassName">NSWindowController</string>
+ <object class="NSMutableDictionary" key="outlets">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSMutableArray" key="dict.sortedKeys">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>otherMenu_</string>
+ <string>otherWindow_</string>
+ </object>
+ <object class="NSMutableArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>NSMenu</string>
+ <string>NSWindow</string>
+ </object>
+ </object>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">AppKit/GTMUILocalizerTest.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSApplication</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier" id="485269976">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">UnitTesting/GTMAppKit+UnitTesting.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSCell</string>
+ <reference key="sourceIdentifier" ref="485269976"/>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSControl</string>
+ <reference key="sourceIdentifier" ref="485269976"/>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSMenu</string>
+ <reference key="sourceIdentifier" ref="485269976"/>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSMenuItem</string>
+ <reference key="sourceIdentifier" ref="485269976"/>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">AppKit/GTMCarbonEvent.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">AppKit/GTMDelegatingTableColumn.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">Foundation/GTMHTTPServer.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">Foundation/GTMNSAppleEventDescriptor+Foundation.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">Foundation/GTMNSObject+KeyValueObserving.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">UnitTesting/GTMCALayer+UnitTesting.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">UnitTesting/GTMNSObject+BindingUnitTesting.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">UnitTesting/GTMNSObject+UnitTesting.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSTabView</string>
+ <reference key="sourceIdentifier" ref="485269976"/>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSTabViewItem</string>
+ <reference key="sourceIdentifier" ref="485269976"/>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSTextField</string>
+ <reference key="sourceIdentifier" ref="485269976"/>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSView</string>
+ <reference key="sourceIdentifier" ref="485269976"/>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSWindow</string>
+ <reference key="sourceIdentifier" ref="485269976"/>
+ </object>
+ </object>
+ </object>
+ <int key="IBDocument.localizationMode">0</int>
+ <string key="IBDocument.LastKnownRelativeProjectPath">../GTM.xcodeproj</string>
+ <int key="IBDocument.defaultPropertyAccessControl">3</int>
+ </data>
+</archive>
diff --git a/AppKit/TestData/GTMUILocalizerMenuState.gtmUTState b/AppKit/TestData/GTMUILocalizerMenuState.gtmUTState
new file mode 100644
index 0000000..eb6c495
--- /dev/null
+++ b/AppKit/TestData/GTMUILocalizerMenuState.gtmUTState
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>$GTMArchive</key>
+ <string>GTMUnitTestingArchive</string>
+ <key>$GTMVersion</key>
+ <integer>1</integer>
+ <key>MenuItem 0</key>
+ <dict>
+ <key>MenuItemIndentationLevel</key>
+ <integer>0</integer>
+ <key>MenuItemIsAlternate</key>
+ <false/>
+ <key>MenuItemIsEnabled</key>
+ <true/>
+ <key>MenuItemIsSeparator</key>
+ <false/>
+ <key>MenuItemKeyEquivalent</key>
+ <string></string>
+ <key>MenuItemState</key>
+ <integer>0</integer>
+ <key>MenuItemTag</key>
+ <integer>0</integer>
+ <key>MenuItemTitle</key>
+ <string>Localized Menu Item</string>
+ </dict>
+ <key>MenuTitle</key>
+ <string>Localized Menu</string>
+</dict>
+</plist>
diff --git a/AppKit/TestData/GTMUILocalizerView1State.gtmUTState b/AppKit/TestData/GTMUILocalizerView1State.gtmUTState
new file mode 100644
index 0000000..e56930c
--- /dev/null
+++ b/AppKit/TestData/GTMUILocalizerView1State.gtmUTState
@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>$GTMArchive</key>
+ <string>GTMUnitTestingArchive</string>
+ <key>$GTMVersion</key>
+ <integer>1</integer>
+ <key>ViewIsHidden</key>
+ <false/>
+ <key>ViewSubView 0</key>
+ <dict>
+ <key>ControlIsEnabled</key>
+ <true/>
+ <key>ControlSelectedCell</key>
+ <dict>
+ <key>CellState</key>
+ <integer>0</integer>
+ <key>CellTag</key>
+ <integer>0</integer>
+ <key>CellTitle</key>
+ <string>^Polly want a caret?</string>
+ <key>CellValue</key>
+ <string>^Polly want a caret?</string>
+ </dict>
+ <key>ControlTag</key>
+ <integer>0</integer>
+ <key>ControlType</key>
+ <string>NSTextField</string>
+ <key>ControlValue</key>
+ <string>^Polly want a caret?</string>
+ <key>ViewIsHidden</key>
+ <false/>
+ </dict>
+ <key>ViewSubView 1</key>
+ <dict>
+ <key>ControlIsEnabled</key>
+ <true/>
+ <key>ControlSelectedCell</key>
+ <dict>
+ <key>CellState</key>
+ <integer>0</integer>
+ <key>CellTag</key>
+ <integer>0</integer>
+ <key>CellTitle</key>
+ <string>Localized Button</string>
+ <key>CellValue</key>
+ <string>0</string>
+ </dict>
+ <key>ControlTag</key>
+ <integer>0</integer>
+ <key>ControlType</key>
+ <string>NSButton</string>
+ <key>ControlValue</key>
+ <string>0</string>
+ <key>ViewIsHidden</key>
+ <false/>
+ </dict>
+</dict>
+</plist>
diff --git a/AppKit/TestData/GTMUILocalizerView2State.gtmUTState b/AppKit/TestData/GTMUILocalizerView2State.gtmUTState
new file mode 100644
index 0000000..ff80b28
--- /dev/null
+++ b/AppKit/TestData/GTMUILocalizerView2State.gtmUTState
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>$GTMArchive</key>
+ <string>GTMUnitTestingArchive</string>
+ <key>$GTMVersion</key>
+ <integer>1</integer>
+ <key>ViewIsHidden</key>
+ <false/>
+ <key>ViewSubView 0</key>
+ <dict>
+ <key>ControlIsEnabled</key>
+ <true/>
+ <key>ControlSelectedCell</key>
+ <dict>
+ <key>CellState</key>
+ <integer>0</integer>
+ <key>CellTag</key>
+ <integer>0</integer>
+ <key>CellTitle</key>
+ <string>Localized Button</string>
+ <key>CellValue</key>
+ <string>0</string>
+ </dict>
+ <key>ControlTag</key>
+ <integer>0</integer>
+ <key>ControlType</key>
+ <string>NSButton</string>
+ <key>ControlValue</key>
+ <string>0</string>
+ <key>ViewAccessibilityHelp</key>
+ <string>Localized ToolTip</string>
+ <key>ViewIsHidden</key>
+ <false/>
+ <key>ViewToolTip</key>
+ <string>Localized ToolTip</string>
+ </dict>
+</dict>
+</plist>
diff --git a/AppKit/TestData/GTMUILocalizerWindow1State.gtmUTState b/AppKit/TestData/GTMUILocalizerWindow1State.gtmUTState
new file mode 100644
index 0000000..4884074
--- /dev/null
+++ b/AppKit/TestData/GTMUILocalizerWindow1State.gtmUTState
@@ -0,0 +1,147 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>$GTMArchive</key>
+ <string>GTMUnitTestingArchive</string>
+ <key>$GTMVersion</key>
+ <integer>1</integer>
+ <key>WindowContent</key>
+ <dict>
+ <key>ViewIsHidden</key>
+ <false/>
+ <key>ViewSubView 0</key>
+ <dict>
+ <key>ControlIsEnabled</key>
+ <true/>
+ <key>ControlSelectedCell</key>
+ <dict>
+ <key>CellState</key>
+ <integer>0</integer>
+ <key>CellTag</key>
+ <integer>0</integer>
+ <key>CellTitle</key>
+ <string>Localized Button</string>
+ <key>CellValue</key>
+ <string>0</string>
+ </dict>
+ <key>ControlTag</key>
+ <integer>0</integer>
+ <key>ControlType</key>
+ <string>NSButton</string>
+ <key>ControlValue</key>
+ <string>0</string>
+ <key>ViewIsHidden</key>
+ <false/>
+ </dict>
+ <key>ViewSubView 1</key>
+ <dict>
+ <key>ControlIsEnabled</key>
+ <true/>
+ <key>ControlSelectedCell</key>
+ <dict>
+ <key>CellState</key>
+ <integer>0</integer>
+ <key>CellTag</key>
+ <integer>0</integer>
+ <key>CellTitle</key>
+ <string>^Button2</string>
+ <key>CellValue</key>
+ <string>0</string>
+ </dict>
+ <key>ControlTag</key>
+ <integer>0</integer>
+ <key>ControlType</key>
+ <string>NSButton</string>
+ <key>ControlValue</key>
+ <string>0</string>
+ <key>ViewIsHidden</key>
+ <false/>
+ </dict>
+ <key>ViewSubView 2</key>
+ <dict>
+ <key>TabItem 0</key>
+ <dict>
+ <key>TabLabel</key>
+ <string>Localized Tab</string>
+ <key>TabView</key>
+ <dict>
+ <key>ViewIsHidden</key>
+ <false/>
+ </dict>
+ </dict>
+ <key>TabItem 1</key>
+ <dict>
+ <key>TabLabel</key>
+ <string>^Tab2</string>
+ <key>TabView</key>
+ <dict>
+ <key>ViewIsHidden</key>
+ <false/>
+ </dict>
+ </dict>
+ <key>ViewIsHidden</key>
+ <false/>
+ <key>ViewSubView 0</key>
+ <dict>
+ <key>ViewIsHidden</key>
+ <false/>
+ </dict>
+ </dict>
+ <key>ViewSubView 3</key>
+ <dict>
+ <key>ControlIsEnabled</key>
+ <true/>
+ <key>ControlSelectedCell</key>
+ <dict>
+ <key>CellState</key>
+ <integer>0</integer>
+ <key>CellTag</key>
+ <integer>0</integer>
+ <key>CellTitle</key>
+ <string>Localized Label</string>
+ <key>CellValue</key>
+ <string>Localized Label</string>
+ </dict>
+ <key>ControlTag</key>
+ <integer>0</integer>
+ <key>ControlType</key>
+ <string>NSTextField</string>
+ <key>ControlValue</key>
+ <string>Localized Label</string>
+ <key>ViewIsHidden</key>
+ <false/>
+ </dict>
+ <key>ViewSubView 4</key>
+ <dict>
+ <key>ControlIsEnabled</key>
+ <true/>
+ <key>ControlSelectedCell</key>
+ <dict>
+ <key>CellState</key>
+ <integer>0</integer>
+ <key>CellTag</key>
+ <integer>0</integer>
+ <key>CellTitle</key>
+ <string>^Label2</string>
+ <key>CellValue</key>
+ <string>^Label2</string>
+ </dict>
+ <key>ControlTag</key>
+ <integer>0</integer>
+ <key>ControlType</key>
+ <string>NSTextField</string>
+ <key>ControlValue</key>
+ <string>^Label2</string>
+ <key>ViewIsHidden</key>
+ <false/>
+ </dict>
+ </dict>
+ <key>WindowIsMain</key>
+ <false/>
+ <key>WindowIsVisible</key>
+ <false/>
+ <key>WindowTitle</key>
+ <string>Window</string>
+</dict>
+</plist>
diff --git a/AppKit/TestData/GTMUILocalizerWindow2State.gtmUTState b/AppKit/TestData/GTMUILocalizerWindow2State.gtmUTState
new file mode 100644
index 0000000..fa3e97a
--- /dev/null
+++ b/AppKit/TestData/GTMUILocalizerWindow2State.gtmUTState
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>$GTMArchive</key>
+ <string>GTMUnitTestingArchive</string>
+ <key>$GTMVersion</key>
+ <integer>1</integer>
+ <key>WindowContent</key>
+ <dict>
+ <key>ViewIsHidden</key>
+ <false/>
+ </dict>
+ <key>WindowIsMain</key>
+ <false/>
+ <key>WindowIsVisible</key>
+ <false/>
+ <key>WindowTitle</key>
+ <string>Localized Window</string>
+</dict>
+</plist>
diff --git a/AppKit/TestData/Resources/English.lproj/Localizable.strings b/AppKit/TestData/Resources/English.lproj/Localizable.strings
new file mode 100644
index 0000000..d8e1b4c
--- /dev/null
+++ b/AppKit/TestData/Resources/English.lproj/Localizable.strings
Binary files differ
diff --git a/GTM.xcodeproj/project.pbxproj b/GTM.xcodeproj/project.pbxproj
index c383752..dda57d4 100644
--- a/GTM.xcodeproj/project.pbxproj
+++ b/GTM.xcodeproj/project.pbxproj
@@ -84,6 +84,18 @@
8B3E292F0EEB53F8000681D8 /* GTMCarbonEvent.h in Headers */ = {isa = PBXBuildFile; fileRef = 8B3E292B0EEB53F3000681D8 /* GTMCarbonEvent.h */; settings = {ATTRIBUTES = (Public, ); }; };
8B3E2A3F0EEB564D000681D8 /* Carbon.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F42E09AD0D19A62F00D5DDE0 /* Carbon.framework */; };
8B3E2A410EEB565B000681D8 /* GTMUnitTestingUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B45A2680DA498A0001148C5 /* GTMUnitTestingUtilities.m */; };
+ 8B40994B0F93C5CC00DF540E /* GTMUILocalizer.h in Headers */ = {isa = PBXBuildFile; fileRef = 8B4099490F93C5CC00DF540E /* GTMUILocalizer.h */; };
+ 8B40994C0F93C5CC00DF540E /* GTMUILocalizer.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B40994A0F93C5CC00DF540E /* GTMUILocalizer.m */; };
+ 8B409BC60F94405A00DF540E /* GTMUILocalizerTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B409BC50F94405A00DF540E /* GTMUILocalizerTest.m */; };
+ 8B409BE60F9442C800DF540E /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 8B409BE50F9442C800DF540E /* Localizable.strings */; };
+ 8B409E8C0F952C2C00DF540E /* GTMUILocalizerTestWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = 8B409E8B0F952C2C00DF540E /* GTMUILocalizerTestWindow.xib */; };
+ 8B409E9A0F952CE600DF540E /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 8B409E990F952CE600DF540E /* Localizable.strings */; };
+ 8B409EC40F9530C200DF540E /* GTMUILocalizerWindow1State.gtmUTState in Resources */ = {isa = PBXBuildFile; fileRef = 8B409EC30F9530C200DF540E /* GTMUILocalizerWindow1State.gtmUTState */; };
+ 8B409EE90F95325000DF540E /* GTMUILocalizerWindow2State.gtmUTState in Resources */ = {isa = PBXBuildFile; fileRef = 8B409EE80F95325000DF540E /* GTMUILocalizerWindow2State.gtmUTState */; };
+ 8B409EF40F95331500DF540E /* GTMUILocalizerMenuState.gtmUTState in Resources */ = {isa = PBXBuildFile; fileRef = 8B409EF30F95331500DF540E /* GTMUILocalizerMenuState.gtmUTState */; };
+ 8B409F060F95341E00DF540E /* GTMUILocalizerTestView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 8B409F050F95341E00DF540E /* GTMUILocalizerTestView.xib */; };
+ 8B409F130F95352500DF540E /* GTMUILocalizerView2State.gtmUTState in Resources */ = {isa = PBXBuildFile; fileRef = 8B409F110F95352500DF540E /* GTMUILocalizerView2State.gtmUTState */; };
+ 8B409F140F95352500DF540E /* GTMUILocalizerView1State.gtmUTState in Resources */ = {isa = PBXBuildFile; fileRef = 8B409F120F95352500DF540E /* GTMUILocalizerView1State.gtmUTState */; };
8B45A03A0DA46A2A001148C5 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0867D69BFE84028FC02AAC07 /* Foundation.framework */; };
8B45A0B80DA46A2F001148C5 /* SenTestingKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F42E089B0D199B1800D5DDE0 /* SenTestingKit.framework */; };
8B45A0D50DA46A57001148C5 /* GTMNSObject+UnitTesting.m in Sources */ = {isa = PBXBuildFile; fileRef = F48FE29C0D198D36009257D2 /* GTMNSObject+UnitTesting.m */; };
@@ -382,6 +394,19 @@
8B3E29290EEB53F3000681D8 /* GTMCarbonEventTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMCarbonEventTest.m; sourceTree = "<group>"; };
8B3E292A0EEB53F3000681D8 /* GTMCarbonEvent.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMCarbonEvent.m; sourceTree = "<group>"; };
8B3E292B0EEB53F3000681D8 /* GTMCarbonEvent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTMCarbonEvent.h; sourceTree = "<group>"; };
+ 8B4099490F93C5CC00DF540E /* GTMUILocalizer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTMUILocalizer.h; sourceTree = "<group>"; };
+ 8B40994A0F93C5CC00DF540E /* GTMUILocalizer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMUILocalizer.m; sourceTree = "<group>"; };
+ 8B409BC50F94405A00DF540E /* GTMUILocalizerTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMUILocalizerTest.m; sourceTree = "<group>"; };
+ 8B409BE30F9442C400DF540E /* English */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = English; path = English.lproj/Localizable.strings; sourceTree = "<group>"; };
+ 8B409E3E0F950DE900DF540E /* GTMUILocalizerTest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTMUILocalizerTest.h; sourceTree = "<group>"; };
+ 8B409E8B0F952C2C00DF540E /* GTMUILocalizerTestWindow.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = GTMUILocalizerTestWindow.xib; sourceTree = "<group>"; };
+ 8B409E970F952CD700DF540E /* English */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = English; path = English.lproj/Localizable.strings; sourceTree = "<group>"; };
+ 8B409EC30F9530C200DF540E /* GTMUILocalizerWindow1State.gtmUTState */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = GTMUILocalizerWindow1State.gtmUTState; sourceTree = "<group>"; };
+ 8B409EE80F95325000DF540E /* GTMUILocalizerWindow2State.gtmUTState */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = GTMUILocalizerWindow2State.gtmUTState; sourceTree = "<group>"; };
+ 8B409EF30F95331500DF540E /* GTMUILocalizerMenuState.gtmUTState */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = GTMUILocalizerMenuState.gtmUTState; sourceTree = "<group>"; };
+ 8B409F050F95341E00DF540E /* GTMUILocalizerTestView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = GTMUILocalizerTestView.xib; sourceTree = "<group>"; };
+ 8B409F110F95352500DF540E /* GTMUILocalizerView2State.gtmUTState */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = GTMUILocalizerView2State.gtmUTState; sourceTree = "<group>"; };
+ 8B409F120F95352500DF540E /* GTMUILocalizerView1State.gtmUTState */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = GTMUILocalizerView1State.gtmUTState; sourceTree = "<group>"; };
8B45A0280DA4696C001148C5 /* UnitTest - UnitTesting.octest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "UnitTest - UnitTesting.octest"; sourceTree = BUILT_PRODUCTS_DIR; };
8B45A1990DA46AAA001148C5 /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = /System/Library/Frameworks/QuartzCore.framework; sourceTree = "<absolute>"; };
8B45A2670DA498A0001148C5 /* GTMUnitTestingUtilities.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTMUnitTestingUtilities.h; sourceTree = "<group>"; };
@@ -685,12 +710,21 @@
path = BuildScripts;
sourceTree = "<group>";
};
+ 8B409E960F952CCA00DF540E /* Resources */ = {
+ isa = PBXGroup;
+ children = (
+ 8B409E990F952CE600DF540E /* Localizable.strings */,
+ );
+ path = Resources;
+ sourceTree = "<group>";
+ };
8B45A2A20DA49C47001148C5 /* GTMUIUnitTestingHarness */ = {
isa = PBXGroup;
children = (
8BAA9E360F7C19D500DF4F12 /* MainMenu.xib */,
8B45A2A70DA49C47001148C5 /* Info.plist */,
8B45A2A80DA49C47001148C5 /* main.m */,
+ 8B409BE50F9442C800DF540E /* Localizable.strings */,
);
path = GTMUIUnitTestingHarness;
sourceTree = "<group>";
@@ -715,6 +749,7 @@
F435E4840DC8F3DC0069CDE8 /* TestData */ = {
isa = PBXGroup;
children = (
+ 8B409E960F952CCA00DF540E /* Resources */,
8B1802410E25592200280961 /* GTMLargeTypeWindowMediumTextTest.gtmUTState */,
8B1801A80E25341B00280961 /* GTMLargeTypeWindowImageTest.gtmUTState */,
8B1801AC0E25341B00280961 /* GTMLargeTypeWindowLongTextTest.gtmUTState */,
@@ -726,6 +761,11 @@
F435DE8A0DC0B7620069CDE8 /* GTMNSBezierPath+RoundRectTest.ppc64.tiff */,
8BE869720DBE89C100749827 /* GTMNSBezierPath+RoundRectTest.x86_64.tiff */,
8B7AD4970DABBB5800B84F4A /* GTMNSBezierPath+ShadingTest.tiff */,
+ 8B409EC30F9530C200DF540E /* GTMUILocalizerWindow1State.gtmUTState */,
+ 8B409EE80F95325000DF540E /* GTMUILocalizerWindow2State.gtmUTState */,
+ 8B409EF30F95331500DF540E /* GTMUILocalizerMenuState.gtmUTState */,
+ 8B409F110F95352500DF540E /* GTMUILocalizerView2State.gtmUTState */,
+ 8B409F120F95352500DF540E /* GTMUILocalizerView1State.gtmUTState */,
);
path = TestData;
sourceTree = "<group>";
@@ -794,6 +834,12 @@
7F511DF80F4B0378009F41B6 /* GTMTheme.m */,
7F511DF70F4B0378009F41B6 /* GTMThemeTest.m */,
F47F1C740D490E5C00925B8F /* GTMShading.h */,
+ 8B4099490F93C5CC00DF540E /* GTMUILocalizer.h */,
+ 8B40994A0F93C5CC00DF540E /* GTMUILocalizer.m */,
+ 8B409E3E0F950DE900DF540E /* GTMUILocalizerTest.h */,
+ 8B409BC50F94405A00DF540E /* GTMUILocalizerTest.m */,
+ 8B409E8B0F952C2C00DF540E /* GTMUILocalizerTestWindow.xib */,
+ 8B409F050F95341E00DF540E /* GTMUILocalizerTestView.xib */,
F435E4840DC8F3DC0069CDE8 /* TestData */,
);
path = AppKit;
@@ -1063,6 +1109,7 @@
7F511DFC0F4B0378009F41B6 /* GTMTheme.h in Headers */,
10998E920F4B5952007F179D /* GTMTransientRootProxy.h in Headers */,
10998EF50F4B5D1A007F179D /* GTMTransientRootPortProxy.h in Headers */,
+ 8B40994B0F93C5CC00DF540E /* GTMUILocalizer.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -1246,6 +1293,7 @@
buildActionMask = 2147483647;
files = (
8BAA9E380F7C19D500DF4F12 /* MainMenu.xib in Resources */,
+ 8B409BE60F9442C800DF540E /* Localizable.strings in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -1280,6 +1328,14 @@
8B1802420E25592200280961 /* GTMLargeTypeWindowMediumTextTest.gtmUTState in Resources */,
7F3EB5540E5F0B0400A7A75E /* GTMUnitTestingImage.tiff in Resources */,
8BAA9B570F7B4C2400DF4F12 /* GTMHotKeyTextFieldTest.xib in Resources */,
+ 8B409E8C0F952C2C00DF540E /* GTMUILocalizerTestWindow.xib in Resources */,
+ 8B409E9A0F952CE600DF540E /* Localizable.strings in Resources */,
+ 8B409EC40F9530C200DF540E /* GTMUILocalizerWindow1State.gtmUTState in Resources */,
+ 8B409EE90F95325000DF540E /* GTMUILocalizerWindow2State.gtmUTState in Resources */,
+ 8B409EF40F95331500DF540E /* GTMUILocalizerMenuState.gtmUTState in Resources */,
+ 8B409F060F95341E00DF540E /* GTMUILocalizerTestView.xib in Resources */,
+ 8B409F130F95352500DF540E /* GTMUILocalizerView2State.gtmUTState in Resources */,
+ 8B409F140F95352500DF540E /* GTMUILocalizerView1State.gtmUTState in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -1481,6 +1537,7 @@
7F511DFE0F4B0378009F41B6 /* GTMTheme.m in Sources */,
10998E8F0F4B593E007F179D /* GTMTransientRootProxy.m in Sources */,
10998EF40F4B5D1A007F179D /* GTMTransientRootPortProxy.m in Sources */,
+ 8B40994C0F93C5CC00DF540E /* GTMUILocalizer.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -1507,6 +1564,7 @@
8BAA9EFB0F7C2AB500DF4F12 /* GTMNSImage+ScalingTest.m in Sources */,
8BAA9EFC0F7C2AB500DF4F12 /* GTMNSWorkspace+RunningTest.m in Sources */,
8BAA9EFD0F7C2AB500DF4F12 /* GTMThemeTest.m in Sources */,
+ 8B409BC60F94405A00DF540E /* GTMUILocalizerTest.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -1541,6 +1599,22 @@
/* End PBXTargetDependency section */
/* Begin PBXVariantGroup section */
+ 8B409BE50F9442C800DF540E /* Localizable.strings */ = {
+ isa = PBXVariantGroup;
+ children = (
+ 8B409BE30F9442C400DF540E /* English */,
+ );
+ name = Localizable.strings;
+ sourceTree = "<group>";
+ };
+ 8B409E990F952CE600DF540E /* Localizable.strings */ = {
+ isa = PBXVariantGroup;
+ children = (
+ 8B409E970F952CD700DF540E /* English */,
+ );
+ name = Localizable.strings;
+ sourceTree = "<group>";
+ };
8BAA9E360F7C19D500DF4F12 /* MainMenu.xib */ = {
isa = PBXVariantGroup;
children = (
diff --git a/ReleaseNotes.txt b/ReleaseNotes.txt
index 2c6fd75..67635c8 100644
--- a/ReleaseNotes.txt
+++ b/ReleaseNotes.txt
@@ -267,6 +267,16 @@ Changes since 1.5.1
on OBJC_DEBUG_FINALIZERS beacuse it spits out a lot of unnecessary false
positives.
+- Added GTMUILocalizer.m for automatically localizing nib files with strings.
+
+- Added better support for NSTabViews to GTMAppKit+UnitTesting. Previously we
+ didn't check the tabs, or recurse into the views.
+
+- Adds support for toolTips, accessibilityHelp and accessibilityDescription to
+ GTMAppKit+UnitTesting. This will break your UI tests based on the older
+ state information.
+
+
Release 1.5.1
Changes since 1.5.0
16-June-2008
diff --git a/UnitTesting/GTMAppKit+UnitTesting.h b/UnitTesting/GTMAppKit+UnitTesting.h
index 016da7e..1d6a08a 100644
--- a/UnitTesting/GTMAppKit+UnitTesting.h
+++ b/UnitTesting/GTMAppKit+UnitTesting.h
@@ -49,6 +49,12 @@
@interface NSMenuItem (GTMUnitTestingAdditions)
@end
+@interface NSTabView (GTMUnitTestingAdditions)
+@end
+
+@interface NSTabViewItem (GTMUnitTestingAdditions)
+@end
+
@protocol GTMUnitTestViewDrawer;
// Fails when the |a1|'s drawing in an area |a2| does not equal the image file named |a3|.
diff --git a/UnitTesting/GTMAppKit+UnitTesting.m b/UnitTesting/GTMAppKit+UnitTesting.m
index c35d07d..b3247fd 100644
--- a/UnitTesting/GTMAppKit+UnitTesting.m
+++ b/UnitTesting/GTMAppKit+UnitTesting.m
@@ -234,6 +234,43 @@ GTM_METHOD_CHECK(NSObject, gtm_unitTestEncodeState:);
@end
+@implementation NSTabView (GTMUnitTestingAdditions)
+
+// Encodes the state of an object in a manner suitable for comparing
+// against a master state file so we can determine whether the
+// object is in a suitable state.
+//
+// Arguments:
+// inCoder - the coder to encode our state into
+- (void)gtm_unitTestEncodeState:(NSCoder*)inCoder {
+ [super gtm_unitTestEncodeState:inCoder];
+ NSTabViewItem *tab = nil;
+ int i = 0;
+ GTM_FOREACH_OBJECT(tab, [self tabViewItems]) {
+ NSString *key = [NSString stringWithFormat:@"TabItem %d", i];
+ [inCoder encodeObject:tab forKey:key];
+ i = i + 1;
+ }
+}
+
+@end
+
+@implementation NSTabViewItem (GTMUnitTestingAdditions)
+
+// Encodes the state of an object in a manner suitable for comparing
+// against a master state file so we can determine whether the
+// object is in a suitable state.
+//
+// Arguments:
+// inCoder - the coder to encode our state into
+- (void)gtm_unitTestEncodeState:(NSCoder*)inCoder {
+ [super gtm_unitTestEncodeState:inCoder];
+ [inCoder encodeObject:[self label] forKey:@"TabLabel"];
+ [inCoder encodeObject:[self view] forKey:@"TabView"];
+}
+
+@end
+
// A view that allows you to delegate out drawing using the formal
// GTMUnitTestViewDelegate protocol above. This is useful when writing up unit
// tests for visual elements.
@@ -327,6 +364,19 @@ GTM_METHOD_CHECK(NSObject, gtm_unitTestEncodeState:);
- (void)gtm_unitTestEncodeState:(NSCoder*)inCoder {
[super gtm_unitTestEncodeState:inCoder];
[inCoder encodeBool:[self isHidden] forKey:@"ViewIsHidden"];
+ [inCoder encodeObject:[self toolTip] forKey:@"ViewToolTip"];
+ NSArray *supportedAttrs = [self accessibilityAttributeNames];
+ if ([supportedAttrs containsObject:NSAccessibilityHelpAttribute]) {
+ NSString *help
+ = [self accessibilityAttributeValue:NSAccessibilityHelpAttribute];
+ [inCoder encodeObject:help forKey:@"ViewAccessibilityHelp"];
+ }
+ if ([supportedAttrs containsObject:NSAccessibilityDescriptionAttribute]) {
+ NSString *description
+ = [self accessibilityAttributeValue:NSAccessibilityDescriptionAttribute];
+ [inCoder encodeObject:description forKey:@"ViewAccessibilityDescription"];
+ }
+
if ([self gtm_shouldEncodeStateForSubviews]) {
NSView *subview = nil;
int i = 0;
diff --git a/UnitTesting/GTMUIUnitTestingHarness/English.lproj/Localizable.strings b/UnitTesting/GTMUIUnitTestingHarness/English.lproj/Localizable.strings
new file mode 100644
index 0000000..6eb49a5
--- /dev/null
+++ b/UnitTesting/GTMUIUnitTestingHarness/English.lproj/Localizable.strings
Binary files differ
diff --git a/UnitTesting/GTMUIUnitTestingHarness/English.lproj/MainMenu.xib b/UnitTesting/GTMUIUnitTestingHarness/English.lproj/MainMenu.xib
index 6cef0cb..2a5aa22 100644
--- a/UnitTesting/GTMUIUnitTestingHarness/English.lproj/MainMenu.xib
+++ b/UnitTesting/GTMUIUnitTestingHarness/English.lproj/MainMenu.xib
@@ -8,7 +8,7 @@
<string key="IBDocument.HIToolboxVersion">353.00</string>
<object class="NSMutableArray" key="IBDocument.EditedObjectIDs">
<bool key="EncodedWithXMLCoder">YES</bool>
- <integer value="29"/>
+ <integer value="377"/>
</object>
<object class="NSArray" key="IBDocument.PluginDependencies">
<bool key="EncodedWithXMLCoder">YES</bool>
@@ -611,7 +611,7 @@
</object>
<object class="NSMenuItem" id="626404410">
<reference key="NSMenu" ref="649796088"/>
- <string key="NSTitle">Format</string>
+ <string type="base64-UTF8" key="NSTitle">wqxNZW51IFRpdGxlA</string>
<string key="NSKeyEquiv"/>
<int key="NSKeyEquivModMask">1048576</int>
<int key="NSMnemonicLoc">2147483647</int>
@@ -619,12 +619,12 @@
<reference key="NSMixedImage" ref="513340101"/>
<string key="NSAction">submenuAction:</string>
<object class="NSMenu" key="NSSubmenu" id="502084290">
- <string key="NSTitle">Format</string>
+ <string type="base64-UTF8" key="NSTitle">wqxNZW51IFRpdGxlA</string>
<object class="NSMutableArray" key="NSMenuItems">
<bool key="EncodedWithXMLCoder">YES</bool>
- <object class="NSMenuItem" id="519768076">
+ <object class="NSMenuItem" id="83534624">
<reference key="NSMenu" ref="502084290"/>
- <string key="NSTitle">Show Fonts</string>
+ <string type="base64-UTF8" key="NSTitle">wqxNZW51IEl0ZW0</string>
<string key="NSKeyEquiv">t</string>
<int key="NSKeyEquivModMask">1048576</int>
<int key="NSMnemonicLoc">2147483647</int>
@@ -705,6 +705,9 @@
</object>
<string key="NSName">_NSMainMenu</string>
</object>
+ <object class="NSCustomObject" id="728136523">
+ <string key="NSClassName">GTMUILocalizer</string>
+ </object>
</object>
<object class="IBObjectContainer" key="IBDocument.Objects">
<object class="NSMutableArray" key="connectionRecords">
@@ -997,6 +1000,14 @@
</object>
<int key="connectionID">374</int>
</object>
+ <object class="IBConnectionRecord">
+ <object class="IBOutletConnection" key="connection">
+ <string key="label">owner_</string>
+ <reference key="source" ref="728136523"/>
+ <reference key="destination" ref="1021"/>
+ </object>
+ <int key="connectionID">376</int>
+ </object>
</object>
<object class="IBMutableOrderedSet" key="objectRecords">
<object class="NSArray" key="orderedObjects">
@@ -1472,17 +1483,12 @@
<reference key="object" ref="502084290"/>
<object class="NSMutableArray" key="children">
<bool key="EncodedWithXMLCoder">YES</bool>
- <reference ref="519768076"/>
<reference ref="1028416764"/>
+ <reference ref="83534624"/>
</object>
<reference key="parent" ref="626404410"/>
</object>
<object class="IBObjectRecord">
- <int key="objectID">344</int>
- <reference key="object" ref="519768076"/>
- <reference key="parent" ref="502084290"/>
- </object>
- <object class="IBObjectRecord">
<int key="objectID">345</int>
<reference key="object" ref="1028416764"/>
<reference key="parent" ref="502084290"/>
@@ -1556,6 +1562,16 @@
<reference key="object" ref="708854459"/>
<reference key="parent" ref="698887838"/>
</object>
+ <object class="IBObjectRecord">
+ <int key="objectID">375</int>
+ <reference key="object" ref="728136523"/>
+ <reference key="parent" ref="1049"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">377</int>
+ <reference key="object" ref="83534624"/>
+ <reference key="parent" ref="502084290"/>
+ </object>
</object>
</object>
<object class="NSMutableDictionary" key="flattenedProperties">
@@ -1624,6 +1640,7 @@
<string>203.ImportedFromIB2</string>
<string>204.IBPluginDependency</string>
<string>204.ImportedFromIB2</string>
+ <string>205.IBEditorWindowLastContentRect</string>
<string>205.IBPluginDependency</string>
<string>205.ImportedFromIB2</string>
<string>205.editorWindowContentRectSynchronizationRect</string>
@@ -1675,9 +1692,9 @@
<string>297.IBPluginDependency</string>
<string>298.IBPluginDependency</string>
<string>299.IBPluginDependency</string>
+ <string>300.IBEditorWindowLastContentRect</string>
<string>300.IBPluginDependency</string>
<string>300.editorWindowContentRectSynchronizationRect</string>
- <string>344.IBPluginDependency</string>
<string>345.IBPluginDependency</string>
<string>346.IBPluginDependency</string>
<string>346.ImportedFromIB2</string>
@@ -1692,6 +1709,8 @@
<string>351.ImportedFromIB2</string>
<string>354.IBPluginDependency</string>
<string>354.ImportedFromIB2</string>
+ <string>375.IBPluginDependency</string>
+ <string>377.IBPluginDependency</string>
<string>56.IBPluginDependency</string>
<string>56.ImportedFromIB2</string>
<string>57.IBEditorWindowLastContentRect</string>
@@ -1789,6 +1808,7 @@
<reference ref="9"/>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<reference ref="9"/>
+ <string>{{186, 593}, {243, 243}}</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<reference ref="9"/>
<string>{{187, 434}, {243, 243}}</string>
@@ -1828,7 +1848,7 @@
<reference ref="9"/>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<reference ref="9"/>
- <string>{{0, 1092}, {407, 20}}</string>
+ <string>{{2560, 1536}, {442, 20}}</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<reference ref="9"/>
<string>{74, 862}</string>
@@ -1840,11 +1860,11 @@
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>{{2790, 1493}, {176, 43}}</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>{{231, 634}, {176, 43}}</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<reference ref="9"/>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<reference ref="9"/>
@@ -1858,6 +1878,8 @@
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<reference ref="9"/>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<reference ref="9"/>
<string>{{12, 909}, {245, 183}}</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
@@ -1881,7 +1903,7 @@
<reference ref="9"/>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<reference ref="9"/>
- <string>{{144, 889}, {199, 203}}</string>
+ <string>{{144, 633}, {199, 203}}</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<reference ref="9"/>
<string>{{145, 474}, {199, 203}}</string>
@@ -1911,7 +1933,7 @@
</object>
</object>
<nil key="sourceID"/>
- <int key="maxID">374</int>
+ <int key="maxID">388</int>
</object>
<object class="IBClassDescriber" key="IBDocument.Classes">
<object class="NSMutableArray" key="referencedPartialClassDescriptions">
@@ -1929,6 +1951,29 @@
</object>
</object>
<object class="IBPartialClassDescription">
+ <string key="className">GTMUILocalizer</string>
+ <string key="superclassName">NSObject</string>
+ <object class="NSMutableDictionary" key="outlets">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSMutableArray" key="dict.sortedKeys">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>otherObjectToLocalize_</string>
+ <string>owner_</string>
+ <string>yetAnotherObjectToLocalize_</string>
+ </object>
+ <object class="NSMutableArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>id</string>
+ <string>id</string>
+ <string>id</string>
+ </object>
+ </object>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">AppKit/GTMUILocalizer.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
<string key="className">NSApplication</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier" id="271775925">
<string key="majorKey">IBProjectSource</string>
diff --git a/UnitTesting/TestData/GTMUnitTestingTestApp.gtmUTState b/UnitTesting/TestData/GTMUnitTestingTestApp.gtmUTState
index 525535b..e7c7e05 100644
--- a/UnitTesting/TestData/GTMUnitTestingTestApp.gtmUTState
+++ b/UnitTesting/TestData/GTMUnitTestingTestApp.gtmUTState
@@ -1166,7 +1166,7 @@
<key>MenuItemTag</key>
<integer>0</integer>
<key>MenuItemTitle</key>
- <string>Show Fonts</string>
+ <string>¬Menu Item</string>
</dict>
<key>MenuItem 1</key>
<dict>
@@ -1188,12 +1188,12 @@
<string>Show Colors</string>
</dict>
<key>MenuTitle</key>
- <string>Format</string>
+ <string>¬Menu Title</string>
</dict>
<key>MenuItemTag</key>
<integer>0</integer>
<key>MenuItemTitle</key>
- <string>Format</string>
+ <string>¬Menu Title</string>
</dict>
<key>MenuItem 4</key>
<dict>
@@ -1411,6 +1411,26 @@
</dict>
<key>ViewSubView 11</key>
<dict>
+ <key>TabItem 0</key>
+ <dict>
+ <key>TabLabel</key>
+ <string>Tab</string>
+ <key>TabView</key>
+ <dict>
+ <key>ViewIsHidden</key>
+ <false/>
+ </dict>
+ </dict>
+ <key>TabItem 1</key>
+ <dict>
+ <key>TabLabel</key>
+ <string>View</string>
+ <key>TabView</key>
+ <dict>
+ <key>ViewIsHidden</key>
+ <false/>
+ </dict>
+ </dict>
<key>ViewIsHidden</key>
<false/>
<key>ViewSubView 0</key>
diff --git a/UnitTesting/TestData/GTMUnitTestingWindow.gtmUTState b/UnitTesting/TestData/GTMUnitTestingWindow.gtmUTState
index 27dd08e..17f77c8 100644
--- a/UnitTesting/TestData/GTMUnitTestingWindow.gtmUTState
+++ b/UnitTesting/TestData/GTMUnitTestingWindow.gtmUTState
@@ -112,6 +112,26 @@
</dict>
<key>ViewSubView 11</key>
<dict>
+ <key>TabItem 0</key>
+ <dict>
+ <key>TabLabel</key>
+ <string>Tab</string>
+ <key>TabView</key>
+ <dict>
+ <key>ViewIsHidden</key>
+ <false/>
+ </dict>
+ </dict>
+ <key>TabItem 1</key>
+ <dict>
+ <key>TabLabel</key>
+ <string>View</string>
+ <key>TabView</key>
+ <dict>
+ <key>ViewIsHidden</key>
+ <false/>
+ </dict>
+ </dict>
<key>ViewIsHidden</key>
<false/>
<key>ViewSubView 0</key>