The big Localization/Translation Thread

So, you want to help us localize/translate Defender’s Quest. Let’s get to it!

FYI, we are looking for a combination of professional and fan (volunteer) translators. We have a limited budget for professional translations, but we’d like to keep people’s names on file in case something comes up. We’re starting with German, and if that goes well we’ll proceed to Spanish and possibly others. For personal reasons I’d also like to do a good Norwegian translation as that’s my native language, but I might have to foot the bill for that one personally :slight_smile: (NOTE: The german translation position has been filled).

[list=1]
:szw72mjx]You need access to the latest test build of the game. PM me asking for a testing invite if you don’t have one already./:m:szw72mjx]
:szw72mjx]Download and install the latest test build/:m:szw72mjx]
:szw72mjx]Find the “locales” folder in the game’s installation directory (might be in a “bin” directory depending on the package type)/:m:szw72mjx][/list:o:szw72mjx]

Once you’re in this folder, this shows you all the locales (language packs) that the game supports. Right now you’ll see a few things:

_flags (folder)
en-US (folder)
de-DE (folder)
index.xml

Open index.xml in a text editor, you’ll see something like this:

<locale id="en-US" default="true" sort="0">	<!--American English-->
	<ui language="Language" region="Region" accept="Okay" />
	<label id="en-US,en-GB,en-CA" language="English" region="United States"/>
	<label id="es-ES" language="Inglés" region="Estados Unidos"/>
	<label id="de-DE" language="Englisch" region="Vereinigte Staaten"/>
	<label id="fr-FR" language="Anglais" region="États-Unis"/>
	<label id="nb-NO" language="Engelsk" region="U.S.A."/>
</locale>	  

The index.xml controls what languages the user can select from the languages menu in the save slot screen. This is the entry for American English, or “en-US”

“en-US” is American English’s locale id. The locale id is the http://en.wikipedia.org/wiki/ISO_639-1 with 2-letter country suffix.

This includes the name of that language in region in its native language as well as other supported languages. You can just put placeholders in and you only need to define stuff for languages you plan on supporting. Right now only en-US and de-DE (German in Germany) are supported, so if you were to add, say, Polish, you would add an entry for “pl-PL” (is that right?). The tag has the words for the interface elements on the locale menu in the specified language.

You might also want to add a flag for your country. Those are stored in the “_flags” folder and are 16x11 png files. I got mine from http://www.famfamfam.com/lab/icons/flags/. Just make your flag’s name match the locale id and it will automatically select it for your locale.

Each locale is a combination of a language (“English”) with a region (“United States”). This is because there are often regional variants of languages and often cross country borders. For instance, there’s German as spoken in Germany (de-DE) and German as spoken in Austria (de-AT). There’s also Canadian English, Costa Rican Spanish, etc, etc.

Anyways, that just defines what languages are available. You then have to supply actual text. The simplest way to get started is to just copy the en-US folder and rename it to your country code, then open the contents and start replacing text. That way, you’ll have something that works right away, but can slowly work through it and see your changes in game.

So, open up en-US and take a look. You’ll see a lot of files. Generally, these are all CSV files, which are spreadsheets formatted as plain text. Generally, a row of cells is written like this:
“Text that fits between”,“two quotation marks and”,“separated by single commas”,

Note that these are normal quotation marks, not fancy left/right variants, and regular commas. Every line MUST end with a comma or the game will choke. Furthermore, in some files I use 4 commas as a separator as a brute-force hack. That might change. Just follow whatever format you see in the given file and you SHOULD be fine. Do not add extraneous punctuation, extra lines, or break the pattern or you risk the file not working. Also, use a plain text editor like Notepad++, do NOT use a word processor like Microsoft Word.

Anyways, I imagine we’ll have lots of fun dealing with encoding issues and CSV stuff as time goes on. This is kind of wild territory we’re in and we’ll figure it out as we go - for now this looks pretty stable though.

So, let’s look at data_status_effects.csv:

"$FLAV_SLOW","Slow",
"$FLAV_FREEZE","Freeze",
"$FLAV_LIGHT","Light",  

This defines the names of the flavors “slow”, “freeze”, and “light”. So, in data files where you’re putting the names of flavors, these flavors are now just ids - the game will attach “$FLAV_” to the front of a flavor id and then try to look up that value in the locale database to display the name. So, you might do this for

Norwegian:

"$FLAV_SLOW","Sakte",
"$FLAV_FREEZE","Frys",
"$FLAV_LIGHT","Lys",  

