]> err.no Git - sope/blob - sope-appserver/WEExtensions/WEStringTableManager.m
added strict OSX bundle dependencies
[sope] / sope-appserver / WEExtensions / WEStringTableManager.m
1 /*
2   Copyright (C) 2004-2005 SKYRIX Software AG
3
4   This file is part of SOPE.
5
6   SOPE is free software; you can redistribute it and/or modify it under
7   the terms of the GNU Lesser General Public License as published by the
8   Free Software Foundation; either version 2, or (at your option) any
9   later version.
10
11   SOPE is distributed in the hope that it will be useful, but WITHOUT ANY
12   WARRANTY; without even the implied warranty of MERCHANTABILITY or
13   FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
14   License for more details.
15
16   You should have received a copy of the GNU Lesser General Public
17   License along with SOPE; see the file COPYING.  If not, write to the
18   Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
19   02111-1307, USA.
20 */
21
22 #include "WEStringTableManager.h"
23 #include "WEStringTable.h"
24 #include "WEResourceManager.h"
25 #include "common.h"
26
27 @implementation WEStringTableManager
28
29 static NSNull *null = nil;
30 static BOOL   debugOn = NO;
31
32 + (void)initialize {
33   NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
34   
35   if (null == nil) null = [[NSNull null] retain];
36   
37   if ((debugOn = [ud boolForKey:@"WEStringTableManagerDebugEnabled"]))
38     NSLog(@"Note: WEStringTableManager debugging is enabled.");
39 }
40
41 - (id)init {
42   if ((self = [super init])) {
43     if ([WOApplication isCachingEnabled]) {
44       // TODO: find proper capacities
45       self->nameToTable = 
46         [[NSMutableDictionary alloc] initWithCapacity:16];
47       self->compLabelCache = 
48         [[NSMutableDictionary alloc] initWithCapacity:512];
49     }
50     else
51       [self logWithFormat:@"Note: label caching is disabled (slow!)."];
52   }
53   return self;
54 }
55
56 - (void)dealloc {
57   [self->compLabelCache release];
58   [self->nameToTable    release];
59   [super dealloc];
60 }
61
62 /* accessors */
63
64 - (NGBundleManager *)bundleManager {
65   static NGBundleManager *bm = nil; // THREAD
66   if (bm == nil) bm = [[NGBundleManager defaultBundleManager] retain];
67   return bm;
68 }
69
70 /* resource pathes */
71
72 + (NSString *)findResourceDirectoryNamed:(NSString *)_name
73   fhsName:(NSString *)_fhs
74 {
75   static NSDictionary *env = nil;
76   NSFileManager *fm;
77   NSString      *path;
78   id tmp;
79   
80   if (env == nil) env = [[[NSProcessInfo processInfo] environment] retain];
81   fm = [NSFileManager defaultManager];
82
83   /* look in GNUstep pathes */
84   
85   tmp = [[WEResourceManager rootPathesInGNUstep] objectEnumerator];
86   while ((path = [tmp nextObject]) != nil) {
87     path = [path stringByAppendingPathComponent:_name];
88     
89     if ([fm fileExistsAtPath:path])
90       return path;
91   }
92   
93   /* look in FHS pathes */
94   
95   tmp = [[WEResourceManager rootPathesInFHS] objectEnumerator];
96   while ((path = [tmp nextObject])) {
97     path = [path stringByAppendingPathComponent:_fhs];
98     if ([fm fileExistsAtPath:path])
99       return path;
100   }
101   return nil;
102 }
103
104 /* labels */
105
106 - (NSString *)labelForKey:(NSString *)_key component:(WOComponent *)_component{
107   WOApplication   *app;
108   NSArray         *langs;
109   NSBundle        *bundle;
110   id              value = nil;
111   NGBundleManager *bm;
112   NSString        *cname;
113   id cacheKey;
114   
115   if ([_key length] == 0)
116     return _key;
117   if (_component == nil)
118     return _key;
119
120   if (null == nil) null = [[NSNull null] retain];
121   
122   cname = [_component name];
123   langs = [(WOSession *)[_component session] languages];
124   
125   /* cache ?? */
126   
127   {
128     /* cache cachekeys here ;-) */
129     cacheKey = [NSArray arrayWithObjects:_key, cname, langs, nil];
130   }
131   
132   if ((value = [self->compLabelCache objectForKey:cacheKey]) != nil) {
133     if (value == null) value = nil;
134     return value;
135   }
136   
137   /* lookup bundle */
138   
139   app = [WOApplication application];
140   
141   bm = [NGBundleManager defaultBundleManager];
142   bundle = [bm bundleProvidingResource:cname ofType:@"WOComponents"];
143   if (bundle == nil)
144     bundle = [NGBundle bundleForClass:[_component class]];
145   
146   /* look in BundleName.strings */
147   
148   value = [self stringForKey:_key
149                 inTableNamed:[bundle bundleName]
150                 withDefaultValue:nil
151                 languages:langs];
152   if (value)
153     goto done;
154   
155   /* look in App.strings */
156   value = [self stringForKey:_key
157                 inTableNamed:cname
158                 withDefaultValue:nil
159                 languages:langs];
160   if (value)
161     goto done;
162   
163   /* no string found, use key as string */
164   value = _key;
165   
166  done:
167 #if DEBUG
168   NSAssert(value, @"missing value ..");
169 #endif
170   
171   [self->compLabelCache setObject:value forKey:cacheKey];
172 #if 0
173   NSLog(@"%s: label cache size now: %i", __PRETTY_FUNCTION__,
174         [self->compLabelCache count]);
175 #endif
176   return value;
177 }
178
179 - (NSString *)_cachedStringForKey:(NSString *)_key
180   inTableNamed:(NSString *)_tableName
181   withDefaultValue:(NSString *)_default
182   languages:(NSArray *)_languages
183 {
184   if (_tableName == nil) _tableName = @"default";
185
186   /* look into string cache */
187   
188   if ([_languages count] > 0) {
189     NSString *tname;
190     NSRange  r;
191     NSString *language;
192     id       table;
193     
194     language = [_languages objectAtIndex:0];
195     
196     r = [language rangeOfString:@"_"];
197     if (r.length > 0)
198       language = [language substringToIndex:r.location];
199     
200     tname = [language stringByAppendingString:@"+++"];
201     tname = [tname stringByAppendingString:_tableName];
202     
203     if ((table = [self->nameToTable objectForKey:tname])) {
204       if (debugOn) {
205         [self logWithFormat:
206                 @"resolved label %@ table %@ in lang %@ (languages=%@)",
207                 _key, _tableName, language,
208                 [_languages componentsJoinedByString:@","]];
209       }
210       return [table stringForKey:_key withDefaultValue:_default];
211     }
212   }
213   else if ([_languages count] == 0) {
214     id table;
215     
216     NSLog(@"WARNING: called %s without languages array %@ !",
217           __PRETTY_FUNCTION__, _languages);
218     
219     if ((table = [self->nameToTable objectForKey:_tableName]))
220       return [table stringForKey:_key withDefaultValue:_default];
221   }
222   
223   return nil;
224 }
225
226 - (NSString *)_bundleStringForKey:(NSString *)_key
227   inTableNamed:(NSString *)_tableName
228   withDefaultValue:(NSString *)_default
229   languages:(NSArray *)_languages
230 {
231   NSBundle     *bundle;
232   NSString     *path;
233   NSEnumerator *e;
234   NSString     *language;
235   
236   if (_tableName == nil) _tableName = @"default";
237   
238   bundle = [[self bundleManager]
239                   bundleProvidingResource:_tableName
240                   ofType:@"strings"];
241   if (bundle == nil)
242     return nil;
243
244   e = [_languages objectEnumerator];
245   while ((language = [e nextObject])) {
246     NSArray *ls;
247     NSRange r;
248       
249     r = [language rangeOfString:@"_"];
250     if (r.length > 0)
251       language = [language substringToIndex:r.location];
252       
253     ls = [NSArray arrayWithObject:language];
254       
255     path = [bundle pathForResource:_tableName
256                    ofType:@"strings"
257                    languages:ls];
258     if (path) {
259       NSString *tname;
260       id table;
261       
262       tname = [language stringByAppendingString:@"+++"];
263       tname = [tname stringByAppendingString:_tableName];
264         
265       table = [WEStringTable stringTableWithPath:path];
266       [self->nameToTable setObject:table forKey:tname];
267       
268       return [table stringForKey:_key withDefaultValue:_default];
269     }
270   }
271     
272   path = [bundle pathForResource:_tableName
273                  ofType:@"strings"
274                  languages:nil];
275     
276   if (path) {
277     WEStringTable *table;
278     
279     table = [WEStringTable stringTableWithPath:path];
280     
281     [self->nameToTable setObject:table forKey:_tableName];
282       
283     return [table stringForKey:_key withDefaultValue:_default];
284   }
285   else if (debugOn) {
286     NSLog(@"WARNING: missing string table %@ in bundle %@",
287           _tableName, [bundle bundlePath]);
288   }
289   return _default;
290 }
291
292 + (NSString *)gsTranslationsSubpath {
293   NSString *p;
294   
295   p = [[WOApplication application] gsTranslationsDirectoryName];
296   if ([p length] == 0)
297     return nil;
298   
299   p = [@"Resources/" stringByAppendingString:p];
300   return p;
301 }
302 + (NSString *)fhsTranslationsSubpath {
303   NSString *p;
304   
305   p = [[WOApplication application] shareTranslationsDirectoryName];
306   if ([p length] == 0)
307     return nil;
308   
309   p = [@"share/" stringByAppendingString:p];
310   return [p stringByAppendingString:@"/translations/"];
311 }
312
313 + (NSArray *)availableTranslations {
314   static NSArray *ltranslations = nil;
315   NSMutableSet  *translations;
316   NSEnumerator  *e;
317   NSFileManager *fm;
318   NSString      *path;
319   NSArray       *translationPathes;
320
321   if (ltranslations != nil)
322     return ltranslations;
323   
324   // TODO: use lists ..., or maybe NGResourceLocator
325   path = [self findResourceDirectoryNamed:[self gsTranslationsSubpath]
326                fhsName:[self fhsTranslationsSubpath]];
327   if (path == nil)
328     return nil;
329
330   translationPathes = [NSArray arrayWithObject:path];
331   translations = [NSMutableSet setWithCapacity:16];
332   fm           = [NSFileManager defaultManager];
333   
334   e = [translationPathes objectEnumerator];
335   while ((path = [e nextObject]) != nil) {
336     NSEnumerator *dl;
337     NSString *l;
338     
339     dl = [[fm directoryContentsAtPath:path] objectEnumerator];
340     while ((l = [dl nextObject]) != nil) {
341       if (![l hasSuffix:@".lproj"])
342         continue;
343       if ([l hasPrefix:@"."])
344         continue;
345       
346       l = [l stringByDeletingPathExtension];
347       [translations addObject:l];
348     }
349   }
350   
351   ltranslations = [[translations allObjects] copy];
352   if ([ltranslations count] > 0) {
353     NSLog(@"Note: located translations: %@", 
354           [ltranslations componentsJoinedByString:@", "]);
355   }
356   else
357     NSLog(@"Note: located no additional translations.");
358   return ltranslations;
359 }
360
361 - (NSString *)_resourceStringForKey:(NSString *)_key
362   inTableNamed:(NSString *)_tableName
363   withDefaultValue:(NSString *)_default
364   languages:(NSArray *)_languages
365 {
366   NSString      *rpath, *path;
367   NSEnumerator  *e;
368   NSString      *language;
369   NSFileManager *fm;
370   
371   if (_tableName == nil) _tableName = @"default";
372   
373   fm = [NSFileManager defaultManager];
374   // TODO: should be NGResourceLocator method?
375   rpath = [[self class] findResourceDirectoryNamed:
376                           [[self class] gsTranslationsSubpath]
377                         fhsName:
378                           [[self class] fhsTranslationsSubpath]];
379   if (rpath == nil) {
380     [self logWithFormat:@"missing translations directory ..."];
381     return nil;
382   }
383   
384   /* look into language projects .. */
385   
386   e = [_languages objectEnumerator];
387   while ((language = [e nextObject]) != nil) {
388     WEStringTable *table;
389     NSArray  *ls;
390     NSRange  r;
391     NSString *tname;
392       
393     r = [language rangeOfString:@"_"];
394     if (r.length > 0)
395       language = [language substringToIndex:r.location];
396     
397     ls = [NSArray arrayWithObject:language];
398     
399     path = [_tableName stringByAppendingPathExtension:@"strings"];
400     path = [[language stringByAppendingPathExtension:@"lproj"]
401                       stringByAppendingPathComponent:path];
402     path = [rpath stringByAppendingPathComponent:path];
403     
404     if (![fm fileExistsAtPath:path])
405       continue;
406     
407     tname = [language stringByAppendingString:@"+++"];
408     tname = [tname stringByAppendingString:_tableName];
409     
410     table = [WEStringTable stringTableWithPath:path];
411     [self->nameToTable setObject:table forKey:tname];
412     
413     return [table stringForKey:_key withDefaultValue:_default];
414   }
415   
416   /* look into resource dir */
417   
418   path = [_tableName stringByAppendingPathExtension:@"strings"];
419   path = [rpath stringByAppendingPathComponent:path];
420
421   if ([fm fileExistsAtPath:path]) {
422     WEStringTable *table;
423     
424     table = [WEStringTable stringTableWithPath:path];
425     if (self->nameToTable == nil)
426       self->nameToTable = [[NSMutableDictionary alloc] initWithCapacity:16];
427     
428     [self->nameToTable setObject:table forKey:_tableName];
429     
430     return [table stringForKey:_key withDefaultValue:_default];
431   }
432   
433   if (debugOn)
434     NSLog(@"WARNING: missing string table %@ in %@", _tableName, path);
435   
436   return _default;
437 }
438
439 - (NSString *)stringForKey:(NSString *)_key
440   inTableNamed:(NSString *)_tableName
441   withDefaultValue:(NSString *)_default
442   languages:(NSArray *)_languages
443 {
444   NSString *s;
445   
446   if (_tableName == nil) _tableName = @"default";
447   
448   /* look into string cache */
449
450   s = [self _cachedStringForKey:_key
451             inTableNamed:_tableName
452             withDefaultValue:_default
453             languages:_languages];
454   if (s != nil) return s;
455   
456   if (self->nameToTable == nil)
457     self->nameToTable = [[NSMutableDictionary alloc] initWithCapacity:16];
458   
459   /* search for string */
460   
461   s = [self _resourceStringForKey:_key
462             inTableNamed:_tableName
463             withDefaultValue:nil
464             languages:_languages];
465   if (s != nil) return s;
466   
467   s = [self _bundleStringForKey:_key
468             inTableNamed:_tableName
469             withDefaultValue:nil
470             languages:_languages];
471   if (s != nil) return s;
472   
473   return nil;
474 }
475
476 /* debugging */
477
478 - (BOOL)isDebuggingEnabled {
479   return debugOn;
480 }
481
482 @end /* WEStringTableManager */
483
484 @implementation WOApplication(WEStringTableManager)
485
486 - (NSString *)shareTranslationsDirectoryName {
487   return [[self name] lowercaseString];
488 }
489 - (NSString *)gsTranslationsDirectoryName {
490   return [self name];
491 }
492
493 @end /* WOApplication(WEStringTableManager) */