2
0
mirror of https://github.com/koreader/koreader synced 2024-11-10 01:10:34 +00:00
koreader/frontend/ui/elements/font_ui_fallbacks.lua
Frans de Jonge b9261e0245
[i18n] Font UI fallbacks: insert replacement characters with template for Weblate compatibility (#11614)
Added in <https://github.com/koreader/koreader/pull/8722> and unproblematic for the past two years, but Weblate has now decided to complain about it to the extent of disabling translations:

> Error message
String contains control character: 'If some book titles, dictionary entries and such are not displayed well but shown as \ufffe\ufffe or ��, it may be necessary to download the required fonts for those languages. They can then be enabled as additional UI fallback fonts.\nFonts for many languages can be downloaded at:\n\nhttps://fonts.google.com/noto\n\nOnly fonts named "Noto Sans xyz" or "Noto Sans xyz UI" (regular, not bold nor italic, not Serif) will be available in this menu. However, bold fonts will be used if their corresponding regular fonts exist.'
2024-04-03 17:55:41 +02:00

161 lines
6.3 KiB
Lua
Raw Blame History

local FFIUtil = require("ffi/util")
local Font = require("ui/font")
local FontList = require("fontlist")
local InfoMessage = require("ui/widget/infomessage")
local UIManager = require("ui/uimanager")
local util = require("util")
local _ = require("gettext")
local T = FFIUtil.template
-- Some "Noto Sans *" fonts are already in ui/font.lua Font.fallbacks,
-- because they are required to display supported UI languages.
-- Don't allow them to be disabled.
-- Duplicated from (and should be kept in sync with) ui/font.lua, but here
-- by face name and there by font file/path name.
local hardcoded_fallbacks = {
"Noto Sans CJK SC",
"Noto Sans Arabic UI",
"Noto Sans Devanagari UI",
"Noto Sans Bengali UI",
}
-- Add any user font after Noto Sans CJK SC in the menu
local additional_fallback_insert_indice = 2 -- (indice in the above list)
local alt_name = {
-- Make it clear this "CJK SC" font actually supports all other CJK variant
["Noto Sans CJK SC"] = "Noto Sans CJK SC (TC, JA, KO)"
}
local fallback_candidates = nil
local fallback_candidates_path_to_name = nil
local genFallbackCandidates = function()
if fallback_candidates then
return
end
fallback_candidates = {}
fallback_candidates_path_to_name = {}
for _, font_path in ipairs(FontList:getFontList()) do
local fontinfo = FontList.fontinfo[font_path] -- (NotoColorEmoji.tff happens to get no fontinfo)
if fontinfo and #fontinfo == 1 then -- Ignore font files with multiple faces
fontinfo = fontinfo[1]
if (util.stringStartsWith(fontinfo.name, "Noto Sans ") or
fontinfo.name == "Noto Emoji") and
not fontinfo.bold and not fontinfo.italic and
not fontinfo.serif and not fontinfo.mono then
fallback_candidates[fontinfo.name] = fontinfo
fallback_candidates_path_to_name[font_path] = fontinfo.name
end
end
end
end
local more_info_text = T(_([[
If some book titles, dictionary entries and such are not displayed well but shown as %1 or %2, it may be necessary to download the required fonts for those languages. They can then be enabled as additional UI fallback fonts.
Fonts for many languages can be downloaded at:
https://fonts.google.com/noto
Only fonts named "Noto Sans xyz" or "Noto Sans xyz UI" (regular, not bold nor italic, not Serif) will be available in this menu. However, bold fonts will be used if their corresponding regular fonts exist.]]), "￾￾", "<EFBFBD><EFBFBD>")
local getSubMenuItems = function()
genFallbackCandidates()
-- Order the menu items in the order the fallback fonts are used
local seen_names = {}
local ordered_names = {}
local checked_names = {}
local enabled_names = {}
for _, name in ipairs(hardcoded_fallbacks) do
table.insert(ordered_names, name)
seen_names[name] = true
checked_names[name] = true
enabled_names[name] = false
end
if G_reader_settings:has("font_ui_fallbacks") then
local additional_fallbacks = G_reader_settings:readSetting("font_ui_fallbacks")
for i=#additional_fallbacks, 1, -1 do
local path = additional_fallbacks[i]
local name = fallback_candidates_path_to_name[path]
if not name or seen_names[name] then
-- No longer found, or made hardcoded: remove it
table.remove(additional_fallbacks, i)
else
table.insert(ordered_names, additional_fallback_insert_indice, name)
seen_names[name] = true
checked_names[name] = true
enabled_names[name] = true
end
end
if #additional_fallbacks == 0 then -- all removed
G_reader_settings:delSetting("font_ui_fallbacks")
end
end
local add_separator_idx = #ordered_names
for name, fontinfo in FFIUtil.orderedPairs(fallback_candidates) do
if not seen_names[name] then
table.insert(ordered_names, name)
checked_names[name] = false
enabled_names[name] = true
end
end
local menu_items = {
{
text = _("About additional UI fallback fonts"),
callback = function()
UIManager:show(InfoMessage:new{
text = more_info_text,
})
end,
keep_menu_open = true,
separator = true,
}
}
for idx, name in ipairs(ordered_names) do
local fontinfo = fallback_candidates[name]
local item = {
text = alt_name[name] or name,
separator = idx == add_separator_idx,
checked_func = function()
return checked_names[name]
end,
enabled_func = function()
return enabled_names[name]
end,
callback = function()
local additional_fallbacks = G_reader_settings:readSetting("font_ui_fallbacks", {})
if checked_names[name] then -- enabled: remove it
for i=#additional_fallbacks, 1, -1 do
if additional_fallbacks[i] == fontinfo.path then
table.remove(additional_fallbacks, i)
if #additional_fallbacks == 0 then
G_reader_settings:delSetting("font_ui_fallbacks")
end
break
end
end
checked_names[name] = false
else -- disabled: add it
if #additional_fallbacks < Font.additional_fallback_max_nb then
table.insert(additional_fallbacks, 1, fontinfo.path)
checked_names[name] = true
else
UIManager:show(InfoMessage:new{
text = T(_("The number of allowed additional fallback fonts is limited to %1.\nUncheck some of them if you want to add this one."), Font.additional_fallback_max_nb),
})
return
end
end
UIManager:askForRestart()
end,
}
table.insert(menu_items, item)
end
return menu_items
end
return {
text = _("Additional UI fallback fonts"),
sub_item_table_func = getSubMenuItems,
}