[RTL UI] Bidi-wrap filenames, paths, urls, metadata

bidi.lua:
- Revert "Alias everything to Bidi.nowrap() when in LTR UI,
  as using LTR isolates seems uneeded when already LTR" (part
  of a628714f) which was a wrong assumption: we need proper
  wrappers for all things paths. Enhance some of these wrappers.
- Fix GetText RTL wrapping which was losing empty lines and
  trailing \n.

- Wrap all paths, directories, filenames in the code with
  these wrappers.
- Wrap all book metadata (title, authors...) with BD.auto(),
  as it helps fixing some edge cases (like open/close quotation
  marks which are not considered as bracket types by FriBiDi).
  (Needed some minor logic changes in CoverBrowser.)

- Tweak hyphenation menu text
- Update forgotten SortWidget for UI mirroring
- KoptConfig: update "justification" index for RTL re-ordering,
  following the recent addition of the page_gap_height option.
This commit is contained in:
poire-z
2020-01-04 01:18:51 +01:00
parent a31abf79de
commit 0599c440cc
50 changed files with 378 additions and 182 deletions

View File

@@ -1,3 +1,4 @@
local BD = require("ui/bidi")
local DataStorage = require("datastorage")
local Device = require("device")
local InfoMessage = require("ui/widget/infomessage") -- luacheck:ignore
@@ -178,7 +179,7 @@ function SSH:addToMainMenu(menu_items)
callback = function()
local info = InfoMessage:new{
timeout = 60,
text = T(_("Put your public SSH keys in %1"), path.."/settings/SSH/authorized_keys"),
text = T(_("Put your public SSH keys in %1"), BD.filepath(path.."/settings/SSH/authorized_keys")),
}
UIManager:show(info)
end,

View File

@@ -1,3 +1,4 @@
local BD = require("ui/bidi")
local InputContainer = require("ui/widget/container/inputcontainer")
local InfoMessage = require("ui/widget/infomessage")
local UIManager = require("ui/uimanager")
@@ -127,7 +128,7 @@ function CalibreCompanion:addToMainMenu(menu_items)
address = G_reader_settings:readSetting("calibre_wireless_url")
address = string.format("%s:%s", address["address"], address["port"])
end
return T(_("Server address (%1)"), address)
return T(_("Server address (%1)"), BD.ltr(address))
end,
sub_item_table = {
{
@@ -214,7 +215,7 @@ function CalibreCompanion:initCalibreMQ(host, port)
self:onReceiveJSON(data)
if not self.connect_message then
UIManager:show(InfoMessage:new{
text = T(_("Connected to calibre server at %1:%2"), host, port),
text = T(_("Connected to calibre server at %1"), BD.ltr(T("%1:%2", host, port))),
})
self.connect_message = true
if self.failed_connect_callback then
@@ -467,7 +468,7 @@ function CalibreCompanion:sendBook(arg)
outfile:close()
logger.info("complete writing file", filename)
UIManager:show(InfoMessage:new{
text = _("Received file:") .. filename,
text = _("Received file:") .. BD.filepath(filename),
timeout = 1,
})
-- switch to JSON data receiving mode

View File

@@ -1,3 +1,4 @@
local BD = require("ui/bidi")
local Blitbuffer = require("ffi/blitbuffer")
local DataStorage = require("datastorage")
local Device = require("device")
@@ -844,7 +845,7 @@ Do you want to prune the cache of removed books?]]
local orig_moved_offset = info.movable:getMovedOffset()
info:free()
info.text = T(_("Indexing %1 / %2…\n\n%3"), i, nb_files, filename)
info.text = T(_("Indexing %1 / %2…\n\n%3"), i, nb_files, BD.filename(filename))
info:init()
local text_widget = table.remove(info.movable[1][1], 3)
local text_widget_size = text_widget:getSize()

View File

