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 |
When Claude uses it
Section titled “When Claude uses it”Complete integration guide for the loco-android Gradle plugin. Use when adding the plugin to a project, configuring translations, or running fetch/push tasks.
Definition
Section titled “Definition”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.
0. Detect AGP Version
Section titled “0. Detect AGP Version”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:
gradle/libs.versions.toml→[versions]section:agp = "X.Y.Z"- Root
build.gradle.kts:id("com.android.application") version "X.Y.Z"// orclasspath("com.android.tools.build:gradle:X.Y.Z") - 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.
1. Before Writing Any Code — Required Questions
Section titled “1. Before Writing Any Code — Required Questions”Ask the user these questions using askUserQuestionTool before touching any files:
-
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
- Options:
-
Languages — Do NOT use
askUserQuestionToolfor 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. -
Default language — Do NOT use
askUserQuestionToolfor this question. Instead, send a plain chat message: “Which is the default/fallback language? (e.g. en)” and wait for the user’s reply. -
Resource directory — “Where is your
res/folder?”- Options:
Default ($projectDir/src/main/res)/Custom path (free text)
- Options:
-
Multiple Loco projects — “Do you use more than one Loco project / API key?”
- Options:
Yes/No
- Options:
-
Filename — “Should the output file be named
strings.xmlor something else?”- Options:
strings.xml (default)/Custom name (free text)
- Options:
Do not proceed until you have answers to all questions.
2. Apply the Plugin
Section titled “2. Apply the Plugin”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
Section titled “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)}Without version catalog (inline version)
Section titled “Without version catalog (inline version)”Root build.gradle.kts:
plugins { id("com.appswithlove.loco") version "<plugin-version>" apply false}App/module build.gradle.kts:
plugins { id("com.appswithlove.loco")}3. Minimal Configuration
Section titled “3. Minimal Configuration”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" }}4. API Key Setup (Preferred: Never Hardcode)
Section titled “4. API Key Setup (Preferred: Never Hardcode)”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.
4.1 local.properties (local dev)
Section titled “4.1 local.properties (local dev)”# local.properties (create it if it doesn't exist — this file must be in .gitignore)locoApiKey=YOUR_LOCO_API_KEYThen 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:
LOCO_REF='op://VAULT/<project> Loco API Key/credential' ./scripts/setup-loco.sh4.2 Environment variable (CI/CD)
Section titled “4.2 Environment variable (CI/CD)”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.
5. All Configuration Properties
Section titled “5. All Configuration Properties”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 }}6. Multiple Loco Projects
Section titled “6. Multiple Loco Projects”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" }}7. Running the Tasks
Section titled “7. Running the Tasks”Fetch translations from Loco → local XML
Section titled “Fetch translations from Loco → local XML”./gradlew locoFetch- Overwrites existing
strings.xmlfiles invalues/andvalues-<lang>/directories. - Android Studio: Gradle panel → Tasks → other →
locoFetch.
Push local strings → Loco
Section titled “Push local strings → Loco”./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.
8. Language Code Mapping
Section titled “8. Language Code Mapping”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.
9. Common Patterns
Section titled “9. Common Patterns”Suppress MissingTranslation lint warnings
Section titled “Suppress MissingTranslation lint warnings”config { ignoreMissingTranslationWarnings = true}Filter by tag
Section titled “Filter by tag”config { tags = "android" // only assets tagged "android" // tags = "android,!beta" // "android" but not "beta"}Add prefix to all resource names
Section titled “Add prefix to all resource names”config { resourceNamePrefix = "myapp_" // "welcome_title" in Loco → "myapp_welcome_title" in XML}Replace custom placeholder format
Section titled “Replace custom placeholder format”config { // Replaces {{variable}} style placeholders with %s placeholderPattern = "~\\{\\{.*?}}"}Regex string replacements
Section titled “Regex string replacements”config { replace = mapOf( "&" to "&", // unescape HTML entities "\\\\n" to "\n", // literal \n to newline )}10. Key Constraints
Section titled “10. Key Constraints”| 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. |
11. Quick Reference
Section titled “11. Quick Reference”Minimal working config (AWL KMP template, androidApp/build.gradle.kts)
Section titled “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 }}Full-featured config
Section titled “Full-featured config”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_" }}
