diff --git a/clients/cli/cmd/internal/placeholders/placeholders.go b/clients/cli/cmd/internal/placeholders/placeholders.go index edd3c970..94d47c8c 100644 --- a/clients/cli/cmd/internal/placeholders/placeholders.go +++ b/clients/cli/cmd/internal/placeholders/placeholders.go @@ -2,10 +2,12 @@ package placeholders import ( "fmt" + "os" "path/filepath" "regexp" "strings" + "github.com/antihax/optional" "github.com/phrase/phrase-cli/cmd/internal/stringz" ) @@ -92,3 +94,22 @@ func Resolve(s, pattern string) (map[string]string, error) { return values, nil } + +func ResolveTranslationKeyPrefix(translationKeyPrefix optional.String, filePath string) (optional.String, error) { + if strings.Contains(translationKeyPrefix.Value(), "") { + currentDir, err := os.Getwd() + if err != nil { + return optional.EmptyString(), err + } + + filePath, err := filepath.Rel(currentDir, filePath) + if err != nil { + return optional.EmptyString(), err + } + + resolvedKeyPrefix := strings.Replace(translationKeyPrefix.Value(), "", filePath, 1) + return optional.NewString(resolvedKeyPrefix), nil + } + + return translationKeyPrefix, nil +} diff --git a/clients/cli/cmd/internal/pull.go b/clients/cli/cmd/internal/pull.go index 523a6a82..4b8c5558 100644 --- a/clients/cli/cmd/internal/pull.go +++ b/clients/cli/cmd/internal/pull.go @@ -144,6 +144,11 @@ func (target *Target) DownloadAndWriteToFile(client *phrase.APIClient, localeFil if target.Params != nil { localVarOptionals = target.Params.LocaleDownloadOpts + translationKeyPrefix, err := placeholders.ResolveTranslationKeyPrefix(target.Params.TranslationKeyPrefix, localeFile.Path) + if err != nil { + return err + } + localVarOptionals.TranslationKeyPrefix = translationKeyPrefix } if localVarOptionals.FileFormat.Value() == "" { @@ -170,6 +175,7 @@ func (target *Target) DownloadAndWriteToFile(client *phrase.APIClient, localeFil debugFprintln("Tags", localVarOptionals.Tags) debugFprintln("Branch", localVarOptionals.Branch) debugFprintln("FormatOptions", localVarOptionals.FormatOptions) + debugFprintln("TranslationKeyPrefix", localVarOptionals.TranslationKeyPrefix) if async { return target.downloadAsynchronously(client, localeFile, localVarOptionals) diff --git a/clients/cli/cmd/internal/push_source.go b/clients/cli/cmd/internal/push_source.go index c60837de..e5265044 100644 --- a/clients/cli/cmd/internal/push_source.go +++ b/clients/cli/cmd/internal/push_source.go @@ -8,6 +8,7 @@ import ( "github.com/antihax/optional" "github.com/phrase/phrase-cli/cmd/internal/paths" + "github.com/phrase/phrase-cli/cmd/internal/placeholders" "github.com/phrase/phrase-go/v3" "github.com/spf13/viper" ) @@ -199,6 +200,12 @@ func (source *Source) uploadFile(client *phrase.APIClient, localeFile *LocaleFil params.Branch = optional.NewString(branch) } + translationKeyPrefix, err := placeholders.ResolveTranslationKeyPrefix(params.UploadCreateOpts.TranslationKeyPrefix, localeFile.Path) + if err != nil { + return nil, err + } + params.UploadCreateOpts.TranslationKeyPrefix = translationKeyPrefix + upload, _, err := client.UploadsApi.UploadCreate(Auth, source.ProjectID, file, params.FileFormat.Value(), params.LocaleId.Value(), ¶ms.UploadCreateOpts) return &upload, err diff --git a/clients/cli/go.sum b/clients/cli/go.sum index 8a0936e6..6e83933a 100644 --- a/clients/cli/go.sum +++ b/clients/cli/go.sum @@ -220,8 +220,8 @@ github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FI github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pelletier/go-toml v1.8.1 h1:1Nf83orprkJyknT6h7zbuEGUEjcyVlCxSUGTENmNCRM= github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc= -github.com/phrase/phrase-go/v3 v3.3.0 h1:kq2eFgKE6mUUZpud1KWsTa1RO4T+ztB2JI3DsCfqHog= -github.com/phrase/phrase-go/v3 v3.3.0/go.mod h1:s0uOYiXLxKAYlaIS6TbKv3efkKFUlY4OB6OL+VgvK90= +github.com/phrase/phrase-go/v3 v3.5.0 h1:zviffIun5A10PNnnSV0ynHjs+VRY7fc9xXVtNOjkm3o= +github.com/phrase/phrase-go/v3 v3.5.0/go.mod h1:s0uOYiXLxKAYlaIS6TbKv3efkKFUlY4OB6OL+VgvK90= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= diff --git a/clients/java/src/test/java/com/phrase/client/api/LocalesApiTest.java b/clients/java/src/test/java/com/phrase/client/api/LocalesApiTest.java index abbdf5f8..ee541541 100644 --- a/clients/java/src/test/java/com/phrase/client/api/LocalesApiTest.java +++ b/clients/java/src/test/java/com/phrase/client/api/LocalesApiTest.java @@ -169,7 +169,14 @@ public void localeDownloadTest() throws ApiException, IOException, InterruptedEx String fallbackLocaleId = null; String sourceLocaleId = null; Object customMetadataFilters = null; - File response = api.localeDownload(projectId, id, xPhraseAppOTP, ifModifiedSince, ifNoneMatch, branch, fileFormat, tags, tag, includeEmptyTranslations, excludeEmptyZeroForms, includeTranslatedKeys, keepNotranslateTags, convertEmoji, formatOptions, encoding, skipUnverifiedTranslations, includeUnverifiedTranslations, useLastReviewedVersion, fallbackLocaleId, sourceLocaleId, customMetadataFilters); + String translationKeyPrefix = null; + Boolean filterByPrefix = null; + File response = api.localeDownload(projectId, id, xPhraseAppOTP, ifModifiedSince, ifNoneMatch, + branch, fileFormat, tags, tag, includeEmptyTranslations, excludeEmptyZeroForms, + includeTranslatedKeys, keepNotranslateTags, convertEmoji, formatOptions, encoding, + skipUnverifiedTranslations, includeUnverifiedTranslations, useLastReviewedVersion, + fallbackLocaleId, sourceLocaleId, translationKeyPrefix, filterByPrefix, + customMetadataFilters); String fileContents = new String(java.nio.file.Files.readAllBytes(response.toPath())); Assert.assertEquals("Correct file contents", fileContents, body); diff --git a/clients/java/src/test/java/com/phrase/client/api/UploadsApiTest.java b/clients/java/src/test/java/com/phrase/client/api/UploadsApiTest.java index a19a8408..3d3699d1 100644 --- a/clients/java/src/test/java/com/phrase/client/api/UploadsApiTest.java +++ b/clients/java/src/test/java/com/phrase/client/api/UploadsApiTest.java @@ -109,6 +109,7 @@ public void uploadCreateTest() throws ApiException, IOException, InterruptedExce Boolean autotranslate = null; Boolean markReviewed = null; Boolean tagOnlyAffectedKeys = null; + String translationKeyPrefix = null; Map nestedFormatOptionsMap = new HashMap<>(); nestedFormatOptionsMap.put("nested_option", "sub_option"); @@ -118,7 +119,11 @@ public void uploadCreateTest() throws ApiException, IOException, InterruptedExce formatOptionsMap.put("fallback_language", "en"); formatOptionsMap.put("more_options", nestedFormatOptionsMap); - Upload response = api.uploadCreate(projectId, file, fileFormat, localeId, xPhraseAppOTP, branch, tags, updateTranslations, updateTranslationKeys, updateTranslationsOnSourceMatch, updateDescriptions, convertEmoji, skipUploadTags, skipUnverification, fileEncoding, localeMapping, formatOptionsMap, autotranslate, markReviewed, tagOnlyAffectedKeys); + Upload response = api.uploadCreate(projectId, file, fileFormat, localeId, xPhraseAppOTP, branch, + tags, updateTranslations, updateTranslationKeys, updateTranslationsOnSourceMatch, + updateDescriptions, convertEmoji, skipUploadTags, skipUnverification, fileEncoding, + localeMapping, formatOptionsMap, autotranslate, markReviewed, tagOnlyAffectedKeys, + translationKeyPrefix); Assert.assertEquals("valid id returned", "id_example", response.getId()); Assert.assertEquals("valid creation date returned", OffsetDateTime.parse("2015-01-28T09:52:53Z"), response.getCreatedAt()); diff --git a/clients/php/test/Api/LocalesApiTest.php b/clients/php/test/Api/LocalesApiTest.php index fa62833b..cea02557 100644 --- a/clients/php/test/Api/LocalesApiTest.php +++ b/clients/php/test/Api/LocalesApiTest.php @@ -165,6 +165,8 @@ public function testLocaleDownload() null, null, null, + null, + null, $custom_metadata_filters ); diff --git a/doc/compiled.json b/doc/compiled.json index cab923ae..2129dca2 100644 --- a/doc/compiled.json +++ b/doc/compiled.json @@ -8775,6 +8775,24 @@ "type": "string" } }, + { + "description": "Download all translation keys, and remove the specified prefix where possible. Warning: this may create duplicate key names if other keys share the same name after the prefix is removed.", + "example": "prefix_", + "name": "translation_key_prefix", + "in": "query", + "schema": { + "type": "string" + } + }, + { + "description": "Only download translation keys containing the specified prefix, and remove the prefix from the generated file.", + "example": null, + "name": "filter_by_prefix", + "in": "query", + "schema": { + "type": "boolean" + } + }, { "name": "custom_metadata_filters", "in": "query", @@ -15059,6 +15077,11 @@ "type": "boolean", "default": false, "example": null + }, + "translation_key_prefix": { + "description": "This prefix will be added to all uploaded translation key names to prevent collisions. Use a meaningful prefix related to your project or file to keep key names organized.", + "type": "string", + "example": "prefix_" } } } diff --git a/paths/locales/download.yaml b/paths/locales/download.yaml index 0f309a8e..406b55be 100644 --- a/paths/locales/download.yaml +++ b/paths/locales/download.yaml @@ -112,6 +112,18 @@ parameters: in: query schema: type: string + - description: "Download all translation keys, and remove the specified prefix where possible. Warning: this may create duplicate key names if other keys share the same name after the prefix is removed." + example: "prefix_" + name: translation_key_prefix + in: query + schema: + type: string + - description: Only download translation keys containing the specified prefix, and remove the prefix from the generated file. + example: + name: filter_by_prefix + in: query + schema: + type: boolean - name: custom_metadata_filters in: query description: | diff --git a/paths/uploads/create.yaml b/paths/uploads/create.yaml index 53115249..0225dfab 100644 --- a/paths/uploads/create.yaml +++ b/paths/uploads/create.yaml @@ -143,4 +143,8 @@ requestBody: type: boolean default: false example: + translation_key_prefix: + description: This prefix will be added to all uploaded translation key names to prevent collisions. Use a meaningful prefix related to your project or file to keep key names organized. + type: string + example: prefix_ x-cli-version: '2.12'