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.
Great tutorial. Don’t think I have seen localisation covered off in GMS2 yet, and you explain things so well.
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
The absolute best tutorial for how to handle localizations in Game Maker, thank you so much.
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.
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
Thank you so much for this. Managed to flawlessly implement it with my current protoype (thankfully still in relatively early stages!)
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.
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!
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!
Just wanted to ask the exact same question - thank you for noting down the solution!
Thank you for taking the time to write down the solution you found!
I haven't need this yet but I know this will be useful for me so yeah THANK YOU, your videos are great!
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.
Massive thanks for the video! Very well done and well explained. Helps a ton!
This deserves more views... This helped me alot.
Love this tutorial! Thanks for this, I will probably use this later on :)
Much appreciated!
Amazing video, thanks so much for this. Your channel is rapidly becoming my favourite along with Matharoo. How can we support you?
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.
@@blobfishdev yeah I bought your pack already although I have a long list of games to get through :D thanks!
Absolutely excellent tutorial. Wonderful.
This is fantastic, thanks so much for sharing!
Excellent tutorial. Thanks!
Thanks for the video! Sorry you had to learn about good localization practices the hard way.
Be sure to encode your csv as UFT-8 too to avoid character issues
Thank you so much for this!
Amazing. Merci!
Thank you this was so helpful!
I LOVE YOU
Thank you! I can't believe this is so simple. =D
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.
What if there are commas in your text?
you saved my life
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.
I didnt knew that this is so easy to do!
Thank you!
Thank you so much! A shame Gamemaker Studio Community lost you to Godot :(
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);
I think in your example your text variable is undefined, it needs to be a string (even if it's an empty one)
@@blobfishdev sorry for answer to late. Thanks so much for the help and for this video which is very helpful.
Anyway to make it so line brakers work? they just stay as
in the text rather than linke braking.
Should you also destroy ds_map?
do you have the answer?
@@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
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
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?
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.
Nice :D ou super ;)
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.
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.
@@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?
@@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
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
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.
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)?
so I need to use load_cvs(filename) and after that use it with json_decode to get the ds ma?
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
great chanel
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?
I think you should be able to change the default separation character to a comma when you export from Excel
@@blobfishdev I attempted that but it didn't work. However, exporting the file from Google Sheets did work.
@@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?
@@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.
Great Tutorial but the Download Link seems offline.
I think it's because of pastebin, it should come back soon
@@blobfishdev It´s still offline. Could you upload it elsewhere?
@@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;
}
Thanks for that!
How to make it with DnD?
I've never used DnD sorry
@@blobfishdev it's okay, I already have swtiched to the GML. Thanks for the content!