Sr. Content Developer at Microsoft, working remotely in PA, TechBash conference organizer, former Microsoft MVP, Husband, Dad and Geek.
158974 stories
·
33 followers

Microsoft Responds to Outcry After Quietly Installing Beta 'Photos' App on Enterprise Machines

2 Shares
Microsoft's cloud storage app OneDrive got a new Photos app in the worst possible way, reports the blog Neowin . "The app is reportedly showing up even on Windows 11 Enterprise machines, despite apparently being a beta application aimed at consumer functionality." One admin questioned why a beta app was appearing on an Enterprise SKU in the first place, while another described the situation as yet another consumer-oriented feature being forced onto corporate PCs. Things get even more frustrating for IT departments because there does not appear to be a straightforward Microsoft-provided way to disable the app... Enterprise administrators generally need to know what is being installed on their managed devices, particularly when a software is labeled as beta. Quietly adding another application and leaving admins to clean it up themselves is therefore unlikely to win Microsoft many fans. But there's another problem, according to the blog Windows Latest. "OneDrive Photos automatically scans your system storage for photos," and apparently "doesn't need a Microsoft account to work, as it can also detect your local files." There's also a People section that groups similar faces in your photos. Microsoft asks for permission before turning it on and explicitly warns that facial data could be considered biometric data in some regions. The company says only you can see the grouped faces, that the data isn't shared with third parties, and that you can delete it by disabling the feature. In a statement to Neowin, Microsoft admitted this new photos "experience" they're "incubating" had gone "more broadly than it should have," and then promised that "We're fixing that." The spokesperson also said the Windows Photos app will "always give you the option of local and cloud photos" and, also a choice of whether or not to use it OneDrive." But there's another "awkward catch," notes the blog Digital Trends. "Users currently can't uninstall OneDrive Photos without removing the main OneDrive app too." Because OneDrive Photos is tied to the main OneDrive sync client, Windows 11 doesn't currently offer a separate uninstall option. The only straightforward way to get rid of OneDrive Photos right now is to uninstall OneDrive itself... Removing the main client can also affect its File Explorer integration and shortcuts... Microsoft says this will change. The company is working on controls that will let users remove OneDrive Photos separately from the main OneDrive app. On enterprise PCs managed through Intune, Microsoft says the app will automatically disappear where it isn't supported.

Read more of this story at Slashdot.

Read the whole story
alvinashcraft
57 minutes ago
reply
Pennsylvania, USA
Share this story
Delete

Reflections on Ai4 conference - some absences from a tech writer's POV

1 Share
This week I attended Ai4, promoted as the largest AI conference in North America. It was held at The Venetian in Las Vegas and lasted 3 days. The conference had more than 12,000 attendees (from 100 countries), 400 exhibitors/sponsors, 1,000 speakers, 700 sessions, keynotes from luminaries, and more. Needless to say, the sheer size of the conference was overwhelming. In this post, I'll share thoughts about the conference, focusing on a few key things:
  • The overwhelming size of the conference
  • The focus on agents and near absence of skills talk
  • The absence of any vendors tackling tech comm solutions
  • Whether conferences are good places to learn


Read the whole story
alvinashcraft
58 minutes ago
reply
Pennsylvania, USA
Share this story
Delete

From Self Taught to Committee Member's First Accepted Paper

1 Share
Jason and Mathieu are joined by the self-taught Alex (Waffl3x) to discuss the C++ standards committee, defaulted post-fix operators, and self-taught computer engineering. Alex shares his experience of joining the C++ Committee and his efforts to get more people involved.

News

Links





Download audio: https://media.transistor.fm/be29f77b/86621f4b.mp3
Read the whole story
alvinashcraft
59 minutes ago
reply
Pennsylvania, USA
Share this story
Delete

Implement BFF using Auth0, Angular and ASP.NET Core

1 Share

This post should how to implement a web application which needs secure access and secure identities. The application uses Angular as the UI tech, ASP.NET Core as the backend tech and a backend for frontend security architecture using OpenID Connect, OAuth and Auth0 as the identity provider.

Code: https://github.com/damienbod/Auth0BffDpopApi

