Skip to content

Commit b888717

Browse files
committed
Restructure download_localized_catalog into explicit scan -> download -> AI-fill
Make the lane mirror the pipeline order: (1) generate_strings_catalog scans the code, (2) it now downloads the current GlotPress translations itself (ios_download_strings_files_from_glotpress) and folds them in, (3) CatalogStrings AI-fills the rest. Adds a locales:fr,de scope (and skip_download:true) so a run can be kept cheap. Uploading the AI drafts back to GlotPress (the eventual step 4) stays out — it builds on the existing GlotPress import integration (gp_update_metadata_source), not done here.
1 parent ebd153b commit b888717

1 file changed

Lines changed: 53 additions & 15 deletions

File tree

fastlane/lanes/localization_catalog.rb

Lines changed: 53 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -82,29 +82,42 @@
8282
end
8383
end
8484

85-
# Folds the downloaded GlotPress translations (human) plus AI machine translations into Localizable.xcstrings,
86-
# the future regular-string backing store, as `human ?? existing-machine ?? AI ?? English` (see CatalogStrings).
85+
# Builds Localizable.xcstrings, the future regular-string backing store, end to end:
86+
# 1. scan the code into the English catalog (generate_strings_catalog),
87+
# 2. download the current GlotPress translations and fold them in (human => translated),
88+
# 3. AI-fill the cells GlotPress left empty (=> needs_review) — see CatalogStrings.fold_translations!.
89+
# Uploading the AI drafts back to GlotPress as needs-review for human review (the eventual "step 4") is a
90+
# separate step, not done here — it builds on the existing GlotPress import integration (cf.
91+
# gp_update_metadata_source). For now the machine cells live in the catalog as needs_review.
8792
#
88-
# STAGED, NOT SHIPPED: the catalog isn't the runtime store yet (the app still ships Localizable.strings), so
89-
# this only pre-populates it for the eventual cutover — it changes nothing users see.
93+
# STAGED, NOT SHIPPED: Localizable.xcstrings isn't the runtime store yet (the app still ships
94+
# Localizable.strings), so this only pre-populates it for the cutover — it changes nothing users see.
9095
#
91-
# MANUAL ONLY — deliberately NOT wired into download_localized_strings or any CI step: it runs xcstringstool
92-
# extraction, calls the translation API (cost), and commits a large catalog, so it's run on demand, not on
93-
# every release. Run `download_localized_strings` first (so the per-locale `.strings` exist) and, for the AI
94-
# rung, set ANTHROPIC_API_KEY.
95-
desc 'Folds GlotPress + AI translations into Localizable.xcstrings (staged backing store; run manually, not in CI)'
96-
lane :download_localized_catalog do
97-
generate_strings_catalog # refresh the English source + reconcile (creates the catalog on first run)
98-
catalog = JSON.parse(File.read(LOCALIZABLE_CATALOG))
96+
# MANUAL ONLY — deliberately not wired into download_localized_strings or any CI step: it runs xcstringstool
97+
# extraction, downloads from GlotPress, calls the translation API (cost), and commits a large catalog. Set
98+
# ANTHROPIC_API_KEY for step 3; scope a cheap run with `locales:fr` (and add `skip_download:true` to reuse the
99+
# committed .strings instead of re-downloading).
100+
desc 'Builds Localizable.xcstrings: scan code, fold GlotPress translations, AI-fill the rest (staged; manual, not CI)'
101+
lane :download_localized_catalog do |options|
102+
resources_dir = File.join(PROJECT_ROOT_FOLDER, 'WordPress', 'Resources')
103+
locales = catalog_target_locales(options[:locales]) # all ship locales, or the `locales:fr,de` subset
104+
105+
# 1. Scan the code into the English catalog (create / update + reconcile changed sources).
106+
generate_strings_catalog
107+
108+
# 2. Download the current GlotPress translations for those locales (skip to reuse the committed .strings).
109+
download_catalog_strings(resources_dir, locales) unless catalog_flag?(options[:skip_download])
99110

111+
# 2b + 3. Fold the human translations in (=> translated), then AI-fill the cells GlotPress left empty.
112+
catalog = JSON.parse(File.read(LOCALIZABLE_CATALOG))
100113
written = CatalogStrings.fold_translations!(
101114
catalog,
102-
translations_by_locale: catalog_translations_by_locale(File.join(PROJECT_ROOT_FOLDER, 'WordPress', 'Resources')),
103-
locales: GLOTPRESS_TO_LPROJ_APP_LOCALE_CODES.values.uniq,
115+
translations_by_locale: catalog_translations_by_locale(resources_dir),
116+
locales: locales.values.uniq,
104117
ai_translator: catalog_ai_translator
105118
)
106119
File.write(LOCALIZABLE_CATALOG, "#{JSON.pretty_generate(catalog)}\n")
107-
UI.success("Folded translations into #{File.basename(LOCALIZABLE_CATALOG)} (#{written} cells across locales).")
120+
UI.success("Built #{File.basename(LOCALIZABLE_CATALOG)}: folded #{written} cell(s) across #{locales.values.uniq.size} locale(s).")
108121

109122
git_add(path: LOCALIZABLE_CATALOG, shell_escape: false)
110123
git_commit(path: [LOCALIZABLE_CATALOG], message: 'Update Localizable.xcstrings translations (staged)', allow_nothing_to_commit: true)
@@ -229,6 +242,31 @@ def report_catalog(path, extracted_count:, reconciled_count:)
229242
UI.success(message)
230243
end
231244

245+
# The { glotpress => lproj } locale map to operate on: all ship locales, or the subset named in `locales:`
246+
# (a comma-separated list of lproj codes, e.g. `locales:fr,de`) for a cheap scoped run.
247+
def catalog_target_locales(spec)
248+
return GLOTPRESS_TO_LPROJ_APP_LOCALE_CODES if spec.to_s.strip.empty?
249+
250+
wanted = spec.to_s.split(',').map(&:strip)
251+
selected = GLOTPRESS_TO_LPROJ_APP_LOCALE_CODES.select { |_glotpress, lproj| wanted.include?(lproj) }
252+
UI.user_error!("No known ship locales among #{spec.inspect} (use lproj codes, e.g. fr,de,pt-BR)") if selected.empty?
253+
selected
254+
end
255+
256+
# Step 2: download the current GlotPress translations for the given locales into their `*.lproj` dirs.
257+
def download_catalog_strings(resources_dir, locales)
258+
ios_download_strings_files_from_glotpress(
259+
project_url: GLOTPRESS_APP_STRINGS_PROJECT_URL,
260+
locales: locales,
261+
download_dir: resources_dir
262+
)
263+
end
264+
265+
# A fastlane CLI flag, which arrives as a string ("true"/"1"/"yes") or a real boolean.
266+
def catalog_flag?(value)
267+
%w[true 1 yes].include?(value.to_s.strip.downcase)
268+
end
269+
232270
# { lproj => { key => human value } } from the downloaded translation `.strings`. The flat plural keys present
233271
# in these files aren't catalog keys, so the fold ignores them (they belong to Plurals.xcstrings).
234272
def catalog_translations_by_locale(dir)

0 commit comments

Comments
 (0)