Internationalization
First of all great job on developing this plugin.
Feauture
I will like for their to either be a way for me to change the name of titles like references, code actions, etc. so I can add my translations.
Or if this plugin had translations in it it will be great.
Reason
I am currently working on adding internationalization in my own setup , most so I can practice Japanese and not bother other people who might want it in english.
My Approach
I am getting the texts from plugins to exchange them with text that will be translated depending the vim language set.
To get the language set I am using a variable build in which Is
vim.v.lang
Once I get the language it changes the values depending on the languages available and translations. For me I used a function that when text goes inside It tries to look for an available translation. if it doesnt have then it returns default string.
-- Current Language
local lang = string.sub(vim.v.lang, 1, 2)
-- Translations
local file = io.open(vim.fn.stdpath('config').."/lua/andrem222/lang/"..lang..".json", "rb")
-- Decoded translations
local jsonFile
-- Check if translation available to decode
if file then
jsonFile = vim.json.decode(file:read("*a"))
file:close()
end
--- This function returns the translation if available of text in the parameter
--- @param description string Description to translate
--- @return any
function Msgstr(description)
if not jsonFile then return description end
if not jsonFile[description] or jsonFile[description]["msgstr"] == '' then return description end
return jsonFile[description]["msgstr"]
end
NVim Approach
They instead used a cmakelist to get translations and exchange them using gettext.
This one might be better but I am still not knowledgable about that and I'm trying to learn it now to see how to make it work.
https://github.com/neovim/neovim/blob/master/src/nvim/po/CMakeLists.txt
got new way of handling
To load translation I developed this:
-- Current Language
local lang = string.sub(vim.v.lang, 1, 2)
local po_path = vim.fn.stdpath('config') .. "/lua/andrem222/po/" .. lang .. ".po"
-- Translation table
local translatedTable = {}
-- Basic PO file parser
local function parse_po(path)
local file = io.open(path, "r")
if not file then return {} end
local translations = {}
local mode = nil
local current_msgid = {}
local current_msgstr = {}
local function unescape(str)
return str:gsub('\\"', '"'):gsub("\\n", "\n")
end
for line in file:lines() do
if line:match('^msgid%s+"') then
mode = "msgid"
current_msgid = { line:match('^msgid%s+"(.*)"') }
current_msgstr = {}
elseif line:match('^msgstr%s+"') then
mode = "msgstr"
current_msgstr = { line:match('^msgstr%s+"(.*)"') }
elseif line:match('^"') then
local str = line:match('^"(.*)"')
if mode == "msgid" then
table.insert(current_msgid, str)
elseif mode == "msgstr" then
table.insert(current_msgstr, str)
end
elseif line == "" then
-- End of one entry
if #current_msgid > 0 and #current_msgstr > 0 then
local msgid_text = unescape(table.concat(current_msgid))
local msgstr_text = unescape(table.concat(current_msgstr))
translations[msgid_text] = msgstr_text
end
mode = nil
current_msgid = {}
current_msgstr = {}
end
end
-- Handle the last entry if file doesn't end with blank line
if #current_msgid > 0 and #current_msgstr > 0 then
local msgid_text = unescape(table.concat(current_msgid))
local msgstr_text = unescape(table.concat(current_msgstr))
translations[msgid_text] = msgstr_text
end
file:close()
return translations
end
-- Load translations
translatedTable = parse_po(po_path)
--- This function returns the translation if available and not empty
--- @param description string Description to translate
--- @param values string[]? Optional list of variables
--- @return string
function Msgstr(description, values)
local translated = translatedTable[description]
if not translated or translated == "" then
translated = description
end
if values then
local unpack = table.unpack or unpack
local ok, formatted = pcall(string.format, translated, unpack(values))
if ok then
translated = formatted
end
end
return translated
end
But to make all the PO's I made this cmake code
cmake_minimum_required(VERSION 3.31.6)
project(Internationalization)
set(NVIM_DIR "${CMAKE_SOURCE_DIR}/../../..")
set(TRANSLATION_DIR "${CMAKE_SOURCE_DIR}")
set(TEMPLATE_FILE "${TRANSLATION_DIR}/template.pot")
set(TRANSLATION_SCRIPT_TYPE "po")
set(LANGUAGES en ja)
file(MAKE_DIRECTORY ${TRANSLATION_DIR})
file(GLOB_RECURSE LUA_FILES "${NVIM_DIR}/*.lua")
add_custom_command(
OUTPUT ${TEMPLATE_FILE}
DEPENDS ${LUA_FILES}
COMMAND bash -c "mkdir -p '${TRANSLATION_DIR}' && cd '${NVIM_DIR}' && xgettext -L Lua --keyword=Msgstr --directory=. -o '${TEMPLATE_FILE}' $(find . -name '*.lua')"
COMMENT "Generating template.pot from Lua source"
VERBATIM
)
foreach(LANGUAGE ${LANGUAGES})
set(PO_FILE "${TRANSLATION_DIR}/${LANGUAGE}.${TRANSLATION_SCRIPT_TYPE}")
add_custom_command(
OUTPUT ${PO_FILE}
DEPENDS ${TEMPLATE_FILE}
COMMAND bash -c "if [ -f '${PO_FILE}' ]; then msgmerge --update --backup=off '${PO_FILE}' '${TEMPLATE_FILE}'; else cp '${TEMPLATE_FILE}' '${PO_FILE}' && sed -i '' -e 's/charset=CHARSET/charset=UTF-8/' -e 's/^\\\"Language:.*\\\"$/\\\"Language: ${LANGUAGE}\\\\n\\\"/' '${PO_FILE}'; fi"
COMMENT "Merging or creating ${LANGUAGE}.po"
VERBATIM
)
add_custom_target(${LANGUAGE}_po ALL DEPENDS ${PO_FILE})
endforeach()
add_custom_target(update_translations ALL
DEPENDS ${TEMPLATE_FILE}
)
If you are interested I will like to implement this here. Not everyone who uses neovim is proficient in english, and I want to make it possible for everyone to enjoy the popular plugins in their own language, including this one. I know english and spanish, and I know some level of japanese to translate a good amount of stuff.
This is a preview of it working. the lualine and telescope I managed to grab their texts to replace them with what I wanted.
For example the telescope one looks like this.
live_grep = {
prompt_title = Msgstr("Live Grep"),
results_title = Msgstr("Results"),
preview_title = Msgstr("Grep Preview")
},
it made two PO
which the japanese one looks as follows,
#: lua/andrem222/plugins/tools.lua:170 lua/andrem222/plugins/tools.lua:176
#: lua/andrem222/plugins/tools.lua:182 lua/andrem222/plugins/tools.lua:188
#: lua/andrem222/plugins/tools.lua:194 lua/andrem222/plugins/tools.lua:199
#: lua/andrem222/plugins/tools.lua:205
msgid "Results"
msgstr "結果"
#: lua/andrem222/plugins/tools.lua:171 lua/andrem222/plugins/tools.lua:177
#: lua/andrem222/plugins/tools.lua:183 lua/andrem222/plugins/tools.lua:200
#: lua/andrem222/plugins/tools.lua:206
msgid "Grep Preview"
msgstr "グレッププレビュー"
#: lua/andrem222/plugins/tools.lua:175
msgid "Buffers"
msgstr "ブッファ"
#: lua/andrem222/plugins/tools.lua:181
msgid "Workspace Diagnostics"
msgstr "ワークスペース診断"
#: lua/andrem222/plugins/tools.lua:187 lua/andrem222/plugins/ui.lua:307
msgid "Help"
msgstr "ヘルプ"
#: lua/andrem222/plugins/tools.lua:189
msgid "Help Preview"
msgstr "ヘルププレビュー"
#: lua/andrem222/plugins/tools.lua:193 lua/andrem222/plugins/ui.lua:180
msgid "Keymaps"
msgstr "キーマップ"
#: lua/andrem222/plugins/tools.lua:198
msgid "Live Grep"
msgstr "ライブGrep"