How you do localised languages?

Questions about the LÖVE API, installing LÖVE and other support related questions go here.
Forum rules
Before you make a thread asking for help, read this.
User avatar
togFox
Party member
Posts: 841
Joined: Sat Jan 30, 2021 9:46 am
Location: Brisbane, Oztralia

How you do localised languages?

Post by togFox »

I've never done this before. Someone has offered to convert my game to Korean. I now need to work out how to "unhardcode" all my love.graphics.print statements.

I'm thinking a global variable that tells me the current language (because it can be changed). 1 = english and 2 = korean. I'll call this "LANGUAGE"

I can then do an if/then for every single love.graphics.print and every love.graphics.draw that contains english.

Code: Select all

If LANGUAGE == 1 then
   love.graphics.setFont(FONT[mydefaultfont])
   love.graphic.print("Start", 100, 100)
else
   love.graphics.setFont(FONT[mykoreanfont])
   love.graphics.print("시작", 100, 100)      -- <-- this means "start"
end
Any other way? It seems very code/labour intensive for something that a lot of software around the planet does (support multiple languages).
Last project:
https://togfox.itch.io/hwarang
A card game that brings sword fighting to life.
Current project:
pen and paper gridiron. Build a team then watch simulated matches: https://togfox.itch.io/pad-and-pencil-gridiron
User avatar
dusoft
Party member
Posts: 765
Joined: Fri Nov 08, 2013 12:07 am
Location: Europe usually
Contact:

Re: How you do localised languages?

Post by dusoft »

Somebody just posted this:
viewtopic.php?t=96283

But if you want to go with a simple approach, just have a table of messages for each language and then just print based on your language variable...
User avatar
togFox
Party member
Posts: 841
Joined: Sat Jan 30, 2021 9:46 am
Location: Brisbane, Oztralia

Re: How you do localised languages?

Post by togFox »

Hmm. That library looks more than what I need - uncanny timing though.

I could have a table with two indexes - one for language and one for the text. For example

Code: Select all

dialogue[1][1] = "Start"
dialogue[2][1] = "시작"
The first index is the language and the 2nd index is the string of text. I guess even easier would be

Code: Select all

dialogue[LANGUAGE][1] = "Start"
dialogue[LANGUAGE][1] = "시작"
example

Code: Select all

love.graphics.print(dialogue[LANGUAGE][1], 100, 100)
Last project:
https://togfox.itch.io/hwarang
A card game that brings sword fighting to life.
Current project:
pen and paper gridiron. Build a team then watch simulated matches: https://togfox.itch.io/pad-and-pencil-gridiron
User avatar
dusoft
Party member
Posts: 765
Joined: Fri Nov 08, 2013 12:07 am
Location: Europe usually
Contact:

Re: How you do localised languages?

Post by dusoft »

Even easier is having strings for indexes from my experience. I would also switch message index with language index, but that's likely just my preference:

Code: Select all

dialogue['start']['en'] = "Start"
dialogue['start']['kr'] = "시작"
This should make it easier to translate when comparing strings in place.
User avatar
ivan
Party member
Posts: 1939
Joined: Fri Mar 07, 2008 1:39 pm
Contact:

Re: How you do localised languages?

Post by ivan »

The simplest way by far is to use a global reference:

Code: Select all

en = { start = "Start" }
jp = { start = "시작" }
lang = en
print(lang.start)
lang = ja
print(lang.start)
Lua metatables can used if you want to be able to fallback to another language.
User avatar
dusoft
Party member
Posts: 765
Joined: Fri Nov 08, 2013 12:07 am
Location: Europe usually
Contact:

Re: How you do localised languages?

Post by dusoft »

ivan wrote: Mon Jan 20, 2025 4:38 pm The simplest way by far is to use a global reference:

Code: Select all

en = { start = "Start" }
jp = { start = "시작" }
lang = en
print(lang.start)
lang = ja
print(lang.start)
Very good example.
RNavega
Party member
Posts: 457
Joined: Sun Aug 16, 2020 1:28 pm

Re: How you do localised languages?

Post by RNavega »

That new i18n library seems pretty cool.

To add to what's being said, maybe put all the strings into a separate Lua script / data file so that it's easier to pass around to any translators. They don't need a full copy of your game, just the strings file, provided it gives them enough context to know where the strings are used as that would help with translation.
They will need the game or at least some previewer tool in case there's a risk of the translated strings overflowing the UI elements containing them.

