我正在寻找一种从ISO 639-3语言代码构造CultureInfo
对象的方法。我没有在MSDN中找到任何关于它的内容,并试图从所有文化列表中获取它不起作用...
CultureInfo cInfo = CultureInfo.GetCultures(CultureTypes.AllCultures)
.FirstOrDefault(r => String.Equals(r.ThreeLetterISOLanguageName, "CCH", StringComparison.CurrentCultureIgnoreCase));
将始终返回null(请注意,“CCH”是ISO-639-3列表中的一种语言)。
感谢任何想法,谢谢!
答案 0 :(得分:7)
MSDN documentation表示CultureInfo对象只有ISO 639-2 three-letter code
和ISO 639-1 two-letter code
。这意味着您需要某种映射才能将ISO 639-3
代码链接到特定的CultureInfo
实例。
This Wikipedia page包含映射表。也许您可以剪切并粘贴到XML文件中,并将其用作类库中的嵌入式资源,以便提供映射。或者甚至只是在某处定义静态Dictionary<string,string>
。
或者,我确信会有第三方图书馆可以为你做这件事(虽然我不知道有什么不在我的脑海里)。
我没有意识到ISO 639-3
代码只有有时会映射到ISO 639-2
代码。这里的问题是CultureInfo
类不是为了处理ISO 639-3
规范而设计的,所以你可能必须找到一个完全不同的CultureInfo第三方实现来支持它 - 或者自己制作它。
答案 1 :(得分:2)
我发现自己需要ISO 639-3的枚举。如果你实际上不需要将它映射到CultureInfo,那么这可能会有所帮助:
http://snipplr.com/view/76196/enum-for-iso-6393-language-codes/
答案 2 :(得分:2)
我有类似的需要在ISO 639-2B / T和ISO 639-3格式之间进行转换。 我创建了一个TT4解决方案,在编译时生成所有7K +条目的列表。 我本可以使用字典而不是列表,但我正在搜索多个字段,因此价值不大。
从http://www-01.sil.org/iso639-3/download.asp下载并解压缩制表符分隔的文本文件 将其复制到项目路径,根据需要重命名。
创建设计时模板文件:https://msdn.microsoft.com/en-us/library/dd820620.aspx
<#@ template debug="true" hostspecific="true" language="C#" #>
<#@ output extension=".cs" #>
<#@ assembly name="System.Core" #>
<#@ assembly name="Microsoft.VisualBasic.dll" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Text" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ import namespace="Microsoft.VisualBasic.FileIO" #>
// Generated code
using System.Collections.Generic;
namespace Foo
{
// ISO 639-3
// http://www-01.sil.org/iso639-3/download.asp
public class ISO_639_3
{
// The three-letter 639-3 identifier
public string Id { get; set; }
// Equivalent 639-2 identifier of the bibliographic applications code set, if there is one
public string Part2B { get; set; }
// Equivalent 639-2 identifier of the terminology applications code set, if there is one
public string Part2T { get; set; }
// Equivalent 639-1 identifier, if there is one
public string Part1 { get; set; }
// I(ndividual), M(acrolanguage), S(pecial)
public string Scope { get; set; }
// A(ncient), C(onstructed), E(xtinct), H(istorical), L(iving), S(pecial)
public string Language_Type { get; set; }
// Reference language name
public string Ref_Name { get; set; }
// Comment relating to one or more of the columns
public string Comment { get; set; }
// Create a list of all known codes
public static List<ISO_639_3> Create()
{
List<ISO_639_3> list = new List<ISO_639_3> {
<#
// Setup text parser
string filename = this.Host.ResolvePath("iso-639-3.tab");
TextFieldParser tfp = new TextFieldParser(filename)
{
TextFieldType = FieldType.Delimited,
Delimiters = new[] { ",", "\t" },
HasFieldsEnclosedInQuotes = true,
TrimWhiteSpace = true
};
// Read first row as header
string[] header = tfp.ReadFields();
// Read rows from file
// For debugging limit the row count
//int maxrows = 10;
int maxrows = int.MaxValue;
int rowcount = 0;
string term = "";
while (!tfp.EndOfData && rowcount < maxrows)
{
// Read row of data from the file
string[] row = tfp.ReadFields();
rowcount ++;
// Add "," on all but last line
term = tfp.EndOfData || rowcount >= maxrows ? "" : ",";
// Add new item from row data
#>
new ISO_639_3 { Id = "<#=row[0]#>", Part2B = "<#=row[1]#>", Part2T = "<#=row[2]#>", Part1 = "<#=row[3]#>", Scope = "<#=row[4]#>", Language_Type = "<#=row[5]#>", Ref_Name = "<#=row[6]#>", Comment = "<#=row[7]#>" }<#=term#>
<#
}
#>
};
return list;
}
}
}
生成的代码将为包含所有语言的列表创建初始化程序。 这个文件很大,它会降低编辑速度,编译需要很长时间,除非你需要,否则保持卸载。 片段:
public static List<ISO_639_3> Create()
{
List<ISO_639_3> list = new List<ISO_639_3> {
new ISO_639_3 { Id = "aaa", Part2B = "", Part2T = "", Part1 = "", Scope = "I", Language_Type = "L", Ref_Name = "Ghotuo", Comment = "" },
new ISO_639_3 { Id = "aab", Part2B = "", Part2T = "", Part1 = "", Scope = "I", Language_Type = "L", Ref_Name = "Alumu-Tesu", Comment = "" },
new ISO_639_3 { Id = "aac", Part2B = "", Part2T = "", Part1 = "", Scope = "I", Language_Type = "L", Ref_Name = "Ari", Comment = "" },
使用生成的列表根据需要进行映射,例如
public static ISO_639_3 GetISO_639_3(string language)
{
// Create list if it does not exist
if (Program.Default.ISO6393List == null)
{
Program.Default.ISO6393List = ISO_639_3.Create();
}
// Match the input string type
ISO_639_3 lang = null;
if (language.Length > 3 && language.ElementAt(2) == '-')
{
// Treat the language as a culture form, e.g. en-us
CultureInfo cix = new CultureInfo(language);
// Recursively call using the ISO 639-2 code
return GetISO_639_3(cix.ThreeLetterISOLanguageName);
}
else if (language.Length > 3)
{
// Try long form
lang = Program.Default.ISO6393List.Where(item => item.Ref_Name.Equals(language, StringComparison.OrdinalIgnoreCase)).FirstOrDefault();
if (lang != null)
return lang;
}
else if (language.Length == 3)
{
// Try 639-3
lang = Program.Default.ISO6393List.Where(item => item.Id.Equals(language, StringComparison.OrdinalIgnoreCase)).FirstOrDefault();
if (lang != null)
return lang;
// Try the 639-2/B
lang = Program.Default.ISO6393List.Where(item => item.Part2B.Equals(language, StringComparison.OrdinalIgnoreCase)).FirstOrDefault();
if (lang != null)
return lang;
// Try the 639-2/T
lang = Program.Default.ISO6393List.Where(item => item.Part2T.Equals(language, StringComparison.OrdinalIgnoreCase)).FirstOrDefault();
if (lang != null)
return lang;
}
else if (language.Length == 2)
{
// Try 639-1
lang = Program.Default.ISO6393List.Where(item => item.Part1.Equals(language, StringComparison.OrdinalIgnoreCase)).FirstOrDefault();
if (lang != null)
return lang;
}
// Not found
return lang;
}
答案 3 :(得分:1)
请查看C#的文本模板。 (* .TT)
它允许您在项目中重新保存文件时生成文件:
<#@import namespace="System.Globalization"#>
<#@ output extension=".cs" #>
namespace YourProject.Enum
{
enum eLanguage
{
Unknown,
<#
CultureInfo[] cultures = CultureInfo.GetCultures(CultureTypes.AllCultures);
foreach (var culture in cultures) { #>
<#= culture.TwoLetterISOLanguageName #>
<#
}
#>
Other
}
}
答案 4 :(得分:0)
这里是类固醇的枚举
namespace System.Globalization
{
using System.Collections.Generic;
public enum SpokenLang
{
[Spoken.Lang("Afaraf", "aa", "aar")]
Afar,
[Spoken.Lang("Аҧсуа", "ab", "abk")]
Abkhazian,
[Spoken.Lang("Afrikaans", "af", "afr")]
Afrikaans,
[Spoken.Lang("Akan", "ak", "aka")]
Akan,
[Spoken.Lang("አማርኛ", "am", "amh")]
Amharic,
[Spoken.Lang("العربية", "ar", "ara")]
Arabic,
[Spoken.Lang("Aragonés", "an", "arg")]
Aragonese,
[Spoken.Lang("অসমীয়া", "as", "asm")]
Assamese,
[Spoken.Lang("авар мацӀ", "av", "ava")]
Avaric,
[Spoken.Lang("Avestan", "ae", "ave")]
Avestan,
[Spoken.Lang("Aymar aru", "ay", "aym")]
Aymara,
[Spoken.Lang("Azərbaycan dili", "az", "aze")]
Azerbaijani,
[Spoken.Lang("башҡорт теле", "ba", "bak")]
Bashkir,
[Spoken.Lang("Bamanankan", "bm", "bam")]
Bambara,
[Spoken.Lang("Беларуская", "be", "bel")]
Belarusian,
[Spoken.Lang("বাংলা", "bn", "ben")]
Bengali,
[Spoken.Lang("Bislama", "bi", "bis")]
Bislama,
[Spoken.Lang("བོད་ཡིག", "bo", "bod")]
Tibetan,
[Spoken.Lang("Bosanski jezik", "bs", "bos")]
Bosnian,
[Spoken.Lang("Brezhoneg", "br", "bre")]
Breton,
[Spoken.Lang("български език", "bg", "bul")]
Bulgarian,
[Spoken.Lang("Català", "ca", "cat")]
Catalan,
[Spoken.Lang("Česky", "cs", "ces")]
Czech,
[Spoken.Lang("Chamoru", "ch", "cha")]
Chamorro,
[Spoken.Lang("нохчийн мотт", "ce", "che")]
Chechen,
[Spoken.Lang("Словѣньскъ", "cu", "chu")]
ChurchSlavic,
[Spoken.Lang("чӑваш чӗлхи", "cv", "chv")]
Chuvash,
[Spoken.Lang("Kernewek", "kw", "cor")]
Cornish,
[Spoken.Lang("Corsu", "co", "cos")]
Corsican,
[Spoken.Lang("ᓀᐦᐃᔭᐍᐏᐣ", "cr", "cre")]
Cree,
[Spoken.Lang("Cymraeg", "cy", "cym")]
Welsh,
[Spoken.Lang("Dansk", "da", "dan")]
Danish,
[Spoken.Lang("Deutsch", "de", "deu")]
German,
[Spoken.Lang("ދިވެހި", "dv", "div")]
Dhivehi,
[Spoken.Lang("རྫོང་ཁ", "dz", "dzo")]
Dzongkha,
[Spoken.Lang("Ελληνικά", "el", "ell")]
ModernGreek,
[Spoken.Lang("English", "en", "eng")]
English,
[Spoken.Lang("Esperanto", "eo", "epo")]
Esperanto,
[Spoken.Lang("Eesti keel", "et", "est")]
Estonian,
[Spoken.Lang("Euskara", "eu", "eus")]
Basque,
[Spoken.Lang("Ɛʋɛgbɛ", "ee", "ewe")]
Ewe,
[Spoken.Lang("Føroyskt", "fo", "fao")]
Faroese,
[Spoken.Lang("فارسی", "fa", "fas")]
Persian,
[Spoken.Lang("Vosa Vakaviti", "fj", "fij")]
Fijian,
[Spoken.Lang("Suomen kieli", "fi", "fin")]
Finnish,
[Spoken.Lang("Français", "fr", "fra")]
French,
[Spoken.Lang("Frysk", "fy", "fry")]
WesternFrisian,
[Spoken.Lang("Fulfulde", "ff", "ful")]
Fulah,
[Spoken.Lang("Gàidhlig", "gd", "gla")]
ScottishGaelic,
[Spoken.Lang("Gaeilge", "ga", "gle")]
Irish,
[Spoken.Lang("Galego", "gl", "glg")]
Galician,
[Spoken.Lang("Ghaelg", "gv", "glv")]
Manx,
[Spoken.Lang("Avañe'ẽ", "gn", "grn")]
Guarani,
[Spoken.Lang("ગુજરાતી", "gu", "guj")]
Gujarati,
[Spoken.Lang("Kreyòl ayisyen", "ht", "hat")]
Haitian,
[Spoken.Lang("هَوُسَ", "ha", "hau")]
Hausa,
[Spoken.Lang("Serbo-Croatian", "sh", "hbs")]
SerboCroatian,
[Spoken.Lang("עברית", "he", "heb")]
Hebrew,
[Spoken.Lang("Otjiherero", "hz", "her")]
Herero,
[Spoken.Lang("हिन्दी", "hi", "hin")]
Hindi,
[Spoken.Lang("Hiri Motu", "ho", "hmo")]
HiriMotu,
[Spoken.Lang("Hrvatski", "hr", "hrv")]
Croatian,
[Spoken.Lang("magyar", "hu", "hun")]
Hungarian,
[Spoken.Lang("Հայերեն", "hy", "hye")]
Armenian,
[Spoken.Lang("Igbo", "ig", "ibo")]
Igbo,
[Spoken.Lang("Ido", "io", "ido")]
Ido,
[Spoken.Lang("ꆇꉙ", "ii", "iii")]
SichuanYi,
[Spoken.Lang("ᐃᓄᒃᑎᑐᑦ", "iu", "iku")]
Inuktitut,
[Spoken.Lang("Interlingue", "ie", "ile")]
Interlingue,
[Spoken.Lang("Interlingua", "ia", "ina")]
Interlingua,
[Spoken.Lang("Bahasa Indonesia", "id", "ind")]
Indonesian,
[Spoken.Lang("Iñupiaq", "ik", "ipk")]
Inupiaq,
[Spoken.Lang("Íslenska", "is", "isl")]
Icelandic,
[Spoken.Lang("Italiano", "it", "ita")]
Italian,
[Spoken.Lang("Basa Jawa", "jv", "jav")]
Javanese,
[Spoken.Lang("日本語", "ja", "jpn")]
Japanese,
[Spoken.Lang("Kalaallisut", "kl", "kal")]
Kalaallisut,
[Spoken.Lang("ಕನ್ನಡ", "kn", "kan")]
Kannada,
[Spoken.Lang("कश्मीरी", "ks", "kas")]
Kashmiri,
[Spoken.Lang("ქართული", "ka", "kat")]
Georgian,
[Spoken.Lang("Kanuri", "kr", "kau")]
Kanuri,
[Spoken.Lang("Қазақ тілі", "kk", "kaz")]
Kazakh,
[Spoken.Lang("ភាសាខ្មែរ", "km", "khm")]
Khmer,
[Spoken.Lang("Gĩkũyũ", "ki", "kik")]
Kikuyu,
[Spoken.Lang("Kinyarwanda", "rw", "kin")]
Kinyarwanda,
[Spoken.Lang("кыргыз тили", "ky", "kir")]
Kirghiz,
[Spoken.Lang("коми кыв", "kv", "kom")]
Komi,
[Spoken.Lang("KiKongo", "kg", "kon")]
Kongo,
[Spoken.Lang("한국어", "ko", "kor")]
Korean,
[Spoken.Lang("Kuanyama", "kj", "kua")]
Kuanyama,
[Spoken.Lang("Kurdî", "ku", "kur")]
Kurdish,
[Spoken.Lang("ພາສາລາວ", "lo", "lao")]
Lao,
[Spoken.Lang("Latine", "la", "lat")]
Latin,
[Spoken.Lang("Latviešu valoda", "lv", "lav")]
Latvian,
[Spoken.Lang("Limburgs", "li", "lim")]
Limburgan,
[Spoken.Lang("Lingála", "ln", "lin")]
Lingala,
[Spoken.Lang("Lietuvių kalba", "lt", "lit")]
Lithuanian,
[Spoken.Lang("Lëtzebuergesch", "lb", "ltz")]
Luxembourgish,
[Spoken.Lang("kiluba", "lu", "lub")]
LubaKatanga,
[Spoken.Lang("Luganda", "lg", "lug")]
Ganda,
[Spoken.Lang("Kajin M̧ajeļ", "mh", "mah")]
Marshallese,
[Spoken.Lang("മലയാളം", "ml", "mal")]
Malayalam,
[Spoken.Lang("मराठी", "mr", "mar")]
Marathi,
[Spoken.Lang("македонски јазик", "mk", "mkd")]
Macedonian,
[Spoken.Lang("Fiteny malagasy", "mg", "mlg")]
Malagasy,
[Spoken.Lang("Malti", "mt", "mlt")]
Maltese,
[Spoken.Lang("Монгол", "mn", "mon")]
Mongolian,
[Spoken.Lang("Te reo Māori", "mi", "mri")]
Maori,
[Spoken.Lang("Bahasa Melayu", "ms", "msa")]
Malay,
[Spoken.Lang("ဗမာစာ", "my", "mya")]
Burmese,
[Spoken.Lang("Ekakairũ Naoero", "na", "nau")]
Nauru,
[Spoken.Lang("Diné bizaad", "nv", "nav")]
Navajo,
[Spoken.Lang("Ndébélé", "nr", "nbl")]
SouthNdebele,
[Spoken.Lang("isiNdebele", "nd", "nde")]
NorthNdebele,
[Spoken.Lang("Owambo", "ng", "ndo")]
Ndonga,
[Spoken.Lang("नेपाली", "ne", "nep")]
Nepali,
[Spoken.Lang("Nederlands", "nl", "nld")]
Dutch,
[Spoken.Lang("Norsk nynorsk", "nn", "nno")]
NorwegianNynorsk,
[Spoken.Lang("Norsk bokmål", "nb", "nob")]
NorwegianBokmål,
[Spoken.Lang("Norsk", "no", "nor")]
Norwegian,
[Spoken.Lang("ChiCheŵa", "ny", "nya")]
Nyanja,
[Spoken.Lang("Occitan", "oc", "oci")]
Occitan,
[Spoken.Lang("ᐊᓂᔑᓈᐯᒧᐎᓐ", "oj", "oji")]
Ojibwa,
[Spoken.Lang("ଓଡ଼ିଆ", "or", "ori")]
Oriya,
[Spoken.Lang("Afaan Oromoo", "om", "orm")]
Oromo,
[Spoken.Lang("Ирон ӕвзаг", "os", "oss")]
Ossetian,
[Spoken.Lang("ਪੰਜਾਬੀ", "pa", "pan")]
Panjabi,
[Spoken.Lang("पािऴ", "pi", "pli")]
Pali,
[Spoken.Lang("Polski", "pl", "pol")]
Polish,
[Spoken.Lang("Português", "pt", "por")]
Portuguese,
[Spoken.Lang("پښتو", "ps", "pus")]
Pushto,
[Spoken.Lang("Runa Simi", "qu", "que")]
Quechua,
[Spoken.Lang("Rumantsch grischun", "rm", "roh")]
Romansh,
[Spoken.Lang("Română", "ro", "ron")]
Romanian,
[Spoken.Lang("kiRundi", "rn", "run")]
Rundi,
[Spoken.Lang("русский язык", "ru", "rus")]
Russian,
[Spoken.Lang("Yângâ tî sängö", "sg", "sag")]
Sango,
[Spoken.Lang("संस्कृतम्", "sa", "san")]
Sanskrit,
[Spoken.Lang("සිංහල", "si", "sin")]
Sinhala,
[Spoken.Lang("Slovenčina", "sk", "slk")]
Slovak,
[Spoken.Lang("Slovenščina", "sl", "slv")]
Slovenian,
[Spoken.Lang("Davvisámegiella", "se", "sme")]
NorthernSami,
[Spoken.Lang("Gagana fa'a Samoa", "sm", "smo")]
Samoan,
[Spoken.Lang("chiShona", "sn", "sna")]
Shona,
[Spoken.Lang("सिन्धी", "sd", "snd")]
Sindhi,
[Spoken.Lang("Soomaaliga", "so", "som")]
Somali,
[Spoken.Lang("seSotho", "st", "sot")]
SouthernSotho,
[Spoken.Lang("Español", "es", "spa")]
Spanish,
[Spoken.Lang("Shqip", "sq", "sqi")]
Albanian,
[Spoken.Lang("sardu", "sc", "srd")]
Sardinian,
[Spoken.Lang("српски језик", "sr", "srp")]
Serbian,
[Spoken.Lang("SiSwati", "ss", "ssw")]
Swati,
[Spoken.Lang("Basa Sunda", "su", "sun")]
Sundanese,
[Spoken.Lang("Kiswahili", "sw", "swa")]
Swahili,
[Spoken.Lang("Svenska", "sv", "swe")]
Swedish,
[Spoken.Lang("Reo Mā`ohi", "ty", "tah")]
Tahitian,
[Spoken.Lang("தமிழ்", "ta", "tam")]
Tamil,
[Spoken.Lang("татарча", "tt", "tat")]
Tatar,
[Spoken.Lang("తెలుగు", "te", "tel")]
Telugu,
[Spoken.Lang("тоҷикӣ", "tg", "tgk")]
Tajik,
[Spoken.Lang("Tagalog", "tl", "tgl")]
Tagalog,
[Spoken.Lang("ไทย", "th", "tha")]
Thai,
[Spoken.Lang("ትግርኛ", "ti", "tir")]
Tigrinya,
[Spoken.Lang("faka Tonga", "to", "ton")]
Tonga,
[Spoken.Lang("seTswana", "tn", "tsn")]
Tswana,
[Spoken.Lang("xiTsonga", "ts", "tso")]
Tsonga,
[Spoken.Lang("Türkmen", "tk", "tuk")]
Turkmen,
[Spoken.Lang("Türkçe", "tr", "tur")]
Turkish,
[Spoken.Lang("Twi", "tw", "twi")]
Twi,
[Spoken.Lang("Uyƣurqə", "ug", "uig")]
Uighur,
[Spoken.Lang("українська мова", "uk", "ukr")]
Ukrainian,
[Spoken.Lang("اردو", "ur", "urd")]
Urdu,
[Spoken.Lang("O'zbek", "uz", "uzb")]
Uzbek,
[Spoken.Lang("tshiVenḓa", "ve", "ven")]
Venda,
[Spoken.Lang("Tiếng Việt", "vi", "vie")]
Vietnamese,
[Spoken.Lang("Volapük", "vo", "vol")]
Volapük,
[Spoken.Lang("Walon", "wa", "wln")]
Walloon,
[Spoken.Lang("Wollof", "wo", "wol")]
Wolof,
[Spoken.Lang("isiXhosa", "xh", "xho")]
Xhosa,
[Spoken.Lang("ייִדיש", "yi", "yid")]
Yiddish,
[Spoken.Lang("Yorùbá", "yo", "yor")]
Yoruba,
[Spoken.Lang("Saɯ cueŋƅ", "za", "zha")]
Zhuang,
[Spoken.Lang("中文", "zh", "zho")]
Chinese,
[Spoken.Lang("isiZulu", "zu", "zul")]
Zulu,
}
public static class Spoken
{
public class LangAttribute : Attribute
{
public LangAttribute(string name, string iso639_1, string iso639_3)
{
NativeName = name;
Iso639_1 = iso639_1;
Iso639_3 = iso639_3;
}
public string Iso639_1 { get; }
public string Iso639_3 { get; }
public string NativeName { get; }
}
static readonly Dictionary<string, SpokenLang> isoMap = new Dictionary<string, SpokenLang>();
static readonly Dictionary<SpokenLang, CultureInfo> langToCultureInfo = new Dictionary<SpokenLang, CultureInfo>();
static Spoken()
{
foreach (SpokenLang lang in Enum.GetValues(typeof(SpokenLang)))
{
var langAttr = lang.GetLangAttribute();
isoMap.Add(langAttr.Iso639_1, lang);
isoMap.Add(langAttr.Iso639_3, lang);
isoMap.Add(lang.ToString(), lang);
isoMap.Add(langAttr.NativeName, lang);
}
}
public static bool TryParseLanguageString(string str, out SpokenLang result)
=> isoMap.TryGetValue(str, out result);
public static SpokenLang ParseLanguageString(string str)
{
if (!TryParseLanguageString(str, out var result))
throw new ArgumentException($"unknown language string: {str}");
return result;
}
public static LangAttribute GetLangAttribute(this SpokenLang enumVal)
{
var type = enumVal.GetType();
var memInfo = type.GetMember(enumVal.ToString());
var attributes = memInfo[0].GetCustomAttributes(typeof(LangAttribute), false);
return (attributes.Length > 0) ? (LangAttribute)attributes[0] : null;
}
public static CultureInfo ToCultureInfo(this SpokenLang language) =>
CultureInfo.GetCultureInfo(language.GetLangAttribute().Iso639_1);
}
}
从@PieterV的t4模板构建