GameMaker Studio 2 - Localization Tutorial (Avoid Wasting Months of Work)

Поділитися
Вставка
  • Опубліковано 6 лют 2025
  • Translation script: pastebin.com/H...
    Font Drop: fontdrop.info/
    My game on Localizor: www.localizor....
    📩 I share more info like this, tips and behind-the-scenes in my newsletter: blobfish.dev
    ▬▬▬▬▬▬▬▬▬▬▬▬ FOLLOW ME ▬▬▬▬▬▬▬▬▬▬▬▬
    📩 Newsletter: blobfish.dev (best way to follow my work)
    🐤 Twitter: / blobfishdev
    👽 Discord: thomasgervraud...
    🐱‍👤 Steam: store.steampow...
    🐱‍🐉 Itch: thomasgvd.itch...
    ▬▬▬▬▬▬▬▬▬▬▬▬ MY GAMES ▬▬▬▬▬▬▬▬▬▬▬▬
    🎮 Space Gladiators: store.steampow...
    🥔 Lost Potato: store.steampow...
    When I started out making my first game, I was writing all of my texts directly within my code. Maybe you're doing the same thing and honestly... that's a mistake. You'll waste a huge amount of time later on when you want to translate your game to other languages. You'll have to track down all of your texts and write code to change it depending on the language selected by your player and that'll just be very VERY annoying to fix.
    Fortunately, there's a better and simpler way to do it. You can spend a few minutes right now setting up your project the right way and you'll save potentially months of effort down the line.
    THE SOLUTION
    What you want to do is to have all of the text that will need to be translated later on inside of external files. Those files will be in either .csv or .json format and they'll be easily tweakable using other tools like Google sheets or Localizor. I personally use .csv files.
    Then inside of your codebase you'll have keys that reference the actual text contained in those files. For example, you'll have a key like item_sword in your code where you want to display the name of your sword item, and in your external file you'll have the actual text which is "Sword". You can have one file for each language that you're translating to or just one big file with one column for each language if you don't have too much text.
    3 TIPS
    1. Replace numbers in your translations by symbols like {a} or {0} that you'll be able to replace in your code with the actual values. This way you won't have to change the value for every language you support every time you need to tweak the balance of your game and you won't have duplicate entries in your localization file.
    2. Don't concatenate strings inside of your code like this. In some languages the order of the words is different than in English so just put the whole text inside of your localization file.
    3. You'll need to add new ranges of characters inside of your fonts for some languages (like French) if you want the special characters to be displayed correctly. Also, you should check that your font supports the characters you'll need before choosing it by using a tool like FontDrop. You'll most likely need another font for Asian languages like Chinese, Japanese and Korean and you'll need to switch between the fonts depending on your current locale.

