react-native-lingua 0.1.0 → 0.1.2
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.
- package/Lingua.podspec +43 -0
- package/android/build.gradle +24 -0
- package/cpp/lingua.cpp +1 -1
- package/package.json +11 -4
- package/rust/Cargo.toml +16 -0
- package/rust/Makefile +88 -0
- package/rust/build.rs +31 -0
- package/rust/rust-toolchain.toml +11 -0
- package/rust/src/lib.rs +264 -0
package/Lingua.podspec
CHANGED
|
@@ -17,5 +17,48 @@ Pod::Spec.new do |s|
|
|
|
17
17
|
s.private_header_files = "ios/**/*.h"
|
|
18
18
|
s.vendored_frameworks = "ios/*.xcframework"
|
|
19
19
|
|
|
20
|
+
# Build Rust library from source during pod install
|
|
21
|
+
s.prepare_command = <<-CMD
|
|
22
|
+
set -e
|
|
23
|
+
|
|
24
|
+
# Check if Rust is installed
|
|
25
|
+
if ! command -v cargo &> /dev/null; then
|
|
26
|
+
echo "❌ Error: Rust is not installed."
|
|
27
|
+
echo "Please install Rust: curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh"
|
|
28
|
+
exit 1
|
|
29
|
+
fi
|
|
30
|
+
|
|
31
|
+
# Check if iOS targets are installed
|
|
32
|
+
if ! rustup target list --installed | grep -q "aarch64-apple-ios"; then
|
|
33
|
+
echo "❌ Error: iOS Rust targets not installed."
|
|
34
|
+
echo "Please run: rustup target add aarch64-apple-ios aarch64-apple-ios-sim"
|
|
35
|
+
exit 1
|
|
36
|
+
fi
|
|
37
|
+
|
|
38
|
+
# Build the xcframework
|
|
39
|
+
cd rust
|
|
40
|
+
make header
|
|
41
|
+
|
|
42
|
+
# Build for iOS targets
|
|
43
|
+
echo "Building Rust library for iOS..."
|
|
44
|
+
cargo build --release --target aarch64-apple-ios
|
|
45
|
+
cargo build --release --target aarch64-apple-ios-sim
|
|
46
|
+
|
|
47
|
+
# Create xcframework
|
|
48
|
+
mkdir -p generated/include
|
|
49
|
+
xcodebuild -create-xcframework \
|
|
50
|
+
-library target/aarch64-apple-ios/release/liblingua_native.a \
|
|
51
|
+
-headers generated/include \
|
|
52
|
+
-library target/aarch64-apple-ios-sim/release/liblingua_native.a \
|
|
53
|
+
-headers generated/include \
|
|
54
|
+
-output generated/liblingua_native.xcframework
|
|
55
|
+
|
|
56
|
+
# Copy to ios directory
|
|
57
|
+
cp -rf generated/*.xcframework ../ios/
|
|
58
|
+
cp -f generated/include/liblingua.h ../cpp/
|
|
59
|
+
|
|
60
|
+
echo "✅ Rust library built successfully"
|
|
61
|
+
CMD
|
|
62
|
+
|
|
20
63
|
install_modules_dependencies(s)
|
|
21
64
|
end
|
package/android/build.gradle
CHANGED
|
@@ -86,6 +86,30 @@ repositories {
|
|
|
86
86
|
google()
|
|
87
87
|
}
|
|
88
88
|
|
|
89
|
+
// Build Rust library before native build
|
|
90
|
+
task buildRustHeader(type: Exec) {
|
|
91
|
+
workingDir file("${projectDir}/../rust")
|
|
92
|
+
commandLine 'make', 'header'
|
|
93
|
+
|
|
94
|
+
// Always run to ensure header is up to date
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
task buildRust(type: Exec) {
|
|
98
|
+
workingDir file("${projectDir}/../rust")
|
|
99
|
+
commandLine 'make', 'android'
|
|
100
|
+
|
|
101
|
+
// Only run if jniLibs don't exist
|
|
102
|
+
onlyIf {
|
|
103
|
+
!file("${projectDir}/jniLibs/arm64-v8a/liblingua_native.a").exists()
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
dependsOn buildRustHeader
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
tasks.matching { it.name.startsWith('externalNativeBuild') }.configureEach {
|
|
110
|
+
dependsOn buildRust
|
|
111
|
+
}
|
|
112
|
+
|
|
89
113
|
def kotlin_version = getExtOrDefault("kotlinVersion")
|
|
90
114
|
|
|
91
115
|
dependencies {
|
package/cpp/lingua.cpp
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-native-lingua",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "A lingua-rs wrapper for React Native",
|
|
5
5
|
"main": "./lib/module/index.js",
|
|
6
6
|
"types": "./lib/typescript/src/index.d.ts",
|
|
@@ -16,8 +16,13 @@
|
|
|
16
16
|
"src",
|
|
17
17
|
"lib",
|
|
18
18
|
"android",
|
|
19
|
-
"ios",
|
|
19
|
+
"ios/*.{h,mm}",
|
|
20
20
|
"cpp",
|
|
21
|
+
"rust/src",
|
|
22
|
+
"rust/Cargo.toml",
|
|
23
|
+
"rust/build.rs",
|
|
24
|
+
"rust/Makefile",
|
|
25
|
+
"rust/rust-toolchain.toml",
|
|
21
26
|
"*.podspec",
|
|
22
27
|
"react-native.config.js",
|
|
23
28
|
"!ios/build",
|
|
@@ -26,10 +31,13 @@
|
|
|
26
31
|
"!android/gradlew",
|
|
27
32
|
"!android/gradlew.bat",
|
|
28
33
|
"!android/local.properties",
|
|
34
|
+
"!cpp/liblingua.h",
|
|
29
35
|
"!**/__tests__",
|
|
30
36
|
"!**/__fixtures__",
|
|
31
37
|
"!**/__mocks__",
|
|
32
|
-
"!**/.*"
|
|
38
|
+
"!**/.*",
|
|
39
|
+
"README.md",
|
|
40
|
+
"LICENSE"
|
|
33
41
|
],
|
|
34
42
|
"workspaces": [
|
|
35
43
|
"example"
|
|
@@ -158,7 +166,6 @@
|
|
|
158
166
|
"lint": "eslint \"**/*.{js,ts,tsx}\"",
|
|
159
167
|
"clean": "del-cli android/build example/android/build example/android/app/build example/ios/build lib",
|
|
160
168
|
"build:rust": "cd rust && make all",
|
|
161
|
-
"postinstall": "npm run build:rust || echo 'Warning: Rust build failed. Pre-built binaries may be needed.'",
|
|
162
169
|
"release": "release-it --only-version"
|
|
163
170
|
}
|
|
164
171
|
}
|
package/rust/Cargo.toml
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
[package]
|
|
2
|
+
name = "lingua_native"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
edition = "2021"
|
|
5
|
+
|
|
6
|
+
[lib]
|
|
7
|
+
name = "lingua_native"
|
|
8
|
+
crate-type = ["staticlib", "cdylib"]
|
|
9
|
+
|
|
10
|
+
[dependencies]
|
|
11
|
+
lingua = "1.7.2"
|
|
12
|
+
libc = "0.2"
|
|
13
|
+
lazy_static = "1.4"
|
|
14
|
+
|
|
15
|
+
[build-dependencies]
|
|
16
|
+
cbindgen = "0.26.0"
|
package/rust/Makefile
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
ARCHS_IOS = aarch64-apple-ios aarch64-apple-ios-sim
|
|
2
|
+
ARCHS_ANDROID = aarch64-linux-android armv7-linux-androideabi x86_64-linux-android i686-linux-android
|
|
3
|
+
LIB_FILE = liblingua_native.a
|
|
4
|
+
DYLIB = liblingua_native.so
|
|
5
|
+
XCFRAMEWORK = liblingua_native.xcframework
|
|
6
|
+
|
|
7
|
+
all: ios android
|
|
8
|
+
|
|
9
|
+
# Generate header only (fast, for development)
|
|
10
|
+
header:
|
|
11
|
+
mkdir -p generated/include
|
|
12
|
+
cargo build --lib
|
|
13
|
+
cp -f generated/include/liblingua.h ../cpp/
|
|
14
|
+
@echo "✅ Header generated and copied to cpp/"
|
|
15
|
+
|
|
16
|
+
ios: clean header ios-build package-xcframework copy-ios notify
|
|
17
|
+
|
|
18
|
+
android: header android-build copy-android notify
|
|
19
|
+
|
|
20
|
+
notify:
|
|
21
|
+
@echo "🟢 Build Completed!"
|
|
22
|
+
|
|
23
|
+
lint:
|
|
24
|
+
cargo clippy --all-features --all-targets -- --no-deps -D warnings
|
|
25
|
+
|
|
26
|
+
fix:
|
|
27
|
+
cargo clippy --all-targets --all-features --fix --allow-dirty --allow-staged
|
|
28
|
+
cargo fmt --all
|
|
29
|
+
|
|
30
|
+
clean:
|
|
31
|
+
rm -rf generated/$(XCFRAMEWORK)
|
|
32
|
+
rm -rf ../ios/$(XCFRAMEWORK)
|
|
33
|
+
|
|
34
|
+
# iOS Build
|
|
35
|
+
|
|
36
|
+
.PHONY: $(ARCHS_IOS)
|
|
37
|
+
$(ARCHS_IOS): %:
|
|
38
|
+
cargo build --target $@ --release
|
|
39
|
+
|
|
40
|
+
package-xcframework: $(ARCHS_IOS)
|
|
41
|
+
mkdir -p generated/include
|
|
42
|
+
xcodebuild -create-xcframework \
|
|
43
|
+
-library target/aarch64-apple-ios/release/$(LIB_FILE) \
|
|
44
|
+
-headers generated/include \
|
|
45
|
+
-library target/aarch64-apple-ios-sim/release/$(LIB_FILE) \
|
|
46
|
+
-headers generated/include \
|
|
47
|
+
-output generated/$(XCFRAMEWORK)
|
|
48
|
+
|
|
49
|
+
copy-ios:
|
|
50
|
+
cp -rf generated/*.xcframework ../ios
|
|
51
|
+
cp -f generated/include/liblingua.h ../cpp/
|
|
52
|
+
@echo "✅ iOS xcframework and headers copied"
|
|
53
|
+
|
|
54
|
+
ios-build:
|
|
55
|
+
cargo build --release --target aarch64-apple-ios
|
|
56
|
+
cargo build --release --target aarch64-apple-ios-sim
|
|
57
|
+
|
|
58
|
+
# Android Build
|
|
59
|
+
|
|
60
|
+
.PHONY: $(ARCHS_ANDROID)
|
|
61
|
+
$(ARCHS_ANDROID): %:
|
|
62
|
+
cargo ndk --target $@ --platform 31 build --release
|
|
63
|
+
|
|
64
|
+
android-build:
|
|
65
|
+
cargo ndk --target aarch64-linux-android --platform 31 build --release
|
|
66
|
+
cargo ndk --target armv7-linux-androideabi --platform 31 build --release
|
|
67
|
+
cargo ndk --target i686-linux-android --platform 31 build --release
|
|
68
|
+
cargo ndk --target x86_64-linux-android --platform 31 build --release
|
|
69
|
+
|
|
70
|
+
copy-android:
|
|
71
|
+
# Copy header
|
|
72
|
+
mkdir -p ../android/src/main/jni/include
|
|
73
|
+
cp -rf generated/include/* ../android/src/main/jni/include
|
|
74
|
+
# Copy per architecture generated .a files
|
|
75
|
+
mkdir -p ../android/jniLibs/x86
|
|
76
|
+
cp target/i686-linux-android/release/$(LIB_FILE) ../android/jniLibs/x86
|
|
77
|
+
|
|
78
|
+
mkdir -p ../android/jniLibs/x86_64
|
|
79
|
+
cp target/x86_64-linux-android/release/$(LIB_FILE) ../android/jniLibs/x86_64
|
|
80
|
+
|
|
81
|
+
mkdir -p ../android/jniLibs/armeabi-v7a
|
|
82
|
+
cp target/armv7-linux-androideabi/release/$(LIB_FILE) ../android/jniLibs/armeabi-v7a
|
|
83
|
+
|
|
84
|
+
mkdir -p ../android/jniLibs/arm64-v8a
|
|
85
|
+
cp target/aarch64-linux-android/release/$(LIB_FILE) ../android/jniLibs/arm64-v8a
|
|
86
|
+
@echo "✅ Android libraries copied"
|
|
87
|
+
|
|
88
|
+
.PHONY: all ios android clean notify lint fix ios-build android-build copy-ios copy-android package-xcframework header
|
package/rust/build.rs
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
extern crate cbindgen;
|
|
2
|
+
|
|
3
|
+
use std::env;
|
|
4
|
+
|
|
5
|
+
fn generate_c_headers() {
|
|
6
|
+
let crate_dir = env::var("CARGO_MANIFEST_DIR").unwrap();
|
|
7
|
+
let header_path = "generated/include/liblingua.h";
|
|
8
|
+
|
|
9
|
+
cbindgen::Builder::new()
|
|
10
|
+
.with_crate(crate_dir)
|
|
11
|
+
.with_language(cbindgen::Language::Cxx)
|
|
12
|
+
.with_include_guard("lingua_h")
|
|
13
|
+
.with_autogen_warning(
|
|
14
|
+
"/* Warning, this file is autogenerated by cbindgen. Don't modify this manually. */",
|
|
15
|
+
)
|
|
16
|
+
.with_namespace("lingua")
|
|
17
|
+
.with_cpp_compat(true)
|
|
18
|
+
.generate()
|
|
19
|
+
.expect("Unable to generate bindings")
|
|
20
|
+
.write_to_file(header_path);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
fn main() {
|
|
24
|
+
// Tell Cargo that if the given file changes, to rerun this build script.
|
|
25
|
+
println!("cargo:rerun-if-changed=src/lib.rs");
|
|
26
|
+
|
|
27
|
+
if std::env::var("CARGO_CFG_CLIPPY").is_err() {
|
|
28
|
+
// Only run cbindgen if not running cargo clippy
|
|
29
|
+
generate_c_headers();
|
|
30
|
+
}
|
|
31
|
+
}
|
package/rust/src/lib.rs
ADDED
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
use lingua::{IsoCode639_1, Language, LanguageDetector, LanguageDetectorBuilder};
|
|
2
|
+
use std::ffi::{c_char, CStr, CString};
|
|
3
|
+
use std::os::raw::c_int;
|
|
4
|
+
use std::ptr;
|
|
5
|
+
use std::str::FromStr;
|
|
6
|
+
use std::sync::Mutex;
|
|
7
|
+
|
|
8
|
+
lazy_static::lazy_static! {
|
|
9
|
+
static ref GLOBAL_ERROR: Mutex<Option<String>> = Mutex::new(None);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
// Error handling
|
|
13
|
+
// Helper function for internal use
|
|
14
|
+
unsafe fn set_error_internal(err: *mut *const c_char, error_message: &str) {
|
|
15
|
+
let mut global_error = GLOBAL_ERROR.lock().unwrap();
|
|
16
|
+
*global_error = Some(error_message.to_string());
|
|
17
|
+
|
|
18
|
+
if !err.is_null() {
|
|
19
|
+
// Point to the stored error string in GLOBAL_ERROR
|
|
20
|
+
if let Some(ref error_str) = *global_error {
|
|
21
|
+
*err = error_str.as_ptr() as *const c_char;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Exported C function (not actually used, but needed for header generation)
|
|
27
|
+
#[no_mangle]
|
|
28
|
+
pub unsafe extern "C" fn set_error(err: *mut *const c_char, error_message: *const c_char) {
|
|
29
|
+
if error_message.is_null() {
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
let c_str = CStr::from_ptr(error_message);
|
|
33
|
+
if let Ok(s) = c_str.to_str() {
|
|
34
|
+
set_error_internal(err, s);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
#[no_mangle]
|
|
39
|
+
pub extern "C" fn get_error() -> *const c_char {
|
|
40
|
+
let global_error = GLOBAL_ERROR.lock().unwrap();
|
|
41
|
+
match &*global_error {
|
|
42
|
+
Some(err) => err.as_ptr() as *const c_char,
|
|
43
|
+
None => ptr::null(),
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// LanguageDetector opaque pointer type
|
|
48
|
+
pub struct LinguaDetector {
|
|
49
|
+
detector: LanguageDetector,
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/// Creates a language detector with all languages
|
|
53
|
+
#[no_mangle]
|
|
54
|
+
pub extern "C" fn lingua_detector_create_all() -> *mut LinguaDetector {
|
|
55
|
+
let detector = LanguageDetectorBuilder::from_all_languages().build();
|
|
56
|
+
Box::into_raw(Box::new(LinguaDetector { detector }))
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/// Creates a language detector with specific languages
|
|
60
|
+
/// languages: comma-separated ISO 639-1 codes (e.g., "en,fr,de,es")
|
|
61
|
+
#[no_mangle]
|
|
62
|
+
pub unsafe extern "C" fn lingua_detector_create_from_languages(
|
|
63
|
+
languages: *const c_char,
|
|
64
|
+
error: *mut *const c_char,
|
|
65
|
+
) -> *mut LinguaDetector {
|
|
66
|
+
if languages.is_null() {
|
|
67
|
+
set_error_internal(error, "languages parameter is null");
|
|
68
|
+
return ptr::null_mut();
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
let c_str = CStr::from_ptr(languages);
|
|
72
|
+
let lang_str = match c_str.to_str() {
|
|
73
|
+
Ok(s) => s,
|
|
74
|
+
Err(_) => {
|
|
75
|
+
set_error_internal(error, "Invalid UTF-8 in languages parameter");
|
|
76
|
+
return ptr::null_mut();
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
let lang_codes: Vec<&str> = lang_str.split(',').map(|s| s.trim()).collect();
|
|
81
|
+
let mut selected_iso_codes = Vec::new();
|
|
82
|
+
|
|
83
|
+
for code in lang_codes {
|
|
84
|
+
let upper_code = code.to_uppercase();
|
|
85
|
+
match IsoCode639_1::from_str(&upper_code) {
|
|
86
|
+
Ok(iso_code) => selected_iso_codes.push(iso_code),
|
|
87
|
+
Err(_) => {
|
|
88
|
+
let error_msg = format!("Invalid language code: {}", code);
|
|
89
|
+
set_error_internal(error, &error_msg);
|
|
90
|
+
return ptr::null_mut();
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if selected_iso_codes.is_empty() {
|
|
96
|
+
set_error_internal(error, "No valid languages provided");
|
|
97
|
+
return ptr::null_mut();
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
let detector = LanguageDetectorBuilder::from_iso_codes_639_1(&selected_iso_codes).build();
|
|
101
|
+
Box::into_raw(Box::new(LinguaDetector { detector }))
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/// Detects the language of the given text
|
|
105
|
+
/// Returns the ISO 639-1 code (e.g., "en") or null if detection failed
|
|
106
|
+
#[no_mangle]
|
|
107
|
+
pub unsafe extern "C" fn lingua_detect_language(
|
|
108
|
+
detector: *const LinguaDetector,
|
|
109
|
+
text: *const c_char,
|
|
110
|
+
error: *mut *const c_char,
|
|
111
|
+
) -> *mut c_char {
|
|
112
|
+
if detector.is_null() {
|
|
113
|
+
set_error_internal(error, "detector is null");
|
|
114
|
+
return ptr::null_mut();
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
if text.is_null() {
|
|
118
|
+
set_error_internal(error, "text is null");
|
|
119
|
+
return ptr::null_mut();
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
let detector = &(*detector).detector;
|
|
123
|
+
let c_str = CStr::from_ptr(text);
|
|
124
|
+
let text_str = match c_str.to_str() {
|
|
125
|
+
Ok(s) => s,
|
|
126
|
+
Err(_) => {
|
|
127
|
+
set_error_internal(error, "Invalid UTF-8 in text");
|
|
128
|
+
return ptr::null_mut();
|
|
129
|
+
}
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
match detector.detect_language_of(text_str) {
|
|
133
|
+
Some(language) => {
|
|
134
|
+
let iso_code = language.iso_code_639_1().to_string().to_lowercase();
|
|
135
|
+
match CString::new(iso_code) {
|
|
136
|
+
Ok(c_string) => c_string.into_raw(),
|
|
137
|
+
Err(_) => {
|
|
138
|
+
set_error_internal(error, "Failed to create C string");
|
|
139
|
+
ptr::null_mut()
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
None => ptr::null_mut(),
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/// Computes the confidence value for a specific language
|
|
148
|
+
#[no_mangle]
|
|
149
|
+
pub unsafe extern "C" fn lingua_compute_language_confidence(
|
|
150
|
+
detector: *const LinguaDetector,
|
|
151
|
+
text: *const c_char,
|
|
152
|
+
language_code: *const c_char,
|
|
153
|
+
error: *mut *const c_char,
|
|
154
|
+
) -> f64 {
|
|
155
|
+
if detector.is_null() || text.is_null() || language_code.is_null() {
|
|
156
|
+
set_error_internal(error, "null parameter");
|
|
157
|
+
return 0.0;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
let detector = &(*detector).detector;
|
|
161
|
+
let text_str = CStr::from_ptr(text).to_str().unwrap_or("");
|
|
162
|
+
let lang_str = CStr::from_ptr(language_code).to_str().unwrap_or("");
|
|
163
|
+
|
|
164
|
+
// Convert the ISO code string to a Language enum
|
|
165
|
+
// We need to iterate through all possible languages and match
|
|
166
|
+
let all_languages = Language::all();
|
|
167
|
+
let language = all_languages
|
|
168
|
+
.into_iter()
|
|
169
|
+
.find(|lang| {
|
|
170
|
+
lang.iso_code_639_1()
|
|
171
|
+
.to_string()
|
|
172
|
+
.eq_ignore_ascii_case(lang_str)
|
|
173
|
+
})
|
|
174
|
+
.unwrap_or_else(|| {
|
|
175
|
+
set_error_internal(error, "Invalid language code");
|
|
176
|
+
Language::English // Default fallback, but we return 0.0 anyway
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
if language == Language::English && !lang_str.eq_ignore_ascii_case("en") {
|
|
180
|
+
return 0.0; // Error was already set
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
detector.compute_language_confidence(text_str, language)
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/// Result structure for confidence values
|
|
187
|
+
#[repr(C)]
|
|
188
|
+
pub struct ConfidenceValue {
|
|
189
|
+
pub language_code: *mut c_char,
|
|
190
|
+
pub confidence: f64,
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/// Computes confidence values for all languages
|
|
194
|
+
/// Returns an array of ConfidenceValue structs and sets the count
|
|
195
|
+
#[no_mangle]
|
|
196
|
+
pub unsafe extern "C" fn lingua_compute_language_confidence_values(
|
|
197
|
+
detector: *const LinguaDetector,
|
|
198
|
+
text: *const c_char,
|
|
199
|
+
count: *mut c_int,
|
|
200
|
+
error: *mut *const c_char,
|
|
201
|
+
) -> *mut ConfidenceValue {
|
|
202
|
+
if detector.is_null() || text.is_null() || count.is_null() {
|
|
203
|
+
set_error_internal(error, "null parameter");
|
|
204
|
+
return ptr::null_mut();
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
let detector = &(*detector).detector;
|
|
208
|
+
let text_str = match CStr::from_ptr(text).to_str() {
|
|
209
|
+
Ok(s) => s,
|
|
210
|
+
Err(_) => {
|
|
211
|
+
set_error_internal(error, "Invalid UTF-8");
|
|
212
|
+
return ptr::null_mut();
|
|
213
|
+
}
|
|
214
|
+
};
|
|
215
|
+
|
|
216
|
+
let confidence_values = detector.compute_language_confidence_values(text_str);
|
|
217
|
+
*count = confidence_values.len() as c_int;
|
|
218
|
+
|
|
219
|
+
let mut results: Vec<ConfidenceValue> = Vec::with_capacity(confidence_values.len());
|
|
220
|
+
|
|
221
|
+
for (language, confidence) in confidence_values {
|
|
222
|
+
let iso_code = language.iso_code_639_1().to_string().to_lowercase();
|
|
223
|
+
if let Ok(c_string) = CString::new(iso_code) {
|
|
224
|
+
results.push(ConfidenceValue {
|
|
225
|
+
language_code: c_string.into_raw(),
|
|
226
|
+
confidence,
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
let ptr = results.as_mut_ptr();
|
|
232
|
+
std::mem::forget(results);
|
|
233
|
+
ptr
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/// Frees a string allocated by lingua
|
|
237
|
+
#[no_mangle]
|
|
238
|
+
pub unsafe extern "C" fn lingua_free_string(s: *mut c_char) {
|
|
239
|
+
if !s.is_null() {
|
|
240
|
+
let _ = CString::from_raw(s);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/// Frees the confidence values array
|
|
245
|
+
#[no_mangle]
|
|
246
|
+
pub unsafe extern "C" fn lingua_free_confidence_values(values: *mut ConfidenceValue, count: c_int) {
|
|
247
|
+
if !values.is_null() {
|
|
248
|
+
for i in 0..count {
|
|
249
|
+
let value = &mut *values.offset(i as isize);
|
|
250
|
+
if !value.language_code.is_null() {
|
|
251
|
+
let _ = CString::from_raw(value.language_code);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
let _ = Vec::from_raw_parts(values, count as usize, count as usize);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/// Destroys the detector and frees memory
|
|
259
|
+
#[no_mangle]
|
|
260
|
+
pub unsafe extern "C" fn lingua_detector_destroy(detector: *mut LinguaDetector) {
|
|
261
|
+
if !detector.is_null() {
|
|
262
|
+
let _ = Box::from_raw(detector);
|
|
263
|
+
}
|
|
264
|
+
}
|