Zum Inhalt springen

loco-android plugin integration

Type Skill
Plugin awl-android · v0.0.19
Invoke /awl-android:loco-plugin-integration
Source plugins/awl-android/skills/loco-plugin-integration/SKILL.md

Complete integration guide for the loco-android Gradle plugin. Use when adding the plugin to a project, configuring translations, or running fetch/push tasks.

com.appswithlove.loco is a Gradle plugin that synchronizes Android strings.xml files with Loco (localise.biz). It exposes two tasks: locoFetch (download translations) and locoPush (upload strings).

Version matrix:

Plugin version AGP requirement
1.2.0 (latest) AGP 9.0+
0.4.1 AGP < 9.0

Before applying the plugin, check the project’s AGP version in the root build.gradle.kts or libs.versions.toml and use the correct plugin version accordingly.


Before touching any files, read the project’s AGP version to determine which plugin version to use.

Check these files in order until you find the version:

  1. gradle/libs.versions.toml[versions] section:
    agp = "X.Y.Z"
  2. Root build.gradle.kts:
    id("com.android.application") version "X.Y.Z"
    // or
    classpath("com.android.tools.build:gradle:X.Y.Z")
  3. Root build.gradle (Groovy):
    classpath 'com.android.tools.build:gradle:X.Y.Z'

Decision:

AGP version Plugin version to use
≥ 9.0.0 1.2.0
< 9.0.0 0.4.1

Use the resolved version as <plugin-version> throughout all steps below.


Ask the user these questions using askUserQuestionTool before touching any files:

  1. API key location — “Where do you want to store your Loco API key?”

    • Options: local.properties / gradle.properties / Environment variable (LOCO_API_KEY) / Hardcoded in DSL
  2. Languages — Do NOT use askUserQuestionTool for this question. Instead, send a plain chat message: “Which language codes do you need to fetch? (e.g. en, de, fr-FR)” and wait for the user’s reply.

  3. Default language — Do NOT use askUserQuestionTool for this question. Instead, send a plain chat message: “Which is the default/fallback language? (e.g. en)” and wait for the user’s reply.

  4. Resource directory — “Where is your res/ folder?”

    • Options: Default ($projectDir/src/main/res) / Custom path (free text)
  5. Multiple Loco projects — “Do you use more than one Loco project / API key?”

    • Options: Yes / No
  6. Filename — “Should the output file be named strings.xml or something else?”

    • Options: strings.xml (default) / Custom name (free text)

Do not proceed until you have answers to all questions.


Choose the plugin version based on the AGP version detected in section 0.

Check if the project uses a version catalog (gradle/libs.versions.toml exists). Use the appropriate approach:

With version catalog (gradle/libs.versions.toml exists) — preferred

Abschnitt betitelt „With version catalog (gradle/libs.versions.toml exists) — preferred“

Add to gradle/libs.versions.toml:

[versions]
loco = "<plugin-version>"
[plugins]
loco = { id = "com.appswithlove.loco", version.ref = "loco" }

Root build.gradle.kts:

plugins {
alias(libs.plugins.loco) apply false
}

App/module build.gradle.kts:

plugins {
alias(libs.plugins.loco)
}

Root build.gradle.kts:

plugins {
id("com.appswithlove.loco") version "<plugin-version>" apply false
}

App/module build.gradle.kts:

plugins {
id("com.appswithlove.loco")
}

app/build.gradle.kts
Loco {
config {
apiKey = "YOUR_LOCO_API_KEY" // see section 4 for secure alternatives
lang = listOf("en", "de", "fr")
defLang = "en"
resDir = "$projectDir/src/main/res"
}
}

Groovy DSL equivalent:

Loco {
config {
apiKey = 'YOUR_LOCO_API_KEY'
lang = ['en', 'de', 'fr']
defLang = 'en'
resDir = "$projectDir/src/main/res"
}
}

The plugin resolves apiKey using this priority chain (first match wins):

Priority Source How
1 DSL apiKey = "..." Hardcoded — avoid in production
2 Gradle property -PlocoApiKey=… CLI flag or gradle.properties entry locoApiKey=…
3 local.properties Add locoApiKey=YOUR_KEY (must be gitignored)
4 Environment variable LOCO_API_KEY=YOUR_KEY

When apiKey is omitted from the DSL block, the plugin falls through to the next source automatically. Recommended approach for local dev: local.properties. Recommended for CI: environment variable.

# local.properties (create it if it doesn't exist — this file must be in .gitignore)
locoApiKey=YOUR_LOCO_API_KEY

Then omit apiKey from the DSL:

Loco {
config {
lang = listOf("en", "de")
defLang = "en"
resDir = "$projectDir/src/main/res"
}
}

The AWL templates ship scripts/setup-loco.sh, which reads the key from 1Password and appends it to local.properties:

