Add localization/language domain and locale support

git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@7910 72102866-910b-0410-8b05-ffd578937521
This commit is contained in:
Adam Fedor 2000-10-27 15:54:35 +00:00
parent 114fd9ed5d
commit 25e9904876
22 changed files with 1042 additions and 169 deletions

View file

@ -70,6 +70,7 @@ FILE_AUTHORS = \
GNU_MFILES = \
GSCompatibility.m \
GSLocale.m \
Unicode.m \
behavior.m \
o_array.m \
@ -117,6 +118,7 @@ libgnustep-base.def
GNU_HEADERS = \
fast.x \
GSLocale.h \
GSUnion.h \
GSIArray.h \
GSIMap.h \

196
Source/GSLocale.m Normal file
View file

@ -0,0 +1,196 @@
/* GSLocale - various functions for localization
Copyright (C) 2000 Free Software Foundation, Inc.
Written by: Adam Fedor <fedor@gnu.org>
Created: Oct 2000
This file is part of the GNUstep Base Library.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with this library; if not, write to the Free
Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA.
*/
#include <config.h>
#include <base/GSLocale.h>
#include <Foundation/NSDictionary.h>
#include <Foundation/NSArray.h>
#ifdef HAVE_LOCALE_H
#include <locale.h>
#include <langinfo.h>
#include <Foundation/NSUserDefaults.h>
#include <Foundation/NSBundle.h>
/* Set the locale for libc functions from the supplied string or from
the environment if not specified. This function should be called
as soon as possible after the start of the program. Passing
@"" will set the locale from the environment varialbes LC_ALL or LANG (or
whatever is specified by setlocale) Passing nil will just return the
current locale. */
NSString *
GSSetLocale(NSString *locale)
{
const char *clocale;
clocale = NULL;
if (locale)
clocale = [locale cString];
clocale = setlocale(LC_ALL, clocale);
if (clocale == NULL || strcmp(clocale, "C") == 0
|| strcmp(clocale, "POSIX") == 0)
clocale = NULL;
locale = nil;
if (clocale)
locale = [NSString stringWithCString: clocale];
return locale;
}
#define GSLanginfo(value) [NSString stringWithCString: nl_langinfo (value)]
/* Creates a locale dictionary from information provided by i18n functions.
Many, but not all, of the keys are filled in or inferred from the
available information */
NSDictionary *
GSDomainFromDefaultLocale(void)
{
int i;
struct lconv *lconv;
NSMutableDictionary *dict;
NSMutableArray *arr;
NSString *str1, *str2;
/* Time/Date Information */
dict = [NSMutableDictionary dictionary];
arr = [NSMutableArray arrayWithCapacity: 7];
for (i = 0; i < 7; i++)
[arr addObject: GSLanginfo(DAY_1+i)];
[dict setObject: arr forKey: NSWeekDayNameArray];
arr = [NSMutableArray arrayWithCapacity: 7];
for (i = 0; i < 7; i++)
[arr addObject: GSLanginfo(ABDAY_1+i)];
[dict setObject: arr forKey: NSShortWeekDayNameArray];
arr = [NSMutableArray arrayWithCapacity: 12];
for (i = 0; i < 12; i++)
[arr addObject: GSLanginfo(MON_1+i)];
[dict setObject: arr forKey: NSMonthNameArray];
arr = [NSMutableArray arrayWithCapacity: 12];
for (i = 0; i < 12; i++)
[arr addObject: GSLanginfo(ABMON_1+i)];
[dict setObject: arr forKey: NSShortMonthNameArray];
str1 = GSLanginfo(AM_STR);
str2 = GSLanginfo(PM_STR);
if (str1 && str2)
[dict setObject: [NSArray arrayWithObjects: str1, str2, nil]
forKey: NSAMPMDesignation];
[dict setObject: GSLanginfo(D_T_FMT) forKey: NSTimeDateFormatString];
[dict setObject: GSLanginfo(D_FMT) forKey: NSShortDateFormatString];
[dict setObject: GSLanginfo(T_FMT) forKey: NSTimeFormatString];
lconv = localeconv();
/* Currency Information */
if (lconv->currency_symbol)
[dict setObject: [NSString stringWithCString: lconv->currency_symbol ]
forKey: NSCurrencySymbol];
if (lconv->int_curr_symbol)
[dict setObject: [NSString stringWithCString: lconv->int_curr_symbol]
forKey: NSInternationalCurrencyString ];
if (lconv->mon_decimal_point)
[dict setObject: [NSString stringWithCString: lconv->mon_decimal_point]
forKey: NSInternationalCurrencyString ];
if (lconv->mon_thousands_sep)
[dict setObject: [NSString stringWithCString: lconv->mon_thousands_sep]
forKey: NSInternationalCurrencyString ];
/* FIXME: Get currency format from localeconv */
/* Miscellaneous */
if (nl_langinfo(YESSTR))
[dict setObject: GSLanginfo(YESSTR) forKey: @"NSYesStr"];
if (nl_langinfo(NOSTR))
[dict setObject: GSLanginfo(NOSTR) forKey: @"NSNoStr"];
str1 = [NSString stringWithCString: setlocale(LC_ALL, NULL)];
[dict setObject: str1 forKey: NSLocale];
str2 = GSLanguageFromLocale(str1);
if (str2)
[dict setObject: str2 forKey: NSLanguageName];
return dict;
}
NSString *
GSLanguageFromLocale(NSString *locale)
{
NSString *language = nil;
NSString *aliases = nil;
if (locale == nil)
return @"English";
aliases = [NSBundle pathForGNUstepResource: @"Locale"
ofType: @"aliases"
inDirectory: @"Resources/Languages"];
if (aliases)
{
NSDictionary *dict;
dict = [NSDictionary dictionaryWithContentsOfFile: aliases];
language = [dict objectForKey: locale];
if (language == nil && [locale pathExtension])
{
locale = [locale stringByDeletingPathExtension];
language = [dict objectForKey: locale];
}
if (language == nil)
{
locale = [locale substringFromRange: NSMakeRange(0, 2)];
language = [dict objectForKey: locale];
}
}
return language;
}
#else /* HAVE_LOCALE_H */
NSString *
GSSetLocale(NSString *locale)
{
return nil;
}
NSDictionary *
GSDomainFromDefaultLocale(void)
{
return nil;
}
NSString *
GSLanguageFromLocale(NSString *locale)
{
return nil;
}
#endif /* !HAVE_LOCALE_H */

