模块:Tr
跳到导航
跳到搜索
亦可参看英文模块:Module:Tr。那边可能有更完整或更正确的信息。
作为 Template:tr 的补充,为 Template:tr 提供深度扩展分析辅助,以及最终的fallback。
local VariablesLua = mw.ext.VariablesLua
local database = mw.loadData( 'Module:Tr/loaddata' )
local main_database = database.main
local prefix_database = database.prefix
local postfix_database = database.postfix
local with_of = false --全局记录输入文本是否符合 ??s of ?? 格式。
-- 所有local函数,统一定义方便递归
local stripArticle, tcBasic, procNumberPostfix, procParenPostfix, proc, tcPlural, translate
-- 尝试检查各种可能的复数变体,匹配就tc返回,否则nil
function tcPlural(str)
if not str then return end
local m, n = nil, 0
--需要特殊形态处理?: ??s of ????
if with_of then
m, n = str:gsub('^(.+)s( of .+)$', '%1%2')
if n>0 then
if main_database[m] then return main_database[m] end
m, n = str:gsub('^(.+)es( of .+)$', '%1%2') -- -es
if n>0 then
if main_database[m] then return main_database[m] end
m, n = str:gsub('^(.+)ies( of .+)$', '%1y%2') -- -ies
if n>0 then
if main_database[m] then return main_database[m] end
else
--和上一个匹配互斥,放在else可以略节约。
m, n = str:match('^(.+)ves( of .+)$') -- -ves
if m then
str = m..'f'..n -- -f
if main_database[str] then return main_database[str] end
str = m..'fe'..n -- -fe
if main_database[str] then return main_database[str] end
end
end
end
end
end
--of标记是全局的,但这里处理的还可能是其中不带of的子片段。
if not with_of or n == 0 then
m = str:match('^(.+)s$') -- -s
if m then
if main_database[m] then
return main_database[m]
end
m = m:match('^(.+)e$') -- -es
if m then
if main_database[m] then
return main_database[m]
end
m = m:match('^(.+)i$') -- ies
if m then
m = m..'y'
if main_database[m] then return main_database[m] end
else
--和上一个匹配互斥,放在else可以省点事
m = str:match('^(.+)ves$') -- ves
if m then
m = m..'f' -- -f
if main_database[m] then return main_database[m] end
m = m..'e' -- -fe
if main_database[m] then return main_database[m] end
end
end
end
end
end
end
-- 脱冠词 成功脱掉返回脱掉后的文本,失败nil
function stripArticle(str)
return str:match('^the%s+(.+)$') or str:match('^an?%s+(.+)$')
end
-- 基本:冠词/复数。
function tcBasic(en)
if main_database[en] then return main_database[en] end
local main = stripArticle(en)
if main then
--有冠词
if main_database[main] then return main_database[main] end
local zh = tcPlural(main)
if zh then return zh end
else
--无冠词
if main_database[en] then return main_database[en] end
end
return tcPlural(en)
end
-- 尝试去掉数字后缀,然后再次尝试做处理。
function procNumberPostfix(en)
local main, number = en:match('^(.-)%s+([%d%.]+)$') --后缀允许数字和.混合。可能会带来少数没意义的匹配,问题不大。
if main then
zh = tcBasic(main) or proc(main) --主体部分还是要尝试基本款翻译,以应对 block 2 这样的情况
if zh then
return zh..' '..number..' ' --中文和数字之间要有空格
end
end
end
-- 尝试去掉数字前缀,然后再次尝试做处理。
function procNumberPrefix(en)
local number, main = en:match('^([%d%.]+)%s+(.-)$') --前缀允许数字和.混合。可能会带来少数没意义的匹配,问题不大。
if main then
zh = tcBasic(main) or proc(main) --主体部分还是要尝试基本款翻译,以应对 2 block 这样的情况
if zh then
return ' '..number..' '..zh --中文和数字之间要有空格
end
end
end
-- 尝试去掉括号后缀,然后再次尝试做处理。
function procParenPostfix(en)
local main, postfix, postfix_zh
main, postfix = en:match('^(.-)%s*(%b())$')
if main then
postfix_zh = postfix_database[postfix] -- 带着括号直接看是否有专用后缀
if not postfix_zh then
postfix = postfix:sub(2,-2)
postfix_zh = postfix_database[postfix]
if not postfix_zh then
-- 处理 (Variant 2) 这类型后缀
local main, number = postfix:match('^(.-)%s+([%d]+)$')
if main then
postfix_zh = postfix_database[main] or tcBasic(main) or proc(main)
if postfix_zh then
postfix_zh = postfix_zh..' '..number..' ' --中文和数字之间要有空格
end
end
end
if not postfix_zh then
postfix_zh = tcBasic(postfix) or proc(postfix)
end
if postfix_zh then
postfix_zh = '('..postfix_zh..')' -- 用中文括号。
end
end
end
--加上前面主体部分
if postfix_zh then
local zh = tcBasic(main) or proc(main)
if zh then return zh..postfix_zh end
end
end
-- 复杂处理:先尝试做,/and/or分割,然后考虑数字后缀、然后尝试括号后缀、然后专用后缀/前缀、然后普通后缀/前缀。
function proc(en)
local a, b, zh_a, zh_b, sep
-- and/or 要考虑牛津逗号的问题,否则会导致 a, b, and c 最后被分离出“and c”片段。另外 “, and”附近的空格问题要处理。
-- X and Y
a, sep, b = en:match('^(.-)%s*([,%s])%s*and%s+(.+)$')
if a then
zh_a = tcBasic(a) or proc(a)
if zh_a then
zh_b = tcBasic(b) or proc(b)
if zh_b then
return zh_a..((sep == ',') and '、和' or '和')..zh_b
end
end
end
-- X or Y
a, sep, b = en:match('^(.-)%s*([,%s])%s*or%s+(.+)$')
if a then
zh_a = tcBasic(a) or proc(a)
if zh_a then
zh_b = tcBasic(b) or proc(b)
if zh_b then
return zh_a..((sep == ',') and '、或' or '或')..zh_b
end
end
end
-- X , Y and X / Y
a, sep, b = en:match('^(.-)%s*([,/])%s*(.+)$')
if a then
zh_a = tcBasic(a) or proc(a)
if zh_a then
zh_b = tcBasic(b) or proc(b)
if zh_b then
return zh_a..((sep == ',') and '、' or '/')..zh_b
end
end
end
local zh, postfix_main, prefix_main, postfix, prefix, postfix_zh, prefix_zh
zh = procNumberPostfix(en) or procNumberPrefix(en) or procParenPostfix(en)
if zh then return zh end
--尝试解析后缀
postfix_main, postfix = en:match('^(.-)%s+(%S+)$')
--专用后缀
if postfix_main then
postfix_zh = postfix_database[postfix]
if postfix_zh then
zh = tcBasic(postfix_main) or proc(postfix_main)
if zh then return zh..postfix_zh end
end
end
--尝试解析前缀:
en = stripArticle(en) or en -- 预脱冠词,否则前缀分析没意义
prefix, prefix_main = en:match('^(%S+)%s+(.+)$')
-- 专用前缀
if prefix_main then
prefix_zh = prefix_database[prefix]
if prefix_zh then
zh = tcBasic(prefix_main) or proc(prefix_main)
if zh then return prefix_zh..zh end
end
end
-- 普通后缀:
if postfix_main then
postfix_zh = tcBasic(postfix)
if postfix_zh then
zh = tcBasic(postfix_main) or proc(postfix_main)
if zh then return zh..postfix_zh end
end
end
-- 普通前缀
if prefix_main then
prefix_zh = tcBasic(prefix)
if prefix_zh then
zh = tcBasic(prefix_main) or proc(prefix_main)
if zh then return prefix_zh..zh end
end
end
end
function translate(input, doTcBasic)
local en = input:lower()
local zh -- 翻译结果
-- 考虑 ??s of ?? 的情况
if en:match('^(.+)s( of .+)$') then
with_of = true -- 保存这个信息可以在无of的场景节约一些正则匹配成本
zh = tcPlural(en)
if zh then return zh end
end
if doTcBasic then
-- 指定了要做基本翻译,是通过module调用的,要补充一下基本处理
zh = tcBasic(en)
if zh then return zh end
else
--从template来的,补充一下对 -ves结尾的测试
local matches = en:match('^(.+)ves$')
if matches then
local m = stripArticle(matches) or matches -- 冠词还是要脱
--无冠词版本这里4个其实有重复,但不影响效率。
local t = m..'f'
if main_database[t] then return main_database[t] end
local t = m..'fe'
if main_database[t] then return main_database[t] end
local t = matches..'f'
if main_database[t] then return main_database[t] end
local t = matches..'fe'
if main_database[t] then return main_database[t] end
end
end
--补一个特殊情况:the开头的再试试不脱冠词直接测试复数(诸如 The Axe 这种天生带 the 的专有名词)
if en:match('^the%s+(.+)$') then
zh = tcPlural(en)
if zh then return zh end
end
-- 其他复杂变体
zh = proc(en)
if zh then
-- 处理空格问题
zh = zh:gsub("%s+"," ")
zh = zh:gsub("%s*(%s*","(")
zh = zh:gsub("%s*)%s*",")")
zh = zh:gsub("!%s+","!")
zh = zh:gsub("?%s+","?")
zh = zh:match("^%s*(.-)%s*$")
return zh
end
-- 最后的fallback,要用原始大小写
return input
end
-- 主返回点
return {
-- 给module调用的
translate = function(en)
return translate(en, true)
end,
-- 给module调用的,对应于link=y的{{tr}}
translateLink = function(en)
return database.pagename[en] or translate(en, true)
end,
--template调用
go = function(frame)
local input = frame.args['t']
return translate(input, false)
end,
-- 给 template:tr 加载数据
loadData = function(frame)
for k,v in pairs(database.main) do
VariablesLua.vardefine( 'ct-'..k, v )
end
for k,v in pairs(database.pagename) do
VariablesLua.vardefine( 'ctPN-'..k, v )
end
end,
--清除数据缓存
purge = function()
mw.ext.LuaCache.delete( 'tr__database')
end
}