Blogs in this series

  1. Implement BFF using Auth0, Angular and ASP.NET Core
  2. Implement secure downstream APIs using DPoP and Auth0
  3. Use Aspire to implement and deploy the security architecture

Target setup

In this setup, it is planned to implement the recommended authentication for applications and users which uses best practices and recommended authentication flows.

Used security standards:

  • OpenID Connect code flow with PKCE
  • Confidential client using client assertions (private key JWT )
  • No JWT shared in the public (accessible from JS)
  • HTTP only secure cookies used for the session
  • Asynchronous encryption to sign the tokens
  • DPoP used for the all access tokens
  • OAuth PAR used with the OpenID Connect flow
  • tokens stored correctly (encrypted) in a secure backend

The OpenID Connect authentication flow can be displayed in the flowing figure:

UI backend

At present, web applications should authenticate applications with users using OpenID Connect code flow and a confidential client using client assertions (private Key JWT) to authenticate the client application. It is recommended to use OAuth PAR but this is only supported in the Auth0 Enterprise setup. No authentication security logic should be implemented in a client application running in the browser. A trusted backend is now required to implement web authentication in an industry security recommended way. PKCE is always used with OpenID Connect code flow.

Downstream APIs should use OAuth DPoP whenever possible or when you are not already using MTLS. DPoP is easy to implement in ASP.NET Core if it is supported by your identity provider and you have the correct license for the identity provider used in your solution. At present ASP.NET Core is still missing the DPoP APIs in the standard library.

The ASP.NET Core application in this demo implements the OpenID Connect and OAuth flows using the Microsoft client Nuget package called: Microsoft.AspNetCore.Authentication.OpenIdConnect. See this solution for an alternative implementation with less security features: https://github.com/damienbod/bff-auth0-aspnetcore-angular

Private Key JWT (client assertions) is used to authenticate the client application. This is done by using a public and private key to create a JWT client assertion. Auth0 uses the public key to validate the client assertion. This way, the secret, i.e. the private key is never shared. In the demo, the certificate is not loaded or used correctly. This would need to be read through a configuration and stored in a secure location which can support secret rotation then. I aim to rotate secrets like this on every deployment. Not sure how this would be achieved using Auth0.

Note: Auth0 DPoP only supports ES256

Here is an Auth0 client implementation example:

// Dev only!
var privatePem = File.ReadAllText(Path.Combine(builder.Environment.ContentRootPath, "rsa256-oidc-private.pem"));
var publicPem = File.ReadAllText(Path.Combine(builder.Environment.ContentRootPath, "rsa256-oidc-public.pem"));

// Deployments, Aspire setup
//var webDpopClientPrivatePem = builder.Configuration.GetValue<string>("WebDpopClientPrivatePem");
//var webDpopClientPublicPem = builder.Configuration.GetValue<string>("WebDpopClientPublicPem");

var rsaCertificate = X509Certificate2.CreateFromPem(publicPem, privatePem);
var rsaCertificateKey = new RsaSecurityKey(rsaCertificate.GetRSAPrivateKey());

builder.Services.AddAuthentication(options =>
{
    options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
    options.DefaultChallengeScheme = "Auth0"; // OpenIdConnectDefaults.AuthenticationScheme;
    options.DefaultSignOutScheme = "Auth0"; // OpenIdConnectDefaults.AuthenticationScheme;
})
.AddCookie(options =>
{
    options.Cookie.Name = "__Host-Http-Auth0-Web";
    options.Cookie.SameSite = SameSiteMode.Lax;
    // can be strict if same-site
    //options.Cookie.SameSite = SameSiteMode.Strict;
})
.AddOpenIdConnect("Auth0", options =>
{
    options.Events = OidcEventHandlers.OidcEvents(builder.Configuration);

    options.Authority = $"https://{configuration["Auth0:Domain"]}";
    options.ClientId = configuration["Auth0:ClientId"];
    //options.ClientSecret = "configuration["Auth0:ClientSecret"];
    options.ResponseType = OpenIdConnectResponseType.Code;
    options.Scope.Clear();
    options.Scope.Add("openid");
    options.Scope.Add("profile");
    options.Scope.Add("email");
 
    //options.CallbackPath = new PathString(configuration["Auth0:CallbackPath"]);

    options.ClaimsIssuer = "Auth0";
    options.SaveTokens = true;
    options.UsePkce = true;

    // broken with Auth0, DPoP, PAR and client assertions
    options.GetClaimsFromUserInfoEndpoint = false;
    options.TokenValidationParameters.NameClaimType = "name";

    options.PushedAuthorizationBehavior = PushedAuthorizationBehavior.Require;
});

