update download script

This commit is contained in:
Elizaveta
2025-11-05 16:25:34 +03:00
parent 244c1011b0
commit ee977f5993
3 changed files with 84 additions and 50 deletions

View File

@@ -1,4 +1,4 @@
import React, { useState } from 'react'; import React, { useState, useEffect } from 'react';
import { Trans, useTranslation } from 'react-i18next'; import { Trans, useTranslation } from 'react-i18next';
@@ -43,7 +43,7 @@ export const Form = ({
onSubmit, onSubmit,
}: FormProps) => { }: FormProps) => {
const { t, i18n } = useTranslation(); const { t, i18n } = useTranslation();
React.useEffect(() => { useEffect(() => {
preloadServicesLocale(i18n.language).catch(() => {}); preloadServicesLocale(i18n.language).catch(() => {});
}, [i18n.language]); }, [i18n.language]);
const { const {

View File

@@ -22,19 +22,15 @@ import (
"github.com/AdguardTeam/golibs/syncutil" "github.com/AdguardTeam/golibs/syncutil"
) )
const (
hostlistRegistryProjectID = "hostlists-registry"
jsonMessageKey = "message"
jsonIndentPrefix = ""
jsonIndentString = " "
)
// download and save all translations. // download and save all translations.
func (c *twoskyClient) download(ctx context.Context, l *slog.Logger) (err error) { func (c *twoskyClient) download(ctx context.Context, l *slog.Logger) (err error) {
return c.downloadTo(ctx, l, localesDir, defaultBaseFile)
}
// downloadTo downloads and saves all translations to the specified outputDir
// using the specified baseFile name.
func (c *twoskyClient) downloadTo(
ctx context.Context,
l *slog.Logger,
outputDir string,
baseFile string,
) (err error) {
var numWorker int var numWorker int
flagSet := flag.NewFlagSet("download", flag.ExitOnError) flagSet := flag.NewFlagSet("download", flag.ExitOnError)
@@ -53,6 +49,29 @@ func (c *twoskyClient) downloadTo(
usage("count must be positive") usage("count must be positive")
} }
// download locales from AdGuard Home crowdin project
if err = c.downloadTo(ctx, l, localesDir, defaultBaseFile, numWorker); err != nil {
return err
}
// download services from AdGuard Hostlist Registry crowdin project
c.projectID = hostlistRegistryProjectID
if err = c.downloadTo(ctx, l, servicesLocalesDir, servicesBaseFile, numWorker); err != nil {
return err
}
return nil
}
// downloads and saves all translations to the specified outputDir
// using the specified baseFile name.
func (c *twoskyClient) downloadTo(
ctx context.Context,
l *slog.Logger,
outputDir string,
baseFile string,
numWorker int,
) (err error) {
downloadURI := c.uri.JoinPath("download") downloadURI := c.uri.JoinPath("download")
wg := &sync.WaitGroup{} wg := &sync.WaitGroup{}
@@ -70,11 +89,6 @@ func (c *twoskyClient) downloadTo(
baseFile: baseFile, baseFile: baseFile,
} }
// Ensure output directory exists.
if err = os.MkdirAll(outputDir, 0o775); err != nil {
return fmt.Errorf("creating output dir: %w", err)
}
for range numWorker { for range numWorker {
wg.Go(dw.run) wg.Go(dw.run)
} }
@@ -93,40 +107,65 @@ func (c *twoskyClient) downloadTo(
return nil return nil
} }
func normalizeServicesJSON(data []byte) []byte { // extracts the "message" values into a flat map and returns the result
if b, err := unwrapMessageJSON(data); err == nil { // formatted with consistent JSON indentation
data = b func normalizeServicesJSON(data []byte) ([]byte, error) {
unwrapped, err := extractMessagesJSON(data)
if err != nil {
return nil, fmt.Errorf("normalize services json: extraction failed: %w", err)
} }
if b, err := indentJSON(data); err == nil {
data = b indented, err := indentJSON(unwrapped)
if err != nil {
return nil, fmt.Errorf("normalize services json: indentation failed: %w", err)
} }
return data
return indented, nil
} }
func unwrapMessageJSON(data []byte) ([]byte, error) { // converts a wrapped services JSON of shape
var wrapped map[string]struct { // {"key": {"message": "..."}} into a flat {"key": "..."}
Message string `json:"message"` func extractMessagesJSON(input []byte) ([]byte, error) {
var wrapped map[string]map[string]any
if err := json.Unmarshal(input, &wrapped); err != nil {
return nil, fmt.Errorf("extract json: unmarshal wrapped payload: %w", err)
} }
if err := json.Unmarshal(data, &wrapped); err != nil {
return nil, err flattened := make(map[string]string, len(wrapped))
for key, inner := range wrapped {
rawValue, ok := inner[jsonMessageKey]
if !ok {
return nil, fmt.Errorf("extract json: missing %q field for key %q", jsonMessageKey, key)
}
message, ok := rawValue.(string)
if !ok {
return nil, fmt.Errorf("extract json: %q field for key %q is not a string", jsonMessageKey, key)
}
flattened[key] = message
} }
flat := make(map[string]string, len(wrapped))
for k, v := range wrapped { result, err := json.Marshal(flattened)
flat[k] = v.Message if err != nil {
return nil, fmt.Errorf("extract json: marshal flattened map: %w", err)
} }
return json.Marshal(flat)
return result, nil
} }
func indentJSON(data []byte) ([]byte, error) { // formats JSON using the configured prefix and indent
var buf bytes.Buffer func indentJSON(data []byte) (b []byte, err error) {
if err := json.Indent(&buf, data, "", " "); err != nil { var buffer bytes.Buffer
return nil, err
err = json.Indent(&buffer, data, jsonIndentPrefix, jsonIndentString)
if err != nil {
return nil, fmt.Errorf("indent json: formatting failed: %w", err)
} }
return buf.Bytes(), nil
return buffer.Bytes(), nil
} }
// printFailedLocales prints sorted list of failed downloads, if any. l and
// failed must not be nil.
func printFailedLocales( func printFailedLocales(
ctx context.Context, ctx context.Context,
l *slog.Logger, l *slog.Logger,
@@ -191,7 +230,10 @@ func saveToFile(
} }
if baseFile == servicesBaseFile { if baseFile == servicesBaseFile {
data = normalizeServicesJSON(data) data, err = normalizeServicesJSON(data)
if err != nil {
return fmt.Errorf("normalize services JSON for %q: %w", code, err)
}
} }
name := filepath.Join(outputDir, code+".json") name := filepath.Join(outputDir, code+".json")

View File

@@ -90,11 +90,6 @@ func main() {
cli = errors.Must(conf.toClient()) cli = errors.Must(conf.toClient())
errors.Check(cli.download(ctx, l)) errors.Check(cli.download(ctx, l))
case "download-services":
cli = errors.Must(conf.toClient())
cli.projectID = "hostlists-registry"
errors.Check(cli.downloadTo(ctx, l, servicesLocalesDir, servicesBaseFile))
case "unused": case "unused":
err := unused(ctx, l, conf.LocalizableFiles[0]) err := unused(ctx, l, conf.LocalizableFiles[0])
errors.Check(err) errors.Check(err)
@@ -120,10 +115,7 @@ Commands:
summary summary
Print summary. Print summary.
download [-n <count>] download [-n <count>]
Download translations. count is a number of concurrent downloads. Download translations (both app and services). count is a number of concurrent downloads.
download-services [-n <count>]
Download services translations from 'hostlists-registry' project into
client/src/__locales-services.
unused unused
Print unused strings. Print unused strings.
upload upload