Modul:GetString/Test

Aus Wikivoyage
Dokumentation für das Modul GetString/Test[Ansicht] [Bearbeiten] [Versionsgeschichte] [Aktualisieren]

Verwendung

Das Submodul ist eine Testversion des Moduls GetString und nicht für den Produktivbetrieb geeignet. Es dient der Weiterentwicklung des Moduls GetString, damit sich Änderungen nicht auf alle Artikel auswirken, die das Modul benutzen.

Das Modul ist eine Spielwiese und nicht zum produktiven Einsatz vorgesehen. Der derzeitige Code ist eine finale Testversion. Er enthält für Analysezwecke zusätzliche Wikidata-Abfragen und Ausgaben.

Diverse Properties

Beispiele:

  • Sabah (Postleitzahl, ein Wert auf WD): {{#invoke:GetString/Test|getStrings|Q179029|P281}} – 88000–91999
  • Sabah (Vorwahl, ein Wert auf WD): {{#invoke:GetString/Test|getStrings|Q179029|P473}} – 087-089
  • Chiang Mai (Postleitzahl, mehrere Werte auf WD): {{#invoke:GetString/Test|getStrings|Q233588|P281}} – 50000, 50110, 50120, 50130, 50140, 50150, 50160, 50170, 50180, 50190, 50210, 50220, 50230, 50240, 50250, 50260, 50270, 50280, 50310, 50320, 50350, 50360
  • Chiang Mai (Postleitzahl, mehrere Werte auf WD, nur einen ausgeben): {{#invoke:GetString/Test|getStrings|Q233588|P281|single}} – 50000
  • Chiang Mai (Postleitzahl, mehrere Werte auf WD, anderes Trennzeichen): {{#invoke:GetString/Test|getStrings|Q233588|P281||| - }} – 50000 - 50110 - 50120 - 50130 - 50140 - 50150 - 50160 - 50170 - 50180 - 50190 - 50210 - 50220 - 50230 - 50240 - 50250 - 50260 - 50270 - 50280 - 50310 - 50320 - 50350 - 50360
  • Chiang Mai (Postleitzahl, mehrere Werte auf WD, bei mehr als 3 einen Bereich ausgeben): {{#invoke:GetString/Test|getStrings|Q233588|P281||||3}} – 50000…50360
  • Chiang Mai (Vorwahl, mehrere Werte auf WD): {{#invoke:GetString/Test|getStrings|Q233588|P473}} – 52, 53
  • Wat Phra Singh (Name in Amts- oder Originalsprache): {{#invoke:GetString/Test|getStrings|Q1657130|P1705}} – วัดพระสิงห์วรมหาวิหาร
  • Wat Phra Singh (Name in Amts- oder Originalsprache + Sprachangabe): {{#invoke:GetString/Test|getStrings|Q1657130|P1705||text_with_language}} – วัดพระสิงห์วรมหาวิหาร (th)
  • Glas-Hotel (alle Adressen + Sprachangabe): {{#invoke:GetString/Test|getStrings|Q95795329|P6375||text_with_language|<br />}}
    • Wincentego Witosa 50, 68-200 Żary, Polen (de-ch)
      Wincentego Witosa 50, 68-200 Żary, Polen (de)
      Wincentego Witosa 50, 68-200 Żary, Poland (en)
      Wincentego Witosa 50, 68-200 Żary, Polska (pl)
      Wincentego Witosa 50, 68-200 Żary, Polen (de-at)
  • Glas-Hotel (Telefonnummer): {{#invoke:GetString/Test|getStrings|Q95795329|P1329}}
  • Raffles Hotel (Telefonnummer): {{#invoke:GetString/Test|getStrings|Q1538837|P1329}}

Social Media:

  • Facebook-Profilkennung (Text): {{#invoke:GetString/Test|getStrings|Q303521|P2013}} – %C3%98ksnes-Kommune-502985983155281, Vik-kommune-119583634837133
  • Facebook-Profilkennung (Symbol): {{#invoke:GetString/Test|getStrings|Q303521|P2013||symbol_with_link}} ,
  • Facebook-Seite (Text): {{#invoke:GetString/Test|getStrings|Q28006122|P4003}} – Norra-begravningsplatsen/102511286468672
  • Facebook-Seite (Symbol): {{#invoke:GetString/Test|getStrings|Q28006122|P4003||symbol_with_link}}
  • Facebook-Numeric-ID (Text): {{#invoke:GetString/Test|getStrings|Q124877748|P11705}} – 100068861146457
  • Facebook-Numeric-ID (Symbol): {{#invoke:GetString/Test|getStrings|Q124877748|P11705||symbol_with_link}}
  • Facebook-Places-ID (Text): {{#invoke:GetString/Test|getStrings|Q38555|P1997}} – 112889718724492
  • Facebook-Places-ID (Symbol): {{#invoke:GetString/Test|getStrings|Q38555|P1997||symbol_with_link}}
  • Instagram-Benutzername (Text): {{#invoke:GetString/Test|getStrings|Q1538837|P2003}} – raffleshotelsingapore
  • Instagram-Benutzername (Symbol): {{#invoke:GetString/Test|getStrings|Q1538837|P2003||symbol_with_link}}
  • X-Benutzername (Text): {{#invoke:GetString/Test|getStrings|Q1538837|P2002}} – RafflesHotelSIN
  • X-Benutzername (Symbol): {{#invoke:GetString/Test|getStrings|Q1538837|P2002||symbol_with_link}}
  • YouTube-Kanal von Jimmy Wales (Text): {{#invoke:GetString/Test|getStrings|Q181|P2397}} – UCEbFsO2sM_wjTn44YAgSrrg
  • YouTube-Kanal von Jimmy Wales (Symbol): {{#invoke:GetString/Test|getStrings|Q181|P2397||symbol_with_link}}
  • YouTube-Kanal von Basshunter (Text): {{#invoke:GetString/Test|getStrings|Q383541|P2397}} – UCAHZJOFVlIRuaZfX3xvN5aw, UCKT4B4C8QAnZV5NpvmykcLA, UCXUCegBr2GL7mo6O1l-xvVw, UCXgtKN-w_WZvMPfqOWy_BsQ, UCmm1ImM6ZlyYk5rUbbEdM3w
  • YouTube-Kanal von Basshunter (Symbol): {{#invoke:GetString/Test|getStrings|Q383541|P2397||symbol_with_link}} , , , ,
  • Hashtag von 1Lib1ref (Text): {{#invoke:GetString/Test|getStrings|Q22080614|P2572}} – 1lib1ref
  • Hashtag von 1Lib1ref (Symbol): {{#invoke:GetString/Test|getStrings|Q22080614|P2572||symbol_with_link}}
  • TikTok-Place-ID von Aachen (Text): {{#invoke:GetString/Test|getStrings|Q1017|P11559}} – 22535865202610466
  • TikTok-Place-ID von Aachen (Symbol): {{#invoke:GetString/Test|getStrings|Q1017|P11559||symbol_with_link}}
  • TikTok-Benutzername von Ariana Grande (Text): {{#invoke:GetString/Test|getStrings|Q151892|P7085}} – arianagrande
  • TikTok-Benutzername von Ariana Grande (Symbol): {{#invoke:GetString/Test|getStrings|Q151892|P7085||symbol_with_link}}
  • Flickr-Benutzerkennung von Ian Emes (Text): {{#invoke:GetString/Test|getStrings|Q5981474|P3267}} – ianemes
  • Flickr-Benutzerkennung von Ian Emes (Symbol): {{#invoke:GetString/Test|getStrings|Q5981474|P3267||symbol_with_link}}

Quickbars

Sabah
Postleitzahl88000–91999
Vorwahl087-089
Vorwahl087
Vorwahl087-089
Chiang Mai
Postleitzahl50000…50360
Vorwahl52, 53
Wat Phra Singh
Name (lokal)วัดพระสิงห์วรมหาวิหาร
Raffles Hotel
Telefonnummer+800 17 23 35 37
Telefonnummer+6563371886


Test WD-Abgleich anhand Hà Giang (Provinz). Daten fehlen auf WD > sollten nicht als unterschiedlich dargestellt werden. Es sind zwei PLZ-Zeilen eingebunden, die zweite sollte fehlen, solange auf WD nichts angegeben ist.

Hà Giang
Fläche7.884,3 km²
Einwohnerzahl660.700
Postleitzahl019
Postleitzahl
Webseitewww.hagiang.gov.vn


Unterdrückung Anzeige anhand Hà Giang (Provinz).

Hà Giang
Postleitzahlnein
Webseitewww.hagiang.gov.vn

Hinweise
--[=[ getString 2023-10-04
* local getStringsWithLanguage (internal use)
* getStrings
* getStringsQuickbar
* getSocialMediaQuickbar
]=]
local GetString = {}

-- maximum number of entries in quickbar
local maxDisplayQuickbar = 3

-- categories and properties for data evaluation
local gsp = require ( 'Modul:GetString/Properties' )

-- modules for linking and formatting phone number
-- loaded later,if necessary
local cm
local lp
local args = {}

-- returns nil, if both values are equal, otherwise the value
-- similar to the SQL function nullif()
local function nilIf ( value, equalValue )

   if ( value == nil ) then
      return nil
   elseif ( tostring ( value ) == tostring ( equalValue ) ) then
      return nil
   else
      return value
   end

end

-- returns the first value that is not nil
-- similar to the SQL function coalesce()
local function coalesce ( value1, value2, value3 )
   return value1 or value2 or value3
end


-- getStringsWithLanguage() -> just for internal use. it delivers the url to the public functions
-- gets text of a property
-- it supports the datatypes "string", "monolingualtext" and "external-id"
--   id: Wikidata-ID (own, if not provided)
--   property: requested property
local getStringsWithLanguage = function ( id, property )

   -- local variables
   -- ID of the item
   -- Determined, if not provided
   local localID = id or mw.wikibase.getEntityIdForCurrentPage() or ''
   
   -- compatibility to existing modules and templates:
   -- some use the keyword "self" for using the own entity-ID
   if ( ( localID == 'self' ) or ( localID == '' ) ) then
      localID = mw.wikibase.getEntityIdForCurrentPage() or ''
   end

   -- no Wikidata object
   if localID == '' then
      return {}, '', ''
   end
   
   -- maintenance categories
   local maintenanceCategory = ''

   -- property
   local requestedProperty = property or 'none';
   if string.sub(requestedProperty,1,1) ~= 'P' then
      requestedProperty = 'none'
   end

   -- no property given: category and exit
   if requestedProperty == 'none' then
      maintenanceCategory = maintenanceCategory .. gsp.noProperty
      return {}, localID, maintenanceCategory
   end

   -- property unknown in table "gsp": category but continue
   if not gsp[requestedProperty] then
      maintenanceCategory = maintenanceCategory .. gsp.unknownProperty
   end

   -- phone numner? load the linkphone modules
   if requestedProperty == 'P1329' then
      cm = require( 'Module:CountryData' )
      lp = require( 'Module:LinkPhone' )
   end

   -- getting the values
   local wdStatements = mw.wikibase.getBestStatements( localID, requestedProperty )

   -- running through the array and store it in a simple table
   local wdValues = {}
   local language = ''
   for i, entry in pairs ( wdStatements ) do

      -- check for string
      if entry.mainsnak.datatype == 'string' then

         -- phone numner? then format and link it
         -- and adding to the list
         if requestedProperty == 'P1329' then
            args = {
               phone      = entry.mainsnak.datavalue.value,
               format     = true,
               isFax      = false,
               isTollfree = false
            }
            args.cc, args.size = cm.getCountryCode()
            wdValues[i] = lp.linkPhoneNumbers( args )
         else
            wdValues[i] = entry.mainsnak.datavalue.value
         end

      end -- if entry.mainsnak.datatype == 'string'

      -- check for monolingualtext
      if entry.mainsnak.datatype == 'monolingualtext' then

         -- getting the amount, converted into a number
         language = entry.mainsnak.datavalue.value.language or i

         -- adding to the list
         if not wdValues[language] then
            wdValues[language] = entry.mainsnak.datavalue.value.text
         end

      end -- if entry.mainsnak.datatype == 'monolingualtext'

      -- check for external-id
      if entry.mainsnak.datatype == 'external-id' then

         wdValues[i] = entry.mainsnak.datavalue.value

      end -- if entry.mainsnak.datatype == 'external-id'

      language = ''

   end -- for i, entry in ipairs ( wdStatements )

   -- adding category
   if #wdValues > 0 then
      maintenanceCategory = maintenanceCategory .. '[[Kategorie:Seiten, die die Wikidata-Eigenschaft ' .. requestedProperty .. ' benutzen]]'
   end


   table.sort( wdValues, function(a,b) return a < b end )

   return wdValues, localID, maintenanceCategory
 
end


-- getStrings() 
-- gets the text of a property including its language
--   first parameters: see internal function
--   values:
--      - single: Only one (first) entry is shown
--      - all (standard): All entries ar shown
--   show:
--      - text (standard): just the text
--      - text_with_language: text with language in brackets
--      - symbol_with_link: for social media: displays the link as symbol
--   delimiter: delmiter between the numbers; standard is comma and breakable space
--   max: if the number of rows exceeds the given maximum, it will be displayed as a range
GetString.getStrings = function ( id, property, values, show, delimiter, max )

   -- returning String
   local returnString = ''

   -- values
   local requestedValues = values or 'all'

   -- WD-Values
   local stringList = {}
   local wikidataID = ''
   local categories = ''
   stringList, wikidataID, categories = getStringsWithLanguage ( id, property, values )

   -- show options
   local stringShow = coalesce ( nilIf ( show, '' ), 'text' )

   -- no delimiter in front of the first entry
   local stringDelimiter = ''

   -- maximum
   local maxDisplay = tonumber( max ) or 0
   local showRange = false

   -- displaying the values
   local allValues = true
   local numberOfRows = #stringList
   if ( maxDisplay > 0 ) and ( numberOfRows > maxDisplay ) then
      showRange = true
   end
   local rowNumber = 1
   for key, value in pairs( stringList ) do

      if allValues then

         if showRange and ( rowNumber + 1 == numberOfRows ) then

            returnString = returnString .. '…'

         elseif  not ( showRange ) or ( rowNumber == 1 ) or ( rowNumber == numberOfRows ) then

            -- displaying with language
            if stringShow == 'text_with_language' then

               if key == '1' then
                  returnString = returnString .. stringDelimiter .. value
               else
                  returnString = returnString .. stringDelimiter .. value .. ' (' .. key .. ')'
               end

            -- displaying social media with string
            elseif stringShow == 'symbol_with_link' then

               local span = mw.html.create ( 'span' )
               local href = ''


               -- property specific classes and links
               if gsp[property] ~= nil then

                  if gsp[property].cssSymbolClass ~= nil then
                     span:addClass('voy-social-media voy-social-media-icon ' .. gsp[property].cssSymbolClass)
                  end

                  if gsp[property].link ~= nil then
                     span:wikitext('[' .. mw.ustring.format ( gsp[property].link, value ) .. ' <span title="' .. gsp[property].label .. '" data-icon="facebook" style="color-adjust:exact;-webkit-print-color-adjust:exact;print-color-adjust:exact"> </span>]' )
                  else
                     span:wikitext(value)
                     categories = categories .. '[[Kategorie:Social-Media-Link nicht konfiguriert]]'
                  end

                  returnString = returnString .. stringDelimiter .. tostring(span)

               end

            -- displaying just the text
            else 
               returnString = returnString .. stringDelimiter .. value
            end
            
            -- setting the delimiter after the first entry
            if not ( showRange ) then
               stringDelimiter = delimiter or ', '
            end

            -- Abbruch?
            if requestedValues == 'single' then
               allValues = false
            end

         end

      end

      rowNumber = rowNumber + 1

   end

   -- providing the list of capitals
   return returnString .. categories

end


-- Get the string as wiki markup for quickbars (infoboxes)
-- "values" is set to "all", if not provided
-- "label" is an optional text, shwon as key in the left side of the quickbar entry
GetString.getStringsQuickbar = function ( id, property, values, wikiValue, label )

   -- checking property
   if coalesce ( property, '' ) == '' then
      return ''
   end
   
   -- getting the link with the base function above
   local wikidataID
   local wikidataStrings = {}
   local wikidataCategories
   wikidataStrings, wikidataID, wikidataCategories = getStringsWithLanguage ( id, property )
   
   -- if it is taken from wikidata, this variable provides an additional class for: MediaWiki:Gadget-Wikidata-Content.css
   -- space is needed becaus its simply added to the existing class string
   local wikidataClass = ' voy-wikidata-content'

   -- comparing a possibly given value with Wikidata
   local category = ''

   -- is Wikidata value not available?
   local wikidataNoData = false
   if wikidataID == '' then
      if gsp[property] ~= nil then
         category = gsp[property].noData
         wikidataNoData = true
      end
      wikidataClass = ''
   end

   -- values
   local requestedValues = coalesce ( nilIf ( values, '' ), 'all' )

   -- creating text for display
   local returnString = ''
   local stringDelimiter = ''
   local allValues = true

   -- maximum
   local showRange = false

   -- Wikidate comparision tags
   -- is Wikidata available
   local wikidataAvailable = true

   -- is one of the entries equal to the wiki value?
   local wikidataOneIsEqual = false
   local wikidataEqualData = false

   -- are none of the entries equal to the wiki value?
   -- starting with false
   -- later initialised to true, when local wiki value is given
   local wikidataNothingIsEqual = false
   local wikidataNoEqualData = false

   -- processing nil value for wikiValue
   if wikiValue == nil then 
      wikiValue = '' 
   end
   
   -- wikivalues, that can be interpreted as "TRUE" are removed and Wikidata is used
   -- it's used to switch entries on and off in Quickbars
   if wikiValue =='yes'
      or wikiValue == 'y'
      or wikiValue == 'true'
      or wikiValue == 'wahr'
      or wikiValue == 'ja'
      or wikiValue == 'j'
   then
      wikiValue = ''
   end

   -- processing wikiValue
   if wikiValue ~= '' then
      -- starting with TRUE by default
      -- set to FALSE if one value is equal
      wikidataNothingIsEqual = true
      wikidataNoEqualData = true
   end

   -- displaying the values and compare with Wikidata
   local allValues = true
   local numberOfRows = #wikidataStrings
   if ( maxDisplayQuickbar > 0 ) and ( numberOfRows > maxDisplayQuickbar ) then
      showRange = true
   end
   local rowNumber = 1
   for key, value in pairs( wikidataStrings ) do

      if allValues then

         if showRange and ( rowNumber + 1 == numberOfRows ) then

            returnString = returnString .. '…'

         elseif  not ( showRange ) or ( rowNumber == 1 ) or ( rowNumber == numberOfRows ) then

            returnString = returnString .. stringDelimiter .. value
            
            -- setting the delimiter after the first entry
            if not ( showRange ) then
               stringDelimiter = ', '
            end

            -- Abbruch?
            if requestedValues == 'single' then
               allValues = false
            end

         end

      end

      -- comparing with Wikidata
      if wikiValue ~= '' then
         if value == wikiValue then
            wikidataOneIsEqual = true
         end
      end

      rowNumber = rowNumber + 1

   end

   -- phone numner? then format and link it
   if wikiValue ~= '' and coalesce ( property, '' ) == 'P1329' then
      cm = require( 'Module:CountryData' )
      lp = require( 'Module:LinkPhone' )
      args = {
         phone      = wikiValue,
         format     = true,
         isFax      = false,
         isTollfree = false
      }
      args.cc, args.size = cm.getCountryCode()
      wikiValue = lp.linkPhoneNumbers( args )
   end

   -- saving wikidata value in case of a local string is used
   local wikidataString = returnString

   -- do not state "different values", when no wikidata value is available
   if numberOfRows == 0 then
      wikidataNoEqualData = false
      wikidataClass = ''
   end

   -- Setting Wikidata categories
   if wikiValue ~= '' then

      -- setting the categories
      if gsp[property] ~= nil then

         if wikidataOneIsEqual then 
            category = category .. gsp[property].equalData
            wikidataEqualData = true
         end

         if wikidataNothingIsEqual and numberOfRows > 0 then 
            category = category .. gsp[property].noEqualData
            wikidataNoEqualData = true
         end

      end

      -- displaying the wikiValue
      returnString = wikiValue

      -- removing the WD-Class
      wikidataClass = ''

   end

   -- displaying only, if any data available
   local trClass = ''
   if returnString == '' then
      trClass = ' voy-qb-empty'
   end

   -- creating the table row 
   local tr = mw.html.create ( 'tr' )
   
   -- first table cell (heading)
   local trLabel = label or '&nbsp;'
   if gsp[property] then
        trClass = ' ' .. gsp[property].cssQBClass .. trClass
        if trLabel == '&nbsp;' then
           trLabel = gsp[property].label
        end
   end
   tr:addClass('voy-qb-item' .. trClass )
      :tag('th')
      :addClass('voy-qb-item-key')
      :wikitext(trLabel)
   
   -- second table cell (the requested number)
   if wikidataNoData then
      tr:tag('td')
         :addClass( 'voy-qb-item-value1 voy-qb-item-value-nowikidata' .. wikidataClass )
            :attr('data-wikidata-id',wikidataID)
            :attr('title','Kein(e) ' .. trLabel .. ' auf Wikidata' )
         :wikitext( returnString )
   elseif wikidataEqualData then
      tr:tag('td')
            :addClass( 'voy-qb-item-value1 voy-qb-item-value-wikidata-equal' .. wikidataClass )
            :attr('data-wikidata-id',wikidataID)
            :attr('data-wikidata-value',wikidataString)
            :attr('title','Daten identisch - Angabe auf Wikidata: ' .. wikidataString)
            :wikitext( returnString )
   elseif wikidataNoEqualData then
      tr:tag('td')
            :addClass( 'voy-qb-item-value1 voy-qb-item-value-wikidata-noequal' .. wikidataClass )
            :attr('data-wikidata-id',wikidataID)
            :attr('data-wikidata-value',wikidataString)
            :attr('title','Daten unterschiedlich - Angabe auf Wikidata: ' .. wikidataString )
            :wikitext( returnString )
   else
      tr:tag('td')
            :addClass( 'voy-qb-item-value1' .. wikidataClass )
            :attr('data-wikidata-id',wikidataID)
            :attr('data-wikidata-value',wikidataString)
            :wikitext( returnString )
   end

   -- returning the row
   return tostring ( tr ) .. category .. wikidataCategories
   
end




-- Gets the socialmedia-Links and creates a quickbar row
GetString.getSocialMediaQuickbar = function ( id )

   -- creating the wikitext for the row
   -- facebook profile
   local socialmedia = coalesce ( GetString.getStrings ( id, 'P2013', 'all', 'symbol_with_link' ) )

   -- facebook page
   socialmedia = socialmedia .. coalesce ( GetString.getStrings ( id, 'P4003', 'all', 'symbol_with_link' ) )

   -- facebook places id
   socialmedia = socialmedia .. coalesce ( GetString.getStrings ( id, 'P1997', 'all', 'symbol_with_link' ) )

   -- instagram username
   socialmedia = socialmedia .. coalesce ( GetString.getStrings ( id, 'P2003', 'all', 'symbol_with_link' ) )

   -- X username
   socialmedia = socialmedia .. coalesce ( GetString.getStrings ( id, 'P2002', 'all', 'symbol_with_link' ) )

   -- YouTube-Kanal
   socialmedia = socialmedia .. coalesce ( GetString.getStrings ( id, 'P2397', 'all', 'symbol_with_link' ) )

   -- Hashtag
   socialmedia = socialmedia .. coalesce ( GetString.getStrings ( id, 'P2572', 'all', 'symbol_with_link' ) )

   -- creating only, when any social media is available
   if socialmedia ~= '' then

      -- creating the table row 
      local tr = mw.html.create ( 'tr' )

      tr:addClass('voy-qb-item voy-qb-item-social-media' )
         :tag('th')
         :addClass('voy-qb-item-key')
         :wikitext( 'Soziale Medien' )

      tr:tag('td')
         :addClass( 'voy-qb-item-value1' )
         :wikitext(socialmedia)

      -- returning the row
      return tostring ( tr )

   else -- socialmedia == '' then

      return ''

   end -- if socialmedia ~= '' then

end

-- Providing template access
local p = {}

function p.getStrings( frame )
   return GetString.getStrings( frame.args[ 1 ], frame.args[ 2 ], frame.args[ 3 ], frame.args[ 4 ], frame.args[ 5 ], frame.args[ 6 ] ) or ""
end

function p.getStringsQuickbar( frame )
   return GetString.getStringsQuickbar( frame.args[ 1 ], frame.args[ 2 ], frame.args[ 3 ], frame.args[ 4 ] ) or ""
end

function p.getSocialMediaQuickbar( frame )
   return GetString.getSocialMediaQuickbar( frame.args[ 1 ] ) or ""
end

-- for usage in other modules
-- using it the following way:
--
-- local GetString = require( 'Module:GetString' )
-- foo = getString.GetString().xxx( id, lang )
function p.GetString()
   return GetString
end

return p