And save the file. Keep in mind, if you use a spreadsheet program to edit these files, they might trash the formatting and it will have to be put back in manually. That’s not TOO big of a deal - I can be on hand to give tips for fixing them after the fact, but in general if the formatting breaks, the game will choke, guaranteed. It might even choke if they’re perfectly formatted! I’ll be here to bail you out, either way.

So, basically all a translator has to do is make their own localization folder, enable it in the localization index file, replace english strings in their copied folder, and then reload the game. You can select your new locale from the save slot menu, and if nothing goes wrong, you’ll see your new text!

Caveats:

[list]
:szw72mjx]Encoding Save your text files as UTF-8. If you don’t know what this is, just save your files normally and if they choke, talk to me and we’ll fix it :slight_smile:
/
:m:szw72mjx]
:szw72mjx]Font support will take some time. The latest version of the game uses an extended Latin character set, so I know it will pick up ümläüts and Nørwegiån characters, etc, but I’m not so sure about those little cross-lines on Polish characters, for instance. Also, asian, arabic and cyrillic characters DEFINITELY won’t work, I’ll have to do some major overhauling to support that, which means there’d have to be either proven interest in Asian markets, or a lot of demand from asian/arabic fans, for us to consider it. Cyrillic is not as big a deal and we’ll add that soon because it’s only a few extra dozen characters and otherwise behaves mostly like latin text (ie, left to right, similar word lengths, etc).
So, if you do a translation and some characters aren’t showing up, post a list of them on this forum and I’ll add them in the next build if I can.
/
:m:szw72mjx]
:szw72mjx]Legal issues have to be worked out. Nothing’s stopping you from doing your translation, of course, but I need to talk to my lawyer to find out if and how we can include them in the game. I also want to modify the EULA to explicitly give you the permissions you need to translate the text and share it with your friends because technically those rights might still be reserved by us. We’ll do a similar thing with regards to mods. So, eventually I’ll post some fancy legalese that basically says if you make a translation and want us to include it in the official game, you’ll have to sign over some rights to us (probably some kind of Creative Commons license) and let us use it freely.
/
:m:szw72mjx]
:szw72mjx]Grammar, word order, word length, varies wildly between languages. More than likely, a translation you’re working on might not fit in the space allotted. There’s two solutions: 1) get me to make the game more flexibile, and 2) get creative, perhaps by thinking of a shorter synonym. I’ll try to help where I can, but often you’ll have to resort to 2). In reasonably situations, I’ll try to add support to the game in places I anticipate this will happen - such as cutscenes. There’s already support for making cutscene lines show only if a certain locale is active, so if one line in English becomes enough for two in German, you can add a second line for the German one in this way and still have the cutscene play smoothly. Again, talk to me and we’ll work it out.
/
:m:szw72mjx]
:szw72mjx]Full documentation is forthcoming. This is just to get you started. There’s a lot of tips/tricks and tools available to you that I don’t have time to cover just yet, but I will get to them all eventually. /:m:szw72mjx][/list:u:szw72mjx]

Hi there! Ghorpm from GOG(dot)com (spam filter didn’t allow me to post URL), I guess some of you might know me :wink: I’m very excited about it but I also wanted to let you know that I’m open to cooperation and any kind of constructive criticism will be highly appreciated!

Cool!

BTW: You should be free to post links after your first-post and 24 hours. Denying links to first-posts keeps us spam free.

This sounds super great, and I’ll be available to help with any technical stuffs. I’m adding support to the system right now to also adjust user interface elements on a per-locale basis, as the German translation text (as expected) overflowed about 25% of our text fields :slight_smile:

Useful updates in today’s release notes for test version 1.0.59:

viewtopic.php?f=12&t=242&start=360#p3849

Small issues in data_system.csv:

  1. “$FLAV_DESC_ARMOR_BREAK_SHORT”,“Attack ignores several points of armor.”,
    Should be ‘destroys’, not ‘ignores’

  2. Cancelled by burn… does this apply even for hitback burn when the target is immune to burn? That is, if a water foe is slowed down and hits somebody who has 100% hitback with burn, will the foe become unslowed?

  3. STAT_DESC_ATTACK_ENEMY - misleading “maximal” - or at least I’ve never noticed any random factor in enemy damage.

  4. a bit of inconsistency in STAT_DESC items in general - sometimes you bother to explain exact mechanics, sometimes not.

  5. MISC_EVADE vs. MISC_EVASION? where is it used?

  1. Good catch, just a typo there.

  2. When a new flavor is applied, the first thing that is checked is immunity - if the target is immune, the function exits early. Only afterwards does it check for cancelling effects. So, if you’re chilled and immune to fire, getting hit with fire should NOT remove the chill. I haven’t tested this to confirm, but looking at the code, that would be the expected behavior.

  3. Although enemies have an attack stat which acts as a multiplier on the dmg values of their individual attacks. What’s actually being shown here though is the raw damage output of their strongest attack, or their only attack if they don’t have more than one. I can see how that’s a bit confusing - there’s no die roll, it’s just if they have one attack that does 50 dmg and one that does 100 dmg, it will display 100 next to the sword icon.

  4. I’ll look into it.

  5. MISC_EVASION doesn’t seem to be used anywhere. Dunno how that got in there.

