Module:Languages: Difference between revisions

From Imperivm Romanvm
Jump to navigation Jump to search
No edit summary
 
No edit summary
Tag: Manual revert
 
(3 intermediate revisions by 2 users not shown)
Line 1: Line 1:
--[=[
local export = {}
Not globally exposed. Internal function only.


language_subpages( frame, transform, options )
local function do_entry_name_or_sort_key_replacements(text, replacements)
Parameters
if replacements.from then
    frame:    The frame that was passed to the method invoked. The first argument or the page argument will be respected.
for i, from in ipairs(replacements.from) do
    transform: A transform function. Example: function( basepagename, subpagename, code, langname ) end
local to = replacements.to[i] or ""
    options:  An object with options. Example: { abort= { on=function() end, time=0.8 }  }
text = mw.ustring.gsub(text, from, to)
        Following options are available:
end
        abort: Aborts iterating over the subpages if one of the conditions is met. If the process is aborted, nil is returned!
end
            on: Function to be called if an abort-condition was met.
            cycles: The maximum number of subpages to run over.
if replacements.remove_diacritics then
            time: Maximum time to spend running over the subpages.
text = mw.ustring.toNFD(text)
text = mw.ustring.gsub(text,
'[' .. replacements.remove_diacritics .. ']',
'')
text = mw.ustring.toNFC(text)
end
return text
end


]=]
local Language = {}
function language_subpages( frame, transform, options )
    local args, pargs, options = frame.args, ( frame:getParent() or {} ).args or {}, options or {};
    local title = args.page or args[1] or pargs.page or pargs[1] or "";
    local abort = options.abort or {};
    local at, clock = type( abort.on ), os.clock();
    local ac = function()
        if  at == 'function' or ( at == 'table' and getmetatable(abort.on).__call ) then
            abort.on();
        end
    end
    local tt = type( transform );
    local page = require( 'Module:Page' );


    title = page.clean(title);
function Language:getCode()
return self._code
end


    if tt == 'function' or ( tt == 'table' and getmetatable(transform).__call ) then
        local fetch, pages, langcode, langname = mw.language.fetchLanguageName, {};
--[==[


    / \
function Language:getCanonicalName()
    / | \
return self._rawData[1] or self._rawData.canonicalName
  /  ·  \
  ¯¯¯¯¯¯¯
  Page.subpages() no longer works because it attempted to parse the HTML content generated by
  calling the parser function "Special:Prefixindex:" which is no longer expanded in Lua but
  converted to a "stripped tag" (containing a unique identifier surrounded by ASCII DEL characters)
  representing the tag name and its parameters.
  The actual expansion of stripped tags can no longer be performed in Lua.
  Now unstripping these tags just kills ALL these tags (except "wiki" tags) instead of performing
  their expansion by running the extension code. Only MediaWiki can unstrip these tags in texts after
  they have been returned by Lua.
  For this reason, page.subpages() is now completely empty (Module:Page no longer works).
  This cannot be bypassed, except by using a Scribunto extension library if lifting the limits set by mw.unstrip.
  Note that "Special:Prefixindex:" is also costly, even if it just requires a single database query to
  get all subpages, instead of one costly #ifexist or one costly mw.title() property reading per
  tested subpage to know if it exists.
  For now there's still no reliable way to get a list of subpages, or performing queries similar to
  the [[Special:Prefixindex]] page or list members of a category like when viewing a category page.
  Ideally, there should exist a method for such queries on Title objects returned by the mw.title library;
  but for now there's none.
  In Lua now, the only expansion possible with an immediate effect is the expansion of standard templates,
  all special tags or special pages, or parser function extensions do not work (Only the #expr parser
  function is supported by using an external Scribunto library).
--]==]
        for pg in page.subpages( title, { ignoreNS=true } ) do
            if abort.cycles then
                abort.cycles = abort.cycles - 1
                if 0 == abort.cycles then return ac()  end
            end
            if abort.time then
                if (os.clock() - clock) > abort.time then return ac()  end
            end
            if mw.ustring.len( pg ) <= 12 then
                langcode = string.lower( pg );
                langname = fetch( langcode );
                if langname ~= '' then
                    table.insert( pages, transform( title, pg, langcode, langname ) );
                end
            end
        end
        return pages;
    end
    return {};
end
end


function cloneArgs(frame)
 
    local args, pargs = {}, {}
function Language:getDisplayForm()
    for k,v in pairs( frame.args ) do args[k] = v end
return self:getCanonicalName()
    if frame:getParent() then
        for k,v in pairs( frame:getParent().args ) do pargs[k] = v end
    end
    return args, pargs
end
end




function Language:getOtherNames(onlyOtherNames)
self:loadInExtraData()
return require("Module:language-like").getOtherNames(self, onlyOtherNames)
end


local p = {};


