eugeneloza / decoherence

Project moved to https://gitlab.com/EugeneLoza/decoherence
GNU General Public License v3.0
10 stars 7 forks source link

Use CGE localization #533

Open eugeneloza opened 6 years ago

eugeneloza commented 6 years ago

Hardly a localization engine can handle extremely complex string trees of the Decoherence, but implement at least CGE constants for languages, maybe also read/write conventions (if that'll be easier than writing them raw). see https://github.com/castle-engine/castle-engine/pull/92

eugeneloza commented 6 years ago

TCastleLocalisation.SystemLanguage to get default language (check if it will be changed in future).

ResonantExplosion commented 6 years ago

It doesn't seem that any of those featured are usable because we use neither resource strings, nor CastleUI; except for automatic language detection. And I'm not sure if we need it, maybe using "English" would be a better default, because other languages may be of lower quality and lack voice acting. E.g. we can also check if current system language is available and it's quality and prompt the user on first run. But that seems lots of work with very little gain.

michaliskambi commented 6 years ago

Note that both approaches (GetText, Benedikt XML

michaliskambi commented 6 years ago

(sorry, I send above too soon :) )

Note that both approaches (GetText with PO files, Benedikt XML format for translations) can also be used to perform explicit translation id->localized string (or English string->localized string, at your choice). In case of custom XML this is (or will be, once we merge the PR) part of CastleLocalization singleton, in case of GetText this is already part of FPC GetText unit (TMOFile.TranslateString, if I remember right). So you are not limited to only translating CGE UI controls or resourcestrings. Using a ready localization system you get automatic reading of standard translation format. In case of PO file (GetText), this also means you can use things like poedit application, or that Google online service to collaborate on translations and auto-translate.

eugeneloza commented 6 years ago

Yes, but I'm unaware how this will work on a non-regular and non-uniform texts. I'll try to formulate the problem (thou I'm not really sure, maybe, I'm just not seeing something obvious). E.g. let's imagine have 10000 phrases in-game. 1000 of those are items and monster names, 1000 are descriptions, 1000 interface and utility phrases and 7000 - dialogue phrases (in cut-scenes). The development starts with 1-1-10-5 and gradually goes up through changing, rewriting the whole cut-scenes dialogues, deleting and inserting, deprecating, proof reading and other processes that will change the phrase container structure dramatically. They are called in-game through a complex list of dynamic string-to-string dictionaries. E.g. a cut-scene is a timed list of events (like moving actors, playing sounds, saying phrases, changing the world (e.g. opening a door or spawning an actor/item)). I have a very little idea how this can be implemented through hard-coded resourcestrings. Yes, I'll use some sort of 'alias' for every phrase which I bind through TDictionary to specific translated phrase, and the TDictionary itself is read/written to a separate XML file. So, in the end, game situations reference Alias and by the key Alias the TDictionary returns the specific phrase with its parameters (not only text, but also voice acting file if it's available, and, maybe, even the specific font/display method to use). Of course, using tools like Transifex is cool and doesn't require writing a WYSIWYG multiform editor for game data, allows for easy contribution-friendly environment. But I just don't see how could it work in a complex case, when the amount and content of the phrases (linked not only to text) changes dramatically during development.

michaliskambi commented 6 years ago

At the "bottom", most translation systems have a simple function "take this id and translate into language X" :)

And the CGE localization (both localization approaches discussed in https://github.com/castle-engine/castle-engine/pull/92 -- the GetText approach, and the "custom XML format" approach) can be used to perform this function. In case of "GetText approach", you actually don't need anything from CGE, the GetText unit from FPC provides a ready mechanism for it: https://www.freepascal.org/docs-html/fcl/gettext/tmofile.translate.html .

The important thing is that you do not need to put everything into resourcestrings. The localization can be used "at the bottom" of a larger system. As long as you can express your localization needs in terms of "take this id and translate into language X", then both localizations systems we discuss in https://github.com/castle-engine/castle-engine/pull/92 may be useful to you:)

eugeneloza commented 6 years ago

Well, I've just had quiet some experience with po files and they look like this:

: ../data/inventory/torso_armor.lua:94

msgid "Karlate Breastplate" msgstr "Карлатинський панцир"

: ../data/inventory/torso_armor.lua:95

msgid "" "Standard Karlate issued equipment. Effectively protects the torso from most " "types of attack." msgstr "Стандартна панцирна броня Карлатинської армії. Ефективно захищає торс від більшості атак."

: ../data/inventory/torso_armor.lua:105

msgid "Shouldered Breastplate" msgstr "Панцир із наплечниками"

: ../data/inventory/torso_armor.lua:106

msgid "" "A breastplate with slightly thicker armor than is commonly found and " "additional shoulder guards." msgstr "Трохи міцніший, ніж стандартна панцирна броня, він додатково захищає плечі."

: ../data/inventory/weapons.lua:43

msgid "Wooden Sword" msgstr "Дерев'яний меч."

Such structure is no way extensible... If I change or move a single string (or just a single character in msgid) the whole translation system would collapse into a hard-to-debug state. I'm just scared to work with such system if it would require extensive prolonged changes and keeping multiple languages in sync. E.g. the text above is from Valyria Tear game I've translated into Ukrainian. During last update almost 40% of the translated strings went incompatible (while they are almost identical, but referenced from different units or had undergone some changes), practically wiping out the translation (thou it would have been just to update a word or two per line).

About XML - yes, I'm thinking about a similar system, but the given example:

The localisation strings are stored in a XML file in this format:

<?xml version="1.0" encoding="utf-8"?>
<strings>
<string key="ExampleKey1" value="Example value 1" />
<string key="ExampleKey2" value="Example value 2" />
</strings>

as data/local/en.xml for example.

is not clear, how to handle something like this

<phrase id="ExampleKey1" value="Example value 1" voice="/scenario/this_scenario/voice_acting/phrase1.ogg" symbol="ExclamationSymbol" Emotion="Smile" Style="CalmLeader"/>
<phrase id="ExampleKey2" value="Example value 2" voice="/scenario/this_scenario/voice_acting/phrase2.ogg" symbol="none" Emotion="Smile" Style="CalmLeader"/>

Starting with the issue, that I am unclear whether to store Emotion in phrase or in cut-scene? While it has relation to the phrase, it might be better to operate separately (which in turn complicates cutscene-to-phrase relation). Or, maybe Style tag should actually be stored in a separate file (as it corresponds to a specific voice actor style) and "translation" happens not only between languages, but also between dialogue scenarios (e.g. "rude" will say "Whatddyawant?!" for "Greeting_159", "nice" will "Hi, nice to see you!" and "lazy" will say the same keyphrase "Yeah?"). Moreover, single dialogue script may have different voice acting files (e.g. at least soundprocessed, kinda making voice pitch higher/lower).

Yes, that simply means I'm yet even unsure what and how should be stored at the moment. But it's certainly something more than string-to-string dictionary (at least an extensive interconnected network of those or a database) and most certainly not a static predefined structure that would remain the same from the start until the development end.

michaliskambi commented 6 years ago

Well, I've just had quiet some experience with po files and they look like this:

Note that your example is a problem of using English strings as ids in PO. But that's just one way of using GetText, as a map "English string->localized string". The other way is to have a map "internal id->localized string". You can use "stable internal identifiers" for the strings, GetText doesn't prevent this in any way.

In any case, your needs do seem unique :) It's understandable if you want to develop a custom translation system for your case. I only wanted to clarify some facts about (both) CGE localization systems (that they are not limited to resourcestrings and not limited to CGE UI classes).

eugeneloza commented 6 years ago

your needs do seem unique

Well, the problem is that I don't really understand what exactly I need. I only see the "what should I get in the end", but not the architecture below this all "how to". And the architecture I see at the moment looks really messy and unique (rather messy than unique :)). The fact is that I have to link together a large amount of aliases (key values) and content strings which may vary depending on the current in-game situation. And I don't really understand how to except for "some blurry idea on where to start writing that through TDictionaries" and "no idea on how to handle that through a standard localization system" :)

eugeneloza commented 6 years ago

/so I woke up with clearer thoughts :)/ The most complex part of the translation will be TSpeakingCharacter.Say(PhraseID). Other cases would just be a simple reference between a TextID and Content. In case a NPC or PlayerCharacter speaks it would: TSpeakingCharacter.Say would call TSpeakingCharacter.Voice TSpeakingCharacter.Voice will get the phrase and hopefully audiofile, connected to it. Then it'll throw the phrase back into interface context to show it either through subtitles or through a pop-up balloon, simultaneously playing audiofile. Voice may give different phrases for the same PhraseID and certainly different links to audiofiles. So TCutScene(for current TLanguage) -> TSpeechEvent -> ... -> TSpeakingCharacter.Voice(PhraseID) -> text and URL Not so complex as it seemed :) Also makes Voice phrases different in processing than simple text.

Thou I think I have to postpone this issue a for bit later, because it already requires some experimenting. Once I'm done with the GUI, I can start experimenting with filling it with translated text and then switch to developing cut-scenes. The most problematic there will be developing a WYSIWYG editor for the cut-scenes and corresponding events it would call :)

eugeneloza commented 6 years ago

I've started to implement font manager, that should look similar to a translation manager. So it'll be practically seen how it works in different situations.