Alrighty, for any and all translators, one thing that could be really helpful is if you would set up some sort of cloud-shared drive with me that has your files. This way I can diagnose any problems you run into as quickly as possible.

I’m using Google Drive for my german translator, but anything works so long as it preserves the files verbatim. If you’re up for it, just dump your files on some cloud synch service like google drive or dropbox, and then share it with lars [dot] doucet [at] gmail [dot] com.

One thing I imagine we’ll be running into a LOT is encoding issues. This is new territory for me, but everything I’ve read says as long as we stick to UTF-8 text encoding for our .csv and .xml files, we should probably be fine. If/when we run into a problem, let me know.

When you put text in your files and it doesn’t show up in game correctly, there are several possibilities:
**1) The font set is not supported yet. **
Post what version of the game you’re running and what characters you’re trying to use and I’ll confirm whether they’re supported or whether I need to add that in the next build.

2) You are not properly inserting the characters into the file.
Make sure your text editor is displaying text to you in a font that can support the special characters of your language, and make sure your save settings are correct. Do not use word processors like Microsoft Word or OpenOffice Writer! Instead, make sure you are using a plain-text editor like Notepad++, or alternatively a spreadsheet editor that preserves the correct csv formatting. I’m mostly concerned about the integrity of your text itself - if the csv formatting gets mangled a bit I can fix that up pretty easily with some regular expression find/replace algorithms.

3) The game is not correctly reading your file
Even if fonts are correctly supported, and you did everything right on your end, the final fault may be on the engine’s end. If we can rule out 1 & 2, it’s probably #3 and I’ll step through the code and see what’s going on.

I’ll do everything I can to support you all in this endeavor, so keep the channels of communication open and we’ll deal with problems as they come :slight_smile:

One more in data_system - “$MISC_DEF_SHORT”,“DEF” - and the same “$MISC_DEFENSE_SHORT”
$MISC_QUIT is listed twice.
MISC_PREVIOUS and MISC_PREV - I’ll hopefully find where these occur :slight_smile:
Do we need MISC_OK and MISC_OKAY?

Enemies:
EN_SLUGWORM, EN_SPEEDY, same but different?

Sometimes I have variants of the same word, to deal with different places where I have more or less space. That’s where “ok” and “okay” comes in - really short buttons just use “ok” and longer ones generally use “okay.” Same deal for prev/previous.

For flags that are literally duplicates, those can and probably should be removed.

Other times, there are things that might seem redundant, but might not be. For instance, I might use the word “character” in one place (meaning a person) and again in another place (meaning a letter or number), with two different flags but the same english word. In this case, I have duplicate flags so the translator has flexibility to translate the different senses of the english homonym.

Furthermore, in any case where you see a duplication you can just put a flag as the entry instead of text, and it will make the game look up the reference flag’s text.

So, let’s take a norwegian translation example:

“$MISC_YES”,“ja”
“$MISC_YEAH”,"$MISC_YES"

“Yeah” and “Yes” would be the same word in norwegian, the above would make both of them result in “ja.”

Ok, thanks. So, this only leaves the Speedy vs slugworm issue :slight_smile:

I believe there is an unused enemy by the name of Slugworm - it is not used in any map but it is technically in the enemy definitions, and I used a tool to auto-generate the csv files for the enemies. So speedy should be all you need. I think “slugworm” was the first enemy we ever created, and we never used him again after development started to get underway for real.

Released test version 1.0.60 today, changelog here:

viewtopic.php?f=12&t=242&p=3873#p3873

Lots of it relevant to translators.

Tiny glitch in data_system - UI_UPDATER_PlEASE_WAIT (lowercase l) - I’m okay with leaving it as it is, but I guess that sooner or later somebody will screw up on this :slight_smile:

