PUBLISHED 2 January, 2020
The short answer is yes and I’d like to demonstrate that to you through some statistics from my own game.
Two years ago I released my game “Startup Company” on Steam Early Access. Since release, I have always felt like localization is an important aspect of any game. Upon release, the game supported the following languages: English, French, German, Spanish, Portuguese (BR), Simplified Chinese, Russian and Turkish.
Disclaimer: We, at Hovgaard Games, are current working on a free community translation platform called
Localizor
Who bought the game?
Startup Company sold 50,000 copies within the first month. Let’s see where these customers came from:
Country |
Percentage of sales |
United States |
21% |
France |
15% |
China |
10% |
Germany |
9% |
United Kingdom |
9% |
Canada |
5% |
Australia |
3% |
Spain |
2% |
Belgium |
2% |
Norway |
2% |
It’s no surprise that the USA comes out on the top, however, what is surprising is that in my top 10, countries of non-native English speakers take up a whopping 40% (20,000 copies) of my sales.
It’s impossible to determine if these customers would still buy my game if it wasn’t translated. Instead, we can notice one thing: Korean is nowhere to be found in my top 10…
Introducing Korean translations
In June 2018, I introduced Korean translations to Startup Company. First, let’s take a look at the top 10 from the 1st of March 2018 to the 31st of May 2018 (3 months before the Korean release):
Country |
Percentage of sales |
United States |
19% |
France |
17% |
Germany |
10% |
China |
7% |
United Kingdom |
7% |
Canada |
5% |
Russia |
4% |
Turkey |
4% |
Brazil |
3% |
Australia |
2% |
As you can see, it looks quite similar to the first month after my Early Access release.
Now let’s take a look at the 3 months after I added Korean translations (1st of June 2018 to the 31st of August 2018):
Country |
Percentage of sales |
United States |
19% |
Korea |
16% |
Germany |
9% |
China |
7% |
United Kingdom |
7% |
France |
5% |
Russia |
4% |
Canada |
4% |
Turkey |
3% |
Brazil |
3% |
Notice Korea suddenly came up?
At the time I expected the numbers to slow down, however, to my surprise, it only stabilized. Looking at the last 3 months, Korean has been my 4th best-selling country taking up 6% of all sales.
Steam’s language filter
So how did all of these Korean speaking people suddenly find my game? I didn’t do any advertising, all I did was click the Korean checkbox on my Steam Store Page.
One guess could be that Steam’s algorithms greatly reward localized games to their respective audiences.
Another possibility is that it happened “naturally” due to Steam’s language filter. Let me explain:
If you’re not an English speaker, it’s not only a matter of preference to have games in your native language but it’s also a requirement for most people. Steam knows that! To prove my point, try the following:
- Check out your first 10 games on your Discovery Queue on Steam
- Go to Steam’s language preferences.
- Select Korean as your secondary language.
- Redo Step 1 and notice how you see completely different games, all supporting Korean.