КОМЕНТАРІ • 70

  • @SidFish
    @SidFish 4 роки тому +8

    Great tutorial. Don’t think I have seen localisation covered off in GMS2 yet, and you explain things so well.

  • @anti64
    @anti64 14 днів тому

    Merci pour ça ! Je travaille sur un jeu qui aura BEAUCOUP de textes, dommage qu'il n'y ait pas de solutions encore plus rapides

  • @UltraHealthyVideoGameNerd
    @UltraHealthyVideoGameNerd 7 місяців тому

    The absolute best tutorial for how to handle localizations in Game Maker, thank you so much.

  • @fishyfinthing8854
    @fishyfinthing8854 Рік тому +2

    Hi, I have a couple of suggestions if it's possible:
    Perhaps we don't need to enum all languages. Just let the first row be the language name. Then let the number of the row in the ds_grid contain the language table be the numbers of languages and select the column belong to the chosen language.
    And just in case some language is not completed, we should have a default language to fall back to.
    That way, anyone can add any language by themselves (save for the language that doesn't get supported by the game's fonts and UI). The game can be coded to automatically recognize any additional language by their name on the first row.
    Anyway, thanks for the tutorial! I was able to make my version using your logic combine with my idea.

  • @erict7659
    @erict7659 4 роки тому

    Thank you - I had just started planning my game and was starting to work on localization, right when you posted this video. Your tips at the end were very helpful as well

  • @MadeByChaz
    @MadeByChaz Рік тому

    Thank you so much for this. Managed to flawlessly implement it with my current protoype (thankfully still in relatively early stages!)

  • @feronar-warsongus221
    @feronar-warsongus221 Рік тому

    The way I do it in my game is a bit different. I use a separate JSON file for each language (I.e. stringsEN.json), as well as another JSON file listing all available languages, as well as settings for each one such as which font to use (availableLanguages.json). This makes it easy for modders to add additional languages on their own. I also use a struct rather than a DS map, but I'm not sure if structs were a thing when the video was made.
    I also write the code to load English first regardless of language setting, then load the selected language (If other than English) and have it overwrite the English strings. This makes it so if a string is missing from a given language, it will fall back to English rather than crash the game.
    Also, JSON handles things like
    and commas better than CSV.

  • @RewdanSprites
    @RewdanSprites 9 місяців тому

    This tutorial is really really amazing. Wish I would have seen this back when I started my first game 😅... Congratulations with Brotato by the way!

  • @REALSOLIDSNAKE
    @REALSOLIDSNAKE 3 роки тому +14

    Wonderful tutorial! What a life saver.
    I do have one question, the
    new line command does not pass. It shows as text when the game is running.
    Any ideas on how to make it pass? I know how to make text wrap, but I want specific line breaks as the
    command allows to.
    Thanks a lot once more and keep up the good work. Subbed!
    Edit:
    - Never mind, I managed to fix that. For all interested, just add this line to the Translation script
    text = string_replace_all(text,"\
    ","
    ");
    Wish you all a great day!

    • @fluury
      @fluury 3 роки тому +2

      Just wanted to ask the exact same question - thank you for noting down the solution!

    • @matthias_erzberger
      @matthias_erzberger 8 місяців тому +1

      Thank you for taking the time to write down the solution you found!

  • @billiephan
    @billiephan 4 роки тому

    I haven't need this yet but I know this will be useful for me so yeah THANK YOU, your videos are great!

  • @grabbus
    @grabbus 3 місяці тому

    Just set up language file support in my own game! Already talking to a spanish game translator. I was right to get it done early before I had too much text, it was a pain to track it all down even though there wasnt a lot.

  • @fluury
    @fluury 4 роки тому +1

    Massive thanks for the video! Very well done and well explained. Helps a ton!

  • @jellybean2udios
    @jellybean2udios 3 роки тому

    This deserves more views... This helped me alot.

  • @shabanotti
    @shabanotti 2 роки тому

    Love this tutorial! Thanks for this, I will probably use this later on :)
    Much appreciated!

  • @Setanta93
    @Setanta93 4 роки тому +3

    Amazing video, thanks so much for this. Your channel is rapidly becoming my favourite along with Matharoo. How can we support you?

    • @blobfishdev
      @blobfishdev  4 роки тому +3

      Happy to hear that! Watching and liking the videos is already great. For now I'm experimenting with things but I'll probably make more courses or Patreon and stuff later. Or you could also check out my games if roguelites are your jam.

    • @Setanta93
      @Setanta93 4 роки тому

      @@blobfishdev yeah I bought your pack already although I have a long list of games to get through :D thanks!

  • @NoahNCopeland
    @NoahNCopeland 2 роки тому

    Absolutely excellent tutorial. Wonderful.

  • @Mightyjordy
    @Mightyjordy 2 роки тому

    This is fantastic, thanks so much for sharing!

  • @kwylde
    @kwylde 3 роки тому

    Excellent tutorial. Thanks!

  • @Monochromeglobox
    @Monochromeglobox 4 роки тому +1

    Thanks for the video! Sorry you had to learn about good localization practices the hard way.

  • @dynamic_zero
    @dynamic_zero 3 роки тому +1

    Be sure to encode your csv as UFT-8 too to avoid character issues

  • @SuperRapda
    @SuperRapda 2 роки тому

    Thank you so much for this!

  • @massithegame8753
    @massithegame8753 2 роки тому

    Amazing. Merci!

  • @jimmyyeah7061
    @jimmyyeah7061 Рік тому

    Thank you this was so helpful!

  • @Welvex.
    @Welvex. Місяць тому

    I LOVE YOU

  • @cimex6591
    @cimex6591 3 роки тому

    Thank you! I can't believe this is so simple. =D

  • @maniacallysane489
    @maniacallysane489 2 місяці тому

    im not sure if its because of updates or what have you, but the string replace all line wasn't working for me. i managed to fix it be changing the way the variable was defined.
    my solution was to change the variable definition to:
    if (argument_count > 1) var a = argument[1]; else var a = "";
    essentially does the same thing but the syntax of this definition ran the replacement for me while the other didn't. if anyone is having a similar issue, that was my solution.

  • @stravvman
    @stravvman 2 роки тому +2

    What if there are commas in your text?

  • @jaiv
    @jaiv 2 роки тому

    you saved my life

  • @pymns
    @pymns 3 роки тому +1

    You forgot about Unicode languages like Farsi and Arabic; these languages have a completely different structure. such are pretty hard to implement even for AAA companies as you can see how they mess up the character's order. thanks for the information btw.

  • @akalex770
    @akalex770 2 роки тому

    I didnt knew that this is so easy to do!

  • @myinnocentsoul
    @myinnocentsoul 2 роки тому

    Thank you!

  • @SiegfriedTheCatfish
    @SiegfriedTheCatfish Рік тому +1

    Thank you so much! A shame Gamemaker Studio Community lost you to Godot :(

  • @Madebylaer
    @Madebylaer 2 роки тому

    Hi... I have a problem with code:
    string_replace_all argument 1 incorrect type (undefined) expecting a String (YYGS)
    at gml_Script_Text (line 34) - text = string_replace_all(text,"{a}", a);

    • @blobfishdev
      @blobfishdev  2 роки тому +1

      I think in your example your text variable is undefined, it needs to be a string (even if it's an empty one)

    • @Madebylaer
      @Madebylaer 2 роки тому +1

      @@blobfishdev sorry for answer to late. Thanks so much for the help and for this video which is very helpful.

  • @Crimsonnnnnnn
    @Crimsonnnnnnn Рік тому

    Anyway to make it so line brakers work? they just stay as
    in the text rather than linke braking.

  • @stravvman
    @stravvman 2 роки тому +4

    Should you also destroy ds_map?

    • @pinatranaves
      @pinatranaves 2 місяці тому

      do you have the answer?

    • @stravvman
      @stravvman 2 місяці тому +1

      @@pinatranaves I still don't understand all that memory leaks stuff very well, but as far as I know, yes, you need to destroy all data structures

    • @SaneGameStudios
      @SaneGameStudios 25 днів тому

      do not destroy it, the init function is only called once at the beginningof the game, a ds map is fine to create and let stay exist as long as its not being constantly recreated. since the init script is only called once, the map is only created once then we constantly refer to it

  • @pinatranaves
    @pinatranaves 2 місяці тому

    Very good video, thank you!
    I have a question: when I change the language in a room, it works for all the texts in that room. When I change rooms, the language goes back to the default (it goes back to the language I started the script in).
    Has anyone faced this problem? Does anyone have any tips?

    • @pinatranaves
      @pinatranaves 2 місяці тому

      It seems that it is because the variable I am using to display the texts in draw_gui was defined in the create event of a persistent object, when starting the game. Therefore, it will only get the initial value of the text.
      In order for them to change according to the language, they must be updated in draw_gui or even in the room_start of this persistent object.
      I just removed it from the create event and moved it to the room start event.
      I will do more tests and if there is any news, I will report back here.

  • @thomasmeslin8399
    @thomasmeslin8399 3 роки тому

    Nice :D ou super ;)

  • @Gravedrinker
    @Gravedrinker 4 роки тому +1

    Is it possible to have the .csv be user-accessible and editable/switchable without having to create another build? That would be useful for testing, because testers could try out different values for things. Currently trying to find a good solution to do that.

    • @blobfishdev
      @blobfishdev  4 роки тому

      Doing it this way, the csv file gets shipped with your executable so your players have a copy of it when they install the game. Since your game pulls data from the file when it's being run, your players can modify the translations in the file and it'll be changed in game as well.

    • @Gravedrinker
      @Gravedrinker 4 роки тому

      @@blobfishdev Thanks, good to know. Then the next question becomes, is there an elegant way to have this not be the case? (If doing balancing via csv but then shipping the game via steam for example. While it's great for the testers, you don't necessarily want end users to have that same way to change the game) Would you encrypt the csv for that or is there another way?

    • @blobfishdev
      @blobfishdev  4 роки тому

      @@Gravedrinker That's a good question, I'm actually not sure what would be a good solution to that. Obfuscating the file could be one way but it definitely wouldn't be a 100% secure method

  • @PinClockFuntime
    @PinClockFuntime Рік тому

    I tried this with Japanese and it does not work, no matter what my computer's region is, what is in the csv, or what is in the game

    • @feronar-warsongus221
      @feronar-warsongus221 Рік тому

      Do you have a Japanese font loaded, with the Japanese character range? Are the Japanese characters actually showing up in the IDE? Did you call draw_set_font() for that font? MS Gothic has Japanese characters. For hiragana and katakana, the range is 12353-12531.
      I haven't tried it using CSV files as shown in the tutorial. I use JSON instead and it works fine for me.

  • @MDevlog
    @MDevlog 4 роки тому

    Great Tutorial! maybe this tutorial can work with difficulty too!
    I want to ask a q?
    how I can load a JSON file from included files (like how you made with the CSV one)?

    • @MDevlog
      @MDevlog 4 роки тому

      so I need to use load_cvs(filename) and after that use it with json_decode to get the ds ma?

    • @blobfishdev
      @blobfishdev  4 роки тому +1

      This function will retrieve a json from a file and put it into a map:
      function LoadJsonFromFile(fileName) {
      var myBuffer = buffer_load(fileName);
      var myString = buffer_read(myBuffer, buffer_string);
      var myJson = json_decode(myString);
      buffer_delete(myBuffer);
      return myJson;
      }
      I got it from this video which explains how to use json to save/load your game so that might help you understand it better: ua-cam.com/video/QmxQb1BFQRE/v-deo.html

  • @ksioncdesign7075
    @ksioncdesign7075 3 роки тому

    great chanel

  • @LMG3864
    @LMG3864 3 роки тому

    I'm having an issue with this system. When I save the locale file as a .csv in excel, it separates each column with a semi-colon instead of a coma, and Gamemaker can't seem to read them afterwards. Is there a workaround for this?

    • @blobfishdev
      @blobfishdev  2 роки тому

      I think you should be able to change the default separation character to a comma when you export from Excel

    • @LMG3864
      @LMG3864 2 роки тому

      @@blobfishdev I attempted that but it didn't work. However, exporting the file from Google Sheets did work.

    • @simon7788
      @simon7788 2 роки тому

      @@LMG3864 I tried this, but it doesn't work for me either. The only text the object displays is number 100175, which looks like refference number of ds_map, which is really strange. I copied the script word for word, but it still does this, is there a way to solve it?

    • @LMG3864
      @LMG3864 2 роки тому

      @@simon7788 I can only guess without the actual code. Sounds like you're printing the index instead of the actual text value.
      My problem was with the actual .csv file, as my OS defaults Excel to use semi-colons when saving .csv files and gamemaker can't tell what they are.
      That specific problem was solved by exporting the file using Google Sheets instead, but I have no idea if that'll work for you.

  • @DedSec00
    @DedSec00 3 роки тому

    Great Tutorial but the Download Link seems offline.

    • @blobfishdev
      @blobfishdev  3 роки тому +1

      I think it's because of pastebin, it should come back soon

    • @DedSec00
      @DedSec00 3 роки тому

      @@blobfishdev It´s still offline. Could you upload it elsewhere?

    • @blobfishdev
      @blobfishdev  3 роки тому +1

      @@DedSec00 I don't know what happened but I reuploaded it here: pastebin.com/HjxsVi49
      I'll also paste the code here in case it happens again in the future:
      enum LOCALE { EN, FR }
      global.locale = LOCALE.EN;
      InitTranslations();
      function InitTranslations() {
      global.locData = load_csv("locale.csv");
      var hh = ds_grid_height(global.locData);
      var translations = ds_map_create();
      for (var i = 0; i < hh; i++) {
      ds_map_add(translations, global.locData[# 0, i], i);
      }
      global.translations = translations;
      }
      function Text(key) {
      var text = "";
      if (global.translations[? key] != undefined) {
      var text = global.locData[# 1 + global.locale, global.translations[? key]];
      var a = argument_count > 1 ? argument[1] : "";
      text = string_replace_all(text, "{a}", a);
      } else {
      var text = key;
      }
      return text;
      }

  • @Lekiner
    @Lekiner 2 роки тому

    Thanks for that!

  • @Rich64
    @Rich64 3 роки тому

    How to make it with DnD?

    • @blobfishdev
      @blobfishdev  3 роки тому

      I've never used DnD sorry

    • @Rich64
      @Rich64 3 роки тому

      @@blobfishdev it's okay, I already have swtiched to the GML. Thanks for the content!