2 * Copyright (c) 2014 Apple Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
25 Copyright (c) 1999-2014, Apple Inc. All rights reserved.
26 Responsibility: Tony Parker
29 #include "CFBundle_Internal.h"
30 #if DEPLOYMENT_TARGET_MACOSX
34 #include <CoreFoundation/CFPreferences.h>
35 #include <CoreFoundation/CFURLAccess.h>
38 #pragma mark Localized Strings
41 CF_EXPORT CFStringRef
CFBundleCopyLocalizedString(CFBundleRef bundle
, CFStringRef key
, CFStringRef value
, CFStringRef tableName
) {
42 return CFBundleCopyLocalizedStringForLocalization(bundle
, key
, value
, tableName
, NULL
);
45 CF_EXPORT CFStringRef
CFBundleCopyLocalizedStringForLocalization(CFBundleRef bundle
, CFStringRef key
, CFStringRef value
, CFStringRef tableName
, CFStringRef localizationName
) {
46 CFStringRef result
= NULL
;
47 CFDictionaryRef stringTable
= NULL
;
49 if (!key
) return (value
? (CFStringRef
)CFRetain(value
) : (CFStringRef
)CFRetain(CFSTR("")));
51 // Make sure to check the mixed localizations key early -- if the main bundle has not yet been cached, then we need to create the cache of the Info.plist before we start asking for resources (11172381)
52 (void)CFBundleAllowMixedLocalizations();
54 if (!tableName
|| CFEqual(tableName
, CFSTR(""))) tableName
= _CFBundleDefaultStringTableName
;
56 __CFLock(&bundle
->_lock
);
57 // Only consult the cache when a specific localization has not been requested. We only cache results for the preferred language as determined by normal bundle lookup rules.
58 if (!localizationName
&& bundle
->_stringTable
) {
59 stringTable
= (CFDictionaryRef
)CFDictionaryGetValue(bundle
->_stringTable
, tableName
);
60 if (stringTable
) CFRetain(stringTable
);
64 // Go load the table. First, unlock so we don't hold the lock across file system access.
65 __CFUnlock(&bundle
->_lock
);
67 CFURLRef tableURL
= NULL
;
68 if (localizationName
) {
69 tableURL
= CFBundleCopyResourceURLForLocalization(bundle
, tableName
, _CFBundleStringTableType
, NULL
, localizationName
);
71 tableURL
= CFBundleCopyResourceURL(bundle
, tableName
, _CFBundleStringTableType
, NULL
);
75 CFStringRef nameForSharing
= NULL
;
77 CFDataRef tableData
= NULL
;
79 #pragma GCC diagnostic push
80 #pragma GCC diagnostic ignored "-Wdeprecated"
81 if (CFURLCreateDataAndPropertiesFromResource(kCFAllocatorSystemDefault
, tableURL
, &tableData
, NULL
, NULL
, &errCode
)) {
82 #pragma GCC diagnostic pop
83 CFErrorRef error
= NULL
;
84 stringTable
= (CFDictionaryRef
)CFPropertyListCreateWithData(CFGetAllocator(bundle
), tableData
, kCFPropertyListImmutable
, NULL
, &error
);
85 if (stringTable
&& CFDictionaryGetTypeID() != CFGetTypeID(stringTable
)) {
86 CFRelease(stringTable
);
89 if (!stringTable
&& error
) {
90 CFLog(kCFLogLevelError
, CFSTR("Unable to load string table file: %@ / %@: %@"), bundle
, tableName
, error
);
98 if (nameForSharing
) CFRelease(nameForSharing
);
99 if (tableURL
) CFRelease(tableURL
);
101 if (!stringTable
) stringTable
= CFDictionaryCreate(CFGetAllocator(bundle
), NULL
, NULL
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
103 if ((!CFStringHasSuffix(tableName
, CFSTR(".nocache")) || !_CFExecutableLinkedOnOrAfter(CFSystemVersionLeopard
)) && localizationName
== NULL
) {
104 // Take lock again, because this we will unlock after getting the value out of the table.
105 __CFLock(&bundle
->_lock
);
106 if (!bundle
->_stringTable
) bundle
->_stringTable
= CFDictionaryCreateMutable(CFGetAllocator(bundle
), 0, &kCFCopyStringDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
108 // If another thread beat us to setting this tableName, then we'll just replace it here.
109 CFDictionarySetValue(bundle
->_stringTable
, tableName
, stringTable
);
111 // Take lock again, because this we will unlock after getting the value out of the table.
112 __CFLock(&bundle
->_lock
);
116 result
= (CFStringRef
)CFDictionaryGetValue(stringTable
, key
);
121 __CFUnlock(&bundle
->_lock
);
122 CFRelease(stringTable
);
126 result
= (CFStringRef
)CFRetain(key
);
127 } else if (CFEqual(value
, CFSTR(""))) {
128 result
= (CFStringRef
)CFRetain(key
);
130 result
= (CFStringRef
)CFRetain(value
);
132 static Boolean capitalize
= false;
134 CFMutableStringRef capitalizedResult
= CFStringCreateMutableCopy(kCFAllocatorSystemDefault
, 0, result
);
135 CFLog(__kCFLogBundle
, CFSTR("Localizable string \"%@\" not found in strings table \"%@\" of bundle %@."), key
, tableName
, bundle
);
136 CFStringUppercase(capitalizedResult
, NULL
);
138 result
= capitalizedResult
;