#!/usr/bin/env python3 """ export_cat_translations.py Exports Polylang category translation data from local DB and generates SQL for production. Handles: 1. wp_terms for translated categories 2. wp_term_taxonomy (category + language taxonomy rows) 3. wp_term_taxonomy (term_translations groups) 4. wp_term_relationships (post→translated category assignments) """ import pymysql, re DB = dict(host='172.18.0.2', port=3306, user='wordpress_user', password='wordpress_pass', database='wordpress_db', charset='utf8mb4', cursorclass=pymysql.cursors.DictCursor) # Translated category term_ids on local (ES parent → lang: local_term_id) TRANS_CATS = { 6: {'en': 3077, 'fr': 3083, 'it': 3089, 'pt': 3095}, 21: {'en': 3080, 'fr': 3086, 'it': 3092, 'pt': 3098}, 1646: {'en': 2982, 'fr': 3032, 'it': 3048, 'pt': 3063}, 1647: {'en': 2986, 'fr': 3035, 'it': 3051, 'pt': 3066}, 1648: {'en': 2971, 'fr': 3029, 'it': 3045, 'pt': 3060}, 1650: {'en': 2964, 'fr': 3023, 'it': 3039, 'pt': 3054}, } # Collect all translated term_ids all_trans_ids = [] for mapping in TRANS_CATS.values(): all_trans_ids.extend(mapping.values()) all_trans_ids = sorted(set(all_trans_ids)) all_es_ids = sorted(TRANS_CATS.keys()) db = pymysql.connect(**DB) c = db.cursor() sql_lines = [ "-- Polylang category translations export", "-- Generated by export_cat_translations.py", "-- Run on production AFTER verifying no term_id conflicts", "", "SET NAMES utf8mb4;", "SET foreign_key_checks = 0;", "", ] # ─── 1. wp_terms ────────────────────────────────────────────────────────────── ids_str = ','.join(str(i) for i in all_trans_ids) c.execute(f"SELECT term_id, name, slug, term_group FROM wp_terms WHERE term_id IN ({ids_str})") rows = c.fetchall() sql_lines.append("-- 1. wp_terms (translated category names/slugs)") for r in rows: name = r['name'].replace("'", "''") slug = r['slug'].replace("'", "''") sql_lines.append( f"INSERT IGNORE INTO wp_terms (term_id, name, slug, term_group) " f"VALUES ({r['term_id']}, '{name}', '{slug}', {r['term_group']});" ) sql_lines.append("") # ─── 2. wp_term_taxonomy (category rows for translated terms) ──────────────── c.execute(f""" SELECT term_taxonomy_id, term_id, taxonomy, description, parent, count FROM wp_term_taxonomy WHERE term_id IN ({ids_str}) AND taxonomy='category' """) cat_rows = c.fetchall() sql_lines.append("-- 2. wp_term_taxonomy (taxonomy='category' for translated terms)") for r in cat_rows: desc = r['description'].replace("'", "''") if r['description'] else '' sql_lines.append( f"INSERT IGNORE INTO wp_term_taxonomy " f"(term_taxonomy_id, term_id, taxonomy, description, parent, count) " f"VALUES ({r['term_taxonomy_id']}, {r['term_id']}, 'category', " f"'{desc}', {r['parent']}, {r['count']});" ) sql_lines.append("") # ─── 3. wp_term_taxonomy (language rows for translated terms) ──────────────── c.execute(f""" SELECT term_taxonomy_id, term_id, taxonomy, description, parent, count FROM wp_term_taxonomy WHERE term_id IN ({ids_str}) AND taxonomy='language' """) lang_rows = c.fetchall() sql_lines.append("-- 3. wp_term_taxonomy (taxonomy='language' for translated terms)") for r in lang_rows: desc = r['description'].replace("'", "''") if r['description'] else '' sql_lines.append( f"INSERT IGNORE INTO wp_term_taxonomy " f"(term_taxonomy_id, term_id, taxonomy, description, parent, count) " f"VALUES ({r['term_taxonomy_id']}, {r['term_id']}, 'language', " f"'{desc}', {r['parent']}, {r['count']});" ) sql_lines.append("") # ─── 4. wp_term_taxonomy (term_translations groups for our ES categories) ─── # Get translation groups that contain any of our ES or translated term_ids all_ids_str = ','.join(str(i) for i in all_es_ids + all_trans_ids) c.execute(""" SELECT DISTINCT tt.term_taxonomy_id, tt.term_id, tt.taxonomy, tt.description, tt.parent, tt.count FROM wp_term_taxonomy tt WHERE tt.taxonomy = 'term_translations' """) all_tt_rows = c.fetchall() # Filter to only those that reference our category term_ids relevant_tt = [] for r in all_tt_rows: desc = r['description'] or '' # Check if any of our term_ids appear in the description for tid in all_es_ids + all_trans_ids: if f'i:{tid};' in desc or f'i:{tid}' == desc.strip(): relevant_tt.append(r) break sql_lines.append("-- 4. wp_term_taxonomy (taxonomy='term_translations' groups for our categories)") for r in relevant_tt: desc = r['description'].replace("'", "''") if r['description'] else '' sql_lines.append( f"INSERT INTO wp_term_taxonomy " f"(term_taxonomy_id, term_id, taxonomy, description, parent, count) " f"VALUES ({r['term_taxonomy_id']}, {r['term_id']}, 'term_translations', " f"'{desc}', {r['parent']}, {r['count']}) " f"ON DUPLICATE KEY UPDATE description=VALUES(description), count=VALUES(count);" ) sql_lines.append("") # ─── 5. wp_terms for term_translations taxonomy entries ───────────────────── tt_term_ids = [r['term_id'] for r in relevant_tt] if tt_term_ids: tt_ids_str = ','.join(str(i) for i in tt_term_ids) c.execute(f"SELECT term_id, name, slug, term_group FROM wp_terms WHERE term_id IN ({tt_ids_str})") tt_term_rows = c.fetchall() # Insert before the term_taxonomy rows (we need to reorder — prepend) term_inserts = [] for r in tt_term_rows: name = r['name'].replace("'", "''") slug = r['slug'].replace("'", "''") term_inserts.append( f"INSERT IGNORE INTO wp_terms (term_id, name, slug, term_group) " f"VALUES ({r['term_id']}, '{name}', '{slug}', {r['term_group']});" ) # Insert after section 1 idx = sql_lines.index("-- 2. wp_term_taxonomy (taxonomy='category' for translated terms)") sql_lines[idx:idx] = ["-- 1b. wp_terms for term_translations taxonomy"] + term_inserts + [""] # ─── 6. wp_term_relationships (post→translated category) ───────────────────── # Get term_taxonomy_ids for translated categories cat_tt_ids = [r['term_taxonomy_id'] for r in cat_rows] if cat_tt_ids: cat_tt_str = ','.join(str(i) for i in cat_tt_ids) c.execute(f""" SELECT object_id, term_taxonomy_id, term_order FROM wp_term_relationships WHERE term_taxonomy_id IN ({cat_tt_str}) ORDER BY term_taxonomy_id, object_id """) rel_rows = c.fetchall() sql_lines.append("-- 5. wp_term_relationships (posts → translated categories)") sql_lines.append(f"-- {len(rel_rows)} relationships") # Batch INSERT for efficiency if rel_rows: batch = [] for r in rel_rows: batch.append(f"({r['object_id']},{r['term_taxonomy_id']},{r['term_order']})") if len(batch) >= 500: sql_lines.append( "INSERT IGNORE INTO wp_term_relationships (object_id, term_taxonomy_id, term_order) VALUES " + ','.join(batch) + ";" ) batch = [] if batch: sql_lines.append( "INSERT IGNORE INTO wp_term_relationships (object_id, term_taxonomy_id, term_order) VALUES " + ','.join(batch) + ";" ) sql_lines.append("") # ─── 7. wp_term_relationships (translated terms → language taxonomy) ───────── lang_tt_ids = [r['term_taxonomy_id'] for r in lang_rows] if lang_tt_ids: lang_tt_str = ','.join(str(i) for i in lang_tt_ids) c.execute(f""" SELECT object_id, term_taxonomy_id, term_order FROM wp_term_relationships WHERE term_taxonomy_id IN ({lang_tt_str}) """) lang_rel_rows = c.fetchall() sql_lines.append("-- 6. wp_term_relationships (translated category terms → language taxonomy)") sql_lines.append(f"-- {len(lang_rel_rows)} relationships") if lang_rel_rows: batch = [] for r in lang_rel_rows: batch.append(f"({r['object_id']},{r['term_taxonomy_id']},{r['term_order']})") if len(batch) >= 500: sql_lines.append( "INSERT IGNORE INTO wp_term_relationships (object_id, term_taxonomy_id, term_order) VALUES " + ','.join(batch) + ";" ) batch = [] if batch: sql_lines.append( "INSERT IGNORE INTO wp_term_relationships (object_id, term_taxonomy_id, term_order) VALUES " + ','.join(batch) + ";" ) sql_lines.append("") sql_lines.append("SET foreign_key_checks = 1;") sql_lines.append("") sql_lines.append("-- Done.") db.close() output = '\n'.join(sql_lines) with open('/tmp/cat_translations_prod.sql', 'w', encoding='utf-8') as f: f.write(output) print(f"Written to /tmp/cat_translations_prod.sql") print(f" {len(rows)} translated terms (wp_terms)") print(f" {len(cat_rows)} category taxonomy rows") print(f" {len(lang_rows)} language taxonomy rows") print(f" {len(relevant_tt)} term_translations groups") if cat_tt_ids: print(f" {len(rel_rows)} post→category relationships") if lang_tt_ids: print(f" {len(lang_rel_rows)} term→language relationships")