Определение языка и кодировки. Компонент для CakePHP
Материал скопирован.., источник ниже..
Для моего текущего проекта необходимо определять на каком языке пользователь вводит информацию. Причём это не сложный выбор между PHP и Perl, а, например, между английским и испанским. Сначала я хотел составить список самых распространённых слов в популярных языках - предлоги, частые глаголы и т.д. Почти сразу я понял, что точность будет небольшая, а работы - очень много, даже, если её буду делать не я :). Поэтому пришлось думать дальше. Мне больше всего понравился способ, в котором учитывается частотность букв, двух-, трёх- и четырёхбуквенных сочетаний.
Сначала система обучается - ей скармливается много текста и указывается, какой это язык. Она разбирает их на части и запоминает наиболее часто встречаемые как эталонные для этого языка.
Потом берётся текст с неизвестным языком и определяются наиболее часто встречаемые части в нём. Эти части сравниваются с эталонными и на основе этого определяется вероятность каждого из языков.
Я взял за основу код с http://boxoffice.ch/pseudo/ng.php, удалил лишнее, немного оптимизировал алгоритм и сделал код красивый и подходящий для CakePHP.
Не каждый человек сможет понять, что это за язык :). А компонент понял - russian-utf8. Кстати, сразу видно бонус - определяется кодировка. Для русского поддерживаются ISO, KOI8-R, UTF-8, Windows-1251.
Если надо определить несколько наиболее вероятных языков, то надо указать второй параметр true.
Чтобы компонент заработал, нужно скачать скомпилированную информацию о языках и положить этот fingerprint.dat в app/vendors.
А вот и сам компонент:
<?php /**
* Language detection component
*
* Most of code was copied from http://boxoffice.ch/pseudo/code_expl/code_class.php
* but a lot of things were simplified and beautified
*
* @link http://php.southpark.com.ua
* @author Vladimir Luchaninov
* @version 1.0 (3 Dec 2007)
*
*/
class LangDetectComponent {
protected $fingerprint = null;
protected $ngrams = array();
//reasonable defaults
public $ngramCount = 350; //default nb of ngrams created from analyzed text
public $maxDelta = 140000; //stop evaluation deviate strongly
/**
* Main function
*
* @param string $text Text with unknown language
* @param bool $onlyBest
* true - detect the best language
* false - detect all languages with possibilities
* @return LangDetect
*/
function detect($text, $onlyBest = true) {
if (empty($text)) {
trigger_error('Text should not be empty');
return false;
}
/**
* Get list of the available languages
*
* @return array List of languages
*/
function listLanguages() {
$languages = array_keys($this->fingerprint);
sort($languages);
return $languages;
}
/**
* Create ngram-array of given string
*
* @param string $text
*
*/
protected function createNGrams($text) {
$array_words = explode(" ", $text);
$ngrams = array();
foreach($array_words as $word) {
$word = "_". $word . "_";
$wordLength = strlen($word);
for ($i=0; $i <$wordLength; $i++) { //start position within word
for ($s=1; $s <4+1; $s++) { //length of ngram
if (($i + $s) <$wordLength + 1) { //length depends on postion
$ngrams[] = substr($word, $i, $s);
}
}
}
}
/**
* Compare ngrams: Textinput vs lm-files.
*
* @return array of languages with lowest deviation
*/
protected function compareNGrams() {
$limit = $this->maxDelta;
foreach ($this->fingerprint as $basename => $language) {
$delta = 0;
//compare each ngram of input text to current lm-array
foreach ($this->ngrams as $key => $ngram){
//match
if(in_array($ngram, $language)) {
$delta += abs($key - array_search($ngram, $language));
//no match
} else {
$delta += 400;
}
//abort: this language already differs too much
if ($delta> $this->maxDelta) {
break;
}
} // End comparison with current language
//include only non-aborted languages in result array
if ($delta <($this->maxDelta)-400) {
$result[$basename] = $delta;
}
} //End comparison all languages