diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..0872015 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,14 @@ +root = true + +[*] +indent_size = 2 +max_line_length = 100 +indent_style = space +insert_final_newline = true +trim_trailing_whitespace = true +ij_kotlin_allow_trailing_comma = true +ij_kotlin_allow_trailing_comma_on_call_site = true + +[*.{kt, kts}] +ktlint_standard_filename = disabled +ktlint_standard_max-line-length = disabled \ No newline at end of file diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000..2a97d97 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,16 @@ +# Lines starting with '#' are comments. +# Each line is a file pattern followed by one or more owners. + +# More details are here: https://help.github.com/articles/about-codeowners/ + +# The '*' pattern is global owners. +# Not adding in this PR, but I'd like to try adding a global owner set with the entire team. +# One interpretation of their docs is that global owners are added only if not removed +# by a more local rule. + +# Order is important. The last matching pattern has the most precedence. +# The folders are ordered as follows: + +# In each subsection folders are ordered first by depth, then alphabetically. +# This should make it easy to add new rules without breaking existing ones. +* @teogor \ No newline at end of file diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..35787e1 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,13 @@ +# These are supported funding model platforms + +github: [ teogor ] +patreon: # Replace with a single Patreon username +open_collective: # Replace with a single Open Collective username +ko_fi: # Replace with a single Ko-fi username +tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel +community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry +liberapay: # Replace with a single Liberapay username +issuehunt: # Replace with a single IssueHunt username +otechie: # Replace with a single Otechie username +lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry +custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] diff --git a/.github/workflows/publish-docs.yml b/.github/workflows/publish-docs.yml new file mode 100644 index 0000000..df09483 --- /dev/null +++ b/.github/workflows/publish-docs.yml @@ -0,0 +1,106 @@ +name: Publish API Docs + +on: + workflow_dispatch: + push: + branches: + - main + +permissions: + contents: write + pages: write + id-token: write + pull-requests: write + +# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. +# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. +concurrency: + group: "api-docs" + cancel-in-progress: false + +env: + DESTINATION_BRANCH: docs/${{ github.event.repository.owner.name }}/${{ github.event.repository.name }}/${{ github.sha }} + REPOSITORY: ${{ github.event.repository.name }} + REPO: ${{ github.event.repository.owner.name }}/${{ github.event.repository.name }} + DISPLAY_NAME: Xenoglot + +jobs: + synchronize-documentation: + runs-on: ubuntu-latest + name: Synchronize Repository API Documentation + steps: + - uses: actions/checkout@v4 + + # Grants execute permission to gradle (safety step) + - name: Grant Permissions to gradlew + run: chmod +x gradlew + + - name: Setup Java + uses: actions/setup-java@v3 + with: + distribution: temurin + java-version: 17 + + - name: Generate API Documentation + run: ./gradlew dokkaHtmlMultiModule + + - name: Relocate API Documentation Assets + run: | + mkdir -p docs/${{ env.REPOSITORY }}/documentation + cp -r build/dokka/htmlMultiModule/* docs/${{ env.REPOSITORY }}/documentation + + - id: publish-docs + name: Deploy API Documentation + uses: ./.github/workflows/sync-docs/ + env: + API_TOKEN_GITHUB: ${{ secrets.PERSONAL_ACCESS_TOKEN }} + with: + source-file: docs/${{ env.REPOSITORY }}/documentation/ + destination-folder: ${{ env.REPOSITORY }} + destination-repo: teogor/source.teogor.dev + user-email: open-source@teogor.dev + user-name: Teodor Grigor + destination-branch-create: ${{ env.DESTINATION_BRANCH }} + + build-and-deploy: + needs: synchronize-documentation + name: Build and Deploy Updated API Documentation + runs-on: ubuntu-latest + steps: + - name: Checkout other repository + uses: actions/checkout@v3 + with: + repository: teogor/source.teogor.dev + token: ${{ secrets.PERSONAL_ACCESS_TOKEN }} + + - name: Create PR (Pull Request) + run: | + json=$(curl -L \ + -X POST \ + -H "Accept: application/vnd.github+json" \ + -H "Authorization: Bearer ${{ secrets.PERSONAL_ACCESS_TOKEN }}" \ + -H "X-GitHub-Api-Version: 2022-11-28" \ + https://api.github.com/repos/teogor/source.teogor.dev/pulls \ + -d '{ + "title":"Updated documentation for `${{ env.REPO }}`", + "body":"## 🚀 **Updated Documentation for ${{ env.DISPLAY_NAME }}** 🚀\n\nThis pull request brings you the latest updates to the [${{ env.DISPLAY_NAME }}](https://github.com/${{ env.REPO }}) documentation, ensuring you have access to the most comprehensive and informative guide for using this powerful library.\n\n-------\n\n* Generated by [Publish Docs](https://github.com/${{ env.REPO }}/actions/workflows/publish-docs.yml)\n\n* From [${{ env.REPO }}](https://github.com/${{ env.REPO }})", + "head":"${{ env.DESTINATION_BRANCH }}", + "base":"main" + }' | jq .) + + number=$(jq '.number' <<< ${json}) + + curl -L \ + -X POST \ + -H "Accept: application/vnd.github+json" \ + -H "Authorization: Bearer ${{ secrets.PERSONAL_ACCESS_TOKEN }}" \ + -H "X-GitHub-Api-Version: 2022-11-28" \ + https://api.github.com/repos/teogor/source.teogor.dev/issues/${number}/labels \ + -d '{"labels":["@documentation"]}' + + curl -L \ + -X PUT \ + -H "Accept: application/vnd.github+json" \ + -H "Authorization: Bearer ${{ secrets.PERSONAL_ACCESS_TOKEN }}" \ + -H "X-GitHub-Api-Version: 2022-11-28" \ + https://api.github.com/repos/teogor/source.teogor.dev/pulls/${number}/merge \ diff --git a/.github/workflows/publish-to-maven.yml b/.github/workflows/publish-to-maven.yml new file mode 100644 index 0000000..fa14266 --- /dev/null +++ b/.github/workflows/publish-to-maven.yml @@ -0,0 +1,36 @@ +name: Publish to Maven + +on: + workflow_dispatch: + +jobs: + publish: + name: Snapshot build and publish + runs-on: ubuntu-latest + environment: PRODUCTION + timeout-minutes: 120 + env: + ORG_GRADLE_PROJECT_mavenCentralUsername: ${{ secrets.OSSRH_USERNAME }} + ORG_GRADLE_PROJECT_mavenCentralPassword: ${{ secrets.OSSRH_PASSWORD }} + ORG_GRADLE_PROJECT_signingInMemoryKeyId: ${{ secrets.SIGNING_KEY_ID }} + ORG_GRADLE_PROJECT_signingInMemoryKeyPassword: ${{ secrets.SIGNING_PASSWORD }} + ORG_GRADLE_PROJECT_signingInMemoryKey: ${{ secrets.SIGNING_KEY }} + SONATYPE_CONNECT_TIMEOUT_SECONDS: 120 + SONATYPE_CLOSE_TIMEOUT_SECONDS: 1800 + + steps: + - name: Check out code + uses: actions/checkout@v3.1.0 + + - name: Set up JDK 17 + uses: actions/setup-java@v3.5.1 + with: + distribution: 'zulu' + java-version: 17 + + - name: Grant Permission to Execute Gradle + run: chmod +x gradlew + + - name: Publish to MavenCentral + run: | + ./gradlew :publish --no-configuration-cache diff --git a/.github/workflows/sync-docs/action.yml b/.github/workflows/sync-docs/action.yml new file mode 100644 index 0000000..4fd1113 --- /dev/null +++ b/.github/workflows/sync-docs/action.yml @@ -0,0 +1,65 @@ +name: 'Publish Docs' +description: 'Comprehensive publish docs' +branding: + icon: 'git-commit' + color: 'red' +inputs: + source-file: + description: 'Source file from the origin directory' + required: true + destination-repo: + description: 'Destination repository' + required: true + destination-folder: + description: 'Directory to push the file to' + required: false + user-email: + description: 'Email for the git commit' + required: true + user-name: + description: 'GitHub username for the commit' + required: true + destination-branch: + description: 'branch to push file to, defaults to main' + required: false + destination-branch-create: + description: 'Destination branch to create for this commit' + required: false + commit-message: + description: 'A custom message for the commit' + required: false + rename: + description: 'Rename the destination file' + required: false + use-rsync: + description: 'Copy files/directories using rsync instead of cp. Experimental feature, please know your use case' + required: false + git-server: + description: 'Git server host, default github.com' + required: false + default: github.com +runs: + using: "composite" + steps: + - name: Make all .sh files executable + run: find . -type f -name "*.sh" -exec chmod +x {} \; + shell: bash + + - run: echo "${{ github.action_path }}" >> $GITHUB_PATH + shell: bash + + - name: Execute Entrypoint Script + shell: bash + env: + SOURCE_FILE: ${{ inputs.source-file }} + DESTINATION_REPO: ${{ inputs.destination-repo }} + DESTINATION_FOLDER: ${{ inputs.destination-folder }} + USER_EMAIL: ${{ inputs.user-email }} + USER_NAME: ${{ inputs.user-name }} + DESTINATION_BRANCH: ${{ inputs.destination-branch }} + DESTINATION_BRANCH_CREATE: ${{ inputs.destination-branch-create }} + COMMIT_MESSAGE: ${{ inputs.commit-message }} + GIT_SERVER: ${{ inputs.git-server }} + RENAME: ${{ inputs.rename }} + USE_RSYNC: ${{ inputs.use-rsync }} + run: sync-files.sh diff --git a/.github/workflows/sync-docs/sync-files.sh b/.github/workflows/sync-docs/sync-files.sh new file mode 100644 index 0000000..b655fa1 --- /dev/null +++ b/.github/workflows/sync-docs/sync-files.sh @@ -0,0 +1,85 @@ +#!/bin/sh + +set -e +set -x + +# Validate required input +if [ -z "$SOURCE_FILE" ]; then + echo "Error: Source file must be specified." + exit 1 +fi + +# Set default values for optional variables +if [ -z "$GIT_SERVER" ]; then + GIT_SERVER="github.com" +fi + +if [ -z "$DESTINATION_BRANCH" ]; then + DESTINATION_BRANCH="main" +fi + +# Clone the destination Git repository +OUTPUT_BRANCH="$DESTINATION_BRANCH" + +CLONE_DIR=$(mktemp -d) +echo "Cloning destination Git repository: $DESTINATION_REPO" + +git config --global user.email "$USER_EMAIL" +git config --global user.name "$USER_NAME" + +git clone --single-branch --branch $DESTINATION_BRANCH "https://x-access-token:$API_TOKEN_GITHUB@$GIT_SERVER/$DESTINATION_REPO.git" "$CLONE_DIR" + +# Determine the destination file path +DEST_COPY="$CLONE_DIR/$DESTINATION_FOLDER" + +if [ ! -z "$RENAME" ]; then + echo "Renaming file to: ${RENAME}" + DEST_COPY="$CLONE_DIR/$DESTINATION_FOLDER/$RENAME" +fi + +# Delete the previous folder if it exists +if [ -d "$DEST_COPY" ]; then + echo "Deleting existing folder: $DEST_COPY" + rm -rf "$DEST_COPY" +fi + +# Copy the source file to the destination repository +echo "Copying contents to Git repo: $SOURCE_FILE" + +mkdir -p $CLONE_DIR/$DESTINATION_FOLDER + +if [ -z "$USE_RSYNC" ]; then + echo "Copying using cp" + cp -R "$SOURCE_FILE" "$DEST_COPY" +else + echo "Copying using rsync" + rsync -avrh "$SOURCE_FILE" "$DEST_COPY" +fi + +# Check out the specified branch or create a new one +cd "$CLONE_DIR" + +if [ ! -z "$DESTINATION_BRANCH_CREATE" ]; then + echo "Creating new branch: $DESTINATION_BRANCH_CREATE" + git checkout -b "$DESTINATION_BRANCH_CREATE" + OUTPUT_BRANCH="$DESTINATION_BRANCH_CREATE" +fi + +if [ -z "$COMMIT_MESSAGE" ]; then + COMMIT_MESSAGE="Automated updates based on https://$GIT_SERVER/${GITHUB_REPOSITORY}/commit/${GITHUB_SHA}" +fi + +# Add the copied file to the staging area +echo "Adding git commit" +git add . + +# Commit changes if there are any +if git status | grep -q "Changes to be committed"; then + echo "Committing changes with message: $COMMIT_MESSAGE" + git commit --message "$COMMIT_MESSAGE" + + echo "Pushing git commit to branch: $OUTPUT_BRANCH" + git push -u origin HEAD:"$OUTPUT_BRANCH" +else + echo "No changes detected, skipping commit and push" +fi diff --git a/.gitignore b/.gitignore index 347e252..8a11ac9 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,9 @@ # Gradle files .gradle/ build/ +!gradle/wrapper/gradle-wrapper.jar +!**/src/main/**/build/ +!**/src/test/**/build/ # Local configuration file (sdk path, etc) local.properties @@ -15,6 +18,37 @@ captures/ *.apk output.json +### IntelliJ IDEA ### +.idea/modules.xml +.idea/jarRepositories.xml +.idea/compiler.xml +.idea/libraries/ +*.iws +*.iml +*.ipr +out/ +!**/src/main/**/out/ +!**/src/test/**/out/ + +### Eclipse ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache +bin/ +!**/src/main/**/bin/ +!**/src/test/**/bin/ + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ + # IntelliJ *.iml .idea/ @@ -22,12 +56,18 @@ misc.xml deploymentTargetDropDown.xml render.experimental.xml +### VS Code ### +.vscode/ + +### Mac OS ### +.DS_Store + # Keystore files *.jks *.keystore -# Google Services (e.g. APIs or Firebase) -google-services.json - # Android Profiling *.hprof + +# Google Services (e.g. APIs or Firebase) +google-services.json diff --git a/.resources/bom/1.0.0-alpha01/dependencies-1.0.0-alpha01.json b/.resources/bom/1.0.0-alpha01/dependencies-1.0.0-alpha01.json new file mode 100644 index 0000000..0de5644 --- /dev/null +++ b/.resources/bom/1.0.0-alpha01/dependencies-1.0.0-alpha01.json @@ -0,0 +1,392 @@ +[ + { + "completeName": "Xenoglot Android", + "name": "android", + "displayName": "Android", + "description": "🌍 Xenoglot seamlessly masters languages and locales, empowering developers to integrate multilingual capabilities into their applications with ease.", + "groupId": "dev.teogor.xenoglot", + "artifactId": "android", + "version": { + "major": 1, + "minor": 0, + "patch": 0, + "flag": "Alpha", + "versionQualifier": 1 + }, + "path": ":android", + "dependencies": [ + { + "type": "dev.teogor.winds.api.model.Dependency", + "implementationType": "api", + "group": "androidx.appcompat", + "artifact": "appcompat", + "version": "1.6.1" + }, + { + "type": "dev.teogor.winds.api.model.LocalProjectDependency", + "implementationType": "debugAndroidTestCompileClasspath", + "projectName": "Xenoglot", + "modulePath": ":android" + }, + { + "type": "dev.teogor.winds.api.model.Dependency", + "implementationType": "debugAndroidTestCompileClasspath", + "group": "androidx.appcompat", + "artifact": "appcompat", + "version": "1.6.1" + }, + { + "type": "dev.teogor.winds.api.model.LocalProjectDependency", + "implementationType": "debugAndroidTestRuntimeClasspath", + "projectName": "Xenoglot", + "modulePath": ":android" + }, + { + "type": "dev.teogor.winds.api.model.LocalProjectDependency", + "implementationType": "debugUnitTestCompileClasspath", + "projectName": "Xenoglot", + "modulePath": ":android" + }, + { + "type": "dev.teogor.winds.api.model.Dependency", + "implementationType": "debugUnitTestCompileClasspath", + "group": "androidx.appcompat", + "artifact": "appcompat", + "version": "1.6.1" + }, + { + "type": "dev.teogor.winds.api.model.LocalProjectDependency", + "implementationType": "debugUnitTestRuntimeClasspath", + "projectName": "Xenoglot", + "modulePath": ":android" + }, + { + "type": "dev.teogor.winds.api.model.Dependency", + "implementationType": "dokkaGfmPartialPlugin", + "group": "org.jetbrains.dokka", + "artifact": "analysis-kotlin-descriptors", + "version": "1.9.0" + }, + { + "type": "dev.teogor.winds.api.model.Dependency", + "implementationType": "dokkaGfmPartialPlugin", + "group": "org.jetbrains.dokka", + "artifact": "dokka-base", + "version": "1.9.0" + }, + { + "type": "dev.teogor.winds.api.model.Dependency", + "implementationType": "dokkaGfmPlugin", + "group": "org.jetbrains.dokka", + "artifact": "analysis-kotlin-descriptors", + "version": "1.9.0" + }, + { + "type": "dev.teogor.winds.api.model.Dependency", + "implementationType": "dokkaGfmPlugin", + "group": "org.jetbrains.dokka", + "artifact": "dokka-base", + "version": "1.9.0" + }, + { + "type": "dev.teogor.winds.api.model.Dependency", + "implementationType": "dokkaHtmlPartialPlugin", + "group": "org.jetbrains.dokka", + "artifact": "analysis-kotlin-descriptors", + "version": "1.9.0" + }, + { + "type": "dev.teogor.winds.api.model.Dependency", + "implementationType": "dokkaHtmlPartialPlugin", + "group": "org.jetbrains.dokka", + "artifact": "dokka-base", + "version": "1.9.0" + }, + { + "type": "dev.teogor.winds.api.model.Dependency", + "implementationType": "dokkaHtmlPlugin", + "group": "org.jetbrains.dokka", + "artifact": "analysis-kotlin-descriptors", + "version": "1.9.0" + }, + { + "type": "dev.teogor.winds.api.model.Dependency", + "implementationType": "dokkaHtmlPlugin", + "group": "org.jetbrains.dokka", + "artifact": "dokka-base", + "version": "1.9.0" + }, + { + "type": "dev.teogor.winds.api.model.Dependency", + "implementationType": "dokkaJavadocPartialPlugin", + "group": "org.jetbrains.dokka", + "artifact": "analysis-kotlin-descriptors", + "version": "1.9.0" + }, + { + "type": "dev.teogor.winds.api.model.Dependency", + "implementationType": "dokkaJavadocPartialPlugin", + "group": "org.jetbrains.dokka", + "artifact": "dokka-base", + "version": "1.9.0" + }, + { + "type": "dev.teogor.winds.api.model.Dependency", + "implementationType": "dokkaJavadocPlugin", + "group": "org.jetbrains.dokka", + "artifact": "analysis-kotlin-descriptors", + "version": "1.9.0" + }, + { + "type": "dev.teogor.winds.api.model.Dependency", + "implementationType": "dokkaJavadocPlugin", + "group": "org.jetbrains.dokka", + "artifact": "dokka-base", + "version": "1.9.0" + }, + { + "type": "dev.teogor.winds.api.model.Dependency", + "implementationType": "dokkaJekyllPartialPlugin", + "group": "org.jetbrains.dokka", + "artifact": "analysis-kotlin-descriptors", + "version": "1.9.0" + }, + { + "type": "dev.teogor.winds.api.model.Dependency", + "implementationType": "dokkaJekyllPartialPlugin", + "group": "org.jetbrains.dokka", + "artifact": "dokka-base", + "version": "1.9.0" + }, + { + "type": "dev.teogor.winds.api.model.Dependency", + "implementationType": "dokkaJekyllPlugin", + "group": "org.jetbrains.dokka", + "artifact": "analysis-kotlin-descriptors", + "version": "1.9.0" + }, + { + "type": "dev.teogor.winds.api.model.Dependency", + "implementationType": "dokkaJekyllPlugin", + "group": "org.jetbrains.dokka", + "artifact": "dokka-base", + "version": "1.9.0" + }, + { + "type": "dev.teogor.winds.api.model.LocalProjectDependency", + "implementationType": "releaseUnitTestCompileClasspath", + "projectName": "Xenoglot", + "modulePath": ":android" + }, + { + "type": "dev.teogor.winds.api.model.Dependency", + "implementationType": "releaseUnitTestCompileClasspath", + "group": "androidx.appcompat", + "artifact": "appcompat", + "version": "1.6.1" + }, + { + "type": "dev.teogor.winds.api.model.LocalProjectDependency", + "implementationType": "releaseUnitTestRuntimeClasspath", + "projectName": "Xenoglot", + "modulePath": ":android" + } + ], + "canBePublished": true, + "names": [ + "Xenoglot", + "Android" + ] + }, + { + "completeName": "Xenoglot Core", + "name": "core", + "displayName": "Core", + "description": "🌍 Xenoglot seamlessly masters languages and locales, empowering developers to integrate multilingual capabilities into their applications with ease.", + "groupId": "dev.teogor.xenoglot", + "artifactId": "core", + "version": { + "major": 1, + "minor": 0, + "patch": 0, + "flag": "Alpha", + "versionQualifier": 1 + }, + "path": ":core", + "dependencies": [ + { + "type": "dev.teogor.winds.api.model.Dependency", + "implementationType": "dokkaGfmPartialPlugin", + "group": "org.jetbrains.dokka", + "artifact": "analysis-kotlin-descriptors", + "version": "1.9.0" + }, + { + "type": "dev.teogor.winds.api.model.Dependency", + "implementationType": "dokkaGfmPartialPlugin", + "group": "org.jetbrains.dokka", + "artifact": "dokka-base", + "version": "1.9.0" + }, + { + "type": "dev.teogor.winds.api.model.Dependency", + "implementationType": "dokkaGfmPlugin", + "group": "org.jetbrains.dokka", + "artifact": "analysis-kotlin-descriptors", + "version": "1.9.0" + }, + { + "type": "dev.teogor.winds.api.model.Dependency", + "implementationType": "dokkaGfmPlugin", + "group": "org.jetbrains.dokka", + "artifact": "dokka-base", + "version": "1.9.0" + }, + { + "type": "dev.teogor.winds.api.model.Dependency", + "implementationType": "dokkaHtmlPartialPlugin", + "group": "org.jetbrains.dokka", + "artifact": "analysis-kotlin-descriptors", + "version": "1.9.0" + }, + { + "type": "dev.teogor.winds.api.model.Dependency", + "implementationType": "dokkaHtmlPartialPlugin", + "group": "org.jetbrains.dokka", + "artifact": "dokka-base", + "version": "1.9.0" + }, + { + "type": "dev.teogor.winds.api.model.Dependency", + "implementationType": "dokkaHtmlPlugin", + "group": "org.jetbrains.dokka", + "artifact": "analysis-kotlin-descriptors", + "version": "1.9.0" + }, + { + "type": "dev.teogor.winds.api.model.Dependency", + "implementationType": "dokkaHtmlPlugin", + "group": "org.jetbrains.dokka", + "artifact": "dokka-base", + "version": "1.9.0" + }, + { + "type": "dev.teogor.winds.api.model.Dependency", + "implementationType": "dokkaJavadocPartialPlugin", + "group": "org.jetbrains.dokka", + "artifact": "analysis-kotlin-descriptors", + "version": "1.9.0" + }, + { + "type": "dev.teogor.winds.api.model.Dependency", + "implementationType": "dokkaJavadocPartialPlugin", + "group": "org.jetbrains.dokka", + "artifact": "dokka-base", + "version": "1.9.0" + }, + { + "type": "dev.teogor.winds.api.model.Dependency", + "implementationType": "dokkaJavadocPlugin", + "group": "org.jetbrains.dokka", + "artifact": "analysis-kotlin-descriptors", + "version": "1.9.0" + }, + { + "type": "dev.teogor.winds.api.model.Dependency", + "implementationType": "dokkaJavadocPlugin", + "group": "org.jetbrains.dokka", + "artifact": "dokka-base", + "version": "1.9.0" + }, + { + "type": "dev.teogor.winds.api.model.Dependency", + "implementationType": "dokkaJekyllPartialPlugin", + "group": "org.jetbrains.dokka", + "artifact": "analysis-kotlin-descriptors", + "version": "1.9.0" + }, + { + "type": "dev.teogor.winds.api.model.Dependency", + "implementationType": "dokkaJekyllPartialPlugin", + "group": "org.jetbrains.dokka", + "artifact": "dokka-base", + "version": "1.9.0" + }, + { + "type": "dev.teogor.winds.api.model.Dependency", + "implementationType": "dokkaJekyllPlugin", + "group": "org.jetbrains.dokka", + "artifact": "analysis-kotlin-descriptors", + "version": "1.9.0" + }, + { + "type": "dev.teogor.winds.api.model.Dependency", + "implementationType": "dokkaJekyllPlugin", + "group": "org.jetbrains.dokka", + "artifact": "dokka-base", + "version": "1.9.0" + }, + { + "type": "dev.teogor.winds.api.model.Dependency", + "implementationType": "embeddedKotlin", + "group": "org.jetbrains.kotlin", + "artifact": "kotlin-stdlib-jdk8", + "version": "1.8.20" + }, + { + "type": "dev.teogor.winds.api.model.Dependency", + "implementationType": "embeddedKotlin", + "group": "org.jetbrains.kotlin", + "artifact": "kotlin-reflect", + "version": "1.8.20" + }, + { + "type": "dev.teogor.winds.api.model.Dependency", + "implementationType": "kotlinCompilerPluginClasspathMain", + "group": "org.jetbrains.kotlin", + "artifact": "kotlin-scripting-compiler-embeddable", + "version": "1.9.10" + }, + { + "type": "dev.teogor.winds.api.model.Dependency", + "implementationType": "kotlinCompilerPluginClasspathMain", + "group": "org.jetbrains.kotlin", + "artifact": "kotlin-sam-with-receiver", + "version": "1.9.10" + }, + { + "type": "dev.teogor.winds.api.model.Dependency", + "implementationType": "kotlinCompilerPluginClasspathMain", + "group": "org.jetbrains.kotlin", + "artifact": "kotlin-assignment", + "version": "1.9.10" + }, + { + "type": "dev.teogor.winds.api.model.Dependency", + "implementationType": "kotlinCompilerPluginClasspathTest", + "group": "org.jetbrains.kotlin", + "artifact": "kotlin-scripting-compiler-embeddable", + "version": "1.9.10" + }, + { + "type": "dev.teogor.winds.api.model.Dependency", + "implementationType": "kotlinCompilerPluginClasspathTest", + "group": "org.jetbrains.kotlin", + "artifact": "kotlin-sam-with-receiver", + "version": "1.9.10" + }, + { + "type": "dev.teogor.winds.api.model.Dependency", + "implementationType": "kotlinCompilerPluginClasspathTest", + "group": "org.jetbrains.kotlin", + "artifact": "kotlin-assignment", + "version": "1.9.10" + } + ], + "canBePublished": true, + "names": [ + "Xenoglot", + "Core" + ] + } +] \ No newline at end of file diff --git a/.resources/bom/versions.json b/.resources/bom/versions.json new file mode 100644 index 0000000..45f2028 --- /dev/null +++ b/.resources/bom/versions.json @@ -0,0 +1,12 @@ +[ + { + "version": { + "major": 1, + "minor": 0, + "patch": 0, + "flag": "Alpha", + "versionQualifier": 1 + }, + "date": 1700339293 + } +] \ No newline at end of file diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..82d6e58 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,51 @@ +# Teogor's Open Source Community Guidelines + +In the realm of Teogor, the creativity and collaboration of open source contributors are +wholeheartedly celebrated. Teogor, as an individual, values the diverse tapestry of skills, +experiences, cultures, and perspectives that weave into the projects and communities. + +All Teogor open source projects and communities adhere to a principle of inclusivity, embodying a +commitment to treating each individual respectfully, regardless of gender identity, expression, +sexual orientation, disabilities, neurodiversity, physical appearance, body size, ethnicity, +nationality, race, age, religion, or analogous personal attributes. + +Teogor, as a person, places the highest importance on respectful behavior. + +Respectful behavior embodies: + +- Being considerate, kind, constructive, and supportive. +- Refraining from engaging in derogatory, discriminatory, harassing, hateful, sexualized, or + physically threatening conduct, speech, or imagery. +- Choosing not to participate in unwelcome physical contact. + +Certain Teogor open source projects may adopt a specific project code of conduct that outlines +further expectations for participants. In most cases, these projects will align with the +Teogor-developed [Contributor Covenant][]. + +[Contributor Covenant]: https://source.teogor.dev/docs/releasing/template/CODE_OF_CONDUCT/ + +## Seeking Peaceful Resolution + +In the realm of disagreements, Teogor recognizes that not all conflict is detrimental. Positive +outcomes often arise from healthy debates and disagreements. However, as a persona, Teogor insists +that disrespectful behavior is never acceptable. + +When encountering disrespectful behavior, Teogor encourages addressing the issue directly with the +individuals involved. Many matters can be swiftly resolved, granting participants control over their +dispute's outcome. If resolution is unattainable or if behavior is menacing or harassing, reporting +the incident is encouraged. The environment strives to provide a welcoming and secure space for +participants. + +## Reporting Concerns + +For specific Teogor open source projects, a designated Project Steward, embodying the persona of +Teogor, will handle reports of code of conduct violations. In instances where a project lacks a +Project Steward, issues can be reported by reaching out to open-source@teogor.dev. + +Each complaint will be meticulously investigated, although direct responses may not always be +provided. Discretion will be exercised in determining how to address reported incidents, ranging +from no action to permanent expulsion from the project and related spaces. Accused parties will be +informed of the report's details, affording them an opportunity to discuss it prior to any action. +Reporter identities will be kept confidential when sharing report specifics with the accused. For +situations involving potential harm, such as ongoing harassment or threats, immediate action may be +taken without advance notice. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..ff4bebe --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,36 @@ +# How to become a contributor and submit your own code + +## Contributor License Agreements + +We'd love to accept your sample apps and patches! Before we can take them, we +have to jump a couple of legal hurdles. + +Please fill out either the individual or corporate Contributor License Agreement +(CLA). + +* If you are an individual writing original source code and you're sure you + own the intellectual property, then you'll need to sign + an [individual CLA](https://developers.google.com/open-source/cla/individual). +* If you work for a company that wants to allow you to contribute your work, + then you'll need to sign + a [corporate CLA](https://developers.google.com/open-source/cla/corporate). + +Follow either of the two links above to access the appropriate CLA and +instructions for how to sign and return it. Once we receive it, we'll be able to +accept your pull requests. + +## Contributing A Patch + +1. Submit an issue describing your proposed change to the repo in question. +1. The repo owner will respond to your issue promptly. +1. If your proposed change is accepted, and you haven't already done so, sign a + Contributor License Agreement (see details above). +1. Fork the desired repo, develop and test your code changes. +1. Ensure that your code adheres to the existing style in the sample to which + you are contributing. Refer to the + [Google Cloud Platform Samples Style Guide](https://github.com/GoogleCloudPlatform/Template/wiki/style.html) + for the + recommended coding standards for this organization. +1. Ensure that your code has an appropriate set of unit tests which all pass. +1. Submit a pull request. + diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..7429714 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,41 @@ +# Security Policy + +## Supported Versions + +Please refer to this table to see which versions of the project are currently receiving security +updates: + +| Version | Supported | +|---------|--------------------| +| Release | :white_check_mark: | +| ALPHA | :x: | +| BETA | :x: | + + +## Reporting a Vulnerability + +If you discover a security vulnerability within this project, please follow these steps: + +1. **Do Not Disclose Publicly:** Do not publicly disclose the vulnerability until it has been + addressed and a security release is available. + +2. **Contact Us:** Report the vulnerability to us by sending an email + to [open-source@teogor.dev](mailto:open-source@teogor.dev). You can encrypt sensitive information + using our [PGP key](https://source.teogor.dev/security/pgp-key) to ensure the confidentiality of the report. + +3. **Provide Details:** Please provide us with detailed information about the vulnerability, + including steps to reproduce, affected versions, and potential impact. + +4. **Acknowledgment:** We will acknowledge your report within 48 hours to confirm that we have + received it and are investigating. + +5. **Resolution:** Our security team will work to validate and address the vulnerability. We will + keep you informed about our progress and expected timelines for resolution. + +6. **Disclosure:** Once the vulnerability is fixed and a release is available, we will work with you + to determine an appropriate timeline for public disclosure. We appreciate your patience during + this process. + +Thank you for helping us improve the security of our project. + +*[PGP key](https://source.teogor.dev/security/pgp-key)* diff --git a/android/.gitignore b/android/.gitignore new file mode 100644 index 0000000..42afabf --- /dev/null +++ b/android/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/android/api/android.api b/android/api/android.api new file mode 100644 index 0000000..52d29a6 --- /dev/null +++ b/android/api/android.api @@ -0,0 +1,6 @@ +public final class dev/teogor/xenoglot/android/LanguageUtils { + public static final field INSTANCE Ldev/teogor/xenoglot/android/LanguageUtils; + public final fun setAppLanguage (Ljava/lang/String;)V + public final fun setAppLanguage (Ljava/util/Locale;)V +} + diff --git a/android/build.gradle.kts b/android/build.gradle.kts new file mode 100644 index 0000000..ae2af25 --- /dev/null +++ b/android/build.gradle.kts @@ -0,0 +1,46 @@ +/* + * Copyright 2023 teogor (Teodor Grigor) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +plugins { + alias(libs.plugins.android.library) + alias(libs.plugins.kotlin.android) + id("dev.teogor.winds") +} + +kotlin { + jvmToolchain(17) +} + +android { + namespace = "dev.teogor.querent.common" + + compileSdk = 34 + + defaultConfig { + minSdk = 21 + } +} + +dependencies { + api(libs.androidx.appcompat) +} + +winds { + mavenPublish { + displayName = "Android" + name = "android" + } +} diff --git a/android/src/main/AndroidManifest.xml b/android/src/main/AndroidManifest.xml new file mode 100644 index 0000000..49392cb --- /dev/null +++ b/android/src/main/AndroidManifest.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + + + diff --git a/android/src/main/kotlin/dev/teogor/xenoglot/android/LanguageUtils.kt b/android/src/main/kotlin/dev/teogor/xenoglot/android/LanguageUtils.kt new file mode 100644 index 0000000..ab12b60 --- /dev/null +++ b/android/src/main/kotlin/dev/teogor/xenoglot/android/LanguageUtils.kt @@ -0,0 +1,48 @@ +/* + * Copyright 2023 teogor (Teodor Grigor) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.teogor.xenoglot.android + +import androidx.appcompat.app.AppCompatDelegate +import androidx.core.os.LocaleListCompat +import java.util.Locale + +/** + * Object that handles language settings and provides methods to change + * the application language. + */ +object LanguageUtils { + + /** + * Changes the application language based on the specified locale. + * + * @param locale The locale to set as the application language. + */ + fun setAppLanguage(locale: Locale) { + setAppLanguage(locale.language) + } + + /** + * Changes the application language based on the specified language tag. + * + * @param languageTag The language tag to set as the application language. + */ + fun setAppLanguage(languageTag: String) { + AppCompatDelegate.setApplicationLocales( + LocaleListCompat.forLanguageTags(languageTag), + ) + } +} diff --git a/bom/.gitignore b/bom/.gitignore new file mode 100644 index 0000000..42afabf --- /dev/null +++ b/bom/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/bom/build.gradle.kts b/bom/build.gradle.kts new file mode 100644 index 0000000..0981989 --- /dev/null +++ b/bom/build.gradle.kts @@ -0,0 +1,27 @@ +/* + * Copyright 2023 teogor (Teodor Grigor) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +plugins { + id("dev.teogor.winds") +} + +winds { + mavenPublish { + displayName = "BoM" + name = "bom" + + defineBoM() + } +} diff --git a/build.gradle.kts b/build.gradle.kts new file mode 100644 index 0000000..6dca5b4 --- /dev/null +++ b/build.gradle.kts @@ -0,0 +1,198 @@ +import com.vanniktech.maven.publish.SonatypeHost +import dev.teogor.winds.api.MavenPublish +import dev.teogor.winds.api.getValue +import dev.teogor.winds.api.model.Developer +import dev.teogor.winds.api.model.LicenseType +import dev.teogor.winds.api.model.Version +import dev.teogor.winds.api.provider.Scm +import dev.teogor.winds.gradle.utils.afterWindsPluginConfiguration +import dev.teogor.winds.gradle.utils.attachTo +import org.jetbrains.dokka.gradle.DokkaMultiModuleTask +import org.jetbrains.dokka.gradle.DokkaPlugin +import org.jetbrains.dokka.gradle.DokkaTask + +buildscript { + repositories { + google() + mavenCentral() + } +} + +plugins { + // Kotlin Suite + alias(libs.plugins.kotlin.jvm) apply false + alias(libs.plugins.kotlin.multiplatform) apply false + alias(libs.plugins.kotlin.android) apply false + + // Android Tools + alias(libs.plugins.android.library) apply false + + alias(libs.plugins.winds) + alias(libs.plugins.vanniktech.maven) + alias(libs.plugins.dokka) + alias(libs.plugins.spotless) + alias(libs.plugins.api.validator) +} + +winds { + buildFeatures { + mavenPublish = true + + docsGenerator = true + } + + mavenPublish { + displayName = "Xenoglot" + name = "xenoglot" + + canBePublished = false + + description = + "\uD83C\uDF0D Xenoglot seamlessly masters languages and locales, empowering developers to integrate multilingual capabilities into their applications with ease." + + groupId = "dev.teogor.xenoglot" + artifactIdElements = 1 + + version = Version( + major = 1, + minor = 0, + patch = 0, + ).setAlphaRelease(1) + + // TODO winds + // required by dokka + project.version = version!!.toString() + + inceptionYear = 2023 + + sourceControlManagement( + Scm.Git( + owner = "teogor", + repo = "xenoglot", + ), + ) + + addLicense(LicenseType.APACHE_2_0) + + addDeveloper(TeogorDeveloper()) + } + + docsGenerator { + name = "Xenoglot" + identifier = "xenoglot" + alertOnDependentModules = true + } +} + +afterWindsPluginConfiguration { winds -> + val mavenPublish: MavenPublish by winds + if (mavenPublish.canBePublished) { + mavenPublishing { + publishToMavenCentral(SonatypeHost.S01) + signAllPublications() + + @Suppress("UnstableApiUsage") + pom { + coordinates( + groupId = mavenPublish.groupId!!, + artifactId = mavenPublish.artifactId!!, + version = mavenPublish.version!!.toString(), + ) + mavenPublish attachTo this + } + } + } +} + +data class TeogorDeveloper( + override val id: String = "teogor", + override val name: String = "Teodor Grigor", + override val email: String = "open-source@teogor.dev", + override val url: String = "https://teogor.dev", + override val roles: List = listOf("Code Owner", "Developer", "Designer", "Maintainer"), + override val timezone: String = "UTC+2", + override val organization: String = "Teogor", + override val organizationUrl: String = "https://github.com/teogor", +) : Developer + +val ktlintVersion = "0.50.0" + +val excludedProjects = listOf( + project.name, +) + +subprojects { + if (!excludedProjects.contains(this.name)) { + apply() + configure { + kotlin { + target("**/*.kt") + targetExclude("**/build/**/*.kt") + ktlint(ktlintVersion) + .userData( + mapOf( + "ktlint_code_style" to "official", + "ij_kotlin_allow_trailing_comma" to "true", + // These rules were introduced in ktlint 0.46.0 and should not be + // enabled without further discussion. They are disabled for now. + // See: https://github.com/pinterest/ktlint/releases/tag/0.46.0 + "disabled_rules" to + "filename," + + "annotation,annotation-spacing," + + "argument-list-wrapping," + + "double-colon-spacing," + + "enum-entry-name-case," + + "multiline-if-else," + + "no-empty-first-line-in-method-block," + + "package-name," + + "trailing-comma," + + "spacing-around-angle-brackets," + + "spacing-between-declarations-with-annotations," + + "spacing-between-declarations-with-comments," + + "unary-op-spacing," + + "no-trailing-spaces," + + "no-wildcard-imports," + + "max-line-length", + ), + ) + licenseHeaderFile(rootProject.file("spotless/copyright.kt")) + trimTrailingWhitespace() + endWithNewline() + } + format("kts") { + target("**/*.kts") + targetExclude("**/build/**/*.kts") + // Look for the first line that doesn't have a block comment (assumed to be the license) + licenseHeaderFile(rootProject.file("spotless/copyright.kts"), "(^(?![\\/ ]\\*).*$)") + } + format("xml") { + target("**/*.xml") + targetExclude("**/build/**/*.xml") + // Look for the first XML tag that isn't a comment (