@@ -1,3 +1,4 @@
local BD = require("ui/bidi")
local DocumentRegistry = require("document/documentregistry")
local DocSettings = require("docsettings")
local FileManagerBookInfo = require("apps/filemanager/filemanagerbookinfo")
@@ -95,7 +96,7 @@ function CoverMenu:updateItems(select_number)
self:updatePageInfo(select_number)
if self.show_path then
self.path_text:setText(self.path)
self.path_text:setText(BD.directory(self.path))
end
self.show_parent.dithered = self._has_cover_images
UIManager:setDirty(self.show_parent, function()

View File

@@ -230,7 +230,7 @@ function ListMenuItem:update()
local pad_width = Screen:scaleBySize(10) -- on the left, in between, and on the right
local wleft_width = dimen.w - wright:getWidth() - 3*pad_width
local wleft = TextBoxWidget:new{
text = self.text,
text = BD.directory(self.text),
face = Font:getFace("cfont", _fontSize(20)),
width = wleft_width,
alignment = "left",
@@ -479,17 +479,26 @@ function ListMenuItem:update()
local series_mode = BookInfoManager:getSetting("series_mode")
-- whether to use or not title and authors
-- (We wrap each metadata text with BD.auto() to get for each of them
-- the text direction from the first strong character - which should
-- individually be the best thing, and additionnaly prevent shuffling
-- if concatenated.)
if self.do_filename_only or bookinfo.ignore_meta then
title = filename_without_suffix -- made out above
title = BD.auto(title)
authors = nil
else
title = bookinfo.title and bookinfo.title or filename_without_suffix
title = BD.auto(title)
authors = bookinfo.authors
-- If multiple authors (crengine separates them with \n), we
-- can display them on multiple lines, but limit to 2, and
-- append "et al." to the 2nd if there are more
if authors and authors:find("\n") then
authors = util.splitToArray(authors, "\n")
for i=1, #authors do
authors[i] = BD.auto(authors[i])
end
if #authors > 1 and bookinfo.series and series_mode == "series_in_separate_line" then
authors = { T(_("%1 et al."), authors[1]) }
elseif #authors > 2 then
@@ -499,12 +508,15 @@ function ListMenuItem:update()
-- as we'll fit 3 lines instead of 2, we can avoid some loops by starting from a lower font size
fontsize_title = _fontSize(17)
fontsize_authors = _fontSize(15)
elseif authors then
authors = BD.auto(authors)
end
end
-- add Series metadata if requested
if bookinfo.series then
-- Shorten calibre series decimal number (#4.0 => #4)
bookinfo.series = bookinfo.series:gsub("(#%d+)%.0$", "%1")
bookinfo.series = BD.auto(bookinfo.series)
if series_mode == "append_series_to_title" then
if title then
title = title .. " - " .. bookinfo.series
@@ -645,6 +657,7 @@ function ListMenuItem:update()
if self.file_deleted then -- unless file was deleted (can happen with History)
hint = " " .. _("(deleted)")
end
local text = BD.filename(self.text)
local text_widget
local fontsize_no_bookinfo = _fontSize(18)
repeat
@@ -652,7 +665,7 @@ function ListMenuItem:update()
text_widget:free()
end
text_widget = TextBoxWidget:new{
text = self.text .. hint,
text = text .. hint,
face = Font:getFace("cfont", fontsize_no_bookinfo),
width = dimen.w - 2 * Screen:scaleBySize(10),
alignment = "left",

View File

@@ -99,10 +99,15 @@ local FakeCover = FrameContainer:new{
padding = 0,
bordersize = Size.line.thin,
dim = nil,
-- Provided filename, title and authors should not be BD wrapped
filename = nil,
file_deleted = nil,
title = nil,
authors = nil,
-- The *_add should be provided BD wrapped if needed
filename_add = nil,
title_add = nil,
authors_add = nil,
-- these font sizes will be scaleBySize'd by Font:getFace()
authors_font_max = 20,
authors_font_min = 6,
@@ -123,14 +128,25 @@ function FakeCover:init()
local title = self.title
local filename = self.filename
-- (some engines may have already given filename (without extension) as title)
local bd_wrap_title_as_filename = false
if not title then -- use filename as title (big and centered)
title = filename
filename = nil
if not self.title_add and self.filename_add then
-- filename_add ("…" or "(deleted)") always comes without any title_add
self.title_add = self.filename_add
self.filename_add = nil
end
bd_wrap_title_as_filename = true
end
if filename then
filename = BD.filename(filename)
end
-- If no authors, and title is filename without extension, it was
-- probably made by an engine, and we can consider it a filename, and
-- act according to common usage in naming files.
if not authors and title and self.filename:sub(1,title:len()) == title then
if not authors and title and self.filename and self.filename:sub(1,title:len()) == title then
bd_wrap_title_as_filename = true
-- Replace a hyphen surrounded by spaces (which most probably was
-- used to separate Authors/Title/Serie/Year/Categorie in the
-- filename with a \n
@@ -150,16 +166,35 @@ function FakeCover:init()
-- together on a last line: so, move the zero-width-space
-- before it.
title = title:gsub("%.\xE2\x80\x8B(%w%w?%w?%w?%w?)$", "\xE2\x80\x8B.%1")
-- These substitutions will hopefully have no impact with the following BD wrapping
end
if title then
title = bd_wrap_title_as_filename and BD.filename(title) or BD.auto(title)
end
-- If multiple authors (crengine separates them with \n), we
-- can display them on multiple lines, but limit to 3, and
-- append "et al." on a 4th line if there are more
if authors and authors:find("\n") then
authors = util.splitToArray(authors, "\n")
for i=1, #authors do
authors[i] = BD.auto(authors[i])
end
if #authors > 3 then
authors = { authors[1], authors[2], T(_("%1 et al."), authors[3]) }
end
authors = table.concat(authors, "\n")
elseif authors then
authors = BD.auto(authors)
end
-- Add any _add, which must be already BD wrapped if needed
if self.filename_add then
filename = (filename and filename or "") .. self.filename_add
end
if self.title_add then
title = (title and title or "") .. self.title_add
end
if self.authors_add then
authors = (authors and authors or "") .. self.authors_add
end
-- We build the VerticalGroup widget with decreasing font sizes till
@@ -207,7 +242,7 @@ function FakeCover:init()
end
if filename then
filename_wg = TextBoxWidget:new{
text = BD.filename(filename),
text = filename,
face = Font:getFace("cfont", math.max(self.filename_font_max - sizedec, self.filename_font_min)),
width = text_width,
alignment = "center",
@@ -429,6 +464,7 @@ function MosaicMenuItem:update()
if text:match('/$') then -- remove /, more readable
text = text:sub(1, -2)
end
text = BD.directory(text)
local nbitems = TextBoxWidget:new{
text = self.mandatory,
face = Font:getFace("infont", 15),
@@ -553,25 +589,27 @@ function MosaicMenuItem:update()
else
-- add Series metadata if requested
local series_mode = BookInfoManager:getSetting("series_mode")
local title_add, authors_add
if bookinfo.series then
-- Shorten calibre series decimal number (#4.0 => #4)
bookinfo.series = bookinfo.series:gsub("(#%d+)%.0$", "%1")
bookinfo.series = BD.auto(bookinfo.series)
if series_mode == "append_series_to_title" then
if bookinfo.title then
bookinfo.title = bookinfo.title .. " - " .. bookinfo.series
title_add = " - " .. bookinfo.series
else
bookinfo.title = bookinfo.series
title_add = bookinfo.series
end
end
if not bookinfo.authors then
if series_mode == "append_series_to_authors" or series_mode == "series_in_separate_line" then
bookinfo.authors = bookinfo.series
authors_add = bookinfo.series
end
else
if series_mode == "append_series_to_authors" then
bookinfo.authors = bookinfo.authors .. " - " .. bookinfo.series
authors_add = " - " .. bookinfo.series
elseif series_mode == "series_in_separate_line" then
bookinfo.authors = bookinfo.authors .. "\n \n" .. bookinfo.series
authors_add = "\n \n" .. bookinfo.series
end
end
end
@@ -585,6 +623,8 @@ function MosaicMenuItem:update()
filename = self.text,
title = not bookinfo.ignore_meta and bookinfo.title,
authors = not bookinfo.ignore_meta and bookinfo.authors,
title_add = not bookinfo.ignore_meta and title_add,
authors_add = not bookinfo.ignore_meta and authors_add,
file_deleted = self.file_deleted,
}
}
@@ -622,7 +662,8 @@ function MosaicMenuItem:update()
width = dimen.w,
height = dimen.h,
bordersize = border_size,
filename = self.text .. "\n" .. hint,
filename = self.text,
filename_add = "\n" .. hint,
initial_sizedec = 4, -- start with a smaller font when filenames only
file_deleted = self.file_deleted,
}

View File

@@ -1,3 +1,4 @@
local BD = require("ui/bidi")
local DataStorage = require("datastorage")
local FFIUtil = require("ffi/util")
local InputDialog = require("ui/widget/inputdialog")
@@ -48,7 +49,7 @@ function DocSettingTweak:editDirectoryDefaults()
directory_defaults_file:close()
local config_editor
config_editor = InputDialog:new{
title = T(_("Directory Defaults: %1"),directory_defaults_path),
title = T(_("Directory Defaults: %1"), BD.filepath(directory_defaults_path)),
input = defaults,
input_type = "string",
para_direction_rtl = false, -- force LTR

View File

@@ -1,3 +1,4 @@
local BD = require("ui/bidi")
local InputContainer = require("ui/widget/container/inputcontainer")
local LoginDialog = require("ui/widget/logindialog")
local InfoMessage = require("ui/widget/infomessage")
@@ -242,7 +243,7 @@ For Windows: netsh interface portproxy add listeningaddress:0.0.0.0 listeningpor
For Linux: $socat tcp-listen:41185,reuseaddr,fork tcp:localhost:41184
For more information, please visit https://github.com/koreader/koreader/wiki/Evernote-export.]])
,DataStorage:getDataDir())
, BD.dirpath(DataStorage:getDataDir()))
})
end
}
@@ -595,7 +596,7 @@ function EvernoteExporter:exportClippings(clippings)
end
end
if (self.html_export or self.txt_export) and export_count > 0 then
msg = msg .. T(_("\nNotes can be found in %1/."), realpath(self.clipping_dir))
msg = msg .. T(_("\nNotes can be found in %1/."), BD.dirpath(realpath(self.clipping_dir)))
end
UIManager:show(InfoMessage:new{ text = msg })
end

View File

@@ -1,3 +1,4 @@
local BD = require("ui/bidi")
local DataStorage = require("datastorage")
--local DownloadBackend = require("internaldownloadbackend")
--local DownloadBackend = require("luahttpdownloadbackend")
@@ -125,7 +126,7 @@ function NewsDownloader:addToMainMenu(menu_items)
callback = function()
UIManager:show(InfoMessage:new{
text = T(_("News downloader retrieves RSS and Atom news entries and stores them to:\n%1\n\nEach entry is a separate html file, that can be browsed by KOReader file manager.\nItems download limit can be configured in Settings."),
news_download_dir_path)
BD.dirpath(news_download_dir_path))
})
end,
},
@@ -183,7 +184,7 @@ function NewsDownloader:loadConfigAndProcessFeeds()
local download_full_article = feed.download_full_article == nil or feed.download_full_article
local include_images = feed.include_images
if url and limit then
local feed_message = T(_("Processing %1/%2:\n%3"), idx, total_feed_entries, url)
local feed_message = T(_("Processing %1/%2:\n%3"), idx, total_feed_entries, BD.url(url))
UI:info(feed_message)
NewsDownloader:processFeedSource(url, tonumber(limit), unsupported_feeds_urls, download_full_article, include_images, feed_message)
else
@@ -198,7 +199,7 @@ function NewsDownloader:loadConfigAndProcessFeeds()
for k,url in pairs(unsupported_feeds_urls) do
unsupported_urls = unsupported_urls .. url
if k ~= #unsupported_feeds_urls then
unsupported_urls = unsupported_urls .. ", "
unsupported_urls = BD.url(unsupported_urls) .. ", "
end
end
UI:info(T(_("Downloading news finished. Could not process some feeds. Unsupported format in: %1"), unsupported_urls))
@@ -408,7 +409,7 @@ function NewsDownloader:changeFeedConfig()
feed_config_file:close()
local config_editor
config_editor = InputDialog:new{
title = T(_("Config: %1"),feed_config_path),
title = T(_("Config: %1"), BD.filepath(feed_config_path)),
input = config,
input_type = "string",
para_direction_rtl = false, -- force LTR

View File

@@ -1,3 +1,4 @@
local BD = require("ui/bidi")
local DataStorage = require("datastorage")
local DocSettings = require("frontend/docsettings")
local ReadHistory = require("readhistory")
@@ -103,7 +104,7 @@ function Send2Ebook:addToMainMenu(menu_items)
keep_menu_open = true,
callback = function()
UIManager:show(InfoMessage:new{
text = T(_('Send2Ebook lets you send articles found on PC/Android phone to your Ebook reader (using ftp server).\n\nMore details: https://github.com/mwoz123/send2ebook\n\nDownloads to local folder: %1'), download_dir_path)
text = T(_('Send2Ebook lets you send articles found on PC/Android phone to your Ebook reader (using ftp server).\n\nMore details: https://github.com/mwoz123/send2ebook\n\nDownloads to local folder: %1'), BD.dirpath(download_dir_path))
})
end,
},
@@ -143,7 +144,7 @@ function Send2Ebook:process()
local ftp_files_table = FtpApi:listFolder(connection_url .. ftp_config.folder, ftp_config.folder) --args looks strange but otherwise resonse with invalid paths
if not ftp_files_table then
info = InfoMessage:new{ text = T(_("Could not get file list for server: %1, user: %2, folder: %3"), ftp_config.address, ftp_config.username, ftp_config.folder) }
info = InfoMessage:new{ text = T(_("Could not get file list for server: %1, user: %2, folder: %3"), BD.ltr(ftp_config.address), ftp_config.username, BD.dirpath(ftp_config.folder)) }
else
local total_entries = table.getn(ftp_files_table)
logger.dbg("Send2Ebook: total_entries ", total_entries)

