Last active
August 29, 2026 09:06
-
-
Save flodolo/dcf48d10c5250f926ebc94985ccfae0f to your computer and use it in GitHub Desktop.
Rewrite XLIFF original
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| from django.db import transaction | |
| from pontoon.base.models import Entity, Project, Resource, Section | |
| XLIFF = [Resource.Format.XLIFF, Resource.Format.XCODE] | |
| project = Project.objects.get(slug="firefox-for-ios") | |
| resources = list(project.resources.filter(obsolete=False, format__in=XLIFF).order_by("path")) | |
| print(f"{len(resources)} XLIFF/XCODE resources, sync_disabled={project.sync_disabled}") | |
| for res in resources: | |
| print(f"{res.path} [{res.format}]") | |
| originals = sorted({s.key[0] for s in Section.objects.filter(resource=res) if s.key}) | |
| for original in originals: | |
| n = Entity.objects.filter(resource=res, obsolete=False, key__0=original).count() | |
| print(f" {original!r}: {n} live entities") | |
| # ---- STEP 2: the renames, and the counters --------------------------------- | |
| # Copy the old values verbatim from the STEP 1 output. Re-paste this whole STEP | |
| # before each run of STEP 3, so the counters restart at zero. | |
| RENAMES = { | |
| "Client/en.lproj/InfoPlist.strings": "Client/en-US.lproj/InfoPlist.strings", | |
| "Extensions/ActionExtension/en.lproj/InfoPlist.strings": "Extensions/ActionExtension/en-US.lproj/InfoPlist.strings", | |
| "Shared/Supporting Files/en.lproj/ActivityStream.strings": "Shared/Supporting Files/en-US.lproj/ActivityStream.strings", | |
| "Shared/Supporting Files/en.lproj/AddressToolbar.strings": "Shared/Supporting Files/en-US.lproj/AddressToolbar.strings", | |
| "Shared/Supporting Files/en.lproj/Alert.strings": "Shared/Supporting Files/en-US.lproj/Alert.strings", | |
| "Shared/Supporting Files/en.lproj/Alerts.strings": "Shared/Supporting Files/en-US.lproj/Alerts.strings", | |
| "Shared/Supporting Files/en.lproj/AppIconSelection.strings": "Shared/Supporting Files/en-US.lproj/AppIconSelection.strings", | |
| "Shared/Supporting Files/en.lproj/BiometricAuthentication.strings": "Shared/Supporting Files/en-US.lproj/BiometricAuthentication.strings", | |
| "Shared/Supporting Files/en.lproj/BookmarkPanel.strings": "Shared/Supporting Files/en-US.lproj/BookmarkPanel.strings", | |
| "Shared/Supporting Files/en.lproj/BookmarkPanelDeleteConfirm.strings": "Shared/Supporting Files/en-US.lproj/BookmarkPanelDeleteConfirm.strings", | |
| "Shared/Supporting Files/en.lproj/Bookmarks.strings": "Shared/Supporting Files/en-US.lproj/Bookmarks.strings", | |
| "Shared/Supporting Files/en.lproj/BottomSheet.strings": "Shared/Supporting Files/en-US.lproj/BottomSheet.strings", | |
| "Shared/Supporting Files/en.lproj/Camera.strings": "Shared/Supporting Files/en-US.lproj/Camera.strings", | |
| "Shared/Supporting Files/en.lproj/ContextualHints.strings": "Shared/Supporting Files/en-US.lproj/ContextualHints.strings", | |
| "Shared/Supporting Files/en.lproj/CredentialProvider.strings": "Shared/Supporting Files/en-US.lproj/CredentialProvider.strings", | |
| "Shared/Supporting Files/en.lproj/Credentials.strings": "Shared/Supporting Files/en-US.lproj/Credentials.strings", | |
| "Shared/Supporting Files/en.lproj/CustomizeFirefoxHome.strings": "Shared/Supporting Files/en-US.lproj/CustomizeFirefoxHome.strings", | |
| "Shared/Supporting Files/en.lproj/DisplayCard.strings": "Shared/Supporting Files/en-US.lproj/DisplayCard.strings", | |
| "Shared/Supporting Files/en.lproj/Edit Card.strings": "Shared/Supporting Files/en-US.lproj/Edit Card.strings", | |
| "Shared/Supporting Files/en.lproj/EditAddress.strings": "Shared/Supporting Files/en-US.lproj/EditAddress.strings", | |
| "Shared/Supporting Files/en.lproj/EditCard.strings": "Shared/Supporting Files/en-US.lproj/EditCard.strings", | |
| "Shared/Supporting Files/en.lproj/EngagementNotification.strings": "Shared/Supporting Files/en-US.lproj/EngagementNotification.strings", | |
| "Shared/Supporting Files/en.lproj/EnhancedTrackingProtection.strings": "Shared/Supporting Files/en-US.lproj/EnhancedTrackingProtection.strings", | |
| "Shared/Supporting Files/en.lproj/ErrorState.strings": "Shared/Supporting Files/en-US.lproj/ErrorState.strings", | |
| "Shared/Supporting Files/en.lproj/ExternalLink.strings": "Shared/Supporting Files/en-US.lproj/ExternalLink.strings", | |
| "Shared/Supporting Files/en.lproj/FirefoxHomepage.strings": "Shared/Supporting Files/en-US.lproj/FirefoxHomepage.strings", | |
| "Shared/Supporting Files/en.lproj/FirefoxLogins.strings": "Shared/Supporting Files/en-US.lproj/FirefoxLogins.strings", | |
| "Shared/Supporting Files/en.lproj/FirefoxSync.strings": "Shared/Supporting Files/en-US.lproj/FirefoxSync.strings", | |
| "Shared/Supporting Files/en.lproj/Footer.strings": "Shared/Supporting Files/en-US.lproj/Footer.strings", | |
| "Shared/Supporting Files/en.lproj/FxANotification.strings": "Shared/Supporting Files/en-US.lproj/FxANotification.strings", | |
| "Shared/Supporting Files/en.lproj/GoogleLens.strings": "Shared/Supporting Files/en-US.lproj/GoogleLens.strings", | |
| "Shared/Supporting Files/en.lproj/JumpBackIn.strings": "Shared/Supporting Files/en-US.lproj/JumpBackIn.strings", | |
| "Shared/Supporting Files/en.lproj/KeyboardAccessory.strings": "Shared/Supporting Files/en-US.lproj/KeyboardAccessory.strings", | |
| "Shared/Supporting Files/en.lproj/LibraryPanel.strings": "Shared/Supporting Files/en-US.lproj/LibraryPanel.strings", | |
| "Shared/Supporting Files/en.lproj/LiveActivity.strings": "Shared/Supporting Files/en-US.lproj/LiveActivity.strings", | |
| "Shared/Supporting Files/en.lproj/LoginsHelper.strings": "Shared/Supporting Files/en-US.lproj/LoginsHelper.strings", | |
| "Shared/Supporting Files/en.lproj/MainMenu.strings": "Shared/Supporting Files/en-US.lproj/MainMenu.strings", | |
| "Shared/Supporting Files/en.lproj/Microsurvey.strings": "Shared/Supporting Files/en-US.lproj/Microsurvey.strings", | |
| "Shared/Supporting Files/en.lproj/NativeErrorPage.strings": "Shared/Supporting Files/en-US.lproj/NativeErrorPage.strings", | |
| "Shared/Supporting Files/en.lproj/Notification.strings": "Shared/Supporting Files/en-US.lproj/Notification.strings", | |
| "Shared/Supporting Files/en.lproj/Onboarding.strings": "Shared/Supporting Files/en-US.lproj/Onboarding.strings", | |
| "Shared/Supporting Files/en.lproj/PasswordAutofill.strings": "Shared/Supporting Files/en-US.lproj/PasswordAutofill.strings", | |
| "Shared/Supporting Files/en.lproj/PasswordGenerator.strings": "Shared/Supporting Files/en-US.lproj/PasswordGenerator.strings", | |
| "Shared/Supporting Files/en.lproj/PrivacyDashboard.strings": "Shared/Supporting Files/en-US.lproj/PrivacyDashboard.strings", | |
| "Shared/Supporting Files/en.lproj/QRCode.strings": "Shared/Supporting Files/en-US.lproj/QRCode.strings", | |
| "Shared/Supporting Files/en.lproj/ReaderModeBar.strings": "Shared/Supporting Files/en-US.lproj/ReaderModeBar.strings", | |
| "Shared/Supporting Files/en.lproj/RelayMask.strings": "Shared/Supporting Files/en-US.lproj/RelayMask.strings", | |
| "Shared/Supporting Files/en.lproj/RememberCard.strings": "Shared/Supporting Files/en-US.lproj/RememberCard.strings", | |
| "Shared/Supporting Files/en.lproj/ResearchSurface.strings": "Shared/Supporting Files/en-US.lproj/ResearchSurface.strings", | |
| "Shared/Supporting Files/en.lproj/ScanQRCode.strings": "Shared/Supporting Files/en-US.lproj/ScanQRCode.strings", | |
| "Shared/Supporting Files/en.lproj/SearchEngineSelection.strings": "Shared/Supporting Files/en-US.lproj/SearchEngineSelection.strings", | |
| "Shared/Supporting Files/en.lproj/SearchHeaderTitle.strings": "Shared/Supporting Files/en-US.lproj/SearchHeaderTitle.strings", | |
| "Shared/Supporting Files/en.lproj/SearchZero.strings": "Shared/Supporting Files/en-US.lproj/SearchZero.strings", | |
| "Shared/Supporting Files/en.lproj/SelectCreditCard.strings": "Shared/Supporting Files/en-US.lproj/SelectCreditCard.strings", | |
| "Shared/Supporting Files/en.lproj/Settings.strings": "Shared/Supporting Files/en-US.lproj/Settings.strings", | |
| "Shared/Supporting Files/en.lproj/Share.strings": "Shared/Supporting Files/en-US.lproj/Share.strings", | |
| "Shared/Supporting Files/en.lproj/SnackBar.strings": "Shared/Supporting Files/en-US.lproj/SnackBar.strings", | |
| "Shared/Supporting Files/en.lproj/SocialMediaApp.strings": "Shared/Supporting Files/en-US.lproj/SocialMediaApp.strings", | |
| "Shared/Supporting Files/en.lproj/SocialShare.strings": "Shared/Supporting Files/en-US.lproj/SocialShare.strings", | |
| "Shared/Supporting Files/en.lproj/SuggestedSites.strings": "Shared/Supporting Files/en-US.lproj/SuggestedSites.strings", | |
| "Shared/Supporting Files/en.lproj/Summarize.strings": "Shared/Supporting Files/en-US.lproj/Summarize.strings", | |
| "Shared/Supporting Files/en.lproj/Summarizer.strings": "Shared/Supporting Files/en-US.lproj/Summarizer.strings", | |
| "Shared/Supporting Files/en.lproj/TabLocation.strings": "Shared/Supporting Files/en-US.lproj/TabLocation.strings", | |
| "Shared/Supporting Files/en.lproj/TabToolbar.strings": "Shared/Supporting Files/en-US.lproj/TabToolbar.strings", | |
| "Shared/Supporting Files/en.lproj/TabsTray.strings": "Shared/Supporting Files/en-US.lproj/TabsTray.strings", | |
| "Shared/Supporting Files/en.lproj/TermsOfUse.strings": "Shared/Supporting Files/en-US.lproj/TermsOfUse.strings", | |
| "Shared/Supporting Files/en.lproj/Toolbar.strings": "Shared/Supporting Files/en-US.lproj/Toolbar.strings", | |
| "Shared/Supporting Files/en.lproj/ToolbarLocation.strings": "Shared/Supporting Files/en-US.lproj/ToolbarLocation.strings", | |
| "Shared/Supporting Files/en.lproj/Translations.strings": "Shared/Supporting Files/en-US.lproj/Translations.strings", | |
| "Shared/Supporting Files/en.lproj/UpdateCard.strings": "Shared/Supporting Files/en-US.lproj/UpdateCard.strings", | |
| "Shared/Supporting Files/en.lproj/Upgrade.strings": "Shared/Supporting Files/en-US.lproj/Upgrade.strings", | |
| "Shared/Supporting Files/en.lproj/WebCompatReporter.strings": "Shared/Supporting Files/en-US.lproj/WebCompatReporter.strings", | |
| "Shared/Supporting Files/en.lproj/WebContextMenu.strings": "Shared/Supporting Files/en-US.lproj/WebContextMenu.strings", | |
| "Shared/Supporting Files/en.lproj/WebView.strings": "Shared/Supporting Files/en-US.lproj/WebView.strings", | |
| "Shared/Supporting Files/en.lproj/WorldCup.strings": "Shared/Supporting Files/en-US.lproj/WorldCup.strings", | |
| "Shared/Supporting Files/en.lproj/ZoomPageBar.strings": "Shared/Supporting Files/en-US.lproj/ZoomPageBar.strings", | |
| "Shared/en.lproj/3DTouchActions.strings": "Shared/en-US.lproj/3DTouchActions.strings", | |
| "Shared/en.lproj/AuthenticationManager.strings": "Shared/en-US.lproj/AuthenticationManager.strings", | |
| "Shared/en.lproj/ClearHistoryConfirm.strings": "Shared/en-US.lproj/ClearHistoryConfirm.strings", | |
| "Shared/en.lproj/ClearPrivateData.strings": "Shared/en-US.lproj/ClearPrivateData.strings", | |
| "Shared/en.lproj/ClearPrivateDataConfirm.strings": "Shared/en-US.lproj/ClearPrivateDataConfirm.strings", | |
| "Shared/en.lproj/Default Browser.strings": "Shared/en-US.lproj/Default Browser.strings", | |
| "Shared/en.lproj/ErrorPages.strings": "Shared/en-US.lproj/ErrorPages.strings", | |
| "Shared/en.lproj/FindInPage.strings": "Shared/en-US.lproj/FindInPage.strings", | |
| "Shared/en.lproj/HistoryPanel.strings": "Shared/en-US.lproj/HistoryPanel.strings", | |
| "Shared/en.lproj/Intro.strings": "Shared/en-US.lproj/Intro.strings", | |
| "Shared/en.lproj/Localizable.strings": "Shared/en-US.lproj/Localizable.strings", | |
| "Shared/en.lproj/LoginManager.strings": "Shared/en-US.lproj/LoginManager.strings", | |
| "Shared/en.lproj/Menu.strings": "Shared/en-US.lproj/Menu.strings", | |
| "Shared/en.lproj/PrivateBrowsing.strings": "Shared/en-US.lproj/PrivateBrowsing.strings", | |
| "Shared/en.lproj/Search.strings": "Shared/en-US.lproj/Search.strings", | |
| "Shared/en.lproj/Shared.strings": "Shared/en-US.lproj/Shared.strings", | |
| "Shared/en.lproj/Storage.strings": "Shared/en-US.lproj/Storage.strings", | |
| "Shared/en.lproj/Today.strings": "Shared/en-US.lproj/Today.strings", | |
| "WidgetKit/en.lproj/Localizable.strings": "WidgetKit/en-US.lproj/Localizable.strings" | |
| } | |
| DRY_RUN = True | |
| sections_done = 0 | |
| entities_done = 0 | |
| problems = [] | |
| # ---- STEP 3: preview which resources are affected -------------------------- | |
| for res in resources: | |
| for old, new in RENAMES.items(): | |
| live = Entity.objects.filter(resource=res, obsolete=False, key__0=old).count() | |
| gone = Entity.objects.filter(resource=res, obsolete=True, key__0=old).count() | |
| clash = Entity.objects.filter(resource=res, obsolete=False, key__0=new).count() | |
| if live or gone or clash: | |
| note = f", {clash} ALREADY ON THE NEW KEY" if clash else "" | |
| print(f"{res.path}: {old!r} -> {new!r}: {live} live, {gone} obsolete{note}") | |
| # ---- STEP 4: rewrite key[0] ------------------------------------------------ | |
| # Obsolete entities are left alone on purpose: they can no longer match | |
| # anything, and rewriting them could collide with a live entity. The `clash` | |
| # check guards the same risk -- there is no unique constraint on | |
| # (resource, key), so sync would collapse a duplicate key into one dict entry | |
| # and silently obsolete one entity of the pair. | |
| assert project.sync_disabled, "Set sync_disabled=True in the Django admin first" | |
| with transaction.atomic(): | |
| for res in resources: | |
| for old, new in RENAMES.items(): | |
| sections = list(Section.objects.filter(resource=res, key__0=old)) | |
| entities = list(Entity.objects.filter(resource=res, obsolete=False, key__0=old)) | |
| if not sections and not entities: | |
| continue | |
| clash = Entity.objects.filter(resource=res, obsolete=False, key__0=new).count() | |
| if clash: | |
| problems.append(f"{res.path}: {clash} live entities already use {new!r}") | |
| continue | |
| for s in sections: | |
| s.key[0] = new | |
| for e in entities: | |
| e.key[0] = new | |
| Section.objects.bulk_update(sections, ["key"], batch_size=10000) | |
| Entity.objects.bulk_update(entities, ["key"], batch_size=10000) | |
| sections_done += len(sections) | |
| entities_done += len(entities) | |
| print(f"{res.path}: {old!r} -> {new!r} ({len(sections)} sections, {len(entities)} entities)") | |
| print(f"TOTAL: {sections_done} sections, {entities_done} entities") | |
| for p in problems: | |
| print(f"PROBLEM: {p}") | |
| if problems or DRY_RUN: | |
| transaction.set_rollback(True) | |
| print("ROLLED BACK -- nothing written") | |
| else: | |
| print("COMMITTED") |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment