Ren'Py Define Characters

Mescalino

Active Member
Aug 20, 2017
942
6,687
Curious,

Whats the difference:

Python:
define mc = Character(_("You"))
and
Python:
define mc = Character("You")
Is theer a specific reason one would add the extra _ ?

Thanks
 

79flavors

Well-Known Member
Respected User
Jun 14, 2018
1,607
2,256
Yup. RenPy does a pretty good job at extracting things that need to be marked for translation. However, sometimes you need to manually tell it "translate this too"... and that's where the _( ) comes in.

That said, it's kinda unusual for developers to have different default names for different translations. William tends to always be William. Daisy is always Daisy. Instead, names (except for the MC) are usually fixed.

So, much more common would be something like:

Python:
default mc_name = "Unknown"

define mc = Character("[mc_name]")

label start:

    scene black with fade

    $ mc_name = renpy.input("What is your name? {i}(Press ENTER for 'Yusef'){/i}")
    $ mc_name = mc_name.strip() or "Yusef"

    mc "Hi, my name is [mc]."

    "*** THE END ***"
    return

Better would be something like:

Python:
default mc_name = "Unknown"

define mc = Character("[mc_name]", color="#4169E1", what_prefix='"', what_suffix='"')
define mct = Character("[mc_name]", color="#4169E1", what_prefix='{i}(', what_suffix='){/i}')

define c = Character("Chuck", color="#FAEBD7", what_prefix='"', what_suffix='"')
define k = Character("Kathleen", color="#9F9F9F", what_prefix='"', what_suffix='"')

label start:

    scene black with fade

    $ mc_name = renpy.input("What is your name? {i}(Press ENTER for 'Yusef'){/i}")
    $ mc_name = mc_name.strip() or "Yusef"

    mc "Hi, my name is [mc]."
    mct "... and I'm thinking about [k]."

    "*** THE END ***"
    return

Coding it this way means all dialogue "spoken" by the character is surrounded by double quotes. And all character "thoughts" are show in parentheses and italics (which some devs do the hard way, by adding the markup tags to each and every line of dialogue manually). It's a little extra coding for an easier life later, as well as being slightly more polished.

And yes, I'm mixing strings within double quotes and strings within single quotes. I find it easier than using escape characters for ".
 
Last edited:
  • Like
Reactions: Mescalino

Mescalino

Active Member
Aug 20, 2017
942
6,687
Yup. RenPy does a pretty good job at extracting things that need to be marked for translation. However, sometimes you need to manually tell it "translate this too"... and that's where the _( ) comes in.

That said, it's kinda unusual for developers to have different default names for different translations. William tends to always be William. Daisy is always Daisy. Instead, names (except for the MC) are usually fixed.

So, much more common would be something like:

Python:
default mc_name = "Unknown"

define mc = Character("[mc_name]")

label start:

    scene black with fade

    $ mc_name = renpy.input("What is your name? {i}(Press ENTER for 'Yusef'){/i}")
    $ mc_name = mc_name.strip() or "Yusef"

    mc "Hi, my name is [mc]."

    "*** THE END ***"
    return

Better would be something like:

Python:
default mc_name = "Unknown"

define mc = Character("[mc_name]", color="#4169E1", what_prefix='"', what_suffix='"')
define mct = Character("[mc_name]", color="#4169E1", what_prefix='{i}(', what_suffix='){/i}')

define c = Character("Chuck", color="#FAEBD7", what_prefix='"', what_suffix='"')
define k = Character("Kathleen", color="#9F9F9F", what_prefix='"', what_suffix='"')

label start:

    scene black with fade

    $ mc_name = renpy.input("What is your name? {i}(Press ENTER for 'Yusef'){/i}")
    $ mc_name = mc_name.strip() or "Yusef"

    mc "Hi, my name is [mc]."
    mct "... and I'm thinking about [k]."

    "*** THE END ***"
    return

Coding it this way means all dialogue "spoken" by the character is surrounded by double quotes. And all character "thoughts" are show in parentheses and italics (which some devs do the hard way, by adding the markup tags to each and every line of dialogue manually). It's a little extra coding for an easier life later, as well as being slightly more polished.

And yes, I'm mixing strings within double quotes and strings within single quotes. I find it easier than using escape characters for ".
My VN will have a fixed name for teh MC (its somewhat part of the story).

I am doing everything myself so progress is slow and see i have lots to learn.

I was making a custom GUI and looked at some other VN's i liked and that cought my attention.

As for the thinking part I may actually use that part.

Thanks.
 

moskyx

Forum Fanatic
Jun 17, 2019
4,214
13,949
Usually, personal names aren't translated... unles you have to translate them from or into Cyrillic or any other language with non-western symbols. And also, some side characters have just a generic name (professor, guy, woman...). If you define them as characters, you should wrap that name with the _() thingy to make things easier for translators.

For example if you define a character like this define uw = Character(_("Unknown Woman"), color="#4169E1") the string "Unknown Woman" will be detected by Ren'Py SDK as a translatable text and then these lines will appear somewhere in the translation script:
Python:
translate MyLanguage strings: #MyLanguage being the translation language - Spanish, French or whatever
    old = "Unknown Woman"
    new = "" # <- insert your translation here