I believe that my game used to be excluded from most Korean players because of my lack of localization. Once I added their language, I suddenly came upon the Discovery Queue (and the rest of Steam) causing my Korean sales to boom.
Conclusion
I don’t believe there’s ever ever ever ever a reason not to translate your game into as many languages as possible, as soon as possible.
It’s common practice to release supporting English only and then adding localization later on but I think this is a huge mistake since you’re wasting a tonne of exposure and potential wishlists.
You might not have the money to pay for professional translations, but the least you can do is let your community translate your game using free platforms like Localizor.io.
PUBLISHED 10 September, 2019
I know what you’re thinking: how dare he…
First, let me elaborate on the title. I believe making a successful indie game is not as hard as the gamedev communities want you to believe.
“Indiepocalypse” and Steam Direct are both popular go-to excuses when the dream of becoming a full-time game developer fails. It’s not hard to find threads and blog posts from devastated developers warning hopeful beginners to quit their dream and get a job as a Wordpress developer.
While the excuses above are comforting (it’s not you, it’s the industry), I don’t believe these are ever the reason for failure. Below is a list of what I believe are the most common reasons for shattered gamedev dreams:
You don’t understand how special it is
Let’s look at what it really means to become a successful (and full-time) game developer:
- Every single morning you’ll grab a cup of coffee, sit down in front of your computer and start working on your personal project. No boss, no job, no distractions. Think about that for a second…
- You’re one of the very few people in the world who can make a living from doing what they truly love.
- Your game will most likely affect hundreds of thousands of people. Try to imagine a sports stadium filled with 100,000 cheering fans. Now imagine that every single person in the stadium has played your game. It will take your breath away, literally.
With this in mind, try to understand what you’re trying to achieve. This isn’t your average dream. So to the average person, it will require extreme persistence and consistency and a lot of hard work. Motivation will help you with the first 10%, the remaining 90% is up to you. Ask yourself if this is really for you. If it is, give it all you have and make sure it’s your #1 priority until you’re done. No turning back, full commitment.
You’re competing with the big boys
A pixel-art platform with a twist? Another first-person zombie survival? Don’t do it. I’m not saying there’s no room for more games in popular genres. What I’m saying is that you should target niche genres as a beginner that have less competition and therefore higher chances of success.
Your logo sucks and your assets are awful
This one is incredibly important! Your logo, trailer, assets and UI should be made by a person who has experience and talent in these areas. If that’s not you, it’s time to find a contractor. Jump over to /r/gameDevClassifieds or similar and find someone with experience in the type of work you need.
If you don’t think your game looks exciting, no one else will.
“But I don’t have the budget for that”
Of course you do. It will probably cost you no more than $2000. If that sounds like a lot, remember that incredible goal you’ve set for yourself. I sold my car to pay for assets for Startup Company. You’ll find a way if you take your project seriously.
You released a game no one has heard about
This is a classic. A lot of developers are afraid of showing their incomplete game, so they wait until everything is perfect and then release the game out of thin air, believing that Steam will do the promoting.
Unfortunately, Steam won’t. They don’t give a crap about your game if the wishlist count is not in the 10,000s. Get that Store Page up and running as fast as possible and start collecting wishlisters! Do devlogs, twitch streams, Youtube vlogs, Steam announcements, interviews, anything. It’s all about showing players that something cool is on its way.
You only speak English
While this isn’t a top priority before your release, it’s definitely a low-hanging fruit. Remember, there are a lot of people in the world who like to play games, but who don’t necessarily understand English.
A lot of non-English users have configured Steam to simply filter out games which aren’t translated into their native language. Your game might be amazing, but they will never see it.
Steam is all about algorithms. Adding localization will increase your “score”, make you more visible and ultimately result in more sales.
Try visiting Steam Stats and look at the “language” variable. At the time of writing, only 37% of users are native English speakers. I would never release a game without support for Simplified Chinese, Russian, Spanish, German and French as a minimum.
You didn’t prepare your release in time
This is another missed opportunity for a lot of developers. You get too obsessed at perfecting the game and may even be battling to meet a release date deadline, leaving no time to prepare the party.
I recommend spending at least 2 months before your release doing nothing but marketing. 0% development, 100% marketing.
Use the time to:
- Optimize your marketing assets (screenshots, text and trailer).
- Cold call content creators on Twitch, Mixer, Twitter and YouTube (most importantly). Make sure they have access to your game and tell them when they are allowed to release the content. Don’t underestimate this. I sent more than 500 personalized emails in the weeks before my release.
- Increase your communication with your community. Post on all of your social media accounts daily. Do giveaways, competitions or whatever you can come up with. At this stage, it’s all about making noise and getting noticed.
That’s all
In my opinion, these are the 6 most common reasons why game developers fail to create a successful indie game. Obviously every release and game is unique. No matter how correct your “execution” is, you’ll never get any success unless you have a game that’s worth playing.
Want my advice?
I remember when I was about to release Startup Company. I had thousands of questions and no one that could answer them. So if you’re stuck with something, you can always reach me on Twitter or Discord and I’ll try to answer as well as I can :-)
PUBLISHED 13 February, 2015
I’ve been using cmd.exe with Console2 for a few years now. I haven’t spent much time trying to optimize the experience. Actually the only reason I’m using Console2 is for my copy/paste needs.
Until my friend showed me a screenshot of his new Powershell. It had nice Git integration with colors and auto-complete. My own console was stupid and colorless. Time to geek out.

