LCOV - code coverage report
Current view: top level - src - detector.dart (source / functions) Coverage Total Hit
Test: lcov.info Lines: 100.0 % 18 18
Test Date: 2026-08-24 07:45:26 Functions: - 0 0

            Line data    Source code
       1              : // Copyright 2026 The Authors.
       2              : //
       3              : // Licensed under the Apache License, Version 2.0 (the "License");
       4              : // you may not use this file except in compliance with the License.
       5              : // You may obtain a copy of the License at
       6              : //
       7              : //     https://www.apache.org/licenses/LICENSE-2.0
       8              : //
       9              : // Unless required by applicable law or agreed to in writing, software
      10              : // distributed under the License is distributed on an "AS IS" BASIS,
      11              : // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      12              : // See the License for the specific language governing permissions and
      13              : // limitations under the License.
      14              : 
      15              : import 'backend.dart';
      16              : import 'composite_backend.dart';
      17              : import 'guess.dart';
      18              : import 'script/script_filter.dart' as script_filter;
      19              : 
      20              : /// Detects the language of a piece of text.
      21              : ///
      22              : /// Use [LanguageDetector.pureDart] for the zero-dependency default (a
      23              : /// Unicode script pre-filter plus a character n-gram model, covering 58
      24              : /// languages — see `CompositeBackend`), or supply a custom [backend] for
      25              : /// testing or an alternative scoring strategy.
      26              : ///
      27              : /// Example:
      28              : /// ```dart
      29              : /// final detector = LanguageDetector.pureDart();
      30              : /// switch (detector.detect('Bonjour le monde')) {
      31              : ///   case Detected(best: final guess):
      32              : ///     print('${guess.code} (${guess.confidence})'); // fr (...)
      33              : ///   case Undetermined():
      34              : ///     print('could not determine a language');
      35              : /// }
      36              : /// ```
      37              : final class LanguageDetector {
      38              :   /// Creates a detector scoring with [backend].
      39              :   ///
      40              :   /// [restrictTo], when non-null, is applied as a post-hoc filter over
      41              :   /// [backend]'s results: guesses whose code is not in [restrictTo] are
      42              :   /// dropped before ranking. This works for *any* backend, but does not
      43              :   /// improve a custom backend's internal accuracy — only [pureDart] threads
      44              :   /// [restrictTo] into the scoring stage itself, which is what actually
      45              :   /// sharpens n-gram accuracy.
      46            1 :   LanguageDetector({
      47              :     required this.backend,
      48              :     this.minConfidence = 0.5,
      49              :     this.restrictTo,
      50              :   });
      51              : 
      52              :   /// The scoring strategy this detector delegates to.
      53              :   final LanguageDetectorBackend backend;
      54              : 
      55              :   /// The minimum confidence (in `[0.0, 1.0]`) a guess must reach for
      56              :   /// [detect] to return [Detected] rather than [Undetermined].
      57              :   final double minConfidence;
      58              : 
      59              :   /// If non-null, guesses whose code is not in this set are dropped before
      60              :   /// ranking. See the constructor doc for how this interacts with
      61              :   /// [pureDart]'s backend-level narrowing.
      62              :   final Set<String>? restrictTo;
      63              : 
      64              :   /// The zero-dependency default: a Unicode script pre-filter plus a
      65              :   /// character n-gram model, covering 58 languages (see `CompositeBackend`).
      66              :   ///
      67              :   /// [restrictTo], when supplied, is threaded into the n-gram stage so only
      68              :   /// the given languages' profiles are compared — the biggest practical
      69              :   /// accuracy lever for this detector.
      70            1 :   factory LanguageDetector.pureDart({
      71              :     double minConfidence = 0.5,
      72              :     Set<String>? restrictTo,
      73              :   }) {
      74            1 :     return LanguageDetector(
      75            1 :       backend: CompositeBackend(restrictTo: restrictTo),
      76              :       minConfidence: minConfidence,
      77              :       restrictTo: restrictTo,
      78              :     );
      79              :   }
      80              : 
      81              :   /// Full detection: scores [text] with [backend], applies [restrictTo] (if
      82              :   /// set) and [minConfidence], and ranks the result by descending
      83              :   /// confidence.
      84            1 :   DetectionResult detect(String text) {
      85            2 :     var guesses = backend.score(text);
      86            1 :     final allowed = restrictTo;
      87              :     if (allowed != null) {
      88            1 :       guesses = [
      89            1 :         for (final g in guesses)
      90            3 :           if (allowed.contains(g.code)) g,
      91              :       ];
      92              :     }
      93            1 :     guesses = [...guesses]
      94            5 :       ..sort((a, b) => b.confidence.compareTo(a.confidence));
      95              : 
      96            2 :     if (guesses.isEmpty) return Undetermined(guesses);
      97              : 
      98            1 :     final best = guesses.first;
      99            3 :     if (best.confidence >= minConfidence) {
     100            1 :       return Detected(best, guesses);
     101              :     }
     102            1 :     return Undetermined(guesses);
     103              :   }
     104              : 
     105              :   /// Cheap, script-only classification of [text] — does not run the n-gram
     106              :   /// stage.
     107              :   ///
     108              :   /// Returns a 4-letter ISO 15924 script code (e.g. `"Latn"`, `"Cyrl"`,
     109              :   /// `"Hani"`) for the most common script among the input's letter
     110              :   /// codepoints, or `null` if the input has no scripted letters.
     111              :   ///
     112              :   /// This always uses the built-in script table — it is not affected by a
     113              :   /// custom [backend], since script classification is a fixed, deterministic
     114              :   /// Unicode property lookup, not a pluggable strategy.
     115            2 :   String? dominantScript(String text) => script_filter.dominantScript(text);
     116              : }
        

Generated by: LCOV version 2.0-1