/core/externals/update-engine/externals/google-toolbox-for-mac/AppKit/GTMUILocalizer.m

http://macfuse.googlecode.com/ · Objective C · 428 lines · 354 code · 30 blank · 44 comment · 89 complexity · 90aa971175db6adfe5722919b86aa7ff MD5 · raw file

  1. //
  2. // GTMUILocalizer.m
  3. //
  4. // Copyright 2009 Google Inc.
  5. //
  6. // Licensed under the Apache License, Version 2.0 (the "License"); you may not
  7. // use this file except in compliance with the License. You may obtain a copy
  8. // of the License at
  9. //
  10. // http://www.apache.org/licenses/LICENSE-2.0
  11. //
  12. // Unless required by applicable law or agreed to in writing, software
  13. // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  14. // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  15. // License for the specific language governing permissions and limitations under
  16. // the License.
  17. //
  18. #import "GTMDefines.h"
  19. #import "GTMUILocalizer.h"
  20. @interface GTMUILocalizer (GTMUILocalizerPrivate)
  21. - (void)localizeAccessibility:(id)object;
  22. - (void)localizeBindings:(id)object;
  23. // Never recursively call any of these methods. Always call
  24. // -[self localizeObject:recursively:] otherwise bindings will not be
  25. // localized properly.
  26. - (void)localizeWindow:(NSWindow *)window recursively:(BOOL)recursive;
  27. - (void)localizeToolbar:(NSToolbar *)toolbar;
  28. - (void)localizeView:(NSView *)view recursively:(BOOL)recursive;
  29. - (void)localizeMenu:(NSMenu *)menu recursively:(BOOL)recursive;
  30. - (void)localizeCell:(NSCell *)cell recursively:(BOOL)recursive;
  31. @end
  32. @implementation GTMUILocalizer
  33. - (id)initWithBundle:(NSBundle *)bundle {
  34. if ((self = [super init])) {
  35. bundle_ = [bundle retain];
  36. }
  37. return self;
  38. }
  39. - (void)dealloc {
  40. [bundle_ release];
  41. [super dealloc];
  42. }
  43. - (void)awakeFromNib {
  44. if (owner_) {
  45. NSBundle *newBundle = [[self class] bundleForOwner:owner_];
  46. bundle_ = [newBundle retain];
  47. [self localizeObject:owner_ recursively:YES];
  48. [self localizeObject:otherObjectToLocalize_ recursively:YES];
  49. [self localizeObject:yetAnotherObjectToLocalize_ recursively:YES];
  50. } else {
  51. _GTMDevLog(@"Expected an owner_ set for %@", self);
  52. }
  53. // Won't need these again, clear them.
  54. owner_ = nil;
  55. otherObjectToLocalize_ = nil;
  56. yetAnotherObjectToLocalize_ = nil;
  57. }
  58. + (NSBundle *)bundleForOwner:(id)owner {
  59. NSBundle *newBundle = nil;
  60. if (owner) {
  61. Class class = [NSWindowController class];
  62. if ([owner isKindOfClass:class] && ![owner isMemberOfClass:class]) {
  63. newBundle = [NSBundle bundleForClass:[owner class]];
  64. #if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5
  65. } else if ([owner isKindOfClass:[NSViewController class]]) {
  66. newBundle = [(NSViewController *)owner nibBundle];
  67. #endif
  68. }
  69. if (!newBundle) {
  70. newBundle = [NSBundle mainBundle];
  71. }
  72. }
  73. return newBundle;
  74. }
  75. - (NSString *)localizedStringForString:(NSString *)string {
  76. NSString *localized = nil;
  77. if (bundle_ && [string hasPrefix:@"^"]) {
  78. NSString *notFoundValue = @"__GTM_NOT_FOUND__";
  79. NSString *key = [string substringFromIndex:1];
  80. localized = [bundle_ localizedStringForKey:key
  81. value:notFoundValue
  82. table:nil];
  83. if ([localized isEqualToString:notFoundValue]) {
  84. localized = nil;
  85. }
  86. }
  87. return localized;
  88. }
  89. - (void)localizeObject:(id)object recursively:(BOOL)recursive {
  90. if (object) {
  91. if ([object isKindOfClass:[NSWindowController class]]) {
  92. NSWindow *window = [object window];
  93. [self localizeWindow:window recursively:recursive];
  94. #if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5
  95. } else if ([object isKindOfClass:[NSViewController class]]) {
  96. NSView *view = [object view];
  97. [self localizeView:view recursively:recursive];
  98. #endif
  99. } else if ([object isKindOfClass:[NSMenu class]]) {
  100. [self localizeMenu:(NSMenu *)object recursively:recursive];
  101. } else if ([object isKindOfClass:[NSWindow class]]) {
  102. [self localizeWindow:(NSWindow *)object recursively:recursive];
  103. } else if ([object isKindOfClass:[NSView class]]) {
  104. [self localizeView:(NSView *)object recursively:recursive];
  105. } else if ([object isKindOfClass:[NSApplication class]]) {
  106. // Do the main menu
  107. NSMenu *menu = [object mainMenu];
  108. [self localizeMenu:menu recursively:recursive];
  109. } else if ([object isKindOfClass:[NSCell class]]) {
  110. [self localizeCell:(NSCell *)object recursively:recursive];
  111. } else if ([object isKindOfClass:[NSToolbar class]]) {
  112. [self localizeToolbar:(NSToolbar*)object];
  113. }
  114. [self localizeBindings:object];
  115. }
  116. }
  117. - (void)localizeWindow:(NSWindow *)window recursively:(BOOL)recursive {
  118. NSString *title = [window title];
  119. NSString *localizedTitle = [self localizedStringForString:title];
  120. if (localizedTitle) {
  121. [window setTitle:localizedTitle];
  122. }
  123. if (recursive) {
  124. [self localizeObject:[window contentView] recursively:recursive];
  125. [self localizeObject:[window toolbar] recursively:recursive];
  126. }
  127. }
  128. - (void)localizeToolbar:(NSToolbar *)toolbar {
  129. // NOTE: Like the header says, -items only gives us what is in the toolbar
  130. // which is usually the default items, if the toolbar supports customization
  131. // there is no way to fetch those possible items to tweak their contents.
  132. NSToolbarItem *item;
  133. GTM_FOREACH_OBJECT(item, [toolbar items]) {
  134. NSString *label = [item label];
  135. if (label) {
  136. label = [self localizedStringForString:label];
  137. if (label) {
  138. [item setLabel:label];
  139. }
  140. }
  141. NSString *paletteLabel = [item paletteLabel];
  142. if (paletteLabel) {
  143. paletteLabel = [self localizedStringForString:paletteLabel];
  144. if (paletteLabel) {
  145. [item setPaletteLabel:paletteLabel];
  146. }
  147. }
  148. NSString *toolTip = [item toolTip];
  149. if (toolTip) {
  150. toolTip = [self localizedStringForString:toolTip];
  151. if (toolTip) {
  152. [item setToolTip:toolTip];
  153. }
  154. }
  155. }
  156. }
  157. - (void)localizeView:(NSView *)view recursively:(BOOL)recursive {
  158. if (view) {
  159. // First do tooltips
  160. NSString *toolTip = [view toolTip];
  161. if (toolTip) {
  162. NSString *localizedToolTip = [self localizedStringForString:toolTip];
  163. if (localizedToolTip) {
  164. [view setToolTip:localizedToolTip];
  165. }
  166. }
  167. // Then do accessibility both on views directly...
  168. [self localizeAccessibility:view];
  169. // ...and on control cells (which implement accessibility for the controls
  170. // that contain them)
  171. if ([view isKindOfClass:[NSControl class]]) {
  172. [self localizeAccessibility:[(NSControl *)view cell]];
  173. }
  174. // Must do the menu before the titles, or else this will screw up
  175. // popup menus on us.
  176. [self localizeObject:[view menu] recursively:recursive];
  177. if (recursive) {
  178. NSArray *subviews = [view subviews];
  179. NSView *subview = nil;
  180. GTM_FOREACH_OBJECT(subview, subviews) {
  181. [self localizeObject:subview recursively:recursive];
  182. }
  183. }
  184. // Then do titles
  185. if ([view isKindOfClass:[NSTextField class]]) {
  186. NSString *title = [(NSTextField *)view stringValue];
  187. NSString *localizedTitle = [self localizedStringForString:title];
  188. if (localizedTitle) {
  189. [(NSTextField *)view setStringValue:localizedTitle];
  190. }
  191. } else if ([view respondsToSelector:@selector(title)]
  192. && [view respondsToSelector:@selector(setTitle:)]) {
  193. NSString *title = [view performSelector:@selector(title)];
  194. if (title) {
  195. NSString *localizedTitle = [self localizedStringForString:title];
  196. if (localizedTitle) {
  197. [view performSelector:@selector(setTitle:) withObject:localizedTitle];
  198. }
  199. }
  200. if ([view respondsToSelector:@selector(alternateTitle)]
  201. && [view respondsToSelector:@selector(setAlternateTitle:)]) {
  202. title = [view performSelector:@selector(alternateTitle)];
  203. if (title) {
  204. NSString *localizedTitle = [self localizedStringForString:title];
  205. if (localizedTitle) {
  206. [view performSelector:@selector(setAlternateTitle:)
  207. withObject:localizedTitle];
  208. }
  209. }
  210. }
  211. } else if ([view respondsToSelector:@selector(tabViewItems)]) {
  212. NSArray *items = [view performSelector:@selector(tabViewItems)];
  213. NSEnumerator *itemEnum = [items objectEnumerator];
  214. NSTabViewItem *item = nil;
  215. while ((item = [itemEnum nextObject])) {
  216. NSString *label = [item label];
  217. NSString *localizedLabel = [self localizedStringForString:label];
  218. if (localizedLabel) {
  219. [item setLabel:localizedLabel];
  220. }
  221. if (recursive) {
  222. [self localizeObject:[item view] recursively:recursive];
  223. }
  224. }
  225. }
  226. }
  227. // Do NSTextField placeholders
  228. if ([view isKindOfClass:[NSTextField class]]) {
  229. NSString *placeholder = [[(NSTextField *)view cell] placeholderString];
  230. NSString *localizedPlaceholer = [self localizedStringForString:placeholder];
  231. if (localizedPlaceholer) {
  232. [[(NSTextField *)view cell] setPlaceholderString:localizedPlaceholer];
  233. }
  234. }
  235. // Do any NSMatrix placeholders
  236. if ([view isKindOfClass:[NSMatrix class]]) {
  237. NSMatrix *matrix = (NSMatrix *)view;
  238. // Process the prototype
  239. id cell = [matrix prototype];
  240. [self localizeObject:cell recursively:recursive];
  241. // Process the cells
  242. GTM_FOREACH_OBJECT(cell, [matrix cells]) {
  243. [self localizeObject:cell recursively:recursive];
  244. // The tooltip isn't on a cell, so we do it via the matrix.
  245. NSString *toolTip = [matrix toolTipForCell:cell];
  246. NSString *localizedToolTip = [self localizedStringForString:toolTip];
  247. if (localizedToolTip) {
  248. [matrix setToolTip:localizedToolTip forCell:cell];
  249. }
  250. }
  251. }
  252. // Do NSTableView column headers.
  253. if ([view isKindOfClass:[NSTableView class]]) {
  254. NSTableView *tableView = (NSTableView *)view;
  255. NSArray *columns = [tableView tableColumns];
  256. NSTableColumn *column = nil;
  257. GTM_FOREACH_OBJECT(column, columns) {
  258. [self localizeObject:[column headerCell] recursively:recursive];
  259. }
  260. }
  261. // Do NSSegmentedControl segments.
  262. if ([view isKindOfClass:[NSSegmentedControl class]]) {
  263. NSSegmentedControl *segmentedControl = (NSSegmentedControl *)view;
  264. for (NSInteger i = 0; i < [segmentedControl segmentCount]; ++i) {
  265. NSString *label = [segmentedControl labelForSegment:i];
  266. NSString *localizedLabel = [self localizedStringForString:label];
  267. if (localizedLabel) {
  268. [segmentedControl setLabel:localizedLabel forSegment:i];
  269. }
  270. }
  271. }
  272. // Do NSComboBox items.
  273. if ([view isKindOfClass:[NSComboBox class]]) {
  274. NSComboBox *combobox = (NSComboBox*)view;
  275. // Make sure it doesn't use a DataSource.
  276. if (![combobox usesDataSource]) {
  277. NSMutableArray *localizedValues = [NSMutableArray array];
  278. BOOL replaceValues = NO;
  279. NSString *value;
  280. GTM_FOREACH_OBJECT(value, [combobox objectValues]) {
  281. NSString *localizedValue = nil;
  282. if ([value isKindOfClass:[NSString class]]) {
  283. localizedValue = [self localizedStringForString:value];
  284. }
  285. if (localizedValue) {
  286. replaceValues = YES;
  287. [localizedValues addObject:localizedValue];
  288. } else {
  289. [localizedValues addObject:value];
  290. }
  291. }
  292. if (replaceValues) {
  293. [combobox removeAllItems];
  294. [combobox addItemsWithObjectValues:localizedValues];
  295. }
  296. }
  297. }
  298. }
  299. - (void)localizeMenu:(NSMenu *)menu recursively:(BOOL)recursive {
  300. if (menu) {
  301. NSString *title = [menu title];
  302. NSString *localizedTitle = [self localizedStringForString:title];
  303. if (localizedTitle) {
  304. [menu setTitle:localizedTitle];
  305. }
  306. NSArray *menuItems = [menu itemArray];
  307. NSMenuItem *menuItem = nil;
  308. GTM_FOREACH_OBJECT(menuItem, menuItems) {
  309. title = [menuItem title];
  310. localizedTitle = [self localizedStringForString:title];
  311. if (localizedTitle) {
  312. [menuItem setTitle:localizedTitle];
  313. }
  314. if (recursive) {
  315. [self localizeObject:[menuItem submenu] recursively:recursive];
  316. }
  317. }
  318. }
  319. }
  320. - (void)localizeCell:(NSCell *)cell recursively:(BOOL)recursive {
  321. if (cell) {
  322. NSString *title = [cell title];
  323. NSString *localizedTitle = [self localizedStringForString:title];
  324. if (localizedTitle) {
  325. [cell setTitle:localizedTitle];
  326. }
  327. [self localizeObject:[cell menu] recursively:recursive];
  328. id obj = [cell representedObject];
  329. [self localizeObject:obj recursively:recursive];
  330. }
  331. }
  332. - (void)localizeBindings:(id)object {
  333. NSArray *exposedBindings = [object exposedBindings];
  334. if (exposedBindings) {
  335. NSString *optionsToLocalize[] = {
  336. NSDisplayNameBindingOption,
  337. NSDisplayPatternBindingOption,
  338. NSMultipleValuesPlaceholderBindingOption,
  339. NSNoSelectionPlaceholderBindingOption,
  340. NSNotApplicablePlaceholderBindingOption,
  341. NSNullPlaceholderBindingOption,
  342. };
  343. Class stringClass = [NSString class];
  344. NSString *exposedBinding;
  345. GTM_FOREACH_OBJECT(exposedBinding, exposedBindings) {
  346. NSDictionary *bindingInfo = [object infoForBinding:exposedBinding];
  347. if (bindingInfo) {
  348. id observedObject = [bindingInfo objectForKey:NSObservedObjectKey];
  349. NSString *path = [bindingInfo objectForKey:NSObservedKeyPathKey];
  350. NSDictionary *options = [bindingInfo objectForKey:NSOptionsKey];
  351. if (observedObject && path && options) {
  352. NSMutableDictionary *newOptions
  353. = [NSMutableDictionary dictionaryWithDictionary:options];
  354. BOOL valueChanged = NO;
  355. for (size_t i = 0;
  356. i < sizeof(optionsToLocalize) / sizeof(optionsToLocalize[0]);
  357. ++i) {
  358. NSString *key = optionsToLocalize[i];
  359. NSString *value = [newOptions objectForKey:key];
  360. if ([value isKindOfClass:stringClass]) {
  361. NSString *localizedValue = [self localizedStringForString:value];
  362. if (localizedValue) {
  363. valueChanged = YES;
  364. [newOptions setObject:localizedValue forKey:key];
  365. }
  366. }
  367. }
  368. if (valueChanged) {
  369. // Only unbind and rebind if there is a change.
  370. [object unbind:exposedBinding];
  371. [object bind:exposedBinding
  372. toObject:observedObject
  373. withKeyPath:path
  374. options:newOptions];
  375. }
  376. }
  377. }
  378. }
  379. }
  380. }
  381. - (void)localizeAccessibility:(id)object {
  382. NSArray *supportedAttrs = [object accessibilityAttributeNames];
  383. if ([supportedAttrs containsObject:NSAccessibilityHelpAttribute]) {
  384. NSString *accessibilityHelp
  385. = [object accessibilityAttributeValue:NSAccessibilityHelpAttribute];
  386. if (accessibilityHelp) {
  387. NSString *localizedAccessibilityHelp
  388. = [self localizedStringForString:accessibilityHelp];
  389. if (localizedAccessibilityHelp) {
  390. [object accessibilitySetValue:localizedAccessibilityHelp
  391. forAttribute:NSAccessibilityHelpAttribute];
  392. }
  393. }
  394. }
  395. // We cannot do the same thing with NSAccessibilityDescriptionAttribute; see
  396. // the links in the header file for more details.
  397. }
  398. @end