webmil/text-language-detect-bundle
Symfony bundle for detecting the language of text using the webmil text-language-detect library. Integrates language identification into Symfony apps with service configuration, useful for localization, content filtering, and routing based on detected language.
Before writing any code, understand what this package actually is: a Symfony2 bundle, not a Laravel package. It ships an Extension/Configuration pair and a services.xml that registers the PEAR class Text_LanguageDetect as a container service. There is no service provider, no facade, no config publish tag, and its composer.json will constrain symfony/framework-bundle to a 2.x line — installing it into a modern Laravel app will either fail dependency resolution or drag in dead Symfony 2 components.
Where to look first:
composer.json — note the Symfony 2 requirement and how Text_LanguageDetect is pulled in (PEAR include path vs. vendored copy).DependencyInjection/WebmilTextLanguageDetectExtension.php — this is where the service id and the wrapped class are defined. That's the only piece of real value in the repo.Resources/config/services.xml — the constructor arguments / setter calls the bundle applies (usually setNameMode or a data-directory path).Minimal steps in a Laravel app: skip the bundle, install the underlying library directly.
composer require pear/text_languagedetect
use Text_LanguageDetect;
$detector = new Text_LanguageDetect();
$detector->setNameMode(Text_LanguageDetect::ISO_CODE);
$code = $detector->detectSimple('Bonjour tout le monde'); // e.g. "fra"
First realistic use case: an inbound support/contact form where you want to tag the ticket with a language before routing it to a queue or choosing a reply template.
Bind a singleton in a service provider. The detector lazily loads trigram frequency data on first call; constructing one per job is pure overhead.
$this->app->singleton(\Text_LanguageDetect::class, function () {
$d = new \Text_LanguageDetect();
$d->setNameMode(\Text_LanguageDetect::ISO_CODE);
return $d;
});
Wrap it, don't scatter it. Create App\Support\LanguageDetector with a single detect(string $text): ?string that owns the confidence threshold. Consumers should never see the raw detect() array.
public function detect(string $text): ?string
{
$text = mb_substr(trim($text), 0, 2000, 'UTF-8');
if (mb_strlen($text) < 20) return null;
$results = $this->detector->detect($text); // [ 'english' => 0.83, ... ]
if (empty($results)) return null;
$best = array_key_first($results);
return $results[$best] >= 0.60 ? $best : null;
}
Validation rule. A ValidLanguage rule that checks a submitted body against an Accept-Language or expected-locale field, and warns (not rejects) on mismatch — detection is probabilistic, never a hard gate.
Middleware fallback. If Accept-Language is missing or *, detect from the first POSTed body field and set the locale for that request. Cache the result on the session so you don't re-run on every request.
Batch backfill. Queue job that chunks existing rows (chunkById(200)), detects, and writes a language column. Do it in a job, not a migration — a full-table pass will time out.
Cache by content hash. Cache::remember('lang:'.sha1($text), 86400, fn() => $this->detect($text)). Detection is cheap-ish but not free, and identical form resubmissions are common.
If you are genuinely stuck maintaining a Symfony 2 app and must use the bundle as-is: register the bundle in AppKernel, then inject the service by the id found in services.xml. Don't try to hand-wire the bundle's Extension into Laravel's container — instantiate the underlying class directly instead; that's all the bundle does anyway.
detect()/detectSimple() return full English names ("english"), not ISO codes. Call setNameMode() explicitly or you'll map "english" against a en column and silently store garbage. The constant names also differ between PEAR releases — check the installed source rather than trusting docs.Text_LanguageDetect with underscores. No PSR-4. Composer's PSR-0 fallback or a classmap entry is required; on some setups you also need set_include_path() for the data/ directory holding the trigram models. If those data files aren't present, detection returns an empty array rather than throwing — a classic silent failure.null instead of a wrong answer.substr) can split a multibyte character and corrupt the trigram lookup. Always use mb_substr.mb_substr($text, 0, 2000) is the single biggest performance win.new Text_LanguageDetect() over 10k rows will balloon memory as each instance loads model data. Reuse one instance, or resolve from the container.detect() returns a sorted associative array, highest probability first — but the values are relative scores, not calibrated probabilities. A 0.99 does not mean 99% certain. Tune your threshold empirically against your own fixtures.pear/text_languagedetect directly, or a modern alternative such as umpirsky/language-detect / napp/xray-language-detect, which ship proper PSR-4 autoloading and Laravel-friendly service providers.detectSimple returns the expected code — this catches data-directory and name-mode regressions instantly.How can I help you explore Laravel packages today?