// Dev only!
var webDpopClientPrivatePem = File.ReadAllText(Path.Combine(builder.Environment.ContentRootPath, "ecdsa256-dpop-private.pem"));
var webDpopClientPublicPem = File.ReadAllText(Path.Combine(builder.Environment.ContentRootPath, "ecdsa256-dpop-public.pem"));

var ecdsaCertificate = X509Certificate2.CreateFromPem(webDpopClientPublicPem, webDpopClientPrivatePem);
var ecdsaCertificateKey = new ECDsaSecurityKey(ecdsaCertificate.GetECDsaPrivateKey());

// add automatic token management
builder.Services.AddOpenIdConnectAccessTokenManagement(options =>
{
    // Only ES256 is supported by Auth0 DPoP
    var jwk = JsonWebKeyConverter.ConvertFromSecurityKey(ecdsaCertificateKey);
    jwk.Alg = "ES256";
    options.DPoPJsonWebKey = DPoPProofKey.ParseOrDefault(JsonSerializer.Serialize(jwk));
});

builder.Services.AddUserAccessTokenHttpClient("dpop-api-client", configureClient: client =>
{
    client.BaseAddress = new("https://localhost:7288");
});

OIDC Events

The OidcEventHandlers class implements the default events required for Auth0 and ASP.NET Core OpenID Connect APIs.

using Duende.AccessTokenManagement;
using Duende.AccessTokenManagement.DPoP;
using Duende.IdentityModel;
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using System.Net.Http.Headers;

namespace BffAuth0.Server;

public static class OidcEventHandlers
{
    public static OpenIdConnectEvents OidcEvents(IConfiguration configuration)
    {
        return new OpenIdConnectEvents
        {
            OnAuthorizationCodeReceived = async context => await OnAuthorizationCodeReceivedHandler(context, configuration),

            // use OAuth PAR
            OnPushAuthorization = async context => await OnPushAuthorizationHandler(context, configuration),

            OnRedirectToIdentityProviderForSignOut = async context => await OnRedirectToIdentityProviderForSignOutHandler(context, configuration),

            // standard OIDC flow handlers using JAR and client assertions - not using OAuth PAR
            //OnRedirectToIdentityProvider = async context => await OnRedirectToIdentityProviderHandler(context, configuration),
        };
    }

    private static async Task OnRedirectToIdentityProviderForSignOutHandler(RedirectContext context, IConfiguration configuration)
    {
        var logoutUri = $"https://{configuration["Auth0:Domain"]}/v2/logout?client_id={configuration["Auth0:ClientId"]}";

        var postLogoutUri = context.Properties.RedirectUri;
        if (!string.IsNullOrEmpty(postLogoutUri))
        {
            if (postLogoutUri.StartsWith("/"))
            {
                // transform to absolute
                var request = context.Request;
                postLogoutUri = request.Scheme + "://" + request.Host + request.PathBase + postLogoutUri;
            }
            logoutUri += $"&returnTo={Uri.EscapeDataString(postLogoutUri)}";
        }

        context.Response.Redirect(logoutUri);
        context.HandleResponse();
    }

    private static async Task OnAuthorizationCodeReceivedHandler(AuthorizationCodeReceivedContext context, IConfiguration configuration)
    {
        // https://openid.net/specs/openid-connect-eap-acr-values-1_0-final.html
        if (context.Properties != null && context.Properties.Items.ContainsKey("acr_values"))
        {
            context.ProtocolMessage.AcrValues = context.Properties.Items["acr_values"];
        }

        if (context.TokenEndpointRequest != null)
        {
            context.TokenEndpointRequest.ClientAssertionType = OidcConstants.ClientAssertionTypes.JwtBearer;
            context.TokenEndpointRequest.ClientAssertion = AssertionService.CreateClientToken(configuration);
        }
    }

