I feel your pain. It's ridiculous that Microsoft hadn't provided a better alternative for this in so many years. Even the .NET Core localization uses .resx files. My alternative for this is to create a table with one column foreach language you want to track. Then load it on memory soon in the pipeline (in .NET Core) or in the config phase (older ASP) in the form of a static variable (a dictionary) or I insert it on the local/shared cache with no expiration date.
EDIT
If the quantity of strings to localize is not very big and you have all of them available in all languages you can perfectly put them inlined on the static class itself, rather than having them on an external resource (a database or a file). But having this strings on an external resource has one clear advantage, you can replace them without recompiling the application.
Furthermore, sometimes you won't have all the strings translated in all the availables languages you want to offer. Then, if you have the resources allocated in a database or an external file then you can provide a user interface to allow some users with the required privileges to modify/complete the translations.