BepInEx / BepInEx.ConfigurationManager

Plugin configuration manager for BepInEx
https://www.patreon.com/ManlyMarco
GNU Lesser General Public License v3.0
231 stars 53 forks source link

i18n support #35

Closed hanabi1224 closed 5 months ago

hanabi1224 commented 3 years ago

Add i18n support with dotnet ResourceManager solution, which does not create a hard dependency on i18n resource files and can gracefully fallback to default (en) when they are absent. Please let me know your concerns.

ManlyMarco commented 3 years ago

Currently localization of config manager is done by https://github.com/bbepis/XUnity.AutoTranslator and is translated separately by different communities. The config manager itself doesn't have much to translate, it's mostly about translating the settings themselves, which has to be done either in each plugin, or ideally automagically with autotranslator.

Because of this I'm not sure if this is useful to implement at all, since by itself it doesn't do much and there's already a full translation solution.

hanabi1224 commented 3 years ago

Thanks for the info! I took a rough look at AutoTranslator, I feel it serves a very different purpose. It's definitely a good solution to translate in-game text with automation but I don't think it really fits the specific case to translate in-plugin text although it works, but anyways there's no conflict and ppl can still use it for plugins.

ManlyMarco commented 3 years ago

AutoTranslator is meant to be a drop-in universal localization system, it can either generate translations or load hand-made translations or replace text assets. In my experience having all translations centralized like this is a much better way of doing things than relying on plugins to bring their own translations which are often inconsistent and incomplete. What's worse if a plugin translates its config settings then the settings will be lost if user switches language because the name is used as key, but it's not a problem with AT because it hooks into the IMGUI code and translates strings right before they are drawn. (example of a plugin translation)

CurrentUICulture and others sometimes need to be set to a specific value to avoid bugs in games that were never meant to be used outside of a specific language (usually in Japanese games), so it's not a very reliable source for current language but I don't know of anything better other than asking AutoTranslator through its API.

Still, it looks like this is fine and won't cause trouble, maybe other than throwing and catching some exceptions at startup because of how resource finding is done, which can be annoying if trying to debug something at game start. Also are the resources embedded in the dll or do they need to be in separate folders? (in case it makes folders it's not ideal unless config manager is put in its own folder because it will make a mess in the plugins folder)

hanabi1224 commented 3 years ago

Also are the resources embedded in the dll or do they need to be in separate folders?

It's just standard .net way, generate seperate /.resources.dll.

What's worse if a plugin translates its config settings then the settings will be lost if user switches language because the name is used as key

.net standard i18n resource generates strongly typed class, not string-type key based in API level.

In my experience having all translations centralized like this

As a plugin, I feel it's too complicated to use online translation, as for offline solution, I don't see how AT is better than .net standard way in organizing resources and writing code (for in-plugin text translation)

ghorsington commented 3 years ago

It's just standard .net way, generate seperate /.resources.dll.

Is there a way to have it output normal textual files that one could edit with a text editor? I personally don't mind the usage of original dotnet localization tools, as long as it won't add a level of complexity to actually translating the plugins.

I remember these resource DLLs back from WinForms days. I'm fairly use Microsoft made these with corporate software development in mind, where localizations are done in-house with the same tools that are used to actually compile code. While this is saving developers some time and effort, it makes it a pain for anyone to just add their own localizations.

Despite its name, XUAT nowadays is less for just giving machine translations and more for giving tools to replace any kind of assets in game. It is right now being used mostly for replacing game and plugin resources (strings, textures, sounds, prefab). Automatic machine translations are just a small feature of it now. History can sometimes be peculiar.

One main attraction of using XUAT is that it requires zero tooling for me to do it. All I need is notepad and the game. This makes authoring localizations much easier for anyone, which is critical in an open game modding community.

All in all, my main gripe with the proposed PR is that it has potential to make localizing the plugin actually more difficult if all localizations are packed into a DLL. If there is a possibility to have it generate normal text files, it would be much better. The format doesn't matter (XML, JSON, CSV) as long as all I'd need is just a text editor.

hanabi1224 commented 3 years ago

Is there a way to have it output normal textual files that one could edit with a text editor?

Visual studio have built-in editor for resx files (which are editable xml files) and will auto-generate strongly-typed classes.

image

image

It is right now being used mostly for replacing game and plugin resources

I think in-game text translation and in-plugin text translation are totally different scenarios, it's weird to apply some tricky hack to a scenario where you have full control in source code level, and I doubt AT would provide better setup / dev / installation experience and performance for the latter, and more importantly, the 2 solutions don't collide with each other, ppl can always choose to onboard either or both

