mirror of
https://scm.univ-tours.fr/22107988t/rappaurio-sae501_502.git
synced 2025-08-29 23:45:59 +02:00
458 lines
13 KiB
JavaScript
458 lines
13 KiB
JavaScript
/**
|
||
* @name CeL function for 繁簡中文字詞彙轉換。
|
||
*
|
||
* TODO:<br />
|
||
* 在量大的時候,此方法速度頗慢。 Using Map()<br />
|
||
* words conversion
|
||
*
|
||
* @fileoverview 本檔案包含了繁體/簡體中文轉換的 functions。
|
||
* @example <code>
|
||
|
||
// 在非 Windows 平台上避免 fatal 錯誤。
|
||
CeL.env.ignore_COM_error = true;
|
||
// load module for CeL.CN_to_TW('简体')
|
||
CeL.run('extension.zh_conversion', function() {
|
||
var text = CeL.CN_to_TW('简体中文文字');
|
||
CeL.CN_to_TW.file('from.htm', 'to.htm', 'utf-8');
|
||
});
|
||
|
||
</code>
|
||
* @see https://github.com/BYVoid/OpenCC https://zhconvert.org/
|
||
* https://en.wiktionary.org/wiki/Module:zh
|
||
* @since 2014/6/17 22:39:16
|
||
*/
|
||
|
||
'use strict';
|
||
|
||
// --------------------------------------------------------------------------------------------
|
||
|
||
// 不採用 if 陳述式,可以避免 Eclipse JSDoc 與 format 多縮排一層。
|
||
typeof CeL === 'function' && CeL.run({
|
||
// module name
|
||
name : 'extension.zh_conversion',
|
||
|
||
require : 'data.|data.Convert_Pairs.|application.OS.Windows.file.',
|
||
|
||
// 設定不匯出的子函式。
|
||
no_extend : 'generate_converter',
|
||
|
||
// 為了方便格式化程式碼,因此將 module 函式主體另外抽出。
|
||
code : module_code
|
||
});
|
||
|
||
function module_code(library_namespace) {
|
||
// requiring
|
||
var Convert_Pairs = library_namespace.data.Convert_Pairs;
|
||
|
||
/**
|
||
* null module constructor
|
||
*
|
||
* @class 中文繁簡轉換的 functions
|
||
*/
|
||
var _// JSDT:_module_
|
||
= function() {
|
||
// null module constructor
|
||
};
|
||
|
||
/**
|
||
* for JSDT: 有 prototype 才會將之當作 Class
|
||
*/
|
||
_// JSDT:_module_
|
||
.prototype = {};
|
||
|
||
// ------------------------------------------------------------------------
|
||
|
||
// new RegExp(key, REPLACE_FLAG);
|
||
var REPLACE_FLAG = undefined,
|
||
// using BYVoid / OpenCC 開放中文轉換 (Open Chinese Convert) table.
|
||
// https://github.com/BYVoid/OpenCC/tree/master/data/dictionary
|
||
dictionary_base = library_namespace.get_module_path(this.id, 'OpenCC'
|
||
+ library_namespace.env.path_separator);
|
||
// console.log('dictionary_base: ' + dictionary_base);
|
||
|
||
function Converter(options) {
|
||
// console.log(options);
|
||
|
||
// e.g., .files, .file_filter
|
||
Object.assign(this, options);
|
||
// console.trace(this.files);
|
||
}
|
||
|
||
function Converter_initialization(options) {
|
||
// console.trace(this.files);
|
||
|
||
function item_processor(item) {
|
||
var matched = item.match(/^([^\t]+)\t([^\t]+)$/);
|
||
if (matched) {
|
||
if (!matched[1].trim())
|
||
return;
|
||
if (/ [^\t]+$/.test(matched[2])) {
|
||
if (matched[2].startsWith(matched[1] + ' ')) {
|
||
// console.log('詞有疑意: ' + item);
|
||
// 但像是"小丑"之類的還是必須保留。
|
||
// return;
|
||
}
|
||
// 必須置換,那就換個最常用的。
|
||
return item.replace(/ +[^\t]+$/, '');
|
||
}
|
||
}
|
||
return item;
|
||
}
|
||
|
||
function corrections_item_processor(item, options) {
|
||
var matched = item.match(/^-([^\t\n]{1,30})$/);
|
||
if (!matched) {
|
||
return item;
|
||
}
|
||
remove_key_hash[matched[1]] = options.path;
|
||
return '';
|
||
}
|
||
|
||
function to_full_file_path(file_path) {
|
||
return /[\\\/]/.test(file_path) ? file_path : dictionary_base
|
||
+ file_path + '.txt';
|
||
}
|
||
|
||
this.conversions = [];
|
||
this.files.map(function(file_list) {
|
||
var _options = {
|
||
file_filter : this.file_filter,
|
||
|
||
// no_the_same_key_value : !Array.isArray(file_list)
|
||
// || file_list.length < 2,
|
||
|
||
item_processor : item_processor,
|
||
// 在開始轉換之後就不會再修改辭典檔,因此可移除 .pair_Map。
|
||
may_remove_pair_Map : !options || !options.mode
|
||
};
|
||
|
||
if (!Array.isArray(file_list))
|
||
file_list = [ file_list ];
|
||
|
||
_options.path = file_list
|
||
// 載入 resources。
|
||
.map(function(file_path) {
|
||
if (typeof file_path === 'string')
|
||
return to_full_file_path(file_path);
|
||
// assert: library_namespace.is_Object(file_path)
|
||
var __options = file_path;
|
||
// e.g., for .remove_comments
|
||
if (!__options.file_path && !__options.path
|
||
//
|
||
&& __options.file_name) {
|
||
__options.file_path
|
||
//
|
||
= to_full_file_path(__options.file_name);
|
||
}
|
||
// assert: !!__options.file_path === true
|
||
return __options;
|
||
});
|
||
|
||
var convert_Pairs = new Convert_Pairs(null, _options);
|
||
if (convert_Pairs.pair_Map.size > 0) {
|
||
// console.trace([ convert_Pairs.pair_Map.get('猜拳斗酒') ]);
|
||
this.conversions.push(convert_Pairs);
|
||
}
|
||
}, this);
|
||
delete this.file_filter;
|
||
// console.log(this.conversions);
|
||
// console.trace(this.conversions[0].pair_Map.size);
|
||
|
||
// --------------------------------------
|
||
|
||
if (this.corrections) {
|
||
// keys_to_remove
|
||
var remove_key_hash = Object.create(null);
|
||
// this.conversions: 手動修正表。提供自行更改的功能。
|
||
this.conversions.push(new Convert_Pairs(null, {
|
||
path : dictionary_base.replace(/[^\\\/]+[\\\/]$/,
|
||
this.corrections),
|
||
item_processor : corrections_item_processor,
|
||
remove_comments : true
|
||
}));
|
||
delete this.corrections;
|
||
|
||
if (!library_namespace.is_empty_object(remove_key_hash)) {
|
||
this.conversions.forEach(function(conversion) {
|
||
// console.trace(conversion.pair_Map.get('猜拳斗酒'));
|
||
if (false && conversion.pair_Map.get('猜拳斗酒')) {
|
||
console.log(conversion);
|
||
throw conversion.pair_Map.get('猜拳斗酒');
|
||
}
|
||
conversion.remove(remove_key_hash, {
|
||
remove_matched_path : true
|
||
});
|
||
});
|
||
// free
|
||
remove_key_hash = null;
|
||
}
|
||
}
|
||
|
||
// 設定事前轉換表。
|
||
if (this.prefix_conversions) {
|
||
this.conversions.unshift(new Convert_Pairs(this.prefix_conversions,
|
||
{
|
||
flags : this.flags || REPLACE_FLAG
|
||
}));
|
||
delete this.prefix_conversions;
|
||
}
|
||
|
||
// 設定事後轉換表。
|
||
if (this.postfix_conversions) {
|
||
this.conversions.push(new Convert_Pairs(this.postfix_conversions, {
|
||
flags : this.flags || REPLACE_FLAG
|
||
}));
|
||
delete this.postfix_conversions;
|
||
}
|
||
|
||
// console.trace(this('签'));
|
||
}
|
||
|
||
// convert text
|
||
function convert_text(text, options) {
|
||
if (!this.conversions) {
|
||
this.initialization(options);
|
||
}
|
||
|
||
// 事前轉換表。
|
||
if (options && options.prefix_conversions) {
|
||
text = (new Convert_Pairs(options.prefix_conversions, {
|
||
flags : options.flags || REPLACE_FLAG
|
||
})).convert(text);
|
||
}
|
||
|
||
var for_each_conversion;
|
||
if (!options) {
|
||
} else if (options.mode === 'word') {
|
||
// 僅轉換完全相符的詞彙 key。
|
||
for_each_conversion = function(_text, conversion) {
|
||
// console.log(conversion.pair)
|
||
var convert_to = conversion.get_value(_text);
|
||
return typeof convert_to === 'string' ? convert_to : _text;
|
||
};
|
||
} else if (false && options.mode === 'word_first') {
|
||
// 輸入單一詞彙時使用,以期加快速度...可惜沒有。
|
||
// node.js: 直接開 `conversion.convert(text)` 速度相同,且還包含
|
||
// .special_keys_Map 的轉換,較完整。
|
||
for_each_conversion = function(_text, conversion) {
|
||
var convert_to = conversion.get_value(_text);
|
||
return typeof convert_to === 'string' ? convert_to : conversion
|
||
.convert(_text, options);
|
||
};
|
||
}
|
||
|
||
text = this.conversions.reduce(for_each_conversion
|
||
|| function(_text, conversion) {
|
||
return conversion.convert(_text, options);
|
||
}, text);
|
||
// console.trace(text);
|
||
|
||
if (!(this.max_convert_word_length >= 0)) {
|
||
this.max_convert_word_length = this.conversions.reduce(function(
|
||
length, conversion) {
|
||
return Math.max(length, conversion.pair_Map_by_length.length);
|
||
}, 0);
|
||
// console.trace(this);
|
||
if (this['interface'])
|
||
this['interface'].max_convert_word_length = this.max_convert_word_length;
|
||
}
|
||
|
||
// 事後轉換表。
|
||
if (options && options.postfix_conversions) {
|
||
text = (new Convert_Pairs(options.postfix_conversions, {
|
||
flags : options.flags || REPLACE_FLAG
|
||
})).convert(text);
|
||
}
|
||
|
||
return text;
|
||
}
|
||
|
||
// convert text file
|
||
function convert_file(from, to, target_encoding) {
|
||
var text = library_namespace.get_file(from);
|
||
text = this(text);
|
||
library_namespace.write_file(to, text, target_encoding);
|
||
}
|
||
|
||
Object.assign(Converter.prototype, {
|
||
initialization : Converter_initialization,
|
||
convert : convert_text,
|
||
file : convert_file
|
||
});
|
||
|
||
Converter.options = {
|
||
CN_to_TW : {
|
||
// 事前事後轉換表須事先設定。
|
||
// 不可以 Object.assign(CeL.CN_to_TW.prefix_conversions = {}, {})
|
||
// 來新增事前轉換表。
|
||
// prefix_conversions : {},
|
||
// postfix_conversions : {},
|
||
|
||
files : [ [ 'STPhrases', 'STCharacters',
|
||
// 以 generate_additional_table.js 合併新同文堂和 ConvertZZ 的辭典檔。
|
||
'additional.to_TW.auto-generated',
|
||
// 後來的會覆蓋前面的。
|
||
{
|
||
file_name : 'additional.to_TW',
|
||
remove_comments : true
|
||
} ],
|
||
// ------------------------------------------------------
|
||
// ** 下面的是上面詞彙與單字轉換後的再轉換。
|
||
[ 'TWPhrasesIT',
|
||
// ↑ TWPhrasesIT.txt 有許多常用詞彙,在 corrections_to_TW.txt 取消。
|
||
'TWPhrasesName', 'TWPhrasesOther',
|
||
// 若要篩選或增減 conversion files,可參考範例:
|
||
// start_downloading() @ CeL.application.net.work_crawler.task
|
||
{
|
||
file_name : 'additional.to_TW.phrases',
|
||
remove_comments : true
|
||
} ],
|
||
// https://github.com/BYVoid/OpenCC/blob/master/data/config/s2twp.json
|
||
'TWVariants' ],
|
||
corrections : 'corrections_to_TW.txt'
|
||
},
|
||
TW_to_CN : {
|
||
// 事前事後轉換表須事先設定。
|
||
// prefix_conversions : {},
|
||
// postfix_conversions : {},
|
||
|
||
// https://github.com/BYVoid/OpenCC/blob/master/data/config/tw2s.json
|
||
// https://github.com/BYVoid/OpenCC/blob/master/node/dicts.gypi
|
||
files : [ 'TWVariantsRevPhrases', [ 'TSPhrases', 'TSCharacters',
|
||
// 以 generate_additional_table.js 合併新同文堂和 ConvertZZ 的辭典檔。
|
||
'additional.to_CN.auto-generated',
|
||
// 後來的會覆蓋前面的。
|
||
{
|
||
file_name : 'additional.to_CN',
|
||
remove_comments : true
|
||
} ] ],
|
||
// ------------------------------------------------------
|
||
// ** 下面的是上面詞彙與單字轉換後的再轉換。
|
||
corrections : 'corrections_to_CN.txt'
|
||
}
|
||
};
|
||
|
||
// ------------------------------------------------------------------------
|
||
|
||
function set_as_default(method_name, method) {
|
||
library_namespace[method_name] = _[method_name] = method;
|
||
}
|
||
|
||
function generate_converter(type, options) {
|
||
options = library_namespace.setup_options(options);
|
||
if (!(type in Converter.options)) {
|
||
library_namespace.error(
|
||
//
|
||
'generate_converter: Invalid type: ' + type);
|
||
return;
|
||
}
|
||
|
||
var converter = new Converter(Object.assign(Object
|
||
.clone(Converter.options[type]), options));
|
||
var converter_interface = converter.convert.bind(converter);
|
||
converter['interface'] = converter_interface;
|
||
if (options.set_as_default) {
|
||
set_as_default(type, converter_interface);
|
||
}
|
||
return converter_interface;
|
||
}
|
||
|
||
var cecc;
|
||
function using_CeCC(options) {
|
||
// 前置處理。
|
||
options = library_namespace.setup_options(options);
|
||
if (cecc && !options.force_using_cecc) {
|
||
library_namespace.debug('CeCC loaded.');
|
||
return true;
|
||
}
|
||
|
||
var CeCC;
|
||
try {
|
||
CeCC = require('cecc');
|
||
} catch (e) {
|
||
try {
|
||
// base_path/CeJS/ce.js
|
||
// base_path/Chinese_converter/Chinese_converter.js
|
||
CeCC = require(library_namespace.simplify_path(
|
||
//
|
||
library_namespace.get_module_path().replace(/[^\\\/]*$/,
|
||
'../Chinese_converter/Chinese_converter.js')));
|
||
} catch (e) {
|
||
}
|
||
}
|
||
|
||
if (!CeCC) {
|
||
return;
|
||
}
|
||
|
||
if (!options.try_LTP_server) {
|
||
cecc = new CeCC(options);
|
||
return setup_CeCC_methods(options);
|
||
}
|
||
|
||
return CeCC.has_LTP_server(options).then(function(LTP_URL) {
|
||
if (!LTP_URL)
|
||
return;
|
||
|
||
options.LTP_URL = LTP_URL;
|
||
cecc = new CeCC(options);
|
||
cecc.is_asynchronous = true;
|
||
return setup_CeCC_methods(options);
|
||
});
|
||
}
|
||
|
||
function setup_CeCC_methods(options) {
|
||
library_namespace.info('using_CeCC: Using CeCC ('
|
||
+ (cecc.is_asynchronous ? 'asynchronous' : 'synchronous')
|
||
+ ' version) to convert language.');
|
||
var methods = {
|
||
CN_to_TW : 'to_TW',
|
||
TW_to_CN : 'to_TW'
|
||
};
|
||
for ( var method_name in methods) {
|
||
var cecc_name = methods[method_name];
|
||
if (!cecc.is_asynchronous)
|
||
cecc_name += '_sync';
|
||
var method = cecc[cecc_name].bind(cecc);
|
||
method.is_CeCC = true;
|
||
method.cecc = cecc;
|
||
if (cecc.is_asynchronous)
|
||
method.is_asynchronous = true;
|
||
set_as_default(method_name, method);
|
||
}
|
||
return true;
|
||
}
|
||
|
||
// ------------------------------------------------------------------------
|
||
// export
|
||
|
||
_.generate_converter = generate_converter;
|
||
_.using_CeCC = using_CeCC;
|
||
|
||
try {
|
||
var OpenCC = require('opencc');
|
||
// Load the default Simplified to Traditional config
|
||
_.CN_to_TW = new OpenCC('s2t.json');
|
||
// Sync API
|
||
_.CN_to_TW = _.CN_to_TW.convertSync.bind(_.CN_to_TW);
|
||
|
||
_.TW_to_CN = new OpenCC('t2s.json');
|
||
_.TW_to_CN = _.TW_to_CN.convertSync.bind(_.TW_to_CN);
|
||
|
||
} catch (e) {
|
||
// CeL.application.net.work_crawler.task will re-generate the functions!
|
||
generate_converter('CN_to_TW', {
|
||
set_as_default : true
|
||
});
|
||
generate_converter('TW_to_CN', {
|
||
set_as_default : true
|
||
});
|
||
}
|
||
|
||
// Warning: require('cecc') will overwrite CeL.CN_to_TW, CeL.TW_to_CN !
|
||
|
||
return (_// JSDT:_module_
|
||
);
|
||
|
||
}
|