I18n-factory is a minimal Lua port of I18n-js. This module is used to demonstrate factory pattern support in Docbunto. For this reason, the code is limited to the library's Message and I18n objects.
Do not use this in your wiki! It exists purely for tests.
Documentation
Package items
i18nf.loadMessages(member)- Loads messages from JSON files in MediaWiki namespace. The messages are wrapped in a custom interface:
- This function parses and caches messages in the module as a Lua data table.
- These can be accessed using the
msgmethod of the returned object.
i18nf._getMsg(member)- Fetch a message from the cache of parsed i18n data.
Message
Message object, providing getter methods for messages.
Message:parse(member)- Parse wikitext links in the message and return the result.
Message:markdown(member)- Parse markdown links in the message and return the result.
Message:escape(member)- Escape the message wikitext and return the result.
Message:plain(member)- Return the message with no processing.
I18n
I18n object, providing message getter and language setters.
I18n:msg(member • factory • constructor)- Factory returning
Messageinstance. I18n:useLang(member)- I18n language setter to specificed language.
I18n:inLang(member)- Temporary datastore language setter to a specificed language. Only affects the next
I18n:msgcall. I18n:useContentLang(member)- I18n language setter to
wgContentLanguage. I18n:inContentLang(member)- Temporary language setter to
wgContentLanguage. Only affects the nextI18n:msgcall. I18n:useUserLang(member)- I18n language setter to
wgUserLanguage. - Note: Scribunto only registers
wgUserLanguagewhen an invocation is at the top of the call stack. I18n:inUserLang(member)- Temporary language setter to
wgUserLanguage. The message language reverts to the default language in the nextI18n:msgcall.
See also
--- **I18n-factory** is a minimal Lua port of [[I18n-js]].
-- This module is used to demonstrate factory pattern support in
-- Docbunto. For this reason, the code is limited to the library's
-- @{Message} and @{I18n} objects.
--
-- Do not use this in your wiki! It exists purely for tests.
-- @module i18nf
-- @pragma[off] all
-- @pragma ulist
-- @status unstable
-- @see [[I18n-js|Original JS library]]
-- @see [[wikipedia:Factory method pattern|"Factory method
-- pattern" on Wikipedia]]
-- Load dependencies.
local json = require('Dev:Json')
local fallbacks = require('Dev:Fallbacklist')
local frame = mw.getCurrentFrame()
--- Content language for the wiki.
-- @variable {string} content_lang
local content_lang = mw.language.getContentLanguage():getCode()
--- User language (display preference or content language).
-- @variable {string} user_lang
local user_lang = frame
and frame:preprocess('{{int:lang}}')
or content_lang
--- Cache of loaded I18n object instances.
-- @variable {table} cache
local cache = {}
--- Substitute arguments into the string, where arguments are
-- represented as $n where n > 0.
-- @param {string} msg The message to substitute arguments into.
-- @param {table} args The arguments to substitute in.
-- @local
local function handle_args(msg, args)
for i, a in ipairs(args) do
msg = msg:gsub('%$' .. tostring(i), a)
end
return msg
end
--- Checks whether a language code is valid.
-- @param {?string} code Language code to check.
-- @return {bool} Whether the language code is valid.
-- @local
local function is_valid_code(code)
return
type(code) == 'string' and
#mw.language.fetchLanguageName(code) ~= 0
end
--- Message object, providing getter methods for messages.
-- @type Message
--- Parse wikitext links in the message and return the result.
-- @return {string} Message string with MediaWiki and Markdown
-- rendered.
local function parse(self)
if not self._exists then
return self._msg
end
self._msg = markdown(self._msg)
self._msg = frame and frame:preprocess(self._msg) or self._msg
return self._msg
end
--- Parse markdown links in the message and return the result.
-- @return {string} Message string with Markdown rendered.
local function markdown(self)
-- Bold & italic tags.
self._msg = self._msg:gsub('%*%*%*([^\n*]+)%*%*%*', '<b><i>%1</i></b>')
self._msg = self._msg:gsub('%*%*([^\n*]+)%*%*', '<b>%1</b>')
self._msg = self._msg:gsub('%*([^\n*]+)%*', '<i>%1</i>')
-- Self-closing header support.
self._msg = self._msg:gsub('%f[^\n%z](#+) *([^\n#]+) *#+%s', function (hash, text)
local symbol = '='
return
'\n' .. symbol:rep(#hash) ..
' ' .. text ..
' ' .. symbol:rep(#hash) ..
'\n'
end)
-- External and internal links.
self._msg = self._msg:gsub('%[([^\n%]]+)%]%(([^\n][^\n)]-)%)', '[%2 %1]')
self._msg = self._msg:gsub('%{@link%s+([^}]+)%}', '[%1]')
-- Programming & scientific notation.
self._msg = self._msg:gsub('%f["`]`([^\n`]+)`%f[^"`]', '<code><nowiki>%1</nowiki></code>')
self._msg = self._msg:gsub('%$%$\\ce{([^\n}]+)}%$%$', '<chem>%1</chem>')
self._msg = self._msg:gsub('%$%$([^\n$]+)%$%$', '<math display="inline">%1</math>')
-- Strikethroughs and superscripts.
self._msg = self._msg:gsub('~~([^\n~]+)~~', '<del>%1</del>')
self._msg = self._msg:gsub('%^%(([^)]+)%)', '<sup>%1</sup>')
self._msg = self._msg:gsub('%^%s*([^%s%p]+)', '<sup>%1</sup>')
-- HTML output.
return self._msg
end
--- Escape the message wikitext and return the result.
-- @return {string} Escaped message string.
-- @local
local function escape(self)
self._msg = mw.text.nowiki(self._msg)
return self._msg
end
--- Return the message with no processing.
-- @return {string} Plain message string.
-- @local
local function plain(self)
return self._msg
end
--- I18n object, providing message getter and language setters.
-- @type I18n
local get_msg
--- Factory returning @{Message} instance.
-- @param {string} ... Message key, followed by optional arguments to
-- substitute into.
-- @return {Message} Instance of @{Message}.
-- @factory Message
-- @constructor
-- @local
local function message(self, ...)
local lang = self._lang
if self._tempLang then
lang = self._tempLang
self._tempLang = nil
end
local key = self._key
local name = table.remove(arg, 1)
local no_msg = '<' .. name .. '>'
local msg = get_msg(key, name, lang, self)
if arg.n ~= 0 then
msg = handle_args(msg, args)
end
-- @export
return {
-- Private fields.
_msg = msg,
_exists = msg ~= no_msg;
-- Public methods.
parse = parse,
markdown = markdown,
escape = escape,
plain = plain;
}
end
--- I18n language setter to specificed language.
-- @param {string} code Language code to use.
-- @return {I18n} Object instance of I18n (chainable).
local function use_lang(self, code)
self._lang = is_valid_code(code) and code or self._lang
return self
end
--- Temporary datastore language setter to a specificed language.
-- Only affects the next @{I18n:msg} call.
-- @param {string} code Language code to use.
-- @return {I18n} Object instance of I18n (chainable).
local function in_lang(self, code)
self._tempLang = is_valid_code(code) and code or self._tempLang
return self
end
--- I18n language setter to `wgContentLanguage`.
-- @return {I18n} Object instance of I18n (chainable).
local function use_content_lang(self)
self._lang = content_lang
return self
end
--- Temporary language setter to `wgContentLanguage`.
-- Only affects the next @{I18n:msg} call.
-- @return {I18n} Object instance of I18n (chainable).
local function in_content_lang(self)
self._tempLang = content_lang
return self
end
--- I18n language setter to `wgUserLanguage`.
-- @return {I18n} Object instance of I18n (chainable).
-- @note Scribunto only registers `wgUserLanguage` when an
-- invocation is at the top of the call stack.
local function use_user_lang(self)
self._lang = frame and user_lang or self._lang
return self
end
--- Temporary language setter to `wgUserLanguage`.
-- The message language reverts to the default language in the next
-- @{I18n:msg} call.
-- @return {I18n} Object instance of I18n (chainable).
local function in_user_lang(self)
self._tempLang = i18n.getLang() or self.tempLang
return self
end
--- Factory returning @{I18n} instance.
-- @param {string} name Name of `i18n.json` root page.
-- @return {I18n} Object instance of @{I18n}.
-- @factory I18n
-- @constructor
-- @private
local function i18n(name)
local default_lang = user_lang
local temp_lang = nil
-- @export
return {
-- Private fields.
_lang = default_lang,
_tempLang = temp_lang,
_key = name;
-- Public methods.
msg = message,
useLang = use_lang,
inLang = in_lang,
useContentLang = use_content_lang,
inContentLang = in_content_lang,
useUserLang = use_user_lang,
inUserLang = in_user_lang;
}
end
--- Loads messages from JSON files in MediaWiki namespace.
-- The messages are wrapped in a custom interface:
-- * This function parses and caches messages in the module as a Lua
-- data table.
-- * These can be accessed using the @{I18n:msg|msg} method of
-- the returned object.
-- @param {string} name Name of `i18n.json` root page.
-- @param {table} options Configuration options.
-- @param[opt] {boolean} options.inline Whether the JSON file uses
-- inline comments instead of multiline comments.
-- @return {I18n} Instance of @{I18n} object.
local function loadMessages(name, options)
options = options or {}
page = 'MediaWiki:Custom-' .. name .. '/i18n.json'
if cache[name] then
return i18n(name)
end
local comment_pattern = options.inline
and '^%s*//[^\n]*\n'
or '^%s*/%*[^/]*/\n'
local result = mw.title.new(page):getContent() or '{}'
result = json.decode((result:gsub(comment_pattern, '')))
cache[name] = result
return i18n(name)
end
--- Fetch a message from the cache of parsed i18n data.
-- @param {string} name Message source name.
-- @param {string} key Message key.
-- @param {I18n} i18n Instance of @{I18n} object.
function get_msg(key, name, lang)
if type(lang) == 'string' and mw.text.decode(lang) == '<lang>' then
return '<i18n-lua-' .. key .. '-' .. name .. '>'
end
local data = cache[key] or {}
if data[lang] and data[lang][name] then
return data[lang][name]
end
for _, l in ipairs((fallbacks[lang] or {})) do
if (data[l] or {})[name] then
return data[l][name]
end
end
return '<' .. name .. '>';
end
-- @export
return {
-- Public functions.
loadMessages = loadMessages;
-- Private functions.
_getMsg = get_msg;
}