Tillerinobot icon indicating copy to clipboard operation
Tillerinobot copied to clipboard

move translations to resource files

Open omkelderman opened this issue 8 years ago • 2 comments

In my opinion, anything translations related should be moved to text-based resource files for easier editing/translating. That way people translating do not have to worry about things like "a string is a thing between double quotes" or whatever. Just translate a freakin file and done.

Usually this would be done with something like i18n, but I feel like that is probably not gonna work for this, for multiple reasons:

  • At multiple places it is not just a string, but a collection of stuff where it picks a random one. Even at some place the first part is fixed and the second part is random
  • Tsundere isnt exactly "just" an translation, it does lots of other crap... Noticeably the fake recomendations.
  • Probably other stuff I havent thought about.

As a basic start this should be simple, just a txt-file per language with a key-value pair per line where the value is a string that can be passed to String.format() to include runtime info. For example:

@Override
public String unknownCommand(String command) {
	return "Unknown command \"" + command
			+ "\". Type !help if you need help!";
}

becomes

UnknownCommand=Unknown command "%s". Type !help if you need help!

Also we need to include a thing somewhere at the start of the file which has the name of the language, aka the thing you type in chat to enable that language. I propose a reserved Name key (which you should always put at the start of the file for clarity). This key can be defined multiple times (like for example de English one is called "English" and "Default", dont wanna break the current system ^^)

Of course empty lines and lines started with a pre-defined "comment identifier" (#? //? both?) will be ignored when reading.

One file is set as the "default" one (so that'll be english) and if a file does not have a key defined, it'll use the value of the default one. So no ugly return new Default().someMessage() anymore.

Waay simpler to read, no worries about escaping shit, just text.. (well, if you ever wanna use an % you need to write %% but that wont happen i guess :stuck_out_tongue:)

Now to the "random" part, I guess thats simple: as soon as a key is defined multiple times it means, pick one at random. If the first part of a message is static, and the second part is random: it is simply two messages.

First question: Do we need a Random instance per lang or a global one, or something else? In the current implementation there are a lot of Random instances scattered around everywhere...

Now about the welcomeUser message: Those are just multiple messages, the what-message-when logic should be moved out of the language stuff anyway.

Second question: What to do with multi-line messages? Take the last else-if-clause of the welcomeUser-message: it has 3 lines. Implementing multiple lines by defining a key multiple times is already reserved for the random-feature, so maybe adding a _# suffix with a "line number"? I dunno, I feel like I should not worry about which line I am, altho it is needed if you want multiline responses with random support. No idea yet on how to solve.

Now to the next proplem: Tsundere translations! From what I can see the biggest difference is it has "more" messages. Like, the default translations has things where it responds with nothing. Also we need a system for the fake recomendations, dont want to have that in both tsundere translations files ofc. Maybe the fake recomendatiosn should be moved out of the language-stuff altogeteher? But ofc that doesnt solve the problem :cry: since the fakes are part of the invalidChoice message...

Third question: how to solve fake recomendations being intergrated into the system...

Random question to end with: what the heck is that changed thing?

I probably forgot other stuff....

omkelderman avatar Nov 23 '16 20:11 omkelderman

Maybe the best solution would be to externalize all the strings in the default translation and keep the possibility to override methods in a class implementation. I wouldn't try to "generalize" all those Tsundere shenannigans. Basic translations should be simple, freaky translations can mean that you need to code stuff.

The other thing that is huge about coded languages is build time verification (including format strings with findbugs). As soon as you externalize all that, you need to make sure that everything is tested at build time.

A third thing is that the translation must be instantiable very fast. It is constructed all the time. Loading a resource file every time you load it is suboptimal.

The changed field tells the UserDataManager if the language object has changed, i.e. if it needs to be saved. UserData is saved lazily, so this is pretty important.

Tillerino avatar Nov 24 '16 13:11 Tillerino

About resource loading, it should be done only on app startup, like once. But I can see the point you make about build time verification... I think I'm just gonna try some stuff and see where it goes.

What I would want is something like this:

// getLang doesnt actually construct anything, thats already done, its basically a lookup
Language lang = LanguageManager.getLang("Nederlands");
lang.getMessage("welcomeMessage", "oliebol"); // "Welkom terug, oliebol."

lang = LanguageManager.getLang("Default");
lang.getMessage("welcomeMessage", "oliebol"); // "Welcome back, oliebol."

where getLang probably should throw if its not avaiable, getMessage maybe as well, not sure (it could just return the key like most i18n implementations does afaik). I do agree that this is less optimal then compile time checks that every message is actually there, then just assuming its there in resource files, but I do believe it is worth it by gaining the much easier way of editing said resource files.

The LanguageManager.getLang thing could just go in UserDataManagers getLanguage. How it'll work with setting n stuff, I'll figure out as I go.

About the changed field, only places i see that being used are the Tsundere languages, is that only to preserve the "random" status or something? Or what is actually going on there?

omkelderman avatar Nov 24 '16 14:11 omkelderman

Quote omk: "bruh"

Tillerino avatar Mar 28 '23 08:03 Tillerino