    /// <summary>
    /// Not using OAuth PAR
    /// </summary>
    //private static async Task OnRedirectToIdentityProviderHandler(RedirectContext context, IConfiguration configuration)
    //{
    //    var request = AssertionService.SignAuthorizationRequest(context.ProtocolMessage, configuration);
    //    var clientId = context.ProtocolMessage.ClientId;
    //    var redirectUri = context.ProtocolMessage.RedirectUri;

    //    context.ProtocolMessage.Parameters.Clear();
    //    context.ProtocolMessage.ClientId = clientId;
    //    context.ProtocolMessage.RedirectUri = redirectUri;
    //    context.ProtocolMessage.SetParameter("request", request);
    //}

    private static async Task OnPushAuthorizationHandler(PushedAuthorizationContext context, IConfiguration configuration)
    {
        context.ProtocolMessage.Parameters.Add("client_assertion", AssertionService.CreateClientToken(configuration));
        context.ProtocolMessage.Parameters.Add("client_assertion_type", OidcConstants.ClientAssertionTypes.JwtBearer);

        context.ProtocolMessage.Parameters.Add("audience", configuration["Auth0:Audience"]);

        context.HandleClientAuthentication();

        // https://openid.net/specs/openid-connect-eap-acr-values-1_0-final.html
        if (context.Properties.Items.ContainsKey("acr_values"))
        {
            context.ProtocolMessage.AcrValues = context.Properties.Items["acr_values"];
        }
    }
}

private key JWT implementation

Note: Auth0 uses a special kid setup for the client key JWT, i.e. the ComputeJwkThumbprint is used instead of the thumbprint.

using Duende.IdentityModel;
using Microsoft.AspNetCore.DataProtection.KeyManagement;
using Microsoft.IdentityModel.Tokens;
using System.Globalization;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;

namespace BffAuth0.Server;

public static class AssertionService
{
    public static string CreateClientToken(IConfiguration configuration)
    {
        var now = DateTime.UtcNow;
        var clientId = configuration.GetValue<string>("Auth0:ClientId");
        var authority = configuration.GetValue<string>("Auth0:Authority");

        //var privatePem = configuration.GetValue<string>("WebOidcClientPrivatePem");
        //var publicPem = configuration.GetValue<string>("WebOidcClientPublicPem");
        var privatePem = File.ReadAllText(Path.Combine("", "rsa256-oidc-private.pem"));
        var publicPem = File.ReadAllText(Path.Combine("", "rsa256-oidc-public.pem"));

        var rsaCertificate = X509Certificate2.CreateFromPem(publicPem, privatePem);
        var rsaCertificateKey = new RsaSecurityKey(rsaCertificate.GetRSAPrivateKey());

        string kid = Base64UrlEncoder.Encode(rsaCertificateKey.ComputeJwkThumbprint());
        var signingCredentials = new SigningCredentials(new X509SecurityKey(rsaCertificate, kid), "RS256");

        var token = new JwtSecurityToken(
            clientId,
            authority,
            new List<Claim>()
            {
                new Claim(JwtClaimTypes.JwtId, Guid.NewGuid().ToString()),
                new Claim(JwtClaimTypes.Subject, clientId!),
                new Claim(JwtClaimTypes.IssuedAt, DateTimeOffset.UtcNow.ToUnixTimeSeconds().ToString(), ClaimValueTypes.Integer64)
            },
            now,
            now.AddMinutes(5),
            signingCredentials
        );

        token.Header[JwtClaimTypes.TokenType] = "client-authentication+jwt";

        var tokenHandler = new JwtSecurityTokenHandler();
        tokenHandler.OutboundClaimTypeMap.Clear();

        return tokenHandler.WriteToken(token);
    }
}

UI frontend

Angular is used as the UI tech stack to implement the frontend. Angular supports CSP nonces and loads the Javascript using the nonce from the backend response.

Some characteristics of the UI:

  • No security implementation
  • Uses HTTP only secure cookies to access the BFF APIs
  • Same origin, same site protection required
  • Use CSP nonces to protection the session, supported by Angular
  • Deployed to the BFF wwwroot in production setup

Setup development

