GithubHelp home page GithubHelp logo

kazemcodes / lyricist Goto Github PK

View Code? Open in Web Editor NEW

This project forked from adrielcafe/lyricist

0.0 0.0 0.0 289 KB

๐ŸŒŽ The missing I18N/L10N (internationalization/localization) multiplatform library for Jetpack Compose!

License: MIT License

Kotlin 98.78% CSS 0.24% HTML 0.98%

lyricist's Introduction

Maven metadata URL Android API kotlin ktlint License MIT

Lyricist ๐ŸŒŽ๐ŸŒ๐ŸŒ

The missing I18N and L10N multiplatform library for Jetpack Compose!

Jetpack Compose greatly improved the way we build UIs on Android, but not how we interact with strings. stringResource() works well, but doesn't benefit from the idiomatic Kotlin like Compose.

Lyricist tries to make working with strings as powerful as building UIs with Compose, i.e., working with parameterized string is now typesafe, use of when expression to work with plurals with more flexibility, and even load/update the strings dynamically via an API!

Features

Limitations

  • The XML processor doesn't handle few and many plural values (PRs are welcome)

Why Lyricist?

Inspired by accompanist library: music composing is done by a composer, and since this library is about writing lyrics strings, the role of a lyricist felt like a good name.

Usage

Take a look at the sample app and sample-multi-module for working examples.

