Add expo-keyboard-language native module

New Expo module that exposes keyboard language detection via a node
handle. On iOS it resolves the React Native view tag to the underlying
UITextField/UITextView and returns textInputMode.primaryLanguage (BCP 47
tag). Android and web return null for now.

https://claude.ai/code/session_01AMCp4M56WtFPz1pZtwgcvx
This commit is contained in:
Claude
2026-02-28 14:49:15 +00:00
parent 6cdc1fe2b5
commit 3ca5a865a1
9 changed files with 132 additions and 0 deletions
@@ -0,0 +1,39 @@
apply plugin: 'com.android.library'
group = 'expo.modules.keyboardlanguage'
version = '1.0.0'
def expoModulesCorePlugin = new File(project(":expo-modules-core").projectDir.absolutePath, "ExpoModulesCorePlugin.gradle")
apply from: expoModulesCorePlugin
applyKotlinExpoModulesCorePlugin()
useCoreDependencies()
useExpoPublishing()
def useManagedAndroidSdkVersions = false
if (useManagedAndroidSdkVersions) {
useDefaultAndroidSdkVersions()
} else {
buildscript {
ext.safeExtGet = { prop, fallback ->
rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback
}
}
project.android {
compileSdkVersion safeExtGet("compileSdkVersion", 34)
defaultConfig {
minSdkVersion safeExtGet("minSdkVersion", 21)
targetSdkVersion safeExtGet("targetSdkVersion", 34)
}
}
}
android {
namespace "expo.modules.keyboardlanguage"
defaultConfig {
versionCode 1
versionName "1.0.0"
}
lintOptions {
abortOnError false
}
}
@@ -0,0 +1 @@
<manifest/>
@@ -0,0 +1,15 @@
package expo.modules.keyboardlanguage
import expo.modules.kotlin.modules.Module
import expo.modules.kotlin.modules.ModuleDefinition
class ExpoKeyboardLanguageModule : Module() {
override fun definition() =
ModuleDefinition {
Name("ExpoKeyboardLanguage")
Function("getKeyboardLanguage") { _: Int ->
return@Function null as String?
}
}
}
@@ -0,0 +1,9 @@
{
"platforms": ["ios", "android"],
"ios": {
"modules": ["ExpoKeyboardLanguageModule"]
},
"android": {
"modules": ["expo.modules.keyboardlanguage.ExpoKeyboardLanguageModule"]
}
}
+1
View File
@@ -0,0 +1 @@
export {getKeyboardLanguage} from './src/ExpoKeyboardLanguage'
@@ -0,0 +1,20 @@
Pod::Spec.new do |s|
s.name = 'ExpoKeyboardLanguage'
s.version = '1.0.0'
s.summary = 'Get keyboard language for a text input'
s.description = 'Get the keyboard language (textInputMode) for a specific text input by node handle'
s.author = 'bluesky-social'
s.homepage = 'https://github.com/bluesky-social/social-app'
s.platforms = { :ios => '13.4', :tvos => '13.4' }
s.source = { git: '' }
s.static_framework = true
s.dependency 'ExpoModulesCore'
s.pod_target_xcconfig = {
'DEFINES_MODULE' => 'YES',
'SWIFT_COMPILATION_MODE' => 'wholemodule'
}
s.source_files = "**/*.{h,m,mm,swift,hpp,cpp}"
end
@@ -0,0 +1,33 @@
import ExpoModulesCore
public class ExpoKeyboardLanguageModule: Module {
public func definition() -> ModuleDefinition {
Name("ExpoKeyboardLanguage")
Function("getKeyboardLanguage") { (nodeHandle: Int) -> String? in
guard let view = self.appContext?.findView(withTag: nodeHandle, ofType: UIView.self) else {
return nil
}
if let textInput = self.findTextInput(in: view) {
return textInput.textInputMode?.primaryLanguage
}
return view.textInputMode?.primaryLanguage
}
}
private func findTextInput(in view: UIView) -> UIView? {
if view is UITextField || view is UITextView {
return view
}
for subview in view.subviews {
if let found = findTextInput(in: subview) {
return found
}
}
return nil
}
}
@@ -0,0 +1,11 @@
import {requireNativeModule} from 'expo-modules-core'
const NativeModule = requireNativeModule('ExpoKeyboardLanguage')
/**
* Gets the keyboard language (BCP 47 tag) for the text input identified
* by the given node handle. Returns `null` when unavailable.
*/
export function getKeyboardLanguage(nodeHandle: number): string | null {
return NativeModule.getKeyboardLanguage(nodeHandle)
}
@@ -0,0 +1,3 @@
export function getKeyboardLanguage(_nodeHandle: number): string | null {
return null
}