Best practice to add CJK (Chinese, Japanese, Korean) fonts

I searched the forum and couldn’t find the info in one place, thought I’d put it together in one post.

In the current project I use the following method:

  1. Split your font into 2 fonts: europe font and cjk font

  2. For CJK use only the characters you need, not the whole font. (A situation where text is only displayed. Users do not write text via input.)

to switch fonts just use gui.set_font(), example:

local function updateFont(self)
	local font_name = "main"

	if (global.defaultLanguage == 'ja') then
		font_name = "japanese"
	end

	local listTextFields = {
		"text_field1",
		"text_field2",
		"text_field3"
	}

	for k, v in pairs(listTextFields) do
		gui.set_font(gui.get_node(v), font_name)
	end	

end

to get unique characters you can use the following function (thanks @Dragosha )

local function uniqueSymbols(mytexttable)
    local unique={}
    local extra_characters=""
    for k,v in pairs(mytexttable) do
        if v then
            utf8.gsub(v,".", function(c)
            if  not unique[c] then
                unique[c]=1
                extra_characters=extra_characters..c
            else
                unique[c]=unique[c]+1
            end
            end)
        end
    end
    print(extra_characters)
end

(dont forget to fetch utf8 lib, for example I use this one by @d954mas)

I’ve also attached a little demo (en / ja languages) where you can see how it works:
test_CJK_font.zip (3.9 MB)

also, if there is not much text in the game, you can use one font file, but for this you need to merge into one ttf / otf file. Has anyone tried this method? What tools can be used?

8 Likes

Thank you for the write-up! CJK support is one of those things I’ve been somewhat concerned about.

Do you use a larger font size for e.g. Traditional Chinese in order to make smaller text legible? I’m unsure if it could cause any unexpected layout issues when switching font. Maybe it just works, I don’t know.

When JCash posted about Extension-fontgen a couple of weeks ago, I thought it would be great for CJK when you want to display strings unknown at compile time, like player names etc. I haven’t done any tests with it yet, though.

3 Likes

The Druid contains CJK language example - druid 1.0. You can select languages and see how it looks.

Druid Example using already prepared CJK font with set of all localization characters (I use editor_script from defold-lang to collect them: https://github.com/Insality/defold-lang/blob/main/USE_CASES.md#use-editor-script-to-collect-unique-characters)

To prepare your font (to merge CJK with your current one) you can use FontForge to do that. It has option to “merge” fonts

If you have several fonts, it’s possible to use only one of them in your GUI files. The font resource can be changed with something like `go.set("#gui", “font”, {key = …}). So the live-update with downloadable fonts should be available too

Also there is new extension https://github.com/defold/extension-fontgen. It can generate a font texture to use and seems can be useful also to prepare your fonts with set of characters runtime if usefl

6 Likes

Do you mean how to fix smooth/slighty blurred effect? I noticed that for CJK better to use distance field output format. You can check the example also in Druid example.

@Insality thanks for sharing your experience! Interesting to check extension-fontgen also.

about the changing font - this could be very handy! but I can’t figure out how to use go.set("#gui", “font”, {key = …}) is it possible to call go.set inside .gui script? How would you do it in a small demo I attached in the start post?

I use this script. I have two fonts.
1)base
2)base + cjk

When live update is loaded I changed all fonts in gui.

This is change_font.script. I attached it to every object with gui

local COMMON = require "libs.common"

local HASH_FONT_CHANGED = hash("font_changed")

local T_OPTIONS = { key = hash("game_font") }

go.property("target_gui", msg.url())

function update(self, dt)
	if COMMON.LOCALIZATION.font_all and not self.loaded then
		self.loaded = true
		go.set(self.target_gui, "fonts", COMMON.LOCALIZATION.font_all,T_OPTIONS )
		msg.post(self.target_gui,HASH_FONT_CHANGED)
		msg.post("#", COMMON.HASHES.MSG.DISABLE)
	end
end

5 Likes

No, I mean where on screen the characters are rendered.

Let’s say your Latin characters are 18px and your Traditional Chinese are 24px. When switching font on a text node, the alignment/pivot setting on the node will determine in which direction the extra 6px will expand. If it’s a multiline node, the node width also matters. To get the characters to render in the exact position you want for both languages, you probably need to be a bit more careful about how you set up the text nodes.

Now, I do think it should be fairly straight-forward to get the result we want. At the same time, in previous non-Defold projects, I’ve fixed many small text layout bugs that occurred in some but not all supported languages.

I’m trying to adapt my demo project (which I attached in the forum thread) to use go.set("#gui", “fonts”, ...) for changing font but I don’t understand, what should I specify as the 3rd parameter in my case?
A link to the directory specified in Project -> Bundle Resources where the already prepared japanese.font is located ?

[ UPDATE ]
phew, found a solution. looks very “reinvent the wheel” but works :slight_smile:
create change_font.script and attach it to GO that contains GUI:

go.property("target_gui", msg.url())
go.property("my_font1", resource.font("/fonts/main.font"))
go.property("my_font2", resource.font("/fonts/japanese.font"))

function on_message(self, message_id, message, sender)
	if message_id == hash("update_font") then
		if message.font == "main" then
			self.my_font = self.my_font1
		elseif message.font == "japanese" then
			self.my_font = self.my_font2
		end
		
		go.set(self.target_gui, "fonts", self.my_font, {key = "main"})
	end
	
end

source of this solution:
test_CJK_font2.zip (3.9 MB)

I haven’t faced with this, but now got your idea. but I can see two ways: 1) just set same size font in settings 2) set scale of text depends on size in runtime.

1 Like