Development is setup so that the developers can used there favorite tools and not to be dependent on the backend technology. YARP is used so that the applications can run locally and still use all the security features during development.

Setup production

When the application is deployed, the UI is built into the wwwroot of the backend application and the two tech stacks are deployed as a single container.

Notes

At present the user info endpoint does not work, I have no idea what causes this, but this should be easy to fix. Next steps are to migrate the solution to Aspire and add an API which supports both OAuth DPoP access tokens and standard JWT bearer tokens.

Links

https://auth0.com/docs/quickstart/webapp/aspnet-core

https://auth0.com/blog/backend-for-frontend-pattern-with-auth0-and-dotnet

https://github.com/damienbod/bff-auth0-aspnetcore-angular

https://github.com/damienbod/DPOP-aspnetcore-idp

https://auth0.com/docs/secure/sender-constraining/demonstrating-proof-of-possession-dpop

https://auth0.com/blog/implementing-dpop-with-auth0

https://auth0.com/docs/quickstart/backend/aspnet-core-webapi#using-dpop-for-enhanced-security



Read the whole story
alvinashcraft
59 minutes ago
reply
Pennsylvania, USA
Share this story
Delete

Multi-Language Support for Cross-Platform .NET

1 Share
&&
Uno Platform 6.6 Multilingual Tutorial
TL;DR

Uno Platform 6.6 closes the multilingual gap for cross-platform .NET: full IME composition, Unicode-correct text handling (from 6.5), and automatic font fallback now work together out of the box — and a Deepgram-powered sample even reads the text back out loud.

Software doesn't ship to one country anymore. It ships to everyone, all at once — and if your app can't handle a customer typing in Japanese, reading in Arabic, or hearing a confirmation message in their own language, you've already lost part of your audience before they've even opened a menu.

Let's unpack what it actually takes to build a cross-platform .NET app that speaks the world's languages — and how Uno Platform 6.6 just made that dramatically easier.

Why It Matters

Why Language Support Matters

Localization & Globalization

Localization used to be treated as a checkbox — swap out some strings in a .resx file, ship an app that's "technically" available in 12 markets, and call it a day. That's not what modern users expect anymore.

Real globalization means your app respects how people actually read, write, and speak. That's right-to-left text for Arabic and Hebrew. That's composed characters for Chinese, Japanese, and Korean. That's a font that doesn't fall over the moment someone types in Hindi or Georgian. Get this right, and your app feels native no matter where it's opened. Get it wrong, and every non-English user gets a constant, quiet reminder that they were an afterthought.

Maximize Reach

Here's the honest business case: English is not the internet's default language anymore, and it hasn't been for a while. Mandarin, Hindi, Spanish, Arabic, and dozens of other languages represent enormous, underserved developer and consumer markets. If your app's input fields choke on non-Latin scripts, or your UI renders tofu boxes instead of real characters, you're not just delivering a rough experience — you're closing the door on entire regions before a user gets past the sign-up screen.

Cross-platform .NET through Uno Platform already gets you to Windows, macOS, Linux, iOS, Android, and the browser with one codebase. Multilanguage support is what makes that reach actually count.

Fundamentals

Explain the How

So what's actually involved in making an app "speak" every language? Three things, and each one is trickier than it looks.

IME (Input Method Editor). Languages like Chinese, Japanese, Korean, and Vietnamese can't be typed directly from a standard keyboard — there just aren't enough keys. Instead, users type a phonetic representation (like Pinyin or Romaji) and an IME composes it into the correct characters, often with an in-progress "candidate" view before the text is committed. If your text box doesn't understand IME composition, users simply can't type in these languages — full stop.

Unicode. This is the character-encoding standard that makes it possible to represent virtually every writing system in existence in a single format. But supporting Unicode isn't just "don't crash on non-ASCII bytes." It means correct caret positioning inside multi-byte grapheme clusters, correct selection behavior, and correct rendering direction — because Arabic and Hebrew flow right-to-left, and some scripts combine multiple code points into ligatures that behave as a single visual unit.

Font glyphs. Even once you've got the right characters, you need a font that can actually draw them. No single font contains glyphs for every script on Earth — Latin, CJK, Arabic, Devanagari, Georgian, Thai, and Cyrillic all typically live in different font families. Historically, developers had to hand-pick and swap fonts per language, or accept the dreaded "tofu box" — an empty square where a glyph should be.

