ObjFW  Check-in [7fa757534a]

Overview
Comment:OFLocale: Support for translated plurals
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: 7fa757534a674261d73fa5db5f4c5a3ad5bc159476ccdefcefd0b2074294ef55
User & Date: js on 2020-04-11 23:10:45
Other Links: manifest | tags
Context
2020-04-12
11:43
Fix all Doxygen warnings check-in: 240eccca97 user: js tags: trunk
2020-04-11
23:10
OFLocale: Support for translated plurals check-in: 7fa757534a user: js tags: trunk
19:31
Make OFObject.h and OFString.h C-safe check-in: 6e7e19252b user: js tags: trunk
Changes

Modified src/OFLocale.m from [36d525a782] to [af7c460737].

23
24
25
26
27
28
29

30
31
32
33
34
35
36
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37







+







#import "OFString.h"
#import "OFArray.h"
#import "OFDictionary.h"

#import "OFInitializationFailedException.h"
#import "OFInvalidArgumentException.h"
#import "OFInvalidEncodingException.h"
#import "OFInvalidFormatException.h"
#import "OFOpenItemFailedException.h"

#ifdef OF_AMIGAOS
# include <proto/dos.h>
# include <proto/exec.h>
# include <proto/locale.h>
#endif
79
80
81
82
83
84
85























































86
87
88
89
90
91
92
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







			*language = [OFString stringWithCString: locale
						       encoding: enc];
	} @finally {
		free(locale);
	}
}
#endif

static OFString *
evaluateDictionary(OFDictionary *dictionary, OFDictionary *variables)
{
	OFEnumerator *keyEnumerator = [dictionary keyEnumerator];
	OFEnumerator *objectEnumerator = [dictionary objectEnumerator];
	OFString *key;
	id object;

	while ((key = [keyEnumerator nextObject]) != nil &&
	    (object = [objectEnumerator nextObject]) != nil) {
		OFString *var, *expected;
		size_t pos;

		if (![key isKindOfClass: [OFString class]] ||
		    ![object isKindOfClass: [OFString class]])
			@throw [OFInvalidFormatException exception];

		if (key.length == 0)
			continue;

		pos = [key rangeOfString: @"="].location;
		if (pos == OF_NOT_FOUND)
			@throw [OFInvalidFormatException exception];

		var = [key substringWithRange: of_range(0, pos)];
		expected = [key substringWithRange:
		    of_range(pos + 1, key.length - pos - 1)];

		if ([[variables objectForKey: var] isEqual: expected])
			return object;
	}

	return [dictionary objectForKey: @""];
}

static OFString *
evaluateArray(OFArray *array, OFDictionary *variables)
{
	OFMutableString *string = [OFMutableString string];

	for (id object in array) {
		if ([object isKindOfClass: [OFString class]])
			[string appendString: object];
		else if ([object isKindOfClass: [OFDictionary class]])
			[string appendString:
			    evaluateDictionary(object, variables)];
		else
			@throw [OFInvalidFormatException exception];
	}

	[string makeImmutable];

	return string;
}

@implementation OFLocale
@synthesize language = _language, territory = _territory, encoding = _encoding;
@synthesize decimalPoint = _decimalPoint;

+ (OFLocale *)currentLocale
{
311
312
313
314
315
316
317


318
319
320





321
322
323
324
325
326
327
328
329

330
331
332
333
334
335
336
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391

392
393
394
395
396
397
398
399







+
+



+
+
+
+
+








-
+








- (OFString *)localizedStringForID: (OFConstantString *)ID
			  fallback: (OFConstantString *)fallback
			 arguments: (va_list)arguments
{
	OFMutableString *ret = [OFMutableString string];
	void *pool = objc_autoreleasePoolPush();
	OFMutableDictionary *variables;
	OFConstantString *name;
	const char *UTF8String = NULL;
	size_t last, UTF8StringLength;
	int state = 0;

	variables = [OFMutableDictionary dictionary];
	while ((name = va_arg(arguments, OFConstantString *)) != nil)
		[variables setObject: [va_arg(arguments, id) description]
			      forKey: name];

	for (OFDictionary *strings in _localizedStrings) {
		id string = [strings objectForKey: ID];

		if (string == nil)
			continue;

		if ([string isKindOfClass: [OFArray class]])
			string = [string componentsJoinedByString: @""];
			string = evaluateArray(string, variables);

		UTF8String = [string UTF8String];
		UTF8StringLength = [string UTF8StringLength];
		break;
	}

	if (UTF8String == NULL) {
358
359
360
361
362
363
364
365
366
367
368
369
370
371

372
373
374
375
376
377
378
379
380
381
382

383
384
385
386
387
388

389
390
391
392
393
394
395
396
397
398
399
421
422
423
424
425
426
427



428
429
430

431









432

433






434




435
436
437
438
439
440
441







-
-
-



-
+
-
-
-
-
-
-
-
-
-

-
+
-
-
-
-
-
-
+
-
-
-
-







			} else {
				[ret appendString: @"%"];
				state = 0;
			}
			break;
		case 2:
			if (UTF8String[i] == ']') {
				va_list argsCopy;
				OFConstantString *name;

				OFString *var = [OFString
				    stringWithUTF8String: UTF8String + last
						  length: i - last];

				OFString *value = [variables objectForKey: var];
				/*
				 * We loop, as most of the time, we only have
				 * one or maybe two variables, meaning looping
				 * is faster than constructing a dictionary.
				 */
				va_copy(argsCopy, arguments);
				while ((name = va_arg(argsCopy,
				    OFConstantString *)) != nil) {
					id value = va_arg(argsCopy, id);

					if (value == nil)
				if (value != nil)
						@throw
						    [OFInvalidArgumentException
						    exception];

					if ([name isEqual: var]) {
						[ret appendString:
					[ret appendString: value];
						    [value description]];
						break;
					}
				}

				last = i + 1;
				state = 0;
			}
			break;
		}
	}