Toggle menu
Toggle preferences menu
Toggle personal menu
Not logged in
Your IP address will be publicly visible if you make any edits.

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

require( 'strict' )

local i18n = {}

local metatable = {}
local methodtable = {}

metatable.__index = methodtable

local libraryUtil = require( 'libraryUtil' )
local checkType = libraryUtil.checkType

--- Cache table containing i18n data
--- e.g. cache['en']['SMW'] will get you the SMW table in English
local cache = {}

--- Cache language codes for reuse
local languages = {}


--- Retrieve dataset namespace from key prefix
---
--- @param key string The translation key
--- @return string
local function getNamespace( key )
    local namespace = string.match( key, '([^_]*)' )
    return namespace
end


--- Retrieve a list of applicable language codes
---
--- @return table
local function getLanguageCodes()
    if #languages > 0 then return languages end
    local mwlang = mw.language.getContentLanguage()
    local langCodes = { mwlang:getCode() }

    local fallbackLangCodes = mwlang:getFallbackLanguages()
    if next( fallbackLangCodes ) ~= nil then
        for _, fallbackLangCode in pairs( fallbackLangCodes ) do
            table.insert( langCodes, fallbackLangCode )
        end
    end

    --mw.log( string.format( '🌐 [i18n] Setting language chain: %s', table.concat( langCodes, '→' ) ) )
    return langCodes
end


--- Loads a dataset and saves it to the cache
---
--- @param lang string
--- @param namespace string
--- @return table|nil { data = "The dataset", keys = "Translation key mapped to index" }
local function load( lang, namespace )
    -- Init language cache if it does not exist
    if cache[ lang ] == nil then
        cache[ lang ] = {}
    end

    if cache[ lang ][ namespace ] then
        return cache[ lang ][ namespace ]
    end

    local datasetName = string.format( 'Module:i18n/%s/%s.json', namespace, lang )
    local success, data = pcall( mw.loadJsonData, datasetName )

    if not success then
        --mw.log( string.format( '🚨 [i18n] Loading dataset[%s][%s]: %s not found on wiki', lang, namespace, datasetName ) )
        -- Cache the empty result so we do not run mw.loadJsonData again
        cache[ lang ][ namespace ] = {}
        return
    end

    cache[ lang ][ namespace ] = data
    --mw.log( string.format( '⌛ [i18n] Loading dataset[%s][%s]: %s', lang, namespace, datasetName ) )

    return cache[ lang ][ namespace ]
end


--- Returns translated message (or key if returnKey is enabled)
---
--- @param key string The translation key
--- @param options table|nil Optional options
--- @return string|nil
function methodtable.translate( self, key, options )
    options = options or {
        ['returnKey'] = true
    }

    checkType( 'Module:i18n.translate', 1, self, 'table' )
    checkType( 'Module:i18n.translate', 2, key, 'string' )
    checkType( 'Module:i18n.translate', 3, options, 'table' )

    --mw.log( string.format( '🔍 [i18n] Looking for message: %s', key ) )

    local namespace = getNamespace( key )
    if namespace == nil then
        -- No namespace found error
        --mw.log( string.format( '❌ [i18n] Namespace cannot be found from: %s', key ) )
        if options['returnKey'] == true then
            return key
        else
            return
        end
    end

    languages = getLanguageCodes()

    local message
    local i = 1

    while ( message == nil and i <= #languages ) do
        local dataset = load( languages[ i ], namespace )
        if dataset then
            local match = dataset[ key ]
            if match then
                message = match
                --mw.log( string.format( '✅ [i18n] Found message: %s', message ) )
            end
        end
        i = i + 1
    end

    if message == nil then
        --mw.log( string.format( '❌ [i18n] Could not found message: %s', key ) )
        if options['returnKey'] == true then
            message = key
        end
    end

    return message
end


--- Normalize a string to be used as an i18n key by converting whitespace to underscores and making all characters lowercase
---
--- @param str string The string to normalize
--- @return string
function methodtable.normalizeKey( self, str )
    checkType( 'Module:i18n.normalizeKey', 1, self, 'table' )
    checkType( 'Module:i18n.normalizeKey', 2, str, 'string' )

    return string.lower( string.gsub( str, '%s+', '_' ) )
end


--- New Instance
---
--- @return table i18n
function i18n.new( self )
    local instance = {}

    setmetatable( instance, metatable )

    return instance
end


return i18n