Start by declaring your strings on a data class, class or interface (pick one). The strings can be anything (really, it's up to you): Char, String, AnnotatedString, List<String>, Set<String> or even lambdas!

data class Strings(
    val simple: String,
    val annotated: AnnotatedString,
    val parameter: (locale: String) -> String,
    val plural: (count: Int) -> String,
    val list: List<String>
)

Next, create instances for each supported language and annotate with @LyricistStrings. The languageTag must be an IETF BCP47 compliant language tag (docs). You must flag one of them as default.

@LyricistStrings(languageTag = Locales.EN, default = true)
val EnStrings = Strings(
    simple = "Hello Compose!",

    annotated = buildAnnotatedString {
        withStyle(SpanStyle(color = Color.Red)) { 
            append("Hello ") 
        }
        withStyle(SpanStyle(fontWeight = FontWeight.Light)) { 
            append("Compose!") 
        }
    },

    parameter = { locale ->
        "Current locale: $locale"
    },

    plural = { count ->
        val value = when (count) {
            1, 2 -> "few"
            in 3..10 -> "bunch of"
            else -> "lot of"
        }
        "I have a $value apples"
    },

    list = listOf("Avocado", "Pineapple", "Plum")
)

@LyricistStrings(languageTag = Locales.PT)
val PtStrings = Strings(/* pt strings */)

@LyricistStrings(languageTag = Locales.ES)
val EsStrings = Strings(/* es strings */)

@LyricistStrings(languageTag = Locales.RU)
val RuStrings = Strings(/* ru strings */)

Lyricist will generate the LocalStrings property, a CompositionLocal that provides the strings of the current locale. It will also generate rememberStrings() and ProvideStrings(), call them to make LocalStrings accessible down the tree.

val lyricist = rememberStrings()

ProvideStrings(lyricist) {
    // Content
}

// Or just 
ProvideStrings {
    // Content
}
Writing the code for yourself

Don't want to enable KSP to generate the code for you? No problem! Follow the steps below to integrate with Lyricist manually.

First, map each supported language tag to their corresponding instances.

val strings = mapOf(
    Locales.EN to EnStrings,
    Locales.PT to PtStrings,
    Locales.ES to EsStrings,
    Locales.RU to RuStrings
)

Next, create your LocalStrings and choose one translation as default.

val LocalStrings = staticCompositionLocalOf { EnStrings }

Finally, use the same functions, rememberStrings() and ProvideStrings(), to make your LocalStrings accessible down the tree. But this time you need to provide your strings and LocalStrings manually.

val lyricist = rememberStrings(strings)

ProvideStrings(lyricist, LocalStrings) {
    // Content
}

Now you can use LocalStrings to retrieve the current strings.

val strings = LocalStrings.current

Text(text = strings.simple)
// > Hello Compose!

Text(text = strings.annotated)
// > Hello Compose!

Text(text = strings.parameter(lyricist.languageTag))
// > Current locale: en

Text(text = strings.plural(1))
Text(text = strings.plural(5))
Text(text = strings.plural(20))
// > I have a few apples
// > I have a bunch of apples
// > I have a lot of apples

Text(text = strings.list.joinToString())
// > Avocado, Pineapple, Plum

Use the Lyricist instance provided by rememberStrings() to change the current locale. This will trigger a recomposition that will update the strings wherever they are being used.

lyricist.languageTag = Locales.PT

Controlling the visibility

To control the visibility (public or internal) of the generated code, provide the following (optional) argument to KSP in the module's build.gradle.

ksp {
    arg("lyricist.internalVisibility", "true")
}

Important: Lyricist uses the System locale as default. It won't persist the current locale on storage, is outside its scope.

Multi module settings

If you are using Lyricist on a multi module project and the generated declarations (LocalStrings, rememberStrings(), ProvideStrings()) are too generic for you, provide the following (optional) arguments to KSP in the module's build.gradle.

ksp {
    arg("lyricist.packageName", "com.my.app")
    arg("lyricist.moduleName", project.name)
}

Let's say you have a "dashboard" module, the generated declarations will be LocalDashboardStrings, rememberDashboardStrings() and ProvideDashboardStrings().

Migrating from strings.xml

So you liked Lyricist, but already have a project with thousands of strings spread over multiples files? I have good news for you: Lyricist can extract these existing strings and generate all the code you just saw above.

Similar to the multi module setup, you must provide a few arguments to KSP. Lyricist will search for strings.xml files in the resources path. You can also provide a language tag to be used as default value for the LocalStrings.

ksp {
    // Required
    arg("lyricist.xml.resourcesPath", android.sourceSets.main.res.srcDirs.first().absolutePath)
    
    // Optional
    arg("lyricist.packageName", "com.my.app")
    arg("lyricist.xml.moduleName", "xml")
    arg("lyricist.xml.defaultLanguageTag", "en")
}

After the first build, the well-known rememberStrings() and ProvideStrings() (naming can vary depending on your KSP settings) will be available for use. Lyricist will also generated a Locales object containing all language tags currently in use in your project.

val lyricist = rememberStrings(strings)

ProvideStrings(lyricist, LocalStrings) {
    // Content
}

lyricist.languageTag = Locales.PT

You can easily migrate from strings.xml to Lyricist just by copying the generated files to your project. That way, you can finally say goodbye to strings.xml.

Troubleshooting

Can't use the generated code on my IDE

You should set manually the source sets of the generated files, like described here.

buildTypes {
    debug {
        sourceSets {
            main.java.srcDirs += 'build/generated/ksp/debug/kotlin/'
        }
    }
    release {
        sourceSets {
            main.java.srcDirs += 'build/generated/ksp/release/kotlin/'
        }
    }
}

Import to your project

  1. Importing the KSP plugin in the project's build.gradle then apply to your module's build.gradle
plugins {
    id("com.google.devtools.ksp") version "${ksp-latest-version}"
}
  1. Add the desired dependencies to your module's build.gradle
// Required
implementation("cafe.adriel.lyricist:lyricist:${latest-version}")

// If you want to use @LyricistStrings to generate code for you
ksp("cafe.adriel.lyricist:lyricist-processor:${latest-version}")

// If you want to migrate from strings.xml
ksp("cafe.adriel.lyricist:lyricist-processor-xml:${latest-version}")

Multiplatform setup

Doing code generation only at commonMain. Currently workaround, for more information see KSP Issue 567

dependencies {
    add("kspCommonMainMetadata", "cafe.adriel.lyricist:lyricist-processor:${latest-version}")
}

tasks.withType<org.jetbrains.kotlin.gradle.dsl.KotlinCompile<*>>().all {
    if(name != "kspCommonMainKotlinMetadata") {
        dependsOn("kspCommonMainKotlinMetadata")
    }
}

kotlin.sourceSets.commonMain {
    kotlin.srcDir("build/generated/ksp/metadata/commonMain/kotlin")
}

Version Catalog

[versions]
lyricist = {latest-version}

[libraries]
lyricist-library = { module = "cafe.adriel.lyricist:lyricist", version.ref = "lyricist" }
lyricist-processor = { module = "cafe.adriel.lyricist:lyricist-processor", version.ref = "lyricist" }
lyricist-processorXml = { module = "cafe.adriel.lyricist:lyricist-processor-xml", version.ref = "lyricist" }

Current version: Maven metadata URL

lyricist's People

Contributors

adrielcafe avatar devsrsouza avatar phucynwa avatar kazemcodes avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.