If you don't use the _() symbol, that string can still be translated, but Ren'Py SDK won't extract it automatically and the translator will need to write those lines of code himself. And, as that translation function only works if the old string matches identically the one in the original script, it's better to let the machine handle this, because if the translator writes old "Unknown woman" instead of old "Unknown Woman", the translation won't be displayed.

This applies for that renpy.input text too. So in 79flavors 's example, best practices would be

Python:
default mc_name = _("Unknown")

define mc = Character("[mc_name]")

label start:

    scene black with fade

    $ mc_name = renpy.input(_("What is your name? {i}(Press ENTER for 'Yusef'){/i}"))
    $ mc_name = mc_name.strip() or "Yusef"

    mc "Hi, my name is [mc]."

    "*** THE END ***"
    return

And:

Python:
default mc_name = _("Unknown")

define mc = Character("[mc_name]", color="#4169E1", what_prefix='"', what_suffix='"')
define mct = Character("[mc_name]", color="#4169E1", what_prefix='{i}(', what_suffix='){/i}')

define c = Character(_("Chuck"), color="#FAEBD7", what_prefix='"', what_suffix='"')
define k = Character(_("Kathleen"), color="#9F9F9F", what_prefix='"', what_suffix='"')

label start:

    scene black with fade

    $ mc_name = renpy.input(_("What is your name? {i}(Press ENTER for 'Yusef'){/i}"))
    $ mc_name = mc_name.strip() or "Yusef"

    mc "Hi, my name is [mc]."
    mct "... and I'm thinking about [k]."

    "*** THE END ***"
    return
 

Mescalino