--[=[
function Language:getAliases()
Usage:
self:loadInExtraData()
{{#invoke:languages|internal|Template:Adjective}}
return self._extraData.aliases or {}
]=]
end
function p.internal(frame)
 
    return table.concat(
 
        language_subpages( frame,
function Language:getVarieties(flatten)
            function( title, page, code, name )
self:loadInExtraData()
                return mw.ustring.format(
return require("Module:language-like").getVarieties(self, flatten)
                    '<bdi class="language lang-%s" lang="%s">[[%s/%s|%s]]</bdi>',
end
                    code, code,
 
                    title, page,
 
                    name
function Language:getType()
                );
return self._rawData.type or "regular"
            end
end
        ),
 
        '&nbsp;<b>·</b>&#32;'
 
    );
function Language:getWikimediaLanguages()
if not self._wikimediaLanguageObjects then
local m_wikimedia_languages = require("Module:wikimedia languages")
self._wikimediaLanguageObjects = {}
local wikimedia_codes = self._rawData.wikimedia_codes or { self._code }
for _, wlangcode in ipairs(wikimedia_codes) do
table.insert(self._wikimediaLanguageObjects, m_wikimedia_languages.getByCode(wlangcode))
end
end
return self._wikimediaLanguageObjects
end
 
function Language:getWikipediaArticle()
if self._rawData.wikipedia_article then
return self._rawData.wikipedia_article
elseif self._wikipedia_article then
return self._wikipedia_article
elseif self:getWikidataItem() and mw.wikibase then
self._wikipedia_article = mw.wikibase.sitelink(self:getWikidataItem(), 'enwiki')
end
if not self._wikipedia_article then
self._wikipedia_article = mw.ustring.gsub(self:getCategoryName(), "Creole language", "Creole")
end
return self._wikipedia_article
end
 
function Language:makeWikipediaLink()
return "[[w:" .. self:getWikipediaArticle() .. "|" .. self:getCanonicalName() .. "]]"
end
end


--[=[
function Language:getWikidataItem()
Usage:
local item = self._rawData[2]
{{#invoke:languages|external|Template:Adjective}}
]=]
if type(item) == "number" then
function p.external(frame)
return "Q" .. item
    return table.concat(
else
        language_subpages( frame,
return item
            function( title, page, code, name )
end
                return mw.ustring.format(
                    '<bdi class="language lang-%s" lang="%s">[%s/%s %s]</bdi>',
                    code, code,
                    tostring( mw.uri.fullUrl( title ) ), page:gsub( ' ', '_' ),
                    name
                );
            end
        ),
        '&nbsp;<b>·</b>&#32;'
    );
end
end


--[=[
function Language:getScripts()
forEachLanguage
if not self._scriptObjects then
local m_scripts = require("Module:scripts")
self._scriptObjects = {}
for _, sc in ipairs(self:getScriptCodes()) do
table.insert(self._scriptObjects, m_scripts.getByCode(sc))
end
end
return self._scriptObjects
end


This function iterates over all language codes known to MediaWiki based on a maintained list
function Language:getScriptCodes()
replacing patterns in a pattern-string for each language
return self._rawData.scripts or self._rawData[4] or { "None" }
end


Usage
function Language:getFamily()
{{#invoke:Languages|forEachLanguage
if self._familyObject then
  |pattern=patternstring
return self._familyObject
  |before=string to insert before iteration
end
  |after=string added after iteration
  |sep=separator string between iterations
local family = self._rawData[3] or self._rawData.family
  |inLang=langcode used for $lnTrP and $lnTrUC1
if family then
}}
self._familyObject = require("Module:families").getByCode(family)
end
return self._familyObject
end


Parameters
    pattern: A pattern string which is processed for each language and which is concatenated at the end and returned as one string
    before: A string that is inserted before the concatenated result
    after: A string that is inserted after the concatenated result
    sep: A string that is inserted between each line created from the pattern while iterating (like ProcessedPattern_sep_ProcessedPattern_sep_ProcessedPattern)
    inLang: Langcode to use for $lnTrP and $lnTrUC1
    preprocess: if set to a non-empty value, the output will be preprocessed before being returned.


Warning
function Language:getAncestors()
    The output is still not prepreprocessed by default: so parser functions and magic keywords generated by the pattern are still not executed and replaced,
if not self._ancestorObjects then
    and template transclusions are still not expanded (see examples in other functions in this module).
self._ancestorObjects = {}
    When using this function directly from a MediaWiki page or template, this means it is only possible to use patterns generating basic MediaWiki formatting
    or HTML tags. It you want the output to be preprocessed (in the given frame), set the preprocess parameter to a non-empty string.
if self._rawData.ancestors then
   
for _, ancestor in ipairs(self._rawData.ancestors) do
Patterns
table.insert(self._ancestorObjects, export.getByCode(ancestor) or require("Module:etymology languages").getByCode(ancestor))
    $lc - language code such as en or de
end
    $lnP - language name in own language (autonym)
else
    $lnUC1 - language name in own language (autonym), first letter upper case
local fam = self:getFamily()
    $lnTrP - language name translated to the language requested by language code passed to inLang
local protoLang = fam and fam:getProtoLanguage() or nil
    $lnTrUC1 - language name translated to the language requested by language code passed to inLang, first letter upper case
-- For the case where the current language is the proto-language
-- of its family, we need to step up a level higher right from the start.
if protoLang and protoLang:getCode() == self:getCode() then
fam = fam:getFamily()
protoLang = fam and fam:getProtoLanguage() or nil
end
while not protoLang and not (not fam or fam:getCode() == "qfa-not") do
fam = fam:getFamily()
protoLang = fam and fam:getProtoLanguage() or nil
end
table.insert(self._ancestorObjects, protoLang)
end
end
return self._ancestorObjects
end


Example
local function iterateOverAncestorTree(node, func)
    {{#invoke:Languages|forEachLanguage|pattern=<span lang="$lc" xml:lang="$lc" class="language lang-$lc">[[Page/$lc|$lnP]]</span>}}
for _, ancestor in ipairs(node:getAncestors()) do
]=]
if ancestor then
local ret = func(ancestor) or iterateOverAncestorTree(ancestor, func)
if ret then
return ret
end
end
end
end


-- =p.forEachLanguage({ args= { pattern = "$lc - $lnTrP\n", inLang = "en" } })
function Language:getAncestorChain()
function p.forEachLanguage(frame)
if not self._ancestorChain then
    local l = require("Module:Languages/List")
self._ancestorChain = {}
local step = #self:getAncestors() == 1 and self:getAncestors()[1] or nil
while step do
table.insert(self._ancestorChain, 1, step)
step = #step:getAncestors() == 1 and step:getAncestors()[1] or nil
end
end
return self._ancestorChain
end


    local ret = {}
    local lang    = mw.language
    local line
    local pattern = frame.args.pattern  or frame.args[1] or ""
    local prefix  = frame.args.before    or frame.args[2] or ""
    local postfix = frame.args.after    or frame.args[3] or ""
    local sep    = frame.args.sep      or frame.args.separator or frame.args[4] or ""
    local inLang  = frame.args.inLang    or frame.args[5] or nil
    local preprocess = frame.args.preprocess or frame.args[6] or ""


    local langNameUCFirstReq          = not not pattern:find( "$lnUC1", 1, true )
function Language:hasAncestor(otherlang)
    local langNameReq                  = not not pattern:find( "$lnP", 1, true ) or langNameUCFirstReq
local function compare(ancestor)
    local langNameTranslatedUCFirstReq = not not pattern:find( "$lnTrUC1", 1, true )
return ancestor:getCode() == otherlang:getCode()
    local langNameTranslatedReq        = not not pattern:find( "$lnTrP", 1, true ) or langNameTranslatedUCFirstReq
end
    local contentLangInstance = mw.language.getContentLanguage()
    local inLangLangInstance
return iterateOverAncestorTree(self, compare) or false
    local l = mw.language.fetchLanguageNames() -- autonyms
end
    local lTr
    local lcIdList = require('Module:Languages/List').getSortedList( l )


    if langNameTranslatedReq then
        inLangLangInstance = --[==[
            mw.getLanguage( inLang ) -- Quota hit in :ucfirst() if using too many langInstances
            --]==] contentLangInstance
        lTr = mw.language.fetchLanguageNames( inLang ) -- translated names
    end


    for _, lcId in pairs( lcIdList ) do
function Language:getCategoryName(nocap)
        local subst = lcId:gsub('%%', '%%%%')
local name = self:getCanonicalName()
        line = pattern:gsub( "%$lc", subst )
        local langName, langInstance
-- If the name already has "language" in it, don't add it.
        -- autonym (name of lcId in locale lcId)
if not name:find("[Ll]anguage$") then
        if langNameReq then
name = name .. " language"
            langName = l[lcId]
end
            subst = langName:gsub('%%', '%%%%')
if not nocap then
            line = line:gsub( "%$lnP", subst )
name = mw.getContentLanguage():ucfirst(name)
        end
end
        if langNameUCFirstReq then
return name
            langInstance = --[==[
end
                mw.getLanguage( lcId ) -- Quota hit in :ucfirst() if using too many langInstances
                --]==] contentLangInstance
            langName = langInstance:ucfirst( langName )
            subst = langName:gsub('%%', '%%%%')
            line = line:gsub( "%$lnUC1", subst )
        end


        -- translated name (name of lcId in locale inLang)
        if langNameTranslatedReq then
            langName = lTr[lcId]
            subst = langName:gsub('%%', '%%%%')
            line = line:gsub( "%$lnTrP", subst )
        end
        if langNameTranslatedUCFirstReq then
            langName = inLangLangInstance:ucfirst( langName )
            subst = langName:gsub('%%', '%%%%')
            line = line:gsub( "%$lnTrUC1", subst )
        end


        table.insert(ret, line)
function Language:makeCategoryLink()
    end
return "[[:Category:" .. self:getCategoryName() .. "|" .. self:getDisplayForm() .. "]]"
    ret = prefix .. table.concat( ret, sep ) .. postfix
    if preprocess ~= '' then
        ret = frame:preprocess(ret)
    end
    return ret
end
end


--[=[
 
Provide logic for [[Template:Lle]] (Language Links external, to be substituted, language names written exactly as #language would provide them)
function Language:getStandardCharacters()
Warning: may expands too many costly #ifexist without limitation (if not substituted into a separate "/lang" template)
return self._rawData.standardChars
]=]
function p.lle(frame)
    return frame:preprocess(
        p.forEachLanguage({
            args = {
                pattern = '{{subst:#ifexist:{{{1}}}/$lc|[{{subst:fullurl:{{{1}}}/$lc}} <bdi class="language lang-$lc" lang="$lc">$lnP</bdi>]&nbsp;<b>∙</b>&#32;<!--\n-->}}'
            }
        })
    )
end
end


--[=[
 
Provide logic for [[Template:Ll]] (Language Links internal, to be substituted, language names written exactly as #language would provide them)
function Language:makeEntryName(text)
Warning: may expands too many costly #ifexist without limitation (if not substituted into a separate "/lang" template)
text = mw.ustring.match(text, "^[¿¡]?(.-[^%s%p].-)%s*[؟?!;՛՜ ՞ ՟?!︖︕।॥။၊་།]?$") or text
]=]
function p.ll(frame)
if self:getCode() == "ar" then
    return frame:preprocess(
local U = mw.ustring.char
        p.forEachLanguage({
local taTwiil = U(0x640)
            args = {
local waSla = U(0x671)
                pattern = '{{subst:#ifexist:{{{1}}}/$lc|[[{{{1}}}/$lc|<bdi class="language lang-$lc" lang="$lc">$lnP</bdi>]]&nbsp;<b>∙</b>&#32;<!--\n-->}}'
-- diacritics ordinarily removed by entry_name replacements
            }
local Arabic_diacritics = U(0x64B, 0x64C, 0x64D, 0x64E, 0x64F, 0x650, 0x651, 0x652, 0x670)
        })
    )
if text == waSla or mw.ustring.find(text, "^" .. taTwiil .. "?[" .. Arabic_diacritics .. "]" .. "$") then
return text
end
end
if type(self._rawData.entry_name) == "table" then
text = do_entry_name_or_sort_key_replacements(text, self._rawData.entry_name)
end
return text
end
end




--------------------------------------------------------
-- Return true if the language has display processing enabled, i.e. lang:makeDisplayText()
--- Different approaches for [[Template:Lang links]] ---
-- does non-trivial processing.
--------------------------------------------------------
function Language:hasDisplayProcessing()
return not not self._rawData.display
end


--[=[
Provide logic for [[Template:Lang links]]
Using a cute Hybrid-Method:
    First check the subpages which is quite fast; if there are too many fall back to checking for each language page individually
]=]


-- =p.langLinksNonExpensive({ args= { page='Commons:Picture of the Year/2010' }, getParent=function() end })
-- Apply display-text replacements to `text`, if any.
-- =p.langLinksNonExpensive({ args= { page='Main Page' }, getParent=function() end })
function Language:makeDisplayText(text)
-- =p.langLinksNonExpensive({ args= { page='Template:No_source_since' }, getParent=function() end })
if type(self._rawData.display) == "table" then
-- =p.langLinksNonExpensive({ args= { page='MediaWiki:Gadget-HotCat' }, getParent=function() end })
text = do_entry_name_or_sort_key_replacements(text, self._rawData.display)
function p.langLinksNonExpensive(frame)
end
    local args, pargs = frame.args, ( frame:getParent() or {} ).args or {};
    local title = args.page or args[1] or pargs.page or pargs[1] or "";
return text
    local contentLangInstance = mw.language.getContentLanguage();
    local pages2
    if frame.preprocess == nil then
        frame = mw.getCurrentFrame()
    end
--[==[
    local options = {
        abort = {
            time = 3.5,
            on = function()
                pages2 = p.forEachLanguage({
                    args = {
                      pattern = '{{#ifexist:' .. title .. '/$lc|[[' .. title .. '/$lc|<bdi lang="$lc">$lnP</bdi>]]&nbsp;<b>∙</b>&#32;}}'
                    }
                })
            end
        }
    }
    local pages = language_subpages( frame,
        function( title, page, code, langname )
            return mw.ustring.format(
                '[[%s/%s|<bdi lang="%s">%s</bdi>]]</span>&nbsp;<b>∙</b>&#32;',
                title, page, code, langname
            )
        end, options );
    return pages2 and frame:preprocess(pages2) or table.concat(pages, '');
--]==]
    return frame:preprocess(
        p.forEachLanguage( {
            args = {
                pattern = '{{#ifexist:' .. title .. '/$lc|[[' .. title .. '/$lc|<bdi lang="$lc">$lnP</bdi>]]&nbsp;<b>∙</b>&#32;}}'
            }
        })
    )
end
end


---------------------------------------------------------
----------------- [[Template:Autolang]] -----------------
---------------------------------------------------------
--[[
  Works like {{autotranslate}} just allowing an unlimited number of arguments, even named arguments.
  It's doing Magic! No arguments should be passed to {{#invoke:}}
]]


function p.autolang(frame)
-- Add to data tables?
    local args, pargs = cloneArgs( frame )
local has_dotted_undotted_i = {
    if nil == args.useargs then
["az"] = true,
        if not args.base then args = pargs end
["crh"] = true,
    elseif 'both' == args.useargs then
["gag"] = true,
        for k,v in pairs(args) do pargs[k] = v end
["kaa"] = true,
        args = pargs
["tt"] = true,
    elseif 'parent' == args.useargs then
["tr"] = true,
        args = pargs
["zza"] = true,
        if pargs.base and not args.base then
}
            args.base = pargs.base
 
        end
function Language:makeSortKey(name, sc)
    end
if has_dotted_undotted_i[self:getCode()] then
    local base = args.base
name = name:gsub("I", "ı")
    local userlang = frame:preprocess( '{{Int:Lang}}' )
end
    local tl, tlns = 'Template:', 10
    local tlb, fallback1, currenttemplate
name = mw.ustring.lower(name)
    local fallback, contentlang = mw.text.split( userlang, '-', true )[1], mw.language.getContentLanguage():getCode()
-- Remove initial hyphens and *
local hyphens_regex = "^[-־ـ*]+(.)"
name = mw.ustring.gsub(name, hyphens_regex, "%1")
-- If there are language-specific rules to generate the key, use those
if type(self._rawData.sort_key) == "table" then
name = do_entry_name_or_sort_key_replacements(name, self._rawData.sort_key)
elseif type(self._rawData.sort_key) == "string" then
name = require("Module:" .. self._rawData.sort_key).makeSortKey(name, self:getCode(), sc and sc:getCode())
end
-- Remove parentheses, as long as they are either preceded or followed by something
name = mw.ustring.gsub(name, "(.)[()]+", "%1")
name = mw.ustring.gsub(name, "[()]+(.)", "%1")
if has_dotted_undotted_i[self:getCode()] then
name = name:gsub("i", "İ")
end
return mw.ustring.upper(name)
end


    local createReturn = function(title)
function Language:overrideManualTranslit()
        local ret
if self._rawData.override_translit then
        local tlargs = {}
return true
        -- When LUA is invoked, templates are already expanded. This must be respected.
else
        return frame:expandTemplate{ title = title, args = args }
return false
    end
end
end


    if not base then
        return ("'autolang' in [[Module:Languages]] was called but the 'base' parameter could not be found." ..
            "The base parameter specifies the template that's subpages will be sought for a suitable translation.")
    end
    tlb = tl .. base .. '/'


    currenttemplate = tlb .. userlang
function Language:transliterate(text, sc, module_override)
    local ok, exists = pcall( function()
if not ((module_override or self._rawData.translit_module) and text) then
        return mw.title.new( currenttemplate, tlns ).exists
return nil
    end )
end
    if ok and exists then
        return createReturn(currenttemplate)
if module_override then
    end
require("Module:debug").track("module_override")
end
return require("Module:" .. (module_override or self._rawData.translit_module)).tr(text, self:getCode(), sc and sc:getCode() or nil)
end


    fallback1 = frame:preprocess( '{{Fallback|1=' .. base .. '|2=' .. userlang .. '}}' )
function Language:hasTranslit()
    if fallback1 ~= contentlang then
return self._rawData.translit_module and true or false
        return createReturn(tlb .. fallback1)
end
    end


    currenttemplate = tlb .. fallback
    local ok, exists = pcall( function()
        return mw.title.new( currenttemplate, tlns ).exists
    end )
    if ok and exists then
        return createReturn(currenttemplate)
    end


    currenttemplate = tlb .. contentlang
function Language:link_tr()
    local ok, exists = pcall( function()
return self._rawData.link_tr and true or false
        return mw.title.new( currenttemplate, tlns ).exists
    end )
    if ok and exists then
        return createReturn(currenttemplate)
    end
    return createReturn(tl .. base)
end
end


--[=[
 
Usage:
function Language:toJSON()
{{#invoke:languages|isKnownLanguageTag|gsw}} -> 1
local entryNamePatterns = nil
{{#invoke:languages|isKnownLanguageTag|doesNotExist}} ->
local entryNameRemoveDiacritics = nil
]=]
function p.isKnownLanguageTag(frame)
if self._rawData.entry_name then
    return mw.language.isKnownLanguageTag( frame.args[1] or frame.args.tag or frame.args.code or '' ) and '1' or ''
entryNameRemoveDiacritics = self._rawData.entry_name.remove_diacritics
if self._rawData.entry_name.from then
entryNamePatterns = {}
for i, from in ipairs(self._rawData.entry_name.from) do
local to = self._rawData.entry_name.to[i] or ""
table.insert(entryNamePatterns, { from = from, to = to })
end
end
end
local ret = {
ancestors = self._rawData.ancestors,
canonicalName = self:getCanonicalName(),
categoryName = self:getCategoryName("nocap"),
code = self._code,
entryNamePatterns = entryNamePatterns,
entryNameRemoveDiacritics = entryNameRemoveDiacritics,
family = self._rawData[3] or self._rawData.family,
otherNames = self:getOtherNames(true),
aliases = self:getAliases(),
varieties = self:getVarieties(),
scripts = self._rawData.scripts or self._rawData[4],
type = self:getType(),
wikimediaLanguages = self._rawData.wikimedia_codes,
wikidataItem = self:getWikidataItem(),
}
return require("Module:JSON").toJSON(ret)
end
 
 
-- Do NOT use these methods!
-- All uses should be pre-approved on the talk page!
function Language:getRawData()
return self._rawData
end
 
function Language:getRawExtraData()
self:loadInExtraData()
return self._extraData
end
 
Language.__index = Language
 
 
function export.getDataModuleName(code)
if code:find("^%l%l$") then
return "languages/data2"
elseif code:find("^%l%l%l$") then
local prefix = code:sub(1, 1)
return "languages/data3/" .. prefix
elseif code:find("^[%l-]+$") then
return "languages/datax"
else
return nil
end
end
 
 
function export.getExtraDataModuleName(code)
if code:find("^%l%l$") then
return "languages/extradata2"
elseif code:find("^%l%l%l$") then
local prefix = code:sub(1, 1)
return "languages/extradata3/" .. prefix
elseif code:find("^[%l-]+$") then
return "languages/extradatax"
else
return nil
end
end
 
 
local function getRawLanguageData(code)
local modulename = export.getDataModuleName(code)
return modulename and mw.loadData("Module:" .. modulename)[code] or nil
end
 
 
local function getRawExtraLanguageData(code)
local modulename = export.getExtraDataModuleName(code)
return modulename and mw.loadData("Module:" .. modulename)[code] or nil
end
 
 
function Language:loadInExtraData()
if not self._extraData then
-- load extra data from module and assign to meta table
-- use empty table as a fallback if extra data is nil
local meta = getmetatable(self)
meta._extraData = getRawExtraLanguageData(self._code) or {}
setmetatable(self, meta)
end
end
 
 
function export.makeObject(code, data)
if data and data.deprecated then
require("Module:debug").track {
"languages/deprecated",
"languages/deprecated/" .. code
}
end
return data and setmetatable({ _rawData = data, _code = code }, Language) or nil
end
 
 
function export.getByCode(code, paramForError, allowEtymLang, allowFamily)
if type(code) ~= "string" then
error("The function getByCode expects a string as its first argument, but received " .. (code == nil and "nil" or "a " .. type(code)) .. ".")
end
local retval = export.makeObject(code, getRawLanguageData(code))
if not retval and allowEtymLang then
retval = require("Module:etymology languages").getByCode(code)
end
if not retval and allowFamily then
retval = require("Module:families").getByCode(code)
end
if not retval and paramForError then
require("Module:languages/errorGetBy").code(code, paramForError, allowEtymLang, allowFamily)
end
return retval
end
 
 
function export.getByName(name, errorIfInvalid)
local byName = mw.loadData("Module:languages/by name")
local code = byName.all and byName.all[name] or byName[name]
if not code then
if errorIfInvalid then
error("The language name \"" .. name .. "\" is not valid. See [[Wiktionary:List of languages]].")
else
return nil
end
end
return export.makeObject(code, getRawLanguageData(code))
end
 
function export.getByCanonicalName(name, errorIfInvalid, allowEtymLang, allowFamily)
local byName = mw.loadData("Module:languages/canonical names")
local code = byName and byName[name]
 
local retval = code and export.makeObject(code, getRawLanguageData(code)) or nil
if not retval and allowEtymLang then
retval = require("Module:etymology languages").getByCanonicalName(name)
end
if not retval and allowFamily then
local famname = name:match("^(.*) languages$")
famname = famname or name
retval = require("Module:families").getByCanonicalName(famname)
end
if not retval and errorIfInvalid then
require("Module:languages/errorGetBy").canonicalName(name, allowEtymLang, allowFamily)
end
return retval
end
end


function p.file_languages(frame)
--[[ If language is an etymology language, iterates through parent languages
    local M_link = require( 'Module:Link' )
until it finds a non-etymology language. ]]
    local contentLangInstance = mw.language.getContentLanguage()
function export.getNonEtymological(lang)
    local pattern = frame.args.pattern or '%s (%s)'
while lang:getType() == "etymology language" do
    local original = frame.args.original or mw.title.getCurrentTitle().text
local parentCode = lang:getParentCode()
    local ext_start, _ = string.find( original, '\.%w+$' )
lang = export.getByCode(parentCode)
    local file_ext = string.sub( original, ext_start )
or require("Module:etymology languages").getByCode(parentCode)
    original = string.sub( original, 0, ext_start - 1 )
or require("Module:families").getByCode(parentCode)
    return frame:preprocess(
end
        '<gallery>\n' ..
        (table.concat(
return lang
            M_link.forEachLink(
                p.forEachLanguage({
                    args = { pattern = '[[$lc]]' }
                }),
                function( linkInfo )
                    local filename = mw.ustring.format( pattern, original, linkInfo.text ) .. file_ext
                    local ok, exists = pcall( function()
                            return mw.title.new( filename, 6 ).exists
                        end )
                    if ok and exists then
                        return mw.ustring.format( '%s|%s',
                            filename,
                            mw.language.fetchLanguageName( linkInfo.text )
                        )
                    else
                        return nil
                    end
                end
            ), '\n'
        )) ..
        '\n</gallery>'
    )
end
end


function p.runTests()
-- for backwards compatibility only; modules should require the /error themselves
    return p.langLinksNonExpensive({
function export.err(lang_code, param, code_desc, template_tag, not_real_lang)
        args = {
return require("Module:languages/error")(lang_code, param, code_desc, template_tag, not_real_lang)
            page = 'Module:Languages/testcases/test'
        },
        getParent = function() end
    }) ==
        '[[Module:Languages/testcases/test/de|<bdi lang="de">Deutsch</bdi>]]&nbsp;<b>∙</b>&#32;' ..
        '[[Module:Languages/testcases/test/en|<bdi lang="en">English</bdi>]]&nbsp;<b>∙</b>&#32;'
end
end


return p;
return export

Latest revision as of 15:23, 9 June 2022

Documentation for this module may be created at Module:Languages/doc

local export = {}

local function do_entry_name_or_sort_key_replacements(text, replacements)
	if replacements.from then
		for i, from in ipairs(replacements.from) do
			local to = replacements.to[i] or ""
			text = mw.ustring.gsub(text, from, to)
		end
	end
	
	if replacements.remove_diacritics then
		text = mw.ustring.toNFD(text)
		text = mw.ustring.gsub(text,
			'[' .. replacements.remove_diacritics .. ']',
			'')
		text = mw.ustring.toNFC(text)
	end
	
	return text
end

local Language = {}

function Language:getCode()
	return self._code
end


function Language:getCanonicalName()
	return self._rawData[1] or self._rawData.canonicalName
end


function Language:getDisplayForm()
	return self:getCanonicalName()
end


function Language:getOtherNames(onlyOtherNames)
	self:loadInExtraData()
	return require("Module:language-like").getOtherNames(self, onlyOtherNames)
end


function Language:getAliases()
	self:loadInExtraData()
	return self._extraData.aliases or {}
end


function Language:getVarieties(flatten)
	self:loadInExtraData()
	return require("Module:language-like").getVarieties(self, flatten)
end


function Language:getType()
	return self._rawData.type or "regular"
end


function Language:getWikimediaLanguages()
	if not self._wikimediaLanguageObjects then
		local m_wikimedia_languages = require("Module:wikimedia languages")
		self._wikimediaLanguageObjects = {}
		local wikimedia_codes = self._rawData.wikimedia_codes or { self._code }
		
		for _, wlangcode in ipairs(wikimedia_codes) do
			table.insert(self._wikimediaLanguageObjects, m_wikimedia_languages.getByCode(wlangcode))
		end
	end
	
	return self._wikimediaLanguageObjects
end

function Language:getWikipediaArticle()
	if self._rawData.wikipedia_article then
		return self._rawData.wikipedia_article 
	elseif self._wikipedia_article then
		return self._wikipedia_article
	elseif self:getWikidataItem() and mw.wikibase then
		self._wikipedia_article = mw.wikibase.sitelink(self:getWikidataItem(), 'enwiki')
	end
	if not self._wikipedia_article then
		self._wikipedia_article = mw.ustring.gsub(self:getCategoryName(), "Creole language", "Creole")
	end
	return self._wikipedia_article
end

function Language:makeWikipediaLink()
	return "[[w:" .. self:getWikipediaArticle() .. "|" .. self:getCanonicalName() .. "]]"
end

function Language:getWikidataItem()
	local item = self._rawData[2]
	
	if type(item) == "number" then
		return "Q" .. item
	else
		return item
	end
end

function Language:getScripts()
	if not self._scriptObjects then
		local m_scripts = require("Module:scripts")
		self._scriptObjects = {}
		
		for _, sc in ipairs(self:getScriptCodes()) do
			table.insert(self._scriptObjects, m_scripts.getByCode(sc))
		end
	end
	
	return self._scriptObjects
end

function Language:getScriptCodes()
	return self._rawData.scripts or self._rawData[4] or { "None" }
end

function Language:getFamily()
	if self._familyObject then
		return self._familyObject
	end
		
	local family = self._rawData[3] or self._rawData.family 
	if family then
		self._familyObject = require("Module:families").getByCode(family)
	end
	
	return self._familyObject
end


function Language:getAncestors()
	if not self._ancestorObjects then
		self._ancestorObjects = {}
		
		if self._rawData.ancestors then
			for _, ancestor in ipairs(self._rawData.ancestors) do
				table.insert(self._ancestorObjects, export.getByCode(ancestor) or require("Module:etymology languages").getByCode(ancestor))
			end
		else
			local fam = self:getFamily()
			local protoLang = fam and fam:getProtoLanguage() or nil
			
			-- For the case where the current language is the proto-language
			-- of its family, we need to step up a level higher right from the start.
			if protoLang and protoLang:getCode() == self:getCode() then
				fam = fam:getFamily()
				protoLang = fam and fam:getProtoLanguage() or nil
			end
			
			while not protoLang and not (not fam or fam:getCode() == "qfa-not") do
				fam = fam:getFamily()
				protoLang = fam and fam:getProtoLanguage() or nil
			end
			
			table.insert(self._ancestorObjects, protoLang)
		end
	end
	
	return self._ancestorObjects
end

local function iterateOverAncestorTree(node, func)
	for _, ancestor in ipairs(node:getAncestors()) do
		if ancestor then
			local ret = func(ancestor) or iterateOverAncestorTree(ancestor, func)
			if ret then
				return ret
			end
		end
	end
end

function Language:getAncestorChain()
	if not self._ancestorChain then
		self._ancestorChain = {}
		local step = #self:getAncestors() == 1 and self:getAncestors()[1] or nil
		
		while step do
			table.insert(self._ancestorChain, 1, step)
			step = #step:getAncestors() == 1 and step:getAncestors()[1] or nil
		end
	end
	
	return self._ancestorChain
end


function Language:hasAncestor(otherlang)
	local function compare(ancestor)
		return ancestor:getCode() == otherlang:getCode()
	end
	
	return iterateOverAncestorTree(self, compare) or false
end


function Language:getCategoryName(nocap)
	local name = self:getCanonicalName()
	
	-- If the name already has "language" in it, don't add it.
	if not name:find("[Ll]anguage$") then
		name = name .. " language"
	end
	if not nocap then
		name = mw.getContentLanguage():ucfirst(name)
	end
	return name
end


function Language:makeCategoryLink()
	return "[[:Category:" .. self:getCategoryName() .. "|" .. self:getDisplayForm() .. "]]"
end


function Language:getStandardCharacters()
	return self._rawData.standardChars
end


function Language:makeEntryName(text)
	text = mw.ustring.match(text, "^[¿¡]?(.-[^%s%p].-)%s*[؟?!;՛՜ ՞ ՟?!︖︕।॥။၊་།]?$") or text
	
	if self:getCode() == "ar" then
		local U = mw.ustring.char
		local taTwiil = U(0x640)
		local waSla = U(0x671)
		-- diacritics ordinarily removed by entry_name replacements
		local Arabic_diacritics = U(0x64B, 0x64C, 0x64D, 0x64E, 0x64F, 0x650, 0x651, 0x652, 0x670)
		
		if text == waSla or mw.ustring.find(text, "^" .. taTwiil .. "?[" .. Arabic_diacritics .. "]" .. "$") then
			return text
		end
	end
	
	if type(self._rawData.entry_name) == "table" then
		text = do_entry_name_or_sort_key_replacements(text, self._rawData.entry_name)
	end
	
	return text
end


-- Return true if the language has display processing enabled, i.e. lang:makeDisplayText()
-- does non-trivial processing.
function Language:hasDisplayProcessing()
	return not not self._rawData.display
end


-- Apply display-text replacements to `text`, if any.
function Language:makeDisplayText(text)
	if type(self._rawData.display) == "table" then
		text = do_entry_name_or_sort_key_replacements(text, self._rawData.display)
	end
	
	return text
end


-- Add to data tables?
local has_dotted_undotted_i = {
	["az"] = true,
	["crh"] = true,
	["gag"] = true,
	["kaa"] = true,
	["tt"] = true,
	["tr"] = true,
	["zza"] = true,
}

function Language:makeSortKey(name, sc)
	if has_dotted_undotted_i[self:getCode()] then
		name = name:gsub("I", "ı")
	end
	
	name = mw.ustring.lower(name)
	
	-- Remove initial hyphens and *
	local hyphens_regex = "^[-־ـ*]+(.)"
	name = mw.ustring.gsub(name, hyphens_regex, "%1")
	
	-- If there are language-specific rules to generate the key, use those
	if type(self._rawData.sort_key) == "table" then
		name = do_entry_name_or_sort_key_replacements(name, self._rawData.sort_key)
	elseif type(self._rawData.sort_key) == "string" then
		name = require("Module:" .. self._rawData.sort_key).makeSortKey(name, self:getCode(), sc and sc:getCode())
	end
	
	-- Remove parentheses, as long as they are either preceded or followed by something
	name = mw.ustring.gsub(name, "(.)[()]+", "%1")
	name = mw.ustring.gsub(name, "[()]+(.)", "%1")
	
	if has_dotted_undotted_i[self:getCode()] then
		name = name:gsub("i", "İ")
	end
	
	return mw.ustring.upper(name)
end

function Language:overrideManualTranslit()
	if self._rawData.override_translit then
		return true
	else
		return false
	end
end


function Language:transliterate(text, sc, module_override)
	if not ((module_override or self._rawData.translit_module) and text) then
		return nil
	end
	
	if module_override then
		require("Module:debug").track("module_override")
	end
	
	return require("Module:" .. (module_override or self._rawData.translit_module)).tr(text, self:getCode(), sc and sc:getCode() or nil)
end

function Language:hasTranslit()
	return self._rawData.translit_module and true or false
end


function Language:link_tr()
	return self._rawData.link_tr and true or false
end


function Language:toJSON()
	local entryNamePatterns = nil
	local entryNameRemoveDiacritics = nil
	
	if self._rawData.entry_name then
		entryNameRemoveDiacritics = self._rawData.entry_name.remove_diacritics
		if self._rawData.entry_name.from then
			entryNamePatterns = {}
			for i, from in ipairs(self._rawData.entry_name.from) do
				local to = self._rawData.entry_name.to[i] or ""
				table.insert(entryNamePatterns, { from = from, to = to })
			end
		end
	end
	
	local ret = {
		ancestors = self._rawData.ancestors,
		canonicalName = self:getCanonicalName(),
		categoryName = self:getCategoryName("nocap"),
		code = self._code,
		entryNamePatterns = entryNamePatterns,
		entryNameRemoveDiacritics = entryNameRemoveDiacritics,
		family = self._rawData[3] or self._rawData.family,
		otherNames = self:getOtherNames(true),
		aliases = self:getAliases(),
		varieties = self:getVarieties(),
		scripts = self._rawData.scripts or self._rawData[4],
		type = self:getType(),
		wikimediaLanguages = self._rawData.wikimedia_codes,
		wikidataItem = self:getWikidataItem(),
	}
	
	return require("Module:JSON").toJSON(ret)
end


-- Do NOT use these methods!
-- All uses should be pre-approved on the talk page!
function Language:getRawData()
	return self._rawData
end

function Language:getRawExtraData()
	self:loadInExtraData()
	return self._extraData
end

Language.__index = Language


function export.getDataModuleName(code)
	if code:find("^%l%l$") then
		return "languages/data2"
	elseif code:find("^%l%l%l$") then
		local prefix = code:sub(1, 1)
		return "languages/data3/" .. prefix
	elseif code:find("^[%l-]+$") then
		return "languages/datax"
	else
		return nil
	end
end


function export.getExtraDataModuleName(code)
	if code:find("^%l%l$") then
		return "languages/extradata2"
	elseif code:find("^%l%l%l$") then
		local prefix = code:sub(1, 1)
		return "languages/extradata3/" .. prefix
	elseif code:find("^[%l-]+$") then
		return "languages/extradatax"
	else
		return nil
	end
end


local function getRawLanguageData(code)
	local modulename = export.getDataModuleName(code)
	return modulename and mw.loadData("Module:" .. modulename)[code] or nil
end


local function getRawExtraLanguageData(code)
	local modulename = export.getExtraDataModuleName(code)
	return modulename and mw.loadData("Module:" .. modulename)[code] or nil
end


function Language:loadInExtraData()
	if not self._extraData then
		-- load extra data from module and assign to meta table
		-- use empty table as a fallback if extra data is nil
		local meta = getmetatable(self)
		meta._extraData = getRawExtraLanguageData(self._code) or {}
		setmetatable(self, meta)
	end
end


function export.makeObject(code, data)
	if data and data.deprecated then
		require("Module:debug").track {
			"languages/deprecated",
			"languages/deprecated/" .. code
		}
	end
	
	return data and setmetatable({ _rawData = data, _code = code }, Language) or nil
end


function export.getByCode(code, paramForError, allowEtymLang, allowFamily)
	if type(code) ~= "string" then
		error("The function getByCode expects a string as its first argument, but received " .. (code == nil and "nil" or "a " .. type(code)) .. ".")
	end
	
	local retval = export.makeObject(code, getRawLanguageData(code))
	if not retval and allowEtymLang then
		retval = require("Module:etymology languages").getByCode(code)
	end
	if not retval and allowFamily then
		retval = require("Module:families").getByCode(code)
	end
	if not retval and paramForError then
		require("Module:languages/errorGetBy").code(code, paramForError, allowEtymLang, allowFamily)
	end
	return retval
end


function export.getByName(name, errorIfInvalid)
	local byName = mw.loadData("Module:languages/by name")
	local code = byName.all and byName.all[name] or byName[name]
	
	if not code then
		if errorIfInvalid then
			error("The language name \"" .. name .. "\" is not valid. See [[Wiktionary:List of languages]].")
		else
			return nil
		end
	end
	
	return export.makeObject(code, getRawLanguageData(code))
end

function export.getByCanonicalName(name, errorIfInvalid, allowEtymLang, allowFamily)
	local byName = mw.loadData("Module:languages/canonical names")
	local code = byName and byName[name]

	local retval = code and export.makeObject(code, getRawLanguageData(code)) or nil
	if not retval and allowEtymLang then
		retval = require("Module:etymology languages").getByCanonicalName(name)
	end
	if not retval and allowFamily then
		local famname = name:match("^(.*) languages$")
		famname = famname or name
		retval = require("Module:families").getByCanonicalName(famname)
	end
	if not retval and errorIfInvalid then
		require("Module:languages/errorGetBy").canonicalName(name, allowEtymLang, allowFamily)
	end
	return retval
end

--[[	If language is an etymology language, iterates through parent languages
		until it finds a non-etymology language. ]]
function export.getNonEtymological(lang)
	while lang:getType() == "etymology language" do
		local parentCode = lang:getParentCode()
		lang = export.getByCode(parentCode)
			or require("Module:etymology languages").getByCode(parentCode)
			or require("Module:families").getByCode(parentCode)
	end
	
	return lang
end

-- for backwards compatibility only; modules should require the /error themselves
function export.err(lang_code, param, code_desc, template_tag, not_real_lang)
	return require("Module:languages/error")(lang_code, param, code_desc, template_tag, not_real_lang)
end

return export