View File

@@ -1,3 +1,4 @@
local BD = require("ui/bidi")
local BookStatusWidget = require("ui/widget/bookstatuswidget")
local ConfirmBox = require("ui/widget/confirmbox")
local DataStorage = require("datastorage")
@@ -235,7 +236,7 @@ Cannot open database in %1.
The database may have been moved or deleted.
Do you want to create an empty database?
]]),
db_location),
BD.filepath(db_location)),
cancel_text = _("Close"),
cancel_callback = function()
return

View File

@@ -171,7 +171,7 @@ Enable this if you are mostly editing code, HTML, CSS…]]),
local file_path = self.history[i]
local directory, filename = util.splitFilePathName(file_path) -- luacheck: no unused
table.insert(sub_item_table, {
text = T("%1. %2", i, filename),
text = T("%1. %2", i, BD.filename(filename)),
keep_menu_open = true,
callback = function(touchmenu_instance)
self:setupWhenDoneFunc(touchmenu_instance)
@@ -186,10 +186,10 @@ Enable this if you are mostly editing code, HTML, CSS…]]),
local filesize = util.getFormattedSize(attr.size)
local lastmod = os.date("%Y-%m-%d %H:%M", attr.modification)
text = T(_("File path:\n%1\n\nFile size: %2 bytes\nLast modified: %3\n\nRemove this file from text editor history?"),
file_path, filesize, lastmod)
BD.filepath(file_path), filesize, lastmod)
else
text = T(_("File path:\n%1\n\nThis file does not exist anymore.\n\nRemove it from text editor history?"),
file_path)
BD.filepath(file_path))
end
UIManager:show(ConfirmBox:new{
text = text,
@@ -332,7 +332,7 @@ function TextEditor:checkEditFile(file_path, from_history, possibly_new_file)
local attr = lfs.attributes(file_path)
if not possibly_new_file and not attr then
UIManager:show(ConfirmBox:new{
text = T(_("This file does not exist anymore:\n\n%1\n\nDo you want to create it and start editing it?"), file_path),
text = T(_("This file does not exist anymore:\n\n%1\n\nDo you want to create it and start editing it?"), BD.filepath(file_path)),
ok_text = _("Yes"),
cancel_text = _("No"),
ok_callback = function()
@@ -350,7 +350,7 @@ function TextEditor:checkEditFile(file_path, from_history, possibly_new_file)
if attr then -- File exists
if attr.mode ~= "file" then
UIManager:show(InfoMessage:new{
text = T(_("This file is not a regular file:\n\n%1"), file_path)
text = T(_("This file is not a regular file:\n\n%1"), BD.filepath(file_path))
})
return
end
@@ -368,7 +368,7 @@ function TextEditor:checkEditFile(file_path, from_history, possibly_new_file)
if not from_history and attr.size > self.min_file_size_warn then
UIManager:show(ConfirmBox:new{
text = T(_("This file is %2:\n\n%1\n\nAre you sure you want to open it?\n\nOpening big files may take some time."),
file_path, util.getFriendlySize(attr.size)),
BD.filepath(file_path), util.getFriendlySize(attr.size)),
ok_text = _("Yes"),
cancel_text = _("No"),
ok_callback = function()
@@ -389,7 +389,7 @@ function TextEditor:checkEditFile(file_path, from_history, possibly_new_file)
self:editFile(file_path)
else
UIManager:show(InfoMessage:new{
text = T(_("This file can not be created:\n\n%1\n\nReason: %2"), file_path, err)
text = T(_("This file can not be created:\n\n%1\n\nReason: %2"), BD.filepath(file_path), err)
})
return
end
@@ -526,7 +526,7 @@ Lua syntax check failed:
KOReader may crash if this is saved.
Do you really want to save to this file?
%2]]), parse_error, file_path), _("Do not save"), _("Save anyway"))
%2]]), parse_error, BD.filepath(file_path)), _("Do not save"), _("Save anyway"))
-- we'll get the safer "Do not save" on tap outside
if save_anyway then
local ok, err = self:saveFileContent(file_path, content)
@@ -543,7 +543,7 @@ Do you really want to save to this file?
Text content is empty.
Do you want to keep this file as empty, or do you prefer to delete it?
%1]]), file_path), _("Keep empty file"), _("Delete file"))
%1]]), BD.filepath(file_path)), _("Keep empty file"), _("Delete file"))
-- we'll get the safer "Keep empty file" on tap outside
if delete_file then
local ok, err = self:deleteFile(file_path)