Active Member
Aug 20, 2017
942
6,687
Ok, though I have no plan other then English (I'm Dutch native) and my other language skills except for German are bad (German I would consider mediocre but can hold a conversation)

Is there something i need to take in account to make it easy on someone if he/she was willing to translate my VN?

I do intent to create a prologue.rpy, chapter1.rpy etc. This so i keep it compact and can make quick fixes easier.
 

moskyx

Forum Fanatic
Jun 17, 2019
4,214
13,949
Basically, always remember to use the _() symbol when defining a character as we saw above, but also when creating a textbutton, showing some text or tooltip... so basically every time you write something to be displayed outside the dialog blocks and choice menus, as Ren'Py SDK won't extract those strings by default.

Also, you'll probably need to fix typos and rewrite some sentences in future releases, without changing its actual meaning, so the old translation could still be valid as, most of times, translators would have already translated the intended sentence and your fixed version shouldn't mean any change in the translation. But... Ren'Py doesn't know that and it will create a new line in the translation file, invalidating the older translation. That's because Ren'Py encrypts the dialog lines and uses a hash to shorten them and link them to their respective translations. So if you change something in the original line, its encrypted hash changes too and your translator will need to translate that line again... unless you add the old hash to the new line.

For example, for a dialog line like this:

Python:
label start:
    mc "Hi, my name is [mc]" # note that it's missing a full stop.
Ren'Py will create this in the translation file:
Python:
# game/script.rpy:33
translate spanish start_657286c9: # spanish is the target language, and start_657286c9 is the encrypted hash for that line

    # mc "Hi, my name is [mc]"
    mc "Hola, me llamo [mc]." # that's the Spanish translation I wrote for that line, including the missing full stop.
Basically, when running the game, Ren'Py will read the original script, search in the translation files for each line's encrypted hash (start_657286c9 in this case) and display on screen the translated lines.

Now, let's think you want to fix that little typo and add a full stop at the end of your line.
Python:
label start:
    mc "Hi, my name is [mc]." # nice, you fixed it!
My old translation shouldn't be affected as it was already correct. But that little change of yours means that now the content to be encrypted is different and Ren'Py won't find this line's new hash in the translation files, so the old translation won't work anymore and Ren'Py will display the text in English. The translator will generate again those translation files with the Ren'Py SDK, and that's what he/she would find now:
Python:
# game/script.rpy:33
translate spanish start_539e1991: # note that now the numbers in the hash are completely different (old hash = start_657286c9)

    # mc "Hi, my name is [mc]."
    mc "" # When generating the translation files, this line will appear either blank or with the original text on it
So the translator will need to rewrite the translated line again or find the older one (still present in the script but now completely useless because of the different hash) and copypaste its content onto the new blank line.

UNLESS... when fixing that little typo, you do it like this:
Python:
label start:
    mc "Hi, my name is [mc]." id start_657286c9 # now you're identifying this line with the old hash
Now, when Ren'Py reaches this line while running the translation, it will look for the old hash in the translation files instead of calculating and searching a new one. And that's great because the meaning of that line is still the same as before, so there was no real reason to force a change in the already existing translation.

As you see, it's a bit complicated as you'd need to actually take a look at the translation files in order to find the old encrypted hash. So I won't get mad if you don't do it, but it would be a very nice gesture. Basically, just keep this trick in mind in case you decide to write your own translations, as it will save you some time.
 
Last edited:

Mescalino

Active Member
Aug 20, 2017
942
6,687
8< -----snip----->8

Now, when Ren'Py reaches this line while running the translation, it will look for the old hash in the translation files instead of calculating and searching a new one. And that's great because the meaning of that line is still the same as before, so there was no real reason to force a change in the already existing translation.

As you see, it's a bit complicated as you'd need to actually take a look at the translation files in order to find the old encrypted hash. So I won't get mad if you don't do it, but it would be a very nice gesture. Basically, just keep this trick in mind in case you decide to write your own translations, as it will save you some time.
Thanks for that explanation.
As it stands now my buttons have the word Yes and No part of the image file.

I am guessing this is undesirable as it would make translating it impossible.

Also im guessing:
1628418594538.png
The red line generates that translation file you speak off?

Testing with the tutorial:

Code:
I'm sorry, but an uncaught exception occurred.

After initialization, but before game start.
Exception: Language 'english' does not have any translations.

-- Full Traceback ------------------------------------------------------------

Full traceback:
  File "renpy/bootstrap.py", line 326, in bootstrap
    renpy.main.main()
  File "renpy/main.py", line 617, in main
    run(restart)
  File "renpy/main.py", line 109, in run
    if not renpy.arguments.post_init():
  File "renpy/arguments.py", line 304, in post_init
    return commands[command]()
  File "renpy/translation/extract.py", line 68, in extract_strings
    extract_strings_core(language, args.destination, args.merge, args.force)
  File "renpy/translation/extract.py", line 32, in extract_strings_core
    raise Exception("Language %r does not have any translations." % language)
Exception: Language 'english' does not have any translations.

Windows-10-10.0.19041
Ren'Py 7.4.7.1862
Ren'Py Tutorial Game Ren'Py 7.4.7.1862
Sun Aug  8 12:33:04 2021
Im guessing i need to define the language somewhere.
 

moskyx

Forum Fanatic
Jun 17, 2019
4,214
13,949
Thanks for that explanation.
As it stands now my buttons have the word Yes and No part of the image file.

I am guessing this is undesirable as it would make translating it impossible.
Yeah, obviously pics can't be "translated" in a text script. Translators will need to create another pic with the desired text and then store it in a mimic route within the tl folder (for instance, if your original button is saved as game/images/buttons/button_yes.jpeg, then my Spanish button should be saved as game/tl/spanish/images/buttons/button_yes.jpeg. But you don't need to edit your script: if Ren'Py is running the translation, it will display the pic stored in the translation folder instead of the original one.

So to be really 'translation friendly' you should either avoid any pics with text on them (and that includes buttons), or provide the background of those pics (the pic without the text) to your translators so they only need to add a text layer to those base pics. But I only know a couple of devs who do this.

Also im guessing:
View attachment 1341884
The red line generates that translation file you speak off?

Testing with the tutorial:

Code:
I'm sorry, but an uncaught exception occurred.

After initialization, but before game start.
Exception: Language 'english' does not have any translations.

-- Full Traceback ------------------------------------------------------------

Full traceback:
  File "renpy/bootstrap.py", line 326, in bootstrap
    renpy.main.main()
  File "renpy/main.py", line 617, in main
    run(restart)
  File "renpy/main.py", line 109, in run
    if not renpy.arguments.post_init():
  File "renpy/arguments.py", line 304, in post_init
    return commands[command]()
  File "renpy/translation/extract.py", line 68, in extract_strings
    extract_strings_core(language, args.destination, args.merge, args.force)
  File "renpy/translation/extract.py", line 32, in extract_strings_core
    raise Exception("Language %r does not have any translations." % language)
Exception: Language 'english' does not have any translations.

Windows-10-10.0.19041
Ren'Py 7.4.7.1862
Ren'Py Tutorial Game Ren'Py 7.4.7.1862
Sun Aug  8 12:33:04 2021
Im guessing i need to define the language somewhere.
Not sure what you did there. After clicking on "Generate Translations" a new screen should appear, where you can type your desired language and after that you just need to click on the new "Generate Translations" button you see now. What you type in that language box is just an identification - if you type yellow, when you hit "Generate Translations" Ren'Py will just create a tl/yellow folder with all the translation scripts (they'll be named exactly as the original scripts that contain any translatable text), translation functions in these scripts will say something like translate yellow strings: or similar and then you'd also need to add somewhere a button or a textbutton with action Language("yellow") in order to activate the translation, but you could write your "yellow" translation in whatever language you like. Actually, Ren'Py tutorials and documentations use the term "piglatin" for their translation's stuff to show it can work with any language, as long as you always use the same word as your language identifyer (although I never liked that word, it feels kind of derogatory to me). Anyway, what I'm trying to say is that "english" should not be the problem here.

Note that the original language of the game will always be identified as None by Ren'Py, no matter what the actual language is. If you take a look at the Tutorial's game/tl folder you'll see a bunch of language folders. As the tutorial was originally created in English, if there's an "english" folder is just because you have just created it and it should contain all the translation files (I've done it myself right know and everything works fine on my end). If there's no "english" folder... well, I just don't know what you did to get that exception.