ghorsington commented 3 years ago

Visual studio have built-in editor for resx files (which are editable xml files) and will auto-generate strongly-typed classes.

But as you said before, these XML files will then get compiled into DLLs?

I indeed understand that, but what if I don't want to download the entire Visual Studio just to localize this plugin? I can imagine this in a professional context where everyone is expected to use specialized programs and tools to produce an artefact. However, we work in an environment where people who want to contribute to a project come from different levels. I absolutely do not see how packing localisations into a DLL is of any benefit both to the end-user and someone who just wants to edit or author a translation for a new language.

I think in-game text translation and in-plugin text translation are totally different scenarios, it's weird to apply some tricky hack to a scenario

I personally never said I am against the idea of having i18n support built-in into a plugin. In fact, I have done so myself multiple times. What does matter to me, however, is the format. In one of the projects I've worked on before, I made translations loadable from JSON files. This allowed anyone to edit and create new translations. Thanks to this, the plugin I made has translations for five languages and all of them were contributed by people who didn't program or have any IDE or special tooling preinstalled. Moreover, I am aware of more translations existing that people distribute as simple JSON files. Again, it lowers the bar of entry for something as simple but important as localisation.

While XUAT is based on hooking into the game code (if you consider hooking game methods "hacky", I'm not sure how you have been modding Unity games until now), it allows this kind of easy translation authoring. While not documented, XUAT does allow plugin translation by simply adding a text file with a specialized name.

2 solutions don't collide with each other, ppl can always choose to onboard either or both

Indeed, and by extension, XUAT allows to localise any plugins without any change to their code. It's a plus, of course, and I don't mind if ConfigurationManager has its own custom in-plugin localisation as long as all you need to author and build translations is a simple text editor.

All in all, I personally do not feel like this PR should be merged as-is. I am fine with the idea of plugin having its own built-in localisation. However, I do not agree with localisations being embedded into DLLs and having no way to localise the plugin without using Visual Studio or .NET SDK in general. My suggestion is to either check if .NET's own default localisation system allows to load localisations dynamically from text files OR add some simple custom system that allows loading translations in some textual file format.


Small aside:

I'm not sure if many people are aware of it nowadays, but GNU GetText is a long-time battle-tested solution for localising anything with textual files. You can edit its .mo files either by hand with a text editor or optionally use something like Poedit. It's quite a bit heftier than just JSON/CSV, but it supports format strings and other specialities. There exist quite a few managed implementations for it in .NET, for example NGettext.

ManlyMarco commented 3 years ago

The part about not needing extra tools is huge in context of game modding because most translators do not have VS installed and don't even know anything about it. Editing text files is much easier for them and you don't have to teach them any new skills, plus it works really well with git and the github file editor - a lot of translations are submitted as a file edit on github website itself so the translators don't even have to learn git other than the rough concepts.

I don't like baking translations into plugins because I've seen bugs get introduced by this before (works in one language but not the other), and because it's not possible to hot reload translations and edit them live like with AT which is a major part of the workflow. Having some plugins be special instead of having a single centralized localization system seems like not the best solution to me - it will give you translations if all you have is this plugin and a couple others, but when the scene for that game grows you'll have 10 different translation setups that no one will want to contribute to.

hanabi1224 commented 3 years ago

I personally never said I am against the idea of having i18n support built-in into a plugin. In fact, I have done so myself multiple times. What does matter to me, however, is the format.

I don't think this is supported by .net built-in solution, the best way to have this is to create a NuGet package with a shared project in it (NuGet shared source packages), that way you can localize the plugin in whatever way you want while do not create any external dependencies (even DLL).

While XUAT is based on hooking into the game code

That is the point, self-contained localization and externally hijacking localization are totally different solutions and the latter always works even if a game provides built-in localization, by its nature.

So the argument comes to 2 parts, I think we all agree that a self-contained solution does not collide with an externally hijacking solution, it's just that .net built-in self-contained solution is not toolset free and in this case, you would rather not do the plugin-centric one, because there is always another game-centric solution. That is fine for plugins that are dedicated to the game, but for a plugin like this one whose usage is not limited to a certain game, I still don't think a game-centric solution fits better but yeah I do see your points that current plugin-centric solution with no external dependencies has its cons which is essential to non-tech modders and I would not insist, plz feel free to close the PR, and thank you for your response, both :)