「
モジュール:AgeEstimator
」を編集中
ナビゲーションに移動
検索に移動
警告:
ログインしていません。編集を行うと、あなたの IP アドレスが公開されます。
ログイン
または
アカウントを作成
すれば、あなたの編集はその利用者名とともに表示されるほか、さまざまなメリットもあります。
スパム攻撃防止用のチェックです。 決して、ここには、値の入力は
しない
でください!
-- Module:AgeEstimator local p = {} local function trim(s) if s == nil then return '' end s = tostring(s) return mw.ustring.gsub(s, '^%s*(.-)%s*$', '%1') end -- 実質的な情報を含まない値(プレースホルダー)かどうか local function isPlaceholder(s) s = trim(s) if s == '' then return true end if mw.ustring.match(s, '^[%-_%s%.•・、。,,.-ー–—// ]+$') then return true end local lower = mw.ustring.lower(s) local nolist = {'不明','なし','無し','未知','未定','未詳','不詳', 'unknown','n/a','none','tbd','null','nil'} for _, w in ipairs(nolist) do if lower == w then return true end end return false end local APPROX = { '前後','頃','ごろ','くらい','ぐらい','弱','強','約', 'およそ','大体','ほぼ','辺り','あたり','付近', '大约','约','左右','前后','上下', '약','정도','쯤','경', 'примерно','около','приблизительно','почти','с лишним', 'approximately','around','about','circa','-ish','or so', 'حوالي','نحو','قرابة','تقريبا', } local EARLY = { '初め','初期','初頭','前半','上期','上半期','上半','前段','前叶','前葉', '초반','초기','상반기', 'начало','начале','первая','первой','ранн', 'early','first','1st', 'المبكر','الأول', } local MID = { '中頃','中ごろ','半ば','中盤','中期','中葉', '중반','중기', 'середина','середине','средн', 'mid','middle', 'الأوسط','منتصف', } local LATE = { '終わり','末期','終期','晩期','最末期','末','後半','下期','下半期','下半','後期', '后期','末期','末','后半', '후반','후기','하반기', 'конец','конце','конца','поздн','последн','вторая','второй', 'late','last','second','2nd', 'المتأخر','الأخير','الثاني', } local MONTH_IN = { january=1, february=2, march=3, april=4, may=5, june=6, july=7, august=8, september=9, october=10, november=11, december=12, jan=1, feb=2, mar=3, apr=4, jun=6, jul=7, aug=8, sep=9, sept=9, oct=10, nov=11, dec=12, ['января']=1, ['февраля']=2, ['марта']=3, ['апреля']=4, ['мая']=5, ['июня']=6, ['июля']=7, ['августа']=8, ['сентября']=9, ['октября']=10, ['ноября']=11, ['декабря']=12, ['январь']=1, ['февраль']=2, ['март']=3, ['апрель']=4, ['май']=5, ['июнь']=6, ['июль']=7, ['август']=8, ['сентябрь']=9, ['октябрь']=10, ['ноябрь']=11, ['декабрь']=12, ['يناير']=1, ['فبراير']=2, ['مارس']=3, ['أبريل']=4, ['ابريل']=4, ['مايو']=5, ['يونيو']=6, ['يوليو']=7, ['أغسطس']=8, ['اغسطس']=8, ['سبتمبر']=9, ['أكتوبر']=10, ['اكتوبر']=10, ['نوفمبر']=11, ['ديسمبر']=12, } local MONTH_OUT = { en = {'January','February','March','April','May','June', 'July','August','September','October','November','December'}, ru = {'января','февраля','марта','апреля','мая','июня', 'июля','августа','сентября','октября','ноября','декабря'}, ar = {'يناير','فبراير','مارس','أبريل','مايو','يونيو', 'يوليو','أغسطس','سبتمبر','أكتوبر','نوفمبر','ديسمبر'}, } local function containsAny(s, list) for _, w in ipairs(list) do if mw.ustring.find(s, w, 1, true) then return true end end return false end local function findMonthDayByName(s) local lower_s = mw.ustring.lower(s) for name, num in pairs(MONTH_IN) do if mw.ustring.find(lower_s, name, 1, true) then local d = mw.ustring.match(lower_s, '(%d%d?)%s*' .. name) if not d then d = mw.ustring.match(lower_s, name .. '%s*(%d%d?)') end if d then return num, tonumber(d) end end end return nil end local function normalize(s) s = tostring(s or '') s = mw.ustring.gsub(s, '[??]', '') s = mw.ustring.gsub(s, '[0-9]', function(c) return string.char(string.byte(c) - 0xFEE0) end) local map = { ['٠']='0',['١']='1',['٢']='2',['٣']='3',['٤']='4', ['٥']='5',['٦']='6',['٧']='7',['٨']='8',['٩']='9', ['۰']='0',['۱']='1',['۲']='2',['۳']='3',['۴']='4', ['۵']='5',['۶']='6',['۷']='7',['۸']='8',['۹']='9', } s = mw.ustring.gsub(s, '[٠-٩۰-۹]', function(c) return map[c] end) s = mw.ustring.gsub(s, ' ', ' ') s = mw.ustring.gsub(s, '[ー-−―─—–~~〜]', '-') s = mw.ustring.lower(s) s = mw.ustring.gsub(s, '%s*から%s*', '-') s = mw.ustring.gsub(s, '%s*まで%s*', '-') s = mw.ustring.gsub(s, '%s*至%s*', '-') s = mw.ustring.gsub(s, '%s*到%s*', '-') s = mw.ustring.gsub(s, '%s*[Tt][Oo]%s*', '-') s = mw.ustring.gsub(s, '%s+по%s+', '-') s = mw.ustring.gsub(s, '%s*[Bb][Ii][Ss]%s+', '-') s = mw.ustring.gsub(s, '^%s*[Ff][Rr][Oo][Mm]%s+', '') s = mw.ustring.gsub(s, '^%s*[Bb][Oo][Rr][Nn]%s+(in%s+|on%s+)?', '') s = mw.ustring.gsub(s, '^%s*[Dd][Ii][Ee][Dd]%s+(in%s+|on%s+)?', '') s = mw.ustring.gsub(s, '^%s*[Dd][Ee]%s+', '') s = mw.ustring.gsub(s, '^%s*[Cc][Ii][Rr][Cc][Aa]%s+', '') s = mw.ustring.gsub(s, '^%s*[Cc][Aa]%.?%s+', '') s = mw.ustring.gsub(s, '^%s*с%s+', '') s = mw.ustring.gsub(s, '^%s*от%s+', '') return mw.text.trim(s) end local function extractNumbers(s) local nums = {} for n in mw.ustring.gmatch(s, '%d+') do table.insert(nums, tonumber(n)) end return nums end local function isApproximate(s) if containsAny(s, APPROX) then return true end if mw.ustring.find(s, '-', 1, true) then return true end return false end -- 元号(令和の終端は現在年) local CURRENT_YEAR = tonumber(os.date('%Y')) or 2026 local ERAS = { {'明治', 1868, 1912}, {'大正', 1912, 1926}, {'昭和', 1926, 1989}, {'平成', 1989, 2019}, {'令和', 2019, CURRENT_YEAR}, } local function parseEra(s) for _, era in ipairs(ERAS) do local name, S, E = era[1], era[2], era[3] if mw.ustring.find(s, name, 1, true) then local L = E - S local n = tonumber(mw.ustring.match(s, '(%d+)年')) if n and n < 100 then return {year = S + n - 1, exact = false} end local dec = tonumber(mw.ustring.match(s, '(%d+)年代')) if dec and dec < 100 then return {year = S + dec - 1 + 5, exact = false} end if containsAny(s, {'末期','終期','晩期','最末期'}) then return {year = E - L/12, exact = false} end if containsAny(s, EARLY) then return {year = S + L/6, exact = false} end if containsAny(s, MID) then return {year = S + L/2, exact = false} end if containsAny(s, LATE) then return {year = S + 5*L/6, exact = false} end return {year = S + L/2, exact = false} end end return nil end local function parseYMD(s) local y1,m1,d1,y2,m2,d2 = mw.ustring.match( s, '(%d%d%d%d)年(%d%d?)月(%d%d?)日.-(%d%d%d%d)年(%d%d?)月(%d%d?)日') if y1 then return {year=(tonumber(y1)+tonumber(y2))/2, month=(tonumber(m1)+tonumber(m2))/2, day=(tonumber(d1)+tonumber(d2))/2, exact=false} end local y3,m3,d3,d4 = mw.ustring.match( s, '(%d%d%d%d)年(%d%d?)月(%d%d?)日.-(%d%d?)日') if y3 then return {year=tonumber(y3), month=tonumber(m3), day=(tonumber(d3)+tonumber(d4))/2, exact=false} end local y4,m4,d4b = mw.ustring.match(s, '(%d%d%d%d)年(%d%d?)月(%d%d?)日') if y4 then return {year=tonumber(y4),month=tonumber(m4),day=tonumber(d4b),exact=true} end local yk1,mk1,dk1,yk2,mk2,dk2 = mw.ustring.match( s, '(%d%d%d%d)년%s*(%d%d?)월%s*(%d%d?)일.-(%d%d%d%d)년%s*(%d%d?)월%s*(%d%d?)일') if yk1 then return {year=(tonumber(yk1)+tonumber(yk2))/2, month=(tonumber(mk1)+tonumber(mk2))/2, day=(tonumber(dk1)+tonumber(dk2))/2, exact=false} end local yk,mk,dk = mw.ustring.match(s, '(%d%d%d%d)년%s*(%d%d?)월%s*(%d%d?)일') if yk then return {year=tonumber(yk),month=tonumber(mk),day=tonumber(dk),exact=true} end local ya, ma, da = mw.ustring.match(s, '(%d%d%d%d)[%-/%.](%d%d?)[%-/%.](%d%d?)') if ya then return {year=tonumber(ya),month=tonumber(ma),day=tonumber(da),exact=true} end local y_named = tonumber(mw.ustring.match(s, '(%d%d%d%d)')) if y_named then local mn, dd = findMonthDayByName(s) if mn and dd then return {year=y_named, month=mn, day=dd, exact=true} end end local y5,m5 = mw.ustring.match(s, '(%d%d%d%d)年(%d%d?)月') if y5 then return {year=tonumber(y5),month=tonumber(m5),day=15,exact=false} end local y5k,m5k = mw.ustring.match(s, '(%d%d%d%d)년%s*(%d%d?)월') if y5k then return {year=tonumber(y5k),month=tonumber(m5k),day=15,exact=false} end local y6 = tonumber(mw.ustring.match(s, '(%d%d%d%d)年')) if y6 then if containsAny(s, {'上半期','上期','上半年','上半','상반기', 'первая','первой','first half','1st half','النصف الأول'}) then return {year=y6,month=3,day=15,exact=false} end if containsAny(s, {'下半期','下期','下半年','下半','하반기', 'вторая','второй','second half','2nd half','النصف الثاني'}) then return {year=y6,month=9,day=15,exact=false} end return {year=y6,exact=true} end local y6k = tonumber(mw.ustring.match(s, '(%d%d%d%d)년')) if y6k then if containsAny(s, {'상반기'}) then return {year=y6k,month=3,day=15,exact=false} end if containsAny(s, {'하반기'}) then return {year=y6k,month=9,day=15,exact=false} end return {year=y6k,exact=true} end return nil end local function parseDecade(s, nums) local era = parseEra(s) if era then return era end if #nums == 0 then return nil end local off = 5 if containsAny(s, EARLY) then off = 2 elseif containsAny(s, MID) then off = 5 elseif containsAny(s, LATE) then off = 7 end local base4 = mw.ustring.match(s, '(%d%d%d%d)s') if base4 then return {year = tonumber(base4) + off, exact = false} end local base2 = mw.ustring.match(s, '(%d%d)s') if base2 then return {age = tonumber(base2) + off, exact = false} end local nendai = mw.ustring.find(s, '年代', 1, true) or mw.ustring.find(s, '년대', 1, true) local dai = mw.ustring.find(s, '代', 1, true) or mw.ustring.find(s, '台', 1, true) or mw.ustring.find(s, '대', 1, true) if nendai then local base = nums[1] if base < 100 then base = base + 1900 end return {year = base + off, exact = false} end local ru = mw.ustring.match(s, '(%d%d%d%d)[%-]?[ех]') if ru then return {year = tonumber(ru) + off, exact = false} end if dai then local base = nums[1] if base >= 1900 then return {year = base + off, exact = false} end if base < 100 then return {age = base + off, exact = false} end end return nil end local function parseAge(s, nums) local markers = {'歳','才','살','세','years old','year old','y.o.','yo.', ' سنة','سنةً','عاما','عاماً','عام'} local found = false for _, m in ipairs(markers) do if mw.ustring.find(s, m, 1, true) then found = true; break end end if not found then return nil end if #nums == 0 then return nil end if #nums >= 2 then local a,b = nums[1],nums[2] if a>b then a,b = b,a end return {age=(a+b)/2, exact=false} end local a = nums[1] if mw.ustring.find(s, '弱', 1, true) then a = a - 1 end if mw.ustring.find(s, '強', 1, true) then a = a + 1 end return {age=a, exact=not isApproximate(s)} end local function parseExpression(s) s = normalize(s) if s == '' then return nil end local nums = extractNumbers(s) if #nums == 0 and not mw.ustring.find(s, '昭和', 1, true) and not mw.ustring.find(s, '平成', 1, true) and not mw.ustring.find(s, '大正', 1, true) and not mw.ustring.find(s, '明治', 1, true) and not mw.ustring.find(s, '令和', 1, true) then return nil end local dec = parseDecade(s, nums); if dec then return dec end local age = parseAge(s, nums); if age then return age end local ymd = parseYMD(s); if ymd then return ymd end if #nums >= 2 then local a,b = nums[1], nums[2] if a >= 1000 and a <= 2200 and b < 100 then local mult = 10 ^ #tostring(b) b = math.floor(a / mult) * mult + b end if a > b then a,b = b,a end if a >= 1000 and b <= 2200 then return {year=(a+b)/2, exact=false} else return {age=(a+b)/2, exact=false} end end local n = nums[1] if n >= 1000 and n <= 2200 then return {year=n, exact=not isApproximate(s)} else return {age=n, exact=not isApproximate(s)} end end -- 月日のみを抽出(年なし) local function parseMonthDay(s) if s == '' then return nil end local bm, bd = mw.ustring.match(s, '(%d%d?)月(%d%d?)日') if bm and bd then return {month=tonumber(bm), day=tonumber(bd), exact=true} end bm, bd = mw.ustring.match(s, '(%d%d?)월%s*(%d%d?)일') if bm and bd then return {month=tonumber(bm), day=tonumber(bd), exact=true} end bm = mw.ustring.match(s, '(%d%d?)月') if bm then return {month=tonumber(bm), day=15, exact=false} end bm = mw.ustring.match(s, '(%d%d?)월') if bm then return {month=tonumber(bm), day=15, exact=false} end local mn, dd = findMonthDayByName(s) if mn and dd then return {month=mn, day=dd, exact=true} end local nm, nd = mw.ustring.match(s, '^(%d%d?)[%-/%.](%d%d?)$') if nm and nd then return {month=tonumber(nm), day=tonumber(nd), exact=true} end return nil end -- 生年の末尾に付いた「年」「년」を数値のみの場合に限って除去 local function stripYearSuffix(s) s = trim(s) if s == '' then return s end local n = mw.ustring.match(s, '^(%d+)年$') or mw.ustring.match(s, '^(%d+)년$') if n then return n end return s end local function parseBirth(birth, birth2) birth = stripYearSuffix(birth) if birth ~= '' and birth2 == '' then return parseExpression(birth) end if birth == '' and birth2 ~= '' then return parseMonthDay(birth2) end if birth ~= '' and birth2 ~= '' then local by = tonumber(birth) if by and by >= 1000 and by <= 2200 then local bm, bd = mw.ustring.match(birth2, '(%d%d?)月(%d%d?)日') if bm and bd then return {year=by, month=tonumber(bm), day=tonumber(bd), exact=true} end bm, bd = mw.ustring.match(birth2, '(%d%d?)월%s*(%d%d?)일') if bm and bd then return {year=by, month=tonumber(bm), day=tonumber(bd), exact=true} end local bm2 = mw.ustring.match(birth2, '(%d%d?)月') if bm2 then return {year=by, month=tonumber(bm2), day=15, exact=false} end bm2 = mw.ustring.match(birth2, '(%d%d?)월') if bm2 then return {year=by, month=tonumber(bm2), day=15, exact=false} end local mn, dd = findMonthDayByName(birth2) if mn and dd then return {year=by, month=mn, day=dd, exact=true} end local nm, nd = mw.ustring.match(birth2, '^(%d%d?)[%-/%.](%d%d?)$') if nm and nd then return {year=by, month=tonumber(nm), day=tonumber(nd), exact=true} end end local combined if mw.ustring.find(birth2,'月') or mw.ustring.find(birth2,'日') or mw.ustring.find(birth2,'월') or mw.ustring.find(birth2,'일') then combined = birth .. '年' .. birth2 else combined = birth .. birth2 end return parseExpression(combined) end return nil end local function units(lang) if lang=='en' then return ' years old',' approx.' end if lang=='ar' then return ' سنة',' حوالي' end if lang=='zh' then return '岁','左右' end if lang=='zh-hant' then return '歲','左右' end if lang=='ko' then return '살','경' end if lang=='ru' then return ' лет',' ок.' end return '歳','頃' end local function formatBirthDate(lang, y, m, d) if m and d then if lang=='ko' then return string.format('%d년 %d월 %d일', y, m, d) elseif lang=='en' then return MONTH_OUT.en[m] .. ' ' .. d .. ', ' .. y elseif lang=='ru' then return d .. ' ' .. MONTH_OUT.ru[m] .. ' ' .. y elseif lang=='ar' then return d .. ' ' .. MONTH_OUT.ar[m] .. ' ' .. y else return string.format('%d年%d月%d日', y, m, d) end else if lang=='ko' then return tostring(y) .. '년' elseif lang=='en' or lang=='ru' or lang=='ar' then return tostring(y) else return tostring(y) .. '年' end end end local function formatMonthDay(lang, m, d) if lang=='ko' then return string.format('%d월 %d일', m, d) elseif lang=='en' then return MONTH_OUT.en[m] .. ' ' .. d elseif lang=='ru' then return d .. ' ' .. MONTH_OUT.ru[m] elseif lang=='ar' then return d .. ' ' .. MONTH_OUT.ar[m] else return string.format('%d月%d日', m, d) end end function p.field(frame) local args = frame:getParent().args local ageStr = trim(args['年齢']) local birth = trim(args['生年']) local birth2 = trim(args['生年2']) local lang = trim(args['lang']); if lang=='' then lang='ja' end lang = mw.ustring.lower(lang) local ageInfo = (ageStr ~= '') and parseExpression(ageStr) or nil local birthInfo = parseBirth(birth, birth2) local refY = tonumber(os.date('%Y')) local refM = tonumber(os.date('%m')) local refD = tonumber(os.date('%d')) local finalAge, ageApprox, ageRange = nil, false, nil local src = ageInfo or birthInfo if src then if src.age then finalAge = src.age ageApprox = not src.exact elseif src.year then finalAge = refY - src.year if src.month and src.day then if refM < src.month or (refM == src.month and refD < src.day) then finalAge = finalAge - 1 end end ageApprox = not src.exact if lang=='ja' and not ageInfo and src.exact and not src.month and src.year then local y = math.floor(src.year + 0.5) local mx = refY - y ageRange = {min=mx-1, max=mx} end end end local ageUnit, approxMark = units(lang) local birthOut = '' if birthInfo then if birthInfo.year then local by = math.floor(birthInfo.year + 0.5) local bm = birthInfo.month and math.floor(birthInfo.month + 0.5) or nil local bd = birthInfo.day and math.floor(birthInfo.day + 0.5) or nil birthOut = formatBirthDate(lang, by, bm, bd) elseif birthInfo.month and birthInfo.day then local bm = math.floor(birthInfo.month + 0.5) local bd = math.floor(birthInfo.day + 0.5) birthOut = formatMonthDay(lang, bm, bd) end if birthOut ~= '' and not birthInfo.exact then birthOut = birthOut .. approxMark end end if birth ~= '' and birth2 ~= '' and birthInfo and birthInfo.exact and not birthInfo.month then if lang=='en' or lang=='ar' or lang=='ru' then birthOut = birth2 .. ', ' .. birth elseif lang=='ja' or lang=='zh' or lang=='zh-hant' then birthOut = birth .. '年' .. birth2 elseif lang=='ko' then birthOut = birth .. '년 ' .. birth2 end end if birthOut == '' and finalAge then local by = math.floor(refY - finalAge + 0.5) if lang=='en' or lang=='ru' or lang=='ar' then birthOut = tostring(by) elseif lang=='ko' then birthOut = tostring(by) .. '년' else birthOut = tostring(by) .. '年' end birthOut = birthOut .. approxMark end local ageOut = '' if ageRange then ageOut = tostring(ageRange.min)..'~'..tostring(ageRange.max)..ageUnit elseif finalAge then local a = math.floor(finalAge + 0.5) ageOut = tostring(a)..ageUnit if ageApprox then ageOut = ageOut .. approxMark end end if ageInfo == nil and ageStr ~= '' and not isPlaceholder(ageStr) then ageOut = ageStr end if birthInfo == nil and (birth ~= '' or birth2 ~= '') then local combined if birth ~= '' and birth2 ~= '' then combined = birth .. ' ' .. birth2 elseif birth ~= '' then combined = birth else combined = birth2 end if not isPlaceholder(combined) then birthOut = combined end end if lang=='en' then if ageOut~='' and birthOut~='' then return ageOut..', born '..birthOut end if ageOut~='' then return ageOut end if birthOut~='' then return 'born '..birthOut end elseif lang=='ar' then if ageOut~='' and birthOut~='' then return ageOut..'، ولد في '..birthOut end if ageOut~='' then return ageOut end if birthOut~='' then return 'ولد في '..birthOut end elseif lang=='zh' or lang=='zh-hant' then if ageOut~='' and birthOut~='' then return ageOut..','..birthOut..'生' end if ageOut~='' then return ageOut end if birthOut~='' then return birthOut..'生' end elseif lang=='ko' then if ageOut~='' and birthOut~='' then return ageOut..', '..birthOut..'생' end if ageOut~='' then return ageOut end if birthOut~='' then return birthOut..'생' end elseif lang=='ru' then if ageOut~='' and birthOut~='' then return ageOut..', род. '..birthOut end if ageOut~='' then return ageOut end if birthOut~='' then return 'род. '..birthOut end else if ageOut~='' and birthOut~='' then return ageOut..'、'..birthOut..'生まれ' end if ageOut~='' then return ageOut end if birthOut~='' then return birthOut..'生まれ' end end return '' end function p.hasdata(frame) local args = frame:getParent().args local ageStr = trim(args['年齢']) local birth = trim(args['生年']) local birth2 = trim(args['生年2']) if ageStr ~= '' and not isPlaceholder(ageStr) then return 'x' end if birth ~= '' and not isPlaceholder(birth) then return 'x' end if birth2 ~= '' and not isPlaceholder(birth2) then return 'x' end return '' end return p
編集内容の要約:
静かの海wikiへの投稿はすべて、他の投稿者によって編集、変更、除去される場合があります。 自分が書いたものが他の人に容赦なく編集されるのを望まない場合は、ここに投稿しないでください。
また、投稿するのは、自分で書いたものか、パブリック ドメインまたはそれに類するフリーな資料からの複製であることを約束してください(詳細は
静かの海wiki:著作権
を参照)。
著作権保護されている作品は、許諾なしに投稿しないでください!
キャンセル
編集ヘルプ
(新しいウィンドウで開きます)
このページで使用されているテンプレート:
モジュール:AgeEstimator/doc
(
編集
)
ナビゲーション メニュー
個人用ツール
ログインしていません
トーク
投稿記録
アカウント作成
ログイン
名前空間
モジュール
議論
English
表示
閲覧
ソースを編集
履歴表示
その他
検索
案内
メインページ
最近の更新
おまかせ表示
MediaWiki についてのヘルプ
ツール
リンク元
関連ページの更新状況
特別ページ
ページ情報