View File

@@ -2,6 +2,7 @@
@module koplugin.wallabag
]]
local BD = require("ui/bidi")
local DataStorage = require("datastorage")
local DocSettings = require("docsettings")
local Event = require("ui/event")
@@ -163,7 +164,7 @@ function Wallabag:addToMainMenu(menu_items)
else
path = filemanagerutil.abbreviate(self.directory)
end
return T(_("Set download directory (%1)"), path)
return T(_("Set download directory (%1)"), BD.dirpath(path))
end,
keep_menu_open = true,
callback = function(touchmenu_instance)
@@ -261,7 +262,7 @@ The 'Synchronize remotely deleted files' option will remove local files that no
More details: https://wallabag.org
Downloads to directory: %1]]), filemanagerutil.abbreviate(self.directory))
Downloads to directory: %1]]), BD.dirpath(filemanagerutil.abbreviate(self.directory)))
})
end,
},
@@ -824,7 +825,7 @@ Enter the details of your Wallabag server and account.
Client ID and client secret are long strings so you might prefer to save the empty settings and edit the config file directly in your installation directory:
%1/wallabag.lua
Restart KOReader after editing the config file.]]), DataStorage:getSettingsDir())
Restart KOReader after editing the config file.]]), BD.dirpath(DataStorage:getSettingsDir()))
self.settings_dialog = MultiInputDialog:new {
title = _("Wallabag settings"),
@@ -994,11 +995,11 @@ function Wallabag:onAddWallabagArticle(article_url)
local wallabag_result = self:addArticle(article_url)
if wallabag_result then
UIManager:show(InfoMessage:new{
text = T(_("Article added to Wallabag:\n%1"), article_url),
text = T(_("Article added to Wallabag:\n%1"), BD.url(article_url)),
})
else
UIManager:show(InfoMessage:new{
text = T(_("Error adding link to Wallabag:\n%1"), article_url),
text = T(_("Error adding link to Wallabag:\n%1"), BD.url(article_url)),
})
end