Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -52,3 +52,6 @@ eml-files/
# Merged PRs report
merged-prs-*.md
merged-prs-*.csv

# Weblate cache
cli/weblate-cli/weblate-components-cache.json
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ tracked in our [sprint board](https://github.com/orgs/thunderbird/projects/20/vi
We welcome contributions from everyone.

- Development: Have you done a little bit of Kotlin? The [CONTRIBUTING](docs/CONTRIBUTING.md) guide will help you get started
- Translations: Do you speak a language aside from English? [Translating is easy](https://hosted.weblate.org/projects/tb-android/) and just takes a few minutes for your first success.
- Translations: Do you speak a language aside from English? [Translating is easy](https://hosted.weblate.org/projects/thunderbird/thunderbird-android/) and just takes a few minutes for your first success.
- We have [a number of other contribution opportunities](https://blog.thunderbird.net/2024/09/contribute-to-thunderbird-for-android/) available.
- Thunderbird is supported solely by financial contributions from users like you. [Make a financial contribution today](https://www.thunderbird.net/donate/mobile/?form=tfa)!
- Make sure to check out the [Mozilla Community Participation Guidelines](https://www.mozilla.org/about/governance/policies/participation/).
Expand Down
6 changes: 4 additions & 2 deletions cli/translation-cli/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Translation CLI

This is a command line interface that will check the [weblate](https://hosted.weblate.org/projects/tb-android/#languages) translation state for all languages and print out the ones that are above a certain threshold.
This is a command line interface that will check the [weblate](https://hosted.weblate.org/projects/thunderbird/thunderbird-android/#languages) translation state for all languages and print out the ones that are above a certain threshold.

## Usage

Expand All @@ -9,11 +9,13 @@ To use this script you need to have a [weblate token](https://hosted.weblate.org
You can run the script with the following command:

```bash
./scripts/translation --token <weblate-token> [--threshold 70]
./scripts/translation --token <weblate-token> [--threshold 70] [--log-level INFO]
```

It will print out the languages that are above the threshold. The default threshold is 70. You can change it by passing the `--threshold` argument.

The `--log-level` option controls the verbosity of the Weblate API client's logs. Supported values are: `NONE`, `INFO`, `HEADERS`, `BODY`, `ALL`. The default is `NONE`.

If you want a code example, you can pass the `--print-all` argument. It will print out example code for easier integration into the project.

```bash
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@ package net.thunderbird.cli.translation
import net.thunderbird.cli.translation.net.Language
import net.thunderbird.cli.translation.net.Translation
import net.thunderbird.cli.translation.net.WeblateClient
import net.thunderbird.cli.translation.net.WeblateConfig

class LanguageCodeLoader(
private val client: WeblateClient = WeblateClient(),
config: WeblateConfig = WeblateConfig(),
private val client: WeblateClient = WeblateClient(config = config),
) {
fun loadCurrentAndroidLanguageCodes(token: String, threshold: Double): List<String> {
val languages = client.loadLanguages(token)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ import com.github.ajalt.clikt.parameters.options.flag
import com.github.ajalt.clikt.parameters.options.option
import com.github.ajalt.clikt.parameters.options.required
import com.github.ajalt.clikt.parameters.types.double
import com.github.ajalt.clikt.parameters.types.enum
import io.ktor.client.plugins.logging.LogLevel
import net.thunderbird.cli.translation.net.WeblateConfig

const val TRANSLATED_THRESHOLD = 70.0

Expand All @@ -29,9 +32,16 @@ class TranslationCli(
help = "Print code example",
).flag()

private val logLevel: LogLevel by option(
"--log-level",
help = "Log level for the Weblate API client",
).enum<LogLevel>(ignoreCase = true).default(LogLevel.NONE)

override fun help(context: Context): String = "Translation CLI"

override fun run() {
val config = WeblateConfig(logLevel = logLevel)
val languageCodeLoader = LanguageCodeLoader(config = config)
val languageCodes = languageCodeLoader.loadCurrentAndroidLanguageCodes(token, threshold)
val androidLanguageCodes = languageCodes.map { AndroidLanguageCodeHelper.fixLanguageCodeFormat(it) }
val size = languageCodes.size
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import io.ktor.client.call.body
import io.ktor.client.engine.cio.CIO
import io.ktor.client.plugins.contentnegotiation.ContentNegotiation
import io.ktor.client.plugins.logging.DEFAULT
import io.ktor.client.plugins.logging.LogLevel
import io.ktor.client.plugins.logging.Logger
import io.ktor.client.plugins.logging.Logging
import io.ktor.client.request.get
Expand All @@ -15,8 +14,8 @@ import kotlinx.coroutines.runBlocking
import kotlinx.serialization.json.Json

class WeblateClient(
private val client: HttpClient = createClient(),
private val config: WeblateConfig = WeblateConfig(),
private val client: HttpClient = createClient(config),
) {
fun loadLanguages(token: String): List<Language> {
val languages: List<Language>
Expand Down Expand Up @@ -63,11 +62,11 @@ class WeblateClient(
}

private companion object {
fun createClient(): HttpClient {
fun createClient(config: WeblateConfig): HttpClient {
return HttpClient(CIO) {
install(Logging) {
logger = Logger.DEFAULT
level = LogLevel.NONE
level = config.logLevel
}
install(ContentNegotiation) {
json(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,20 @@
package net.thunderbird.cli.translation.net

import io.ktor.client.plugins.logging.LogLevel

/**
* Configuration for Weblate API
*
* @property baseUrl Base URL of the Weblate API
* @property projectName Name of the Weblate project
* @property defaultComponent Default component to use for translations
* @property logLevel Log level for the Weblate API client
*/
data class WeblateConfig(
val baseUrl: String = "https://hosted.weblate.org/api/",
val projectName: String = "tb-android",
val defaultComponent: String = "app-strings",
val projectName: String = "thunderbird",
val defaultComponent: String = "thunderbird-android%252Fapp-common",
val logLevel: LogLevel = LogLevel.NONE,
private val defaultHeaders: Map<String, String> = mapOf(
"Accept" to "application/json",
"Authorization" to "Token $PLACEHOLDER_TOKEN",
Expand Down
45 changes: 34 additions & 11 deletions cli/weblate-cli/README.md
Original file line number Diff line number Diff line change
@@ -1,33 +1,54 @@
# Weblate CLI

This is a command line interface that inspects Weblate project components and applies a
"golden" component configuration. It's intended for maintainers to review component configuration
consistency and, when appropriate, patch components to match the golden config.
"default" component configuration. It's intended for maintainers to review component configuration
consistency and, when appropriate, patch components to match the component config.

## Usage

You need a Weblate API token (available from your Weblate account profile). A convenience wrapper script
is provided at `./scripts/weblate` which builds and runs the CLI.

The CLI uses a subcommand pattern: `weblate [OPTIONS] COMMAND [ARGS]...`

Available commands:
- `update`: Update managed components with the standard configuration.
- `create`: Create missing components on Weblate based on local modules.
- `delete`: Delete a component from Weblate.

Basic examples:

```bash
# Dry-run using the default golden config and include file
./scripts/weblate --token YOUR_WEBLATE_TOKEN --dry-run
# Dry-run using the default configuration and managed components file
./scripts/weblate --token YOUR_WEBLATE_TOKEN --dry-run update

# Apply changes to managed components
./scripts/weblate --token YOUR_WEBLATE_TOKEN update

# Apply changes to included components
./scripts/weblate --token YOUR_WEBLATE_TOKEN
# Create missing components
./scripts/weblate --token YOUR_WEBLATE_TOKEN create

# Use a custom include file and golden config
./scripts/weblate --token YOUR_WEBLATE_TOKEN --include-file-path ./cli/weblate-cli/include-components.txt --golden-config-path ./cli/weblate-cli/golden-component-config.json --dry-run
# Delete a component by slug
./scripts/weblate --token YOUR_WEBLATE_TOKEN delete --slug component-slug-to-delete

# Use a custom managed components file, component config, and set log level to ALL
./scripts/weblate --token YOUR_WEBLATE_TOKEN --managed-components-file ./cli/weblate-cli/managed-components.txt --component-config-file ./cli/weblate-cli/default-component-config.json --log-level ALL --dry-run update
```

## Available options

- `--token`: Weblate API token (required).
- `--component-config-file`: Path to component config JSON (default: `./cli/weblate-cli/default-component-config.json`).
- `--managed-components-file`: Path to file with managed component slugs (default: `./cli/weblate-cli/managed-components.txt`).
- `--dry-run`: Dry run the command without making any changes.
- `--log-level`: Log level for the Weblate API client (`NONE`, `INFO`, `HEADERS`, `BODY`, `ALL`). Default: `NONE`.

## Defaults

- Golden config: `./cli/weblate-cli/golden-component-config.json`
- Include file: `./cli/weblate-cli/include-components.txt`
- Component config: `./cli/weblate-cli/default-component-config.json`
- Managed components file: `./cli/weblate-cli/managed-components.txt`

## Include file format
## Managed components file format

- One component slug per non-empty line. Inline comments are allowed after `#` and full-line comments that
start with `#` are ignored.
Expand All @@ -45,4 +66,6 @@ app-common
## Safety notes

- Always run with `--dry-run` first to verify diffs before applying changes to the live Weblate instance.
- The `update` command only processes components listed in the managed components file.
- Use `./scripts/weblate --help` to see all available commands and options.

Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
"commit_pending_age": 24,
"auto_lock_error": true,

"commit_message": "chore(i18n): translated using Weblate\r\n\r\nTranslation: {{ project_name }}/{{ component_name }}\r\nTranslate-URL: {{ url }}",
"commit_message": "chore(i18n): translated using Weblate ({{ language_name }})\r\n\r\nTranslation: {{ project_name }}/{{ component_name }}\r\nTranslate-URL: {{ url }}",
"add_message": "chore(i18n): added translation using Weblate ({{ language_name }})",
"delete_message": "chore(i18n): deleted translation using Weblate ({{ language_name }})",
"merge_message": "chore(i18n): merge branch '{{ component_remote_branch }}' into Weblate",
Expand Down
30 changes: 0 additions & 30 deletions cli/weblate-cli/include-components.txt

This file was deleted.

37 changes: 37 additions & 0 deletions cli/weblate-cli/managed-components.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# Managed components
## Main Component
app-common # ID: 44665
## Child Components
core-ui-compose-designsystem # ID: 49031
legacy-ui-base # ID: 49036
legacy-ui-folder # ID: 49035
legacy-ui-legacy # ID: 44667
app-thunderbird # ID: 46953
app-k9mail # ID: 46954
core-ui-setting-impl-dialog # ID: 46955
core-ui-design-system # ID: 46956
feature-account-common # ID: 46957
feature-account-edit # ID: 46958
feature-account-oauth # ID: 46959
feature-account-server-certificate # ID: 46960
feature-account-server-settings # ID: 46961
feature-account-server-validation # ID: 49047
feature-account-settings-impl # ID: 46962
feature-account-setup # ID: 46963
feature-debug-settings # ID: 49053
feature-funding-googleplay # ID: 49052
feature-mail-message-composer # ID: 49043
feature-mail-message-list-api # ID: 49045
feature-mail-message-list-internal # ID: 49044
feature-migration-qrcode # ID: 49048
feature-navigation-drawer-dropdown # ID: 49046
feature-notification-api # ID: 49042
feature-onboarding-migration-thunderbird # ID: 49051
feature-onboarding-permissions # ID: 49050
feature-onboarding-welcome # ID: 49049
feature-settings-import # ID: 49037
feature-widget-message-list # ID: 49039
feature-widget-message-list-glance # ID: 49040
feature-widget-shortcut # ID: 49038
feature-widget-unread # ID: 49041

Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package net.thunderbird.cli.weblate

import io.ktor.client.plugins.logging.LogLevel

data class CliConfig(
val token: String,
val componentConfigFile: String,
val managedComponentsFile: String,
val dryRun: Boolean,
val logLevel: LogLevel,
)
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
package net.thunderbird.cli.weblate

import com.github.ajalt.clikt.core.main
import com.github.ajalt.clikt.core.subcommands
import net.thunderbird.cli.weblate.command.CreateComponent
import net.thunderbird.cli.weblate.command.DeleteComponent
import net.thunderbird.cli.weblate.command.UpdateComponent

fun main(args: Array<String>) = WeblateCli().main(args)
fun main(args: Array<String>) = WeblateCli()
.subcommands(
UpdateComponent(),
CreateComponent(),
DeleteComponent(),
).main(args)
Loading
Loading