View file

@ -361,7 +361,7 @@ static inline int getDigits(const char *from, char *to, int limit)
{
if (source[sourceIdx] != format[formatIdx])
{
NSLog(@"Expected literal '%c' but gmtt '%c'",
NSLog(@"Expected literal '%c' but got '%c'",
format[formatIdx], source[sourceIdx]);
}
sourceIdx++;

View file

@ -42,6 +42,8 @@
#include <Foundation/NSProcessInfo.h>
#include <Foundation/NSDistributedLock.h>
#include <Foundation/NSRunLoop.h>
#include <Foundation/NSBundle.h>
#include <base/GSLocale.h>
/* Wait for access */
#define _MAX_COUNT 5 /* Max 10 sec. */
@ -76,6 +78,7 @@ static Class NSStringClass;
*************************************************************************/
static NSUserDefaults *sharedDefaults = nil;
static NSMutableString *processName = nil;
static NSMutableArray *userLanguages = nil;
/*************************************************************************
*** Getting the Shared Instance
@ -103,6 +106,109 @@ static BOOL setSharedDefaults = NO; /* Flag to prevent infinite recursion */
DESTROY(sharedDefaults);
}
/* Create a locale dictionary when we have absolutely no information
about the locale. This method should go away, since it will never
be called in a properly installed system. */
+ (NSDictionary *) _unlocalizedDefaults
{
NSDictionary *registrationDefaults;
NSArray *ampm;
NSArray *long_day;
NSArray *long_month;
NSArray *short_day;
NSArray *short_month;
NSArray *earlyt;
NSArray *latert;
NSArray *hour_names;
NSArray *ymw_names;
ampm = [NSArray arrayWithObjects: @"AM", @"PM", nil];
short_month = [NSArray arrayWithObjects:
@"Jan",
@"Feb",
@"Mar",
@"Apr",
@"May",
@"Jun",
@"Jul",
@"Aug",
@"Sep",
@"Oct",
@"Nov",
@"Dec",
nil];
long_month = [NSArray arrayWithObjects:
@"January",
@"February",
@"March",
@"April",
@"May",
@"June",
@"July",
@"August",
@"September",
@"October",
@"November",
@"December",
nil];
short_day = [NSArray arrayWithObjects:
@"Sun",
@"Mon",
@"Tue",
@"Wed",
@"Thu",
@"Fri",
@"Sat",
nil];
long_day = [NSArray arrayWithObjects:
@"Sunday",
@"Monday",
@"Tuesday",
@"Wednesday",
@"Thursday",
@"Friday",
@"Saturday",
nil];
earlyt = [NSArray arrayWithObjects:
@"prior",
@"last",
@"past",
@"ago",
nil];
latert = [NSArray arrayWithObjects:
@"next",
nil];
ymw_names = [NSArray arrayWithObjects:
@"year",
@"month",
@"week",
nil];
hour_names = [NSArray arrayWithObjects:
[NSArray arrayWithObjects: @"0", @"midnight", nil],
[NSArray arrayWithObjects: @"12", @"noon", @"lunch", nil],
[NSArray arrayWithObjects: @"10", @"morning", nil],
[NSArray arrayWithObjects: @"14", @"afternoon", nil],
[NSArray arrayWithObjects: @"19", @"dinner", nil],
nil];
registrationDefaults = [NSDictionary dictionaryWithObjectsAndKeys:
ampm, NSAMPMDesignation,
long_month, NSMonthNameArray,
long_day, NSWeekDayNameArray,
short_month, NSShortMonthNameArray,
short_day, NSShortWeekDayNameArray,
@"DMYH", NSDateTimeOrdering,
@"tomorrow", NSNextDayDesignations,
@"nextday", NSNextNextDayDesignations,
@"yesterday", NSPriorDayDesignations,
@"today", NSThisDayDesignations,
earlyt, NSEarlierTimeDesignations,
latert, NSLaterTimeDesignations,
hour_names, NSHourNameDesignations,
ymw_names, NSYearMonthWeekDesignations,
nil];
return registrationDefaults;
}
+ (NSUserDefaults*) standardUserDefaults
/*
Returns the shared defaults object. If it doesn't exist yet, it's
@ -112,118 +218,67 @@ static BOOL setSharedDefaults = NO; /* Flag to prevent infinite recursion */
convenience; other instances may also be created.
*/
{
BOOL added_locale, added_lang;
id lang;
NSArray *uL;
NSEnumerator *enumerator;
if (setSharedDefaults)
return sharedDefaults;
setSharedDefaults = YES;
// Create new sharedDefaults (NOTE: Not added to the autorelease pool!)
sharedDefaults = [[self alloc] init];
if (sharedDefaults == nil)
{
NSLog(@"WARNING - unable to create shared user defaults!\n");
return nil;
}
[sharedDefaults __createStandardSearchList];
if (sharedDefaults)
/* Set up language constants */
added_locale = NO;
added_lang = NO;
uL = [[self class] userLanguages];
enumerator = [uL objectEnumerator];
while ((lang = [enumerator nextObject]))
{
NSUserDefaults *defs;
NSDictionary *registrationDefaults;
NSArray *ampm;
NSArray *long_day;
NSArray *long_month;
NSArray *short_day;
NSArray *short_month;
NSArray *earlyt;
NSArray *latert;
NSArray *hour_names;
NSArray *ymw_names;
defs = [NSUserDefaults standardUserDefaults];
ampm = [NSArray arrayWithObjects: @"AM", @"PM", nil];
short_month = [NSArray arrayWithObjects:
@"Jan",
@"Feb",
@"Mar",
@"Apr",
@"May",
@"Jun",
@"Jul",
@"Aug",
@"Sep",
@"Oct",
@"Nov",
@"Dec",
nil];
long_month = [NSArray arrayWithObjects:
@"January",
@"February",
@"March",
@"April",
@"May",
@"June",
@"July",
@"August",
@"September",
@"October",
@"November",
@"December",
nil];
short_day = [NSArray arrayWithObjects:
@"Sun",
@"Mon",
@"Tue",
@"Wed",
@"Thu",
@"Fri",
@"Sat",
nil];
long_day = [NSArray arrayWithObjects:
@"Sunday",
@"Monday",
@"Tuesday",
@"Wednesday",
@"Thursday",
@"Friday",
@"Saturday",
nil];
earlyt = [NSArray arrayWithObjects:
@"prior",
@"last",
@"past",
@"ago",
nil];
latert = [NSArray arrayWithObjects:
@"next",
nil];
ymw_names = [NSArray arrayWithObjects:
@"year",
@"month",
@"week",
nil];
hour_names = [NSArray arrayWithObjects:
[NSArray arrayWithObjects: @"0", @"midnight", nil],
[NSArray arrayWithObjects: @"12", @"noon", @"lunch", nil],
[NSArray arrayWithObjects: @"10", @"morning", nil],
[NSArray arrayWithObjects: @"14", @"afternoon", nil],
[NSArray arrayWithObjects: @"19", @"dinner", nil],
nil];
registrationDefaults = [NSDictionary dictionaryWithObjectsAndKeys:
ampm, NSAMPMDesignation,
long_month, NSMonthNameArray,
long_day, NSWeekDayNameArray,
short_month, NSShortMonthNameArray,
short_day, NSShortWeekDayNameArray,
@"DMYH", NSDateTimeOrdering,
@"tomorrow", NSNextDayDesignations,
@"nextday", NSNextNextDayDesignations,
@"yesterday", NSPriorDayDesignations,
@"today", NSThisDayDesignations,
earlyt, NSEarlierTimeDesignations,
latert, NSLaterTimeDesignations,
hour_names, NSHourNameDesignations,
ymw_names, NSYearMonthWeekDesignations,
nil];
[sharedDefaults registerDefaults: registrationDefaults];
NSString *path;
NSDictionary *dict;
path = [NSBundle pathForGNUstepResource: lang
ofType: nil
inDirectory: @"Resources/Languages"];
dict = nil;
if (path)
dict = [NSDictionary dictionaryWithContentsOfFile: path];
if (dict)
{
[sharedDefaults setVolatileDomain: dict forName: lang];
added_lang = YES;
}
else if (added_locale == NO)
{
NSString *locale = GSSetLocale(nil);
if (locale == nil)
break;
/* See if we can get the dictionary from i18n functions.
Note that we get the dict from the current locale regardless
of what 'lang' is, since it should match anyway. */
/* Also, I don't think that the i18n routines can handle more than
one locale, but tell me if I'm wrong... */
if (GSLanguageFromLocale(locale))
lang = GSLanguageFromLocale(locale);
dict = GSDomainFromDefaultLocale();
if (dict)
[sharedDefaults setVolatileDomain: dict forName: lang];
added_locale = YES;
}
}
else
if (added_lang == NO)
{
NSLog(@"WARNING - unable to create shared user defaults!\n");
/* Ack! We should never get here */
NSLog(@"Improper installation: No language locale found");
[sharedDefaults registerDefaults: [self _unlocalizedDefaults]];
}
return sharedDefaults;
}
@ -231,14 +286,33 @@ static BOOL setSharedDefaults = NO; /* Flag to prevent infinite recursion */
+ (NSArray*) userLanguages
{
NSMutableArray *uL = [NSMutableArray arrayWithCapacity: 5];
NSArray *currLang = [[self standardUserDefaults]
stringArrayForKey: @"Languages"];
NSEnumerator *enumerator;
id obj;
if (!currLang)
{ // Try to build it from the env
NSArray *currLang;
NSString *locale;
if (userLanguages)
return userLanguages;
userLanguages = RETAIN([NSMutableArray arrayWithCapacity: 5]);
locale = GSSetLocale(@"");
if (sharedDefaults == nil)
{
/* Create our own defaults to get "Languages" since sharedDefaults
depends on us */
NSUserDefaults *tempDefaults;
tempDefaults = [[self alloc] init];
if (tempDefaults)
{
[tempDefaults __createStandardSearchList];
currLang = [tempDefaults stringArrayForKey: @"Languages"];
RELEASE(tempDefaults);
}
}
else
currLang = [[self standardUserDefaults] stringArrayForKey: @"Languages"];
if (currLang == nil && locale && GSLanguageFromLocale(locale))
currLang = [NSArray arrayWithObject: GSLanguageFromLocale(locale)];
if (currLang == nil)
{
const char *env_list;
NSString *env;
@ -249,19 +323,17 @@ static BOOL setSharedDefaults = NO; /* Flag to prevent infinite recursion */
currLang = RETAIN([env componentsSeparatedByString: @";"]);
}
}
if (currLang)
[uL addObjectsFromArray: currLang];
// Check if "English" is includet
enumerator = [uL objectEnumerator];
while ((obj = [enumerator nextObject]))
{
if ([obj isEqualToString: @"English"])
return uL;
}
[uL addObject: @"English"];
if (currLang)
[userLanguages addObjectsFromArray: currLang];
/* Check if "English" is included. We do this to make sure all the
required language constants are set somewhere if they aren't set
in the default language */
if ([userLanguages containsObject: @"English"] == NO)
[userLanguages addObject: @"English"];
return uL;
return userLanguages;
}
+ (void) setUserLanguages: (NSArray*)languages
@ -941,15 +1013,15 @@ static NSString *pathForUser(NSString *user)
// 2. Application
[_searchList addObject: processName];
// 3. User's preferred languages
// 3. NSGlobalDomain
[_searchList addObject: NSGlobalDomain];
// 4. User's preferred languages
while ((object = [enumerator nextObject]))
{
[_searchList addObject: object];
}
// 4. NSGlobalDomain
[_searchList addObject: NSGlobalDomain];
// 5. NSRegistrationDomain
[_searchList addObject: NSRegistrationDomain];

View file

@ -248,6 +248,9 @@ NSString *NSLanguageName;
NSString *NSFormalName;
/* For GNUstep */
NSString *NSLocale;
/*
* Keys for the NSDictionary returned by [NSConnection -statistics]
@ -409,6 +412,8 @@ GSBuildStrings()
= [[GSCString alloc] initWithCString: "NSLaterTimeDesignations"];
NSLoadedClasses
= [[GSCString alloc] initWithCString: "NSLoadedClasses"];
NSLocale
= [[GSCString alloc] initWithCString: "NSLocale"];
*(NSString**)&NSMallocException
= [[GSCString alloc] initWithCString: "NSMallocException"];
NSMonthNameArray