From 1c89992b2af18acb457819b94f50fee247620d28 Mon Sep 17 00:00:00 2001 From: rowanbeentje Date: Sat, 12 Jun 2010 15:20:57 +0000 Subject: - Implement the Wil shipley/Golden % Braeburn localisation method for .xibs; add a build stage to Release/Dist builds which generates .strings files for each nib, and swizzle nib loading to automatically translate strings based on any localised .strings files - Fix the genstring stage of the build script and move it to Release/Dist builds only - Update Localizable.strings to match latest code --- Source/DMLocalizedNibBundle.m | 217 ++++++++++++++++++++++++++++++++++ Source/xibLocalizationPostprocessor.m | 62 ++++++++++ 2 files changed, 279 insertions(+) create mode 100644 Source/DMLocalizedNibBundle.m create mode 100644 Source/xibLocalizationPostprocessor.m (limited to 'Source') diff --git a/Source/DMLocalizedNibBundle.m b/Source/DMLocalizedNibBundle.m new file mode 100644 index 00000000..e9200bc0 --- /dev/null +++ b/Source/DMLocalizedNibBundle.m @@ -0,0 +1,217 @@ +// DMLocalizedNibBundle.m +// +// Created by William Jon Shipley on 2/13/05. +// Copyright © 2005-2009 Golden % Braeburn, LLC. All rights reserved except as below: +// This code is provided as-is, with no warranties or anything. You may use it in your projects as you wish, but you must leave this comment block (credits and copyright) intact. That's the only restriction -- Golden % Braeburn otherwise grants you a fully-paid, worldwide, transferrable license to use this code as you see fit, including but not limited to making derivative works. + + +#import +#import + + +@interface NSBundle (DMLocalizedNibBundle) ++ (BOOL)deliciousLocalizingLoadNibFile:(NSString *)fileName externalNameTable:(NSDictionary *)context withZone:(NSZone *)zone; +@end + +@interface NSBundle () ++ (void)_localizeStringsInObject:(id)object table:(NSString *)table; ++ (NSString *)_localizedStringForString:(NSString *)string table:(NSString *)table; +// localize particular attributes in objects ++ (void)_localizeTitleOfObject:(id)object table:(NSString *)table; ++ (void)_localizeAlternateTitleOfObject:(id)object table:(NSString *)table; ++ (void)_localizeStringValueOfObject:(id)object table:(NSString *)table; ++ (void)_localizePlaceholderStringOfObject:(id)object table:(NSString *)table; ++ (void)_localizeToolTipOfObject:(id)object table:(NSString *)table; +@end + + +@implementation NSBundle (DMLocalizedNibBundle) + +#pragma mark NSObject + ++ (void)load; +{ + NSAutoreleasePool *autoreleasePool = [[NSAutoreleasePool alloc] init]; + if (self == [NSBundle class]) { + method_exchangeImplementations(class_getClassMethod(self, @selector(loadNibFile:externalNameTable:withZone:)), class_getClassMethod(self, @selector(deliciousLocalizingLoadNibFile:externalNameTable:withZone:))); + } + [autoreleasePool release]; +} + + +#pragma mark API + ++ (BOOL)deliciousLocalizingLoadNibFile:(NSString *)fileName externalNameTable:(NSDictionary *)context withZone:(NSZone *)zone; +{ + NSString *localizedStringsTableName = [[fileName lastPathComponent] stringByDeletingPathExtension]; + NSString *localizedStringsTablePath = [[NSBundle mainBundle] pathForResource:localizedStringsTableName ofType:@"strings"]; + if (localizedStringsTablePath && ![[[localizedStringsTablePath stringByDeletingLastPathComponent] lastPathComponent] isEqualToString:@"English.lproj"]) { + + NSNib *nib = [[NSNib alloc] initWithContentsOfURL:[NSURL fileURLWithPath:fileName]]; + NSMutableArray *topLevelObjectsArray = [context objectForKey:NSNibTopLevelObjects]; + if (!topLevelObjectsArray) { + topLevelObjectsArray = [NSMutableArray array]; + context = [NSMutableDictionary dictionaryWithDictionary:context]; + [(NSMutableDictionary *)context setObject:topLevelObjectsArray forKey:NSNibTopLevelObjects]; + } + BOOL success = [nib instantiateNibWithExternalNameTable:context]; + [self _localizeStringsInObject:topLevelObjectsArray table:localizedStringsTableName]; + + [nib release]; + return success; + + } else { + return [self deliciousLocalizingLoadNibFile:fileName externalNameTable:context withZone:zone]; + } +} + + + +#pragma mark Private API + ++ (void)_localizeStringsInObject:(id)object table:(NSString *)table; +{ + if ([object isKindOfClass:[NSArray class]]) { + NSArray *array = object; + + for (id nibItem in array) + [self _localizeStringsInObject:nibItem table:table]; + + } else if ([object isKindOfClass:[NSCell class]]) { + NSCell *cell = object; + + if ([cell isKindOfClass:[NSActionCell class]]) { + NSActionCell *actionCell = (NSActionCell *)cell; + + if ([actionCell isKindOfClass:[NSButtonCell class]]) { + NSButtonCell *buttonCell = (NSButtonCell *)actionCell; + if ([buttonCell imagePosition] != NSImageOnly) { + [self _localizeTitleOfObject:buttonCell table:table]; + [self _localizeStringValueOfObject:buttonCell table:table]; + [self _localizeAlternateTitleOfObject:buttonCell table:table]; + } + + } else if ([actionCell isKindOfClass:[NSTextFieldCell class]]) { + NSTextFieldCell *textFieldCell = (NSTextFieldCell *)actionCell; + // Following line is redundant with other code, localizes twice. + // [self _localizeTitleOfObject:textFieldCell table:table]; + [self _localizeStringValueOfObject:textFieldCell table:table]; + [self _localizePlaceholderStringOfObject:textFieldCell table:table]; + + } else if ([actionCell type] == NSTextCellType) { + [self _localizeTitleOfObject:actionCell table:table]; + [self _localizeStringValueOfObject:actionCell table:table]; + } + } + + } else if ([object isKindOfClass:[NSMenu class]]) { + NSMenu *menu = object; + [self _localizeTitleOfObject:menu table:table]; + + [self _localizeStringsInObject:[menu itemArray] table:table]; + + } else if ([object isKindOfClass:[NSMenuItem class]]) { + NSMenuItem *menuItem = object; + [self _localizeTitleOfObject:menuItem table:table]; + + [self _localizeStringsInObject:[menuItem submenu] table:table]; + + } else if ([object isKindOfClass:[NSView class]]) { + NSView *view = object; + [self _localizeToolTipOfObject:view table:table]; + + if ([view isKindOfClass:[NSBox class]]) { + NSBox *box = (NSBox *)view; + [self _localizeTitleOfObject:box table:table]; + + } else if ([view isKindOfClass:[NSControl class]]) { + NSControl *control = (NSControl *)view; + + if ([view isKindOfClass:[NSButton class]]) { + NSButton *button = (NSButton *)control; + + if ([button isKindOfClass:[NSPopUpButton class]]) { + NSPopUpButton *popUpButton = (NSPopUpButton *)button; + NSMenu *menu = [popUpButton menu]; + + [self _localizeStringsInObject:[menu itemArray] table:table]; + } else + [self _localizeStringsInObject:[button cell] table:table]; + + + } else if ([view isKindOfClass:[NSMatrix class]]) { + NSMatrix *matrix = (NSMatrix *)control; + + NSArray *cells = [matrix cells]; + [self _localizeStringsInObject:cells table:table]; + + for (NSCell *cell in cells) { + + NSString *localizedCellToolTip = [self _localizedStringForString:[matrix toolTipForCell:cell] table:table]; + if (localizedCellToolTip) + [matrix setToolTip:localizedCellToolTip forCell:cell]; + } + + } else if ([view isKindOfClass:[NSSegmentedControl class]]) { + NSSegmentedControl *segmentedControl = (NSSegmentedControl *)control; + + NSUInteger segmentIndex, segmentCount = [segmentedControl segmentCount]; + for (segmentIndex = 0; segmentIndex < segmentCount; segmentIndex++) { + NSString *localizedSegmentLabel = [self _localizedStringForString:[segmentedControl labelForSegment:segmentIndex] table:table]; + if (localizedSegmentLabel) + [segmentedControl setLabel:localizedSegmentLabel forSegment:segmentIndex]; + + [self _localizeStringsInObject:[segmentedControl menuForSegment:segmentIndex] table:table]; + } + + } else + [self _localizeStringsInObject:[control cell] table:table]; + + } + + [self _localizeStringsInObject:[view subviews] table:table]; + + } else if ([object isKindOfClass:[NSWindow class]]) { + NSWindow *window = object; + [self _localizeTitleOfObject:window table:table]; + + [self _localizeStringsInObject:[window contentView] table:table]; + + } +} + ++ (NSString *)_localizedStringForString:(NSString *)string table:(NSString *)table; +{ + if (![string length]) + return nil; + + static NSString *defaultValue = @"I AM THE DEFAULT VALUE"; + NSString *localizedString = [[NSBundle mainBundle] localizedStringForKey:string value:defaultValue table:table]; + if (localizedString != defaultValue) { + return localizedString; + } else { +#ifdef BETA_BUILD + NSLog(@" not going to localize string %@", string); + return string; // [string uppercaseString] +#else + return string; +#endif + } +} + + +#define DM_DEFINE_LOCALIZE_BLAH_OF_OBJECT(blahName, capitalizedBlahName) \ ++ (void)_localize ##capitalizedBlahName ##OfObject:(id)object table:(NSString *)table; \ +{ \ + NSString *localizedBlah = [self _localizedStringForString:[object blahName] table:table]; \ + if (localizedBlah) \ + [object set ##capitalizedBlahName:localizedBlah]; \ +} + +DM_DEFINE_LOCALIZE_BLAH_OF_OBJECT(title, Title) +DM_DEFINE_LOCALIZE_BLAH_OF_OBJECT(alternateTitle, AlternateTitle) +DM_DEFINE_LOCALIZE_BLAH_OF_OBJECT(stringValue, StringValue) +DM_DEFINE_LOCALIZE_BLAH_OF_OBJECT(placeholderString, PlaceholderString) +DM_DEFINE_LOCALIZE_BLAH_OF_OBJECT(toolTip, ToolTip) + +@end \ No newline at end of file diff --git a/Source/xibLocalizationPostprocessor.m b/Source/xibLocalizationPostprocessor.m new file mode 100644 index 00000000..591e92b7 --- /dev/null +++ b/Source/xibLocalizationPostprocessor.m @@ -0,0 +1,62 @@ +// xibLocalizationPostprocessor.m +// +// Created by William Shipley on 4/14/08. +// Copyright © 2005-2009 Golden % Braeburn, LLC. + +#import + + +int main(int argc, const char *argv[]) +{ + NSAutoreleasePool *autoreleasePool = [[NSAutoreleasePool alloc] init]; { + if (argc != 3) { + fprintf(stderr, "Usage: %s inputfile outputfile\n", argv[0]); + exit (-1); + } + + NSError *error = nil; + NSStringEncoding usedEncoding; + NSString *rawXIBStrings = [NSString stringWithContentsOfFile:[NSString stringWithUTF8String:argv[1]] usedEncoding:&usedEncoding error:&error]; + if (error) { + fprintf(stderr, "Error reading %s: %s\n", argv[1], error.localizedDescription.UTF8String); + exit (-1); + } + + NSMutableString *outputStrings = [NSMutableString string]; + NSUInteger lineCount = 0; + NSString *lastComment = nil; + for (NSString *line in [rawXIBStrings componentsSeparatedByString:@"\n"]) { + lineCount++; + + if ([line hasPrefix:@"/*"]) { // eg: /* Class = "NSMenuItem"; title = "Quit Library"; ObjectID = "136"; */ + lastComment = line; + continue; + + } else if (line.length == 0) { + lastComment = nil; + continue; + + } else if ([line hasPrefix:@"\""] && [line hasSuffix:@"\";"]) { // eg: "136.title" = "Quit Library"; + + NSRange quoteEqualsQuoteRange = [line rangeOfString:@"\" = \""]; + if (quoteEqualsQuoteRange.length && NSMaxRange(quoteEqualsQuoteRange) < line.length - 1) { + if (lastComment) { + [outputStrings appendString:lastComment]; + [outputStrings appendString:@"\n"]; + } + NSString *stringNeedingLocalization = [line substringFromIndex:NSMaxRange(quoteEqualsQuoteRange)]; // chop off leading: "blah" = " + stringNeedingLocalization = [stringNeedingLocalization substringToIndex:stringNeedingLocalization.length - 2]; // chop off trailing: "; + [outputStrings appendFormat:@"\"%@\" = \"%@\";\n\n", stringNeedingLocalization, stringNeedingLocalization]; + continue; + } + } + + NSLog(@"Warning: skipped garbage input line %d, contents: \"%@\"", lineCount, line); + } + + if (outputStrings.length && ![outputStrings writeToFile:[NSString stringWithUTF8String:argv[2]] atomically:NO encoding:NSUTF8StringEncoding error:&error]) { + fprintf(stderr, "Error writing %s: %s\n", argv[2], error.localizedDescription.UTF8String); + exit (-1); + } + } [autoreleasePool release]; +} \ No newline at end of file -- cgit v1.2.3