Also, could we get some explanation about the _NUMS business? What are the available options and what will they do? I don’t mind experimenting by trial and error, guessing that font:N defines size, off_x and off_y define offset against stock position, but there’s several more and I don’t fancy trying to spot the differences if I fiddle with them.

And a small question - are the things after $ sign case sensitive? I think I’ve seen $N and $n in a few places… are they really equivalent or is it on purpose with some possible difference in behaviour?

And using another item’s name, is it just a redirect or can I glue them together? (Would this be OK?)

“$MISC_LOADING”,“Loading…”,
“$MISC_LOADING_PERCENT”,"$MISC_LOADING%",

One more thing - achievement vs. achievements, in many languages it’s a wasted effort unless you include the “new” with it, as many languages flex adverbs with verbs, so new <ACHIEVE_SINGULAR_OR_PLURAL> will look good only with either singular or plural. (There might be more cases)

Responses:

-Will fix that glitch. I believe the system automatically uppercase()'s any flags from the csv file, but it should be fixed before it throws someone off.

As for the NUMS, they amount to a spot-fix hack. Had I been smart and built a unified, data-driven UI system, it would have been trivial to throw in some little per-locale layouts and offset tags to deal with situations where some language (I’m looking at you, German) overflows the boundaries of the English text.

Unfortunately the UI for Defender’s Quest I is a giant, hard-coded mess. So, while I’ll make a much better UI system for DefQ II, I created a quick spot-fix for localization here.

So, for instance, in German the words for most of the buttons in the options menu overflow the boundaries. So, I create an additional flag, and then instead of putting localization text in it, I put some name:value pairs. Then in the horrible hard-coded mess of UI logic, I read those values and add them to the hard-coded positioning logic. This lets me inject a small amount of flexibility into the worst areas on a case-by-case basis.

So, in English here’s the entry for the “Accept” button text in the options menu:
“$UI_ACCEPT_NUMS”,“font:18,off_y:1,off_x:-3”,

And here it is in German:
“$UI_ACCEPT_NUMS”,“font:15,off_y:1,off_x:0”,

The variables that are available to you are simply the ones you see in the English example.

Here’s how it works in code:

/*Refreshes the options menu localization*/
public function refreshLocalization():void{         

   //Get an object with font size and offset parameters 
   var bn:Object = U.sysNums("$UI_ACCEPT_NUMS","font","off_x","off_y"]);  

  //Update the label, font size, and offsets of the accept button
   btn_accept.changeSimpleLabel(0xffffff, bn.font, U.sysTxt("$MISC_ACCEPT", "fu"), true, 1, true, bn.off_y, bn.off_x);			
   //stuf...
}  

So basically, what you see is what you get with regards to the variables in a “_NUMS” localization entry. I have to specifically add support for each thing there. Terrible system, I know, we’ll do a better one next time, but it’s still less work to throw in spot fixes for what I expect will be several dozen places then recode the whole UI system from the ground up. I’ll try to post a glossary tomorrow for the ones that aren’t self-explanatory.

Also… word of warning. off_x / off_y are indeed simple offsets, but as I said the UI logic is… bad. Often the positions of things are hard-coded to be dependent on the positions of other things, so… yeah. I’ll be available to help whenever major layout surgery is needed :smiley:

When using another entry’s name, it is just a simple redirect. If the entry starts with a “$” then it will try to do a redirect. So I’m afraid your example wouldn’t work, it would try to look up a flag by the name “$MISC_LOADING%”

Re: singular/plural - I think you’re right. In general, it’s probably best if I avoid “code grammer” where you stitch things together and instead just have entire phrases for the singular/plural variant, so as much grammar rules as possible can be offloaded to the translator rather than stitched together by the computer, which would have to assume some locale’s rules (usually english).

Thanks for the explanation. Just two followup questions:

  1. Are the _NUMS strings picked up automatically? That is, for any text entry, can I add the corresponding _NUMS to be picked up and applied? Or is it restricted to i.e. buttons? Or is it restricted to a certain fixed list of entries?

  2. Is the functionality dependant on particular object? That is, _NUMS string for $foo supports “boxes” and _NUMS string for $bar doesn’t, based on actual code?

My main issue behind this question is whether we should
a) Blindly copy all NUMS from en-US version
b) be expected to tweak them on our own
c) know how to tweak them :slight_smile:

In short, they are not flexible at all.

  1. There are explicitly hard-coded calls in the code to look for _NUMS locale flags, just like there’s explicit calls to look for specific text flags (ie, the title of the accessibility menu, the label for this specific checkbox, etc, etc). So although technically this method can be used anywhere, it is restricted to specific calls and flags I have put in. So I think in the sense you are using the word “automatically” the answer is no. What you see in the english/german localizations is all that is there. I’ll do my best to document these things.

If a NUMS value is not entered for a flag that the game is looking for, the game will not throw an error (at least it shouldn’t) but those values will be interpreted as 0. This usually mean offsets of 0, ie, no change in position, but wherever things like font size are concerned, it usually has safety checks to use the default size in case of a null NUMS value(since size 0 font = crash).

  1. Absolutely. I put these in on a case-by-case basis just to deal with likely trouble points. You can re-arrange some of the buttons in the controls menu, but not all of them - just the ones that had problems in German. I wish I had a universal solution on hand, but given our codebase the best I can do to get this done and move on to the sequel (and better UI code!) is just do spot fixes as needed.

a) Yes, that’s fine, or just leave them out - the game shouldn’t crash
b) Nope, not unless you see a trouble spot, and this flexibility is mostly so I can do per-locale UI tweaks.
c) Not really, but I’ll put it in the eventual documentation anyway

Also:

I just realized I think I can put comments directly in the csv files, adding an additional field that the parser just ignores. That could help a lot with things, I think.

UPDATE: Confirmed, it works right out of the box! I’m now putting comments in throughout the localization files. Remember, don’t translate these as they don’t show up in game and are only there to help document the system!

A few more consistency questions - comparing data_enemy and data_enemy_plus:

Removed entries: SLUGWORM,3_11_BOSS, four secret bosses.

Changed entries:
Snake Ghoul became Snake Knight
River Snake Ghoul became Serpent Ghoul
“$EN_ARMORED SPEEDY_LOWXP”,“FAST”,“Worm”,"Swift Armored Spawn|These worm-Revenant have developed a thick carapace.

  • in normal, it’s called Shell Worm. Intentional or not, dropping the Shell?
    EN_RIGHT_HAND - in plus it’s missing the bit about being LEFT hand. Intentional or not?

Added entry - where is that one used?
EN_NORMAL_EXP_LOWXP

EN_FINGER_PLUS - this one is empty in en-US, but leaving it equally empty in cs-CZ throws errors.

Basically I’m trying to find out what are the “real” differences between normal and plus for localization purposes - and after a bit of reshuffling/reordering, I get these things above plus:
LOWXP versions of SHEEP
SQ7 business.

So, once the few inconsistencies are sorted out, a comment in data_enemy_plus.csv could just point the localizer to these two sets of data, telling them to copy their data_enemy, add _PLUS to identifiers, “+” to name and shortname for all existing entries.
(Edit: And I didn’t find any text differences at all, except the ++ identifiers)

Data_status_effects - FLAV_PHYSICAL_RANGED and FLAV_PHYSICAL-RANGED.
FLAV_AREA is listed twice.

Yeah, there’s a lot of little glitches like this, thanks for pointing 'em out. I’ll fix them up in the next build.

Okay - question time!

So, right now in localization files you can do simple re-directs for duplicated information rather than typing the same exact text twice. For instance, if you have a cell that says “$EN_SPEEDY_DESC” (flavor text for speedy worms) and you want to copy the same thing for “$EN_SPEED_PLUS_DESC” (speedy worms in new game+) you just type in the first flag’s name, “$EN_SPEEDY_DESC” as the value for the second.

The problem is, there are certain parts in the game where I’m using the $ character, for instance, for journal variables. And if this is the first letter in the string, the game thinks it’s a redirect and chokes when it doesn’t find a flag with that name.

I already had problems with the “$N” variable, which I use for linebreaks, since if you started a string with that you’d have the same problem. I’m already going to switch the system over to use “” for linebreaks, since that’s more consistent with the existing localization format and easier to visually separate when you’re working with raw text.

So I’m thinking about possible ways to fix this along those lines.

[list=1]
:367cq4x9]Make re-directs “smart” so it can guess your intent
This is the lazy approach, and I’m not really in favor of it. I think it would be more work for everyone and wouldn’t get good results. The only advantage is we don’t have to change the existing format./
:m:367cq4x9]
:367cq4x9]Make all redirects explicit with a unique invocation
Ie, make a redirect line something like this: “$EN_SPEEDY_DESC”. This means we’ll have to go back and fix all existing re-directs, but it has the advantage that it’s perfectly clear what the localizer’s intent is, and dead easy for the parser to understand. If there’s a flag at the beginning, remove that and then replace the string with the value for the flag represented by the remaining text. /
:m:367cq4x9][/list:o:367cq4x9]