Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .bazelrc
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,6 @@ common --color=yes
test --test_output=errors

# Lint configuration
build:lint --aspects=//tools/lint:linters.bzl%ktlint,//tools/lint:linters.bzl%buildifier
build:lint --aspects=//tools/lint:linters.bzl%detekt,//tools/lint:linters.bzl%buildifier
build:lint --output_groups=rules_lint_human
build:lint --@aspect_rules_lint//lint:fail_on_violation
1 change: 0 additions & 1 deletion .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ trim_trailing_whitespace = true
[*.{kt,kts}]
indent_size = 4
max_line_length = 120
ktlint_code_style = intellij_idea
ij_kotlin_allow_trailing_comma = true
ij_kotlin_allow_trailing_comma_on_call_site = true

Expand Down
2 changes: 1 addition & 1 deletion BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package(default_visibility = ["//visibility:public"])

exports_files([
".editorconfig",
"ktlint-baseline.xml",
"detekt.yml",
])

alias(
Expand Down
7 changes: 2 additions & 5 deletions MODULE.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ bazel_dep(name = "buildifier_prebuilt", version = "10.1.0", dev_dependency = Tru
maven = use_extension("@rules_jvm_external//:extensions.bzl", "maven")
maven.install(
artifacts = [
"io.gitlab.arturbosch.detekt:detekt-cli:1.23.8",
"junit:junit:4.13.2",
"org.jetbrains.kotlinx:kotlinx-coroutines-core:1.11.0",
"org.jetbrains.kotlinx:kotlinx-coroutines-test:1.11.0",
Expand All @@ -27,11 +28,7 @@ maven.install(
)
use_repo(maven, "maven")

# Lint & Format tools
# Formatting tools
format_tools = use_extension("@aspect_rules_lint//format:extensions.bzl", "tools")
format_tools.ktfmt()
use_repo(format_tools, "ktfmt")

lint_tools = use_extension("@aspect_rules_lint//lint:extensions.bzl", "tools")
lint_tools.ktlint()
use_repo(lint_tools, "com_github_pinterest_ktlint")
25 changes: 0 additions & 25 deletions MODULE.bazel.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,12 @@ import kotlin.math.min
* Levenshtein distance on word stems.
*/
object CognateDetector {
private const val MIN_WORD_LENGTH = 2
private const val MIN_EXACT_MATCH_LENGTH = 3
private const val MIN_STEM_LENGTH = 2
private const val MIN_FUZZY_STEM_LENGTH = 3
private const val MAX_FUZZY_DISTANCE = 1

/** Suffix conversion pairs between Spanish and English. */
private val SUFFIX_MAP: List<Pair<String, String>> =
listOf(
Expand Down Expand Up @@ -60,7 +66,7 @@ object CognateDetector {
*/
fun detectCognate(spanishWord: String, englishGloss: String): Boolean {
val es = normalize(spanishWord)
if (es.length <= 2) return false
if (es.length <= MIN_WORD_LENGTH) return false

val candidates = extractCandidateWords(englishGloss)
if (candidates.isEmpty()) return false
Expand All @@ -73,38 +79,32 @@ object CognateDetector {
val es = normalize(spanishWord)
val en = normalize(englishWord)

if (es.length <= 2 || en.length <= 2) return false
if (es.length <= MIN_WORD_LENGTH || en.length <= MIN_WORD_LENGTH) return false

// Direct identical match for words > 3 characters (e.g. "natural" == "natural")
if (es.length > 3 && es == en) {
if (es.length > MIN_EXACT_MATCH_LENGTH && es == en) {
return true
}

// Direct match after accent normalization (e.g. "café" -> "cafe")
val esUnaccented = stripAccents(es)
if (esUnaccented.length > 3 && esUnaccented == en) {
if (esUnaccented.length > MIN_EXACT_MATCH_LENGTH && esUnaccented == en) {
return true
}

// Suffix mapping and stem comparison
for ((esSuffix, enSuffix) in SUFFIX_MAP) {
return SUFFIX_MAP.any { (esSuffix, enSuffix) ->
if (es.endsWith(esSuffix) && en.endsWith(enSuffix)) {
val esStem = stripAccents(es.removeSuffix(esSuffix))
val enStem = en.removeSuffix(enSuffix)

// Exact match on short stems (e.g. "nación" -> "na" == "nation" -> "na")
if (esStem == enStem && esStem.length >= 2) {
return true
}

// Levenshtein distance <= 1 for stems of length >= 3
if (esStem.length >= 3 && levenshteinDistance(esStem, enStem) <= 1) {
return true
}
(esStem == enStem && esStem.length >= MIN_STEM_LENGTH) ||
(esStem.length >= MIN_FUZZY_STEM_LENGTH &&
levenshteinDistance(esStem, enStem) <= MAX_FUZZY_DISTANCE)
} else {
false
}
}

return false
}

/** Normalizes a word by lowercasing and trimming whitespace and non-letter edge characters. */
Expand Down Expand Up @@ -138,7 +138,7 @@ object CognateDetector {
val words = WORD_TOKEN_REGEX.findAll(withoutParticle).map { it.value }
for (word in words) {
val normalized = normalize(word)
if (normalized.length >= 2 && normalized !in result) {
if (normalized.length >= MIN_WORD_LENGTH && normalized !in result) {
result.add(normalized)
}
}
Expand Down
Loading
Loading