lingua_rs 0.5.0 → 0.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +55 -3
- data/exe/lingua +125 -0
- data/ext/lingua/extconf.rb +6 -0
- data/lib/lingua/version.rb +1 -1
- metadata +6 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 94314611395fba47d42021ab91fc1f0db7b7f5311a64796a8c98b36372c5b2b0
|
|
4
|
+
data.tar.gz: c627fa1a1510945411506662fb22907546f4a5916352680aa09e86f5f93f9d33
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 202e31c4f0775dfd7f9976720efcde6c0d7af4a3169771d3690f01f5b960a55406e697a06ecf6c48a682ad39f264ccfec2b2dc14bdc7d5a3fc0fcfd5d966c06a
|
|
7
|
+
data.tar.gz: 618a2269f52c7dd1a0999f93f13e7a58b02aa6e2a584a161e89c914166b7299dd03ef3b2401599997ef91d58ef7c2586daca2968bf125efc0edbf4bd62efb51c
|
data/README.md
CHANGED
|
@@ -25,7 +25,7 @@ Then run `bundle install`.
|
|
|
25
25
|
|
|
26
26
|
### Build requirements
|
|
27
27
|
|
|
28
|
-
A Rust toolchain is required to compile the native extension.
|
|
28
|
+
A Rust toolchain (**1.87+**) is required to compile the native extension.
|
|
29
29
|
|
|
30
30
|
## Usage
|
|
31
31
|
|
|
@@ -157,9 +157,18 @@ detector.detect_multiple_batch(['Bonjour le monde. Hello world.', 'Ich bin müde
|
|
|
157
157
|
| Method | Return type | Description |
|
|
158
158
|
|---|---|---|
|
|
159
159
|
| `detect_batch(texts)` | `Array<Language\|nil>` | Detect language of each text |
|
|
160
|
-
| `detect_multiple_batch(texts)` | `Array<Array<Segment>>` | Mixed-language detection for each text |
|
|
161
|
-
| `confidence_values_batch(texts)` | `Array<Array<ConfidenceResult>>` | Confidence values for each text |
|
|
162
160
|
| `confidence_batch(texts, language)` | `Array<Float>` | Confidence for a specific language across texts |
|
|
161
|
+
| `confidence_values_batch(texts)` | `Array<Array<ConfidenceResult>>` | Confidence values for each text |
|
|
162
|
+
| `detect_multiple_batch(texts)` | `Array<Array<Segment>>` | Mixed-language detection for each text |
|
|
163
|
+
|
|
164
|
+
Results are returned in the same order as the input, so you can pair them with `zip`:
|
|
165
|
+
|
|
166
|
+
```ruby
|
|
167
|
+
texts = ['Bonjour le monde', 'Hello world', 'Hallo Welt']
|
|
168
|
+
texts.zip(detector.detect_batch(texts)) do |text, language|
|
|
169
|
+
puts "#{text} => #{language}"
|
|
170
|
+
end
|
|
171
|
+
```
|
|
163
172
|
|
|
164
173
|
### Error handling
|
|
165
174
|
|
|
@@ -231,6 +240,49 @@ Returned by `detect_multiple`.
|
|
|
231
240
|
| `to_s` | `String` | `'French (0-22): Parlez-vous français? '` |
|
|
232
241
|
| `inspect` | `String` | `'#<Lingua::Segment French (0-22) "Parlez-vous français? ">'` |
|
|
233
242
|
|
|
243
|
+
## Command-line tool
|
|
244
|
+
|
|
245
|
+
The gem ships with a `lingua` executable that wraps the library for shell use.
|
|
246
|
+
|
|
247
|
+
```bash
|
|
248
|
+
# Text as argument or on stdin
|
|
249
|
+
lingua 'Bonjour le monde' # => French
|
|
250
|
+
echo 'Hello world' | lingua # => English
|
|
251
|
+
|
|
252
|
+
# Output format
|
|
253
|
+
lingua -f iso 'Bonjour le monde' # => fr
|
|
254
|
+
lingua -f iso6393 'Bonjour le monde' # => fra
|
|
255
|
+
lingua -f json 'Bonjour le monde' # => {"name":"French","iso":"fr","iso6393":"fra"}
|
|
256
|
+
|
|
257
|
+
# Confidence values
|
|
258
|
+
lingua -c -l en,fr,de 'Bonjour le monde'
|
|
259
|
+
# French: 0.8217
|
|
260
|
+
# German: 0.1216
|
|
261
|
+
# English: 0.0567
|
|
262
|
+
|
|
263
|
+
# Mixed-language detection
|
|
264
|
+
lingua -m -l en,fr,de 'Parlez-vous français? Hello world.'
|
|
265
|
+
# French (0-22): Parlez-vous français?
|
|
266
|
+
# English (22-34): Hello world.
|
|
267
|
+
|
|
268
|
+
# Restrict candidates
|
|
269
|
+
lingua -l en,fr,de 'Bonjour'
|
|
270
|
+
|
|
271
|
+
# Batch: one text per line (use "-" for stdin)
|
|
272
|
+
lingua --batch texts.txt
|
|
273
|
+
cat texts.txt | lingua --batch -
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
| Option | Description |
|
|
277
|
+
|---|---|
|
|
278
|
+
| `-f`, `--format FORMAT` | Output format: `name` (default), `iso`, `iso6393`, `json` |
|
|
279
|
+
| `-c`, `--confidences` | Output confidence for all candidate languages |
|
|
280
|
+
| `-m`, `--multiple` | Detect multiple languages within the text |
|
|
281
|
+
| `-l`, `--languages LIST` | Comma-separated list of languages (names or ISO codes) |
|
|
282
|
+
| `--batch FILE` | Treat each line of FILE (or `-` for stdin) as a separate input |
|
|
283
|
+
| `-v`, `--version` | Show version |
|
|
284
|
+
| `-h`, `--help` | Show help |
|
|
285
|
+
|
|
234
286
|
## Optimization: selecting languages
|
|
235
287
|
|
|
236
288
|
By default, all 75 languages are compiled into the native extension (~278 MB). If you only need a subset, select languages at build time to reduce binary size and improve detection speed.
|
data/exe/lingua
ADDED
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require 'optparse'
|
|
5
|
+
require 'json'
|
|
6
|
+
require 'lingua'
|
|
7
|
+
|
|
8
|
+
def build_parser(options)
|
|
9
|
+
OptionParser.new do |opts|
|
|
10
|
+
opts.banner = 'Usage: lingua [options] [TEXT]'
|
|
11
|
+
opts.separator ''
|
|
12
|
+
opts.separator 'If TEXT is omitted, input is read from stdin.'
|
|
13
|
+
opts.separator ''
|
|
14
|
+
opts.separator 'Options:'
|
|
15
|
+
|
|
16
|
+
opts.on('-f', '--format FORMAT', %w[iso iso6393 name json],
|
|
17
|
+
'Output format: name (default), iso, iso6393, json') { |f| options[:format] = f.to_sym }
|
|
18
|
+
opts.on('-c', '--confidences',
|
|
19
|
+
'Output confidence values for all candidate languages') { options[:mode] = :confidences }
|
|
20
|
+
opts.on('-m', '--multiple',
|
|
21
|
+
'Detect multiple languages within the text') { options[:mode] = :multiple }
|
|
22
|
+
opts.on('-l', '--languages LIST',
|
|
23
|
+
'Restrict detection to comma-separated languages (names or ISO codes)') do |list|
|
|
24
|
+
options[:languages] = list.split(',').map(&:strip)
|
|
25
|
+
end
|
|
26
|
+
opts.on('--batch FILE',
|
|
27
|
+
'Treat each line of FILE (or "-" for stdin) as a separate input') { |f| options[:batch] = f }
|
|
28
|
+
opts.on('-h', '--help', 'Show this help') do
|
|
29
|
+
puts opts
|
|
30
|
+
exit
|
|
31
|
+
end
|
|
32
|
+
opts.on('-v', '--version', 'Show version') do
|
|
33
|
+
puts Lingua::VERSION
|
|
34
|
+
exit
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
options = { format: :name, mode: :detect, languages: nil, batch: nil }
|
|
40
|
+
build_parser(options).parse!
|
|
41
|
+
|
|
42
|
+
def format_language(lang, format)
|
|
43
|
+
return nil if lang.nil?
|
|
44
|
+
|
|
45
|
+
case format
|
|
46
|
+
when :iso then lang.to_iso
|
|
47
|
+
when :iso6393 then lang.to_iso6393
|
|
48
|
+
when :name then lang.name
|
|
49
|
+
when :json then { name: lang.name, iso: lang.to_iso, iso6393: lang.to_iso6393 }
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def render_detect(lang, format)
|
|
54
|
+
formatted = format_language(lang, format)
|
|
55
|
+
if format == :json
|
|
56
|
+
JSON.generate(formatted)
|
|
57
|
+
else
|
|
58
|
+
formatted.to_s
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def render_confidences(results, format)
|
|
63
|
+
if format == :json
|
|
64
|
+
JSON.generate(results.map do |r|
|
|
65
|
+
{ language: format_language(r.language, :iso), confidence: r.confidence }
|
|
66
|
+
end)
|
|
67
|
+
else
|
|
68
|
+
results.map { |r| "#{format_language(r.language, format)}: #{format('%.4f', r.confidence)}" }
|
|
69
|
+
.join("\n")
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def render_multiple(segments, format)
|
|
74
|
+
if format == :json
|
|
75
|
+
JSON.generate(segments.map do |s|
|
|
76
|
+
{ language: format_language(s.language, :iso),
|
|
77
|
+
start_index: s.start_index,
|
|
78
|
+
end_index: s.end_index,
|
|
79
|
+
text: s.text }
|
|
80
|
+
end)
|
|
81
|
+
else
|
|
82
|
+
segments.map do |s|
|
|
83
|
+
"#{format_language(s.language, format)} (#{s.start_index}-#{s.end_index}): #{s.text}"
|
|
84
|
+
end.join("\n")
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def process(text, detector, options)
|
|
89
|
+
case options[:mode]
|
|
90
|
+
when :detect
|
|
91
|
+
render_detect(detector.detect(text), options[:format])
|
|
92
|
+
when :confidences
|
|
93
|
+
render_confidences(detector.confidence_values(text), options[:format])
|
|
94
|
+
when :multiple
|
|
95
|
+
render_multiple(detector.detect_multiple(text), options[:format])
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
begin
|
|
100
|
+
detector_options = {}
|
|
101
|
+
detector_options[:languages] = options[:languages] if options[:languages]
|
|
102
|
+
detector = Lingua::Detector.new(**detector_options)
|
|
103
|
+
|
|
104
|
+
if options[:batch]
|
|
105
|
+
io = options[:batch] == '-' ? $stdin : File.open(options[:batch], 'r')
|
|
106
|
+
io.each_line do |line|
|
|
107
|
+
line = line.chomp
|
|
108
|
+
next if line.empty?
|
|
109
|
+
|
|
110
|
+
puts process(line, detector, options)
|
|
111
|
+
end
|
|
112
|
+
io.close unless io == $stdin
|
|
113
|
+
else
|
|
114
|
+
text = ARGV.empty? ? $stdin.read : ARGV.join(' ')
|
|
115
|
+
text = text.strip
|
|
116
|
+
if text.empty?
|
|
117
|
+
warn 'lingua: no input text'
|
|
118
|
+
exit 1
|
|
119
|
+
end
|
|
120
|
+
puts process(text, detector, options)
|
|
121
|
+
end
|
|
122
|
+
rescue Lingua::UnknownLanguageError => e
|
|
123
|
+
warn "lingua: #{e.message}"
|
|
124
|
+
exit 1
|
|
125
|
+
end
|
data/ext/lingua/extconf.rb
CHANGED
|
@@ -11,6 +11,12 @@ selection = with_config('lingua-languages', nil) || ENV.fetch('LINGUA_LANGUAGES'
|
|
|
11
11
|
languages = selection.to_s.split(',').map { |lang| lang.strip.downcase }.reject(&:empty?)
|
|
12
12
|
|
|
13
13
|
create_rust_makefile('lingua/lingua') do |r|
|
|
14
|
+
# Cargo strips debuginfo from release builds by default. With rustc <= 1.98.0,
|
|
15
|
+
# stripping leaves the LINKEDIT string pool misaligned and dyld on macOS 27+
|
|
16
|
+
# refuses to load the bundle ("mis-aligned LINKEDIT string pool"), see
|
|
17
|
+
# rust-lang/rust#157750. Can be removed once rustc >= 1.98.1 is the minimum.
|
|
18
|
+
r.extra_rustflags << '-Cstrip=none' if RUBY_PLATFORM.include?('darwin')
|
|
19
|
+
|
|
14
20
|
unless languages.empty?
|
|
15
21
|
r.extra_cargo_args << '--no-default-features'
|
|
16
22
|
r.features = languages
|
data/lib/lingua/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: lingua_rs
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.6.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Sébastien Vrillaud
|
|
8
8
|
bindir: exe
|
|
9
9
|
cert_chain: []
|
|
10
|
-
date: 2026-
|
|
10
|
+
date: 2026-10-03 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
12
|
- !ruby/object:Gem::Dependency
|
|
13
13
|
name: rb_sys
|
|
@@ -42,7 +42,8 @@ description: Native Ruby bindings for the Lingua Rust library, bringing highly a
|
|
|
42
42
|
scores and ISO 639-1/639-3 language codes.
|
|
43
43
|
email:
|
|
44
44
|
- kochka@gmail.com
|
|
45
|
-
executables:
|
|
45
|
+
executables:
|
|
46
|
+
- lingua
|
|
46
47
|
extensions:
|
|
47
48
|
- ext/lingua/extconf.rb
|
|
48
49
|
extra_rdoc_files: []
|
|
@@ -50,6 +51,7 @@ files:
|
|
|
50
51
|
- LICENSE.txt
|
|
51
52
|
- README.md
|
|
52
53
|
- Rakefile
|
|
54
|
+
- exe/lingua
|
|
53
55
|
- ext/lingua/Cargo.toml
|
|
54
56
|
- ext/lingua/extconf.rb
|
|
55
57
|
- ext/lingua/src/confidence_result.rs
|
|
@@ -84,7 +86,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
84
86
|
- !ruby/object:Gem::Version
|
|
85
87
|
version: '0'
|
|
86
88
|
requirements: []
|
|
87
|
-
rubygems_version: 4.0.
|
|
89
|
+
rubygems_version: 4.0.10
|
|
88
90
|
specification_version: 4
|
|
89
91
|
summary: High-accuracy language detection powered by Lingua (Rust).
|
|
90
92
|
test_files: []
|