Taking Android apps as an example, they have that 'strings.xml' file per supported language, each in a different folder labeled by the locale that the strings file represents. Say, the file "res/values-fr/strings.xml" is the strings file for the French language.
More info here:
- https://developer.android.com/guide/top ... #BestMatch
- https://developer.android.com/guide/top ... rce#String

Love 12 is going to let you consult the system locales with this function here:
https://love2d.org/wiki/love.system.getPreferredLocales

But it can still be done in 11.x if you use FFI to access the SDL2 function that gives you the list of preferred locales:

Code: Select all

-- Prints a list of preferred system locales onto the console.
-- This code is Public Domain, it belongs to you.

io.stdout:setvbuf('no')

local ffi = require('ffi')

ffi.cdef([[
// https://github.com/libsdl-org/SDL/blob/SDL2/include/SDL_locale.h#L43
typedef struct
{
    const char *language;
    const char *country;
} SDL_Locale;

//https://github.com/libsdl-org/SDL/blob/SDL2/include/SDL_locale.h#L91
SDL_Locale * SDL_GetPreferredLocales(void);

// https://github.com/libsdl-org/SDL/blob/SDL2/include/SDL_stdinc.h#L460
void SDL_free(void *mem);
]])

local SDL = (jit.os == "Windows") and ffi.load("SDL2") or ffi.C
-- SDL2 Docs:
-- https://wiki.libsdl.org/SDL2/SDL_GetPreferredLocales#syntax
local rawLocales = SDL.SDL_GetPreferredLocales()

local nextLocale = true
local index = 0
while nextLocale do
    local rawLocale = rawLocales[index]
    -- The last locale object will have its 'language' field set to NULL / nil.
    if rawLocale.language ~= nil and index < 5 then
        -- From the SDL docs:
        -- "Please note that not all of these strings are 2 characters; some are three or more."
        -- So we need to use the ffi.string() helper function instead of just
        -- manually reading from the 'const char*' fields.
        local language = ffi.string(rawLocale.language)
        local country = ffi.string(rawLocale.country)
        print(('(Locale %i) Language: %s / Country: %s'):format(index, language, country))
        index = index + 1
    else
        nextLocale = false
    end
end

-- Need to explicitly free the created object.
SDL.SDL_free(rawLocales)

error('Finished')
Last edited by RNavega on Wed Feb 12, 2025 8:52 pm, edited 2 times in total.
User avatar
BrotSagtMist
Party member
Posts: 680
Joined: Fri Aug 06, 2021 10:30 pm

Re: How you do localised languages?

Post by BrotSagtMist »

Just use metatables for the smallest impact on already existing code.
All your translator gotta do is write the translation table, put it in a file, and then you load it.
If memory serves right, widelands for example uses _"string" for their translations.
You can even insert a catch in the metatable that automatically generates a translation file as words are uses ready to be edited.

Code: Select all

translation={
language="Sprache",
Print="Drucken",
Assface="Arschgesicht",
}
t=setmetatable({},{__index=function(_,b,_) return translation[b] or b end})
print("Print")
print(t.Print)
print(t.language)
print(t.nevermind)

l=function(s) return t[s] end

print(l"Print")
print(l"not translated")
print(l"Assface")
obey
User avatar
knorke
Party member
Posts: 275
Joined: Wed Jul 14, 2010 7:06 pm
Contact:

Re: How you do localised languages?

Post by knorke »

Here is a small game that uses a very simple system:
viewtopic.php?t=95693
It is not really anything new over what was already posted but it is a running example.
The file is txt.lua and the translations are in folder txt\
Press L to change language.
If a string is missing in some language then it falls back to english for that string.
Trivial but important, I think.

For projects with more text it might sense to have some helper functions:
1) A check whether some translations are missing.

2) During development new strings might be added (or removed) when some translation were already created.
(Adding mute_audio="Mute Audio" to several files by hand would quickly get annoying.) There should be a way to add the new string to all languages at once, with a "--TRANSLATE ME!" comment.
User avatar
dusoft
Party member
Posts: 765
Joined: Fri Nov 08, 2013 12:07 am
Location: Europe usually
Contact:

Re: How you do localised languages?

Post by dusoft »

I can also point people to a standardized .po/.mo file formats, e.g. an editor: https://poedit.net/
This is the most standardized way when working with multiple translators as it is easy to translate, see what's missing etc.

However I am not awae some good library support for Lua. I only found an OpenResty-related class:
https://github.com/bjne/lua-resty-i18n
Post Reply

Who is online

Users browsing this forum: Bing [Bot], Google [Bot] and 4 guests