Put those three together, and you've got the real technical bar for multilingual support. Most frameworks handle maybe one of these well. Uno Platform 6.6 handles all three.

6.6 Release

Uno Platform 6.6 Brings in Full Language Support

This didn't happen overnight — it's been a two-release arc.

6.5 brought Unicode support. TextBox got proper handling of non-Latin scripts: correct caret positioning, mouse and keyboard selection across multi-byte characters, and arrow-key navigation between grapheme clusters instead of raw codepoints. If your keyboard could output the characters directly, Uno Platform could handle them correctly. The one gap: composition-based input — IME — wasn't there yet.

6.6 has full IME composition. That gap is closed. Uno Platform 6.6 adds complete IME composition support across Windows, WebAssembly, Android, iOS, macOS, and Linux. Users can compose, review, and confirm characters using whatever input method is already built into their OS — no extra configuration, no platform-specific workarounds. Type Pinyin, get Chinese characters. Type Romaji, get Kana and Kanji. Type Hangul components, get composed Korean syllables. It just works, out of the box, on every target.

And automatic font fallback. This is the piece that quietly makes everything else look good. A single TextBlock using the default font family can now render Latin, CJK, Arabic, Georgian, and other supported scripts side by side, in the same sentence, without a developer ever setting a FontFamily. Uno Platform resolves each glyph the default font can't draw through a fallback service automatically. No more mixing and matching font families by hand. No more tofu boxes.

What This Means for You

Type it (IME), store and navigate it correctly (Unicode), and see it rendered properly (font fallback) — the full multilingual stack, working together, with zero extra configuration.

Demo

Walk Through of Multi-Language Support

Talk is easy — let's see it in action. I put together a sample app, UnoMultiLanguage, that demonstrates every piece of this working live, across a handful of scripts.

Let's See Things in Action

Here's the full app, front and center — an IME test box, automatic font fallback preview, and a voice readout section powered by Deepgram (more on that shortly).

Full multilingual sample app in Uno Platform 6.6
The UnoMultiLanguage sample app, showing font fallback, IME input, and Deepgram voice readout in one view.

Font Fallback in Action

This single line uses the default FontFamily — nothing custom set anywhere on the page. English, Chinese, Japanese, Korean, Hindi, Arabic, Georgian, Thai, Greek, Russian, Hebrew, and an emoji, all rendering correctly, side by side, resolved automatically per glyph.

Automatic font fallback rendering multiple scripts in one TextBlock
One TextBlock, one default font, eleven scripts — all resolved automatically.

IME Support Across Languages

Drop into the text box, and it's ready for whatever your OS input method throws at it.

Try your IME text box, ready for input
The IME test box, ready for Pinyin, Kana, Hangul, or Telex input.

Switch to a Japanese IME, type in Romaji, and watch it compose into proper Kana and Kanji:

Japanese IME composition in the sample app
Romaji input composed into Kana and Kanji, live.

Same story with Korean — type Hangul components, and the IME composes full syllable blocks:

Korean Hangul IME composition
Hangul components composing into full Korean syllable blocks.

Pointing Out RTL Support

Right-to-left scripts get proper treatment too — correct text direction, correct caret behavior, correct rendering:

Arabic right-to-left text rendering correctly
Arabic text, rendered right-to-left with correctly connected letterforms.

Mixing Scripts

And because font fallback works per glyph rather than per control, you can genuinely mix scripts in one sentence — English, Chinese, Japanese, Korean, Hindi, Arabic, Georgian, Thai, Greek, and Russian, all in a single line of committed text:

Mixed scripts committed in a single text input
One sentence, ten scripts, committed as a single line of text.

This is the kind of thing that used to require careful font juggling and per-language TextBlocks. Now it's just… text.

Voice

Adding Voice to Multi-Language

Reading and typing is half the story. The sample app goes one step further and reads text back out loud, in multiple languages, using Deepgram.

Deepgram offers a straightforward text-to-speech API — send text and an API key, get back audio. No local model to manage, no platform-specific speech engine quirks to work around across five operating systems.