Terminal-Fenster
LOCO_REF='op://VAULT/<project> Loco API Key/credential' ./scripts/setup-loco.sh

Set LOCO_API_KEY as a masked GitLab CI/CD variable. No change to build.gradle.kts needed when also using local.properties locally — the fallback chain handles both.


Loco {
config {
// Required
apiKey = "..." // Loco export API key
lang = listOf("en", "de", "pt-BR") // languages to fetch
defLang = "en" // maps to values/strings.xml
resDir = "$projectDir/src/main/res" // Android res directory
// Optional
fileName = "strings" // output filename, default: "strings"
hideComments = false // strip Loco comments from XML
tags = "android,!beta" // filter assets by tag (comma-sep, ! = exclude, * = wildcard)
fallbackLang = "en" // fallback when translation is missing
orderByAssetId = false // sort assets alphabetically
status = "translated,!fuzzy" // filter by translation status
saveDefLangDuplicate = false // also save defLang in values-<lang>/ folder
resourceNamePrefix = "app_" // prefix added to every resource name
placeholderPattern = "~\\{\\{.*?}}" // regex to replace custom placeholders with %s
ignoreMissingTranslationWarnings = false // inject tools:ignore="MissingTranslation"
replace = mapOf("\\\\n" to "\n") // regex string replacements applied to values
index = "id" // asset lookup key ("id" or "text")
locoBaseUrl = "https://localise.biz/api/export/locale" // override export endpoint
locoImportBaseUrl = "https://localise.biz/api/import" // override import endpoint
}
}

Each config {} block is independent. All blocks are processed by both locoFetch and locoPush.

Loco {
config {
apiKey = "API_KEY_PROJECT_A"
lang = listOf("en", "de")
defLang = "en"
resDir = "$projectDir/src/main/res"
fileName = "strings"
}
config {
apiKey = "API_KEY_PROJECT_B"
lang = listOf("en", "fr")
defLang = "en"
resDir = "$projectDir/src/main/res"
fileName = "marketing_strings"
}
}

Terminal-Fenster
./gradlew locoFetch
  • Overwrites existing strings.xml files in values/ and values-<lang>/ directories.
  • Android Studio: Gradle panel → Tasks → other → locoFetch.
Terminal-Fenster
./gradlew locoPush
  • Adds new strings; does not delete anything from Loco.
  • Requires a full-access (Developer) API key, not a read-only export key.
  • Android Studio: Gradle panel → Tasks → other → locoPush.

Loco uses IETF BCP 47 codes. The plugin converts them to Android resource folder naming automatically:

Loco code Android folder
en values/ (if defLang) or values-en/
de values-de/
pt-BR values-pt-rBR/
zh-Hans values-b+zh+Hans/

Use the Loco format in the lang list — the plugin handles the conversion.


config {
ignoreMissingTranslationWarnings = true
}
config {
tags = "android" // only assets tagged "android"
// tags = "android,!beta" // "android" but not "beta"
}
config {
resourceNamePrefix = "myapp_"
// "welcome_title" in Loco → "myapp_welcome_title" in XML
}
config {
// Replaces {{variable}} style placeholders with %s
placeholderPattern = "~\\{\\{.*?}}"
}
config {
replace = mapOf(
"&amp;" to "&", // unescape HTML entities
"\\\\n" to "\n", // literal \n to newline
)
}

Constraint Detail
AGP version Plugin 1.2.0 requires AGP 9.0+. Use plugin 0.4.1 for older AGP versions.
API key for push Export-only keys (read-only) cannot push. Use a Developer key from Loco → Developer tools → API keys.
locoFetch overwrites Existing XML files are fully replaced on each fetch — do not manually edit fetched files.
locoPush is additive Push only adds new strings; it never deletes from Loco.
defLang Must be one of the codes in lang. Maps to values/strings.xml (no language suffix).
No source sets The plugin applies at project level; no variant-specific setup needed.
Deprecated task updateLoco still works but is an alias for locoFetch — use locoFetch instead.

Minimal working config (AWL KMP template, androidApp/build.gradle.kts)

Abschnitt betitelt „Minimal working config (AWL KMP template, androidApp/build.gradle.kts)“
Loco {
config {
// apiKey resolved from local.properties (locoApiKey) or LOCO_API_KEY env var
lang = listOf("en", "de")
defLang = "en"
resDir = "$projectDir/src/main/res"
hideComments = true
orderByAssetId = true
}
}
Loco {
config {
lang = listOf("en", "de", "fr", "pt-BR")
defLang = "en"
resDir = "$projectDir/src/main/res"
fileName = "strings"
hideComments = true
tags = "android"
fallbackLang = "en"
orderByAssetId = true
status = "translated"
ignoreMissingTranslationWarnings = true
resourceNamePrefix = "app_"
}
}