From b5f7d26c9d4e1151114e557d959a7c99a5300669 Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Mon, 12 Oct 2026 11:01:13 +0000 Subject: [PATCH 1/8] chore(deps): update tonistiigi/xx docker tag to v1.10.0 --- vendor/github.com/docker/distribution/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vendor/github.com/docker/distribution/Dockerfile b/vendor/github.com/docker/distribution/Dockerfile index ebd42c24..84880e24 100644 --- a/vendor/github.com/docker/distribution/Dockerfile +++ b/vendor/github.com/docker/distribution/Dockerfile @@ -2,7 +2,7 @@ ARG GO_VERSION=1.20.8 ARG ALPINE_VERSION=3.18 -ARG XX_VERSION=1.2.1 +ARG XX_VERSION=1.10.0 FROM --platform=$BUILDPLATFORM tonistiigi/xx:${XX_VERSION} AS xx FROM --platform=$BUILDPLATFORM golang:${GO_VERSION}-alpine${ALPINE_VERSION} AS base From e611a3be38bb0eed17e59ba427dd889920e108c0 Mon Sep 17 00:00:00 2001 From: jade Date: Wed, 14 Oct 2026 17:13:01 +1100 Subject: [PATCH 2/8] Sort invalid versions to the bottom rather than panicking --- pkg/recipe/recipe.go | 33 +++++++++++++++------------------ pkg/recipe/recipe_test.go | 38 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+), 18 deletions(-) diff --git a/pkg/recipe/recipe.go b/pkg/recipe/recipe.go index 592e2452..a5fa1b6e 100644 --- a/pkg/recipe/recipe.go +++ b/pkg/recipe/recipe.go @@ -592,15 +592,7 @@ func ReadReposMetadata(debug bool) (RepoCatalogue, error) { // sortRecipeVersions sorts the recipe semver versions func sortRecipeVersions(versions RecipeVersions) { sort.Slice(versions, func(i, j int) bool { - version1, err := tagcmp.Parse(getVersionString(versions[i])) - if err != nil { - panic(err) - } - version2, err := tagcmp.Parse(getVersionString(versions[j])) - if err != nil { - panic(err) - } - return version1.IsLessThan(version2) + return safeCompareVersions(getVersionString(versions[i]), getVersionString(versions[j])) }) } @@ -616,18 +608,23 @@ func getVersionString(versionMap map[string]map[string]ServiceMeta) string { // sortVersionStrings sorts a list of semver version strings func sortVersionStrings(versions []string) { sort.Slice(versions, func(i, j int) bool { - version1, err := tagcmp.Parse(versions[i]) - if err != nil { - panic(err) - } - version2, err := tagcmp.Parse(versions[j]) - if err != nil { - panic(err) - } - return version1.IsLessThan(version2) + return safeCompareVersions(versions[i], versions[j]) }) } +// Invalid versions are sorted as less than valid ones. +func safeCompareVersions(v1, v2 string) bool { + version1, err1 := tagcmp.Parse(v1) + if err1 != nil { + return true + } + version2, err2 := tagcmp.Parse(v2) + if err2 != nil { + return false + } + return version1.IsLessThan(version2) +} + // GetRecipeCatalogueVersions list the recipe versions listed in the recipe catalogue. func GetRecipeCatalogueVersions(recipeName string, catl RecipeCatalogue) ([]string, error) { var versions []string diff --git a/pkg/recipe/recipe_test.go b/pkg/recipe/recipe_test.go index 9f3c5748..c623f161 100644 --- a/pkg/recipe/recipe_test.go +++ b/pkg/recipe/recipe_test.go @@ -123,3 +123,41 @@ func TestGetVersionLabelLocalDoesNotUseTimeoutLabel(t *testing.T) { assert.NotEqual(t, label, timeout) } } + +// This is not a full test of comparison, as that occurs in the external +// tagcmp package. This is a sunny-day integration test +func TestSortRecipeVersionsSortsTwoValidStrings(t *testing.T) { + versions := RecipeVersions{ + {"2.0.0": {}}, // Index 0 + {"1.0.0": {}}, // Index 1 + } + + sortRecipeVersions(versions) + + assert.Equal(t, "1.0.0", getVersionString(versions[0]), "Version 1.0.0 should be first") + assert.Equal(t, "2.0.0", getVersionString(versions[1]), "Version 2.0.0 should be second") +} + +func TestSortRecipeVersionsSortsInvalidVersionStringToTheBottom(t *testing.T) { + versions := RecipeVersions{ + {"2.0.0": {}}, // Index 0 + {"invalid": {}}, // Index 1 + } + + sortRecipeVersions(versions) + + assert.Equal(t, "invalid", getVersionString(versions[0]), "The invalid version should be first") + assert.Equal(t, "2.0.0", getVersionString(versions[1]), "Version 2.0.0 should be second") +} + +func TestSortRecipeVersionsSortsInvalidVersionStringToTheBottomInReverse(t *testing.T) { + versions := RecipeVersions{ + {"invalid": {}}, // Index 0 + {"2.0.0": {}}, // Index 1 + } + + sortRecipeVersions(versions) + + assert.Equal(t, "invalid", getVersionString(versions[0]), "The invalid version should be first") + assert.Equal(t, "2.0.0", getVersionString(versions[1]), "Version 2.0.0 should be second") +} From 14f05407f39cdaf07e710a13d68cb464e669e0cc Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Wed, 14 Oct 2026 15:02:32 +0000 Subject: [PATCH 3/8] chore(deps): update module github.com/leonelquinteros/gotext to v1.8.0 --- go.mod | 43 +- go.sum | 4 +- vendor/coopcloud.tech/tagcmp/.drone.yml | 6 +- .../github.com/docker/distribution/Dockerfile | 2 +- .../leonelquinteros/gotext/AGENTS.md | 64 +++ .../leonelquinteros/gotext/CODE_OF_CONDUCT.md | 47 +- .../leonelquinteros/gotext/CONTRIBUTING.md | 18 +- .../leonelquinteros/gotext/GEMINI.md | 9 + .../leonelquinteros/gotext/README.md | 309 ++++------ .../leonelquinteros/gotext/domain.go | 475 ++++++++++----- .../leonelquinteros/gotext/gotext.go | 284 +++++---- .../leonelquinteros/gotext/helper.go | 58 +- .../leonelquinteros/gotext/locale.go | 377 ++++++------ .../leonelquinteros/gotext/mkdocs.yml | 40 ++ .../github.com/leonelquinteros/gotext/mo.go | 162 +++--- .../gotext/plurals/compiler.go | 540 ++++++++++++------ .../gotext/plurals/expression.go | 34 ++ .../leonelquinteros/gotext/plurals/math.go | 3 + .../leonelquinteros/gotext/plurals/tests.go | 17 + .../github.com/leonelquinteros/gotext/po.go | 383 ++++++++++--- .../leonelquinteros/gotext/translation.go | 20 +- .../leonelquinteros/gotext/translator.go | 74 ++- vendor/github.com/pjbgf/sha1cd/Dockerfile.arm | 2 +- .../github.com/pjbgf/sha1cd/Dockerfile.arm64 | 2 +- vendor/modules.txt | 4 +- 25 files changed, 1837 insertions(+), 1140 deletions(-) create mode 100644 vendor/github.com/leonelquinteros/gotext/AGENTS.md create mode 100644 vendor/github.com/leonelquinteros/gotext/GEMINI.md create mode 100644 vendor/github.com/leonelquinteros/gotext/mkdocs.yml diff --git a/go.mod b/go.mod index 769c6b52..ce0550b9 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module coopcloud.tech/abra -go 1.26.0 +go 1.27 require ( coopcloud.tech/tagcmp v0.0.0-20260515102403-c26951b55977 @@ -9,17 +9,23 @@ require ( github.com/charmbracelet/bubbletea v1.3.10 github.com/charmbracelet/lipgloss v1.1.0 github.com/charmbracelet/log v1.0.0 + github.com/containers/image v3.0.2+incompatible + github.com/decentral1se/passgen v1.0.1 github.com/distribution/reference v0.6.0 github.com/docker/cli v28.4.0+incompatible github.com/docker/docker v28.5.2+incompatible github.com/docker/go-units v0.5.0 github.com/go-git/go-git/v5 v5.19.3 github.com/google/go-cmp v0.7.0 - github.com/leonelquinteros/gotext v1.7.2 + github.com/hashicorp/go-retryablehttp v0.7.8 + github.com/leonelquinteros/gotext v1.8.0 github.com/moby/sys/signal v0.7.1 github.com/moby/term v0.5.2 github.com/pkg/errors v0.9.1 github.com/schollz/progressbar/v3 v3.19.1 + github.com/spf13/cobra v1.10.1 + github.com/stretchr/testify v1.12.1 + golang.org/x/sys v0.49.0 golang.org/x/term v0.47.0 gopkg.in/yaml.v3 v3.0.1 gotest.tools/v3 v3.5.2 @@ -48,15 +54,18 @@ require ( github.com/containerd/errdefs/pkg v0.3.0 // indirect github.com/containerd/log v0.1.0 // indirect github.com/containerd/platforms v0.2.1 // indirect + github.com/containers/storage v1.38.2 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.7 // indirect github.com/cyphar/filepath-securejoin v0.6.1 // indirect github.com/docker/distribution v2.8.3+incompatible // indirect + github.com/docker/docker-credential-helpers v0.9.5 // indirect github.com/docker/go-connections v0.6.0 // indirect github.com/docker/go-metrics v0.0.1 // indirect github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7 // indirect github.com/emirpasic/gods v1.18.1 // indirect github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect github.com/felixge/httpsnoop v1.0.4 // indirect + github.com/fvbommel/sortorder v1.1.0 // indirect github.com/ghodss/yaml v1.0.0 // indirect github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect github.com/go-git/go-billy/v5 v5.9.2 // indirect @@ -65,7 +74,9 @@ require ( github.com/go-logr/stdr v1.2.2 // indirect github.com/go-viper/mapstructure/v2 v2.5.0 // indirect github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect + github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect github.com/google/uuid v1.6.0 // indirect + github.com/gorilla/mux v1.8.1 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect @@ -83,7 +94,9 @@ require ( github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db // indirect github.com/moby/docker-image-spec v1.3.1 // indirect github.com/moby/go-archive v0.1.0 // indirect + github.com/moby/patternmatcher v0.6.1 // indirect github.com/moby/sys/atomicwriter v0.1.0 // indirect + github.com/moby/sys/sequential v0.6.0 // indirect github.com/moby/sys/user v0.4.0 // indirect github.com/moby/sys/userns v0.1.0 // indirect github.com/morikuni/aec v1.1.0 // indirect @@ -92,18 +105,23 @@ require ( github.com/muesli/termenv v0.16.0 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect + github.com/opencontainers/image-spec v1.1.1 // indirect github.com/opencontainers/runc v1.1.13 // indirect github.com/opencontainers/runtime-spec v1.1.0 // indirect github.com/pjbgf/sha1cd v0.7.0 // indirect + github.com/prometheus/client_golang v1.23.2 // indirect github.com/prometheus/client_model v0.6.2 // indirect github.com/prometheus/common v0.67.5 // indirect github.com/prometheus/procfs v0.20.1 // indirect github.com/rivo/uniseg v0.4.7 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect + github.com/sergi/go-diff v1.4.0 // indirect github.com/sirupsen/logrus v1.9.4 // indirect github.com/skeema/knownhosts v1.3.2 // indirect github.com/spf13/pflag v1.0.10 // indirect + github.com/theupdateframework/notary v0.7.0 // indirect github.com/xanzy/ssh-agent v0.3.3 // indirect + github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect github.com/xeipuuv/gojsonschema v1.2.0 // indirect github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect @@ -134,27 +152,6 @@ require ( gopkg.in/yaml.v2 v2.4.0 // indirect ) -require ( - github.com/containers/image v3.0.2+incompatible - github.com/containers/storage v1.38.2 // indirect - github.com/decentral1se/passgen v1.0.1 - github.com/docker/docker-credential-helpers v0.9.5 // indirect - github.com/fvbommel/sortorder v1.1.0 // indirect - github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect - github.com/gorilla/mux v1.8.1 // indirect - github.com/hashicorp/go-retryablehttp v0.7.8 - github.com/moby/patternmatcher v0.6.1 // indirect - github.com/moby/sys/sequential v0.6.0 // indirect - github.com/opencontainers/image-spec v1.1.1 // indirect - github.com/prometheus/client_golang v1.23.2 // indirect - github.com/sergi/go-diff v1.4.0 // indirect - github.com/spf13/cobra v1.10.1 - github.com/stretchr/testify v1.12.1 - github.com/theupdateframework/notary v0.7.0 // indirect - github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect - golang.org/x/sys v0.49.0 -) - replace github.com/docker/cli v28.4.0+incompatible => git.coopcloud.tech/toolshed/docker-cli v28.5.3-0.20260202112816-30df2d0b3a00+incompatible replace github.com/spf13/cobra => github.com/decentral1se/cobra v1.10.2 diff --git a/go.sum b/go.sum index af9c7b11..55b571a5 100644 --- a/go.sum +++ b/go.sum @@ -602,8 +602,8 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= -github.com/leonelquinteros/gotext v1.7.2 h1:bDPndU8nt+/kRo1m4l/1OXiiy2v7Z7dfPQ9+YP7G1Mc= -github.com/leonelquinteros/gotext v1.7.2/go.mod h1:9/haCkm5P7Jay1sxKDGJ5WIg4zkz8oZKw4ekNpALob8= +github.com/leonelquinteros/gotext v1.8.0 h1:wwpxINWmWq3JN3WFI9BoSupBFrvAqxZTtf3qppijYkU= +github.com/leonelquinteros/gotext v1.8.0/go.mod h1:Wqke380+8unZq2F4oTmmqmbUY9zWQMzwImdC34XOAo8= github.com/lib/pq v0.0.0-20150723085316-0dad96c0b94f/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/linuxkit/virtsock v0.0.0-20201010232012-f8cee7dfc7a3/go.mod h1:3r6x7q95whyfWQpmGZTu3gk3v2YkMi05HEzl7Tf7YEo= github.com/lucasb-eyer/go-colorful v1.4.0 h1:UtrWVfLdarDgc44HcS7pYloGHJUjHV/4FwW4TvVgFr4= diff --git a/vendor/coopcloud.tech/tagcmp/.drone.yml b/vendor/coopcloud.tech/tagcmp/.drone.yml index 2aefe487..c5f78899 100644 --- a/vendor/coopcloud.tech/tagcmp/.drone.yml +++ b/vendor/coopcloud.tech/tagcmp/.drone.yml @@ -3,16 +3,16 @@ kind: pipeline name: coopcloud.tech/tagcmp steps: - name: gofmt - image: golang:1.27 + image: golang:1.26 commands: - test -z "$(gofmt -l .)" - name: go build - image: golang:1.27 + image: golang:1.26 commands: - go build -v . - name: go test - image: golang:1.27 + image: golang:1.26 commands: - go test . -cover diff --git a/vendor/github.com/docker/distribution/Dockerfile b/vendor/github.com/docker/distribution/Dockerfile index 84880e24..ebd42c24 100644 --- a/vendor/github.com/docker/distribution/Dockerfile +++ b/vendor/github.com/docker/distribution/Dockerfile @@ -2,7 +2,7 @@ ARG GO_VERSION=1.20.8 ARG ALPINE_VERSION=3.18 -ARG XX_VERSION=1.10.0 +ARG XX_VERSION=1.2.1 FROM --platform=$BUILDPLATFORM tonistiigi/xx:${XX_VERSION} AS xx FROM --platform=$BUILDPLATFORM golang:${GO_VERSION}-alpine${ALPINE_VERSION} AS base diff --git a/vendor/github.com/leonelquinteros/gotext/AGENTS.md b/vendor/github.com/leonelquinteros/gotext/AGENTS.md new file mode 100644 index 00000000..1bb16fea --- /dev/null +++ b/vendor/github.com/leonelquinteros/gotext/AGENTS.md @@ -0,0 +1,64 @@ +# AGENTS.md - gotext Project Guidelines + +This document is the **source of truth** for the `gotext` project's architecture, conventions, and development workflows. All contributors — human or AI agent — must adhere to these guidelines. + +Agent-specific configuration files (e.g., `GEMINI.md`, `CLAUDE.md`) should reference this document rather than duplicating its content. + +## Project Overview + +`gotext` is a GNU Gettext implementation for Go (Golang). It provides a thread-safe, hierarchical translation system that supports PO/MO files, plural forms, and context-aware translations. + +## Core Architecture + +The project follows a hierarchical structure for managing translations: + +1. **Global Package API (`gotext.go`)**: Provides convenient, global functions (e.g., `Get`, `GetD`, `GetN`) that rely on a shared `globalConfig`. +2. **Locales (`locale.go`)**: Represent a specific language (e.g., "en_US", "es_ES"). A `Locale` container manages multiple `Domains`. +3. **Domains (`domain.go`)**: The core translation engine for a specific set of messages. It handles: + - Message retrieval (with support for plural forms and `msgctxt`). + - Header parsing (including plural-form rules). + - Thread-safety via `sync.RWMutex`. +4. **Translators (`translator.go`)**: An interface for translation sources. + - **PO (`po.go`)**: Parsers for human-readable Gettext Portable Object files. + - **MO (`mo.go`)**: Parsers for binary Gettext Machine Object files. +5. **Plural Forms (`plurals/`)**: A dedicated package for compiling and evaluating GNU Gettext plural form expressions. + +## CLI Tools + +- **xgotext (`cli/xgotext/`)**: A tool to scan Go source files, extract translatable strings (matching `Get`, `GetD`, etc.), and generate/update PO files. + +## Key Files & Symbols + +- `gotext.go`: `Configure`, `Get`, `GetD`, `GetN`. +- `locale.go`: `Locale`, `NewLocale`, `AddDomain`. +- `domain.go`: `Domain`, `Get`, `GetN`, `parseHeaders`. +- `translator.go`: `Translator` interface. +- `plurals/compiler.go`: `Compile` function for plural expressions. + +## Development Workflows + +### Branching & PRs + +- **Feature Branches**: All development MUST happen in dedicated feature branches (e.g., `feature/description-of-change`, `fix/bug-description`, `refactor/topic`). +- **Target Branch**: All Pull Requests MUST be made against the `master` branch. +- **Review Requirement**: No direct commits to `master` are allowed. All changes must be reviewed via a PR. +- **GitHub Integration**: If the `gh` CLI tool is installed and authenticated, it should be used for managing Pull Requests, Issues, and Reviews. Otherwise, these tasks must be performed manually through the GitHub web interface. + +### Coding Standards + +- **Idiomatic Go**: Follow standard Go conventions (`gofmt`, `go vet`). +- **Thread Safety**: Ensure all shared state (like in `Domain`) is protected by appropriate synchronization primitives. +- **Testing**: + - Every new feature or bug fix must include corresponding tests. + - Use the `fixtures/` directory for sample PO/MO files. + - Run `go test ./...` to ensure no regressions. + +### Pre-Submit Checklist + +Before submitting a Pull Request, verify: + +1. All existing tests pass: `go test ./...` +2. Code passes static analysis: `go vet ./...` +3. Code is properly formatted: `gofmt` +4. New features or bug fixes include corresponding tests. +5. Commit messages are clear and descriptive. diff --git a/vendor/github.com/leonelquinteros/gotext/CODE_OF_CONDUCT.md b/vendor/github.com/leonelquinteros/gotext/CODE_OF_CONDUCT.md index 3ed9d207..4c1e205c 100644 --- a/vendor/github.com/leonelquinteros/gotext/CODE_OF_CONDUCT.md +++ b/vendor/github.com/leonelquinteros/gotext/CODE_OF_CONDUCT.md @@ -1,46 +1,3 @@ -# Contributor Covenant Code of Conduct +# CODE OF CONDUCT -## Our Pledge - -In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. - -## Our Standards - -Examples of behavior that contributes to creating a positive environment include: - -* Using welcoming and inclusive language -* Being respectful of differing viewpoints and experiences -* Gracefully accepting constructive criticism -* Focusing on what is best for the community -* Showing empathy towards other community members - -Examples of unacceptable behavior by participants include: - -* The use of sexualized language or imagery and unwelcome sexual attention or advances -* Trolling, insulting/derogatory comments, and personal or political attacks -* Public or private harassment -* Publishing others' private information, such as a physical or electronic address, without explicit permission -* Other conduct which could reasonably be considered inappropriate in a professional setting - -## Our Responsibilities - -Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. - -Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. - -## Scope - -This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. - -## Enforcement - -Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at leonel.quinteros@gmail.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. - -Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. - -## Attribution - -This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] - -[homepage]: http://contributor-covenant.org -[version]: http://contributor-covenant.org/version/1/4/ +The canonical text of this project's Code of Conduct lives at [docs/CODE_OF_CONDUCT.md](docs/CODE_OF_CONDUCT.md). \ No newline at end of file diff --git a/vendor/github.com/leonelquinteros/gotext/CONTRIBUTING.md b/vendor/github.com/leonelquinteros/gotext/CONTRIBUTING.md index 56957d00..cb986bd7 100644 --- a/vendor/github.com/leonelquinteros/gotext/CONTRIBUTING.md +++ b/vendor/github.com/leonelquinteros/gotext/CONTRIBUTING.md @@ -1,19 +1,9 @@ # CONTRIBUTING -This open source project welcomes everybody that wants to contribute to it by implementing new features, fixing bugs, testing, creating documentation or simply talk about it. +Thank you for considering contributing to `gotext`! All kinds of contributions — features, bug fixes, tests, documentation, or discussions — are welcome. -Most contributions will start by creating a new Issue to discuss what is the contribution about and to agree on the steps to move forward. +The canonical contribution guidelines live at [docs/CONTRIBUTING.md](docs/CONTRIBUTING.md). -## Issues - -All issues reports are welcome. Open a new Issue whenever you want to report a bug, request a change or make a proposal. - -This should be your start point of contribution. - - -## Pull Requests - -If you have any changes that can be merged, feel free to send a Pull Request. - -Usually, you'd want to create a new Issue to discuss about the change you want to merge and why it's needed or what it solves. +Before contributing, please also read [AGENTS.md](AGENTS.md): it is the source of truth for project architecture, conventions, and development workflows (branching, PR process, coding standards, and the pre-submit checklist). +Start point: open an [Issue](https://github.com/leonelquinteros/gotext/issues) to discuss your idea, then send a Pull Request against `master`. \ No newline at end of file diff --git a/vendor/github.com/leonelquinteros/gotext/GEMINI.md b/vendor/github.com/leonelquinteros/gotext/GEMINI.md new file mode 100644 index 00000000..4a005a8f --- /dev/null +++ b/vendor/github.com/leonelquinteros/gotext/GEMINI.md @@ -0,0 +1,9 @@ +# GEMINI.md - Gemini CLI Instructions for gotext + +> **Important**: Read and follow [`AGENTS.md`](./AGENTS.md) first. It is the source of truth for project architecture, conventions, and development workflows. The instructions below are specific to Gemini CLI and supplement — not replace — those guidelines. + +## Gemini-Specific Instructions + +- Always work in a feature branch. Never commit directly to `master`. +- Before submitting, verify changes with existing and new tests (`go test ./...`). +- Propose a PR to `master` after confirmation, using the `gh` CLI when available. diff --git a/vendor/github.com/leonelquinteros/gotext/README.md b/vendor/github.com/leonelquinteros/gotext/README.md index c8ee2ace..c3be433c 100644 --- a/vendor/github.com/leonelquinteros/gotext/README.md +++ b/vendor/github.com/leonelquinteros/gotext/README.md @@ -9,258 +9,151 @@ [GNU gettext utilities](https://www.gnu.org/software/gettext) for Go. +`gotext` is a native Go implementation of the GNU Gettext utilities. It provides a thread-safe, flexible, and powerful way to handle internationalization (i18n) and localization (l10n) in your Go applications. -# Features +--- -- Implements GNU gettext support in native Go. -- Complete support for [PO files](https://www.gnu.org/software/gettext/manual/html_node/PO-Files.html) including: - - Support for multiline strings and headers. - - Support for variables inside translation strings using Go's [fmt syntax](https://golang.org/pkg/fmt/). - - Support for [pluralization rules](https://www.gnu.org/software/gettext/manual/html_node/Translating-plural-forms.html). - - Support for [message contexts](https://www.gnu.org/software/gettext/manual/html_node/Contexts.html). -- Support for MO files. -- Thread-safe: This package is safe for concurrent use across multiple goroutines. -- It works with UTF-8 encoding as it's the default for Go language. -- Unit tests available. -- Language codes are automatically simplified from the form `en_UK` to `en` if the first isn't available. -- Ready to use inside Go templates. -- Objects are serializable to []byte to store them in cache. -- Support for Go Modules. +**📖 [Read the Full Documentation](https://leonelquinteros.github.io/gotext/)** +--- -# License +### Table of Contents +- [Features](#features) +- [Installation](#installation) +- [Getting Started](#getting-started) +- [CLI Tool (xgotext)](#cli-tool-xgotext) +- [Advanced Usage](#advanced-usage) + - [Locale Object](#using-locale-object) + - [Plural Forms](#use-plural-forms-of-translations) + - [Dynamic Variables](#using-dynamic-variables-on-translations) +- [Directory Structure](#locales-directories-structure) +- [Contributing](#contributing) +- [License](#license) -[MIT license](LICENSE) +--- +## Features +- Native Go implementation of Gettext (the core library has no external dependencies). +- Full support for **PO and MO files**. +- **Pluralization rules** support via GNU Gettext plural-form expressions. +- **Message context** support (`msgctxt`). +- Thread-safe for concurrent use. +- Works with UTF-8 by default. +- Integrated CLI tool (`xgotext`) for string extraction. +- Serializable objects for caching. +- Plain-string results that plug directly into Go's `text/template` and `html/template` pipelines. -# Documentation +--- -Refer to package documentation at (https://pkg.go.dev/github.com/leonelquinteros/gotext) - - -# Installation - -``` +## Installation +```bash go get github.com/leonelquinteros/gotext ``` -- There are no requirements or dependencies to use this package. -- No need to install GNU gettext utilities (unless specific needs of CLI tools). -- No need for environment variables. Some naming conventions are applied but not needed. +--- - -# Usage examples - -## Using package for single language/domain settings - -For quick/simple translations you can use the package level functions directly. +## Getting Started +For a quick start, use the package-level API: ```go +package main + import ( "fmt" "github.com/leonelquinteros/gotext" ) func main() { - // Configure package - gotext.Configure("/path/to/locales/root/dir", "en_UK", "domain-name") + // Configure package: locales path, language, and domain + gotext.Configure("/path/to/locales", "en_US", "default") - // Translate text from default domain - fmt.Println(gotext.Get("My text on 'domain-name' domain")) + // Simple translation + fmt.Println(gotext.Get("Hello, world!")) - // Translate text from a different domain without reconfigure - fmt.Println(gotext.GetD("domain2", "Another text on a different domain")) + // Translation with variables + fmt.Println(gotext.Get("Hello, %s!", "Gopher")) } - ``` -## Using dynamic variables on translations +For more details, see the [Getting Started Guide](docs/GETTING_STARTED.md). -All translation strings support dynamic variables to be inserted without translate. -Use the fmt.Printf syntax (from Go's "fmt" package) to specify how to print the non-translated variable inside the translation string. +--- + +## CLI Tool (xgotext) +`gotext` includes a command-line tool to extract translatable strings from your Go source code. + +**Install xgotext:** +```bash +go install github.com/leonelquinteros/gotext/cli/xgotext@latest +``` + +**Extract strings:** +```bash +xgotext -in . -out locales/templates +``` +The command generates `.pot` templates under `locales/templates`; merge those templates into language-specific `.po` files. + +See the [xgotext Documentation](docs/xgotext.md) for full usage details. + +--- + +## Advanced Usage + +### Using Locale object +For managing multiple languages or domains independently: ```go -import ( - "fmt" - "github.com/leonelquinteros/gotext" -) - -func main() { - // Configure package - gotext.Configure("/path/to/locales/root/dir", "en_UK", "domain-name") - - // Set variables - name := "John" - - // Translate text with variables - fmt.Println(gotext.Get("Hi, my name is %s", name)) -} - +l := gotext.NewLocale("/path/to/locales", "es_UY") +l.AddDomain("default") +fmt.Println(l.Get("Translate this")) ``` - -## Using Locale object - -When having multiple languages/domains/libraries at the same time, you can create Locale objects for each variation -so you can handle each settings on their own. +### Use plural forms of translations +`gotext` handles complex pluralization rules defined in PO headers: ```go -import ( - "fmt" - "github.com/leonelquinteros/gotext" -) - -func main() { - // Create Locale with library path and language code - l := gotext.NewLocale("/path/to/locales/root/dir", "es_UY") - - // Load domain '/path/to/locales/root/dir/es_UY/default.po' - l.AddDomain("default") - - // Translate text from default domain - fmt.Println(l.Get("Translate this")) - - // Load different domain - l.AddDomain("translations") - - // Translate text from domain - fmt.Println(l.GetD("translations", "Translate this")) -} +// GetN(singular, plural, quantity, args...) +fmt.Println(gotext.GetN("I have one apple.", "I have %d apples.", 5, 5)) ``` +See the [Plural Forms Guide](docs/PLURALS.md) for more examples. -This is also helpful for using inside templates (from the "text/template" package), where you can pass the Locale object to the template. -If you set the Locale object as "Loc" in the template, then the template code would look like: - -``` -{{ .Loc.Get "Translate this" }} -``` - - -## Using the Po object to handle .po files and PO-formatted strings - -For when you need to work with PO files and strings, -you can directly use the Po object to parse it and access the translations in there in the same way. - +### Using dynamic variables on translations +Supports standard `fmt` package syntax: ```go -import ( - "fmt" - "github.com/leonelquinteros/gotext" -) - -func main() { - // Set PO content - str := ` -msgid "Translate this" -msgstr "Translated text" - -msgid "Another string" -msgstr "" - -msgid "One with var: %s" -msgstr "This one sets the var: %s" -` - - // Create Po object - po := gotext.NewPo() - po.Parse(str) - - fmt.Println(po.Get("Translate this")) -} +name := "John" +fmt.Println(gotext.Get("Hi, my name is %s", name)) ``` +### More advanced scenarios +- **Embedded translation files** (`embed.FS`) and named `%(name)s` formatting: see the [Advanced Usage Guide](docs/ADVANCED.md). +- **Languages fallback**: pass multiple languages to `Configure` as a colon-separated list (`gotext.Configure("/path/to/locales", "es_UY:es:en_US", "default")`). +- **Serialization/caching** of parsed locales: `Locale.MarshalBinary` / `UnmarshalBinary`. -## Use plural forms of translations - -PO format supports defining one or more plural forms for the same translation. -Relying on the PO file headers, a Plural-Forms formula can be set on the translation file -as defined in (https://www.gnu.org/savannah-checkouts/gnu/gettext/manual/html_node/Plural-forms.html) - -```go -import ( - "fmt" - "github.com/leonelquinteros/gotext" -) - -func main() { - // Set PO content - str := ` -msgid "" -msgstr "" - -# Header below -"Plural-Forms: nplurals=2; plural=(n != 1);\n" - -msgid "Translate this" -msgstr "Translated text" - -msgid "Another string" -msgstr "" - -msgid "One with var: %s" -msgid_plural "Several with vars: %s" -msgstr[0] "This one is the singular: %s" -msgstr[1] "This one is the plural: %s" -` - - // Create Po object - po := new(gotext.Po) - po.Parse(str) - - fmt.Println(po.GetN("One with var: %s", "Several with vars: %s", 54, v)) - // "This one is the plural: Variable" -} -``` - - -# Locales directories structure - -The package will assume a directories structure starting with a base path that will be provided to the package configuration -or to object constructors depending on the use, but either will use the same convention to lookup inside the base path. - -Inside the base directory where will be the language directories named using the language and country 2-letter codes (en_US, es_AR, ...). -All package functions can lookup after the simplified version for each language in case the full code isn't present but the more general language code exists. -So if the language set is `en_UK`, but there is no directory named after that code and there is a directory named `en`, -all package functions will be able to resolve this generalization and provide translations for the more general library. - -The language codes are assumed to be [ISO 639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) codes (2-letter codes). -That said, most functions will work with any coding standard as long the directory name matches the language code set on the configuration. - -Then, there can be a `LC_MESSAGES` containing all PO files or the PO files themselves. -A library directory structure can look like: +--- +## Locales directories structure +The package expects a standard Gettext directory structure: ``` /path/to/locales -/path/to/locales/en_US -/path/to/locales/en_US/LC_MESSAGES -/path/to/locales/en_US/LC_MESSAGES/default.po -/path/to/locales/en_US/LC_MESSAGES/extras.po -/path/to/locales/en_UK -/path/to/locales/en_UK/LC_MESSAGES -/path/to/locales/en_UK/LC_MESSAGES/default.po -/path/to/locales/en_UK/LC_MESSAGES/extras.po -/path/to/locales/en_AU -/path/to/locales/en_AU/LC_MESSAGES -/path/to/locales/en_AU/LC_MESSAGES/default.po -/path/to/locales/en_AU/LC_MESSAGES/extras.po -/path/to/locales/es -/path/to/locales/es/default.po -/path/to/locales/es/extras.po -/path/to/locales/es_ES -/path/to/locales/es_ES/default.po -/path/to/locales/es_ES/extras.po -/path/to/locales/fr -/path/to/locales/fr/default.po -/path/to/locales/fr/extras.po + /en_US + /LC_MESSAGES + default.po + /es_ES + /LC_MESSAGES + default.po ``` +Translation files can live either inside an `LC_MESSAGES` subdirectory or directly under the language code (`/es_ES/default.po`); both layouts are supported. -And so on... +It supports automatic language simplification (e.g., falling back from `en_UK` to `en`). +--- -# Contribute +## Contributing +We welcome contributions of all kinds! +- [Contributing Guidelines](CONTRIBUTING.md) +- [Code of Conduct](CODE_OF_CONDUCT.md) -- Please, contribute. -- Use the package on your projects. -- Report issues on Github. -- Send pull requests for bugfixes and improvements. -- Send proposals on Github issues. +--- +## License +`gotext` is released under the [MIT License](LICENSE). diff --git a/vendor/github.com/leonelquinteros/gotext/domain.go b/vendor/github.com/leonelquinteros/gotext/domain.go index 328b2555..779253a1 100644 --- a/vendor/github.com/leonelquinteros/gotext/domain.go +++ b/vendor/github.com/leonelquinteros/gotext/domain.go @@ -4,7 +4,7 @@ import ( "bytes" "encoding/gob" "fmt" - "regexp" + "maps" "sort" "strconv" "strings" @@ -64,9 +64,6 @@ func (m HeaderMap) Del(key string) { // Get value for key from HeaderMap func (m HeaderMap) Get(key string) string { - if m == nil { - return "" - } v := m[key] if len(v) == 0 { return "" @@ -81,9 +78,6 @@ func (m HeaderMap) Set(key, value string) { // Values returns all values for a given key from HeaderMap func (m HeaderMap) Values(key string) []string { - if m == nil { - return nil - } return m[key] } @@ -100,29 +94,111 @@ func NewDomain() *Domain { return domain } +func cloneRefs(refs []string) []string { + if refs == nil { + return nil + } + + owned := make([]string, len(refs)) + copy(owned, refs) + return owned +} + +func cloneHeaderMap(headers HeaderMap) HeaderMap { + if headers == nil { + return nil + } + + owned := make(HeaderMap, len(headers)) + for key, values := range headers { + owned[key] = cloneRefs(values) + } + return owned +} + +func cloneTranslation(trans *Translation) *Translation { + if trans == nil { + return nil + } + + owned := NewTranslation() + owned.ID = trans.ID + owned.PluralID = trans.PluralID + owned.dirty = trans.dirty + owned.Refs = cloneRefs(trans.Refs) + + maps.Copy(owned.Trs, trans.Trs) + + return owned +} + +func cloneTranslations(translations map[string]*Translation) map[string]*Translation { + if translations == nil { + return nil + } + + owned := make(map[string]*Translation, len(translations)) + for id, trans := range translations { + owned[id] = cloneTranslation(trans) + } + return owned +} + +func cloneContextTranslations(contexts map[string]map[string]*Translation) map[string]map[string]*Translation { + if contexts == nil { + return nil + } + + owned := make(map[string]map[string]*Translation, len(contexts)) + for context, translations := range contexts { + owned[context] = cloneTranslations(translations) + } + return owned +} + +func (do *Domain) hasTranslation(id string) bool { + do.trMutex.RLock() + defer do.trMutex.RUnlock() + + return do.translations[id] != nil +} + +func (do *Domain) hasContextTranslation(id, context string) bool { + do.trMutex.RLock() + defer do.trMutex.RUnlock() + + return do.contextTranslations[context][id] != nil +} + // SetPluralResolver sets a custom plural resolver function func (do *Domain) SetPluralResolver(f func(int) int) { + do.pluralMutex.Lock() + defer do.pluralMutex.Unlock() + do.customPluralResolver = f } func (do *Domain) pluralForm(n int) int { - // do we really need locking here? not sure how this plurals.Expression works, so sticking with it for now + // Snapshot plural state before calling an expression or custom resolver. do.pluralMutex.RLock() - defer do.pluralMutex.RUnlock() + pluralforms := do.pluralforms + customPluralResolver := do.customPluralResolver + do.pluralMutex.RUnlock() + + if pluralforms != nil { + return pluralforms.Eval(uint32(n)) + } // Failure fallback - if do.pluralforms == nil { - if do.customPluralResolver != nil { - return do.customPluralResolver(n) - } - - /* Use the Germanic plural rule. */ - if n == 1 { - return 0 - } - return 1 + if customPluralResolver != nil { + return customPluralResolver(n) } - return do.pluralforms.Eval(uint32(n)) + + /* Use the Germanic plural rule. */ + if n == 1 { + return 0 + } + return 1 } // parseHeaders retrieves data from previously parsed headers. it's called by both Mo and Po when parsing @@ -136,18 +212,16 @@ func (do *Domain) parseHeaders() { languageKey := "Language" pluralFormsKey := "Plural-Forms" - rawLines := strings.Split(raw, "\n") - for _, line := range rawLines { + for line := range strings.SplitSeq(raw, "\n") { if len(line) == 0 { continue } - colonIdx := strings.Index(line, ":") - if colonIdx < 0 { + key, value, ok := strings.Cut(line, ":") + if !ok { continue } - key := line[:colonIdx] lowerKey := strings.ToLower(key) if lowerKey == strings.ToLower(languageKey) { languageKey = key @@ -155,7 +229,7 @@ func (do *Domain) parseHeaders() { pluralFormsKey = key } - value := strings.TrimSpace(line[colonIdx+1:]) + value = strings.TrimSpace(value) do.Headers.Add(key, value) } @@ -168,22 +242,18 @@ func (do *Domain) parseHeaders() { return } - // Split plural form header value - pfs := strings.Split(do.PluralForms, ";") - - // Parse values - for _, i := range pfs { - vs := strings.SplitN(i, "=", 2) - if len(vs) != 2 { + for i := range strings.SplitSeq(do.PluralForms, ";") { + key, value, ok := strings.Cut(i, "=") + if !ok { continue } - switch strings.TrimSpace(vs[0]) { + switch strings.TrimSpace(key) { case "nplurals": - do.nplurals, _ = strconv.Atoi(vs[1]) + do.nplurals, _ = strconv.Atoi(value) case "plural": - do.plural = vs[1] + do.plural = value if expr, err := plurals.Compile(do.plural); err == nil { do.pluralforms = expr @@ -221,17 +291,19 @@ func (do *Domain) DropStaleTranslations() { // SetRefs set source references for a given translation func (do *Domain) SetRefs(str string, refs []string) { + ownedRefs := cloneRefs(refs) + do.trMutex.Lock() do.pluralMutex.Lock() defer do.trMutex.Unlock() defer do.pluralMutex.Unlock() - if trans, ok := do.translations[str]; ok { - trans.Refs = refs + if trans, ok := do.translations[str]; ok && trans != nil { + trans.SetRefs(ownedRefs) } else { - trans = NewTranslation() + trans := NewTranslation() trans.ID = str - trans.SetRefs(refs) + trans.SetRefs(ownedRefs) do.translations[str] = trans } } @@ -243,8 +315,8 @@ func (do *Domain) GetRefs(str string) []string { defer do.trMutex.RUnlock() if do.translations != nil { - if trans, ok := do.translations[str]; ok { - return trans.Refs + if trans, ok := do.translations[str]; ok && trans != nil { + return cloneRefs(trans.Refs) } } return nil @@ -268,7 +340,7 @@ func (do *Domain) Set(id, str string) { } // Get retrieves the Translation for the given string. -func (do *Domain) Get(str string, vars ...interface{}) string { +func (do *Domain) Get(str string, vars ...any) string { // Sync read do.trMutex.RLock() defer do.trMutex.RUnlock() @@ -284,8 +356,8 @@ func (do *Domain) Get(str string, vars ...interface{}) string { } // Append retrieves the Translation for the given string. -// Supports optional parameters (vars... interface{}) to be inserted on the formatted string using the fmt.Printf syntax. -func (do *Domain) Append(b []byte, str string, vars ...interface{}) []byte { +// Supports optional parameters (vars... any) to be inserted on the formatted string using the fmt.Printf syntax. +func (do *Domain) Append(b []byte, str string, vars ...any) []byte { // Sync read do.trMutex.RLock() defer do.trMutex.RUnlock() @@ -322,40 +394,48 @@ func (do *Domain) SetN(id, plural string, n int, str string) { } // GetN retrieves the (N)th plural form of Translation for the given string. -// Supports optional parameters (vars... interface{}) to be inserted on the formatted string using the fmt.Printf syntax. -func (do *Domain) GetN(str, plural string, n int, vars ...interface{}) string { +// Supports optional parameters (vars... any) to be inserted on the formatted string using the fmt.Printf syntax. +func (do *Domain) GetN(str, plural string, n int, vars ...any) string { + // Get plural form before acquiring the translation lock so a custom + // resolver is never called while a project lock is held. + pluralForm := do.pluralForm(n) + // Sync read do.trMutex.RLock() defer do.trMutex.RUnlock() if do.translations != nil { if _, ok := do.translations[str]; ok { - return FormatString(do.translations[str].GetN(do.pluralForm(n)), vars...) + return FormatString(do.translations[str].GetN(pluralForm), vars...) } } // Parse plural forms to distinguish between plural and singular - if do.pluralForm(n) == 0 { + if pluralForm == 0 { return FormatString(str, vars...) } return FormatString(plural, vars...) } // AppendN adds the (N)th plural form of Translation for the given string. -// Supports optional parameters (vars... interface{}) to be inserted on the formatted string using the fmt.Printf syntax. -func (do *Domain) AppendN(b []byte, str, plural string, n int, vars ...interface{}) []byte { +// Supports optional parameters (vars... any) to be inserted on the formatted string using the fmt.Printf syntax. +func (do *Domain) AppendN(b []byte, str, plural string, n int, vars ...any) []byte { + // Get plural form before acquiring the translation lock so a custom + // resolver is never called while a project lock is held. + pluralForm := do.pluralForm(n) + // Sync read do.trMutex.RLock() defer do.trMutex.RUnlock() if do.translations != nil { if _, ok := do.translations[str]; ok { - return Appendf(b, do.translations[str].GetN(do.pluralForm(n)), vars...) + return Appendf(b, do.translations[str].GetN(pluralForm), vars...) } } // Parse plural forms to distinguish between plural and singular - if do.pluralForm(n) == 0 { + if pluralForm == 0 { return Appendf(b, str, vars...) } return Appendf(b, plural, vars...) @@ -388,8 +468,8 @@ func (do *Domain) SetC(id, ctx, str string) { } // GetC retrieves the corresponding Translation for a given string in the given context. -// Supports optional parameters (vars... interface{}) to be inserted on the formatted string using the fmt.Printf syntax. -func (do *Domain) GetC(str, ctx string, vars ...interface{}) string { +// Supports optional parameters (vars... any) to be inserted on the formatted string using the fmt.Printf syntax. +func (do *Domain) GetC(str, ctx string, vars ...any) string { do.trMutex.RLock() defer do.trMutex.RUnlock() @@ -408,8 +488,8 @@ func (do *Domain) GetC(str, ctx string, vars ...interface{}) string { } // AppendC retrieves the corresponding Translation for a given string in the given context. -// Supports optional parameters (vars... interface{}) to be inserted on the formatted string using the fmt.Printf syntax. -func (do *Domain) AppendC(b []byte, str, ctx string, vars ...interface{}) []byte { +// Supports optional parameters (vars... any) to be inserted on the formatted string using the fmt.Printf syntax. +func (do *Domain) AppendC(b []byte, str, ctx string, vars ...any) []byte { do.trMutex.RLock() defer do.trMutex.RUnlock() @@ -443,12 +523,14 @@ func (do *Domain) SetNC(id, plural, ctx string, n int, str string) { } else { trans = NewTranslation() trans.ID = id + trans.PluralID = plural trans.SetN(pluralForm, str) context[id] = trans } } else { trans := NewTranslation() trans.ID = id + trans.PluralID = plural trans.SetN(pluralForm, str) do.contextTranslations[ctx] = map[string]*Translation{ id: trans, @@ -457,19 +539,22 @@ func (do *Domain) SetNC(id, plural, ctx string, n int, str string) { } // GetNC retrieves the (N)th plural form of Translation for the given string in the given context. -// Supports optional parameters (vars... interface{}) to be inserted on the formatted string using the fmt.Printf syntax. -func (do *Domain) GetNC(str, plural string, n int, ctx string, vars ...interface{}) string { - do.trMutex.RLock() - defer do.trMutex.RUnlock() +// Supports optional parameters (vars... any) to be inserted on the formatted string using the fmt.Printf syntax. +func (do *Domain) GetNC(str, plural string, n int, ctx string, vars ...any) string { + if do.hasContextTranslation(str, ctx) { + // Get plural form before acquiring the translation lock so a custom + // resolver is never called while a project lock is held. + pluralForm := do.pluralForm(n) - if do.contextTranslations != nil { - if _, ok := do.contextTranslations[ctx]; ok { - if do.contextTranslations[ctx] != nil { - if _, ok := do.contextTranslations[ctx][str]; ok { - return FormatString(do.contextTranslations[ctx][str].GetN(do.pluralForm(n)), vars...) - } + do.trMutex.RLock() + if translations, ok := do.contextTranslations[ctx]; ok && translations != nil { + if trans, ok := translations[str]; ok && trans != nil { + translated := trans.GetN(pluralForm) + do.trMutex.RUnlock() + return FormatString(translated, vars...) } } + do.trMutex.RUnlock() } if n == 1 { @@ -479,19 +564,22 @@ func (do *Domain) GetNC(str, plural string, n int, ctx string, vars ...interface } // AppendNC retrieves the (N)th plural form of Translation for the given string in the given context. -// Supports optional parameters (vars... interface{}) to be inserted on the formatted string using the fmt.Printf syntax. -func (do *Domain) AppendNC(b []byte, str, plural string, n int, ctx string, vars ...interface{}) []byte { - do.trMutex.RLock() - defer do.trMutex.RUnlock() +// Supports optional parameters (vars... any) to be inserted on the formatted string using the fmt.Printf syntax. +func (do *Domain) AppendNC(b []byte, str, plural string, n int, ctx string, vars ...any) []byte { + if do.hasContextTranslation(str, ctx) { + // Get plural form before acquiring the translation lock so a custom + // resolver is never called while a project lock is held. + pluralForm := do.pluralForm(n) - if do.contextTranslations != nil { - if _, ok := do.contextTranslations[ctx]; ok { - if do.contextTranslations[ctx] != nil { - if _, ok := do.contextTranslations[ctx][str]; ok { - return Appendf(b, do.contextTranslations[ctx][str].GetN(do.pluralForm(n)), vars...) - } + do.trMutex.RLock() + if translations, ok := do.contextTranslations[ctx]; ok && translations != nil { + if trans, ok := translations[str]; ok && trans != nil { + translated := trans.GetN(pluralForm) + do.trMutex.RUnlock() + return Appendf(b, translated, vars...) } } + do.trMutex.RUnlock() } if n == 1 { @@ -507,6 +595,14 @@ func (do *Domain) IsTranslated(str string) bool { // IsTranslatedN reports whether a plural string is translated func (do *Domain) IsTranslatedN(str string, n int) bool { + if !do.hasTranslation(str) { + return false + } + + // Get plural form before acquiring the translation lock so a custom + // resolver is never called while a project lock is held. + pluralForm := do.pluralForm(n) + do.trMutex.RLock() defer do.trMutex.RUnlock() @@ -514,10 +610,10 @@ func (do *Domain) IsTranslatedN(str string, n int) bool { return false } tr, ok := do.translations[str] - if !ok { + if !ok || tr == nil { return false } - return tr.IsTranslatedN(do.pluralForm(n)) + return tr.IsTranslatedN(pluralForm) } // IsTranslatedC reports whether a context string is translated @@ -527,6 +623,14 @@ func (do *Domain) IsTranslatedC(str, ctx string) bool { // IsTranslatedNC reports whether a plural context string is translated func (do *Domain) IsTranslatedNC(str string, n int, ctx string) bool { + if !do.hasContextTranslation(str, ctx) { + return false + } + + // Get plural form before acquiring the translation lock so a custom + // resolver is never called while a project lock is held. + pluralForm := do.pluralForm(n) + do.trMutex.RLock() defer do.trMutex.RUnlock() @@ -534,36 +638,24 @@ func (do *Domain) IsTranslatedNC(str string, n int, ctx string) bool { return false } translations, ok := do.contextTranslations[ctx] - if !ok { + if !ok || translations == nil { return false } tr, ok := translations[str] - if !ok { + if !ok || tr == nil { return false } - return tr.IsTranslatedN(do.pluralForm(n)) + return tr.IsTranslatedN(pluralForm) } // GetTranslations returns a copy of every translation in the domain. It does not support contexts. func (do *Domain) GetTranslations() map[string]*Translation { - all := make(map[string]*Translation, len(do.translations)) - do.trMutex.RLock() defer do.trMutex.RUnlock() + all := make(map[string]*Translation, len(do.translations)) for msgID, trans := range do.translations { - newTrans := NewTranslation() - newTrans.ID = trans.ID - newTrans.PluralID = trans.PluralID - newTrans.dirty = trans.dirty - if len(trans.Refs) > 0 { - newTrans.Refs = make([]string, len(trans.Refs)) - copy(newTrans.Refs, trans.Refs) - } - for k, v := range trans.Trs { - newTrans.Trs[k] = v - } - all[msgID] = newTrans + all[msgID] = cloneTranslation(trans) } return all @@ -571,32 +663,18 @@ func (do *Domain) GetTranslations() map[string]*Translation { // GetCtxTranslations returns a copy of every translation in the domain with context func (do *Domain) GetCtxTranslations() map[string]map[string]*Translation { - all := make(map[string]map[string]*Translation, len(do.contextTranslations)) - do.trMutex.RLock() defer do.trMutex.RUnlock() + all := make(map[string]map[string]*Translation, len(do.contextTranslations)) for ctx, translations := range do.contextTranslations { for msgID, trans := range translations { - newTrans := NewTranslation() - newTrans.ID = trans.ID - newTrans.PluralID = trans.PluralID - newTrans.dirty = trans.dirty - if len(trans.Refs) > 0 { - newTrans.Refs = make([]string, len(trans.Refs)) - copy(newTrans.Refs, trans.Refs) - } - for k, v := range trans.Trs { - newTrans.Trs[k] = v - } - if all[ctx] == nil { all[ctx] = make(map[string]*Translation) } - all[ctx][msgID] = newTrans + all[ctx][msgID] = cloneTranslation(trans) } - } return all @@ -604,10 +682,11 @@ func (do *Domain) GetCtxTranslations() map[string]map[string]*Translation { // SourceReference is a struct to hold source reference information type SourceReference struct { - path string - line int - context string - trans *Translation + path string + line int + context string + hasContext bool + trans *Translation } func extractPathAndLine(ref string) (string, int) { @@ -627,6 +706,11 @@ func extractPathAndLine(ref string) (string, int) { // MarshalText implements encoding.TextMarshaler interface // Assists round-trip of POT/PO content func (do *Domain) MarshalText() ([]byte, error) { + do.trMutex.RLock() + do.pluralMutex.RLock() + defer do.trMutex.RUnlock() + defer do.pluralMutex.RUnlock() + var buf bytes.Buffer if len(do.headerComments) > 0 { buf.WriteString(strings.Join(do.headerComments, "\n")) @@ -681,31 +765,29 @@ func (do *Domain) MarshalText() ([]byte, error) { v := do.Headers[k] for _, value := range v { - buf.WriteString("\n\"" + k + ": " + value + "\\n\"") + encoded := EscapeSpecialCharacters(k + ": " + value + "\n") + buf.WriteString("\n\"" + encoded + "\"") } } // Just as with headers, output translations in consistent order (to minimise diffs between round-trips), with (first) source reference taking priority, followed by context and finally ID references := make([]SourceReference, 0) for name, ctx := range do.contextTranslations { - for id, trans := range ctx { - if id == "" { - continue - } + for _, trans := range ctx { if len(trans.Refs) > 0 { path, line := extractPathAndLine(trans.Refs[0]) references = append(references, SourceReference{ - path, - line, - name, - trans, + path: path, + line: line, + context: name, + hasContext: true, + trans: trans, }) } else { references = append(references, SourceReference{ - "", - 0, - name, - trans, + context: name, + hasContext: true, + trans: trans, }) } } @@ -719,17 +801,13 @@ func (do *Domain) MarshalText() ([]byte, error) { if len(trans.Refs) > 0 { path, line := extractPathAndLine(trans.Refs[0]) references = append(references, SourceReference{ - path, - line, - "", - trans, + path: path, + line: line, + trans: trans, }) } else { references = append(references, SourceReference{ - "", - 0, - "", - trans, + trans: trans, }) } } @@ -754,6 +832,9 @@ func (do *Domain) MarshalText() ([]byte, error) { if references[i].context > references[j].context { return false } + if references[i].hasContext != references[j].hasContext { + return !references[i].hasContext + } return references[i].trans.ID < references[j].trans.ID }) @@ -765,7 +846,7 @@ func (do *Domain) MarshalText() ([]byte, error) { buf.WriteByte(byte('\n')) } - if ref.context == "" { + if !ref.hasContext { buf.WriteString("\nmsgid \"" + EscapeSpecialCharacters(trans.ID) + "\"") } else { buf.WriteString("\nmsgctxt \"" + EscapeSpecialCharacters(ref.context) + "\"\nmsgid \"" + EscapeSpecialCharacters(trans.ID) + "\"") @@ -774,9 +855,16 @@ func (do *Domain) MarshalText() ([]byte, error) { if trans.PluralID == "" { buf.WriteString("\nmsgstr \"" + EscapeSpecialCharacters(trans.Trs[0]) + "\"") } else { - buf.WriteString("\nmsgid_plural \"" + trans.PluralID + "\"") - for i, tr := range trans.Trs { - buf.WriteString("\nmsgstr[" + EscapeSpecialCharacters(strconv.Itoa(i)) + "] \"" + tr + "\"") + buf.WriteString("\nmsgid_plural \"" + EscapeSpecialCharacters(trans.PluralID) + "\"") + + pluralIndexes := make([]int, 0, len(trans.Trs)) + for i := range trans.Trs { + pluralIndexes = append(pluralIndexes, i) + } + sort.Ints(pluralIndexes) + + for _, i := range pluralIndexes { + buf.WriteString("\nmsgstr[" + strconv.Itoa(i) + "] \"" + EscapeSpecialCharacters(trans.Trs[i]) + "\"") } } } @@ -786,8 +874,23 @@ func (do *Domain) MarshalText() ([]byte, error) { // EscapeSpecialCharacters escapes special characters in a string func EscapeSpecialCharacters(s string) string { - s = regexp.MustCompile(`([^\\])(")`).ReplaceAllString(s, "$1\\\"") // Escape non-escaped double quotation marks + var escaped strings.Builder + escaped.Grow(len(s)) + for i := range len(s) { + switch s[i] { + case '\\': + escaped.WriteByte('\\') + escaped.WriteByte('\\') + case '"': + escaped.WriteByte('\\') + escaped.WriteByte('"') + default: + escaped.WriteByte(s[i]) + } + } + + s = escaped.String() if strings.Count(s, "\n") == 0 { return s } @@ -821,16 +924,26 @@ func EscapeSpecialCharacters(s string) string { return strings.Join(data, "\n") } +func (do *Domain) translatorEncodingSnapshot() *TranslatorEncoding { + do.trMutex.RLock() + do.pluralMutex.RLock() + defer do.trMutex.RUnlock() + defer do.pluralMutex.RUnlock() + + return &TranslatorEncoding{ + Headers: cloneHeaderMap(do.Headers), + Language: do.Language, + PluralForms: do.PluralForms, + Nplurals: do.nplurals, + Plural: do.plural, + Translations: cloneTranslations(do.translations), + Contexts: cloneContextTranslations(do.contextTranslations), + } +} + // MarshalBinary implements encoding.BinaryMarshaler interface func (do *Domain) MarshalBinary() ([]byte, error) { - obj := new(TranslatorEncoding) - obj.Headers = do.Headers - obj.Language = do.Language - obj.PluralForms = do.PluralForms - obj.Nplurals = do.nplurals - obj.Plural = do.plural - obj.Translations = do.translations - obj.Contexts = do.contextTranslations + obj := do.translatorEncodingSnapshot() var buff bytes.Buffer encoder := gob.NewEncoder(&buff) @@ -845,22 +958,68 @@ func (do *Domain) UnmarshalBinary(data []byte) error { obj := new(TranslatorEncoding) decoder := gob.NewDecoder(buff) - err := decoder.Decode(obj) - if err != nil { + if err := decoder.Decode(obj); err != nil { return err } - do.Headers = obj.Headers + headers := cloneHeaderMap(obj.Headers) + if headers == nil { + headers = make(HeaderMap) + } + translations := cloneTranslations(obj.Translations) + if translations == nil { + translations = make(map[string]*Translation) + } + contextTranslations := cloneContextTranslations(obj.Contexts) + if contextTranslations == nil { + contextTranslations = make(map[string]map[string]*Translation) + } + + for id, trans := range translations { + if trans == nil { + trans = NewTranslation() + trans.ID = id + translations[id] = trans + } + } + for context, translationsForContext := range contextTranslations { + if translationsForContext == nil { + contextTranslations[context] = make(map[string]*Translation) + continue + } + for id, trans := range translationsForContext { + if trans == nil { + trans = NewTranslation() + trans.ID = id + translationsForContext[id] = trans + } + } + } + + var pluralforms plurals.Expression + if expr, err := plurals.Compile(obj.Plural); err == nil { + pluralforms = expr + } + + do.trMutex.Lock() + do.pluralMutex.Lock() + + do.Headers = headers do.Language = obj.Language do.PluralForms = obj.PluralForms do.nplurals = obj.Nplurals do.plural = obj.Plural - do.translations = obj.Translations - do.contextTranslations = obj.Contexts + do.pluralforms = pluralforms + do.translations = translations + do.contextTranslations = contextTranslations + do.pluralTranslations = make(map[string]*Translation) + do.headerComments = make([]string, 0) + do.trBuffer = nil + do.ctxBuffer = "" + do.refBuffer = "" - if expr, err := plurals.Compile(do.plural); err == nil { - do.pluralforms = expr - } + do.pluralMutex.Unlock() + do.trMutex.Unlock() return nil } diff --git a/vendor/github.com/leonelquinteros/gotext/gotext.go b/vendor/github.com/leonelquinteros/gotext/gotext.go index b8cb30ce..2d464351 100644 --- a/vendor/github.com/leonelquinteros/gotext/gotext.go +++ b/vendor/github.com/leonelquinteros/gotext/gotext.go @@ -55,51 +55,59 @@ func init() { domain: "default", languages: []string{FallbackLocale}, library: "/usr/local/share/locale", - locales: nil, + locales: make([]*Locale, 0), } // Register Translator types for gob encoding gob.Register(TranslatorEncoding{}) } -// loadLocales creates a new Locale object for every language (specified using Configure) -// at package level based on the configuration of global configuration . -// It is called when trying to use Get or GetD methods. +// loadLocales ensures package-level locales and their configured domain are loaded. func loadLocales(rebuildCache bool) { globalConfig.Lock() if globalConfig.locales == nil || rebuildCache { - var locales []*Locale + locales := make([]*Locale, 0, len(globalConfig.languages)) for _, language := range globalConfig.languages { locales = append(locales, NewLocale(globalConfig.library, language)) } globalConfig.locales = locales } - for _, locale := range globalConfig.locales { - if _, ok := locale.Domains[globalConfig.domain]; !ok || rebuildCache { - locale.AddDomain(globalConfig.domain) - } - locale.SetDomain(globalConfig.domain) - } - + locales := append([]*Locale(nil), globalConfig.locales...) + domain := globalConfig.domain globalConfig.Unlock() + + for _, locale := range locales { + if locale == nil { + continue + } + if rebuildCache || !locale.hasDomain(domain) { + locale.AddDomain(domain) + } + locale.SetDomain(domain) + } } // GetDomain is the domain getter for the package configuration func GetDomain() string { - var dom string globalConfig.RLock() - if globalConfig.locales != nil { - // All locales have the same domain - dom = globalConfig.locales[0].GetDomain() - } - if dom == "" { - dom = globalConfig.domain + domain := globalConfig.domain + var locale *Locale + for _, candidate := range globalConfig.locales { + if candidate != nil { + locale = candidate + break + } } globalConfig.RUnlock() - return dom + if locale != nil { + if localeDomain := locale.GetDomain(); localeDomain != "" { + return localeDomain + } + } + return domain } // SetDomain sets the name for the domain to be used at package level. @@ -107,8 +115,8 @@ func GetDomain() string { func SetDomain(dom string) { globalConfig.Lock() globalConfig.domain = dom - if globalConfig.locales != nil { - for _, locale := range globalConfig.locales { + for _, locale := range globalConfig.locales { + if locale != nil { locale.SetDomain(dom) } } @@ -131,8 +139,10 @@ func GetLanguage() string { // GetLanguages returns all languages that have been supplied. func GetLanguages() []string { globalConfig.RLock() - defer globalConfig.RUnlock() - return globalConfig.languages + languages := make([]string, len(globalConfig.languages)) + copy(languages, globalConfig.languages) + globalConfig.RUnlock() + return languages } // SetLanguage sets the language code (or colon separated language codes) to be used at package level. @@ -140,7 +150,7 @@ func GetLanguages() []string { func SetLanguage(lang string) { globalConfig.Lock() var languages []string - for _, language := range strings.Split(lang, ":") { + for language := range strings.SplitSeq(lang, ":") { languages = append(languages, SimplifiedLocale(language)) } globalConfig.languages = languages @@ -171,17 +181,20 @@ func SetLibrary(lib string) { // GetLocales returns the locales that have been set for the package configuration. func GetLocales() []*Locale { globalConfig.RLock() - defer globalConfig.RUnlock() - return globalConfig.locales + locales := make([]*Locale, len(globalConfig.locales)) + copy(locales, globalConfig.locales) + globalConfig.RUnlock() + return locales } // GetStorage is the locale storage getter for the package configuration. // // Deprecated: Storage has been renamed to Locale for consistency, use GetLocales instead. func GetStorage() *Locale { - locales := GetLocales() - if len(locales) > 0 { - return locales[0] + for _, locale := range GetLocales() { + if locale != nil { + return locale + } } return nil } @@ -190,24 +203,40 @@ func GetStorage() *Locale { // NewLocale(). This makes it possible to attach custom Domain objects from in-memory po/mo. // The library, language and domain of the first Locale will set the default global configuration. func SetLocales(locales []*Locale) { + ownedLocales := make([]*Locale, len(locales)) + copy(ownedLocales, locales) + globalConfig.Lock() - defer globalConfig.Unlock() + globalConfig.locales = ownedLocales - globalConfig.locales = locales - globalConfig.library = locales[0].path - globalConfig.domain = locales[0].defaultDomain + languages := make([]string, 0, len(ownedLocales)) + var first *Locale + for _, locale := range ownedLocales { + if locale == nil { + continue + } - var languages []string - for _, locale := range locales { + locale.RLock() + if first == nil { + first = locale + globalConfig.library = locale.path + globalConfig.domain = locale.defaultDomain + } languages = append(languages, locale.lang) + locale.RUnlock() } globalConfig.languages = languages + globalConfig.Unlock() } // SetStorage allows overriding the global Locale object with one built manually with NewLocale(). // // Deprecated: Storage has been renamed to Locale for consistency, use SetLocales instead. func SetStorage(locale *Locale) { + if locale == nil { + SetLocales(nil) + return + } SetLocales([]*Locale{locale}) } @@ -219,7 +248,7 @@ func Configure(lib, lang, dom string) { globalConfig.Lock() globalConfig.library = lib var languages []string - for _, language := range strings.Split(lang, ":") { + for language := range strings.SplitSeq(lang, ":") { languages = append(languages, SimplifiedLocale(language)) } globalConfig.languages = languages @@ -230,114 +259,137 @@ func Configure(lib, lang, dom string) { } // Get uses the default domain globally set to return the corresponding Translation of a given string. -// Supports optional parameters (vars... interface{}) to be inserted on the formatted string using the fmt.Printf syntax. -func Get(str string, vars ...interface{}) string { +// Supports optional parameters (vars... any) to be inserted on the formatted string using the fmt.Printf syntax. +func Get(str string, vars ...any) string { return GetD(GetDomain(), str, vars...) } // GetN retrieves the (N)th plural form of Translation for the given string in the default domain. -// Supports optional parameters (vars... interface{}) to be inserted on the formatted string using the fmt.Printf syntax. -func GetN(str, plural string, n int, vars ...interface{}) string { +// Supports optional parameters (vars... any) to be inserted on the formatted string using the fmt.Printf syntax. +func GetN(str, plural string, n int, vars ...any) string { return GetND(GetDomain(), str, plural, n, vars...) } // GetD returns the corresponding Translation in the given domain for a given string. -// Supports optional parameters (vars... interface{}) to be inserted on the formatted string using the fmt.Printf syntax. -func GetD(dom, str string, vars ...interface{}) string { +// Supports optional parameters (vars... any) to be inserted on the formatted string using the fmt.Printf syntax. +func GetD(dom, str string, vars ...any) string { // Try to load default package Locales loadLocales(false) - globalConfig.RLock() - defer globalConfig.RUnlock() - - var tr string - for i, locale := range globalConfig.locales { - if _, ok := locale.Domains[dom]; !ok { - locale.AddDomain(dom) - } - if !locale.IsTranslatedD(dom, str) && i < (len(globalConfig.locales)-1) { + locales := GetLocales() + var fallback *Locale + for _, locale := range locales { + if locale == nil { continue } - tr = locale.GetD(dom, str, vars...) - break + fallback = locale + if !locale.hasDomain(dom) { + locale.AddDomain(dom) + } + if locale.IsTranslatedD(dom, str) { + return locale.GetD(dom, str, vars...) + } } - return tr + if fallback != nil { + return fallback.GetD(dom, str, vars...) + } + return FormatString(str, vars...) } // GetND retrieves the (N)th plural form of Translation in the given domain for a given string. -// Supports optional parameters (vars... interface{}) to be inserted on the formatted string using the fmt.Printf syntax. -func GetND(dom, str, plural string, n int, vars ...interface{}) string { +// Supports optional parameters (vars... any) to be inserted on the formatted string using the fmt.Printf syntax. +func GetND(dom, str, plural string, n int, vars ...any) string { // Try to load default package Locales loadLocales(false) - globalConfig.RLock() - defer globalConfig.RUnlock() - - var tr string - for i, locale := range globalConfig.locales { - if _, ok := locale.Domains[dom]; !ok { - locale.AddDomain(dom) - } - if !locale.IsTranslatedND(dom, str, n) && i < (len(globalConfig.locales)-1) { + locales := GetLocales() + var fallback *Locale + for _, locale := range locales { + if locale == nil { continue } - tr = locale.GetND(dom, str, plural, n, vars...) - break + fallback = locale + if !locale.hasDomain(dom) { + locale.AddDomain(dom) + } + if locale.IsTranslatedND(dom, str, n) { + return locale.GetND(dom, str, plural, n, vars...) + } } - return tr + if fallback != nil { + return fallback.GetND(dom, str, plural, n, vars...) + } + if n == 1 { + return FormatString(str, vars...) + } + return FormatString(plural, vars...) } // GetC uses the default domain globally set to return the corresponding Translation of the given string in the given context. -// Supports optional parameters (vars... interface{}) to be inserted on the formatted string using the fmt.Printf syntax. -func GetC(str, ctx string, vars ...interface{}) string { +// Supports optional parameters (vars... any) to be inserted on the formatted string using the fmt.Printf syntax. +func GetC(str, ctx string, vars ...any) string { return GetDC(GetDomain(), str, ctx, vars...) } // GetNC retrieves the (N)th plural form of Translation for the given string in the given context in the default domain. -// Supports optional parameters (vars... interface{}) to be inserted on the formatted string using the fmt.Printf syntax. -func GetNC(str, plural string, n int, ctx string, vars ...interface{}) string { +// Supports optional parameters (vars... any) to be inserted on the formatted string using the fmt.Printf syntax. +func GetNC(str, plural string, n int, ctx string, vars ...any) string { return GetNDC(GetDomain(), str, plural, n, ctx, vars...) } // GetDC returns the corresponding Translation in the given domain for the given string in the given context. -// Supports optional parameters (vars... interface{}) to be inserted on the formatted string using the fmt.Printf syntax. -func GetDC(dom, str, ctx string, vars ...interface{}) string { +// Supports optional parameters (vars... any) to be inserted on the formatted string using the fmt.Printf syntax. +func GetDC(dom, str, ctx string, vars ...any) string { // Try to load default package Locales loadLocales(false) - globalConfig.RLock() - defer globalConfig.RUnlock() - - var tr string - for i, locale := range globalConfig.locales { - if !locale.IsTranslatedDC(dom, str, ctx) && i < (len(globalConfig.locales)-1) { + locales := GetLocales() + var fallback *Locale + for _, locale := range locales { + if locale == nil { continue } - tr = locale.GetDC(dom, str, ctx, vars...) - break + fallback = locale + if !locale.hasDomain(dom) { + locale.AddDomain(dom) + } + if locale.IsTranslatedDC(dom, str, ctx) { + return locale.GetDC(dom, str, ctx, vars...) + } } - return tr + if fallback != nil { + return fallback.GetDC(dom, str, ctx, vars...) + } + return FormatString(str, vars...) } // GetNDC retrieves the (N)th plural form of Translation in the given domain for a given string. -// Supports optional parameters (vars... interface{}) to be inserted on the formatted string using the fmt.Printf syntax. -func GetNDC(dom, str, plural string, n int, ctx string, vars ...interface{}) string { +// Supports optional parameters (vars... any) to be inserted on the formatted string using the fmt.Printf syntax. +func GetNDC(dom, str, plural string, n int, ctx string, vars ...any) string { // Try to load default package Locales loadLocales(false) - // Return Translation - globalConfig.RLock() - defer globalConfig.RUnlock() - - var tr string - for i, locale := range globalConfig.locales { - if !locale.IsTranslatedNDC(dom, str, n, ctx) && i < (len(globalConfig.locales)-1) { + locales := GetLocales() + var fallback *Locale + for _, locale := range locales { + if locale == nil { continue } - tr = locale.GetNDC(dom, str, plural, n, ctx, vars...) - break + fallback = locale + if !locale.hasDomain(dom) { + locale.AddDomain(dom) + } + if locale.IsTranslatedNDC(dom, str, n, ctx) { + return locale.GetNDC(dom, str, plural, n, ctx, vars...) + } } - return tr + if fallback != nil { + return fallback.GetNDC(dom, str, plural, n, ctx, vars...) + } + if n == 1 { + return FormatString(str, vars...) + } + return FormatString(plural, vars...) } // IsTranslated reports whether a string is translated in given languages. @@ -366,18 +418,23 @@ func IsTranslatedND(dom, str string, n int, langs ...string) bool { } loadLocales(false) - - globalConfig.RLock() - defer globalConfig.RUnlock() - + locales := GetLocales() for _, lang := range langs { lang = SimplifiedLocale(lang) - for _, supportedLocale := range globalConfig.locales { - if lang != supportedLocale.GetActualLanguage(dom) { + for _, supportedLocale := range locales { + if supportedLocale == nil { continue } - return supportedLocale.IsTranslatedND(dom, str, n) + if lang != supportedLocale.GetLanguage() && lang != supportedLocale.GetActualLanguage(dom) { + continue + } + if !supportedLocale.hasDomain(dom) { + supportedLocale.AddDomain(dom) + } + if supportedLocale.IsTranslatedND(dom, str, n) { + return true + } } } return false @@ -398,7 +455,7 @@ func IsTranslatedNC(str string, n int, ctx string, langs ...string) bool { // IsTranslatedDC reports whether a domain context string is translated in given languages. // When the langs argument is omitted, the output of GetLanguages is used. func IsTranslatedDC(dom, str, ctx string, langs ...string) bool { - return IsTranslatedNDC(dom, str, 0, ctx, langs...) + return IsTranslatedNDC(dom, str, 1, ctx, langs...) } // IsTranslatedNDC reports whether a plural domain context string is translated in any of given languages. @@ -409,18 +466,23 @@ func IsTranslatedNDC(dom, str string, n int, ctx string, langs ...string) bool { } loadLocales(false) - - globalConfig.RLock() - defer globalConfig.RUnlock() - + locales := GetLocales() for _, lang := range langs { lang = SimplifiedLocale(lang) - for _, locale := range globalConfig.locales { - if lang != locale.GetActualLanguage(dom) { + for _, supportedLocale := range locales { + if supportedLocale == nil { continue } - return locale.IsTranslatedNDC(dom, str, n, ctx) + if lang != supportedLocale.GetLanguage() && lang != supportedLocale.GetActualLanguage(dom) { + continue + } + if !supportedLocale.hasDomain(dom) { + supportedLocale.AddDomain(dom) + } + if supportedLocale.IsTranslatedNDC(dom, str, n, ctx) { + return true + } } } return false diff --git a/vendor/github.com/leonelquinteros/gotext/helper.go b/vendor/github.com/leonelquinteros/gotext/helper.go index 91ea255d..1138d982 100644 --- a/vendor/github.com/leonelquinteros/gotext/helper.go +++ b/vendor/github.com/leonelquinteros/gotext/helper.go @@ -29,16 +29,21 @@ func SimplifiedLocale(lang string) string { } // FormatString applies text formatting only when needed to parse variables. -func FormatString(str string, vars ...interface{}) string { +func FormatString(str string, vars ...any) string { if len(vars) > 0 { - return fmt.Sprintf(str, vars...) + return FormatStringWithArgs(str, vars) } return str } +// FormatStringWithArgs applies text formatting as a proxy for fmt.Sprintf. +func FormatStringWithArgs(str string, vars []any) string { + return fmt.Sprintf(str, vars...) +} + // Appendf applies text formatting only when needed to parse variables. -func Appendf(b []byte, str string, vars ...interface{}) []byte { +func Appendf(b []byte, str string, vars ...any) []byte { if len(vars) > 0 { return fmt.Appendf(b, str, vars...) } @@ -47,50 +52,49 @@ func Appendf(b []byte, str string, vars ...interface{}) []byte { } // NPrintf support named format -// NPrintf("%(name)s is Type %(type)s", map[string]interface{}{"name": "Gotext", "type": "struct"}) -func NPrintf(format string, params map[string]interface{}) { +// NPrintf("%(name)s is Type %(type)s", map[string]any{"name": "Gotext", "type": "struct"}) +func NPrintf(format string, params map[string]any) { f, p := parseSprintf(format, params) fmt.Printf(f, p...) } // Sprintf support named format // -// Sprintf("%(name)s is Type %(type)s", map[string]interface{}{"name": "Gotext", "type": "struct"}) -func Sprintf(format string, params map[string]interface{}) string { +// Sprintf("%(name)s is Type %(type)s", map[string]any{"name": "Gotext", "type": "struct"}) +func Sprintf(format string, params map[string]any) string { f, p := parseSprintf(format, params) return fmt.Sprintf(f, p...) } -func parseSprintf(format string, params map[string]interface{}) (string, []interface{}) { +func parseSprintf(format string, params map[string]any) (string, []any) { f, n := reformatSprintf(format) - var p []interface{} - for _, v := range n { - p = append(p, params[v]) + var p []any + if len(n) > 0 { + p = make([]any, len(n)) + for i, v := range n { + p[i] = params[v] + } } return f, p } func reformatSprintf(f string) (string, []string) { - m := re.FindAllStringSubmatch(f, -1) - i := re.FindAllStringSubmatchIndex(f, -1) + matches := re.FindAllStringSubmatchIndex(f, -1) + ord := make([]string, 0, len(matches)) + pair := make([]int, 1, 1+2*len(matches)) + pair[0] = 0 - ord := []string{} - for _, v := range m { - ord = append(ord, v[1]) - } - - pair := []int{0} - for _, v := range i { - pair = append(pair, v[2]-1) - pair = append(pair, v[3]+1) + for _, match := range matches { + ord = append(ord, f[match[2]:match[3]]) + pair = append(pair, match[2]-1, match[3]+1) } pair = append(pair, len(f)) - plen := len(pair) - out := "" - for n := 0; n < plen; n += 2 { - out += f[pair[n]:pair[n+1]] + var out strings.Builder + out.Grow(len(f)) + for n := 0; n < len(pair); n += 2 { + out.WriteString(f[pair[n]:pair[n+1]]) } - return out, ord + return out.String(), ord } diff --git a/vendor/github.com/leonelquinteros/gotext/locale.go b/vendor/github.com/leonelquinteros/gotext/locale.go index 7295c313..7cced70d 100644 --- a/vendor/github.com/leonelquinteros/gotext/locale.go +++ b/vendor/github.com/leonelquinteros/gotext/locale.go @@ -9,6 +9,7 @@ import ( "bytes" "encoding/gob" "io/fs" + "maps" "os" "path" "sync" @@ -22,8 +23,6 @@ multiple languages at the same time by working with this object. Example: import ( - "encoding/gob" - "bytes" "fmt" "github.com/leonelquinteros/gotext" ) @@ -90,95 +89,114 @@ func NewLocaleFSWithPath(l string, filesystem fs.FS, p string) *Locale { return loc } -func (l *Locale) findExt(dom, ext string) string { - filename := path.Join(l.path, l.lang, "LC_MESSAGES", dom+"."+ext) - if l.fileExists(filename) { - return filename +func (l *Locale) snapshotPathLanguageFS() (string, string, fs.FS) { + if l == nil { + return "", "", nil } - if len(l.lang) > 2 { - filename = path.Join(l.path, l.lang[:2], "LC_MESSAGES", dom+"."+ext) - if l.fileExists(filename) { - return filename - } - } - - filename = path.Join(l.path, l.lang, dom+"."+ext) - if l.fileExists(filename) { - return filename - } - - if len(l.lang) > 2 { - filename = path.Join(l.path, l.lang[:2], dom+"."+ext) - if l.fileExists(filename) { - return filename - } - } - - return "" + l.RLock() + p := l.path + lang := l.lang + filesystem := l.fs + l.RUnlock() + return p, lang, filesystem } -// GetActualLanguage inspects the filesystem and decides whether to strip -// a CC part of the ll_CC locale string. -func (l *Locale) GetActualLanguage(dom string) string { - extensions := []string{"mo", "po"} - var fp string - for _, ext := range extensions { - // 'll' (or 'll_CC') exists, and it was specified as-is - fp = path.Join(l.path, l.lang, "LC_MESSAGES", dom+"."+ext) - if l.fileExists(fp) { - return l.lang - } - // 'll' exists, but 'll_CC' was specified - if len(l.lang) > 2 { - fp = path.Join(l.path, l.lang[:2], "LC_MESSAGES", dom+"."+ext) - if l.fileExists(fp) { - return l.lang[:2] - } - } - // 'll' (or 'll_CC') exists outside of LC_category, and it was specified as-is - fp = path.Join(l.path, l.lang, dom+"."+ext) - if l.fileExists(fp) { - return l.lang - } - // 'll' exists outside of LC_category, but 'll_CC' was specified - if len(l.lang) > 2 { - fp = path.Join(l.path, l.lang[:2], dom+"."+ext) - if l.fileExists(fp) { - return l.lang[:2] - } - } - } - return "" -} - -func (l *Locale) fileExists(filename string) bool { - if l.fs != nil { - _, err := fs.Stat(l.fs, filename) +func fileExistsIn(filesystem fs.FS, filename string) bool { + if filesystem != nil { + _, err := fs.Stat(filesystem, filename) return err == nil } _, err := os.Stat(filename) return err == nil } +func (l *Locale) findExtWithFS(dom, ext string) (fs.FS, string) { + p, lang, filesystem := l.snapshotPathLanguageFS() + + filename := path.Join(p, lang, "LC_MESSAGES", dom+"."+ext) + if fileExistsIn(filesystem, filename) { + return filesystem, filename + } + + if len(lang) > 2 { + filename = path.Join(p, lang[:2], "LC_MESSAGES", dom+"."+ext) + if fileExistsIn(filesystem, filename) { + return filesystem, filename + } + } + + filename = path.Join(p, lang, dom+"."+ext) + if fileExistsIn(filesystem, filename) { + return filesystem, filename + } + + if len(lang) > 2 { + filename = path.Join(p, lang[:2], dom+"."+ext) + if fileExistsIn(filesystem, filename) { + return filesystem, filename + } + } + + return filesystem, "" +} + +// GetActualLanguage inspects the filesystem and decides whether to strip +// a CC part of the ll_CC locale string. +func (l *Locale) GetActualLanguage(dom string) string { + p, lang, filesystem := l.snapshotPathLanguageFS() + extensions := []string{"po", "mo"} + var fp string + for _, ext := range extensions { + // 'll' (or 'll_CC') exists, and it was specified as-is + fp = path.Join(p, lang, "LC_MESSAGES", dom+"."+ext) + if fileExistsIn(filesystem, fp) { + return lang + } + // 'll' exists, but 'll_CC' was specified + if len(lang) > 2 { + fp = path.Join(p, lang[:2], "LC_MESSAGES", dom+"."+ext) + if fileExistsIn(filesystem, fp) { + return lang[:2] + } + } + // 'll' (or 'll_CC') exists outside of LC_category, and it was specified as-is + fp = path.Join(p, lang, dom+"."+ext) + if fileExistsIn(filesystem, fp) { + return lang + } + // 'll' exists outside of LC_category, but 'll_CC' was specified + if len(lang) > 2 { + fp = path.Join(p, lang[:2], dom+"."+ext) + if fileExistsIn(filesystem, fp) { + return lang[:2] + } + } + } + return "" +} + // AddDomain creates a new domain for a given locale object and initializes the Po object. // If the domain exists, it gets reloaded. func (l *Locale) AddDomain(dom string) { + if l == nil { + return + } + var poObj Translator - file := l.findExt(dom, "po") + filesystem, file := l.findExtWithFS(dom, "po") if file != "" { - poObj = NewPoFS(l.fs) + poObj = NewPoFS(filesystem) // Parse file. poObj.ParseFile(file) } else { - file = l.findExt(dom, "mo") + filesystem, file = l.findExtWithFS(dom, "mo") if file != "" { - poObj = NewMoFS(l.fs) + poObj = NewMoFS(filesystem) // Parse file. poObj.ParseFile(file) } else { - // fallback return if no file found with return } } @@ -212,9 +230,34 @@ func (l *Locale) AddTranslator(dom string, tr Translator) { l.Unlock() } +func (l *Locale) translatorFor(dom string) Translator { + if l == nil { + return nil + } + + l.RLock() + translator := l.Domains[dom] + l.RUnlock() + return translator +} + +func (l *Locale) hasDomain(dom string) bool { + if l == nil { + return false + } + + l.RLock() + _, ok := l.Domains[dom] + l.RUnlock() + return ok +} // GetDomain is the domain getter for Locale configuration func (l *Locale) GetDomain() string { + if l == nil { + return "" + } + l.RLock() dom := l.defaultDomain l.RUnlock() @@ -223,6 +266,10 @@ func (l *Locale) GetDomain() string { // SetDomain sets the name for the domain to be used. func (l *Locale) SetDomain(dom string) { + if l == nil { + return + } + l.Lock() l.defaultDomain = dom l.Unlock() @@ -230,6 +277,10 @@ func (l *Locale) SetDomain(dom string) { // GetLanguage is the lang getter for Locale configuration func (l *Locale) GetLanguage() string { + if l == nil { + return "" + } + l.RLock() lang := l.lang l.RUnlock() @@ -237,51 +288,37 @@ func (l *Locale) GetLanguage() string { } // Get uses a domain "default" to return the corresponding Translation of a given string. -// Supports optional parameters (vars... interface{}) to be inserted on the formatted string using the fmt.Printf syntax. -func (l *Locale) Get(str string, vars ...interface{}) string { +// Supports optional parameters (vars... any) to be inserted on the formatted string using the fmt.Printf syntax. +func (l *Locale) Get(str string, vars ...any) string { return l.GetD(l.GetDomain(), str, vars...) } // GetN retrieves the (N)th plural form of Translation for the given string in the "default" domain. -// Supports optional parameters (vars... interface{}) to be inserted on the formatted string using the fmt.Printf syntax. -func (l *Locale) GetN(str, plural string, n int, vars ...interface{}) string { +// Supports optional parameters (vars... any) to be inserted on the formatted string using the fmt.Printf syntax. +func (l *Locale) GetN(str, plural string, n int, vars ...any) string { return l.GetND(l.GetDomain(), str, plural, n, vars...) } // GetD returns the corresponding Translation in the given domain for the given string. -// Supports optional parameters (vars... interface{}) to be inserted on the formatted string using the fmt.Printf syntax. -func (l *Locale) GetD(dom, str string, vars ...interface{}) string { - // Sync read - l.RLock() - defer l.RUnlock() - - if l.Domains != nil { - if _, ok := l.Domains[dom]; ok { - if l.Domains[dom] != nil { - return l.Domains[dom].Get(str, vars...) - } - } +// Supports optional parameters (vars... any) to be inserted on the formatted string using the fmt.Printf syntax. +func (l *Locale) GetD(dom, str string, vars ...any) string { + translator := l.translatorFor(dom) + if translator != nil { + return translator.Get(str, vars...) } return FormatString(str, vars...) } // GetND retrieves the (N)th plural form of Translation in the given domain for the given string. -// Supports optional parameters (vars... interface{}) to be inserted on the formatted string using the fmt.Printf syntax. -func (l *Locale) GetND(dom, str, plural string, n int, vars ...interface{}) string { - // Sync read - l.RLock() - defer l.RUnlock() - - if l.Domains != nil { - if _, ok := l.Domains[dom]; ok { - if l.Domains[dom] != nil { - return l.Domains[dom].GetN(str, plural, n, vars...) - } - } +// Supports optional parameters (vars... any) to be inserted on the formatted string using the fmt.Printf syntax. +func (l *Locale) GetND(dom, str, plural string, n int, vars ...any) string { + translator := l.translatorFor(dom) + if translator != nil { + return translator.GetN(str, plural, n, vars...) } - // Use western default rule (plural > 1) to handle missing domain default result. + // Use western default rule (n != 1) to handle missing domain default result. if n == 1 { return FormatString(str, vars...) } @@ -289,51 +326,37 @@ func (l *Locale) GetND(dom, str, plural string, n int, vars ...interface{}) stri } // GetC uses a domain "default" to return the corresponding Translation of the given string in the given context. -// Supports optional parameters (vars... interface{}) to be inserted on the formatted string using the fmt.Printf syntax. -func (l *Locale) GetC(str, ctx string, vars ...interface{}) string { +// Supports optional parameters (vars... any) to be inserted on the formatted string using the fmt.Printf syntax. +func (l *Locale) GetC(str, ctx string, vars ...any) string { return l.GetDC(l.GetDomain(), str, ctx, vars...) } // GetNC retrieves the (N)th plural form of Translation for the given string in the given context in the "default" domain. -// Supports optional parameters (vars... interface{}) to be inserted on the formatted string using the fmt.Printf syntax. -func (l *Locale) GetNC(str, plural string, n int, ctx string, vars ...interface{}) string { +// Supports optional parameters (vars... any) to be inserted on the formatted string using the fmt.Printf syntax. +func (l *Locale) GetNC(str, plural string, n int, ctx string, vars ...any) string { return l.GetNDC(l.GetDomain(), str, plural, n, ctx, vars...) } // GetDC returns the corresponding Translation in the given domain for the given string in the given context. -// Supports optional parameters (vars... interface{}) to be inserted on the formatted string using the fmt.Printf syntax. -func (l *Locale) GetDC(dom, str, ctx string, vars ...interface{}) string { - // Sync read - l.RLock() - defer l.RUnlock() - - if l.Domains != nil { - if _, ok := l.Domains[dom]; ok { - if l.Domains[dom] != nil { - return l.Domains[dom].GetC(str, ctx, vars...) - } - } +// Supports optional parameters (vars... any) to be inserted on the formatted string using the fmt.Printf syntax. +func (l *Locale) GetDC(dom, str, ctx string, vars ...any) string { + translator := l.translatorFor(dom) + if translator != nil { + return translator.GetC(str, ctx, vars...) } return FormatString(str, vars...) } // GetNDC retrieves the (N)th plural form of Translation in the given domain for the given string in the given context. -// Supports optional parameters (vars... interface{}) to be inserted on the formatted string using the fmt.Printf syntax. -func (l *Locale) GetNDC(dom, str, plural string, n int, ctx string, vars ...interface{}) string { - // Sync read - l.RLock() - defer l.RUnlock() - - if l.Domains != nil { - if _, ok := l.Domains[dom]; ok { - if l.Domains[dom] != nil { - return l.Domains[dom].GetNC(str, plural, n, ctx, vars...) - } - } +// Supports optional parameters (vars... any) to be inserted on the formatted string using the fmt.Printf syntax. +func (l *Locale) GetNDC(dom, str, plural string, n int, ctx string, vars ...any) string { + translator := l.translatorFor(dom) + if translator != nil { + return translator.GetNC(str, plural, n, ctx, vars...) } - // Use western default rule (plural > 1) to handle missing domain default result. + // Use western default rule (n != 1) to handle missing domain default result. if n == 1 { return FormatString(str, vars...) } @@ -343,13 +366,26 @@ func (l *Locale) GetNDC(dom, str, plural string, n int, ctx string, vars ...inte // GetTranslations returns a copy of all translations in all domains of this locale. It does not support contexts. func (l *Locale) GetTranslations() map[string]*Translation { all := make(map[string]*Translation) + if l == nil { + return all + } l.RLock() - defer l.RUnlock() + translators := make([]Translator, 0, len(l.Domains)) for _, translator := range l.Domains { - for msgID, trans := range translator.GetDomain().GetTranslations() { - all[msgID] = trans + translators = append(translators, translator) + } + l.RUnlock() + + for _, translator := range translators { + if translator == nil { + continue } + domain := translator.GetDomain() + if domain == nil { + continue + } + maps.Copy(all, domain.GetTranslations()) } return all @@ -357,7 +393,7 @@ func (l *Locale) GetTranslations() map[string]*Translation { // IsTranslated reports whether a string is translated func (l *Locale) IsTranslated(str string) bool { - return l.IsTranslatedND(l.GetDomain(), str, 0) + return l.IsTranslatedND(l.GetDomain(), str, 1) } // IsTranslatedN reports whether a plural string is translated @@ -367,27 +403,25 @@ func (l *Locale) IsTranslatedN(str string, n int) bool { // IsTranslatedD reports whether a domain string is translated func (l *Locale) IsTranslatedD(dom, str string) bool { - return l.IsTranslatedND(dom, str, 0) + return l.IsTranslatedND(dom, str, 1) } // IsTranslatedND reports whether a plural domain string is translated func (l *Locale) IsTranslatedND(dom, str string, n int) bool { - l.RLock() - defer l.RUnlock() - - if l.Domains == nil { + translator := l.translatorFor(dom) + if translator == nil { return false } - translator, ok := l.Domains[dom] - if !ok { + domain := translator.GetDomain() + if domain == nil { return false } - return translator.GetDomain().IsTranslatedN(str, n) + return domain.IsTranslatedN(str, n) } // IsTranslatedC reports whether a context string is translated func (l *Locale) IsTranslatedC(str, ctx string) bool { - return l.IsTranslatedNDC(l.GetDomain(), str, 0, ctx) + return l.IsTranslatedNDC(l.GetDomain(), str, 1, ctx) } // IsTranslatedNC reports whether a plural context string is translated @@ -397,22 +431,20 @@ func (l *Locale) IsTranslatedNC(str string, n int, ctx string) bool { // IsTranslatedDC reports whether a domain context string is translated func (l *Locale) IsTranslatedDC(dom, str, ctx string) bool { - return l.IsTranslatedNDC(dom, str, 0, ctx) + return l.IsTranslatedNDC(dom, str, 1, ctx) } // IsTranslatedNDC reports whether a plural domain context string is translated func (l *Locale) IsTranslatedNDC(dom string, str string, n int, ctx string) bool { - l.RLock() - defer l.RUnlock() - - if l.Domains == nil { + translator := l.translatorFor(dom) + if translator == nil { return false } - translator, ok := l.Domains[dom] - if !ok { + domain := translator.GetDomain() + if domain == nil { return false } - return translator.GetDomain().IsTranslatedNC(str, n, ctx) + return domain.IsTranslatedNC(str, n, ctx) } // LocaleEncoding is used as intermediary storage to encode Locale objects to Gob. @@ -425,18 +457,35 @@ type LocaleEncoding struct { // MarshalBinary implements encoding BinaryMarshaler interface func (l *Locale) MarshalBinary() ([]byte, error) { - obj := new(LocaleEncoding) - obj.DefaultDomain = l.defaultDomain - obj.Domains = make(map[string][]byte) - for k, v := range l.Domains { - var err error - obj.Domains[k], err = v.MarshalBinary() + var localePath, lang, defaultDomain string + domains := make(map[string]Translator) + if l != nil { + l.RLock() + localePath = l.path + lang = l.lang + defaultDomain = l.defaultDomain + domains = make(map[string]Translator, len(l.Domains)) + for name, translator := range l.Domains { + if translator != nil { + domains[name] = translator + } + } + l.RUnlock() + } + + obj := &LocaleEncoding{ + Path: localePath, + Lang: lang, + Domains: make(map[string][]byte, len(domains)), + DefaultDomain: defaultDomain, + } + for name, translator := range domains { + data, err := translator.MarshalBinary() if err != nil { return nil, err } + obj.Domains[name] = data } - obj.Lang = l.lang - obj.Path = l.path var buff bytes.Buffer encoder := gob.NewEncoder(&buff) @@ -445,7 +494,7 @@ func (l *Locale) MarshalBinary() ([]byte, error) { return buff.Bytes(), err } -// UnmarshalBinary implements encoding BinaryUnmarshaler interface +// UnmarshalBinary implements encoding.BinaryUnmarshaler interface func (l *Locale) UnmarshalBinary(data []byte) error { buff := bytes.NewBuffer(data) obj := new(LocaleEncoding) @@ -456,12 +505,7 @@ func (l *Locale) UnmarshalBinary(data []byte) error { return err } - l.defaultDomain = obj.DefaultDomain - l.lang = obj.Lang - l.path = obj.Path - - // Decode Domains - l.Domains = make(map[string]Translator) + domains := make(map[string]Translator, len(obj.Domains)) for k, v := range obj.Domains { var tr TranslatorEncoding buff := bytes.NewBuffer(v) @@ -471,8 +515,15 @@ func (l *Locale) UnmarshalBinary(data []byte) error { return err } - l.Domains[k] = tr.GetTranslator() + domains[k] = tr.GetTranslator() } + l.Lock() + l.defaultDomain = obj.DefaultDomain + l.lang = obj.Lang + l.path = obj.Path + l.Domains = domains + l.Unlock() + return nil } diff --git a/vendor/github.com/leonelquinteros/gotext/mkdocs.yml b/vendor/github.com/leonelquinteros/gotext/mkdocs.yml new file mode 100644 index 00000000..01069a13 --- /dev/null +++ b/vendor/github.com/leonelquinteros/gotext/mkdocs.yml @@ -0,0 +1,40 @@ +site_name: gotext +site_description: GNU gettext utilities for Go +site_author: Leonel Quinteros +repo_url: https://github.com/leonelquinteros/gotext +edit_uri: edit/master/docs/ + +theme: + name: material + palette: + primary: indigo + accent: indigo + features: + - navigation.tabs + - navigation.sections + - navigation.top + - search.suggest + - search.highlight + +nav: + - Home: index.md + - Getting Started: GETTING_STARTED.md + - User Guides: + - Plural Forms: PLURALS.md + - xgotext CLI: xgotext.md + - Character Encoding: ENCODING.md + - Advanced Usage: ADVANCED.md + - Best Practices: BEST_PRACTICES.md + - API Reference: https://pkg.go.dev/github.com/leonelquinteros/gotext + - Contributing: + - Guidelines: CONTRIBUTING.md + - Code of Conduct: CODE_OF_CONDUCT.md + - License: LICENSE.md + +markdown_extensions: + - admonition + - pymdownx.highlight: + anchor_linenums: true + - pymdownx.inlinehilite + - pymdownx.snippets + - pymdownx.superfences diff --git a/vendor/github.com/leonelquinteros/gotext/mo.go b/vendor/github.com/leonelquinteros/gotext/mo.go index 389deba6..f285b9cb 100644 --- a/vendor/github.com/leonelquinteros/gotext/mo.go +++ b/vendor/github.com/leonelquinteros/gotext/mo.go @@ -76,42 +76,42 @@ func (mo *Mo) GetDomain() *Domain { } // Get returns the translation for the given string -func (mo *Mo) Get(str string, vars ...interface{}) string { +func (mo *Mo) Get(str string, vars ...any) string { return mo.domain.Get(str, vars...) } // Append a translation string into the domain -func (mo *Mo) Append(b []byte, str string, vars ...interface{}) []byte { +func (mo *Mo) Append(b []byte, str string, vars ...any) []byte { return mo.domain.Append(b, str, vars...) } // GetN returns the translation for the given string and plural form -func (mo *Mo) GetN(str, plural string, n int, vars ...interface{}) string { +func (mo *Mo) GetN(str, plural string, n int, vars ...any) string { return mo.domain.GetN(str, plural, n, vars...) } // AppendN appends a translation string for the given plural form into the domain -func (mo *Mo) AppendN(b []byte, str, plural string, n int, vars ...interface{}) []byte { +func (mo *Mo) AppendN(b []byte, str, plural string, n int, vars ...any) []byte { return mo.domain.AppendN(b, str, plural, n, vars...) } // GetC returns the translation for the given string and context -func (mo *Mo) GetC(str, ctx string, vars ...interface{}) string { +func (mo *Mo) GetC(str, ctx string, vars ...any) string { return mo.domain.GetC(str, ctx, vars...) } // AppendC appends a translation string for the given context into the domain -func (mo *Mo) AppendC(b []byte, str, ctx string, vars ...interface{}) []byte { +func (mo *Mo) AppendC(b []byte, str, ctx string, vars ...any) []byte { return mo.domain.AppendC(b, str, ctx, vars...) } // GetNC returns the translation for the given string, plural form and context -func (mo *Mo) GetNC(str, plural string, n int, ctx string, vars ...interface{}) string { +func (mo *Mo) GetNC(str, plural string, n int, ctx string, vars ...any) string { return mo.domain.GetNC(str, plural, n, ctx, vars...) } // AppendNC appends a translation string for the given plural form and context into the domain -func (mo *Mo) AppendNC(b []byte, str, plural string, n int, ctx string, vars ...interface{}) []byte { +func (mo *Mo) AppendNC(b []byte, str, plural string, n int, ctx string, vars ...any) []byte { return mo.domain.AppendNC(b, str, plural, n, ctx, vars...) } @@ -163,22 +163,18 @@ func (mo *Mo) Parse(buf []byte) { defer mo.domain.trMutex.Unlock() defer mo.domain.pluralMutex.Unlock() - r := bytes.NewReader(buf) - - var magicNumber uint32 - if err := binary.Read(r, binary.LittleEndian, &magicNumber); err != nil { + if len(buf) < 28 { return - // return fmt.Errorf("gettext: %v", err) } + var bo binary.ByteOrder - switch magicNumber { + switch binary.LittleEndian.Uint32(buf[:4]) { case MoMagicLittleEndian: bo = binary.LittleEndian case MoMagicBigEndian: bo = binary.BigEndian default: return - // return fmt.Errorf("gettext: %v", "invalid magic number") } var header struct { @@ -187,82 +183,58 @@ func (mo *Mo) Parse(buf []byte) { MsgIDCount uint32 MsgIDOffset uint32 MsgStrOffset uint32 - HashSize uint32 - HashOffset uint32 - } - if err := binary.Read(r, bo, &header); err != nil { - return - // return fmt.Errorf("gettext: %v", err) - } - if v := header.MajorVersion; v != 0 && v != 1 { - return - // return fmt.Errorf("gettext: %v", "invalid version number") - } - if v := header.MinorVersion; v != 0 && v != 1 { - return - // return fmt.Errorf("gettext: %v", "invalid version number") } - msgIDStart := make([]uint32, header.MsgIDCount) - msgIDLen := make([]uint32, header.MsgIDCount) - if _, err := r.Seek(int64(header.MsgIDOffset), 0); err != nil { + bufLen := uint64(len(buf)) + header.MajorVersion = bo.Uint16(buf[4:6]) + header.MinorVersion = bo.Uint16(buf[6:8]) + header.MsgIDCount = bo.Uint32(buf[8:12]) + header.MsgIDOffset = bo.Uint32(buf[12:16]) + header.MsgStrOffset = bo.Uint32(buf[16:20]) + + if header.MajorVersion != 0 && header.MajorVersion != 1 { return - // return fmt.Errorf("gettext: %v", err) } - for i := 0; i < int(header.MsgIDCount); i++ { - if err := binary.Read(r, bo, &msgIDLen[i]); err != nil { + if header.MinorVersion != 0 && header.MinorVersion != 1 { + return + } + + directorySize := uint64(header.MsgIDCount) * 8 + if uint64(header.MsgIDOffset)+directorySize > bufLen || + uint64(header.MsgStrOffset)+directorySize > bufLen { + return + } + + for i := uint32(0); i < header.MsgIDCount; i++ { + msgIDEntry := int(uint64(header.MsgIDOffset) + uint64(i)*8) + msgIDLen := bo.Uint32(buf[msgIDEntry:]) + msgIDStart := bo.Uint32(buf[msgIDEntry+4:]) + if uint64(msgIDStart) > bufLen || + uint64(msgIDLen) > bufLen-uint64(msgIDStart) { return - // return fmt.Errorf("gettext: %v", err) } - if err := binary.Read(r, bo, &msgIDStart[i]); err != nil { + + msgStrEntry := int(uint64(header.MsgStrOffset) + uint64(i)*8) + msgStrLen := bo.Uint32(buf[msgStrEntry:]) + msgStrStart := bo.Uint32(buf[msgStrEntry+4:]) + if uint64(msgStrStart) > bufLen || + uint64(msgStrLen) > bufLen-uint64(msgStrStart) { return - // return fmt.Errorf("gettext: %v", err) } } - msgStrStart := make([]int32, header.MsgIDCount) - msgStrLen := make([]int32, header.MsgIDCount) - if _, err := r.Seek(int64(header.MsgStrOffset), 0); err != nil { - return - // return fmt.Errorf("gettext: %v", err) - } - for i := 0; i < int(header.MsgIDCount); i++ { - if err := binary.Read(r, bo, &msgStrLen[i]); err != nil { - return - // return fmt.Errorf("gettext: %v", err) - } - if err := binary.Read(r, bo, &msgStrStart[i]); err != nil { - return - // return fmt.Errorf("gettext: %v", err) - } - } + for i := uint32(0); i < header.MsgIDCount; i++ { + msgIDEntry := int(uint64(header.MsgIDOffset) + uint64(i)*8) + msgIDLen := bo.Uint32(buf[msgIDEntry:]) + msgIDStart := bo.Uint32(buf[msgIDEntry+4:]) + msgIDEnd := int(uint64(msgIDStart) + uint64(msgIDLen)) - for i := 0; i < int(header.MsgIDCount); i++ { - if _, err := r.Seek(int64(msgIDStart[i]), 0); err != nil { - return - // return fmt.Errorf("gettext: %v", err) - } - msgIDData := make([]byte, msgIDLen[i]) - if _, err := r.Read(msgIDData); err != nil { - return - // return fmt.Errorf("gettext: %v", err) - } + msgStrEntry := int(uint64(header.MsgStrOffset) + uint64(i)*8) + msgStrLen := bo.Uint32(buf[msgStrEntry:]) + msgStrStart := bo.Uint32(buf[msgStrEntry+4:]) + msgStrEnd := int(uint64(msgStrStart) + uint64(msgStrLen)) - if _, err := r.Seek(int64(msgStrStart[i]), 0); err != nil { - return - // return fmt.Errorf("gettext: %v", err) - } - msgStrData := make([]byte, msgStrLen[i]) - if _, err := r.Read(msgStrData); err != nil { - return - // return fmt.Errorf("gettext: %v", err) - } - - if len(msgIDData) == 0 { - mo.addTranslation(msgIDData, msgStrData) - } else { - mo.addTranslation(msgIDData, msgStrData) - } + mo.addTranslation(buf[int(msgIDStart):msgIDEnd], buf[int(msgStrStart):msgStrEnd]) } // Parse headers @@ -279,36 +251,32 @@ func (mo *Mo) addTranslation(msgid, msgstr []byte) { translation := NewTranslation() var msgctxt []byte var msgidPlural []byte + hasContext := false - d := bytes.Split(msgid, []byte(EotSeparator)) - if len(d) == 1 { - msgid = d[0] - } else { - msgid, msgctxt = d[1], d[0] + if eotIndex := bytes.IndexByte(msgid, EotSeparator[0]); eotIndex >= 0 { + hasContext = true + msgctxt = msgid[:eotIndex] + msgid = msgid[eotIndex+len(EotSeparator):] } - dd := bytes.Split(msgid, []byte(NulSeparator)) - if len(dd) > 1 { - msgid = dd[0] - dd = dd[1:] + if nulIndex := bytes.IndexByte(msgid, NulSeparator[0]); nulIndex >= 0 { + msgidPlural = msgid[nulIndex+1:] + msgid = msgid[:nulIndex] } - translation.ID = string(msgid) - msgidPlural = bytes.Join(dd, []byte(NulSeparator)) if len(msgidPlural) > 0 { translation.PluralID = string(msgidPlural) } - ddd := bytes.Split(msgstr, []byte(NulSeparator)) - if len(ddd) > 0 { - for i, s := range ddd { - translation.Trs[i] = string(s) - } + i := 0 + for s := range bytes.SplitSeq(msgstr, []byte(NulSeparator)) { + translation.Trs[i] = string(s) + i++ } - if len(msgctxt) > 0 { - // With context... + if hasContext { + // With context, including an explicitly empty context... if _, ok := mo.domain.contextTranslations[string(msgctxt)]; !ok { mo.domain.contextTranslations[string(msgctxt)] = make(map[string]*Translation) } diff --git a/vendor/github.com/leonelquinteros/gotext/plurals/compiler.go b/vendor/github.com/leonelquinteros/gotext/plurals/compiler.go index 01cd067a..4cde9390 100644 --- a/vendor/github.com/leonelquinteros/gotext/plurals/compiler.go +++ b/vendor/github.com/leonelquinteros/gotext/plurals/compiler.go @@ -12,18 +12,12 @@ package plurals import ( "errors" "fmt" - "regexp" + "slices" "strconv" "strings" + "unicode" ) -type match struct { - openPos int - closePos int -} - -var pat = regexp.MustCompile(`(\?|:|\|\||&&|==|!=|>=|>|<=|<|%|\d+|n)`) - type testToken interface { compile(tokens []string) (test test, err error) } @@ -31,30 +25,30 @@ type testToken interface { type cmpTestBuilder func(val uint32, flipped bool) test type logicTestBuild func(left test, right test) test -var ternaryToken ternaryStruct +func compileTernary(tokens []string, depth int) (expr Expression, err error) { + if depth > maxParseDepth { + return nil, errors.New("expression nesting is too deep") + } -type ternaryStruct struct{} - -func (ternaryStruct) compile(tokens []string) (expr Expression, err error) { main, err := splitTokens(tokens, "?") if err != nil { - return expr, err + return nil, err } - test, err := compileTest(strings.Join(main.Left, "")) + test, err := compileTestDepth(strings.Join(main.Left, ""), depth+1) if err != nil { - return expr, err + return nil, err } - actions, err := splitTokens(main.Right, ":") + actions, err := splitTernaryTokens(main.Right) if err != nil { - return expr, err + return nil, err } - trueAction, err := compileExpression(strings.Join(actions.Left, "")) + trueAction, err := compileExpressionDepth(strings.Join(actions.Left, ""), depth+1) if err != nil { - return expr, err + return nil, err } - falseAction, err := compileExpression(strings.Join(actions.Right, "")) + falseAction, err := compileExpressionDepth(strings.Join(actions.Right, ""), depth+1) if err != nil { - return expr, nil + return nil, err } return ternary{ test: test, @@ -63,93 +57,145 @@ func (ternaryStruct) compile(tokens []string) (expr Expression, err error) { }, nil } +const maxParseDepth = 1024 + +func splitTernaryTokens(tokens []string) (s splitted, err error) { + depth := 0 + for index, token := range tokens { + switch token { + case "?": + depth++ + case ":": + if depth == 0 { + return splitted{ + Left: tokens[:index], + Right: tokens[index+1:], + }, nil + } + depth-- + } + } + return s, errors.New("':' not found for ternary expression") +} + var constToken constValStruct type constValStruct struct{} func (constValStruct) compile(tokens []string) (expr Expression, err error) { if len(tokens) == 0 { - return expr, errors.New("got nothing instead of constant") + return nil, errors.New("got nothing instead of constant") } if len(tokens) != 1 { - return expr, fmt.Errorf("invalid constant: %s", strings.Join(tokens, "")) + return nil, fmt.Errorf("invalid constant: %s", strings.Join(tokens, "")) } i, err := strconv.Atoi(tokens[0]) if err != nil { - return expr, err + return nil, err } return constValue{value: i}, nil } -func compileLogicTest(tokens []string, sep string, builder logicTestBuild) (test test, err error) { +func compileLogicTestDepth(tokens []string, sep string, builder logicTestBuild, depth int) (test test, err error) { + if depth > maxParseDepth { + return nil, errors.New("expression nesting is too deep") + } split, err := splitTokens(tokens, sep) if err != nil { - return test, err + return nil, err } - left, err := compileTest(strings.Join(split.Left, "")) + left, err := compileTestDepth(strings.Join(split.Left, ""), depth+1) if err != nil { - return test, err + return nil, err } - right, err := compileTest(strings.Join(split.Right, "")) + right, err := compileTestDepth(strings.Join(split.Right, ""), depth+1) if err != nil { - return test, err + return nil, err } return builder(left, right), nil } -var orToken orStruct - -type orStruct struct{} - -func (orStruct) compile(tokens []string) (test test, err error) { - return compileLogicTest(tokens, "||", buildOr) -} func buildOr(left test, right test) test { return or{left: left, right: right} } -var andToken andStruct - -type andStruct struct{} - -func (andStruct) compile(tokens []string) (test test, err error) { - return compileLogicTest(tokens, "&&", buildAnd) -} func buildAnd(left test, right test) test { return and{left: left, right: right} } +func unwrapSingleGroup(tokens []string) ([]string, error) { + for len(tokens) == 1 && strings.HasPrefix(tokens[0], "(") { + inner, err := tokenize(tokens[0]) + if err != nil { + return nil, err + } + if len(inner) == 1 && inner[0] == tokens[0] { + break + } + tokens = inner + } + return tokens, nil +} + +func isSimpleN(tokens []string) bool { + tokens, err := unwrapSingleGroup(tokens) + if err != nil || len(tokens) != 1 { + return false + } + return tokens[0] == "n" +} + +func parseLiteralTokens(tokens []string) (uint32, error) { + tokens, err := unwrapSingleGroup(tokens) + if err != nil { + return 0, err + } + if len(tokens) != 1 { + return 0, errors.New("expected a single integer") + } + return parseUint32(tokens[0]) +} + +func containsSimpleN(tokens []string) bool { + for _, token := range tokens { + if isSimpleN([]string{token}) { + return true + } + } + return false +} + func compileMod(tokens []string) (math math, err error) { split, err := splitTokens(tokens, "%") if err != nil { return math, err } - if len(split.Left) != 1 || split.Left[0] != "n" { + if !isSimpleN(split.Left) { return math, errors.New("modulus operation requires 'n' as left operand") } - if len(split.Right) != 1 { - return math, errors.New("modulus operation requires simple integer as right operand") - } - i, err := parseUint32(split.Right[0]) + i, err := parseLiteralTokens(split.Right) if err != nil { return math, err } - return mod{value: uint32(i)}, nil + if i == 0 { + return math, errors.New("modulus operation requires a non-zero integer as right operand") + } + return mod{value: i}, nil } func subPipe(modTokens []string, actionTokens []string, builder cmpTestBuilder, flipped bool) (test test, err error) { modifier, err := compileMod(modTokens) if err != nil { - return test, err + return nil, err } - if len(actionTokens) != 1 { - return test, errors.New("can only get modulus of integer") + if len(actionTokens) == 0 { + return nil, errors.New("can only get modulus of integer") } - i, err := parseUint32(actionTokens[0]) + i, err := parseLiteralTokens(actionTokens) if err != nil { - return test, err + return nil, err } - action := builder(uint32(i), flipped) + action := builder(i, flipped) return pipe{ modifier: modifier, action: action, @@ -159,31 +205,37 @@ func subPipe(modTokens []string, actionTokens []string, builder cmpTestBuilder, func compileEquality(tokens []string, sep string, builder cmpTestBuilder) (test test, err error) { split, err := splitTokens(tokens, sep) if err != nil { - return test, err + return nil, err } - if len(split.Left) == 1 && split.Left[0] == "n" { - if len(split.Right) != 1 { - return test, errors.New("test can only compare n to integers") - } - i, err := parseUint32(split.Right[0]) + left, err := unwrapSingleGroup(split.Left) + if err != nil { + return nil, err + } + right, err := unwrapSingleGroup(split.Right) + if err != nil { + return nil, err + } + if isSimpleN(left) { + i, err := parseLiteralTokens(right) if err != nil { - return test, err + return nil, err } return builder(i, false), nil - } else if len(split.Right) == 1 && split.Right[0] == "n" { - if len(split.Left) != 1 { - return test, errors.New("test can only compare n to integers") - } - i, err := parseUint32(split.Left[0]) + } + if isSimpleN(right) { + i, err := parseLiteralTokens(left) if err != nil { - return test, err + return nil, err } return builder(i, true), nil - } else if contains(split.Left, "n") && contains(split.Left, "%") { - return subPipe(split.Left, split.Right, builder, false) } - return test, errors.New("equality test must have 'n' as one of the two tests") - + if containsSimpleN(left) && slices.Contains(left, "%") { + return subPipe(left, right, builder, false) + } + if containsSimpleN(right) && slices.Contains(right, "%") { + return subPipe(right, left, builder, true) + } + return nil, errors.New("equality test must have 'n' as one of the two tests") } var eqToken eqStruct @@ -258,8 +310,8 @@ type testTokenDef struct { } var precedence = []testTokenDef{ - {op: "||", token: orToken}, - {op: "&&", token: andToken}, + {op: "||"}, + {op: "&&"}, {op: "==", token: eqToken}, {op: "!=", token: neqToken}, {op: ">=", token: gteToken}, @@ -273,20 +325,10 @@ type splitted struct { Right []string } -// Find index of token in list of tokens -func index(tokens []string, sep string) int { - for index, token := range tokens { - if token == sep { - return index - } - } - return -1 -} - // Split a list of tokens by a token into a splitted struct holding the tokens // before and after the token to be split by. func splitTokens(tokens []string, sep string) (s splitted, err error) { - index := index(tokens, sep) + index := slices.Index(tokens, sep) if index == -1 { return s, fmt.Errorf("'%s' not found in ['%s']", sep, strings.Join(tokens, "','")) } @@ -296,135 +338,277 @@ func splitTokens(tokens []string, sep string) (s splitted, err error) { }, nil } -// Scan a string for parenthesis -func scan(s string) <-chan match { - ch := make(chan match) - go func() { - depth := 0 - opener := 0 - for index, char := range s { - switch char { - case '(': - if depth == 0 { - opener = index - } - depth++ - case ')': - depth-- - if depth == 0 { - ch <- match{ - openPos: opener, - closePos: index + 1, - } - } - } - +func trimLexicalWhitespace(s string) string { + return strings.Map(func(r rune) rune { + if unicode.IsSpace(r) { + return -1 } - close(ch) - }() - return ch + return r + }, s) } -// Split the string into tokens -func split(s string) <-chan string { - ch := make(chan string) - go func() { - s = strings.ReplaceAll(s, " ", "") - if !strings.Contains(s, "(") { - ch <- s - } else { - last := 0 - end := len(s) - for info := range scan(s) { - if last != info.openPos { - ch <- s[last:info.openPos] - } - ch <- s[info.openPos:info.closePos] - last = info.closePos +func lexicalToken(s string, index int) (token string, next int, err error) { + if index >= len(s) { + return "", index, errors.New("unexpected end of expression") + } + + switch s[index] { + case '?', ':', '%', '(', ')': + return s[index : index+1], index + 1, nil + case '|': + if index+1 >= len(s) || s[index+1] != '|' { + return "", index, errors.New("logical OR must use '||'") + } + return "||", index + 2, nil + case '&': + if index+1 >= len(s) || s[index+1] != '&' { + return "", index, errors.New("logical AND must use '&&'") + } + return "&&", index + 2, nil + case '=': + if index+1 >= len(s) || s[index+1] != '=' { + return "", index, errors.New("equality must use '=='") + } + return "==", index + 2, nil + case '!': + if index+1 >= len(s) || s[index+1] != '=' { + return "", index, errors.New("inequality must use '!='") + } + return "!=", index + 2, nil + case '>': + if index+1 < len(s) && s[index+1] == '=' { + return ">=", index + 2, nil + } + return ">", index + 1, nil + case '<': + if index+1 < len(s) && s[index+1] == '=' { + return "<=", index + 2, nil + } + return "<", index + 1, nil + case 'n': + return "n", index + 1, nil + default: + if s[index] >= '0' && s[index] <= '9' { + next = index + 1 + for next < len(s) && s[next] >= '0' && s[next] <= '9' { + next++ } - if last != end { - ch <- s[last:] + return s[index:next], next, nil + } + return "", index, fmt.Errorf("invalid character %q in expression", s[index]) + } +} + +func validateParentheses(s string) error { + depth := 0 + for index := 0; index < len(s); { + token, next, err := lexicalToken(s, index) + if err != nil { + return err + } + switch token { + case "(": + depth++ + case ")": + depth-- + if depth < 0 { + return errors.New("unmatched closing parenthesis") } } - close(ch) - }() - return ch + index = next + } + if depth != 0 { + return errors.New("unmatched opening parenthesis") + } + return nil +} + +func stripRedundantOuterParens(s string) string { + if len(s) < 2 || s[0] != '(' { + return s + } + + leading := 0 + for leading < len(s) && s[leading] == '(' { + leading++ + } + + openers := make([]int, 0, leading) + outer := leading + for index := range s { + switch s[index] { + case '(': + openers = append(openers, index) + case ')': + if len(openers) == 0 { + return s + } + opener := openers[len(openers)-1] + openers = openers[:len(openers)-1] + if opener < outer && index != len(s)-1-opener { + outer = opener + } + } + } + if len(openers) != 0 { + return s + } + return s[outer : len(s)-outer] } // Tokenizes a string into a list of strings, tokens grouped by parenthesis are // not split! If the string starts with ( and ends in ), those are stripped. -func tokenize(s string) []string { - /* - TODO: Properly detect if the string starts with a ( and ends with a ) - and that those two form a matching pair. +func tokenize(s string) ([]string, error) { + s = trimLexicalWhitespace(s) + if s == "" { + return []string{}, nil + } + if err := validateParentheses(s); err != nil { + return nil, err + } + s = stripRedundantOuterParens(s) + if s == "" { + return []string{}, nil + } - Eg: (foo) -> true; (foo)(bar) -> false; - */ - if len(s) == 0 { - return []string{} - } - if s[0] == '(' && s[len(s)-1] == ')' { - s = s[1 : len(s)-1] - } ret := []string{} - for chunk := range split(s) { - if len(chunk) != 0 { - if chunk[0] == '(' && chunk[len(chunk)-1] == ')' { - ret = append(ret, chunk) - } else { - for _, token := range pat.FindAllStringSubmatch(chunk, -1) { - ret = append(ret, token[0]) + for index := 0; index < len(s); { + if s[index] == '(' { + start := index + depth := 0 + groupEnd := false + for index < len(s) { + switch s[index] { + case '(': + depth++ + case ')': + depth-- + if depth == 0 { + index++ + ret = append(ret, s[start:index]) + groupEnd = true + } } + if groupEnd { + break + } + index++ } - } else { - fmt.Printf("Empty chunk in string '%s'\n", s) + if depth != 0 { + return nil, errors.New("unmatched opening parenthesis") + } + continue } + token, next, err := lexicalToken(s, index) + if err != nil { + return nil, err + } + if token == ")" { + return nil, errors.New("unmatched closing parenthesis") + } + ret = append(ret, token) + index = next } - return ret + return ret, nil } // Compile a string containing a plural form expression to a Expression object. func Compile(s string) (expr Expression, err error) { - if s == "0" { - return constValue{value: 0}, nil + s = trimLexicalWhitespace(s) + if s == "" { + return nil, errors.New("empty expression") } - if !strings.Contains(s, "?") { - s += "?1:0" - } - return compileExpression(s) + return compileExpressionDepth(s, 0) } -// Check if a token is in a slice of strings -func contains(haystack []string, needle string) bool { - for _, s := range haystack { - if s == needle { - return true +// Compiles an expression (ternary, numeric leaf, or test). +func compileExpressionDepth(s string, depth int) (expr Expression, err error) { + if depth > maxParseDepth { + return nil, errors.New("expression nesting is too deep") + } + tokens, err := tokenize(s) + if err != nil { + return nil, err + } + if slices.Contains(tokens, "?") { + return compileTernary(tokens, depth) + } + for _, tokenDef := range precedence { + if !slices.Contains(tokens, tokenDef.op) { + continue } + condition, err := compileTestDepth(s, depth+1) + if err != nil { + return nil, err + } + return testValue{condition: condition}, nil } - return false -} - -// Compiles an expression (ternary or constant) -func compileExpression(s string) (expr Expression, err error) { - tokens := tokenize(s) - if contains(tokens, "?") { - return ternaryToken.compile(tokens) + if isSimpleN(tokens) { + return variableValue{}, nil + } + if slices.Contains(tokens, "%") { + modifier, err := compileMod(tokens) + if err != nil { + return nil, err + } + return mathValue{value: modifier}, nil } return constToken.compile(tokens) } // Compiles a test (comparison) -func compileTest(s string) (test test, err error) { - tokens := tokenize(s) +func compileTestDepth(s string, depth int) (test test, err error) { + if depth > maxParseDepth { + return nil, errors.New("expression nesting is too deep") + } + tokens, err := tokenize(s) + if err != nil { + return nil, err + } for _, tokenDef := range precedence { - if contains(tokens, tokenDef.op) { + if !slices.Contains(tokens, tokenDef.op) { + continue + } + switch tokenDef.op { + case "||": + return compileLogicTestDepth(tokens, "||", buildOr, depth) + case "&&": + return compileLogicTestDepth(tokens, "&&", buildAnd, depth) + default: return tokenDef.token.compile(tokens) } } - return test, errors.New("cannot compile") + if slices.Contains(tokens, "%") { + m, err := compileMod(tokens) + if err != nil { + return nil, err + } + return pipe{ + modifier: m, + action: notequal{value: 0}, + }, nil + } + if len(tokens) == 1 && strings.HasPrefix(tokens[0], "(") { + inner, err := tokenize(tokens[0]) + if err != nil { + return nil, err + } + if len(inner) == 1 && inner[0] == tokens[0] { + return nil, errors.New("cannot compile parenthesized test") + } + return compileTestDepth(tokens[0], depth+1) + } + if len(tokens) == 1 && tokens[0] == "n" { + return notequal{value: 0}, nil + } + if value, err := parseLiteralTokens(tokens); err == nil { + return literalTest{value: value}, nil + } + return nil, errors.New("cannot compile") } func parseUint32(s string) (ui uint32, err error) { + s = trimLexicalWhitespace(s) i, err := strconv.ParseUint(s, 10, 32) if err != nil { return ui, err diff --git a/vendor/github.com/leonelquinteros/gotext/plurals/expression.go b/vendor/github.com/leonelquinteros/gotext/plurals/expression.go index a323da99..ec36cc47 100644 --- a/vendor/github.com/leonelquinteros/gotext/plurals/expression.go +++ b/vendor/github.com/leonelquinteros/gotext/plurals/expression.go @@ -20,6 +20,37 @@ func (c constValue) Eval(n uint32) int { return c.value } +type variableValue struct{} + +func (variableValue) Eval(n uint32) int { + return int(n) +} + +type mathValue struct { + value math +} + +func (m mathValue) Eval(n uint32) int { + if m.value == nil { + return -1 + } + return int(m.value.calc(n)) +} + +type testValue struct { + condition test +} + +func (t testValue) Eval(n uint32) int { + if t.condition == nil { + return -1 + } + if t.condition.test(n) { + return 1 + } + return 0 +} + type test interface { test(n uint32) bool } @@ -31,6 +62,9 @@ type ternary struct { } func (t ternary) Eval(n uint32) int { + if t.test == nil { + return -1 + } if t.test.test(n) { if t.trueExpr == nil { return -1 diff --git a/vendor/github.com/leonelquinteros/gotext/plurals/math.go b/vendor/github.com/leonelquinteros/gotext/plurals/math.go index 1a8c4464..8d250364 100644 --- a/vendor/github.com/leonelquinteros/gotext/plurals/math.go +++ b/vendor/github.com/leonelquinteros/gotext/plurals/math.go @@ -15,5 +15,8 @@ type mod struct { } func (m mod) calc(n uint32) uint32 { + if m.value == 0 { + return 0 + } return n % m.value } diff --git a/vendor/github.com/leonelquinteros/gotext/plurals/tests.go b/vendor/github.com/leonelquinteros/gotext/plurals/tests.go index 5e035cfe..8a6f47bb 100644 --- a/vendor/github.com/leonelquinteros/gotext/plurals/tests.go +++ b/vendor/github.com/leonelquinteros/gotext/plurals/tests.go @@ -22,6 +22,14 @@ func (e notequal) test(n uint32) bool { return n != e.value } +type literalTest struct { + value uint32 +} + +func (l literalTest) test(n uint32) bool { + return l.value != 0 +} + type gt struct { value uint32 flipped bool @@ -77,6 +85,9 @@ type and struct { } func (e and) test(n uint32) bool { + if e.left == nil || e.right == nil { + return false + } if !e.left.test(n) { return false } @@ -89,6 +100,9 @@ type or struct { } func (e or) test(n uint32) bool { + if e.left == nil || e.right == nil { + return false + } if e.left.test(n) { return true } @@ -101,5 +115,8 @@ type pipe struct { } func (e pipe) test(n uint32) bool { + if e.modifier == nil || e.action == nil { + return false + } return e.action.test(e.modifier.calc(n)) } diff --git a/vendor/github.com/leonelquinteros/gotext/po.go b/vendor/github.com/leonelquinteros/gotext/po.go index e70eb873..062fbaed 100644 --- a/vendor/github.com/leonelquinteros/gotext/po.go +++ b/vendor/github.com/leonelquinteros/gotext/po.go @@ -42,6 +42,10 @@ type Po struct { domain *Domain fs fs.FS + + parseBufferHasID bool + parseBufferHasPlural bool + parseBufferHasContext bool } type parseState int @@ -52,6 +56,7 @@ const ( msgID msgIDPlural msgStr + msgStrInvalid ) // NewPo should always be used to instantiate a new Po object @@ -94,7 +99,7 @@ func (po *Po) GetRefs(str string) []string { // SetPluralResolver sets the plural resolver function func (po *Po) SetPluralResolver(f func(int) int) { - po.domain.customPluralResolver = f + po.domain.SetPluralResolver(f) } // Set translation @@ -103,12 +108,12 @@ func (po *Po) Set(id, str string) { } // Get translation -func (po *Po) Get(str string, vars ...interface{}) string { +func (po *Po) Get(str string, vars ...any) string { return po.domain.Get(str, vars...) } // Append translation -func (po *Po) Append(b []byte, str string, vars ...interface{}) []byte { +func (po *Po) Append(b []byte, str string, vars ...any) []byte { return po.domain.Append(b, str, vars...) } @@ -118,12 +123,12 @@ func (po *Po) SetN(id, plural string, n int, str string) { } // GetN gets the plural translation -func (po *Po) GetN(str, plural string, n int, vars ...interface{}) string { +func (po *Po) GetN(str, plural string, n int, vars ...any) string { return po.domain.GetN(str, plural, n, vars...) } // AppendN appends the plural translation -func (po *Po) AppendN(b []byte, str, plural string, n int, vars ...interface{}) []byte { +func (po *Po) AppendN(b []byte, str, plural string, n int, vars ...any) []byte { return po.domain.AppendN(b, str, plural, n, vars...) } @@ -133,12 +138,12 @@ func (po *Po) SetC(id, ctx, str string) { } // GetC gets the translation for a given context -func (po *Po) GetC(str, ctx string, vars ...interface{}) string { +func (po *Po) GetC(str, ctx string, vars ...any) string { return po.domain.GetC(str, ctx, vars...) } // AppendC appends the translation for a given context -func (po *Po) AppendC(b []byte, str, ctx string, vars ...interface{}) []byte { +func (po *Po) AppendC(b []byte, str, ctx string, vars ...any) []byte { return po.domain.AppendC(b, str, ctx, vars...) } @@ -148,12 +153,12 @@ func (po *Po) SetNC(id, plural, ctx string, n int, str string) { } // GetNC gets the plural translation for a given context -func (po *Po) GetNC(str, plural string, n int, ctx string, vars ...interface{}) string { +func (po *Po) GetNC(str, plural string, n int, ctx string, vars ...any) string { return po.domain.GetNC(str, plural, n, ctx, vars...) } // AppendNC appends the plural translation for a given context -func (po *Po) AppendNC(b []byte, str, plural string, n int, ctx string, vars ...interface{}) []byte { +func (po *Po) AppendNC(b []byte, str, plural string, n int, ctx string, vars ...any) []byte { return po.domain.AppendNC(b, str, plural, n, ctx, vars...) } @@ -214,16 +219,17 @@ func (po *Po) Parse(buf []byte) { defer po.domain.trMutex.Unlock() defer po.domain.pluralMutex.Unlock() - // Get lines - lines := strings.Split(string(buf), "\n") - // Init buffer po.domain.trBuffer = NewTranslation() po.domain.ctxBuffer = "" po.domain.refBuffer = "" + po.parseBufferHasID = false + po.parseBufferHasPlural = false + po.parseBufferHasContext = false state := head - for _, l := range lines { + activeMsgStrIndex := 0 + for l := range strings.Lines(string(buf)) { // Trim spaces l = strings.TrimSpace(l) @@ -233,44 +239,71 @@ func (po *Po) Parse(buf []byte) { continue } - // Buffer context and continue - if strings.HasPrefix(l, "msgctxt") { - po.parseContext(l) - state = msgCtxt + // Multi line strings and headers + if strings.HasPrefix(l, "\"") { + if !po.parseString(l, state, activeMsgStrIndex) && state != head { + state = msgStrInvalid + } continue } - // Buffer msgid and continue - if strings.HasPrefix(l, "msgid") && !strings.HasPrefix(l, "msgid_plural") { - po.parseID(l) - state = msgID + // Buffer context and continue + if po.hasKeywordBoundary(l, "msgctxt") { + if po.parseContext(l) { + state = msgCtxt + } else { + state = msgStrInvalid + } continue } // Check for plural form - if strings.HasPrefix(l, "msgid_plural") { - po.parsePluralID(l) - po.domain.pluralTranslations[po.domain.trBuffer.PluralID] = po.domain.trBuffer - state = msgIDPlural + if po.hasKeywordBoundary(l, "msgid_plural") { + if state != msgID || !po.parsePluralID(l) { + state = msgStrInvalid + } else { + state = msgIDPlural + } + continue + } + + // Buffer msgid and continue + if po.hasKeywordBoundary(l, "msgid") { + if po.parseID(l) { + state = msgID + } else { + state = msgStrInvalid + } continue } // Save Translation - if strings.HasPrefix(l, "msgstr") { - po.parseMessage(l) - state = msgStr - continue - } + if po.hasKeywordBoundary(l, "msgstr") { + if state != msgID && state != msgIDPlural && state != msgStr { + activeMsgStrIndex = 0 + state = msgStrInvalid + continue + } - // Multi line strings and headers - if strings.HasPrefix(l, "\"") && strings.HasSuffix(l, "\"") { - po.parseString(l, state) + var ok bool + activeMsgStrIndex, ok = po.parseMessage(l) + if ok { + state = msgStr + } else { + activeMsgStrIndex = 0 + state = msgStrInvalid + } continue } } - // Save last Translation buffer. - po.saveBuffer() + // Save last Translation buffer, but do not synthesize an entry after an + // invalid directive. Preserve the historical empty-input entry. + if po.parseBufferHasID { + po.saveBuffer() + } else if len(buf) == 0 { + po.domain.translations[""] = po.domain.trBuffer + } // Parse headers po.domain.parseHeaders() @@ -285,22 +318,30 @@ func (po *Po) Parse(buf []byte) { // saveBuffer takes the context and Translation buffers // and saves it on the translations collection func (po *Po) saveBuffer() { - // With no context... - if po.domain.ctxBuffer == "" { - po.domain.translations[po.domain.trBuffer.ID] = po.domain.trBuffer - } else { - // With context... + if !po.parseBufferHasID { + return + } + + // Store a completed plural ID after all of its continuation lines have + // been decoded. + if po.parseBufferHasPlural { + po.domain.pluralTranslations[po.domain.trBuffer.PluralID] = po.domain.trBuffer + } + + if po.parseBufferHasContext { if _, ok := po.domain.contextTranslations[po.domain.ctxBuffer]; !ok { po.domain.contextTranslations[po.domain.ctxBuffer] = make(map[string]*Translation) } po.domain.contextTranslations[po.domain.ctxBuffer][po.domain.trBuffer.ID] = po.domain.trBuffer - - // Cleanup current context buffer if needed - if po.domain.trBuffer.ID != "" { - po.domain.ctxBuffer = "" - } + // Context applies only to the record it prefixes, even when its ID is empty. + po.domain.ctxBuffer = "" + po.parseBufferHasContext = false + } else { + po.domain.translations[po.domain.trBuffer.ID] = po.domain.trBuffer } + po.parseBufferHasID = false + po.parseBufferHasPlural = false // Flush Translation buffer if po.domain.refBuffer == "" { po.domain.trBuffer = NewTranslation() @@ -328,68 +369,106 @@ func (po *Po) parseComment(l string, state parseState) { // parseContext takes a line starting with "msgctxt", // saves the current Translation buffer and creates a new context. -func (po *Po) parseContext(l string) { +func (po *Po) parseContext(l string) bool { + value, ok := po.parseQuotedDirective(l, "msgctxt") + if !ok { + return false + } + // Save current Translation buffer. po.saveBuffer() - // Buffer context - po.domain.ctxBuffer, _ = strconv.Unquote(strings.TrimSpace(strings.TrimPrefix(l, "msgctxt"))) + // Buffer context, including an explicitly empty context. + po.domain.ctxBuffer = value + po.parseBufferHasContext = true + return true } // parseID takes a line starting with "msgid", // saves the current Translation and creates a new msgid buffer. -func (po *Po) parseID(l string) { +func (po *Po) parseID(l string) bool { + value, ok := po.parseQuotedDirective(l, "msgid") + if !ok { + return false + } + // Save current Translation buffer. po.saveBuffer() // Set id - po.domain.trBuffer.ID, _ = strconv.Unquote(strings.TrimSpace(strings.TrimPrefix(l, "msgid"))) + po.domain.trBuffer.ID = value + po.parseBufferHasID = true + return true } // parsePluralID saves the plural id buffer from a line starting with "msgid_plural" -func (po *Po) parsePluralID(l string) { - po.domain.trBuffer.PluralID, _ = strconv.Unquote(strings.TrimSpace(strings.TrimPrefix(l, "msgid_plural"))) +func (po *Po) parsePluralID(l string) bool { + value, ok := po.parseQuotedDirective(l, "msgid_plural") + if !ok { + return false + } + + po.domain.trBuffer.PluralID = value + po.parseBufferHasPlural = true + return true } // parseMessage takes a line starting with "msgstr" and saves it into the current buffer. -func (po *Po) parseMessage(l string) { - l = strings.TrimSpace(strings.TrimPrefix(l, "msgstr")) - +func (po *Po) parseMessage(l string) (int, bool) { + if !po.hasKeywordBoundary(l, "msgstr") { + return 0, false + } + l = strings.TrimSpace(l[len("msgstr"):]) // Check for indexed Translation forms if strings.HasPrefix(l, "[") { - idx := strings.Index(l, "]") - if idx == -1 { + idx := strings.IndexByte(l, ']') + if idx <= 1 || !isASCIIPluralIndex(l[1:idx]) { // Skip wrong index formatting - return + return 0, false } // Parse index i, err := strconv.Atoi(l[1:idx]) if err != nil { // Skip wrong index formatting - return + return 0, false } // Parse Translation string - po.domain.trBuffer.Trs[i], _ = strconv.Unquote(strings.TrimSpace(l[idx+1:])) + clean, ok := decodePOQuotedString(strings.TrimSpace(l[idx+1:])) + if !ok { + return 0, false + } + po.domain.trBuffer.Trs[i] = clean - // Loop - return + return i, true } // Save single Translation form under 0 index - po.domain.trBuffer.Trs[0], _ = strconv.Unquote(l) + clean, ok := decodePOQuotedString(l) + if !ok { + return 0, false + } + po.domain.trBuffer.Trs[0] = clean + return 0, true } // parseString takes a well formatted string without prefix // and creates headers or attach multi-line strings when corresponding -func (po *Po) parseString(l string, state parseState) { - clean, _ := strconv.Unquote(l) +func (po *Po) parseString(l string, state parseState, activeMsgStrIndex int) bool { + if state == msgStrInvalid { + return false + } + + clean, ok := decodePOQuotedString(l) + if !ok { + return false + } switch state { case msgStr: - // Append to last Translation found - po.domain.trBuffer.Trs[len(po.domain.trBuffer.Trs)-1] += clean + // Append to active Translation form + po.domain.trBuffer.Trs[activeMsgStrIndex] += clean case msgID: // Multiline msgid - Append to current id @@ -402,26 +481,174 @@ func (po *Po) parseString(l string, state parseState) { case msgCtxt: // Multiline context - Append to current context po.domain.ctxBuffer += clean - } + + return true } -// isValidLine checks for line prefixes to detect valid syntax. func (po *Po) isValidLine(l string) bool { - // Check prefix - valid := []string{ - "\"", - "msgctxt", - "msgid", - "msgid_plural", - "msgstr", + if strings.HasPrefix(l, "\"") { + return true } - for _, v := range valid { - if strings.HasPrefix(l, v) { + for _, keyword := range []string{ + "msgctxt", + "msgid_plural", + "msgid", + "msgstr", + } { + if po.hasKeywordBoundary(l, keyword) { return true } } return false } + +func (po *Po) hasKeywordBoundary(l, keyword string) bool { + if !strings.HasPrefix(l, keyword) { + return false + } + if len(l) == len(keyword) { + return true + } + + next := l[len(keyword)] + if keyword == "msgstr" && next == '[' { + return true + } + return next == ' ' || next == '\t' +} + +func (po *Po) parseQuotedDirective(l, keyword string) (string, bool) { + if !po.hasKeywordBoundary(l, keyword) { + return "", false + } + + value, ok := decodePOQuotedString(strings.TrimSpace(l[len(keyword):])) + if !ok { + return "", false + } + return value, true +} + +// decodePOQuotedString decodes a GNU PO double-quoted string. +func decodePOQuotedString(source string) (string, bool) { + if len(source) < 2 || source[0] != '"' { + return "", false + } + + var decoded []byte + for i := 1; i < len(source); { + switch source[i] { + case '"': + if i != len(source)-1 { + return "", false + } + if decoded == nil { + return source[1:i], true + } + return string(decoded), true + + case '\\': + if decoded == nil { + decoded = make([]byte, 0, len(source)-2) + decoded = append(decoded, source[1:i]...) + } + i++ + if i >= len(source) { + return "", false + } + + switch source[i] { + case 'a': + decoded = append(decoded, '\a') + i++ + case 'b': + decoded = append(decoded, '\b') + i++ + case 'f': + decoded = append(decoded, '\f') + i++ + case 'n': + decoded = append(decoded, '\n') + i++ + case 'r': + decoded = append(decoded, '\r') + i++ + case 't': + decoded = append(decoded, '\t') + i++ + case 'v': + decoded = append(decoded, '\v') + i++ + case '\\': + decoded = append(decoded, '\\') + i++ + case '"': + decoded = append(decoded, '"') + i++ + case '0', '1', '2', '3', '4', '5', '6', '7': + var value byte + for digits := 0; i < len(source) && digits < 3; digits++ { + if source[i] < '0' || source[i] > '7' { + break + } + value = value<<3 | (source[i] - '0') + i++ + } + decoded = append(decoded, value) + case 'x': + i++ + if i >= len(source) || !isPOHexDigit(source[i]) { + return "", false + } + var value byte + for i < len(source) && isPOHexDigit(source[i]) { + value = value<<4 | poHexValue(source[i]) + i++ + } + decoded = append(decoded, value) + default: + return "", false + } + + default: + if decoded != nil { + decoded = append(decoded, source[i]) + } + i++ + } + } + + return "", false +} + +func isPOHexDigit(c byte) bool { + return c >= '0' && c <= '9' || + c >= 'a' && c <= 'f' || + c >= 'A' && c <= 'F' +} + +func poHexValue(c byte) byte { + switch { + case c >= '0' && c <= '9': + return c - '0' + case c >= 'a' && c <= 'f': + return c - 'a' + 10 + default: + return c - 'A' + 10 + } +} + +func isASCIIPluralIndex(index string) bool { + if index == "" { + return false + } + for i := 0; i < len(index); i++ { + if index[i] < '0' || index[i] > '9' { + return false + } + } + return true +} diff --git a/vendor/github.com/leonelquinteros/gotext/translation.go b/vendor/github.com/leonelquinteros/gotext/translation.go index 2aa42f87..d6ad2f5b 100644 --- a/vendor/github.com/leonelquinteros/gotext/translation.go +++ b/vendor/github.com/leonelquinteros/gotext/translation.go @@ -49,11 +49,8 @@ func (t *Translation) Set(str string) { // Get returns the string of the translation func (t *Translation) Get() string { - // Look for Translation index 0 - if _, ok := t.Trs[0]; ok { - if t.Trs[0] != "" { - return t.Trs[0] - } + if tr := t.Trs[0]; tr != "" { + return tr } // Return untranslated id by default @@ -68,11 +65,8 @@ func (t *Translation) SetN(n int, str string) { // GetN returns the string of the plural translation func (t *Translation) GetN(n int) string { - // Look for Translation index - if _, ok := t.Trs[n]; ok { - if t.Trs[n] != "" { - return t.Trs[n] - } + if tr := t.Trs[n]; tr != "" { + return tr } // Return untranslated singular if corresponding @@ -86,12 +80,10 @@ func (t *Translation) GetN(n int) string { // IsTranslated reports whether a string is translated func (t *Translation) IsTranslated() bool { - tr, ok := t.Trs[0] - return tr != "" && ok + return t.Trs[0] != "" } // IsTranslatedN reports whether a plural string is translated func (t *Translation) IsTranslatedN(n int) bool { - tr, ok := t.Trs[n] - return tr != "" && ok + return t.Trs[n] != "" } diff --git a/vendor/github.com/leonelquinteros/gotext/translator.go b/vendor/github.com/leonelquinteros/gotext/translator.go index e32e3ab6..9305c94a 100644 --- a/vendor/github.com/leonelquinteros/gotext/translator.go +++ b/vendor/github.com/leonelquinteros/gotext/translator.go @@ -9,18 +9,20 @@ import ( "errors" "io/fs" "os" + + "github.com/leonelquinteros/gotext/plurals" ) -// Translator interface is used by Locale and Po objects.Translator +// Translator interface is used by Locale and Po objects. // It contains all methods needed to parse translation sources and obtain corresponding translations. -// Also implements gob.GobEncoder/gob.DobDecoder interfaces to allow serialization of Locale objects. +// It includes encoding.BinaryMarshaler and encoding.BinaryUnmarshaler methods for Locale serialization. type Translator interface { ParseFile(f string) Parse(buf []byte) - Get(str string, vars ...interface{}) string - GetN(str, plural string, n int, vars ...interface{}) string - GetC(str, ctx string, vars ...interface{}) string - GetNC(str, plural string, n int, ctx string, vars ...interface{}) string + Get(str string, vars ...any) string + GetN(str, plural string, n int, vars ...any) string + GetC(str, ctx string, vars ...any) string + GetNC(str, plural string, n int, ctx string, vars ...any) string MarshalBinary() ([]byte, error) UnmarshalBinary([]byte) error @@ -31,10 +33,10 @@ type Translator interface { // It contains all methods needed to parse translation sources and to append entries to the object. type AppendTranslator interface { Translator - Append(b []byte, str string, vars ...interface{}) []byte - AppendN(b []byte, str, plural string, n int, vars ...interface{}) []byte - AppendC(b []byte, str, ctx string, vars ...interface{}) []byte - AppendNC(b []byte, str, plural string, n int, ctx string, vars ...interface{}) []byte + Append(b []byte, str string, vars ...any) []byte + AppendN(b []byte, str, plural string, n int, vars ...any) []byte + AppendC(b []byte, str, ctx string, vars ...any) []byte + AppendNC(b []byte, str, plural string, n int, ctx string, vars ...any) []byte } // TranslatorEncoding is used as intermediary storage to encode Translator objects to Gob. @@ -63,14 +65,58 @@ type TranslatorEncoding struct { // deserialize into a Po-compatible object. func (te *TranslatorEncoding) GetTranslator() Translator { po := NewPo() - po.domain = NewDomain() - po.domain.Headers = te.Headers + if te == nil { + return po + } + + headers := te.Headers + if headers == nil { + headers = make(HeaderMap) + } + translations := te.Translations + if translations == nil { + translations = make(map[string]*Translation) + } + for id, translation := range translations { + if translation == nil { + translation = NewTranslation() + translation.ID = id + translations[id] = translation + } + } + contexts := te.Contexts + if contexts == nil { + contexts = make(map[string]map[string]*Translation) + } + for context, translationsForContext := range contexts { + if translationsForContext == nil { + contexts[context] = make(map[string]*Translation) + continue + } + for id, translation := range translationsForContext { + if translation == nil { + translation = NewTranslation() + translation.ID = id + translationsForContext[id] = translation + } + } + } + + po.domain.Headers = headers po.domain.Language = te.Language po.domain.PluralForms = te.PluralForms po.domain.nplurals = te.Nplurals po.domain.plural = te.Plural - po.domain.translations = te.Translations - po.domain.contextTranslations = te.Contexts + po.domain.translations = translations + po.domain.contextTranslations = contexts + + if expr, err := plurals.Compile(te.Plural); err == nil { + po.domain.pluralforms = expr + } + + po.Headers = po.domain.Headers + po.Language = po.domain.Language + po.PluralForms = po.domain.PluralForms return po } diff --git a/vendor/github.com/pjbgf/sha1cd/Dockerfile.arm b/vendor/github.com/pjbgf/sha1cd/Dockerfile.arm index 562f4c12..d6444ae9 100644 --- a/vendor/github.com/pjbgf/sha1cd/Dockerfile.arm +++ b/vendor/github.com/pjbgf/sha1cd/Dockerfile.arm @@ -1,4 +1,4 @@ -FROM golang:1.27@sha256:e432b43af23a9328d56a7c499be0476810aa344acbcf65fc7c455d4ff5a40602 +FROM golang:1.26@sha256:f96cc555eb8db430159a3aa6797cd5bae561945b7b0fe7d0e284c63a3b291609 ENV GOOS=linux ENV GOARCH=arm diff --git a/vendor/github.com/pjbgf/sha1cd/Dockerfile.arm64 b/vendor/github.com/pjbgf/sha1cd/Dockerfile.arm64 index 5164af6f..a149d842 100644 --- a/vendor/github.com/pjbgf/sha1cd/Dockerfile.arm64 +++ b/vendor/github.com/pjbgf/sha1cd/Dockerfile.arm64 @@ -1,4 +1,4 @@ -FROM golang:1.27@sha256:e432b43af23a9328d56a7c499be0476810aa344acbcf65fc7c455d4ff5a40602 +FROM golang:1.26@sha256:f96cc555eb8db430159a3aa6797cd5bae561945b7b0fe7d0e284c63a3b291609 ENV GOOS=linux ENV GOARCH=arm64 diff --git a/vendor/modules.txt b/vendor/modules.txt index 9e535fc5..87dab6c0 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -403,8 +403,8 @@ github.com/klauspost/compress/internal/le github.com/klauspost/compress/internal/snapref github.com/klauspost/compress/zstd github.com/klauspost/compress/zstd/internal/xxhash -# github.com/leonelquinteros/gotext v1.7.2 -## explicit; go 1.23.5 +# github.com/leonelquinteros/gotext v1.8.0 +## explicit; go 1.27 github.com/leonelquinteros/gotext github.com/leonelquinteros/gotext/plurals # github.com/lucasb-eyer/go-colorful v1.4.0 From d0d156dc305ac1b5bceca7440e001c5e6c150696 Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Wed, 14 Oct 2026 16:01:23 +0000 Subject: [PATCH 4/8] chore(deps): update golang docker tag --- vendor/coopcloud.tech/tagcmp/.drone.yml | 6 +++--- vendor/github.com/pjbgf/sha1cd/Dockerfile.arm | 2 +- vendor/github.com/pjbgf/sha1cd/Dockerfile.arm64 | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/vendor/coopcloud.tech/tagcmp/.drone.yml b/vendor/coopcloud.tech/tagcmp/.drone.yml index c5f78899..2aefe487 100644 --- a/vendor/coopcloud.tech/tagcmp/.drone.yml +++ b/vendor/coopcloud.tech/tagcmp/.drone.yml @@ -3,16 +3,16 @@ kind: pipeline name: coopcloud.tech/tagcmp steps: - name: gofmt - image: golang:1.26 + image: golang:1.27 commands: - test -z "$(gofmt -l .)" - name: go build - image: golang:1.26 + image: golang:1.27 commands: - go build -v . - name: go test - image: golang:1.26 + image: golang:1.27 commands: - go test . -cover diff --git a/vendor/github.com/pjbgf/sha1cd/Dockerfile.arm b/vendor/github.com/pjbgf/sha1cd/Dockerfile.arm index d6444ae9..384dd33c 100644 --- a/vendor/github.com/pjbgf/sha1cd/Dockerfile.arm +++ b/vendor/github.com/pjbgf/sha1cd/Dockerfile.arm @@ -1,4 +1,4 @@ -FROM golang:1.26@sha256:f96cc555eb8db430159a3aa6797cd5bae561945b7b0fe7d0e284c63a3b291609 +FROM golang:1.27@sha256:820153096fda4de6ac1b7194d12e416b7dd8f4e9a33d17e0345b111b945162d8 ENV GOOS=linux ENV GOARCH=arm diff --git a/vendor/github.com/pjbgf/sha1cd/Dockerfile.arm64 b/vendor/github.com/pjbgf/sha1cd/Dockerfile.arm64 index a149d842..23099c2d 100644 --- a/vendor/github.com/pjbgf/sha1cd/Dockerfile.arm64 +++ b/vendor/github.com/pjbgf/sha1cd/Dockerfile.arm64 @@ -1,4 +1,4 @@ -FROM golang:1.26@sha256:f96cc555eb8db430159a3aa6797cd5bae561945b7b0fe7d0e284c63a3b291609 +FROM golang:1.27@sha256:820153096fda4de6ac1b7194d12e416b7dd8f4e9a33d17e0345b111b945162d8 ENV GOOS=linux ENV GOARCH=arm64 From e458c531f78d40e66782e1c58a5313b8c183997d Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Wed, 14 Oct 2026 20:01:20 +0000 Subject: [PATCH 5/8] chore(deps): update nginx docker tag to v1.31.7 --- pkg/secret/testdir/compose.yaml | 2 +- tests/resources/test_recipe/compose.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/secret/testdir/compose.yaml b/pkg/secret/testdir/compose.yaml index 3d5ae6a9..b40ac7e0 100644 --- a/pkg/secret/testdir/compose.yaml +++ b/pkg/secret/testdir/compose.yaml @@ -3,7 +3,7 @@ version: "3.8" services: app: - image: nginx:1.31.6 + image: nginx:1.31.7 secrets: - test_pass_one - test_pass_two diff --git a/tests/resources/test_recipe/compose.yml b/tests/resources/test_recipe/compose.yml index 34036fbb..b9c421c2 100644 --- a/tests/resources/test_recipe/compose.yml +++ b/tests/resources/test_recipe/compose.yml @@ -3,7 +3,7 @@ version: "3.8" services: app: - image: nginx:1.31.6 + image: nginx:1.31.7 networks: - proxy deploy: From 35c29580bbfdccffcdbdbcb910871bd5e0eee071 Mon Sep 17 00:00:00 2001 From: 3wc <3wc@doesthisthing.work> Date: Wed, 14 Oct 2026 10:32:16 -0400 Subject: [PATCH 6/8] fix: Run `make i18n` --- pkg/i18n/locales/abra.pot | 4 +- pkg/i18n/locales/es.mo | Bin 136911 -> 136901 bytes pkg/i18n/locales/es.po | 704 ++++++++++++++++++++++++++------------ 3 files changed, 490 insertions(+), 218 deletions(-) diff --git a/pkg/i18n/locales/abra.pot b/pkg/i18n/locales/abra.pot index b4a50a2f..9c5a6c8c 100644 --- a/pkg/i18n/locales/abra.pot +++ b/pkg/i18n/locales/abra.pot @@ -7,7 +7,7 @@ msgid "" msgstr "Project-Id-Version: \n" "Report-Msgid-Bugs-To: EMAIL\n" - "POT-Creation-Date: 2026-08-31 19:21+0000\n" + "POT-Creation-Date: 2026-10-14 10:32-0400\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -4475,7 +4475,7 @@ msgstr "" msgid "retrieving docker auth token: failed create docker cli: %s" msgstr "" -#: ./pkg/recipe/recipe.go:659 +#: ./pkg/recipe/recipe.go:656 msgid "retrieving recipes" msgstr "" diff --git a/pkg/i18n/locales/es.mo b/pkg/i18n/locales/es.mo index e07095911a8b49d30dfe8d9f6f4efd2e2b40a2e3..1a8812cb064b3384c622d534f9593daf53e57e9f 100644 GIT binary patch delta 8596 zcmXZhc|cZG8prW-Q4~;7L37Ps5p!Hngfu}^ToDr?lq|OfiNrKOaluBt<A_pQ%; z-7#isEyvsnFs5f6$4oD8OhSFf+%U%M^f_i9e$db{EqQKIgk$d0Uf;+uJ!r3qbj#c0fmb4(qai;=hyAH@>%;e8Cndhw14!Dd()+hA4fg0(R{ z-gQiKE=F)878hVm+>dSX0=B~1?H$tqdtyBth7EBlM&T;_8|#Po9aQ##kH7>wL9Ah3`Gqz8#RILsL=j^ zn#cpx`?b5+$aFzXrlq;|k?nmWD zrEd1Q1guLt6SZ{{QITGRkK=yyF}^9+-F6g-+5C`#3Ry9>!`rAmZ%N8)pk8>_A%xhQ z_LQD>fWtVID!PWcKl%yB1mouzj$ff7dj>V}px#8D@l9(kwAUF}3rC`6KFzzo3>BG= zP}T7h&S0QwDRw1`QXTUM_e)Uk-9qJ1XqsbwD`!kR>iuT@9J7-5hM*=E(BCnxUZ~84 z%A*Me;bW+r=z!%|X(p;Xv!AvTSdIs2??pvs-ZPG=kDF0@d&o0rfVbzUvU>p!F`<_* z7n6rj|60+k ziOQu$&)Ergz%I0(dCs*-v6vewhXbgU2V~m`Jc^pYKx~cEQQ5r(JK_;kGF5%vZebF3 zqdf#Qz)I9aFQdO~huH|Gp^y9HTrM==0#ufNivJ-r*H9sUWrSlmVa+zIiMu>cVmR&V z$d;P&BOMcn6|ovtK}DbeDxy)Sg(Tu>d;$BRoAjb%&T+9A70QXZHiXMiW%Dk^p%=&7g;pr^N6s~ITxf4c;Tay> zhT5}5FFB?P9zZ46Nw0m^Gw>faBH^A*QRUhRl>>#S2~Wf7xDmC5Utll1iEC8<9VXh9 z{D3}ws5Z&&ZEMtlk&l|l+o-+Vh??L<9EjnQ9rFPb&%-0MBVV>Fy^U9B*LlS;@3BYD z6e7xV?@x0~MtNhtnNH3yzG*VU&bTA0OnPA=4!~2qum~H{9zTuJdChwbwx_Y`-m0 z?+@^tidxWmItZlRK+AXO za*Pj~Vs-3=`g>v!>i1EoBYQTsRsHYfB7z$aP?2c3+IEnFYUiRRvJe&O?bsJ9t+Ane z7B#^)Q3GuE+NZHD?TYW%2t=T^_z6^UW}v_RCvl;yoQqo7MpQ@V@ezE0`ph<1YqL2X zm84I4zJU5ROhs+UTc~e95$gR9F%*xXz6GVI_wS;sy{@*-)^8o`Ksyc9;p?caDMBUH zIaEY~-?bgLL9H+Y6@h%uMW{by_MqN7>lw7(E;I(UCB4^E|5|A#H?##V>S$eoIuA-v zU%@M=GHSHJ4m=1ok%_1S=^a#Ve2-edZESY&?<`upO@M%T_TaFbne zE7ZWe)+7))iP})OK$@wB`Wy??#J&a1uOQ`qCP!n$Xk&RR)s;sA= zLca#J;5}B?RNrE=dJt*?1*p&#pe8Q7etu zYQHJ{Q4^bsTEJdxq58keg_0%m6C2tA=wG4dVbnlns8B|JYI7z9b&!n5WZZ-b@jcWz z5%rm!$V;fmY(~}nc~r!LwsGPzzKP^QADML23@4yJ*-+=gQB>%EK~12+cH2%uCFcZG zxxRv`f(00cA7C8rMg0xwd~Q#~AZ$UqF1qR01(suM3Y9)(3 zH=z!oo!Ai{U?O(dWe=Lks1SeVc?aWZNAI?dKpkAGF#*q^p0B^h`jpFsW;Vz36Vzw8 z6w@$luZ=(!YUMMrIxa_@bj7ITyn%|)L;Gy*v_%ax2(^Iun2JZSFGhUjm{-vq$A$Lt zFVt5q?Q1*pF{tv{gc`UM{n?Ewv#9+x7t&D4H5!#W6Hrw#4^^%Qz2E;pofl=OoCrVQ zUx;gBxKPL4P%jjsl4m8V!|kYQIEl(ybI?vG3N=s{)blT)l6fAg{Fb0{;V0CdUq?+e z;*gDCQ+!PI-<}I~l#hDh9aKm+qLQn``@Iykm-P?Z zNvJ*h7wUxj2(@KbP)S?sj7_#wEKmC>R22+FA7)_^PQxeh04j+aoVAg3`*WeaScqC- zF)A01pth#sIr}f9=BO3Mp`K4g4U~i0ntarG@jCkOOANwN)E4}TkD~9qeLo3Vh-?1N zg+ek1RZj0?6+DGLyoyS?kP9{mld%o$T-19-sF3eLZC&Myw%qEWlJsw=E$Zr-<^4Vt zE2#eGaG|nWfLi%xY=S?cCRFW`Eu%Qp3dW-LaxqrL&0c#4Dgwt*Nn5GZzTXTL*}kac z%STnqOzg_|W(yZehCfjuulj@SC>B+AIjG-fp^|kuDgwJu9p6WlZLP~Thq|J+Dg*UZ z9f6wo6!hU*R1STOu9E0J7yc@^Vn2%+p3_i!U5rY?1E`hVM-5!(N4v*uu?Ou=s9bmz z{ab@R+IzhA6%3&reAOOUHLg z6<7;@Lxr~D&-R~MF{tl+g69a-5x)=zxrlpE=A?k0n~)c|8D;f>4sfsPev_x2Xa2R<`fsn z+$cpwpyh3QVDv!k-2~K1-a#LJkE(`ysJ*Om$3`L?HE;$h>t~@3s3KJEtVf-oC8(__ ze^>v92ldy2i&$>tqsp%cwc>r)0xSMuC-NBTB+EjDem?fW?=caZ+_V3F$U*(pyaAOf zTT!`j7M09@p%zy6KK-lyley3g$Kg}B1P9{pSOL@iv>oSTa<^2z#FIt?L=4QcY+HgN#I{LTjNn78jqUkGOv9Im0Wj_y)(WC_5^H- z+M+nrM24esXcTG!E3q+tii-3F)L&LL%cy^q)uUy$o(G{;_9m(v_hSTJL9HN!|JJR6 zTcA20Tkgp9?vtf!Cpu^8_lFDpw5fpQy2@i3~#Z`!Wu}b*QhS8ypni zKT0F97dHl?4_BkIb+6YhMGX)bYzJ(IakPh_&W+`$`akFWUM0l#lYm;tVAPgPNA)`& ztE>KtxllIl_B@R$ms_aMY*;1#gQf}Aq#cL)R-~fJvk*1#Dpc8(p!WD2YJ!a_+x`Zl z`kRPK#y7CD>VFFtn%O;6xrI~-@PF5=q@ zwGqof9qEfu2iG3dmYu}9s{i1s0sgXyM6Ij^YOgX-Gj&l1$tu*|evL|=8>opit!8th zCo0?LV>E6;AD%~r+^KH+ZGc)xPjprHW4Ta=>rhE{5%nF94ztPD2SaI(LQU{x)JoT) zzJ8^izHr;mAPnaITnxjds0nSxZg>&3Wld@Xxc-@B)UahU1vQaRQG0m{mAz%2jcVEz zC7~jbgZh0oYK6t9JwJk~|B#0Q{0CbCcBefGtKb%Nu*4Mqr*r$`Zb*7|e%|OYQ7Hw( zhCdh8W$dtmsD63zzV0cVlY57xrADP^j~iYvJTEt@TjrSTcwbC(OxvjFxTxsXzL=Q! z*f!0h`Cmxy%z`md{YGcz7L3dslQ%lvmz9%QFly{@UxIg$m^FGr{+PUxnL}IVW{>H} U3%SF_W)91a+VWIwXTpR31HVdn+yDRo delta 8578 zcmXZhc|caxy2tUg5fwy5MAS^|b-+9dq~VC7AcMmZ5ztK1I22AtQA6#NLpz6~Woq4S zY1+-lYp?aJ=UMwjcWn$hv@vM& zF`xUMV@z>9$J`DuChHN${5#N?w1$qkWsKSHbIe}+ys=|q`QGd%j=4*JTT{oR(f_EK zV-{2~=1!DjdT{;CXvdu6`U(7#ela)g!!1uZrW@C%wsuTkENSDIc3iI>=eTA!59}W2 zn9f`X?&z2(O!XXvb?N^XevNCfH5PPo%pJ{#>zGzFCUPMTmtzDT$M$#~pTH)a9n%Q2@KJmL8{<5T!A;l#%kgon(#4Jw>zRTh zxSoUh-cD?aH(VNeK&=GFb{>*6A9{W?{9*NOdr%lCLmknnj+gUC8#afk6N)wwgW`qlXRaz?e%k>vr(bkfZgyo zDmNlh?Dx{J0sT>^t(%34^eXIt$I-|0O^sALP%IARgMp}!m0(AFh}!dxq`V%~AOCO& zA@-p^x2Ju;Nt{6y-9cTS@^{As<30?>?@*DwjGB1uK1819o6a<}*SS~^UqsD(zIS~c zDl%W9s^d4D!-E>6+m)=!aLi*|FGtp z4RvX#JlbFoc17hx53IyWN1@7d?9+Av>+t~nBdEwM9pspXxC6Df-+R{1@%9{5b`$U* z6Pk%*F#B2RUn}}5&wlX=Dm!aE=a{zG6qS5WO<`WTB9Z~6x-mNsO&Dqu6PQSOpgq+ zTbPL{^hcl`un{%UU(jE+!)*j}(8u*xT^f4ea#WV@!nX;{9aPBQ808pFShEKs@Sx`f z45xn=*-}$;v|}n`7}mmis0g$`MYIEIA-(YoPQd=?W)?W+EREHuP|g}-L%0rAHlJWT z9zzWfI@U4Au@fqrL&w<_$74Eu7soP_1E^|Ac#*v(ho+(ydgEVqp|xJ}N6t0LG_<#q zv79gNLG9V9Nsg(3WvJx3;Psu!_IfBPB9WePsB-Ov%7N*q3D3tc+>YA9Z?HGs$2F?| z9#iZ}enuZ3G?;4lwlnI$n1q_hhp4^Wj+)>NdAN#6Z=|j9szv(o` zY-W!_rV~-V_t{Lx3ZKO?u8mw8x;l6U)6iazN1fqsqmro@ zb+&(j3S|Wji2JDXKQTI1rXultgQS=9+CO#cC?wWp;p3>#CZHyqj*0jhCgDC*QilAGV-he0 zV{kfZyv;5Rh43_L;L0C3CKE$Z2Sy(1iwkiueuby-u_d-#@1kBdkxT6a{)rlI6>33; zP!GI=ics@qHi-wICgi?OL!sS+`r>WWJD~A$J5Wp1K>bk>nTm~W<_rx&N=aW%K_9ASr`aeRW2^WG^+Zne)AN_$||0UE!R-!_^ z7qc;9jSb~U)C503Jz%fbzl06whpn{{h(>Mk09113qQCxU(@<6}L9J{%YM^WQ7zVAg zZ><)nY)(NX=`)@aP$%I$)RwG3y#tC+_kV#k@hs|HP=UJN`H=e8UN@kj`fZ8{n1dSd zJ=E3|p_1w^f4+oenKtaA*y;3itNJ1qayMl>Y&?!`h9V# z$h9*J-DFqX3H9I%?2XT3RV+ef^)~e38Pv)jpjJ|Uv(1qhOroEOn%IAP?nIT}ZA`=_ zAK8gL@6ynN7Nfp!5jBB2AKN3g1?r@G4)yuFn1Wwp76xyze|il@?d?ibWIn~Acpg=L zy?8^#VIgYet1$%K(=;m6xQq(@P1J)LePUObjy36zKqcqDP%B%9n&?TSj?GQfeZkx8 zggc^-{wP#g&qam)Bh-QqTV3<$r#7pHp(ZdD71|=yjr&k5sz4=KgY9-Dsi@Ci_WDau zIdl}2%>O~HG`iTnQ}R%eT!LD_5sX&-JO5{sB^DLheDtr-^Cap)!8>dyV^LW>5Ot7D z!(Ld73UQ^+>^ac^HIbR9$m~GX{WVm?YL{^0@_Z9ZLob=Zs2R>cf3l&@g)^wo|Bl+r z7N6UGCMr2+pvv_PR23{o<-+IK36G$DLxy}|PsG|7O}`ns8X%KKHynhT*&@^ye1b}n z!>GMGgN^YXW?|%)_QB&&51fUX@FLXxTTu5ML`Cc->RhN-YOCdmQtCgE3)x)I$@UKV za6MMT{iqe6K%HdfD_d?pRK(h$>UuCLcM4F+yaYpWCF*?HikiStR3z`Cwj_3^Yxi)- zPCL-MsFketEJhtb`>`tqeQoQv2kM}C4He?uo@SRlG2=Zaq7JUj*afemzTbSe^&c(` z&1|vf*QnQU1@^;+du#*>PzTKd48!%P36!9c^ByWfzP&bg5>XEthFZWf%)m34jnVrY zGYj2UX=pF2eq;9}2jl6#f-0Y4)PpO~pWWZuGV6fKg&b6JO+n?(3{+JtMV0Fb@AH8D z_Phv2*<@qM|h+WZqRQaqx{op7;-Ea<7*AG1#9<`zDgj#7j zD!CS;?mK}x3GboyzR`Df!a1ninuhv(6S_L-j?&Oc6>!WRwVhB&In?t_R78qVRqz8U zyu)I`cqNAT~Miq%isiDY3n`lC@*v;j44DTd>D*K7QN zs_T$5cIJ_oNIw&`XK$lUxGzy#b_FS)bNtli8=)Z)zuLu?L!>FyR`;#rVC{&U@ ziQ1xc&jRoBd00jDznF%SW;tr*JMeM*6*Zv-=WQ7!qgLRe_Hs3b;tsF>Eh+*(qLMb^ zg1x^jDzZaR$u|jAEekN2=bKU*N`}f8ZO9)%4U~W?yF%3G3sK3s9uF27UAod;MEjjehu5dtf!XO8x7%-7{QJsAr>Qz8Q4@?MAKe7t{)BT(ect z7PVCqQ77OAtcQQ1LK}A7{;AakwV*W5iKrufB@V$4ue)}}K{xE)G{-J{&>a;b7ga8s zP$9p9+LA^$ZD^CRGyNP?WEP-S_z5b9zC?|81r@=l3cJv3R5?y@X(;&)qE>JPwF2|A zjYKO{h?7tUNpH-+Ihcf}yw4l{V(UB#6}bY`0v4ijX%A{E?_(lH+_K+ypQ51)OE3Y? zVjGP7)y_BrRen?P1^f)tG4{3%@f6fv&qA$mEh?wVP!q2CoBbiu7rWDc4YlBJk%hVD zB8^^Ls6a)aX2j`-)ej)0BDnjMXHq;4P zj@p`bDFR(uqrG3<_=NLSQJR)BioGVF^#VIsEq)BgQXi2Bw1 zDJoZXqH^O3Dw(U^wc|CzXw`o<4bAXXd$52;{T#)gAw$TQSXXDsPdeSdhjMx*_EUA_$q3GG1cvO z!%^eSLM7w-n56nIrJqe-eiRj=R;V*Q4fViG)C6BaP3Qwm!z~z(cTs14 zyP7s)g{UKa73$zRjM}mb*g*9k9va{;n^@G!+N1U=7d6w@Q3uH;)ZQLLCC@$7MB-}M z+{i*@`!Z~e#puIpsE~(**>PK-7LtXo>fWWH0k@)(>;~#R9$(ug+tXN+{$$hy=b%=) z1@-o;@N5}w#~FsfTwj8J1_-rpth_{odDNAliWJCZ04dSvJ17B4^Y_~9AS+? zAN@>JBnrLji%=^pLGAe|RN2+38{j|K(lC|&WUPUu=-_u|%iBqv%ObMJ<&7CReuS?@ z^4OTM<9x}Z$4<=u+rk6&oO)T+`enpq4SjLs_>p7B#H8d+7#i;zl$w+klb#WilH}`| z5ue_xRZd3V)M|b5#!rapUzj&${OG(1V+-SbX#@K7@%2qgPxW<4O;768Cow4{B`Y;M M`_rWQ&dk65A0NGOB>(^b diff --git a/pkg/i18n/locales/es.po b/pkg/i18n/locales/es.po index 1dac6e97..56e461a0 100644 --- a/pkg/i18n/locales/es.po +++ b/pkg/i18n/locales/es.po @@ -2,10 +2,11 @@ msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: EMAIL\n" -"POT-Creation-Date: 2026-08-31 19:21+0000\n" +"POT-Creation-Date: 2026-10-14 10:32-0400\n" "PO-Revision-Date: 2026-09-01 22:35+0000\n" "Last-Translator: chasqui \n" -"Language-Team: Spanish \n" +"Language-Team: Spanish \n" "Language: es\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -100,7 +101,8 @@ msgstr "" " abra aplicacion secreto insertar 1312.net my_secret v1 secreto.txt -f\n" "\n" " # insertar secreto desde stdin\n" -" echo \"mmySuperSecret\" | abra aplicacion secreto insertar 1312.net my_secret v1" +" echo \"mmySuperSecret\" | abra aplicacion secreto insertar 1312.net " +"my_secret v1" #: cli/app/list.go:54 msgid "" @@ -159,7 +161,8 @@ msgstr "" " abra aplicacion ejecutar 1312.net db comando_argumento --usuario bar\n" "\n" " # Para ejecutar directamente el comando sin abrir bash con \"--\"\n" -" abra aplicacion ejecutar 1312.net db comando_argumentos --usuario bar -- foo -vvv\n" +" abra aplicacion ejecutar 1312.net db comando_argumentos --usuario bar -- " +"foo -vvv\n" "\n" " # Omitir el servicio si usas \"--local/-l\"\n" " abra aplicacion ejecutar 1312.net comando --local" @@ -172,7 +175,8 @@ msgid "" " # pull lost app .env file\n" " abra app env pull my.gitea.net --server 1312.net" msgstr "" -" # Obtener el archivo de entorno (.env) y sobrescribir modificaciones locales\n" +" # Obtener el archivo de entorno (.env) y sobrescribir modificaciones " +"locales\n" " abra aplicacion entorno obtener 1312.net --force\n" "\n" " # Extraer el archivo .env perdido de la aplicación\n" @@ -412,8 +416,12 @@ msgstr "%s se ha escalado a 1" #: pkg/catalogue/catalogue.go:40 #, c-format -msgid "%s has locally unstaged changes? please commit/remove your changes before proceeding" -msgstr "¿%s tiene cambios no commiteados localmente? Por favor, commitea/elimina tus cambios antes de continuar" +msgid "" +"%s has locally unstaged changes? please commit/remove your changes before " +"proceeding" +msgstr "" +"¿%s tiene cambios no commiteados localmente? Por favor, commitea/elimina tus " +"cambios antes de continuar" #: cli/internal/recipe.go:114 #, c-format @@ -433,7 +441,9 @@ msgstr "%s no tiene secretos para generar, omitiendo..." #: pkg/recipe/compose.go:107 #, c-format msgid "%s has no version label? try running \"abra recipe sync %s\" first?" -msgstr "¿%s no tiene etiqueta de versión? ¿intentaste ejecutar \"abra receta sincronizar %s\" primero?" +msgstr "" +"¿%s no tiene etiqueta de versión? ¿intentaste ejecutar \"abra receta " +"sincronizar %s\" primero?" #: pkg/secret/pass.go:29 #, c-format @@ -534,13 +544,18 @@ msgstr "%s eliminado del almacén de contraseñas" #: cli/app/new.go:224 #, c-format -msgid "%s requires secret generation before deploy, run \"abra app secret generate %s --all\"" -msgstr "%s requiere generación de secretos antes del despliegue, ejecuta \"abra aplicacion secreto generar %s --todos\"" +msgid "" +"%s requires secret generation before deploy, run \"abra app secret generate " +"%s --all\"" +msgstr "" +"%s requiere generación de secretos antes del despliegue, ejecuta \"abra " +"aplicacion secreto generar %s --todos\"" #: cli/app/new.go:228 #, c-format msgid "%s requires secret insertion before deploy (#generate=false)" -msgstr "%s requiere inserción de secretos antes del despliegue (#generate=false)" +msgstr "" +"%s requiere inserción de secretos antes del despliegue (#generate=false)" #: cli/app/new.go:155 #, c-format @@ -688,8 +703,12 @@ msgid "'%s' is not a known version for %s" msgstr "'%s' no es una versión conocida para %s" #: cli/app/volume.go:175 -msgid "'x' indicates selected, enter / return to confirm, ctrl-c to exit, vim mode is enabled" -msgstr "'x' indica seleccionado, presiona Enter para confirmar, Ctrl-C para salir, el modo vim está activado" +msgid "" +"'x' indicates selected, enter / return to confirm, ctrl-c to exit, vim mode " +"is enabled" +msgstr "" +"'x' indica seleccionado, presiona Enter para confirmar, Ctrl-C para salir, " +"el modo vim está activado" #: cli/app/cmd.go:125 #, c-format @@ -713,27 +732,35 @@ msgstr "Se utiliza una plantilla de receta gestionada por la comunidad." msgid "" "A recipe is a blueprint for an app.\n" "\n" -"It is a bunch of config files which describe how to deploy and maintain an app.\n" +"It is a bunch of config files which describe how to deploy and maintain an " +"app.\n" "Recipes are maintained by the Co-op Cloud community and you can use Abra to\n" "read them, deploy them and create apps for you.\n" "\n" -"Anyone who uses a recipe can become a maintainer. Maintainers typically make\n" -"sure the recipe is in good working order and the config upgraded in a timely\n" +"Anyone who uses a recipe can become a maintainer. Maintainers typically " +"make\n" +"sure the recipe is in good working order and the config upgraded in a " +"timely\n" "manner." msgstr "" "Una receta son las instrucciones para crear una aplicación.\n" "\n" -"Es un conjunto de archivos de configuración, que describen cómo desplegar y mantener una aplicación.\n" -"Las recetas son mantenidas por la comunidad Co-op Cloud y puedes usar Abra para\n" +"Es un conjunto de archivos de configuración, que describen cómo desplegar y " +"mantener una aplicación.\n" +"Las recetas son mantenidas por la comunidad Co-op Cloud y puedes usar Abra " +"para\n" "leerlas, desplegarlas y crear aplicaciones para ti.\n" "\n" -"Cualquiera que use una receta puede convertirse en mantenedor. Los mantenedores típicamente se\n" -"aseguran de que la receta funcione correctamente y de que la configuración se actualice de manera oportuna." +"Cualquiera que use una receta puede convertirse en mantenedor. Los " +"mantenedores típicamente se\n" +"aseguran de que la receta funcione correctamente y de que la configuración " +"se actualice de manera oportuna." #: cli/app/remove.go:57 #, c-format msgid "ALERTA ALERTA: deleting %s data and config (local/remote)" -msgstr "ALERTA ALERTA: eliminando los datos y la configuración de %s (local/remoto)" +msgstr "" +"ALERTA ALERTA: eliminando los datos y la configuración de %s (local/remoto)" #. translators: Short description for `server add` command #: cli/server/add.go:30 @@ -745,7 +772,8 @@ msgid "" "Add a new server to your configuration so that it can be managed by Abra.\n" "\n" "Abra relies on the standard SSH command-line and ~/.ssh/config for client\n" -"connection details. You must configure an entry per-host in your ~/.ssh/config\n" +"connection details. You must configure an entry per-host in your ~/.ssh/" +"config\n" "for each server:\n" "\n" " Host 1312.net 1312\n" @@ -754,15 +782,19 @@ msgid "" " Port 12345\n" " IdentityFile ~/.ssh/antifa@somewhere\n" "\n" -"If \"--local\" is passed, then Abra assumes that the current local server is\n" -"intended as the target server. This is useful when you want to have your entire\n" +"If \"--local\" is passed, then Abra assumes that the current local server " +"is\n" +"intended as the target server. This is useful when you want to have your " +"entire\n" "Co-op Cloud config located on the server itself, and not on your local\n" "developer machine. The domain is then set to \"default\"." msgstr "" -"Agrega un servidor nuevo a tu configuración para que pueda ser gestionado por Abra.\n" +"Agrega un servidor nuevo a tu configuración para que pueda ser gestionado " +"por Abra.\n" "\n" "Para agregar servidor Remoto:\n" -"Abra usa el software SSH y de la carpeta ~/.ssh/config obtiene los datos de conexión al servidor. \n" +"Abra usa el software SSH y de la carpeta ~/.ssh/config obtiene los datos de " +"conexión al servidor. \n" "Debes configurar una entrada en ~/.ssh/config para cada servidor:\n" "\n" " Host 1312.net 1312\n" @@ -772,13 +804,15 @@ msgstr "" " IdentityFile ~/.ssh/antifa@1312.net\n" "\n" "Para agregar servidor local;\n" -"Si agregas \"--local\", Abra asumirá que la computadora local actual es el servidor que quieres agregar. \n" +"Si agregas \"--local\", Abra asumirá que la computadora local actual es el " +"servidor que quieres agregar. \n" "Esto es útil cuando quieres tener Abra en el mismo servidor. \n" "El dominio se establece entonces como \"default\"." #: cli/app/labels.go:33 msgid "Both local recipe and live deployment labels are shown." -msgstr "Se muestran tanto la receta local como las etiquetas del despliegue en vivo." +msgstr "" +"Se muestran tanto la receta local como las etiquetas del despliegue en vivo." #: cli/app/backup.go:319 cli/app/backup.go:335 cli/app/check.go:95 #: cli/app/cmd.go:285 cli/app/cp.go:385 cli/app/deploy.go:415 @@ -842,12 +876,16 @@ msgstr "💕 Clona la 📜 receta(s) en local" msgid "" "Compare env vars in both the app \".env\" and recipe \".env.sample\" file.\n" "\n" -"The goal is to ensure that recipe \".env.sample\" env vars are defined in your\n" -"app \".env\" file. Only env var definitions in the \".env.sample\" which are\n" -"uncommented, e.g. \"FOO=bar\" are checked. If an app \".env\" file does not include\n" +"The goal is to ensure that recipe \".env.sample\" env vars are defined in " +"your\n" +"app \".env\" file. Only env var definitions in the \".env.sample\" which " +"are\n" +"uncommented, e.g. \"FOO=bar\" are checked. If an app \".env\" file does not " +"include\n" "these env vars, then \"check\" will complain.\n" "\n" -"Recipe maintainers may or may not provide defaults for env vars within their\n" +"Recipe maintainers may or may not provide defaults for env vars within " +"their\n" "recipes regardless of commenting or not (e.g. through the use of\n" "${FOO:} syntax). \"check\" does not confirm or deny this for you." msgstr "" @@ -890,24 +928,31 @@ msgstr "Crea una nueva 📸 instantánea (respaldo)" msgid "" "Create a new version of a recipe.\n" "\n" -"These versions are then published on the Co-op Cloud recipe catalogue. These\n" +"These versions are then published on the Co-op Cloud recipe catalogue. " +"These\n" "versions take the following form:\n" "\n" " a.b.c+x.y.z\n" "\n" -"Where the \"a.b.c\" part is a semantic version determined by the maintainer. The\n" -"\"x.y.z\" part is the image tag of the recipe \"app\" service (the main container\n" +"Where the \"a.b.c\" part is a semantic version determined by the maintainer. " +"The\n" +"\"x.y.z\" part is the image tag of the recipe \"app\" service (the main " +"container\n" "which contains the software to be used, by naming convention).\n" "\n" "We maintain a semantic versioning scheme (\"a.b.c\") alongside the recipe\n" -"versioning scheme (\"x.y.z\") in order to maximise the chances that the nature of\n" +"versioning scheme (\"x.y.z\") in order to maximise the chances that the " +"nature of\n" "recipe updates are properly communicated. I.e. developers of an app might\n" -"publish a minor version but that might lead to changes in the recipe which are\n" +"publish a minor version but that might lead to changes in the recipe which " +"are\n" "major and therefore require intervention while doing the upgrade work.\n" "\n" "This command will publish your new release to git.coopcloud.tech. This\n" -"requires that you have permission to git push to these repositories and have\n" -"your SSH keys configured on your account. Enable ssh-agent and make sure to add\n" +"requires that you have permission to git push to these repositories and " +"have\n" +"your SSH keys configured on your account. Enable ssh-agent and make sure to " +"add\n" "your private key and enter your passphrase beforehand.\n" "\n" " eval `ssh-agent`\n" @@ -923,14 +968,20 @@ msgstr "" "Donde \"a.b.c\" es la versión semántica que decide el mantenedor. La parte \n" "\"x.y.z\" es la versión semántica oficial de la imagen de la aplicación .\n" "\n" -"Mantenemos versión semántica (\"a.b.c\") junto con la versión de la receta (\"x.y.z\") \n" -"para que sea más claro qué tipo de cambios trae cada actualización. Por ejemplo, \n" -"los desarrolladores de una aplicación pueden publicar una versión menor, pero \n" -"la receta podría necesitar cambios importantes que requieran atención al actualizar.\n" +"Mantenemos versión semántica (\"a.b.c\") junto con la versión de la receta " +"(\"x.y.z\") \n" +"para que sea más claro qué tipo de cambios trae cada actualización. Por " +"ejemplo, \n" +"los desarrolladores de una aplicación pueden publicar una versión menor, " +"pero \n" +"la receta podría necesitar cambios importantes que requieran atención al " +"actualizar.\n" "\n" "Este comando publicará tu nuevo release en git.coopcloud.tech. Necesitas \n" -"permiso para hacer git push en esos repositorios y tener tus claves SSH configuradas \n" -"en tu cuenta. Activa ssh-agent, añade tu clave privada y escribe tu contraseña antes \n" +"permiso para hacer git push en esos repositorios y tener tus claves SSH " +"configuradas \n" +"en tu cuenta. Activa ssh-agent, añade tu clave privada y escribe tu " +"contraseña antes \n" "de ejecutar:\n" "\n" " eval `ssh-agent`\n" @@ -947,21 +998,27 @@ msgid "" "This new app configuration is stored in your $ABRA_DIR directory under the\n" "appropriate server.\n" "\n" -"This command does not deploy your app for you. You will need to run \"abra app\n" +"This command does not deploy your app for you. You will need to run \"abra " +"app\n" "deploy \" to do so.\n" "\n" -"You can see what recipes are available (i.e. values for the [recipe] argument)\n" +"You can see what recipes are available (i.e. values for the [recipe] " +"argument)\n" "by running \"abra recipe ls\".\n" "\n" "Recipe commit hashes are supported values for \"[version]\".\n" "\n" -"Passing the \"--secrets/-S\" flag will automatically generate secrets for your\n" +"Passing the \"--secrets/-S\" flag will automatically generate secrets for " +"your\n" "app and store them encrypted at rest on the chosen target server. These\n" -"generated secrets are only visible at generation time, so please take care to\n" +"generated secrets are only visible at generation time, so please take care " +"to\n" "store them somewhere safe.\n" "\n" -"You can use the \"--pass/-P\" to store these generated passwords locally in a\n" -"pass store (see passwordstore.org for more). The pass command must be available\n" +"You can use the \"--pass/-P\" to store these generated passwords locally in " +"a\n" +"pass store (see passwordstore.org for more). The pass command must be " +"available\n" "on your $PATH." msgstr "" "Crea una nueva aplicación a partir de una receta.\n" @@ -971,13 +1028,15 @@ msgstr "" "Ojo: Este comando no despliega la aplicación.\n" "\n" "Si pasas la opción \"--secrets/-S\" se generarán automáticamente secretos \n" -"para tu aplicación y se guardarán cifrados en el servidor elegido. Esos secretos \n" +"para tu aplicación y se guardarán cifrados en el servidor elegido. Esos " +"secretos \n" "generados solo se muestran al momento de crearlos, así que guárdalos \n" "en un lugar seguro.\n" "\n" "Los hashes de commit de la receta son valores válidos para \"[version]\".\n" "\n" -"Puedes usar \"--pass/-P\" para guardar esas contraseñas generadas localmente \n" +"Puedes usar \"--pass/-P\" para guardar esas contraseñas generadas " +"localmente \n" "en un almacén pass (ver passwordstore.org). El comando pass debe estar \n" "disponible en tu $PATH." @@ -1011,9 +1070,11 @@ msgstr "📤 Despliega una 🚀 aplicación" msgid "" "Deploy an app.\n" "\n" -"This command supports chaos operations. Use \"--chaos/-C\" to deploy your recipe\n" +"This command supports chaos operations. Use \"--chaos/-C\" to deploy your " +"recipe\n" "checkout as-is. Recipe commit hashes are also supported as values for\n" -"\"[version]\". Please note, \"upgrade\"/\"rollback\" do not support chaos operations." +"\"[version]\". Please note, \"upgrade\"/\"rollback\" do not support chaos " +"operations." msgstr "" "Despliega una aplicación.\n" "\n" @@ -1036,7 +1097,8 @@ msgstr "⬇️ Descarga una 📸 instantánea" msgid "" "Downloads a backup.tar.gz to the current working directory.\n" "\n" -"\"--volumes/-v\" includes data contained in volumes alongide paths specified in\n" +"\"--volumes/-v\" includes data contained in volumes alongide paths specified " +"in\n" "\"backupbot.backup.path\" labels." msgstr "" "Descarga un backup.tar.gz al directorio de trabajo actual.\n" @@ -1080,21 +1142,28 @@ msgstr "NOMBRE GENERADO" msgid "" "Generate a new copy of the recipe catalogue.\n" "\n" -"N.B. this command **will** wipe local unstaged changes from your local recipes\n" -"if present. \"--chaos/-C\" on this command refers to the catalogue repository\n" -"(\"$ABRA_DIR/catalogue\") and not the recipes. Please take care not to lose your\n" +"N.B. this command **will** wipe local unstaged changes from your local " +"recipes\n" +"if present. \"--chaos/-C\" on this command refers to the catalogue " +"repository\n" +"(\"$ABRA_DIR/catalogue\") and not the recipes. Please take care not to lose " +"your\n" "changes.\n" "\n" "It is possible to generate new metadata for a single recipe by passing\n" "[recipe]. The existing local catalogue will be updated, not overwritten.\n" "\n" -"It is quite easy to get rate limited by Docker Hub when running this command.\n" -"If you have a Hub account you can \"docker login\" and Abra will automatically\n" +"It is quite easy to get rate limited by Docker Hub when running this " +"command.\n" +"If you have a Hub account you can \"docker login\" and Abra will " +"automatically\n" "use those details.\n" "\n" "Publish your new release to git.coopcloud.tech with \"--publish/-p\". This\n" -"requires that you have permission to git push to these repositories and have\n" -"your SSH keys configured on your account. Enable ssh-agent and make sure to add\n" +"requires that you have permission to git push to these repositories and " +"have\n" +"your SSH keys configured on your account. Enable ssh-agent and make sure to " +"add\n" "your private key and enter your passphrase beforehand.\n" "\n" " eval `ssh-agent`\n" @@ -1177,7 +1246,8 @@ msgstr "📋 Muestra el catálogo de 📜 recetas" #: pkg/recipe/git.go:465 #, c-format msgid "GetRecipeVersions encountered error for %s: %s (collected %d versions)" -msgstr "GetRecipeVersions encontró un error para %s: %s (recopiladas %d versiones)" +msgstr "" +"GetRecipeVersions encontró un error para %s: %s (recopiladas %d versiones)" #: cli/recipe/new.go:125 msgid "Git (user) name to do commits with" @@ -1327,17 +1397,22 @@ msgstr "⚙️ Administrar 📓 catálogo de 📜 recetas" msgid "" "Move an app to a differnt server.\n" "\n" -"This command will migrate an app config and copy secrets and volumes from the\n" -"old server to the new one. The app MUST be deployed on the old server before\n" +"This command will migrate an app config and copy secrets and volumes from " +"the\n" +"old server to the new one. The app MUST be deployed on the old server " +"before\n" "doing the move. The app will be undeployed from the current server but not\n" "deployed on the new server.\n" "\n" -"The \"tar\" command is required on both the old and new server as well as \"sudo\"\n" +"The \"tar\" command is required on both the old and new server as well as " +"\"sudo\"\n" "permissions. The \"rsync\" command is required on your local machine for\n" "transferring volumes.\n" "\n" -"Do not forget to update your DNS records. Don't panic, it might take a while\n" -"for the dust to settle after you move an app. If anything goes wrong, you can\n" +"Do not forget to update your DNS records. Don't panic, it might take a " +"while\n" +"for the dust to settle after you move an app. If anything goes wrong, you " +"can\n" "always move the app config file to the original server and deploy it there\n" "again. No data is removed from the old server.\n" "\n" @@ -1362,7 +1437,8 @@ msgstr "" "de vuelta al servidor original y desplegarlo otra vez. No se elimina \n" "ningún dato del servidor antiguo.\n" "\n" -"Usa \"--ejecucion-de-prueba/-r\" para ver qué secretos y volúmenes se moverán." +"Usa \"--ejecucion-de-prueba/-r\" para ver qué secretos y volúmenes se " +"moverán." #. translators: Short description for `app move` command #: cli/app/move.go:41 @@ -1419,7 +1495,8 @@ msgstr "🧹 Limpiar recursos en un 🕋 servidor" msgid "" "Prunes unused containers, networks, and dangling images.\n" "\n" -"Use \"--volumes/-v\" to remove volumes that are not associated with a deployed\n" +"Use \"--volumes/-v\" to remove volumes that are not associated with a " +"deployed\n" "app. This can result in unwanted data loss if not used carefully." msgstr "" "Elimina contenedores, redes e imágenes que no se usan.\n" @@ -1431,13 +1508,16 @@ msgstr "" #. translators: Short description for `app env pull` command #: cli/app/env.go:80 msgid "Pull app environment values from a deployed app" -msgstr "Obtener (pull) valores de entorno de la aplicación desde una aplicación desplegada" +msgstr "" +"Obtener (pull) valores de entorno de la aplicación desde una aplicación " +"desplegada" #: cli/app/env.go:81 msgid "" "Pull app environment values from a deploymed app.\n" "\n" -"A convenient command for when you've lost your app environment file or want to\n" +"A convenient command for when you've lost your app environment file or want " +"to\n" "synchronize your local app environment values with what is deployed live." msgstr "" "Extrae los valores de entorno de una aplicación desplegada.\n" @@ -1473,7 +1553,8 @@ msgid "" "Remove a managed server.\n" "\n" "Abra will remove the internal bookkeeping ($ABRA_DIR/servers/...) and\n" -"underlying client connection context. This server will then be lost in time,\n" +"underlying client connection context. This server will then be lost in " +"time,\n" "like tears in rain." msgstr "" "Eliminar un servidor gestionado.\n" @@ -1495,7 +1576,8 @@ msgstr "💀 Borrar completamente una 🚀 aplicación (local y remotamente)" #. translators: Short description for `recipe reset` command #: cli/recipe/reset.go:23 msgid "Remove all unstaged changes from recipe config" -msgstr "💀 Borra los cambios no actualizados de la configuración de la 📜 receta" +msgstr "" +"💀 Borra los cambios no actualizados de la configuración de la 📜 receta" #: cli/app/remove.go:29 msgid "" @@ -1504,16 +1586,19 @@ msgid "" "By default, it will prompt for confirmation before proceeding. All secrets,\n" "volumes and the local app env file will be deleted.\n" "\n" -"Only run this command when you are sure you want to completely remove the app\n" +"Only run this command when you are sure you want to completely remove the " +"app\n" "and all associated app data. This is a destructive action, Be Careful!\n" "\n" "If you would like to delete specific volumes or secrets, please use removal\n" "sub-commands under \"app volume\" and \"app secret\" instead.\n" "\n" -"Please note, if you delete the local app env file without removing volumes and\n" +"Please note, if you delete the local app env file without removing volumes " +"and\n" "secrets first, Abra will *not* be able to help you remove them afterwards.\n" "\n" -"To delete everything without prompt, use the \"--force/-f\" or the \"--no-input/n\"\n" +"To delete everything without prompt, use the \"--force/-f\" or the \"--no-" +"input/n\"\n" "flag." msgstr "" "┌──────────────────────────────────────────────────────────────┐\n" @@ -1568,11 +1653,14 @@ msgstr "💀 Elimina 📦 volúmen(es) asociados a una 🚀 aplicación" msgid "" "Remove volumes associated with an app.\n" "\n" -"The app in question must be undeployed before you try to remove volumes. See\n" +"The app in question must be undeployed before you try to remove volumes. " +"See\n" "\"abra app undeploy \" for more.\n" "\n" -"The command is interactive and will show a multiple select input which allows\n" -"you to make a seclection. Use the \"?\" key to see more help on navigating this\n" +"The command is interactive and will show a multiple select input which " +"allows\n" +"you to make a seclection. Use the \"?\" key to see more help on navigating " +"this\n" "interface.\n" "\n" "Passing \"--force/-f\" will select all volumes for removal. Be careful." @@ -1608,19 +1696,25 @@ msgstr "⏪ Revertir una 🚀 aplicación una versión anterior" #. translators: Short description for `app run` command #: cli/app/run.go:30 msgid "Run a command inside a service container" -msgstr "Crea un 🐋 contenedor temporal para ejecutar un 💻 comando especificado" +msgstr "" +"Crea un 🐋 contenedor temporal para ejecutar un 💻 comando especificado" #: cli/app/cmd.go:31 msgid "" "Run an app specific command.\n" "\n" -"These commands are bash functions, defined in the abra.sh of the recipe itself.\n" -"They can be run within the context of a service (e.g. app) or locally on your\n" +"These commands are bash functions, defined in the abra.sh of the recipe " +"itself.\n" +"They can be run within the context of a service (e.g. app) or locally on " +"your\n" "work station by passing \"--local/-l\".\n" "\n" -"N.B. If using the \"--\" style to pass arguments, flags (e.g. \"--local/-l\") must\n" -"be passed *before* the \"--\". It is possible to pass arguments without the \"--\"\n" -"as long as no dashes are present (i.e. \"foo\" works without \"--\", \"-foo\"\n" +"N.B. If using the \"--\" style to pass arguments, flags (e.g. \"--local/-" +"l\") must\n" +"be passed *before* the \"--\". It is possible to pass arguments without the " +"\"--\"\n" +"as long as no dashes are present (i.e. \"foo\" works without \"--\", \"-" +"foo\"\n" "does not)." msgstr "" "Ejecuta un comando específico dentro de la aplicación.\n" @@ -1771,8 +1865,10 @@ msgid "" "Arbitrary secret insertion is not supported. Secrets that are inserted must\n" "match those configured in the recipe beforehand.\n" "\n" -"This command can be useful when you want to manually generate secrets for an app\n" -"environment. Typically, you can let Abra generate them for you on app creation\n" +"This command can be useful when you want to manually generate secrets for an " +"app\n" +"environment. Typically, you can let Abra generate them for you on app " +"creation\n" "(see \"abra app new --secrets/-S\" for more)." msgstr "" "Este comando inserta un secreto en el entorno de una aplicación.\n" @@ -1823,17 +1919,22 @@ msgstr "" msgid "" "This command rolls an app back to a previous version.\n" "\n" -"Unlike \"abra app deploy\", chaos operations are not supported here. Only recipe\n" +"Unlike \"abra app deploy\", chaos operations are not supported here. Only " +"recipe\n" "versions are supported values for \"[version]\".\n" "\n" -"It is possible to \"--force/-f\" an downgrade if you want to re-deploy a specific\n" +"It is possible to \"--force/-f\" an downgrade if you want to re-deploy a " +"specific\n" "version.\n" "\n" -"Only the deployed version is consulted when trying to determine what downgrades\n" -"are available. The live deployment version is the \"source of truth\" in this\n" +"Only the deployed version is consulted when trying to determine what " +"downgrades\n" +"are available. The live deployment version is the \"source of truth\" in " +"this\n" "case. The stored .env version is not consulted.\n" "\n" -"A downgrade can be destructive, please ensure you have a copy of your app data\n" +"A downgrade can be destructive, please ensure you have a copy of your app " +"data\n" "beforehand. See \"abra app backup\" for more." msgstr "" "Este comando revierte una aplicación a una versión anterior.\n" @@ -1857,7 +1958,8 @@ msgstr "" msgid "" "This does not destroy any application data.\n" "\n" -"However, you should remain vigilant, as your swarm installation will consider\n" +"However, you should remain vigilant, as your swarm installation will " +"consider\n" "any previously attached volumes as eligible for pruning once undeployed.\n" "\n" "Passing \"--prune/-p\" does not remove those volumes." @@ -1883,7 +1985,8 @@ msgid "" " # Linux:\n" " $ abra autocomplete bash | sudo tee /etc/bash_completion.d/abra\n" " # macOS:\n" -" $ abra autocomplete bash | sudo tee $(brew --prefix)/etc/bash_completion.d/abra\n" +" $ abra autocomplete bash | sudo tee $(brew --prefix)/etc/bash_completion.d/" +"abra\n" "\n" "Zsh:\n" " # If shell autocompletion is not already enabled in your environment,\n" @@ -1919,7 +2022,8 @@ msgstr "" " # Linux:\n" " $ abra autocomplete bash | sudo tee /etc/bash_completion.d/abra\n" " # macOS:\n" -" $ abra autocomplete bash | sudo tee $(brew --prefix)/etc/bash_completion.d/abra\n" +" $ abra autocomplete bash | sudo tee $(brew --prefix)/etc/bash_completion.d/" +"abra\n" "\n" "Zsh:\n" " # Si la autocompletación no está habilitada, actívala una vez:\n" @@ -1970,7 +2074,8 @@ msgid "" "It will update the relevant compose file tags on the local file system.\n" "\n" "Some image tags cannot be parsed because they do not follow some sort of\n" -"semver-like convention. In this case, all possible tags will be listed and it\n" +"semver-like convention. In this case, all possible tags will be listed and " +"it\n" "is up to the end-user to decide.\n" "\n" "The command is interactive and will show a select input which allows you to\n" @@ -2002,8 +2107,10 @@ msgid "" "\n" "By default, the latest stable release is downloaded.\n" "\n" -"Use \"--rc/-r\" to install the latest release candidate. Please bear in mind that\n" -"it may contain absolutely catastrophic deal-breaker bugs. Thank you very much\n" +"Use \"--rc/-r\" to install the latest release candidate. Please bear in mind " +"that\n" +"it may contain absolutely catastrophic deal-breaker bugs. Thank you very " +"much\n" "for the testing efforts 💗" msgstr "" "Actualiza abra in situ con la última versión estable \n" @@ -2024,17 +2131,22 @@ msgstr "📨 Actualizar una 🚀 aplicación" msgid "" "Upgrade an app.\n" "\n" -"Unlike \"abra app deploy\", chaos operations are not supported here. Only recipe\n" +"Unlike \"abra app deploy\", chaos operations are not supported here. Only " +"recipe\n" "versions are supported values for \"[version]\".\n" "\n" -"It is possible to \"--force/-f\" an upgrade if you want to re-deploy a specific\n" +"It is possible to \"--force/-f\" an upgrade if you want to re-deploy a " +"specific\n" "version.\n" "\n" -"Only the deployed version is consulted when trying to determine what upgrades\n" -"are available. The live deployment version is the \"source of truth\" in this\n" +"Only the deployed version is consulted when trying to determine what " +"upgrades\n" +"are available. The live deployment version is the \"source of truth\" in " +"this\n" "case. The stored .env version is not consulted.\n" "\n" -"An upgrade can be destructive, please ensure you have a copy of your app data\n" +"An upgrade can be destructive, please ensure you have a copy of your app " +"data\n" "beforehand. See \"abra app backup\" for more." msgstr "" "Actualizar una aplicación.\n" @@ -2073,19 +2185,26 @@ msgid "" "Examples:\n" "{{.Example}}{{end}}{{if .HasAvailableSubCommands}}\n" "\n" -"Available Commands:{{range .Commands}}{{if (or .IsAvailableCommand (eq .Name \"help\"))}}\n" -" {{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}{{if .HasAvailableLocalFlags}}\n" +"Available Commands:{{range .Commands}}{{if (or .IsAvailableCommand (eq .Name " +"\"help\"))}}\n" +" {{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}" +"{{if .HasAvailableLocalFlags}}\n" "\n" "Flags:\n" -"{{.LocalFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if .HasAvailableInheritedFlags}}\n" +"{{.LocalFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}" +"{{if .HasAvailableInheritedFlags}}\n" "\n" "Global Flags:\n" -"{{.InheritedFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if .HasHelpSubCommands}}\n" +"{{.InheritedFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}" +"{{if .HasHelpSubCommands}}\n" "\n" -"Additional help topics:{{range .Commands}}{{if .IsAdditionalHelpTopicCommand}}\n" -" {{rpad .CommandPath .CommandPathPadding}} {{.Short}}{{end}}{{end}}{{end}}{{if .HasAvailableSubCommands}}\n" +"Additional help topics:{{range .Commands}}" +"{{if .IsAdditionalHelpTopicCommand}}\n" +" {{rpad .CommandPath .CommandPathPadding}} {{.Short}}{{end}}{{end}}{{end}}" +"{{if .HasAvailableSubCommands}}\n" "\n" -"Use \"{{.CommandPath}} [command] --help\" for more information about a command.{{end}}\n" +"Use \"{{.CommandPath}} [command] --help\" for more information about a " +"command.{{end}}\n" msgstr "" "COMANDO ABREVIADO\n" "─────────────────\n" @@ -2094,10 +2213,13 @@ msgstr "" "{{if .HasExample}}\n" "{{.Example}}{{end}}{{if .HasAvailableSubCommands}}\n" "\n" -"Comandos disponibles:{{range .Commands}}{{if (or .IsAvailableCommand (eq .Name \"help\"))}}\n" -" {{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}{{if .HasHelpSubCommands}}\n" +"Comandos disponibles:{{range .Commands}}{{if (or .IsAvailableCommand " +"(eq .Name \"help\"))}}\n" +" {{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}" +"{{if .HasHelpSubCommands}}\n" "\n" -"Temas de ayuda adicionales:{{range .Commands}}{{if .IsAdditionalHelpTopicCommand}}\n" +"Temas de ayuda adicionales:{{range .Commands}}" +"{{if .IsAdditionalHelpTopicCommand}}\n" " {{rpad .CommandPath .CommandPathPadding}} {{.Short}}{{end}}{{end}}{{end}}\n" "\n" " SINTAXIS\n" @@ -2112,11 +2234,16 @@ msgstr "" "{{.InheritedFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}\n" "\n" "{{if .HasAvailableSubCommands}}\n" -"Use \"{{.CommandPath}} [comando] --help\" para más información sobre un comando.{{end}}\n" +"Use \"{{.CommandPath}} [comando] --help\" para más información sobre un " +"comando.{{end}}\n" #: cli/recipe/fetch.go:28 -msgid "Using \"--force/-f\" Git syncs an existing recipe. It does not erase unstaged changes." -msgstr "Usando \"--force/-f\", Git sincroniza una receta existente. No borra los cambios no preparados." +msgid "" +"Using \"--force/-f\" Git syncs an existing recipe. It does not erase " +"unstaged changes." +msgstr "" +"Usando \"--force/-f\", Git sincroniza una receta existente. No borra los " +"cambios no preparados." #: cli/app/secret.go:117 msgid "VALUE" @@ -2147,20 +2274,27 @@ msgstr "[hijack] Fin de stdout" #, c-format msgid "" "\n" -"The following options are two types of initial semantic version that you can\n" -"pick for %s that will be published in the recipe catalogue. This follows the\n" +"The following options are two types of initial semantic version that you " +"can\n" +"pick for %s that will be published in the recipe catalogue. This follows " +"the\n" "semver convention (more on https://semver.org), here is a short cheatsheet\n" "\n" -" 0.1.0: development release, still hacking. when you make a major upgrade\n" -" you increment the \"y\" part (i.e. 0.1.0 -> 0.2.0) and only move to\n" +" 0.1.0: development release, still hacking. when you make a major " +"upgrade\n" +" you increment the \"y\" part (i.e. 0.1.0 -> 0.2.0) and only move " +"to\n" " using the \"x\" part when things are stable.\n" "\n" " 1.0.0: public release, assumed to be working. you already have a stable\n" -" and reliable deployment of this app and feel relatively confident\n" +" and reliable deployment of this app and feel relatively " +"confident\n" " about it.\n" "\n" -"If you want people to be able alpha test your current config for %s but don't\n" -"think it is quite reliable, go with 0.1.0 and people will know that things are\n" +"If you want people to be able alpha test your current config for %s but " +"don't\n" +"think it is quite reliable, go with 0.1.0 and people will know that things " +"are\n" "likely to change.\n" "\n" msgstr "" @@ -2171,7 +2305,8 @@ msgstr "" "Resumen rápido:\n" "\n" " 0.1.0: versión de desarrollo, todavía en prueba. Cuando hagas una \n" -"mejora mayor aumentas la parte \"y\" (p. ej. 0.1.0 → 0.2.0) y solo pasarás a \n" +"mejora mayor aumentas la parte \"y\" (p. ej. 0.1.0 → 0.2.0) y solo pasarás " +"a \n" "usar la parte \"x\" cuando las cosas estén estables.\n" "\n" " 1.0.0: versión pública, se asume que funciona. Ya tienes un \n" @@ -2189,7 +2324,8 @@ msgid "" "\n" "You need to make a decision about what kind of an update this new recipe\n" "version is. If someone else performs this upgrade, do they have to do some\n" -"migration work or take care of some breaking changes? This can be signaled in\n" +"migration work or take care of some breaking changes? This can be signaled " +"in\n" "the version you specify on the recipe deploy label and is called a semantic\n" "version.\n" "\n" @@ -2199,21 +2335,27 @@ msgid "" "\n" "Here is a semver cheat sheet (more on https://semver.org):\n" "\n" -" major: new features/bug fixes, backwards incompatible (e.g 1.0.0 -> 2.0.0).\n" -" the upgrade won't work without some preparation work and others need\n" +" major: new features/bug fixes, backwards incompatible (e.g 1.0.0 -> " +"2.0.0).\n" +" the upgrade won't work without some preparation work and others " +"need\n" " to take care when performing it. \"it could go wrong\".\n" "\n" -" minor: new features/bug fixes, backwards compatible (e.g. 0.1.0 -> 0.2.0).\n" -" the upgrade should Just Work and there are no breaking changes in\n" +" minor: new features/bug fixes, backwards compatible (e.g. 0.1.0 -> " +"0.2.0).\n" +" the upgrade should Just Work and there are no breaking changes " +"in\n" " the app and the recipe config. \"it should go fine\".\n" "\n" -" patch: bug fixes, backwards compatible (e.g. 0.0.1 -> 0.0.2). this upgrade\n" +" patch: bug fixes, backwards compatible (e.g. 0.0.1 -> 0.0.2). this " +"upgrade\n" " should also Just Work and is mostly to do with minor bug fixes\n" " and/or security patches. \"nothing to worry about\".\n" "\n" msgstr "" "\n" -"Necesitas decidir qué tipo de actualización es esta nueva versión de la receta. \n" +"Necesitas decidir qué tipo de actualización es esta nueva versión de la " +"receta. \n" "Si otra persona realiza esta actualización, ¿tendrá que hacer migraciones \n" "o atender cambios incompatibles? Eso se indica con la versión semántica \n" "que especifiques en la etiqueta de despliegue de la receta.\n" @@ -2224,17 +2366,25 @@ msgstr "" "\n" "Semántica de semver (más en https://semver.org):\n" "\n" -" major: nuevas funciones/arreglos, incompatible hacia atrás (p. ej. 1.0.0 -> 2.0.0).\n" -" la actualización no funcionará sin trabajo de preparación y otros deben\n" +" major: nuevas funciones/arreglos, incompatible hacia atrás (p. ej. 1.0.0 " +"-> 2.0.0).\n" +" la actualización no funcionará sin trabajo de preparación y otros " +"deben\n" " tener cuidado al realizarla. \"podría salir mal\".\n" "\n" -" minor: nuevas funciones/arreglos, compatible hacia atrás (p. ej. 0.1.0 -> 0.2.0).\n" -" la actualización debería funcionar sin problemas y no hay cambios\n" -" incompatibles en la app ni en la configuración de la receta. \"debería ir bien\".\n" +" minor: nuevas funciones/arreglos, compatible hacia atrás (p. ej. 0.1.0 " +"-> 0.2.0).\n" +" la actualización debería funcionar sin problemas y no hay " +"cambios\n" +" incompatibles en la app ni en la configuración de la receta. " +"\"debería ir bien\".\n" "\n" -" patch: correcciones de errores, compatible hacia atrás (p. ej. 0.0.1 -> 0.0.2).\n" -" esta actualización también debería funcionar sin problemas y trata\n" -" principalmente de arreglos menores y/o parches de seguridad. \"nada de qué preocuparse\".\n" +" patch: correcciones de errores, compatible hacia atrás (p. ej. 0.0.1 -> " +"0.0.2).\n" +" esta actualización también debería funcionar sin problemas y " +"trata\n" +" principalmente de arreglos menores y/o parches de seguridad. " +"\"nada de qué preocuparse\".\n" "\n" #: pkg/lint/recipe.go:203 @@ -2400,7 +2550,9 @@ msgstr "aplicacion [comando] [argumento] [opciones]" #: pkg/dns/dns.go:52 #, c-format msgid "app domain %s (%s) does not appear to resolve to app server %s (%s)?" -msgstr "¿El dominio de la aplicación %s (%s) no parece resolverse al servidor de la aplicación %s (%s)?" +msgstr "" +"¿El dominio de la aplicación %s (%s) no parece resolverse al servidor de la " +"aplicación %s (%s)?" #: cli/recipe/release.go:349 msgid "app service is missing image tag?" @@ -2556,7 +2708,9 @@ msgstr "cancelado" #: pkg/catalogue/catalogue.go:59 pkg/recipe/git.go:252 #, c-format msgid "cannot ensure %s is up-to-date, no git remotes configured" -msgstr "no se puede asegurar que %s esté actualizado, no hay remotos de git configurados" +msgstr "" +"no se puede asegurar que %s esté actualizado, no hay remotos de git " +"configurados" #: cli/app/config.go:43 pkg/app/app.go:52 #, c-format @@ -2570,17 +2724,24 @@ msgstr "no se puede obtener la etiqueta %s para %s" #: pkg/recipe/git.go:59 #, c-format -msgid "cannot redeploy previous chaos version (%s), did you mean to use \"--chaos\"?" -msgstr "no se puede redeplegar la versión anterior de caos (%s), ¿Era tu intención usar \"--caos\"?" +msgid "" +"cannot redeploy previous chaos version (%s), did you mean to use \"--chaos\"?" +msgstr "" +"no se puede redeplegar la versión anterior de caos (%s), ¿Era tu intención " +"usar \"--caos\"?" #: cli/app/deploy.go:389 #, c-format msgid "" -"cannot redeploy previous chaos version (%s), did you mean to use \"--chaos\"?\n" -" to return to a regular release, specify a release tag, commit SHA or use \"--latest\"" +"cannot redeploy previous chaos version (%s), did you mean to use \"--" +"chaos\"?\n" +" to return to a regular release, specify a release tag, commit SHA or " +"use \"--latest\"" msgstr "" -"no se puede redesplegar la versión anterior de caos (%s), ¿Era tu intención usar \"--caos\"? \n" -"para volver a un lanzamiento regular, especifica una etiqueta de lanzamiento, SHA de commit o agrega el comando \"--latest\"" +"no se puede redesplegar la versión anterior de caos (%s), ¿Era tu intención " +"usar \"--caos\"? \n" +"para volver a un lanzamiento regular, especifica una etiqueta de " +"lanzamiento, SHA de commit o agrega el comando \"--latest\"" #: pkg/dns/dns.go:38 pkg/dns/dns.go:47 #, c-format @@ -2589,7 +2750,8 @@ msgstr "no se puede resolver ipv4 para %s?" #: cli/recipe/release.go:130 msgid "cannot specify tag and bump type at the same time" -msgstr "no se puede especificar una etiqueta y un tipo de incremento al mismo tiempo" +msgstr "" +"no se puede especificar una etiqueta y un tipo de incremento al mismo tiempo" #: cli/app/secret.go:68 msgid "cannot use '[secret] [version]' and '--all' together" @@ -2670,7 +2832,9 @@ msgstr "caos" #: pkg/secret/secret.go:235 #, c-format -msgid "charset=bytes only supports encoding=base64, got encoding=%s for secret %s, defaulting to base64" +msgid "" +"charset=bytes only supports encoding=base64, got encoding=%s for secret %s, " +"defaulting to base64" msgstr "" #. translators: `app check` command @@ -2753,13 +2917,21 @@ msgstr "el comando %v no salió después de %v: stderr=%q" #: pkg/upstream/commandconn/commandconn.go:172 #, c-format -msgid "command %v has exited with %v, please make sure the URL is valid, and Docker 18.09 or later is installed on the remote host: stderr=%s" -msgstr "el comando %v ha salido con %v, por favor asegúrate de que la URL sea válida y que Docker 18.09 o posterior esté instalado en el host remoto: stderr=%s" +msgid "" +"command %v has exited with %v, please make sure the URL is valid, and Docker " +"18.09 or later is installed on the remote host: stderr=%s" +msgstr "" +"el comando %v ha salido con %v, por favor asegúrate de que la URL sea válida " +"y que Docker 18.09 o posterior esté instalado en el host remoto: stderr=%s" #. translators: `app command` command #: cli/app/cmd.go:27 -msgid "command [service | --local] [[args] [flags] | [flags] -- [args]]" -msgstr "ejecutar [servicio | --local] [[argumento] [opciones] | [opciones] -- [argumento]]" +msgid "" +"command [service | --local] [[args] [flags] | [flags] -- " +"[args]]" +msgstr "" +"ejecutar [servicio | --local] [[argumento] [opciones] " +"| [opciones] -- [argumento]]" #: pkg/upstream/commandconn/commandconn.go:239 #, c-format @@ -2843,12 +3015,15 @@ msgstr "tiempo de conexión agotado para %s" #: pkg/recipe/compose.go:191 #, c-format msgid "considering %s config(s) for label update" -msgstr "considerando la(s) configuración(es) %s para la actualización de etiquetas" +msgstr "" +"considerando la(s) configuración(es) %s para la actualización de etiquetas" #: pkg/recipe/compose.go:123 #, c-format msgid "considering %s config(s) for tag update" -msgstr "considerando la(s) configuración(es) %s para la actualización de etiquetas (tags)" +msgstr "" +"considerando la(s) configuración(es) %s para la actualización de etiquetas " +"(tags)" #: cli/app/undeploy.go:141 cli/server/prune.go:53 #, c-format @@ -3153,7 +3328,8 @@ msgstr "no solicitar un TTY" #: pkg/ssh/ssh.go:26 msgid "docker: is the daemon running / your user has docker permissions?" -msgstr "docker: ¿está corriendo el daemon / tu usuario tiene permisos de Docker?" +msgstr "" +"docker: ¿está corriendo el daemon / tu usuario tiene permisos de Docker?" #: cli/app/new.go:386 msgid "domain" @@ -3211,7 +3387,8 @@ msgstr "ejecución de prueba: no se creó etiqueta de git (%s)" #: cli/recipe/release.go:371 #, c-format msgid "dry run: not syncing label %s for recipe %s" -msgstr "ejecución de prueba: no se sincronizará la etiqueta %s para la receta %s" +msgstr "" +"ejecución de prueba: no se sincronizará la etiqueta %s para la receta %s" #: pkg/git/remote.go:15 #, c-format @@ -3249,11 +3426,14 @@ msgstr "habilitar ejecución de comandos pre/post-hook" #: cli/internal/validate.go:82 #, c-format msgid "ensure %s recipe compose.* files include \"version: '3.8'\"" -msgstr "asegúrate de que los archivos compose.* de la receta %s incluyan \"version: '3.8'\"" +msgstr "" +"asegúrate de que los archivos compose.* de la receta %s incluyan \"version: " +"'3.8'\"" #: pkg/lint/recipe.go:69 msgid "ensure 'version: \"3.8\"' in compose configs" -msgstr "asegúrate que 'version: \"3.8\"' esté en las configuraciones de compose" +msgstr "" +"asegúrate que 'version: \"3.8\"' esté en las configuraciones de compose" #: pkg/lint/recipe.go:163 msgid "ensure \"image: ...\" set on all services" @@ -3270,8 +3450,12 @@ msgid "ensuring env version %s" msgstr "asegurando la versión del entorno (env) %s" #: cli/recipe/upgrade.go:285 -msgid "enter / return to confirm, choose 'skip' to not upgrade this tag, vim mode is enabled" -msgstr "presiona \"enter\" para confirmar; escribe 'skip' para no actualizar esta etiqueta; modo vim habilitado" +msgid "" +"enter / return to confirm, choose 'skip' to not upgrade this tag, vim mode " +"is enabled" +msgstr "" +"presiona \"enter\" para confirmar; escribe 'skip' para no actualizar esta " +"etiqueta; modo vim habilitado" #. translators: `app env` command group #: cli/app/env.go:311 @@ -3384,12 +3568,14 @@ msgstr "🛑 error al hacer commit: %s" #: cli/app/move.go:167 #, c-format msgid "failed to copy %s from %s to local machine: output:%s err:%s" -msgstr "🛑 error al copiar %s desde %s a la máquina local: salida:%s errores:%s" +msgstr "" +"🛑 error al copiar %s desde %s a la máquina local: salida:%s errores:%s" #: cli/app/move.go:173 #, c-format msgid "failed to copy %s from local machine to %s: output:%s err:%s" -msgstr "🛑 error al copiar %s desde la máquina local a %s: salida:%s errores:%s" +msgstr "" +"🛑 error al copiar %s desde la máquina local a %s: salida:%s errores:%s" #: pkg/upstream/stack/stack.go:535 #, c-format @@ -3521,7 +3707,8 @@ msgstr "🛑 No se pudo obtener el último commit de %s: %s" #: pkg/upstream/stack/stack.go:472 #, c-format msgid "failed to retrieve registry auth for image %s: %s" -msgstr "🛑 No se pudo obtener la autenticación del registro para la imagen %s: %s" +msgstr "" +"🛑 No se pudo obtener la autenticación del registro para la imagen %s: %s" #: pkg/git/branch.go:68 #, c-format @@ -3662,7 +3849,9 @@ msgstr "catálogo de recetas generado: %s" #: cli/app/secret.go:145 #, c-format msgid "generated secrets %s shown again, please take note of them %s" -msgstr "los secretos generados %s; se muestran nuevamente, por favor toma nota de ellos %s" +msgstr "" +"los secretos generados %s; se muestran nuevamente, por favor toma nota de " +"ellos %s" #: pkg/app/compose.go:63 #, c-format @@ -3928,7 +4117,9 @@ msgstr "incrementar la parte de parche de la versión" #: pkg/git/init.go:50 #, c-format msgid "init committed all files for new git repo in %s" -msgstr "inicializado y comiteados todos los archivos para el nuevo repositorio git en %s" +msgstr "" +"inicializado y comiteados todos los archivos para el nuevo repositorio git " +"en %s" #: pkg/git/init.go:24 #, c-format @@ -3974,22 +4165,31 @@ msgstr "fuente de enlace inválida, la fuente no puede estar vacía" #: pkg/upstream/convert/service.go:813 #, c-format msgid "invalid credential spec: cannot specify both %s and %s" -msgstr "especificación de credencial inválida: no se pueden especificar ambos %s y %s" +msgstr "" +"especificación de credencial inválida: no se pueden especificar ambos %s y %s" #: pkg/upstream/convert/service.go:815 #, c-format msgid "invalid credential spec: cannot specify both %s, and %s" -msgstr "especificación de credencial inválida: no se pueden especificar tanto %s, como %s" +msgstr "" +"especificación de credencial inválida: no se pueden especificar tanto %s, " +"como %s" #: pkg/upstream/convert/service.go:834 #, c-format -msgid "invalid credential spec: spec specifies config %v, but no such config can be found" -msgstr "especificación de credencial inválida: la especificación indica la configuración %v, pero no se puede encontrar tal configuración" +msgid "" +"invalid credential spec: spec specifies config %v, but no such config can be " +"found" +msgstr "" +"especificación de credencial inválida: la especificación indica la " +"configuración %v, pero no se puede encontrar tal configuración" #: pkg/upstream/container/hijack.go:100 #, c-format msgid "invalid detach escape keys, using default: %s" -msgstr "teclas de escape de separación inválidas, utilizando el valor predeterminado: %s" +msgstr "" +"teclas de escape de separación inválidas, utilizando el valor " +"predeterminado: %s" #: pkg/lint/recipe.go:504 msgid "invalid lightweight tag detected" @@ -4176,7 +4376,8 @@ msgstr "maquina" #: cli/recipe/release.go:97 #, c-format msgid "main app service version for %s is empty?" -msgstr "¿la versión del servicio principal de la aplicación para %s está vacía?" +msgstr "" +"¿la versión del servicio principal de la aplicación para %s está vacía?" #: cli/internal/recipe.go:48 cli/internal/recipe.go:66 #: cli/internal/recipe.go:80 cli/recipe/release.go:631 @@ -4277,13 +4478,23 @@ msgstr "se necesitan 3 o 4 argumentos" #: pkg/upstream/stack/stack.go:352 #, c-format -msgid "network %q is declared as external, but could not be found. You need to create a swarm-scoped network before the stack is deployed, which you can do by running this on the server: docker network create -d overlay proxy" -msgstr "La red %q está declarada como externa, pero no se pudo encontrar. Debes crear una red con alcance swarm antes de desplegar el stack; puedes hacerlo en el servidor mismo con: docker network create -d overlay proxy" +msgid "" +"network %q is declared as external, but could not be found. You need to " +"create a swarm-scoped network before the stack is deployed, which you can do " +"by running this on the server: docker network create -d overlay proxy" +msgstr "" +"La red %q está declarada como externa, pero no se pudo encontrar. Debes " +"crear una red con alcance swarm antes de desplegar el stack; puedes hacerlo " +"en el servidor mismo con: docker network create -d overlay proxy" #: pkg/upstream/stack/stack.go:356 #, c-format -msgid "network %q is declared as external, but it is not in the right scope: %q instead of \"swarm\"" -msgstr "la red %q se declara como externa, pero no está en el alcance correcto: %q en lugar de \"swarm\"" +msgid "" +"network %q is declared as external, but it is not in the right scope: %q " +"instead of \"swarm\"" +msgstr "" +"la red %q se declara como externa, pero no está en el alcance correcto: %q " +"en lugar de \"swarm\"" #: cli/app/undeploy.go:148 cli/server/prune.go:60 #, c-format @@ -4323,7 +4534,8 @@ msgstr "secretos nuevos: %v" #: pkg/git/read.go:88 #, c-format msgid "no %s exists, not reading any global gitignore config" -msgstr "no existe %s, no se está leyendo ninguna configuración global de gitignore" +msgstr "" +"no existe %s, no se está leyendo ninguna configuración global de gitignore" #: pkg/git/read.go:130 #, c-format @@ -4393,7 +4605,9 @@ msgstr "no se proporcionó dominio" #: pkg/recipe/compose.go:248 msgid "no existing label found, automagic insertion not supported yet" -msgstr "no se encontró ninguna etiqueta existente, la inserción automágica no es compatible aún" +msgstr "" +"no se encontró ninguna etiqueta existente, la inserción automágica no es " +"compatible aún" #: cli/recipe/release.go:134 #, c-format @@ -4402,8 +4616,12 @@ msgstr "no se encontraron etiquetas de git para %s" #: cli/recipe/upgrade.go:182 #, c-format -msgid "no new versions available for %s, assuming %s is the latest (use -a/--all-tags to see all anyway)" -msgstr "no hay nuevas versiones disponibles para %s; asumiendo que %s es la más reciente (usa -a/--all-tags para ver todas)" +msgid "" +"no new versions available for %s, assuming %s is the latest (use -a/--all-" +"tags to see all anyway)" +msgstr "" +"no hay nuevas versiones disponibles para %s; asumiendo que %s es la más " +"reciente (usa -a/--all-tags para ver todas)" #: cli/internal/validate.go:64 #, c-format @@ -4526,7 +4744,9 @@ msgstr "no hay suficientes argumentos: %s" #: pkg/secret/secret.go:145 msgid "not generating app secrets, none enabled in recipe config" -msgstr "no se están generando secretos de la aplicación; ninguno habilitado en la configuración de la receta" +msgstr "" +"no se están generando secretos de la aplicación; ninguno habilitado en la " +"configuración de la receta" #: cli/internal/command.go:93 msgid "not requesting a remote TTY" @@ -4539,8 +4759,12 @@ msgstr "no se actualizará %s; se omite, como fue solicitado" #: cli/recipe/upgrade.go:244 #, c-format -msgid "not upgrading from %s to %s for %s, because the upgrade type is more serious than what user wants" -msgstr "no se actualizará %s a %s para %s, porque el tipo de actualización es más serio de lo que el usuario desea" +msgid "" +"not upgrading from %s to %s for %s, because the upgrade type is more serious " +"than what user wants" +msgstr "" +"no se actualizará %s a %s para %s, porque el tipo de actualización es más " +"serio de lo que el usuario desea" #: pkg/upstream/stack/remove.go:73 #, c-format @@ -4671,7 +4895,9 @@ msgstr "por favor, selecciona una versión anterior (downgrade) (versión: %s):" #: cli/app/rollback.go:273 #, c-format msgid "please select a downgrade (version: %s, chaos: %s):" -msgstr "por favor, selecciona una versión anterior (downgrade) (versión: %s, caos: %s):" +msgstr "" +"por favor, selecciona una versión anterior (downgrade) (versión: %s, caos: " +"%s):" #: cli/app/upgrade.go:313 #, c-format @@ -5123,9 +5349,10 @@ msgstr "versiones recuperadas del repositorio local de recetas" #: pkg/upstream/stack/stack.go:468 #, c-format msgid "retrieving docker auth token: failed create docker cli: %s" -msgstr "recuperando token de autenticación de Docker: error al crear docker cli: %s" +msgstr "" +"recuperando token de autenticación de Docker: error al crear docker cli: %s" -#: pkg/recipe/recipe.go:659 +#: pkg/recipe/recipe.go:656 msgid "retrieving recipes" msgstr "obteniendo 📜 recetas" @@ -5184,7 +5411,9 @@ msgstr "regla" #. translators: `app run` command #: cli/app/run.go:27 msgid "run [[args] [flags] | [flags] -- [args]]" -msgstr "lanzar [[argumento] [opciones] | [opciones] -- [argumento]]" +msgstr "" +"lanzar [[argumento] [opciones] | " +"[opciones] -- [argumento]]" #: cli/app/run.go:120 msgid "run command as user" @@ -5274,7 +5503,9 @@ msgstr "secreto [comando] [argumento] [opciones]" #: cli/app/secret.go:295 msgid "secret data not provided on command-line or stdin, prompting" -msgstr "datos del secreto no proporcionados en la línea de comandos o stdin, solicitando" +msgstr "" +"datos del secreto no proporcionados en la línea de comandos o stdin, " +"solicitando" #: pkg/upstream/convert/service.go:72 #, c-format @@ -5308,7 +5539,8 @@ msgstr "los secretos se muestran %s nuevamente, por favor guárdalos %s" #: cli/app/deploy.go:307 #, c-format msgid "selected latest recipe version: %s (from %d available versions)" -msgstr "versión de receta más reciente seleccionada: %s (de %d versiones disponibles)" +msgstr "" +"versión de receta más reciente seleccionada: %s (de %d versiones disponibles)" #: cli/app/new.go:125 #, c-format @@ -5324,7 +5556,8 @@ msgstr "servidor" #: pkg/app/app.go:301 #, c-format msgid "server %s doesn't exist? Run \"abra server ls\" to check" -msgstr "el servidor %s no existe? Ejecuta \"abra servidor listar\" para verificar" +msgstr "" +"el servidor %s no existe? Ejecuta \"abra servidor listar\" para verificar" #. translators: `server` command group #: cli/server/server.go:17 @@ -5352,7 +5585,8 @@ msgstr "falta contexto del servidor, la creación de contexto falló: %s" #: pkg/client/client.go:58 #, c-format msgid "server missing context, run \"abra server add %s\"?" -msgstr "falta contexto del servidor, ¿probaste con \"abra servidor agregar %s\"?" +msgstr "" +"falta contexto del servidor, ¿probaste con \"abra servidor agregar %s\"?" #: pkg/client/client.go:47 #, c-format @@ -5381,13 +5615,18 @@ msgstr "serverAdd: cleanUp: no se pueden listar los archivos en %s: %s" #: cli/recipe/upgrade.go:227 #, c-format -msgid "service %s is at version %s, but pinned to %s, please correct your compose.yml file manually!" -msgstr "el servicio %s está en la versión %s, pero fijado a %s, ¡por favor corrige manualmente tu archivo compose.yml!" +msgid "" +"service %s is at version %s, but pinned to %s, please correct your " +"compose.yml file manually!" +msgstr "" +"el servicio %s está en la versión %s, pero fijado a %s, ¡por favor corrige " +"manualmente tu archivo compose.yml!" #: cli/recipe/upgrade.go:223 #, c-format msgid "service %s, image %s pinned to %s, no compatible upgrade found" -msgstr "servicio %s, imagen %s fijada a %s, no se encontró actualización compatible" +msgstr "" +"servicio %s, imagen %s fijada a %s, no se encontró actualización compatible" #: cli/app/restore.go:113 msgid "services" @@ -5433,7 +5672,8 @@ msgstr "gravedad" #: cli/app/deploy.go:457 cli/app/rollback.go:380 cli/app/upgrade.go:497 msgid "show all configs & images, including unchanged ones" -msgstr "mostrar todas las configuraciones e imágenes, incluidas las no cambiadas" +msgstr "" +"mostrar todas las configuraciones e imágenes, incluidas las no cambiadas" #: cli/app/backup.go:273 msgid "show all paths" @@ -5534,7 +5774,8 @@ msgstr "omitiendo la etiqueta %s: configuración de compose inválida: %s" #: pkg/recipe/git.go:438 #, c-format msgid "skipping tag %s: invalid image reference in service %s: %s" -msgstr "omitiendo la etiqueta %s: referencia de imagen inválida en el servicio %s: %s" +msgstr "" +"omitiendo la etiqueta %s: referencia de imagen inválida en el servicio %s: %s" #: pkg/app/app.go:697 #, c-format @@ -5598,11 +5839,17 @@ msgstr "" #: cli/recipe/release.go:554 msgid "ssh-agent not found. see \"abra recipe release --help\" and try again" -msgstr "ssh-agent no encontrado. consulta \"abra receta publicar --ayuda\" y vuelve a intentarlo" +msgstr "" +"ssh-agent no encontrado. consulta \"abra receta publicar --ayuda\" y vuelve " +"a intentarlo" #: cli/catalogue/catalogue.go:98 -msgid "ssh: SSH_AUTH_SOCK missing, --publish/-p will fail. see \"abra catalogue generate --help\"" -msgstr "ssh: falta SSH_AUTH_SOCK, --publish/-p fallará. consulta \"abra catalogo regenerar --ayuda\"" +msgid "" +"ssh: SSH_AUTH_SOCK missing, --publish/-p will fail. see \"abra catalogue " +"generate --help\"" +msgstr "" +"ssh: falta SSH_AUTH_SOCK, --publish/-p fallará. consulta \"abra catalogo " +"regenerar --ayuda\"" #: cli/app/list.go:295 cli/recipe/list.go:45 msgid "status" @@ -5868,12 +6115,16 @@ msgstr "no se pudo determinar el tipo de receta a partir de %s, env: %v" #: cli/app/env.go:106 #, c-format msgid "unable to determine server of app %s, please pass --server/-s" -msgstr "no se pudo determinar el servidor de la aplicación %s; agrega al comando --server/-s" +msgstr "" +"no se pudo determinar el servidor de la aplicación %s; agrega al comando --" +"server/-s" #: cli/recipe/upgrade.go:252 #, c-format msgid "unable to determine versioning semantics of %s, listing all tags" -msgstr "no se pudieron determinar las reglas de versionado de %s; listando todas las etiquetas" +msgstr "" +"no se pudieron determinar las reglas de versionado de %s; listando todas las " +"etiquetas" #: cli/recipe/fetch.go:71 #, c-format @@ -5948,7 +6199,8 @@ msgstr "no se pudo abrir el work tree de %s: %s" #: cli/recipe/upgrade.go:159 #, c-format msgid "unable to parse %s, error was: %s, skipping upgrade for %s" -msgstr "no se pudo analizar %s, el error fue: %s, omitiendo la actualización para %s" +msgstr "" +"no se pudo analizar %s, el error fue: %s, omitiendo la actualización para %s" #: pkg/recipe/compose.go:153 #, c-format @@ -5993,7 +6245,9 @@ msgstr "no se pudieron leer los remotos en %s: %s" #: cli/recipe/upgrade.go:153 #, c-format msgid "unable to read tag for image %s, is it missing? skipping upgrade for %s" -msgstr "no se pudo leer la etiqueta de la imagen %s; ¿quizás falta? omitiendo actualización para %s" +msgstr "" +"no se pudo leer la etiqueta de la imagen %s; ¿quizás falta? omitiendo " +"actualización para %s" #: cli/app/move.go:210 #, c-format @@ -6008,7 +6262,8 @@ msgstr "no se pudo eliminar el remoto predeterminado en %s: %s" #: cli/app/volume.go:158 #, c-format msgid "unable to remove volume: no volume with name '%s'?" -msgstr "no se pudo eliminar el volumen: ¿no existe un volumen con el nombre '%s'?" +msgstr "" +"no se pudo eliminar el volumen: ¿no existe un volumen con el nombre '%s'?" #: cli/app/secret.go:135 cli/app/secret.go:515 cli/recipe/list.go:83 #: cli/recipe/version.go:113 cli/server/list.go:88 @@ -6291,8 +6546,10 @@ msgstr "versión %s guardada en %s.env" #: cli/app/deploy.go:126 #, c-format -msgid "version '%s' appears to be a chaos commit, but --chaos/-C was not provided" -msgstr "la versión '%s' parece ser un commit de caos, pero no se utilizó --chaos/-C" +msgid "" +"version '%s' appears to be a chaos commit, but --chaos/-C was not provided" +msgstr "" +"la versión '%s' parece ser un commit de caos, pero no se utilizó --chaos/-C" #: pkg/recipe/recipe.go:200 #, c-format @@ -6340,7 +6597,8 @@ msgstr "versión: tomando la versión desde el archivo .env: %s" #: cli/app/deploy.go:379 #, c-format msgid "version: taking version from cli arg: %s" -msgstr "versión: tomando la versión desde el argumento de la línea de comandos: %s" +msgstr "" +"versión: tomando la versión desde el argumento de la línea de comandos: %s" #. translators: `recipe versions` command #: cli/recipe/version.go:23 @@ -6527,21 +6785,31 @@ msgstr "{nombre: %s, " #, c-format #~ msgid "latest git tag (%s) and synced label (%s) are the same?" -#~ msgstr "¿la última etiqueta de git (%s) y la etiqueta sincronizada (%s) son las mismas?" +#~ msgstr "" +#~ "¿la última etiqueta de git (%s) y la etiqueta sincronizada (%s) son las " +#~ "mismas?" #~ msgid "no -p/--publish passed, not publishing" #~ msgstr "no pasó -p/--publish, no se publicará" #, c-format -#~ msgid "no tag specified and no previous tag available for %s, assuming initial release" -#~ msgstr "no se especificó etiqueta y no hay etiqueta previa disponible para %s; asumiendo lanzamiento inicial" +#~ msgid "" +#~ "no tag specified and no previous tag available for %s, assuming initial " +#~ "release" +#~ msgstr "" +#~ "no se especificó etiqueta y no hay etiqueta previa disponible para %s; " +#~ "asumiendo lanzamiento inicial" #, c-format #~ msgid "please fix your synced label for %s and re-run this command" -#~ msgstr "por favor, corrige tu etiqueta sincronizada para %s y vuelve a ejecutar este comando" +#~ msgstr "" +#~ "por favor, corrige tu etiqueta sincronizada para %s y vuelve a ejecutar " +#~ "este comando" #~ msgid "previous git tags detected, assuming new semver release" -#~ msgstr "se detectaron etiquetas git anteriores, asumiendo un nuevo lanzamiento semver" +#~ msgstr "" +#~ "se detectaron etiquetas git anteriores, asumiendo un nuevo lanzamiento " +#~ "semver" #~ msgid "publish new release?" #~ msgstr "¿publicar nueva versión?" @@ -6550,8 +6818,12 @@ msgstr "{nombre: %s, " #~ msgstr "sincronizar [version] [flags]" #, c-format -#~ msgid "unable to read version for %s from synced label. Did you try running \"abra recipe sync %s\" already?" -#~ msgstr "no se pudo leer la versión de %s desde la etiqueta sincronizada. ¿Ya ejecutaste \"abra receta sincronizar %s\"?" +#~ msgid "" +#~ "unable to read version for %s from synced label. Did you try running " +#~ "\"abra recipe sync %s\" already?" +#~ msgstr "" +#~ "no se pudo leer la versión de %s desde la etiqueta sincronizada. ¿Ya " +#~ "ejecutaste \"abra receta sincronizar %s\"?" #, c-format #~ msgid "use %s as the new version?" From e8a494d9f465b3c5cb6a55b16e36d9b3b2709cb4 Mon Sep 17 00:00:00 2001 From: Weblate Date: Thu, 15 Oct 2026 02:55:54 +0000 Subject: [PATCH 7/8] chore: update translation files Updated by "Update PO files to match POT (msgmerge)" add-on in Weblate. Translation: Co-op Cloud/abra Translate-URL: https://translate.coopcloud.tech/projects/co-op-cloud/abra/ --- pkg/i18n/locales/es.po | 700 +++++++++++++---------------------------- 1 file changed, 214 insertions(+), 486 deletions(-) diff --git a/pkg/i18n/locales/es.po b/pkg/i18n/locales/es.po index 56e461a0..82b85579 100644 --- a/pkg/i18n/locales/es.po +++ b/pkg/i18n/locales/es.po @@ -5,8 +5,7 @@ msgstr "" "POT-Creation-Date: 2026-10-14 10:32-0400\n" "PO-Revision-Date: 2026-09-01 22:35+0000\n" "Last-Translator: chasqui \n" -"Language-Team: Spanish \n" +"Language-Team: Spanish \n" "Language: es\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -101,8 +100,7 @@ msgstr "" " abra aplicacion secreto insertar 1312.net my_secret v1 secreto.txt -f\n" "\n" " # insertar secreto desde stdin\n" -" echo \"mmySuperSecret\" | abra aplicacion secreto insertar 1312.net " -"my_secret v1" +" echo \"mmySuperSecret\" | abra aplicacion secreto insertar 1312.net my_secret v1" #: cli/app/list.go:54 msgid "" @@ -161,8 +159,7 @@ msgstr "" " abra aplicacion ejecutar 1312.net db comando_argumento --usuario bar\n" "\n" " # Para ejecutar directamente el comando sin abrir bash con \"--\"\n" -" abra aplicacion ejecutar 1312.net db comando_argumentos --usuario bar -- " -"foo -vvv\n" +" abra aplicacion ejecutar 1312.net db comando_argumentos --usuario bar -- foo -vvv\n" "\n" " # Omitir el servicio si usas \"--local/-l\"\n" " abra aplicacion ejecutar 1312.net comando --local" @@ -175,8 +172,7 @@ msgid "" " # pull lost app .env file\n" " abra app env pull my.gitea.net --server 1312.net" msgstr "" -" # Obtener el archivo de entorno (.env) y sobrescribir modificaciones " -"locales\n" +" # Obtener el archivo de entorno (.env) y sobrescribir modificaciones locales\n" " abra aplicacion entorno obtener 1312.net --force\n" "\n" " # Extraer el archivo .env perdido de la aplicación\n" @@ -416,12 +412,8 @@ msgstr "%s se ha escalado a 1" #: pkg/catalogue/catalogue.go:40 #, c-format -msgid "" -"%s has locally unstaged changes? please commit/remove your changes before " -"proceeding" -msgstr "" -"¿%s tiene cambios no commiteados localmente? Por favor, commitea/elimina tus " -"cambios antes de continuar" +msgid "%s has locally unstaged changes? please commit/remove your changes before proceeding" +msgstr "¿%s tiene cambios no commiteados localmente? Por favor, commitea/elimina tus cambios antes de continuar" #: cli/internal/recipe.go:114 #, c-format @@ -441,9 +433,7 @@ msgstr "%s no tiene secretos para generar, omitiendo..." #: pkg/recipe/compose.go:107 #, c-format msgid "%s has no version label? try running \"abra recipe sync %s\" first?" -msgstr "" -"¿%s no tiene etiqueta de versión? ¿intentaste ejecutar \"abra receta " -"sincronizar %s\" primero?" +msgstr "¿%s no tiene etiqueta de versión? ¿intentaste ejecutar \"abra receta sincronizar %s\" primero?" #: pkg/secret/pass.go:29 #, c-format @@ -544,18 +534,13 @@ msgstr "%s eliminado del almacén de contraseñas" #: cli/app/new.go:224 #, c-format -msgid "" -"%s requires secret generation before deploy, run \"abra app secret generate " -"%s --all\"" -msgstr "" -"%s requiere generación de secretos antes del despliegue, ejecuta \"abra " -"aplicacion secreto generar %s --todos\"" +msgid "%s requires secret generation before deploy, run \"abra app secret generate %s --all\"" +msgstr "%s requiere generación de secretos antes del despliegue, ejecuta \"abra aplicacion secreto generar %s --todos\"" #: cli/app/new.go:228 #, c-format msgid "%s requires secret insertion before deploy (#generate=false)" -msgstr "" -"%s requiere inserción de secretos antes del despliegue (#generate=false)" +msgstr "%s requiere inserción de secretos antes del despliegue (#generate=false)" #: cli/app/new.go:155 #, c-format @@ -703,12 +688,8 @@ msgid "'%s' is not a known version for %s" msgstr "'%s' no es una versión conocida para %s" #: cli/app/volume.go:175 -msgid "" -"'x' indicates selected, enter / return to confirm, ctrl-c to exit, vim mode " -"is enabled" -msgstr "" -"'x' indica seleccionado, presiona Enter para confirmar, Ctrl-C para salir, " -"el modo vim está activado" +msgid "'x' indicates selected, enter / return to confirm, ctrl-c to exit, vim mode is enabled" +msgstr "'x' indica seleccionado, presiona Enter para confirmar, Ctrl-C para salir, el modo vim está activado" #: cli/app/cmd.go:125 #, c-format @@ -732,35 +713,27 @@ msgstr "Se utiliza una plantilla de receta gestionada por la comunidad." msgid "" "A recipe is a blueprint for an app.\n" "\n" -"It is a bunch of config files which describe how to deploy and maintain an " -"app.\n" +"It is a bunch of config files which describe how to deploy and maintain an app.\n" "Recipes are maintained by the Co-op Cloud community and you can use Abra to\n" "read them, deploy them and create apps for you.\n" "\n" -"Anyone who uses a recipe can become a maintainer. Maintainers typically " -"make\n" -"sure the recipe is in good working order and the config upgraded in a " -"timely\n" +"Anyone who uses a recipe can become a maintainer. Maintainers typically make\n" +"sure the recipe is in good working order and the config upgraded in a timely\n" "manner." msgstr "" "Una receta son las instrucciones para crear una aplicación.\n" "\n" -"Es un conjunto de archivos de configuración, que describen cómo desplegar y " -"mantener una aplicación.\n" -"Las recetas son mantenidas por la comunidad Co-op Cloud y puedes usar Abra " -"para\n" +"Es un conjunto de archivos de configuración, que describen cómo desplegar y mantener una aplicación.\n" +"Las recetas son mantenidas por la comunidad Co-op Cloud y puedes usar Abra para\n" "leerlas, desplegarlas y crear aplicaciones para ti.\n" "\n" -"Cualquiera que use una receta puede convertirse en mantenedor. Los " -"mantenedores típicamente se\n" -"aseguran de que la receta funcione correctamente y de que la configuración " -"se actualice de manera oportuna." +"Cualquiera que use una receta puede convertirse en mantenedor. Los mantenedores típicamente se\n" +"aseguran de que la receta funcione correctamente y de que la configuración se actualice de manera oportuna." #: cli/app/remove.go:57 #, c-format msgid "ALERTA ALERTA: deleting %s data and config (local/remote)" -msgstr "" -"ALERTA ALERTA: eliminando los datos y la configuración de %s (local/remoto)" +msgstr "ALERTA ALERTA: eliminando los datos y la configuración de %s (local/remoto)" #. translators: Short description for `server add` command #: cli/server/add.go:30 @@ -772,8 +745,7 @@ msgid "" "Add a new server to your configuration so that it can be managed by Abra.\n" "\n" "Abra relies on the standard SSH command-line and ~/.ssh/config for client\n" -"connection details. You must configure an entry per-host in your ~/.ssh/" -"config\n" +"connection details. You must configure an entry per-host in your ~/.ssh/config\n" "for each server:\n" "\n" " Host 1312.net 1312\n" @@ -782,19 +754,15 @@ msgid "" " Port 12345\n" " IdentityFile ~/.ssh/antifa@somewhere\n" "\n" -"If \"--local\" is passed, then Abra assumes that the current local server " -"is\n" -"intended as the target server. This is useful when you want to have your " -"entire\n" +"If \"--local\" is passed, then Abra assumes that the current local server is\n" +"intended as the target server. This is useful when you want to have your entire\n" "Co-op Cloud config located on the server itself, and not on your local\n" "developer machine. The domain is then set to \"default\"." msgstr "" -"Agrega un servidor nuevo a tu configuración para que pueda ser gestionado " -"por Abra.\n" +"Agrega un servidor nuevo a tu configuración para que pueda ser gestionado por Abra.\n" "\n" "Para agregar servidor Remoto:\n" -"Abra usa el software SSH y de la carpeta ~/.ssh/config obtiene los datos de " -"conexión al servidor. \n" +"Abra usa el software SSH y de la carpeta ~/.ssh/config obtiene los datos de conexión al servidor. \n" "Debes configurar una entrada en ~/.ssh/config para cada servidor:\n" "\n" " Host 1312.net 1312\n" @@ -804,15 +772,13 @@ msgstr "" " IdentityFile ~/.ssh/antifa@1312.net\n" "\n" "Para agregar servidor local;\n" -"Si agregas \"--local\", Abra asumirá que la computadora local actual es el " -"servidor que quieres agregar. \n" +"Si agregas \"--local\", Abra asumirá que la computadora local actual es el servidor que quieres agregar. \n" "Esto es útil cuando quieres tener Abra en el mismo servidor. \n" "El dominio se establece entonces como \"default\"." #: cli/app/labels.go:33 msgid "Both local recipe and live deployment labels are shown." -msgstr "" -"Se muestran tanto la receta local como las etiquetas del despliegue en vivo." +msgstr "Se muestran tanto la receta local como las etiquetas del despliegue en vivo." #: cli/app/backup.go:319 cli/app/backup.go:335 cli/app/check.go:95 #: cli/app/cmd.go:285 cli/app/cp.go:385 cli/app/deploy.go:415 @@ -876,16 +842,12 @@ msgstr "💕 Clona la 📜 receta(s) en local" msgid "" "Compare env vars in both the app \".env\" and recipe \".env.sample\" file.\n" "\n" -"The goal is to ensure that recipe \".env.sample\" env vars are defined in " -"your\n" -"app \".env\" file. Only env var definitions in the \".env.sample\" which " -"are\n" -"uncommented, e.g. \"FOO=bar\" are checked. If an app \".env\" file does not " -"include\n" +"The goal is to ensure that recipe \".env.sample\" env vars are defined in your\n" +"app \".env\" file. Only env var definitions in the \".env.sample\" which are\n" +"uncommented, e.g. \"FOO=bar\" are checked. If an app \".env\" file does not include\n" "these env vars, then \"check\" will complain.\n" "\n" -"Recipe maintainers may or may not provide defaults for env vars within " -"their\n" +"Recipe maintainers may or may not provide defaults for env vars within their\n" "recipes regardless of commenting or not (e.g. through the use of\n" "${FOO:} syntax). \"check\" does not confirm or deny this for you." msgstr "" @@ -928,31 +890,24 @@ msgstr "Crea una nueva 📸 instantánea (respaldo)" msgid "" "Create a new version of a recipe.\n" "\n" -"These versions are then published on the Co-op Cloud recipe catalogue. " -"These\n" +"These versions are then published on the Co-op Cloud recipe catalogue. These\n" "versions take the following form:\n" "\n" " a.b.c+x.y.z\n" "\n" -"Where the \"a.b.c\" part is a semantic version determined by the maintainer. " -"The\n" -"\"x.y.z\" part is the image tag of the recipe \"app\" service (the main " -"container\n" +"Where the \"a.b.c\" part is a semantic version determined by the maintainer. The\n" +"\"x.y.z\" part is the image tag of the recipe \"app\" service (the main container\n" "which contains the software to be used, by naming convention).\n" "\n" "We maintain a semantic versioning scheme (\"a.b.c\") alongside the recipe\n" -"versioning scheme (\"x.y.z\") in order to maximise the chances that the " -"nature of\n" +"versioning scheme (\"x.y.z\") in order to maximise the chances that the nature of\n" "recipe updates are properly communicated. I.e. developers of an app might\n" -"publish a minor version but that might lead to changes in the recipe which " -"are\n" +"publish a minor version but that might lead to changes in the recipe which are\n" "major and therefore require intervention while doing the upgrade work.\n" "\n" "This command will publish your new release to git.coopcloud.tech. This\n" -"requires that you have permission to git push to these repositories and " -"have\n" -"your SSH keys configured on your account. Enable ssh-agent and make sure to " -"add\n" +"requires that you have permission to git push to these repositories and have\n" +"your SSH keys configured on your account. Enable ssh-agent and make sure to add\n" "your private key and enter your passphrase beforehand.\n" "\n" " eval `ssh-agent`\n" @@ -968,20 +923,14 @@ msgstr "" "Donde \"a.b.c\" es la versión semántica que decide el mantenedor. La parte \n" "\"x.y.z\" es la versión semántica oficial de la imagen de la aplicación .\n" "\n" -"Mantenemos versión semántica (\"a.b.c\") junto con la versión de la receta " -"(\"x.y.z\") \n" -"para que sea más claro qué tipo de cambios trae cada actualización. Por " -"ejemplo, \n" -"los desarrolladores de una aplicación pueden publicar una versión menor, " -"pero \n" -"la receta podría necesitar cambios importantes que requieran atención al " -"actualizar.\n" +"Mantenemos versión semántica (\"a.b.c\") junto con la versión de la receta (\"x.y.z\") \n" +"para que sea más claro qué tipo de cambios trae cada actualización. Por ejemplo, \n" +"los desarrolladores de una aplicación pueden publicar una versión menor, pero \n" +"la receta podría necesitar cambios importantes que requieran atención al actualizar.\n" "\n" "Este comando publicará tu nuevo release en git.coopcloud.tech. Necesitas \n" -"permiso para hacer git push en esos repositorios y tener tus claves SSH " -"configuradas \n" -"en tu cuenta. Activa ssh-agent, añade tu clave privada y escribe tu " -"contraseña antes \n" +"permiso para hacer git push en esos repositorios y tener tus claves SSH configuradas \n" +"en tu cuenta. Activa ssh-agent, añade tu clave privada y escribe tu contraseña antes \n" "de ejecutar:\n" "\n" " eval `ssh-agent`\n" @@ -998,27 +947,21 @@ msgid "" "This new app configuration is stored in your $ABRA_DIR directory under the\n" "appropriate server.\n" "\n" -"This command does not deploy your app for you. You will need to run \"abra " -"app\n" +"This command does not deploy your app for you. You will need to run \"abra app\n" "deploy \" to do so.\n" "\n" -"You can see what recipes are available (i.e. values for the [recipe] " -"argument)\n" +"You can see what recipes are available (i.e. values for the [recipe] argument)\n" "by running \"abra recipe ls\".\n" "\n" "Recipe commit hashes are supported values for \"[version]\".\n" "\n" -"Passing the \"--secrets/-S\" flag will automatically generate secrets for " -"your\n" +"Passing the \"--secrets/-S\" flag will automatically generate secrets for your\n" "app and store them encrypted at rest on the chosen target server. These\n" -"generated secrets are only visible at generation time, so please take care " -"to\n" +"generated secrets are only visible at generation time, so please take care to\n" "store them somewhere safe.\n" "\n" -"You can use the \"--pass/-P\" to store these generated passwords locally in " -"a\n" -"pass store (see passwordstore.org for more). The pass command must be " -"available\n" +"You can use the \"--pass/-P\" to store these generated passwords locally in a\n" +"pass store (see passwordstore.org for more). The pass command must be available\n" "on your $PATH." msgstr "" "Crea una nueva aplicación a partir de una receta.\n" @@ -1028,15 +971,13 @@ msgstr "" "Ojo: Este comando no despliega la aplicación.\n" "\n" "Si pasas la opción \"--secrets/-S\" se generarán automáticamente secretos \n" -"para tu aplicación y se guardarán cifrados en el servidor elegido. Esos " -"secretos \n" +"para tu aplicación y se guardarán cifrados en el servidor elegido. Esos secretos \n" "generados solo se muestran al momento de crearlos, así que guárdalos \n" "en un lugar seguro.\n" "\n" "Los hashes de commit de la receta son valores válidos para \"[version]\".\n" "\n" -"Puedes usar \"--pass/-P\" para guardar esas contraseñas generadas " -"localmente \n" +"Puedes usar \"--pass/-P\" para guardar esas contraseñas generadas localmente \n" "en un almacén pass (ver passwordstore.org). El comando pass debe estar \n" "disponible en tu $PATH." @@ -1070,11 +1011,9 @@ msgstr "📤 Despliega una 🚀 aplicación" msgid "" "Deploy an app.\n" "\n" -"This command supports chaos operations. Use \"--chaos/-C\" to deploy your " -"recipe\n" +"This command supports chaos operations. Use \"--chaos/-C\" to deploy your recipe\n" "checkout as-is. Recipe commit hashes are also supported as values for\n" -"\"[version]\". Please note, \"upgrade\"/\"rollback\" do not support chaos " -"operations." +"\"[version]\". Please note, \"upgrade\"/\"rollback\" do not support chaos operations." msgstr "" "Despliega una aplicación.\n" "\n" @@ -1097,8 +1036,7 @@ msgstr "⬇️ Descarga una 📸 instantánea" msgid "" "Downloads a backup.tar.gz to the current working directory.\n" "\n" -"\"--volumes/-v\" includes data contained in volumes alongide paths specified " -"in\n" +"\"--volumes/-v\" includes data contained in volumes alongide paths specified in\n" "\"backupbot.backup.path\" labels." msgstr "" "Descarga un backup.tar.gz al directorio de trabajo actual.\n" @@ -1142,28 +1080,21 @@ msgstr "NOMBRE GENERADO" msgid "" "Generate a new copy of the recipe catalogue.\n" "\n" -"N.B. this command **will** wipe local unstaged changes from your local " -"recipes\n" -"if present. \"--chaos/-C\" on this command refers to the catalogue " -"repository\n" -"(\"$ABRA_DIR/catalogue\") and not the recipes. Please take care not to lose " -"your\n" +"N.B. this command **will** wipe local unstaged changes from your local recipes\n" +"if present. \"--chaos/-C\" on this command refers to the catalogue repository\n" +"(\"$ABRA_DIR/catalogue\") and not the recipes. Please take care not to lose your\n" "changes.\n" "\n" "It is possible to generate new metadata for a single recipe by passing\n" "[recipe]. The existing local catalogue will be updated, not overwritten.\n" "\n" -"It is quite easy to get rate limited by Docker Hub when running this " -"command.\n" -"If you have a Hub account you can \"docker login\" and Abra will " -"automatically\n" +"It is quite easy to get rate limited by Docker Hub when running this command.\n" +"If you have a Hub account you can \"docker login\" and Abra will automatically\n" "use those details.\n" "\n" "Publish your new release to git.coopcloud.tech with \"--publish/-p\". This\n" -"requires that you have permission to git push to these repositories and " -"have\n" -"your SSH keys configured on your account. Enable ssh-agent and make sure to " -"add\n" +"requires that you have permission to git push to these repositories and have\n" +"your SSH keys configured on your account. Enable ssh-agent and make sure to add\n" "your private key and enter your passphrase beforehand.\n" "\n" " eval `ssh-agent`\n" @@ -1246,8 +1177,7 @@ msgstr "📋 Muestra el catálogo de 📜 recetas" #: pkg/recipe/git.go:465 #, c-format msgid "GetRecipeVersions encountered error for %s: %s (collected %d versions)" -msgstr "" -"GetRecipeVersions encontró un error para %s: %s (recopiladas %d versiones)" +msgstr "GetRecipeVersions encontró un error para %s: %s (recopiladas %d versiones)" #: cli/recipe/new.go:125 msgid "Git (user) name to do commits with" @@ -1397,22 +1327,17 @@ msgstr "⚙️ Administrar 📓 catálogo de 📜 recetas" msgid "" "Move an app to a differnt server.\n" "\n" -"This command will migrate an app config and copy secrets and volumes from " -"the\n" -"old server to the new one. The app MUST be deployed on the old server " -"before\n" +"This command will migrate an app config and copy secrets and volumes from the\n" +"old server to the new one. The app MUST be deployed on the old server before\n" "doing the move. The app will be undeployed from the current server but not\n" "deployed on the new server.\n" "\n" -"The \"tar\" command is required on both the old and new server as well as " -"\"sudo\"\n" +"The \"tar\" command is required on both the old and new server as well as \"sudo\"\n" "permissions. The \"rsync\" command is required on your local machine for\n" "transferring volumes.\n" "\n" -"Do not forget to update your DNS records. Don't panic, it might take a " -"while\n" -"for the dust to settle after you move an app. If anything goes wrong, you " -"can\n" +"Do not forget to update your DNS records. Don't panic, it might take a while\n" +"for the dust to settle after you move an app. If anything goes wrong, you can\n" "always move the app config file to the original server and deploy it there\n" "again. No data is removed from the old server.\n" "\n" @@ -1437,8 +1362,7 @@ msgstr "" "de vuelta al servidor original y desplegarlo otra vez. No se elimina \n" "ningún dato del servidor antiguo.\n" "\n" -"Usa \"--ejecucion-de-prueba/-r\" para ver qué secretos y volúmenes se " -"moverán." +"Usa \"--ejecucion-de-prueba/-r\" para ver qué secretos y volúmenes se moverán." #. translators: Short description for `app move` command #: cli/app/move.go:41 @@ -1495,8 +1419,7 @@ msgstr "🧹 Limpiar recursos en un 🕋 servidor" msgid "" "Prunes unused containers, networks, and dangling images.\n" "\n" -"Use \"--volumes/-v\" to remove volumes that are not associated with a " -"deployed\n" +"Use \"--volumes/-v\" to remove volumes that are not associated with a deployed\n" "app. This can result in unwanted data loss if not used carefully." msgstr "" "Elimina contenedores, redes e imágenes que no se usan.\n" @@ -1508,16 +1431,13 @@ msgstr "" #. translators: Short description for `app env pull` command #: cli/app/env.go:80 msgid "Pull app environment values from a deployed app" -msgstr "" -"Obtener (pull) valores de entorno de la aplicación desde una aplicación " -"desplegada" +msgstr "Obtener (pull) valores de entorno de la aplicación desde una aplicación desplegada" #: cli/app/env.go:81 msgid "" "Pull app environment values from a deploymed app.\n" "\n" -"A convenient command for when you've lost your app environment file or want " -"to\n" +"A convenient command for when you've lost your app environment file or want to\n" "synchronize your local app environment values with what is deployed live." msgstr "" "Extrae los valores de entorno de una aplicación desplegada.\n" @@ -1553,8 +1473,7 @@ msgid "" "Remove a managed server.\n" "\n" "Abra will remove the internal bookkeeping ($ABRA_DIR/servers/...) and\n" -"underlying client connection context. This server will then be lost in " -"time,\n" +"underlying client connection context. This server will then be lost in time,\n" "like tears in rain." msgstr "" "Eliminar un servidor gestionado.\n" @@ -1576,8 +1495,7 @@ msgstr "💀 Borrar completamente una 🚀 aplicación (local y remotamente)" #. translators: Short description for `recipe reset` command #: cli/recipe/reset.go:23 msgid "Remove all unstaged changes from recipe config" -msgstr "" -"💀 Borra los cambios no actualizados de la configuración de la 📜 receta" +msgstr "💀 Borra los cambios no actualizados de la configuración de la 📜 receta" #: cli/app/remove.go:29 msgid "" @@ -1586,19 +1504,16 @@ msgid "" "By default, it will prompt for confirmation before proceeding. All secrets,\n" "volumes and the local app env file will be deleted.\n" "\n" -"Only run this command when you are sure you want to completely remove the " -"app\n" +"Only run this command when you are sure you want to completely remove the app\n" "and all associated app data. This is a destructive action, Be Careful!\n" "\n" "If you would like to delete specific volumes or secrets, please use removal\n" "sub-commands under \"app volume\" and \"app secret\" instead.\n" "\n" -"Please note, if you delete the local app env file without removing volumes " -"and\n" +"Please note, if you delete the local app env file without removing volumes and\n" "secrets first, Abra will *not* be able to help you remove them afterwards.\n" "\n" -"To delete everything without prompt, use the \"--force/-f\" or the \"--no-" -"input/n\"\n" +"To delete everything without prompt, use the \"--force/-f\" or the \"--no-input/n\"\n" "flag." msgstr "" "┌──────────────────────────────────────────────────────────────┐\n" @@ -1653,14 +1568,11 @@ msgstr "💀 Elimina 📦 volúmen(es) asociados a una 🚀 aplicación" msgid "" "Remove volumes associated with an app.\n" "\n" -"The app in question must be undeployed before you try to remove volumes. " -"See\n" +"The app in question must be undeployed before you try to remove volumes. See\n" "\"abra app undeploy \" for more.\n" "\n" -"The command is interactive and will show a multiple select input which " -"allows\n" -"you to make a seclection. Use the \"?\" key to see more help on navigating " -"this\n" +"The command is interactive and will show a multiple select input which allows\n" +"you to make a seclection. Use the \"?\" key to see more help on navigating this\n" "interface.\n" "\n" "Passing \"--force/-f\" will select all volumes for removal. Be careful." @@ -1696,25 +1608,19 @@ msgstr "⏪ Revertir una 🚀 aplicación una versión anterior" #. translators: Short description for `app run` command #: cli/app/run.go:30 msgid "Run a command inside a service container" -msgstr "" -"Crea un 🐋 contenedor temporal para ejecutar un 💻 comando especificado" +msgstr "Crea un 🐋 contenedor temporal para ejecutar un 💻 comando especificado" #: cli/app/cmd.go:31 msgid "" "Run an app specific command.\n" "\n" -"These commands are bash functions, defined in the abra.sh of the recipe " -"itself.\n" -"They can be run within the context of a service (e.g. app) or locally on " -"your\n" +"These commands are bash functions, defined in the abra.sh of the recipe itself.\n" +"They can be run within the context of a service (e.g. app) or locally on your\n" "work station by passing \"--local/-l\".\n" "\n" -"N.B. If using the \"--\" style to pass arguments, flags (e.g. \"--local/-" -"l\") must\n" -"be passed *before* the \"--\". It is possible to pass arguments without the " -"\"--\"\n" -"as long as no dashes are present (i.e. \"foo\" works without \"--\", \"-" -"foo\"\n" +"N.B. If using the \"--\" style to pass arguments, flags (e.g. \"--local/-l\") must\n" +"be passed *before* the \"--\". It is possible to pass arguments without the \"--\"\n" +"as long as no dashes are present (i.e. \"foo\" works without \"--\", \"-foo\"\n" "does not)." msgstr "" "Ejecuta un comando específico dentro de la aplicación.\n" @@ -1865,10 +1771,8 @@ msgid "" "Arbitrary secret insertion is not supported. Secrets that are inserted must\n" "match those configured in the recipe beforehand.\n" "\n" -"This command can be useful when you want to manually generate secrets for an " -"app\n" -"environment. Typically, you can let Abra generate them for you on app " -"creation\n" +"This command can be useful when you want to manually generate secrets for an app\n" +"environment. Typically, you can let Abra generate them for you on app creation\n" "(see \"abra app new --secrets/-S\" for more)." msgstr "" "Este comando inserta un secreto en el entorno de una aplicación.\n" @@ -1919,22 +1823,17 @@ msgstr "" msgid "" "This command rolls an app back to a previous version.\n" "\n" -"Unlike \"abra app deploy\", chaos operations are not supported here. Only " -"recipe\n" +"Unlike \"abra app deploy\", chaos operations are not supported here. Only recipe\n" "versions are supported values for \"[version]\".\n" "\n" -"It is possible to \"--force/-f\" an downgrade if you want to re-deploy a " -"specific\n" +"It is possible to \"--force/-f\" an downgrade if you want to re-deploy a specific\n" "version.\n" "\n" -"Only the deployed version is consulted when trying to determine what " -"downgrades\n" -"are available. The live deployment version is the \"source of truth\" in " -"this\n" +"Only the deployed version is consulted when trying to determine what downgrades\n" +"are available. The live deployment version is the \"source of truth\" in this\n" "case. The stored .env version is not consulted.\n" "\n" -"A downgrade can be destructive, please ensure you have a copy of your app " -"data\n" +"A downgrade can be destructive, please ensure you have a copy of your app data\n" "beforehand. See \"abra app backup\" for more." msgstr "" "Este comando revierte una aplicación a una versión anterior.\n" @@ -1958,8 +1857,7 @@ msgstr "" msgid "" "This does not destroy any application data.\n" "\n" -"However, you should remain vigilant, as your swarm installation will " -"consider\n" +"However, you should remain vigilant, as your swarm installation will consider\n" "any previously attached volumes as eligible for pruning once undeployed.\n" "\n" "Passing \"--prune/-p\" does not remove those volumes." @@ -1985,8 +1883,7 @@ msgid "" " # Linux:\n" " $ abra autocomplete bash | sudo tee /etc/bash_completion.d/abra\n" " # macOS:\n" -" $ abra autocomplete bash | sudo tee $(brew --prefix)/etc/bash_completion.d/" -"abra\n" +" $ abra autocomplete bash | sudo tee $(brew --prefix)/etc/bash_completion.d/abra\n" "\n" "Zsh:\n" " # If shell autocompletion is not already enabled in your environment,\n" @@ -2022,8 +1919,7 @@ msgstr "" " # Linux:\n" " $ abra autocomplete bash | sudo tee /etc/bash_completion.d/abra\n" " # macOS:\n" -" $ abra autocomplete bash | sudo tee $(brew --prefix)/etc/bash_completion.d/" -"abra\n" +" $ abra autocomplete bash | sudo tee $(brew --prefix)/etc/bash_completion.d/abra\n" "\n" "Zsh:\n" " # Si la autocompletación no está habilitada, actívala una vez:\n" @@ -2074,8 +1970,7 @@ msgid "" "It will update the relevant compose file tags on the local file system.\n" "\n" "Some image tags cannot be parsed because they do not follow some sort of\n" -"semver-like convention. In this case, all possible tags will be listed and " -"it\n" +"semver-like convention. In this case, all possible tags will be listed and it\n" "is up to the end-user to decide.\n" "\n" "The command is interactive and will show a select input which allows you to\n" @@ -2107,10 +2002,8 @@ msgid "" "\n" "By default, the latest stable release is downloaded.\n" "\n" -"Use \"--rc/-r\" to install the latest release candidate. Please bear in mind " -"that\n" -"it may contain absolutely catastrophic deal-breaker bugs. Thank you very " -"much\n" +"Use \"--rc/-r\" to install the latest release candidate. Please bear in mind that\n" +"it may contain absolutely catastrophic deal-breaker bugs. Thank you very much\n" "for the testing efforts 💗" msgstr "" "Actualiza abra in situ con la última versión estable \n" @@ -2131,22 +2024,17 @@ msgstr "📨 Actualizar una 🚀 aplicación" msgid "" "Upgrade an app.\n" "\n" -"Unlike \"abra app deploy\", chaos operations are not supported here. Only " -"recipe\n" +"Unlike \"abra app deploy\", chaos operations are not supported here. Only recipe\n" "versions are supported values for \"[version]\".\n" "\n" -"It is possible to \"--force/-f\" an upgrade if you want to re-deploy a " -"specific\n" +"It is possible to \"--force/-f\" an upgrade if you want to re-deploy a specific\n" "version.\n" "\n" -"Only the deployed version is consulted when trying to determine what " -"upgrades\n" -"are available. The live deployment version is the \"source of truth\" in " -"this\n" +"Only the deployed version is consulted when trying to determine what upgrades\n" +"are available. The live deployment version is the \"source of truth\" in this\n" "case. The stored .env version is not consulted.\n" "\n" -"An upgrade can be destructive, please ensure you have a copy of your app " -"data\n" +"An upgrade can be destructive, please ensure you have a copy of your app data\n" "beforehand. See \"abra app backup\" for more." msgstr "" "Actualizar una aplicación.\n" @@ -2185,26 +2073,19 @@ msgid "" "Examples:\n" "{{.Example}}{{end}}{{if .HasAvailableSubCommands}}\n" "\n" -"Available Commands:{{range .Commands}}{{if (or .IsAvailableCommand (eq .Name " -"\"help\"))}}\n" -" {{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}" -"{{if .HasAvailableLocalFlags}}\n" +"Available Commands:{{range .Commands}}{{if (or .IsAvailableCommand (eq .Name \"help\"))}}\n" +" {{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}{{if .HasAvailableLocalFlags}}\n" "\n" "Flags:\n" -"{{.LocalFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}" -"{{if .HasAvailableInheritedFlags}}\n" +"{{.LocalFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if .HasAvailableInheritedFlags}}\n" "\n" "Global Flags:\n" -"{{.InheritedFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}" -"{{if .HasHelpSubCommands}}\n" +"{{.InheritedFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if .HasHelpSubCommands}}\n" "\n" -"Additional help topics:{{range .Commands}}" -"{{if .IsAdditionalHelpTopicCommand}}\n" -" {{rpad .CommandPath .CommandPathPadding}} {{.Short}}{{end}}{{end}}{{end}}" -"{{if .HasAvailableSubCommands}}\n" +"Additional help topics:{{range .Commands}}{{if .IsAdditionalHelpTopicCommand}}\n" +" {{rpad .CommandPath .CommandPathPadding}} {{.Short}}{{end}}{{end}}{{end}}{{if .HasAvailableSubCommands}}\n" "\n" -"Use \"{{.CommandPath}} [command] --help\" for more information about a " -"command.{{end}}\n" +"Use \"{{.CommandPath}} [command] --help\" for more information about a command.{{end}}\n" msgstr "" "COMANDO ABREVIADO\n" "─────────────────\n" @@ -2213,13 +2094,10 @@ msgstr "" "{{if .HasExample}}\n" "{{.Example}}{{end}}{{if .HasAvailableSubCommands}}\n" "\n" -"Comandos disponibles:{{range .Commands}}{{if (or .IsAvailableCommand " -"(eq .Name \"help\"))}}\n" -" {{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}" -"{{if .HasHelpSubCommands}}\n" +"Comandos disponibles:{{range .Commands}}{{if (or .IsAvailableCommand (eq .Name \"help\"))}}\n" +" {{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}{{if .HasHelpSubCommands}}\n" "\n" -"Temas de ayuda adicionales:{{range .Commands}}" -"{{if .IsAdditionalHelpTopicCommand}}\n" +"Temas de ayuda adicionales:{{range .Commands}}{{if .IsAdditionalHelpTopicCommand}}\n" " {{rpad .CommandPath .CommandPathPadding}} {{.Short}}{{end}}{{end}}{{end}}\n" "\n" " SINTAXIS\n" @@ -2234,16 +2112,11 @@ msgstr "" "{{.InheritedFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}\n" "\n" "{{if .HasAvailableSubCommands}}\n" -"Use \"{{.CommandPath}} [comando] --help\" para más información sobre un " -"comando.{{end}}\n" +"Use \"{{.CommandPath}} [comando] --help\" para más información sobre un comando.{{end}}\n" #: cli/recipe/fetch.go:28 -msgid "" -"Using \"--force/-f\" Git syncs an existing recipe. It does not erase " -"unstaged changes." -msgstr "" -"Usando \"--force/-f\", Git sincroniza una receta existente. No borra los " -"cambios no preparados." +msgid "Using \"--force/-f\" Git syncs an existing recipe. It does not erase unstaged changes." +msgstr "Usando \"--force/-f\", Git sincroniza una receta existente. No borra los cambios no preparados." #: cli/app/secret.go:117 msgid "VALUE" @@ -2274,27 +2147,20 @@ msgstr "[hijack] Fin de stdout" #, c-format msgid "" "\n" -"The following options are two types of initial semantic version that you " -"can\n" -"pick for %s that will be published in the recipe catalogue. This follows " -"the\n" +"The following options are two types of initial semantic version that you can\n" +"pick for %s that will be published in the recipe catalogue. This follows the\n" "semver convention (more on https://semver.org), here is a short cheatsheet\n" "\n" -" 0.1.0: development release, still hacking. when you make a major " -"upgrade\n" -" you increment the \"y\" part (i.e. 0.1.0 -> 0.2.0) and only move " -"to\n" +" 0.1.0: development release, still hacking. when you make a major upgrade\n" +" you increment the \"y\" part (i.e. 0.1.0 -> 0.2.0) and only move to\n" " using the \"x\" part when things are stable.\n" "\n" " 1.0.0: public release, assumed to be working. you already have a stable\n" -" and reliable deployment of this app and feel relatively " -"confident\n" +" and reliable deployment of this app and feel relatively confident\n" " about it.\n" "\n" -"If you want people to be able alpha test your current config for %s but " -"don't\n" -"think it is quite reliable, go with 0.1.0 and people will know that things " -"are\n" +"If you want people to be able alpha test your current config for %s but don't\n" +"think it is quite reliable, go with 0.1.0 and people will know that things are\n" "likely to change.\n" "\n" msgstr "" @@ -2305,8 +2171,7 @@ msgstr "" "Resumen rápido:\n" "\n" " 0.1.0: versión de desarrollo, todavía en prueba. Cuando hagas una \n" -"mejora mayor aumentas la parte \"y\" (p. ej. 0.1.0 → 0.2.0) y solo pasarás " -"a \n" +"mejora mayor aumentas la parte \"y\" (p. ej. 0.1.0 → 0.2.0) y solo pasarás a \n" "usar la parte \"x\" cuando las cosas estén estables.\n" "\n" " 1.0.0: versión pública, se asume que funciona. Ya tienes un \n" @@ -2324,8 +2189,7 @@ msgid "" "\n" "You need to make a decision about what kind of an update this new recipe\n" "version is. If someone else performs this upgrade, do they have to do some\n" -"migration work or take care of some breaking changes? This can be signaled " -"in\n" +"migration work or take care of some breaking changes? This can be signaled in\n" "the version you specify on the recipe deploy label and is called a semantic\n" "version.\n" "\n" @@ -2335,27 +2199,21 @@ msgid "" "\n" "Here is a semver cheat sheet (more on https://semver.org):\n" "\n" -" major: new features/bug fixes, backwards incompatible (e.g 1.0.0 -> " -"2.0.0).\n" -" the upgrade won't work without some preparation work and others " -"need\n" +" major: new features/bug fixes, backwards incompatible (e.g 1.0.0 -> 2.0.0).\n" +" the upgrade won't work without some preparation work and others need\n" " to take care when performing it. \"it could go wrong\".\n" "\n" -" minor: new features/bug fixes, backwards compatible (e.g. 0.1.0 -> " -"0.2.0).\n" -" the upgrade should Just Work and there are no breaking changes " -"in\n" +" minor: new features/bug fixes, backwards compatible (e.g. 0.1.0 -> 0.2.0).\n" +" the upgrade should Just Work and there are no breaking changes in\n" " the app and the recipe config. \"it should go fine\".\n" "\n" -" patch: bug fixes, backwards compatible (e.g. 0.0.1 -> 0.0.2). this " -"upgrade\n" +" patch: bug fixes, backwards compatible (e.g. 0.0.1 -> 0.0.2). this upgrade\n" " should also Just Work and is mostly to do with minor bug fixes\n" " and/or security patches. \"nothing to worry about\".\n" "\n" msgstr "" "\n" -"Necesitas decidir qué tipo de actualización es esta nueva versión de la " -"receta. \n" +"Necesitas decidir qué tipo de actualización es esta nueva versión de la receta. \n" "Si otra persona realiza esta actualización, ¿tendrá que hacer migraciones \n" "o atender cambios incompatibles? Eso se indica con la versión semántica \n" "que especifiques en la etiqueta de despliegue de la receta.\n" @@ -2366,25 +2224,17 @@ msgstr "" "\n" "Semántica de semver (más en https://semver.org):\n" "\n" -" major: nuevas funciones/arreglos, incompatible hacia atrás (p. ej. 1.0.0 " -"-> 2.0.0).\n" -" la actualización no funcionará sin trabajo de preparación y otros " -"deben\n" +" major: nuevas funciones/arreglos, incompatible hacia atrás (p. ej. 1.0.0 -> 2.0.0).\n" +" la actualización no funcionará sin trabajo de preparación y otros deben\n" " tener cuidado al realizarla. \"podría salir mal\".\n" "\n" -" minor: nuevas funciones/arreglos, compatible hacia atrás (p. ej. 0.1.0 " -"-> 0.2.0).\n" -" la actualización debería funcionar sin problemas y no hay " -"cambios\n" -" incompatibles en la app ni en la configuración de la receta. " -"\"debería ir bien\".\n" +" minor: nuevas funciones/arreglos, compatible hacia atrás (p. ej. 0.1.0 -> 0.2.0).\n" +" la actualización debería funcionar sin problemas y no hay cambios\n" +" incompatibles en la app ni en la configuración de la receta. \"debería ir bien\".\n" "\n" -" patch: correcciones de errores, compatible hacia atrás (p. ej. 0.0.1 -> " -"0.0.2).\n" -" esta actualización también debería funcionar sin problemas y " -"trata\n" -" principalmente de arreglos menores y/o parches de seguridad. " -"\"nada de qué preocuparse\".\n" +" patch: correcciones de errores, compatible hacia atrás (p. ej. 0.0.1 -> 0.0.2).\n" +" esta actualización también debería funcionar sin problemas y trata\n" +" principalmente de arreglos menores y/o parches de seguridad. \"nada de qué preocuparse\".\n" "\n" #: pkg/lint/recipe.go:203 @@ -2550,9 +2400,7 @@ msgstr "aplicacion [comando] [argumento] [opciones]" #: pkg/dns/dns.go:52 #, c-format msgid "app domain %s (%s) does not appear to resolve to app server %s (%s)?" -msgstr "" -"¿El dominio de la aplicación %s (%s) no parece resolverse al servidor de la " -"aplicación %s (%s)?" +msgstr "¿El dominio de la aplicación %s (%s) no parece resolverse al servidor de la aplicación %s (%s)?" #: cli/recipe/release.go:349 msgid "app service is missing image tag?" @@ -2708,9 +2556,7 @@ msgstr "cancelado" #: pkg/catalogue/catalogue.go:59 pkg/recipe/git.go:252 #, c-format msgid "cannot ensure %s is up-to-date, no git remotes configured" -msgstr "" -"no se puede asegurar que %s esté actualizado, no hay remotos de git " -"configurados" +msgstr "no se puede asegurar que %s esté actualizado, no hay remotos de git configurados" #: cli/app/config.go:43 pkg/app/app.go:52 #, c-format @@ -2724,24 +2570,17 @@ msgstr "no se puede obtener la etiqueta %s para %s" #: pkg/recipe/git.go:59 #, c-format -msgid "" -"cannot redeploy previous chaos version (%s), did you mean to use \"--chaos\"?" -msgstr "" -"no se puede redeplegar la versión anterior de caos (%s), ¿Era tu intención " -"usar \"--caos\"?" +msgid "cannot redeploy previous chaos version (%s), did you mean to use \"--chaos\"?" +msgstr "no se puede redeplegar la versión anterior de caos (%s), ¿Era tu intención usar \"--caos\"?" #: cli/app/deploy.go:389 #, c-format msgid "" -"cannot redeploy previous chaos version (%s), did you mean to use \"--" -"chaos\"?\n" -" to return to a regular release, specify a release tag, commit SHA or " -"use \"--latest\"" +"cannot redeploy previous chaos version (%s), did you mean to use \"--chaos\"?\n" +" to return to a regular release, specify a release tag, commit SHA or use \"--latest\"" msgstr "" -"no se puede redesplegar la versión anterior de caos (%s), ¿Era tu intención " -"usar \"--caos\"? \n" -"para volver a un lanzamiento regular, especifica una etiqueta de " -"lanzamiento, SHA de commit o agrega el comando \"--latest\"" +"no se puede redesplegar la versión anterior de caos (%s), ¿Era tu intención usar \"--caos\"? \n" +"para volver a un lanzamiento regular, especifica una etiqueta de lanzamiento, SHA de commit o agrega el comando \"--latest\"" #: pkg/dns/dns.go:38 pkg/dns/dns.go:47 #, c-format @@ -2750,8 +2589,7 @@ msgstr "no se puede resolver ipv4 para %s?" #: cli/recipe/release.go:130 msgid "cannot specify tag and bump type at the same time" -msgstr "" -"no se puede especificar una etiqueta y un tipo de incremento al mismo tiempo" +msgstr "no se puede especificar una etiqueta y un tipo de incremento al mismo tiempo" #: cli/app/secret.go:68 msgid "cannot use '[secret] [version]' and '--all' together" @@ -2832,9 +2670,7 @@ msgstr "caos" #: pkg/secret/secret.go:235 #, c-format -msgid "" -"charset=bytes only supports encoding=base64, got encoding=%s for secret %s, " -"defaulting to base64" +msgid "charset=bytes only supports encoding=base64, got encoding=%s for secret %s, defaulting to base64" msgstr "" #. translators: `app check` command @@ -2917,21 +2753,13 @@ msgstr "el comando %v no salió después de %v: stderr=%q" #: pkg/upstream/commandconn/commandconn.go:172 #, c-format -msgid "" -"command %v has exited with %v, please make sure the URL is valid, and Docker " -"18.09 or later is installed on the remote host: stderr=%s" -msgstr "" -"el comando %v ha salido con %v, por favor asegúrate de que la URL sea válida " -"y que Docker 18.09 o posterior esté instalado en el host remoto: stderr=%s" +msgid "command %v has exited with %v, please make sure the URL is valid, and Docker 18.09 or later is installed on the remote host: stderr=%s" +msgstr "el comando %v ha salido con %v, por favor asegúrate de que la URL sea válida y que Docker 18.09 o posterior esté instalado en el host remoto: stderr=%s" #. translators: `app command` command #: cli/app/cmd.go:27 -msgid "" -"command [service | --local] [[args] [flags] | [flags] -- " -"[args]]" -msgstr "" -"ejecutar [servicio | --local] [[argumento] [opciones] " -"| [opciones] -- [argumento]]" +msgid "command [service | --local] [[args] [flags] | [flags] -- [args]]" +msgstr "ejecutar [servicio | --local] [[argumento] [opciones] | [opciones] -- [argumento]]" #: pkg/upstream/commandconn/commandconn.go:239 #, c-format @@ -3015,15 +2843,12 @@ msgstr "tiempo de conexión agotado para %s" #: pkg/recipe/compose.go:191 #, c-format msgid "considering %s config(s) for label update" -msgstr "" -"considerando la(s) configuración(es) %s para la actualización de etiquetas" +msgstr "considerando la(s) configuración(es) %s para la actualización de etiquetas" #: pkg/recipe/compose.go:123 #, c-format msgid "considering %s config(s) for tag update" -msgstr "" -"considerando la(s) configuración(es) %s para la actualización de etiquetas " -"(tags)" +msgstr "considerando la(s) configuración(es) %s para la actualización de etiquetas (tags)" #: cli/app/undeploy.go:141 cli/server/prune.go:53 #, c-format @@ -3328,8 +3153,7 @@ msgstr "no solicitar un TTY" #: pkg/ssh/ssh.go:26 msgid "docker: is the daemon running / your user has docker permissions?" -msgstr "" -"docker: ¿está corriendo el daemon / tu usuario tiene permisos de Docker?" +msgstr "docker: ¿está corriendo el daemon / tu usuario tiene permisos de Docker?" #: cli/app/new.go:386 msgid "domain" @@ -3387,8 +3211,7 @@ msgstr "ejecución de prueba: no se creó etiqueta de git (%s)" #: cli/recipe/release.go:371 #, c-format msgid "dry run: not syncing label %s for recipe %s" -msgstr "" -"ejecución de prueba: no se sincronizará la etiqueta %s para la receta %s" +msgstr "ejecución de prueba: no se sincronizará la etiqueta %s para la receta %s" #: pkg/git/remote.go:15 #, c-format @@ -3426,14 +3249,11 @@ msgstr "habilitar ejecución de comandos pre/post-hook" #: cli/internal/validate.go:82 #, c-format msgid "ensure %s recipe compose.* files include \"version: '3.8'\"" -msgstr "" -"asegúrate de que los archivos compose.* de la receta %s incluyan \"version: " -"'3.8'\"" +msgstr "asegúrate de que los archivos compose.* de la receta %s incluyan \"version: '3.8'\"" #: pkg/lint/recipe.go:69 msgid "ensure 'version: \"3.8\"' in compose configs" -msgstr "" -"asegúrate que 'version: \"3.8\"' esté en las configuraciones de compose" +msgstr "asegúrate que 'version: \"3.8\"' esté en las configuraciones de compose" #: pkg/lint/recipe.go:163 msgid "ensure \"image: ...\" set on all services" @@ -3450,12 +3270,8 @@ msgid "ensuring env version %s" msgstr "asegurando la versión del entorno (env) %s" #: cli/recipe/upgrade.go:285 -msgid "" -"enter / return to confirm, choose 'skip' to not upgrade this tag, vim mode " -"is enabled" -msgstr "" -"presiona \"enter\" para confirmar; escribe 'skip' para no actualizar esta " -"etiqueta; modo vim habilitado" +msgid "enter / return to confirm, choose 'skip' to not upgrade this tag, vim mode is enabled" +msgstr "presiona \"enter\" para confirmar; escribe 'skip' para no actualizar esta etiqueta; modo vim habilitado" #. translators: `app env` command group #: cli/app/env.go:311 @@ -3568,14 +3384,12 @@ msgstr "🛑 error al hacer commit: %s" #: cli/app/move.go:167 #, c-format msgid "failed to copy %s from %s to local machine: output:%s err:%s" -msgstr "" -"🛑 error al copiar %s desde %s a la máquina local: salida:%s errores:%s" +msgstr "🛑 error al copiar %s desde %s a la máquina local: salida:%s errores:%s" #: cli/app/move.go:173 #, c-format msgid "failed to copy %s from local machine to %s: output:%s err:%s" -msgstr "" -"🛑 error al copiar %s desde la máquina local a %s: salida:%s errores:%s" +msgstr "🛑 error al copiar %s desde la máquina local a %s: salida:%s errores:%s" #: pkg/upstream/stack/stack.go:535 #, c-format @@ -3707,8 +3521,7 @@ msgstr "🛑 No se pudo obtener el último commit de %s: %s" #: pkg/upstream/stack/stack.go:472 #, c-format msgid "failed to retrieve registry auth for image %s: %s" -msgstr "" -"🛑 No se pudo obtener la autenticación del registro para la imagen %s: %s" +msgstr "🛑 No se pudo obtener la autenticación del registro para la imagen %s: %s" #: pkg/git/branch.go:68 #, c-format @@ -3849,9 +3662,7 @@ msgstr "catálogo de recetas generado: %s" #: cli/app/secret.go:145 #, c-format msgid "generated secrets %s shown again, please take note of them %s" -msgstr "" -"los secretos generados %s; se muestran nuevamente, por favor toma nota de " -"ellos %s" +msgstr "los secretos generados %s; se muestran nuevamente, por favor toma nota de ellos %s" #: pkg/app/compose.go:63 #, c-format @@ -4117,9 +3928,7 @@ msgstr "incrementar la parte de parche de la versión" #: pkg/git/init.go:50 #, c-format msgid "init committed all files for new git repo in %s" -msgstr "" -"inicializado y comiteados todos los archivos para el nuevo repositorio git " -"en %s" +msgstr "inicializado y comiteados todos los archivos para el nuevo repositorio git en %s" #: pkg/git/init.go:24 #, c-format @@ -4165,31 +3974,22 @@ msgstr "fuente de enlace inválida, la fuente no puede estar vacía" #: pkg/upstream/convert/service.go:813 #, c-format msgid "invalid credential spec: cannot specify both %s and %s" -msgstr "" -"especificación de credencial inválida: no se pueden especificar ambos %s y %s" +msgstr "especificación de credencial inválida: no se pueden especificar ambos %s y %s" #: pkg/upstream/convert/service.go:815 #, c-format msgid "invalid credential spec: cannot specify both %s, and %s" -msgstr "" -"especificación de credencial inválida: no se pueden especificar tanto %s, " -"como %s" +msgstr "especificación de credencial inválida: no se pueden especificar tanto %s, como %s" #: pkg/upstream/convert/service.go:834 #, c-format -msgid "" -"invalid credential spec: spec specifies config %v, but no such config can be " -"found" -msgstr "" -"especificación de credencial inválida: la especificación indica la " -"configuración %v, pero no se puede encontrar tal configuración" +msgid "invalid credential spec: spec specifies config %v, but no such config can be found" +msgstr "especificación de credencial inválida: la especificación indica la configuración %v, pero no se puede encontrar tal configuración" #: pkg/upstream/container/hijack.go:100 #, c-format msgid "invalid detach escape keys, using default: %s" -msgstr "" -"teclas de escape de separación inválidas, utilizando el valor " -"predeterminado: %s" +msgstr "teclas de escape de separación inválidas, utilizando el valor predeterminado: %s" #: pkg/lint/recipe.go:504 msgid "invalid lightweight tag detected" @@ -4376,8 +4176,7 @@ msgstr "maquina" #: cli/recipe/release.go:97 #, c-format msgid "main app service version for %s is empty?" -msgstr "" -"¿la versión del servicio principal de la aplicación para %s está vacía?" +msgstr "¿la versión del servicio principal de la aplicación para %s está vacía?" #: cli/internal/recipe.go:48 cli/internal/recipe.go:66 #: cli/internal/recipe.go:80 cli/recipe/release.go:631 @@ -4478,23 +4277,13 @@ msgstr "se necesitan 3 o 4 argumentos" #: pkg/upstream/stack/stack.go:352 #, c-format -msgid "" -"network %q is declared as external, but could not be found. You need to " -"create a swarm-scoped network before the stack is deployed, which you can do " -"by running this on the server: docker network create -d overlay proxy" -msgstr "" -"La red %q está declarada como externa, pero no se pudo encontrar. Debes " -"crear una red con alcance swarm antes de desplegar el stack; puedes hacerlo " -"en el servidor mismo con: docker network create -d overlay proxy" +msgid "network %q is declared as external, but could not be found. You need to create a swarm-scoped network before the stack is deployed, which you can do by running this on the server: docker network create -d overlay proxy" +msgstr "La red %q está declarada como externa, pero no se pudo encontrar. Debes crear una red con alcance swarm antes de desplegar el stack; puedes hacerlo en el servidor mismo con: docker network create -d overlay proxy" #: pkg/upstream/stack/stack.go:356 #, c-format -msgid "" -"network %q is declared as external, but it is not in the right scope: %q " -"instead of \"swarm\"" -msgstr "" -"la red %q se declara como externa, pero no está en el alcance correcto: %q " -"en lugar de \"swarm\"" +msgid "network %q is declared as external, but it is not in the right scope: %q instead of \"swarm\"" +msgstr "la red %q se declara como externa, pero no está en el alcance correcto: %q en lugar de \"swarm\"" #: cli/app/undeploy.go:148 cli/server/prune.go:60 #, c-format @@ -4534,8 +4323,7 @@ msgstr "secretos nuevos: %v" #: pkg/git/read.go:88 #, c-format msgid "no %s exists, not reading any global gitignore config" -msgstr "" -"no existe %s, no se está leyendo ninguna configuración global de gitignore" +msgstr "no existe %s, no se está leyendo ninguna configuración global de gitignore" #: pkg/git/read.go:130 #, c-format @@ -4605,9 +4393,7 @@ msgstr "no se proporcionó dominio" #: pkg/recipe/compose.go:248 msgid "no existing label found, automagic insertion not supported yet" -msgstr "" -"no se encontró ninguna etiqueta existente, la inserción automágica no es " -"compatible aún" +msgstr "no se encontró ninguna etiqueta existente, la inserción automágica no es compatible aún" #: cli/recipe/release.go:134 #, c-format @@ -4616,12 +4402,8 @@ msgstr "no se encontraron etiquetas de git para %s" #: cli/recipe/upgrade.go:182 #, c-format -msgid "" -"no new versions available for %s, assuming %s is the latest (use -a/--all-" -"tags to see all anyway)" -msgstr "" -"no hay nuevas versiones disponibles para %s; asumiendo que %s es la más " -"reciente (usa -a/--all-tags para ver todas)" +msgid "no new versions available for %s, assuming %s is the latest (use -a/--all-tags to see all anyway)" +msgstr "no hay nuevas versiones disponibles para %s; asumiendo que %s es la más reciente (usa -a/--all-tags para ver todas)" #: cli/internal/validate.go:64 #, c-format @@ -4744,9 +4526,7 @@ msgstr "no hay suficientes argumentos: %s" #: pkg/secret/secret.go:145 msgid "not generating app secrets, none enabled in recipe config" -msgstr "" -"no se están generando secretos de la aplicación; ninguno habilitado en la " -"configuración de la receta" +msgstr "no se están generando secretos de la aplicación; ninguno habilitado en la configuración de la receta" #: cli/internal/command.go:93 msgid "not requesting a remote TTY" @@ -4759,12 +4539,8 @@ msgstr "no se actualizará %s; se omite, como fue solicitado" #: cli/recipe/upgrade.go:244 #, c-format -msgid "" -"not upgrading from %s to %s for %s, because the upgrade type is more serious " -"than what user wants" -msgstr "" -"no se actualizará %s a %s para %s, porque el tipo de actualización es más " -"serio de lo que el usuario desea" +msgid "not upgrading from %s to %s for %s, because the upgrade type is more serious than what user wants" +msgstr "no se actualizará %s a %s para %s, porque el tipo de actualización es más serio de lo que el usuario desea" #: pkg/upstream/stack/remove.go:73 #, c-format @@ -4895,9 +4671,7 @@ msgstr "por favor, selecciona una versión anterior (downgrade) (versión: %s):" #: cli/app/rollback.go:273 #, c-format msgid "please select a downgrade (version: %s, chaos: %s):" -msgstr "" -"por favor, selecciona una versión anterior (downgrade) (versión: %s, caos: " -"%s):" +msgstr "por favor, selecciona una versión anterior (downgrade) (versión: %s, caos: %s):" #: cli/app/upgrade.go:313 #, c-format @@ -5349,8 +5123,7 @@ msgstr "versiones recuperadas del repositorio local de recetas" #: pkg/upstream/stack/stack.go:468 #, c-format msgid "retrieving docker auth token: failed create docker cli: %s" -msgstr "" -"recuperando token de autenticación de Docker: error al crear docker cli: %s" +msgstr "recuperando token de autenticación de Docker: error al crear docker cli: %s" #: pkg/recipe/recipe.go:656 msgid "retrieving recipes" @@ -5411,9 +5184,7 @@ msgstr "regla" #. translators: `app run` command #: cli/app/run.go:27 msgid "run [[args] [flags] | [flags] -- [args]]" -msgstr "" -"lanzar [[argumento] [opciones] | " -"[opciones] -- [argumento]]" +msgstr "lanzar [[argumento] [opciones] | [opciones] -- [argumento]]" #: cli/app/run.go:120 msgid "run command as user" @@ -5503,9 +5274,7 @@ msgstr "secreto [comando] [argumento] [opciones]" #: cli/app/secret.go:295 msgid "secret data not provided on command-line or stdin, prompting" -msgstr "" -"datos del secreto no proporcionados en la línea de comandos o stdin, " -"solicitando" +msgstr "datos del secreto no proporcionados en la línea de comandos o stdin, solicitando" #: pkg/upstream/convert/service.go:72 #, c-format @@ -5539,8 +5308,7 @@ msgstr "los secretos se muestran %s nuevamente, por favor guárdalos %s" #: cli/app/deploy.go:307 #, c-format msgid "selected latest recipe version: %s (from %d available versions)" -msgstr "" -"versión de receta más reciente seleccionada: %s (de %d versiones disponibles)" +msgstr "versión de receta más reciente seleccionada: %s (de %d versiones disponibles)" #: cli/app/new.go:125 #, c-format @@ -5556,8 +5324,7 @@ msgstr "servidor" #: pkg/app/app.go:301 #, c-format msgid "server %s doesn't exist? Run \"abra server ls\" to check" -msgstr "" -"el servidor %s no existe? Ejecuta \"abra servidor listar\" para verificar" +msgstr "el servidor %s no existe? Ejecuta \"abra servidor listar\" para verificar" #. translators: `server` command group #: cli/server/server.go:17 @@ -5585,8 +5352,7 @@ msgstr "falta contexto del servidor, la creación de contexto falló: %s" #: pkg/client/client.go:58 #, c-format msgid "server missing context, run \"abra server add %s\"?" -msgstr "" -"falta contexto del servidor, ¿probaste con \"abra servidor agregar %s\"?" +msgstr "falta contexto del servidor, ¿probaste con \"abra servidor agregar %s\"?" #: pkg/client/client.go:47 #, c-format @@ -5615,18 +5381,13 @@ msgstr "serverAdd: cleanUp: no se pueden listar los archivos en %s: %s" #: cli/recipe/upgrade.go:227 #, c-format -msgid "" -"service %s is at version %s, but pinned to %s, please correct your " -"compose.yml file manually!" -msgstr "" -"el servicio %s está en la versión %s, pero fijado a %s, ¡por favor corrige " -"manualmente tu archivo compose.yml!" +msgid "service %s is at version %s, but pinned to %s, please correct your compose.yml file manually!" +msgstr "el servicio %s está en la versión %s, pero fijado a %s, ¡por favor corrige manualmente tu archivo compose.yml!" #: cli/recipe/upgrade.go:223 #, c-format msgid "service %s, image %s pinned to %s, no compatible upgrade found" -msgstr "" -"servicio %s, imagen %s fijada a %s, no se encontró actualización compatible" +msgstr "servicio %s, imagen %s fijada a %s, no se encontró actualización compatible" #: cli/app/restore.go:113 msgid "services" @@ -5672,8 +5433,7 @@ msgstr "gravedad" #: cli/app/deploy.go:457 cli/app/rollback.go:380 cli/app/upgrade.go:497 msgid "show all configs & images, including unchanged ones" -msgstr "" -"mostrar todas las configuraciones e imágenes, incluidas las no cambiadas" +msgstr "mostrar todas las configuraciones e imágenes, incluidas las no cambiadas" #: cli/app/backup.go:273 msgid "show all paths" @@ -5774,8 +5534,7 @@ msgstr "omitiendo la etiqueta %s: configuración de compose inválida: %s" #: pkg/recipe/git.go:438 #, c-format msgid "skipping tag %s: invalid image reference in service %s: %s" -msgstr "" -"omitiendo la etiqueta %s: referencia de imagen inválida en el servicio %s: %s" +msgstr "omitiendo la etiqueta %s: referencia de imagen inválida en el servicio %s: %s" #: pkg/app/app.go:697 #, c-format @@ -5839,17 +5598,11 @@ msgstr "" #: cli/recipe/release.go:554 msgid "ssh-agent not found. see \"abra recipe release --help\" and try again" -msgstr "" -"ssh-agent no encontrado. consulta \"abra receta publicar --ayuda\" y vuelve " -"a intentarlo" +msgstr "ssh-agent no encontrado. consulta \"abra receta publicar --ayuda\" y vuelve a intentarlo" #: cli/catalogue/catalogue.go:98 -msgid "" -"ssh: SSH_AUTH_SOCK missing, --publish/-p will fail. see \"abra catalogue " -"generate --help\"" -msgstr "" -"ssh: falta SSH_AUTH_SOCK, --publish/-p fallará. consulta \"abra catalogo " -"regenerar --ayuda\"" +msgid "ssh: SSH_AUTH_SOCK missing, --publish/-p will fail. see \"abra catalogue generate --help\"" +msgstr "ssh: falta SSH_AUTH_SOCK, --publish/-p fallará. consulta \"abra catalogo regenerar --ayuda\"" #: cli/app/list.go:295 cli/recipe/list.go:45 msgid "status" @@ -6115,16 +5868,12 @@ msgstr "no se pudo determinar el tipo de receta a partir de %s, env: %v" #: cli/app/env.go:106 #, c-format msgid "unable to determine server of app %s, please pass --server/-s" -msgstr "" -"no se pudo determinar el servidor de la aplicación %s; agrega al comando --" -"server/-s" +msgstr "no se pudo determinar el servidor de la aplicación %s; agrega al comando --server/-s" #: cli/recipe/upgrade.go:252 #, c-format msgid "unable to determine versioning semantics of %s, listing all tags" -msgstr "" -"no se pudieron determinar las reglas de versionado de %s; listando todas las " -"etiquetas" +msgstr "no se pudieron determinar las reglas de versionado de %s; listando todas las etiquetas" #: cli/recipe/fetch.go:71 #, c-format @@ -6199,8 +5948,7 @@ msgstr "no se pudo abrir el work tree de %s: %s" #: cli/recipe/upgrade.go:159 #, c-format msgid "unable to parse %s, error was: %s, skipping upgrade for %s" -msgstr "" -"no se pudo analizar %s, el error fue: %s, omitiendo la actualización para %s" +msgstr "no se pudo analizar %s, el error fue: %s, omitiendo la actualización para %s" #: pkg/recipe/compose.go:153 #, c-format @@ -6245,9 +5993,7 @@ msgstr "no se pudieron leer los remotos en %s: %s" #: cli/recipe/upgrade.go:153 #, c-format msgid "unable to read tag for image %s, is it missing? skipping upgrade for %s" -msgstr "" -"no se pudo leer la etiqueta de la imagen %s; ¿quizás falta? omitiendo " -"actualización para %s" +msgstr "no se pudo leer la etiqueta de la imagen %s; ¿quizás falta? omitiendo actualización para %s" #: cli/app/move.go:210 #, c-format @@ -6262,8 +6008,7 @@ msgstr "no se pudo eliminar el remoto predeterminado en %s: %s" #: cli/app/volume.go:158 #, c-format msgid "unable to remove volume: no volume with name '%s'?" -msgstr "" -"no se pudo eliminar el volumen: ¿no existe un volumen con el nombre '%s'?" +msgstr "no se pudo eliminar el volumen: ¿no existe un volumen con el nombre '%s'?" #: cli/app/secret.go:135 cli/app/secret.go:515 cli/recipe/list.go:83 #: cli/recipe/version.go:113 cli/server/list.go:88 @@ -6546,10 +6291,8 @@ msgstr "versión %s guardada en %s.env" #: cli/app/deploy.go:126 #, c-format -msgid "" -"version '%s' appears to be a chaos commit, but --chaos/-C was not provided" -msgstr "" -"la versión '%s' parece ser un commit de caos, pero no se utilizó --chaos/-C" +msgid "version '%s' appears to be a chaos commit, but --chaos/-C was not provided" +msgstr "la versión '%s' parece ser un commit de caos, pero no se utilizó --chaos/-C" #: pkg/recipe/recipe.go:200 #, c-format @@ -6597,8 +6340,7 @@ msgstr "versión: tomando la versión desde el archivo .env: %s" #: cli/app/deploy.go:379 #, c-format msgid "version: taking version from cli arg: %s" -msgstr "" -"versión: tomando la versión desde el argumento de la línea de comandos: %s" +msgstr "versión: tomando la versión desde el argumento de la línea de comandos: %s" #. translators: `recipe versions` command #: cli/recipe/version.go:23 @@ -6785,31 +6527,21 @@ msgstr "{nombre: %s, " #, c-format #~ msgid "latest git tag (%s) and synced label (%s) are the same?" -#~ msgstr "" -#~ "¿la última etiqueta de git (%s) y la etiqueta sincronizada (%s) son las " -#~ "mismas?" +#~ msgstr "¿la última etiqueta de git (%s) y la etiqueta sincronizada (%s) son las mismas?" #~ msgid "no -p/--publish passed, not publishing" #~ msgstr "no pasó -p/--publish, no se publicará" #, c-format -#~ msgid "" -#~ "no tag specified and no previous tag available for %s, assuming initial " -#~ "release" -#~ msgstr "" -#~ "no se especificó etiqueta y no hay etiqueta previa disponible para %s; " -#~ "asumiendo lanzamiento inicial" +#~ msgid "no tag specified and no previous tag available for %s, assuming initial release" +#~ msgstr "no se especificó etiqueta y no hay etiqueta previa disponible para %s; asumiendo lanzamiento inicial" #, c-format #~ msgid "please fix your synced label for %s and re-run this command" -#~ msgstr "" -#~ "por favor, corrige tu etiqueta sincronizada para %s y vuelve a ejecutar " -#~ "este comando" +#~ msgstr "por favor, corrige tu etiqueta sincronizada para %s y vuelve a ejecutar este comando" #~ msgid "previous git tags detected, assuming new semver release" -#~ msgstr "" -#~ "se detectaron etiquetas git anteriores, asumiendo un nuevo lanzamiento " -#~ "semver" +#~ msgstr "se detectaron etiquetas git anteriores, asumiendo un nuevo lanzamiento semver" #~ msgid "publish new release?" #~ msgstr "¿publicar nueva versión?" @@ -6818,12 +6550,8 @@ msgstr "{nombre: %s, " #~ msgstr "sincronizar [version] [flags]" #, c-format -#~ msgid "" -#~ "unable to read version for %s from synced label. Did you try running " -#~ "\"abra recipe sync %s\" already?" -#~ msgstr "" -#~ "no se pudo leer la versión de %s desde la etiqueta sincronizada. ¿Ya " -#~ "ejecutaste \"abra receta sincronizar %s\"?" +#~ msgid "unable to read version for %s from synced label. Did you try running \"abra recipe sync %s\" already?" +#~ msgstr "no se pudo leer la versión de %s desde la etiqueta sincronizada. ¿Ya ejecutaste \"abra receta sincronizar %s\"?" #, c-format #~ msgid "use %s as the new version?" From eb62135e3b52da62a09e805f048c69811dc7e712 Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Thu, 15 Oct 2026 03:01:18 +0000 Subject: [PATCH 8/8] chore(deps): update otel/weaver docker tag to v0.28.0 --- vendor/go.opentelemetry.io/otel/dependencies.Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vendor/go.opentelemetry.io/otel/dependencies.Dockerfile b/vendor/go.opentelemetry.io/otel/dependencies.Dockerfile index f0cc942b..4020b163 100644 --- a/vendor/go.opentelemetry.io/otel/dependencies.Dockerfile +++ b/vendor/go.opentelemetry.io/otel/dependencies.Dockerfile @@ -1,4 +1,4 @@ # This is a renovate-friendly source of Docker images. FROM python:3.13.6-slim-bullseye@sha256:e98b521460ee75bca92175c16247bdf7275637a8faaeb2bcfa19d879ae5c4b9a AS python -FROM otel/weaver:v0.21.2@sha256:2401de985c38bdb98b43918e2f43aa36b2afed4aa5669ac1c1de0a17301cd36d AS weaver +FROM otel/weaver:v0.28.0@sha256:5857bb81d8cc7ed608cdcae37c4b459eb020f21dd4acade35f7933cd6762fbcc AS weaver FROM avtodev/markdown-lint:v1@sha256:6aeedc2f49138ce7a1cd0adffc1b1c0321b841dc2102408967d9301c031949ee AS markdown