The steps below requires that you already have Git installed and configured.
##Install Console2
If you haven’t already go install Chocolatey.
Next, install Console2:
You can now run Console2 by hitting Win Key+R
and enter console
.
##Install PowerShell Git integration
Posh-git is Git integration/highlight-coloring-stuff for Powershell. Again we use Chocolatey:
It’s plug and play. Nothing more to do.
##Configuring Console2
If Console2 is already running please restart it. We need the post-git modules imported. Next, go to Edit -> Settings...
. Select Tabs
. Select Powershell
and press the Move up
button until it’s in the top.
Mark the Use default
checkbox. If you like you can set your Startup dir
(mine is C:\code).
In the same Settings window now select Hotkeys
. Find Paste
and change the hotkey to CTRL+V
. You can now paste text into the commandline using CTRL+V
!
##Set up Powershell scripts
I’ve got two going on. The first is the best.
Powershell scripts is “installed” by referring to psm1 files in your so called $profile. Jump to Powershell and enter $profile
. It will return something like C:\Users\jhovgaard\Documents\WindowsPowerShell\Microsoft.PowerShell_profile.ps1
. To include a script simply append a new line to the file like this:
Import-Module "C:\PATH\TO\YOUR\SCRIPT.PSM1"
Search and open .sln file in current folder
My script is available here:
https://gist.github.com/jhovgaard/8a91441e621094544a17
Save the file to your machine and add a reference in your $profile.
Open new Google Search from Powershell
My script is available here:
https://gist.github.com/jhovgaard/9dad2a49a441a83a5874
Save the file to your machine and add a reference in your $profile.
Please notice all changes made to $profile requires restarting your console.
Last, here’s a nice wallpaper for your screens!
http://www.hdwallpapers.in/walls/blurry_abstract-wide.jpg
PUBLISHED 13 November, 2014
What is PJAX and why?
Today most web applications use a layout page, because we need to maintain the same HTML code over multiple pages. This HTML could be the header, footer and so on.
When the user clicks around our application they need to download this header and footer again and again and again.
Some developers use AJAX to download data from the server, and then manipulate with the DOM using Javascript to show this new data. Some use big Javascript frameworks and call their application a SPA. This is clever, but isn’t compatible with search engines and non-javascript users. So developers is forced to support two “versions” - a AJAX version and a pure HTML version. Cumbersome and expensive if you care about SEO.
PJAX fixes this issue. When enabled it will tell the server if it should include the layout HTML or not. If Javascript is enabled (not a crawler) there’s no reason to include the layout. If not, the server returns the complete HTML.
Quote from https://github.com/defunkt/jquery-pjax: pjax works by grabbing html from your server via ajax and replacing the content of a container on your page with the ajax’d html. It then updates the browser’s current url using pushState without reloading your page’s layout or any resources (js, css), giving the appearance of a fast, full page load. But really it’s just ajax and pushState.
So to get to the point: PJAX is a plug-and-play plugin that reduces bandwidth usage, increase performance and is fully compatible with non-javascript users.
Also, please notice that PJAX is also part of the YUI library: http://yuilibrary.com/yui/docs/pjax/, however I will use the standalone version in this demo.
Setting up the demo
I’ll go through setting up the demo application real quick, since this article isn’t about Nancy, but PJAX. If you’re new to Nancy I suggest you start by reading my getting started on Nancy blog posts.
Required Nuget packages for the demo is the following:
install-package nancy.hosting.aspnet
install-package nancy.viewengines.razor
install-package jquery
install-package jquery.pjax
After installing Nuget packages I move the script folder into another folder called “Content” only to follow Nancy conventions.
A HomeModule containing a set of actions:
using Nancy;
namespace NancyPjaxDemo
{
public class HomeModule : NancyModule
{
public HomeModule()
{
Get["/"] = p =>
{
return View["Index"];
};
Get["/first/"] = p =>
{
return View["First"];
};
Get["/second/"] = p =>
{
return View["Second"];
};
}
}
}
A _Layout.cstml view:
@using System
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>@ViewBag.Title</title>
</head>
<body>
Layout loaded: @DateTime.Now
<div id="container">
@RenderBody()
</div>
</body>
</html>
A Index.cshtml view:
@{
Layout = "_Layout.cshtml";
}
<h1>Index page</h1>
<ul>
<li><a href="/first/">First link</a></li>
<li><a href="/second/">Second link</a></li>
</ul>
And at last a First.cshtml and Second.cshtml:
@{
Layout = "_Layout.cshtml";
}
<h1>Welcome to second page!</h1>
<a href="/">Go back to the index page</a>
My solution explorer now looks like this:

