More actions
Documentation for this module may be created at Module:Translate/doc
require( 'strict' ) local Translate = {} local metatable = {} local methodtable = {} metatable.__index = methodtable local libraryUtil = require( 'libraryUtil' ) local checkType = libraryUtil.checkType local i18n = require( 'Module:i18n' ):new() --- Cache table containing i18n data at the 'data' key, and a key map at the 'keys' key --- The table is keyed by the i18n.json module name local cache = {} local i18nDataset = 'Module:Translate/i18n.json' --- Uses the current title as the base and appends '/i18n.json' --- --- @param dataset table --- @return string|nil local function guessDataset( dataset ) if mw.ustring.find( dataset, ':', 1, true ) then return dataset elseif type( dataset ) == 'string' then return mw.ustring.format( 'Module:%s/i18n.json', dataset ) end return nil end --- Loads a dataset and saves it to the cache --- --- @param dataset string --- @return table { data = "The dataset", keys = "Translation key mapped to index" } local function load( dataset ) if cache[ dataset ] ~= nil then return cache[ dataset ] end local data = mw.loadJsonData( dataset ) local keys = {} for index, row in ipairs( data.data ) do keys[ row[ 1 ] ] = index end cache[ dataset ] = { data = data, keys = keys } return cache[ dataset ] end --- Retrieves a message from a dataset and formats it according to parameters and language --- --- @param dataset string --- @param key string --- @param params table --- @param lang string local function formatMessage( dataset, key, params, lang ) local data = load( dataset ) if data.keys[ key ] == nil then error( formatMessage( i18nDataset, 'error_bad_msgkey', { key, dataset }, mw.getContentLanguage():getCode() ) ) end local msg = data.data.data[ data.keys[ key ] ][ 2 ] if msg == nil then error( formatMessage( i18nDataset, 'error_bad_msgkey', { key, dataset }, mw.getContentLanguage():getCode() ) ) end msg = msg[ lang ] or error( mw.ustring.format( 'Language "%s" not found for key "%s"', lang, key ) ) local result = mw.message.newRawMessage( msg, unpack( params or {} ) ) return result:plain() end --- Translates a message --- --- @param dataset string --- @param key string --- @return string function methodtable.format( dataset, key, ... ) dataset = guessDataset( dataset ) checkType('format', 1, dataset, 'string') checkType('format', 2, key, 'string') local lang = mw.getContentLanguage():getCode() return formatMessage( dataset, key, {...}, lang ) end --- Translates a message in a given language --- --- @param lang string --- @param dataset string --- @param key string --- @return string function methodtable.formatInLanguage( lang, dataset, key, ... ) dataset = guessDataset( dataset ) checkType('formatInLanguage', 1, lang, 'string') checkType('formatInLanguage', 2, dataset, 'string') checkType('formatInLanguage', 3, key, 'string') return formatMessage( dataset, key, {...}, lang ) end --- Wrapper function for Translate.getTemplateData that wraps the output in a <templatedata> tag --- --- @param frame table Current MW Frame --- @return string function Translate.doc( frame ) local dataset = frame.args[ 1 ] or frame.args[ 'dataset' ] or ( mw.title.getCurrentTitle().prefixedText .. '/i18n.json' ) return frame:extensionTag( 'templatedata', Translate.getTemplateData( dataset ) ) end --- Base logic taken from Module:TNT --- Iterates through all user settable arguments and outputs json usable in a <templatedata> tag --- --- @param dataset string The data.json page from which the arguments are taken --- @return string Json function Translate.getTemplateData( dataset ) local data = load( guessDataset( dataset ) ) local instance = Translate:new( dataset ) local names = {} for _, field in ipairs( data.data.schema.fields ) do table.insert( names, field.name ) end local numOnly = true local params = {} local paramOrder = {} for _, row in ipairs( data.data.data ) do local newVal = {} local name if row[ 1 ]:sub( 1, 3 ) == 'ARG' then for pos, columnName in ipairs( names ) do if columnName == 'id' then name = instance.format( dataset, row[ pos ] ) elseif columnName ~= 'message' then newVal[ columnName ] = row[ pos ] -- Allow to share examples and label if ( columnName == 'example' or columnName == 'label' ) and type( row[ pos ] ) == 'string' then newVal[ columnName ] = { de = row[ pos ], en = row[ pos ], } end end end if name and newVal[ 'type' ] ~= nil then if type( name ) ~= "number" and ( type( name ) ~= "string" or not mw.ustring.match( name, "^%d+$" ) ) then numOnly = false end params[ name ] = newVal table.insert( paramOrder, name ) -- TODO: Limit this to a specified subset of url args if row[ 1 ]:sub( -3 ) == 'Url' then for i = 1, 4 do local tmp = {} for k, v in pairs( newVal ) do if type( v ) == 'table' then tmp[ k ] = {} for k1, v1 in pairs( v ) do tmp[ k ][ k1 ] = v1 end else tmp[ k ] = v end end local nameI = name .. tostring( i ) tmp[ 'required' ] = false tmp[ 'suggested' ] = false params[ nameI ] = tmp table.insert( paramOrder, nameI ) end end end end end -- Work around json encoding treating {"1":{...}} as an [{...}] if numOnly then params['zzz123']='' end local json = mw.text.jsonEncode({ params = params, paramOrder = paramOrder, description = data.template_description, }) if numOnly then json = mw.ustring.gsub( json,'"zzz123":"",?', "" ) end return json end --- Calls TNT with the given key --- --- @param dataset string The i18n.json page --- @param config table The calling module's config --- @param key string The translation key --- @param addSuffix boolean|nil Adds a language suffix if config.smw_multilingual_text is true --- @return string If the key was not found in the .tab page, the key is returned function methodtable.translate( self, dataset, config, key, addSuffix, ... ) checkType( 'Module:Translate.translate', 1, self, 'table' ) checkType( 'Module:Translate.translate', 2, dataset, 'string' ) checkType( 'Module:Translate.translate', 3, config, 'table' ) checkType( 'Module:Translate.translate', 4, key, 'string' ) checkType( 'Module:Translate.translate', 5, addSuffix, 'boolean', true ) -- Temporary fallback function local prefix = string.match( key, '([^_]*)' ) if prefix == 'SMW' or prefix == 'label' or prefix == 'message' or prefix == 'category' then return i18n:translate( key ) end addSuffix = addSuffix or false local success, translation local function multilingualIfActive( input ) if addSuffix and config.smw_multilingual_text == true then return mw.ustring.format( '%s@%s', input, config.module_lang or mw.getContentLanguage():getCode() ) end return input end if config.module_lang ~= nil then success, translation = pcall( self.formatInLanguage, config.module_lang, dataset, key or '', ... ) else success, translation = pcall( self.format, dataset, key or '', ... ) end if not success or translation == nil then local title = mw.title.new( guessDataset( dataset ) ) if not title.exists then error( mw.ustring.format( 'I18N table "%s" does not exist!', dataset ), 3 ) end return nil end return multilingualIfActive( translation ) end --- New Instance --- --- @return table Translate function Translate.new( self, dataset ) local instance = { dataset = dataset or nil } setmetatable( instance, metatable ) return instance end return Translate