The voice model behind it is Aura-2, Deepgram's latest TTS model, and it currently speaks English, Japanese, Spanish, French, German, Italian, and Dutch through the app (other scripts on the page still render fine through font fallback — they just can't be synthesized yet). If you want to try it yourself before wiring anything up, Deepgram has a live playground where you can test different languages and voices right in the browser.

Here's the voice section in the sample app — pick a language, hit read aloud, hear it back:

Deepgram Aura-2 text-to-speech voice selection and playback
Voice selection and playback, powered by Deepgram's Aura-2 model.

Making the API Call and Playing It Back

The flow is simple end to end: the app sends the committed text plus a voice selector to Deepgram's /speak endpoint, gets audio back, and plays it through Uno Platform's local MediaPlayer on whatever platform the app is running on.

It's a small addition, but it completes the loop: type in any language, see it rendered correctly, and hear it spoken back. That's a genuinely accessible, genuinely global user experience — built with cross-platform C#/XAML and pure .NET for APIs.

Wrap Up

Conclusion

Multilanguage support isn't a nice-to-have anymore — it's table stakes for any app that wants to reach the actual, global population of people who might use it. IME composition, Unicode-correct text handling, and automatic font fallback are three genuinely hard problems, and until now, most cross-platform frameworks made you solve at least one of them yourself.

Uno Platform 6.6 closes that gap. Type in any language your OS supports. Render any script, mixed freely, without touching a FontFamily. And now, with a bit of Deepgram on top, hear it spoken back too.

Clone the sample, swap in your own Deepgram API key, and go type something in a language you don't normally build for. Cheers developers!

The post Multi-Language Support for Cross-Platform .NET appeared first on Uno Platform.

Read the whole story
alvinashcraft
59 minutes ago
reply
Pennsylvania, USA
Share this story
Delete

.NET 11 performance edition

1 Share

.NET 11 performance edition
7 minutes by Steven Giesel

Steven looks into several notable speed improvements that .NET 11 brings with its release. Enum comparisons are 7x faster with no memory allocations, timezone conversions are 2x faster, and GUID parsing is about 20% faster. LINQ Min and Max see big gains for smaller data types like bytes and shorts, though integers and longs show little change. Results vary by machine and setup.

Your AI agent just got the keys to the IDE's mind palace
sponsored by Jetbrains

AI agents burn tokens rediscovering what JetBrains Rider already knows. Not anymore: the newly released Rider 2026.2 ships agent skills that feed Claude, Codex, and other straight answers from the IDE's code model, so refactoring, debugging, and profiling run on real evidence, not greps. Also in this update: built-in GitHub Copilot, bring-your-own completion models, and faster debugging and branch switching.

Multi-Tenant .NET: Shared database with schema separation
7 minutes by Barret Blake

Barret explains how to build a multi-tenant .NET application using a shared database with a separate schema for each tenant. He shows how this approach improves data isolation while removing query filters and tenant IDs. He also discusses the key EF Core model cache issue, schema provisioning, migrations, and the trade-offs compared with other multi-tenant designs.

Understanding the fetch metadata HTTP headers
11 minutes by Andrew Lock

In this post Andrew looks at the Fetch Metadata HTTP headers that have been part of browsers for several years now. He describes what each of the four headers means, when they're sent, and how you might consider using them.

Adding a clone method to a C# record
1 minute by Gérald Barré

C# records provide a simple way to copy objects using the with expression, but sample with { } may not be obvious to every API user. Records also prevent you from defining your own Clone method. Gérald points out that a clean alternative is an extension method that calls with { }, giving consumers a clear and discoverable Clone() API while keeping the record’s built-in behavior intact.

How to optimize SQL queries
20 minutes by Anton Martyniuk

Slow SQL queries can hurt application performance, but many problems can be fixed with a few practical techniques. Anton covers 20 ways to optimize queries, including using indexes correctly, fetching less data, simplifying joins, improving pagination, and keeping transactions short. He explains why execution plans and fresh statistics matter. The key lesson: measure first, then optimize what actually causes the slowdown.

And the most popular article from the last issue was:

Read the whole story
alvinashcraft
1 hour ago
reply
Pennsylvania, USA
Share this story
Delete
Next Page of Stories