Setting up the Javascript
Perfect, our demo is ready. Time to set up PJAX. First, let’s update the layout page to include jQuery and the PJAX library. I’ll add the following two lines just before my </body>
tag:
<script src="~/Content/Scripts/jquery-2.1.0.min.js"></script>
<script src="~/Content/Scripts/jquery.pjax.js"></script>
Next I’ll tell PJAX to use my #container
element which you can find in my _Layout.cshtml page:
<script>
$(document).pjax('a', '#container', { timeout: 1000 });
</script>
This tells PJAX that whenever a user clicks a link, the content should be loaded into #container
. The exact same behavior as Razor’s @RenderBody()
.
To summerize, my _Layout.cshtml
page now looks like this:
@using System
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>@ViewBag.Title</title>
</head>
<body>
Layout loaded: @DateTime.Now
<div id="container">
@RenderBody()
</div>
<script src="~/Content/Scripts/jquery-2.1.0.min.js"></script>
<script src="~/Content/Scripts/jquery.pjax.js"></script>
<script>
$(function() {
$(document).pjax('a', '#container', { timeout: 1000 });
});
</script>
</body>
</html>
Now when I click through my application I will see that PJAX now sends each request using AJAX instead of regular pageloads.

You’ll also notice the X-PJAX: true
header (marked with yellow). This tells our application that this request should not contain the layout HTML, only the new HTML for the requested page.
Because PJAX detects that we are returning the Layout HTML again, it fires a regular pageload. What we need to do is tell Nancy to forget about the Layout page if we see this header.
Setting up Nancy for PJAX
First, we need to provide all views a bool indication whetever the request is coming from PJAX or not. I’ll do this by using the Before hook in Nancy in my HomeModule
:
Before += ctx => {
ViewBag.IsPjax = Request.Headers.Keys.Contains("X-PJAX");
return null;
};
This will make my HomeModule look like this:
using System.Linq;
using Nancy;
namespace NancyPjaxDemo
{
public class HomeModule : NancyModule
{
public HomeModule()
{
Before += ctx =>
{
ViewBag.IsPjax = Request.Headers.Keys.Contains("X-PJAX");
return null;
};
Get["/"] = p =>
{
return View["Index"];
};
Get["/first/"] = p =>
{
return View["First"];
};
Get["/second/"] = p =>
{
return View["Second"];
};
}
}
}
Great, now ViewBag.IsPjax
can tell is if the request is coming from PJAX. Now we simply need to add an if-sentence to our Views, to make them able to ditch the layout if request is coming from PJAX:
@{
Layout = ViewBag.IsPjax ? null : "_Layout.cshtml";
}
I’ll do this everywhere I set the Layout in Razor. In my demo it will be Index.cshtml, First.cshtml and Second.cshtml.
Now when I click around my application, I’ll see that the “Layout loaded” timestamp doesn’t change, but the content and URL does! If I disable Javascript, everything still works as any other regular webpage.

We’re done!
That’s it, one complete PJAX application. You should notice that when you write Javascript for PJAX application it is very important that all events is attached to the #container
object. For example to handle a click event on some button, you should NOT do this:
Wrong way:
$('#MyButton').click(function() {});
Right way:
$('#container').on('click', '#MyButton', function() {});
The reason is that PJAX will inject the content of your pages into the existing DOM. The “wrong example” only hooks up to instances of my #MyButton
on intilization, but PJAX injects #MyButton after initilization.
I hope you see the power of PJAX. I really do think this is the next big thing. I already use it in production and it is a huge timesaver and have really changed the way I do web applications. To be honest I tend to write much more C# than Javascript now, after PJAX. Simplicity at it’s best. PJAX have my vote for the no. 1 web technology of 2014.
The full source of this demo is available at GitHub: https://github.com/jhovgaard/nancy-pjax-demo.
Please leave a comment, I love feedback :-)