⚠ NOTICE
The game has been released in Early Access on 26th of April, 2024.
Please keep in mind that any information provided on this Wiki may be incomplete or subject to change as the game progresses.
Module:I18n: Difference between revisions
From Manor Lords Official Wiki
Created page with "--- I18n library for message storage in Lua datastore collections. -- The module is designed to enable message separation from modules & -- templates. It has support for handling language fallbacks. This -- module is a Lua port of I18n-js and i18n modules that can be loaded -- by it are editable through I18nEdit. -- -- @module i18n -- @version 1.4.0 -- @require Module:Entrypoint -- @require Module:Fallbacklist -- @author..." Tag: Recreated |
No edit summary |
||
| Line 433: | Line 433: | ||
-- @error[322] {string} 'no source supplied to i18n.loadMessages' | -- @error[322] {string} 'no source supplied to i18n.loadMessages' | ||
-- @return {table} I18n datastore instance. | -- @return {table} I18n datastore instance. | ||
-- @usage require(' | -- @usage require('Module:I18n').loadMessages('1', '2') | ||
function i18n.loadMessages(...) | function i18n.loadMessages(...) | ||
local ds | local ds | ||
| Line 454: | Line 454: | ||
source = mw.ustring.find(source, ':') | source = mw.ustring.find(source, ':') | ||
and source | and source | ||
or ' | or 'Module:' .. source .. '/i18n' | ||
ds._messages[i] = mw.loadData(source) | ds._messages[i] = mw.loadData(source) | ||
end | end | ||
Latest revision as of 00:13, 24 December 2025
Documentation for this module may be created at Module:I18n/doc
--- I18n library for message storage in Lua datastore collections.
-- The module is designed to enable message separation from modules &
-- templates. It has support for handling language fallbacks. This
-- module is a Lua port of [[I18n-js]] and i18n modules that can be loaded
-- by it are editable through [[I18nEdit]].
--
-- @module i18n
-- @version 1.4.0
-- @require Module:Entrypoint
-- @require Module:Fallbacklist
-- @author [[User:KockaAdmiralac|KockaAdmiralac]]
-- @author [[User:Speedit|Speedit]]
-- @attribution [[User:Cqm|Cqm]]
-- @release stable
-- @see [[I18n|I18n guide]]
-- @see [[I18n-js]]
-- @see [[I18nEdit]]
-- <nowiki>
local i18n, _i18n = {}, {}
-- Module variables & dependencies.
local title = mw.title.getCurrentTitle()
local fallbacks = require('Module:Fallbacklist')
local entrypoint = require('Module:Entrypoint')
local uselang
--- Argument substitution as $n where n > 0.
-- @function _i18n.handleArgs
-- @param {string} msg Message to substitute arguments into.
-- @param {table} args Arguments table to substitute.
-- @return {string} Resulting message.
-- @local
function _i18n.handleArgs(msg, args)
for i, a in ipairs(args) do
msg = (string.gsub(msg, '%$' .. tostring(i), tostring(a)))
end
return msg
end
--- Checks whether a language code is valid.
-- @function _i18n.isValidCode
-- @param {string} code Language code to check.
-- @return {boolean} Whether the language code is valid.
-- @local
function _i18n.isValidCode(code)
return type(code) == 'string' and #mw.language.fetchLanguageName(code) ~= 0
end
--- Checks whether a message contains unprocessed wikitext.
-- Used to optimise message getter by not preprocessing pure text.
-- @function _i18n.isWikitext
-- @param {string} msg Message to check.
-- @return {boolean} Whether the message contains wikitext.
function _i18n.isWikitext(msg)
return
type(msg) == 'string' and
(
msg:find('%-%-%-%-') or
msg:find('%f[^\n%z][;:*#] ') or
msg:find('%f[^\n%z]==* *[^\n|]+ =*=%f[\n]') or
msg:find('%b<>') or msg:find('\'\'') or
msg:find('%[%b[]%]') or msg:find('{%b{}}')
)
end
--- I18n datastore class.
-- This is used to control language translation and access to individual
-- messages. The datastore instance provides language and message
-- getter-setter methods, which can be used to internationalize Lua modules.
-- The language methods (any ending in `Lang`) are all **chainable**.
-- @type Data
local Data = {}
Data.__index = Data
--- Gets data message utility.
-- This method returns localized messages from the datastore corresponding
-- to a `key`. These messages may have `$n` parameters, which can be
-- replaced by optional argument strings supplied by the `msg` call.
--
-- This function supports [[Lua reference manual#named_arguments|named
-- arguments]]. The named argument syntax is more versatile despite its
-- verbosity; it can be used to select message language & source(s).
-- @function Data:msg
-- @usage
--
-- ds:msg{
-- key = 'message-name',
-- lang = '',
-- args = {...},
-- sources = {}
-- }
--
-- @usage
--
-- ds:msg('message-name', ...)
--
-- @param {string|table} opts Message configuration or key.
-- @param[opt] {string} opts.key Message key to return from the
-- datastore.
-- @param[opt] {table} opts.args Arguments to substitute into the
-- message (`$n`).
-- @param[opt] {table} opts.sources Source names to limit to (see
-- `Data:fromSources`).
-- @param[opt] {table} opts.lang Temporary language to use (see
-- `Data:inLang`).
-- @param[opt] {string} ... Arguments to substitute into the message
-- (`$n`).
-- @error[115] {string} 'missing arguments in Data:msg'
-- @return {string} Localised datastore message or `'<key>'`.
function Data:msg(opts, ...)
local frame = mw.getCurrentFrame()
-- Argument normalization.
if not self or not opts then
error('missing arguments in Data:msg')
end
local key = type(opts) == 'table' and opts.key or opts
local args = opts.args or {...}
-- Configuration parameters.
if opts.sources then
self:fromSources(unpack(opts.sources))
end
if opts.lang then
self:inLang(opts.lang)
end
-- Source handling.
local source_n = self.tempSources or self._sources
local source_i = {}
for n, i in pairs(source_n) do
source_i[i] = n
end
self.tempSources = nil
-- Language handling.
local lang = self.tempLang or self.defaultLang
self.tempLang = nil
-- Language converter handling
-- Types: 'languageConverterMarkup', 'pageSourceCodeLanguage',
-- 'siteLanguage', 'userLanguage'
-- Note: Currently cannot handle page view language without using
-- 'languageConverterMarkup', should use 'userLanguage' in most cases if
-- not able to use 'languageConverterMarkup'.
local languageConversionType = self.tempLanguageConversionType or
'languageConverterMarkup'
self.tempLangConversionType = 'languageConverterMarkup'
-- Message fetching.
local msg
for i, messages in ipairs(self._messages) do
-- Message data.
local msg = nil
-- Language converter support
if lang == 'zh' and
languageConversionType ~= 'pageSourceCodeLanguage' and
languageConversionType ~= 'siteLanguage' and
-- Lua error: too many expensive function calls.
-- (
-- mw.title.getCurrentTitle().pageLang:toBcp47Code() == 'zh' or
-- (
-- -- Module namespace
-- mw.title.getCurrentTitle():inNamespace( 828 ) and
mw.language.getContentLanguage():toBcp47Code() == 'zh'
-- )
-- )
then
local userLang = ( i18n.getLang() or self.defaultLang )
local zh_hani = (messages['zh'] or {})[key]
local zh_hans = (messages['zh-hans'] or {})[key]
local zh_hant = (messages['zh-hant'] or {})[key]
local zh_hant_hk = (messages['zh-hk'] or {})[key]
if
languageConversionType == 'userLanguage'
then
if userLang == 'zh' then
msg = zh_hani or zh_hans or zh_hant or zh_hant_hk
elseif
userLang == 'zh-hans' or
userLang == 'zh-cn' or
userLang == 'zh-my' or
userLang == 'zh-sg'
then
msg = zh_hans or zh_hani or zh_hant or zh_hant_hk
elseif
userLang == 'zh-hant' or
userLang == 'zh-tw'
then
msg = zh_hant or zh_hant_hk or zh_hani or zh_hans
elseif
userLang == 'zh-hk' or
userLang == 'zh-mo'
then
msg = zh_hant_hk or zh_hant or zh_hani or zh_hans
end
elseif zh_hans ~= nil then
if zh_hant ~= nil then
msg = '-{' ..
'zh-hans:' .. zh_hans .. ';' ..
' zh-hant:' .. zh_hant .. ';'
if zh_hant_hk ~= nil then
msg = msg ..
' zh-hk:' .. zh_hant_hk .. ';'
end
msg = msg .. '}-'
elseif zh_hant_hk ~= nil then
msg = '-{' ..
'zh-hans:' .. zh_hans .. ';' ..
' zh-hk:' .. zh_hant_hk .. ';' ..
'}-'
else
msg = zh_hans
end
elseif zh_hant ~= nil then
if zh_hant_hk ~= nil then
msg = '-{' ..
'zh-hant:' .. zh_hant .. ';' ..
' zh-hk:' .. zh_hant_hk .. ';' ..
'}-'
else
msg = zh_hant
end
elseif zh_hant_hk ~= nil then
msg = zh_hant_hk
end
end
if msg == nil then
msg = (messages[lang] or {})[key]
end
-- Fallback support (experimental).
for _, l in ipairs((fallbacks[lang] or {})) do
if msg == nil then
msg = (messages[l] or {})[key]
end
end
-- Internal fallback to 'en'.
msg = msg ~= nil and msg or messages.en[key]
-- Handling argument substitution from Lua.
if msg and source_i[i] and #args > 0 then
msg = _i18n.handleArgs(msg, args)
end
if msg and source_i[i] and lang ~= 'qqx' then
return frame and _i18n.isWikitext(msg)
and frame:preprocess(mw.text.trim(msg))
or mw.text.trim(msg)
end
end
return mw.text.nowiki('<' .. key .. '>')
end
--- Gets localised template parameter from a datastore.
-- This method, given a table of arguments, tries to find a parameter's
-- localized name in the datastore and returns its value, or nil if
-- not present.
--
-- This method always uses the wiki's content language.
-- @function Data:parameter
-- @param {string} parameter Parameter's key in the datastore
-- @param {table} args Arguments to find the parameter in
-- @error[176] {string} 'missing arguments in Data:parameter'
-- @return {string|nil} Parameter's value or nil if not present
function Data:parameter(key, args)
-- Argument normalization.
if not self or not key or not args then
error('missing arguments in Data:parameter')
end
local contentLang = mw.language.getContentLanguage():getCode()
-- Message fetching.
for i, messages in ipairs(self._messages) do
local msg = (messages[contentLang] or {})[key]
if msg ~= nil and args[msg] ~= nil then
return args[msg]
end
for _, l in ipairs((fallbacks[contentLang] or {})) do
if msg == nil or args[msg] == nil then
-- Check next fallback.
msg = (messages[l] or {})[key]
else
-- A localized message was found.
return args[msg]
end
end
-- Fallback to English.
msg = messages.en[key]
if msg ~= nil and args[msg] ~= nil then
return args[msg]
end
end
end
--- Sets temporary source to a specificed subset of datastores.
-- By default, messages are fetched from the datastore in the same
-- order of priority as `i18n.loadMessages`.
-- @function Data:fromSource
-- @param {string} ... Source name(s) to use.
-- @return {Data} Datastore instance.
function Data:fromSource(...)
local c = select('#', ...)
if c ~= 0 then
self.tempSources = {}
for i = 1, c do
local n = select(i, ...)
if type(n) == 'string' and type(self._sources[n]) == 'number' then
self.tempSources[n] = self._sources[n]
end
end
end
return self
end
--- Gets data default language.
-- @function Data:getLang
-- @return {string} Default language to serve datastore messages in.
function Data:getLang()
return self.defaultLang
end
--- Sets data language to `wgUserLanguage`.
-- @function Data:useUserLang
-- @return {Data} Datastore instance.
-- @note Scribunto only registers `wgUserLanguage` when an
-- invocation is at the top of the call stack.
function Data:useUserLang()
self.defaultLang = i18n.getLang() or self.defaultLang
return self
end
--- Sets data language to `wgContentLanguage`.
-- @function Data:useContentLang
-- @return {Data} Datastore instance.
function Data:useContentLang()
self.defaultLang = i18n.getContentLang()
return self
end
--- Sets data language to specificed language.
-- @function Data:useLang
-- @param {string} code Language code to use.
-- @return {Data} Datastore instance.
function Data:useLang(code)
self.defaultLang = _i18n.isValidCode(code)
and code
or self.defaultLang
return self
end
--- Sets temporary data language to `wgUserLanguage`.
-- The data language reverts to the default language in the next
-- @{Data:msg} call.
-- @function Data:inUserLang
-- @return {Data} Datastore instance.
function Data:inUserLang()
self.tempLang = i18n.getLang() or self.tempLang
return self
end
--- Sets temporary data language to `wgContentLanguage`.
-- Only affects the next @{Data:msg} call.
-- @function Data:inContentLang
-- @return {Data} Datastore instance.
function Data:inContentLang()
self.tempLang = i18n.getContentLang()
return self
end
--- Sets temporary data language to a specificed language.
-- Only affects the next @{Data:msg} call.
-- @function Data:inLang
-- @param {string} code Language code to use.
-- @return {Data} Datastore instance.
function Data:inLang(code)
self.tempLang = _i18n.isValidCode(code)
and code
or self.tempLang
return self
end
--- Sets temporary data language to a specificed language conversion resolution.
-- Only affects the next @{Data:msg} call.
-- @function Data:useLanguageConversionType
-- @param {string} convType Language conversion type to use.
-- @return {Data} Datastore instance.
function Data:useLanguageConversionType(convType)
self.tempLanguageConversionType = convType
return self
end
-- Package functions.
--- Gets a localized message by data module key.
-- Can be used to fetch messages in a specific language code through `uselang`
-- parameter. Extra numbered parameters can be supplied for substitution into
-- the data collection's message.
-- @function i18n.getMsg
-- @param {table} frame Frame table from invocation.
-- @param {table} frame.args Metatable containing arguments.
-- @param {string} frame.args[1] ROOTPAGENAME of i18n submodule.
-- @param {string} frame.args[2] Key of i18n message.
-- @param[opt] {string} frame.args.lang Default language of message.
-- @error[271] 'missing arguments in i18n.getMsg'
-- @return {string} I18n message in localised language.
-- @usage {{i18n|getMsg|source|key|arg1|arg2|uselang {{=}} code}}
function i18n.getMsg(frame)
if
not frame or
not frame.args or
not frame.args[1] or
not frame.args[2]
then
error('missing arguments in i18n.getMsg')
end
local source = frame.args[1]
local key = frame.args[2]
-- Pass through extra arguments.
local repl = {}
for i, a in ipairs(frame.args) do
if i >= 3 then
repl[i-2] = a
end
end
-- Load message data.
local ds = i18n.loadMessages(source)
-- Pass through language argument.
ds:inLang(frame.args.uselang)
-- Return message.
return ds:msg { key = key, args = repl }
end
--- I18n data loader for message collections.
-- @function i18n.loadMessages
-- @param {string} ... ROOTPAGENAME/path for target i18n
-- submodules.
-- @error[322] {string} 'no source supplied to i18n.loadMessages'
-- @return {table} I18n datastore instance.
-- @usage require('Module:I18n').loadMessages('1', '2')
function i18n.loadMessages(...)
local ds
local i = 0
local s = {}
for j = 1, select('#', ...) do
local source = select(j, ...)
if type(source) == 'string' and source ~= '' then
i = i + 1
s[source] = i
if not ds then
-- Instantiate datastore.
ds = {}
ds._messages = {}
-- Set default language.
setmetatable(ds, Data)
ds:useUserLang()
end
source = string.gsub(source, '^.', mw.ustring.upper)
source = mw.ustring.find(source, ':')
and source
or 'Module:' .. source .. '/i18n'
ds._messages[i] = mw.loadData(source)
end
end
if not ds then
error('no source supplied to i18n.loadMessages')
else
-- Attach source index map.
ds._sources = s
-- Return datastore instance.
return ds
end
end
--- Gets the language code of the wiki or page contents.
-- @function i18n.getContentLang
-- @usage {{i18n|getContentLang}}
-- @return {string} Content language code.
function i18n.getContentLang()
local code = mw.language.getContentLanguage():getCode()
local subPage = title.subpageText
-- Subpage language test.
if title.isSubpage and _i18n.isValidCode(subPage) then
code = _i18n.isValidCode(subPage) and subPage or code
end
return code
end
--- Gets the language code of the content.
-- Can validate a template's language code through `uselang` parameter.
-- @function i18n.getLang
-- @usage {{i18n|getLang|uselang {{=}} code}}
-- @return {string} Language code.
function i18n.getLang()
local frame = mw.getCurrentFrame() or {}
local parentFrame = frame.getParent and frame:getParent() or {}
local code = i18n.getContentLang()
-- Language argument test.
local langOverride =
(frame.args or {}).uselang or
(parentFrame.args or {}).uselang
if _i18n.isValidCode(langOverride) then
code = langOverride
-- User language test.
elseif parentFrame.preprocess or frame.preprocess then
uselang = uselang
or parentFrame.preprocess
and parentFrame:preprocess('{{int:lang}}')
or frame:preprocess('{{int:lang}}')
local decodedLang = mw.text.decode(uselang)
if decodedLang ~= '<lang>' and decodedLang ~= '⧼lang⧽' then
code = decodedLang == '(lang)'
and 'qqx'
or uselang
end
end
return code
end
--- Template wrapper for [[Template:I18n]].
-- @function i18n.main
-- @param {table} frame Frame invocation object.
-- @return {string} Module output in template context.
-- @usage {{#invoke:i18n|main}}
i18n.main = entrypoint(i18n)
return i18n
-- </nowiki>


