Skip to content
CleverKeys Wiki
implemented v1.2.7 User guide

Language Packs Technical Specification

Overview

Language packs are downloadable packages containing dictionaries, layouts, autocorrect rules, and neural model vocabularies for specific languages.

Key Components

ComponentFilePurpose
PackManagerPackManager.ktDownload and installation
PackRegistryPackRegistry.ktAvailable packs catalog
PackExtractorPackExtractor.ktPackage decompression
PackValidatorPackValidator.ktIntegrity verification
ConfigConfig.ktPack preferences

Data Model

Language Pack Structure

// PackManager.kt
data class LanguagePack(
    val id: String,                    // Unique identifier
    val languageCode: String,          // ISO code (fr, de, es)
    val displayName: String,           // "French"
    val nativeName: String,            // "Français"
    val version: Int,                  // Pack version
    val size: Long,                    // Bytes
    val contents: PackContents,        // Included components
    val downloadUrl: String,           // Package URL
    val checksum: String               // SHA256 hash
)

data class PackContents(
    val hasDictionary: Boolean,
    val hasLayout: Boolean,
    val hasAutocorrect: Boolean,
    val hasVocabulary: Boolean,        // Neural model vocab
    val dictionarySize: Int,           // Word count
    val layoutVariants: List<String>   // Layout IDs
)

Pack Manifest

// pack_manifest.json (inside pack)
{
    "id": "lang_fr",
    "language": "fr",
    "display_name": "French",
    "native_name": "Français",
    "version": 3,
    "contents": {
        "dictionary": "dictionary.bin",
        "layout": "layout_azerty.json",
        "autocorrect": "autocorrect.json",
        "vocabulary": "vocab_fr.txt"
    },
    "metadata": {
        "author": "CleverKeys",
        "updated": "2024-12-01"
    }
}

Pack Registry

Available Packs

// PackRegistry.kt
object PackRegistry {
    private val availablePacks = listOf(
        LanguagePack(
            id = "lang_fr",
            languageCode = "fr",
            displayName = "French",
            nativeName = "Français",
            version = 3,
            size = 8_500_000,
            contents = PackContents(
                hasDictionary = true,
                hasLayout = true,
                hasAutocorrect = true,
                hasVocabulary = true,
                dictionarySize = 85000,
                layoutVariants = listOf("azerty_fr")
            ),
            downloadUrl = "https://packs.cleverkeys.app/fr/v3.zip",
            checksum = "sha256:abc123..."
        ),
        // ... more packs
    )

    fun getAvailablePacks(): List<LanguagePack>
    fun getPack(id: String): LanguagePack?
    fun getPacksForRegion(region: String): List<LanguagePack>
}

Download and Installation

Download Flow

User selects pack

PackManager.downloadPack(packId)

Download from CDN with progress

Verify checksum (SHA256)

PackExtractor.extract(zipFile)

PackValidator.validate(contents)

Install components to appropriate locations

Update installed packs registry

Notify listeners (language available)

Download Implementation

// PackManager.kt
class PackManager {
    suspend fun downloadPack(packId: String): Result<Unit> {
        val pack = registry.getPack(packId) ?: return Result.failure(...)

        // Download with progress
        val tempFile = downloadFile(pack.downloadUrl) { progress ->
            notifyProgress(packId, progress)
        }

        // Verify integrity
        val actualChecksum = tempFile.sha256()
        if (actualChecksum != pack.checksum) {
            tempFile.delete()
            return Result.failure(ChecksumMismatchException())
        }

        // Extract and install
        val extracted = extractor.extract(tempFile, getPackDir(packId))
        validator.validate(extracted)

        // Install components
        installDictionary(extracted, pack.languageCode)
        installLayout(extracted)
        installAutocorrect(extracted, pack.languageCode)
        installVocabulary(extracted, pack.languageCode)

        // Update registry
        installedPacks.add(packId)
        saveInstalledPacks()

        return Result.success(Unit)
    }
}

Pack Contents Installation

Dictionary Installation

// PackManager.kt
private fun installDictionary(packDir: File, langCode: String) {
    val dictFile = File(packDir, "dictionary.bin")
    if (!dictFile.exists()) return

    val targetDir = File(context.filesDir, "dictionaries")
    val targetFile = File(targetDir, "${langCode}_dict.bin")

    dictFile.copyTo(targetFile, overwrite = true)

    // Register with dictionary manager
    dictionaryManager.registerDictionary(langCode, targetFile)
}

Layout Installation

// PackManager.kt
private fun installLayout(packDir: File) {
    val layoutFile = File(packDir, "layout.json")
    if (!layoutFile.exists()) return

    val layout = layoutParser.parse(layoutFile)
    layoutManager.registerLayout(layout)
}

Vocabulary Installation

// PackManager.kt
private fun installVocabulary(packDir: File, langCode: String) {
    val vocabFile = File(packDir, "vocabulary.txt")
    if (!vocabFile.exists()) return

    // Load into neural vocabulary manager
    val vocab = OptimizedVocabulary.loadFromFile(vocabFile)
    vocabularyManager.registerVocabulary(langCode, vocab)
}

Pack Updates

// PackManager.kt
suspend fun checkForUpdates(): List<PackUpdate> {
    val updates = mutableListOf<PackUpdate>()

    installedPacks.forEach { packId ->
        val installed = getInstalledVersion(packId)
        val available = registry.getPack(packId)?.version ?: 0

        if (available > installed) {
            updates.add(PackUpdate(packId, installed, available))
        }
    }

    return updates
}

data class PackUpdate(
    val packId: String,
    val installedVersion: Int,
    val availableVersion: Int
)

Storage Management

// PackManager.kt
fun getPackStorageInfo(): StorageInfo {
    val packsDir = File(context.filesDir, "packs")
    val totalSize = packsDir.walkTopDown()
        .filter { it.isFile }
        .sumOf { it.length() }

    return StorageInfo(
        totalUsed = totalSize,
        packCount = installedPacks.size,
        byPack = installedPacks.associateWith { getPackSize(it) }
    )
}

fun removePack(packId: String) {
    val pack = registry.getPack(packId) ?: return

    // Remove dictionary
    dictionaryManager.unregisterDictionary(pack.languageCode)

    // Remove layout
    pack.contents.layoutVariants.forEach {
        layoutManager.unregisterLayout(it)
    }

    // Remove vocabulary
    vocabularyManager.unregisterVocabulary(pack.languageCode)

    // Delete files
    File(context.filesDir, "packs/$packId").deleteRecursively()

    installedPacks.remove(packId)
    saveInstalledPacks()
}

Configuration

SettingKeyDefaultDescription
Installed Packsinstalled_packs[]Downloaded pack IDs
Auto-Updateauto_update_packsfalseUpdate automatically
WiFi Onlypacks_wifi_onlytrueDownload on WiFi only
Storage Limitpacks_storage_limit500MBMax pack storage