Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 61 additions & 4 deletions src/CoreFoundation/CFBundle.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#nullable enable

using System;
using System.Globalization;
using System.Runtime.InteropServices;

using ObjCRuntime;
Expand Down Expand Up @@ -440,14 +441,17 @@ public NSUrl? SupportFilesDirectoryUrl {
[DllImport (Constants.CoreFoundationLibrary)]
extern static /* CFString */ IntPtr CFBundleCopyLocalizedString (IntPtr bundle, /* CFStringRef */ IntPtr key, /* CFStringRef */ IntPtr value, /* CFStringRef */ IntPtr tableName);

public string? GetLocalizedString (string key, string defaultValue, string? tableName)
/// <summary>Gets a localized string.</summary>
/// <param name="key">The key of the localized string to return.</param>
/// <param name="defaultValue">A default value if no localized string is found for the specified <paramref name="key" />.</param>
/// <param name="tableName">The name of the strings file to look in. The default is the 'Localizable.strings' file.</param>
/// <returns>A localized string for the key <paramref name="key" /> in the table <paramref name="tableName" />.</returns>
/// <remarks>Returns a localized string for the current bundle.</remarks>
public string? GetLocalizedString (string key, string defaultValue, string? tableName = null)
{
if (String.IsNullOrEmpty (key))
throw new ArgumentException (nameof (key));

if (String.IsNullOrEmpty (tableName))
throw new ArgumentException (nameof (tableName));

// we do allow null and simply use an empty string to avoid the extra check
if (defaultValue is null)
defaultValue = string.Empty;
Expand All @@ -465,6 +469,59 @@ public NSUrl? SupportFilesDirectoryUrl {
}
}

[SupportedOSPlatform ("macos15.4")]
[SupportedOSPlatform ("ios18.4")]
[SupportedOSPlatform ("tvos18.4")]
[SupportedOSPlatform ("maccatalyst18.4")]
[DllImport (Constants.CoreFoundationLibrary)]
extern static /* CFStringRef */ IntPtr CFBundleCopyLocalizedStringForLocalizations (/* CFBundleRef */ IntPtr bundle, /* CFStringRef */ IntPtr key, /* CFStringRef */ IntPtr value, /* CFStringRef */ IntPtr tableName, /* CFArrayRef */ IntPtr localizations);

/// <summary>Gets a localized string for a list of possible localizations.</summary>
/// <param name="key">The key of the localized string to return.</param>
/// <param name="defaultValue">A default value if no localized string is found for the specified <paramref name="key" />.</param>
/// <param name="tableName">The name of the strings file to look in. Specify <c>null</c> to use the default 'Localizable.strings' file.</param>
/// <param name="localizations">An array of BCP 47 language codes to determine which localized string to return.</param>
/// <returns>A localized string for the key <paramref name="key" /> in the table <paramref name="tableName" />.</returns>
/// <remarks>Returns the most suitable localized string for the current bundle.</remarks>
[SupportedOSPlatform ("macos15.4")]
[SupportedOSPlatform ("ios18.4")]
[SupportedOSPlatform ("tvos18.4")]
[SupportedOSPlatform ("maccatalyst18.4")]
public string? GetLocalizedString (string key, string? defaultValue, string? tableName, string [] localizations)
{
if (string.IsNullOrEmpty (key))
throw new ArgumentException (nameof (key));

if (localizations is null)
ObjCRuntime.ThrowHelper.ThrowArgumentNullException (nameof (localizations));

using var keyHandle = new TransientCFString (key);
using var defaultValueHandle = new TransientCFString (defaultValue);
using var tableNameHandle = new TransientCFString (tableName);
using var localizationsArrayHandle = new TransientCFObject (CFArray.Create (localizations));
var rv = CFBundleCopyLocalizedStringForLocalizations (Handle, keyHandle, defaultValueHandle, tableNameHandle, localizationsArrayHandle);
return CFString.FromHandle (rv, releaseHandle: true);
}

/// <summary>Gets a localized string for a list of possible localizations.</summary>
/// <param name="key">The key of the localized string to return.</param>
/// <param name="defaultValue">A default value if no localized string is found for the specified <paramref name="key" />.</param>
/// <param name="tableName">The name of the strings file to look in.</param>
/// <param name="localizations">An array of languages to determine which localized string to return.</param>
/// <returns>A localized string for the key <paramref name="key" /> in the table <paramref name="tableName" />.</returns>
/// <remarks>Returns the most suitable localized string for the current bundle.</remarks>
[SupportedOSPlatform ("macos15.4")]
[SupportedOSPlatform ("ios18.4")]
[SupportedOSPlatform ("tvos18.4")]
[SupportedOSPlatform ("maccatalyst18.4")]
public string? GetLocalizedString (string key, string defaultValue, string tableName, params CultureInfo [] localizations)
{
var locs = new string [localizations.Length];
for (var i = 0; i < localizations.Length; i++)
locs [i] = localizations [i].Name;
return GetLocalizedString (key, defaultValue, tableName, locs);
}

[DllImport (Constants.CoreFoundationLibrary)]
extern static /* CFArray */ IntPtr CFBundleCopyLocalizationsForPreferences (/* CFArrayRef */ IntPtr locArray, /* CFArrayRef */ IntPtr prefArray);

Expand Down
16 changes: 16 additions & 0 deletions src/CoreFoundation/CFString.cs
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,22 @@ internal static class CFObject {

[DllImport (Constants.CoreFoundationLibrary)]
internal extern static IntPtr CFRetain (IntPtr obj);

/// <summary>Does nothing if <paramref name="obj" /> is IntPtr.Zero, otherwise calls CFRelease.</summary>
internal static void SafeRelease (IntPtr obj)
{
if (obj == IntPtr.Zero)
return;
CFRelease (obj);
}

/// <summary>Does nothing if <paramref name="obj" /> is IntPtr.Zero, otherwise calls CFRetain.</summary>
internal static IntPtr SafeRetain (IntPtr obj)
{
if (obj == IntPtr.Zero)
return obj;
return CFRetain (obj);
}
}

[SupportedOSPlatform ("ios")]
Expand Down
32 changes: 32 additions & 0 deletions src/ObjCRuntime/TransientCFObject.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
using System;
using System.Runtime.InteropServices;

using CoreFoundation;

#nullable enable

namespace ObjCRuntime {
// a short-lived holder for a CFObject handle for native interop, the pointer will be released when holder is disposed.
// typical usage:
// using var cfobj = CFArray.Create ("a", "b");
// SomePInvoke (cfobj);
//
internal ref struct TransientCFObject {
#if !COREBUILD
IntPtr ptr;

public TransientCFObject (IntPtr ptr)
{
this.ptr = ptr;
}

public void Dispose ()
{
CFObject.SafeRelease (ptr);
}

public static implicit operator IntPtr (TransientCFObject obj) => obj.ptr;
public static implicit operator NativeHandle (TransientCFObject obj) => obj.ptr;
#endif
}
}
1 change: 1 addition & 0 deletions src/frameworks.sources
Original file line number Diff line number Diff line change
Expand Up @@ -1937,6 +1937,7 @@ SHARED_CORE_SOURCES = \
ObjCRuntime/NativeAttribute.cs \
ObjCRuntime/NativeHandle.cs \
ObjCRuntime/NativeNameAttribute.cs \
ObjCRuntime/TransientCFObject.cs \
ObjCRuntime/TransientCFString.cs \
ObjCRuntime/TransientString.cs \
ObjCRuntime/NFloat.cs \
Expand Down
1 change: 0 additions & 1 deletion tests/cecil-tests/Documentation.KnownFailures.txt
Original file line number Diff line number Diff line change
Expand Up @@ -24553,7 +24553,6 @@ M:CoreFoundation.CFBundle.GetBundlesFromDirectory(Foundation.NSUrl,System.String
M:CoreFoundation.CFBundle.GetInfoDictionary(Foundation.NSUrl)
M:CoreFoundation.CFBundle.GetLocalizations(Foundation.NSUrl)
M:CoreFoundation.CFBundle.GetLocalizationsForPreferences(System.String[],System.String[])
M:CoreFoundation.CFBundle.GetLocalizedString(System.String,System.String,System.String)
M:CoreFoundation.CFBundle.GetMain
M:CoreFoundation.CFBundle.GetPreferredLocalizations(System.String[])
M:CoreFoundation.CFBundle.GetResourceUrl(Foundation.NSUrl,System.String,System.String,System.String)
Expand Down
10 changes: 10 additions & 0 deletions tests/common/TestRuntime.cs
Original file line number Diff line number Diff line change
Expand Up @@ -504,6 +504,16 @@ public static bool CheckXcodeVersion (int major, int minor, int build = 0)
return CheckMacSystemVersion (15, 2);
#else
throw new NotImplementedException ($"Missing platform case for Xcode {major}.{minor}");
#endif
case 3:
#if __TVOS__
return ChecktvOSSystemVersion (18, 4);
#elif __IOS__
return CheckiOSSystemVersion (18, 4);
#elif MONOMAC
return CheckMacSystemVersion (15, 4);
#else
throw new NotImplementedException ($"Missing platform case for Xcode {major}.{minor}");
#endif
default:
throw new NotImplementedException ($"Missing version logic for checking for Xcode {major}.{minor}");
Expand Down
Loading