store: el árbol de categorías, igual que el original (colores y sin fantasmas)

Investigadas las 750 categorías de la BD contra el HTML real del original. Los
ítems estaban perfectos (554 hojas, 3068 ítems, mismo contenido en cada una: no
faltaba ninguno). Todo lo roto era de categorías:

- 45 CATEGORÍAS FANTASMA, todas con nombre de código ("11-1", "23-10"). El seed
  dedujo el árbol de los códigos de las hojas ("11-1-1" => padre "11-1"), pero
  el original NO siempre pinta ese nivel: en 45 hojas el "-1-" del código no
  corresponde a nada y cuelgan directas de su raíz. Ahora se lee el anidamiento
  real (<ul>/<li>): 705 categorías (20 + 131 + 554) en vez de 750.
- El nombre de una categoría NO es texto plano, es HTML, y lo estábamos
  aplanando: 141 nombres perdían su color y 40 cabeceras el suyo. Hay dos
  mecanismos y los dos hacen falta: la clase en la CABECERA, que sustituye a
  first-brown para colorear la categoría entera ("Brujo" en color de brujo), y
  los <span class="no-toggle X"> dentro del nombre (clases y las etiquetas
  [Nivel 232 a 245] / (DPS) en otro tono).
- 21 nombres TRUNCADOS en Tokens de Armadura: guardábamos "Guerrero" donde el
  original dice "Guerrero, Sacerdote, Druida" (solo se quedó el primer span).

El HTML se trocea en el SERVIDOR (`parseCategoryName`) y al cliente le llegan
solo texto + nombre de clase, así que no hay HTML de la BD entrando en el DOM.
El texto sin color va suelto, NO envuelto en <span>: el tema tiene
`span { color: #fff }` y lo pondría blanco en vez de heredar el de la cabecera.

`name` pasa a VARCHAR(512): con el HTML, el más largo mide 198 y no cabía en 191.
El name_en se genera en el propio seed traduciendo solo el TEXTO y respetando
las etiquetas, así que sql/store_category_en.sql sobra y se borra.
gen_store_catalog.py regenera el seed entero (categorías del HTML, ítems de la
BD, que ya estaban verificados) y es reproducible byte a byte.

Verificado en el navegador contra el original: mismo árbol, 0 categorías con
nombre de código, "[Nivel 232 a 245]" en marrón tenue y los colores de clase
oficiales (Guerrero #C79C6E, Sacerdote blanco, Druida #FF7D0A). En inglés
también, conservando los colores.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-15 11:24:19 +00:00
parent fb28e97307
commit 24248bc603
6 changed files with 1068 additions and 1064 deletions
+20 -3
View File
@@ -1,6 +1,6 @@
'use client'
import { useEffect, useState } from 'react'
import { Fragment, useEffect, useState } from 'react'
import { useTranslations, useLocale } from 'next-intl'
import { CharacterSelect, type CharOption } from '@/components/CharacterSelect'
import { WowheadLink } from '@/components/WowheadLink'
@@ -62,8 +62,25 @@ function CategoryNode({
}, [open])
return (
<li>
<span className={`${spanClass} first-brown`} role="button" tabIndex={0} onClick={() => setOpen((o) => !o)}>
<i className={`fas fa-angle-right green-info${open ? ' fa-rotate-90' : ''}`} /> {cat.name}
{/* `cat.css` y no `first-brown` fijo: el original colorea algunas
categorías enteras por clase (p.ej. "Brujo" en color de brujo) poniendo
esa clase EN LUGAR de first-brown. Y dentro del nombre, cada parte
puede llevar la suya ("Guerrero, Sacerdote, Druida", "[Nivel 232 a
245]" en otro tono). */}
<span className={`${spanClass} ${cat.css}`} role="button" tabIndex={0} onClick={() => setOpen((o) => !o)}>
<i className={`fas fa-angle-right green-info${open ? ' fa-rotate-90' : ''}`} />{' '}
{/* El texto sin color va SUELTO, no envuelto: el tema tiene
`span { color: #fff }`, así que meterlo en un <span> lo pondría
blanco en vez de heredar el color de la cabecera. */}
{cat.name.map((p, i) =>
p.cls ? (
<span key={i} className={`no-toggle ${p.cls}`}>
{p.text}
</span>
) : (
<Fragment key={i}>{p.text}</Fragment>
),
)}
</span>
{open && (
<>
+61 -3
View File
@@ -27,9 +27,25 @@ export interface StoreItem {
preview: string | null
}
/**
* Un trozo del nombre de una categoría: texto y, si lo lleva, la clase de color
* con la que hay que pintarlo (`warrior`, `second-brown`…).
*
* El nombre viene de BD como HTML (así lo hace el original: colorea los nombres
* de clase y pone las etiquetas en otro tono). Se trocea AQUÍ, en el servidor, y
* al cliente le llega ya estructurado: así se pinta con React y no hace falta
* meter HTML de la BD en el DOM.
*/
export interface NamePart {
text: string
cls?: string
}
export interface StoreCategory {
code: string
name: string
name: NamePart[]
/** Clase de la cabecera; a veces sustituye a first-brown para colorearla entera. */
css: string
depth: number
children: StoreCategory[]
items: StoreItem[]
@@ -39,6 +55,7 @@ interface CatRow extends RowDataPacket {
code: string
parent_code: string | null
name: string
css: string
depth: number
sort: number
}
@@ -55,6 +72,40 @@ interface ItemRow extends RowDataPacket {
sort: number
}
/** `<span class="no-toggle X">…</span>` — la única etiqueta que usa el original. */
const NAME_SPAN = /<span class="no-toggle ([a-z0-9- ]+)">([^<]*)<\/span>/g
function unescapeHtml(s: string): string {
return s
.replace(/&lt;/g, '<')
.replace(/&gt;/g, '>')
.replace(/&quot;/g, '"')
.replace(/&#39;/g, "'")
.replace(/&amp;/g, '&')
}
/**
* Trocea el nombre HTML de una categoría en partes con su color.
*
* Solo entiende `<span class="no-toggle X">`, que es lo único que usa el
* catálogo. Cualquier otra etiqueta se ignora y queda su texto: si alguien mete
* HTML raro en la BD, aquí NO puede acabar inyectado (al cliente solo le llegan
* texto y un nombre de clase).
*/
export function parseCategoryName(html: string): NamePart[] {
const parts: NamePart[] = []
let last = 0
for (const m of html.matchAll(NAME_SPAN)) {
const plain = html.slice(last, m.index).replace(/<[^>]*>/g, '')
if (plain) parts.push({ text: unescapeHtml(plain) })
if (m[2]) parts.push({ text: unescapeHtml(m[2]), cls: m[1] })
last = m.index + m[0].length
}
const tail = html.slice(last).replace(/<[^>]*>/g, '')
if (tail) parts.push({ text: unescapeHtml(tail) })
return parts.length ? parts : [{ text: unescapeHtml(html.replace(/<[^>]*>/g, '')) }]
}
/**
* Árbol completo de la tienda (categorías anidadas con sus ítems en las hojas).
*
@@ -75,7 +126,7 @@ export async function getStoreCatalog(locale: string): Promise<StoreCategory[]>
let items: ItemRow[] = []
try {
;[cats] = await db(DB.default).query<CatRow[]>(
`SELECT code, parent_code, COALESCE(NULLIF(${catNameCol}, ''), name) AS name, depth, sort
`SELECT code, parent_code, COALESCE(NULLIF(${catNameCol}, ''), name) AS name, css, depth, sort
FROM home_store_category ORDER BY sort`,
)
;[items] = await db(DB.default).query<ItemRow[]>(
@@ -91,7 +142,14 @@ export async function getStoreCatalog(locale: string): Promise<StoreCategory[]>
const nodes = new Map<string, StoreCategory>()
for (const c of cats) {
nodes.set(c.code, { code: c.code, name: c.name, depth: c.depth, children: [], items: [] })
nodes.set(c.code, {
code: c.code,
name: parseCategoryName(c.name),
css: c.css || 'first-brown',
depth: c.depth,
children: [],
items: [],
})
}
const roots: StoreCategory[] = []
for (const c of cats) {
+212
View File
@@ -0,0 +1,212 @@
#!/usr/bin/env python
"""Regenera sql/store_catalog.sql desde el HTML real del store original.
USO: /root/NovaWoW/.venv/bin/python web-next/sql/db2/gen_store_catalog.py
(necesita /root/store1.html y la BD, para los ítems)
Por qué se regenera desde el HTML y no se parchea la BD: el seed anterior dedujo
el árbol de los CÓDIGOS de las hojas ("11-1-1" => padre "11-1"), y eso inventa
categorías fantasma. En el original, 45 hojas cuelgan DIRECTAS de su raíz: el
"-1-" del código no corresponde a ningún nivel pintado. Aquí se lee el
anidamiento de verdad (<ul>/<li>).
Además el nombre de una categoría NO es texto plano, lleva HTML:
- color en la cabecera: <span class="store-subcat warlock">Brujo</span>
- color dentro del nombre: <span class="no-toggle warrior">Guerrero</span>, ...
- etiquetas: Armas PvE <span class="no-toggle third-brown">[Nivel 232 a 245]</span>
Se conserva tal cual (columna `name`) + la clase de la cabecera (`css`). El
inglés (`name_en`) se genera traduciendo SOLO el texto y respetando las etiquetas.
Los ÍTEMS se vuelcan de la BD, no del HTML: ya están verificados contra el
original (554 hojas, 3068 ítems, mismo contenido en cada una).
"""
import os
import re
import subprocess
import sys
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
from translate_categories import tr # noqa: E402
HERE = os.path.dirname(os.path.abspath(__file__))
OUT = os.path.join(HERE, '..', 'store_catalog.sql')
SRC = '/root/store1.html'
TOKEN = re.compile(
r'<span class="store-(cat|subcat|sub-subcat) ([a-z0-9\- ]+)">'
r'|<div class="item-list" id="([0-9\-]+)">'
)
SPAN_TAG = re.compile(r'</?span\b')
def span_inner(s, start):
"""Contenido de un <span>, respetando anidamiento."""
i = s.index('>', start) + 1
depth, j = 1, i
while True:
m = SPAN_TAG.search(s, j)
if not m:
return s[i:]
if m.group(0) == '<span':
depth += 1
else:
depth -= 1
if depth == 0:
return s[i:m.start()]
j = m.end()
def clean(name):
name = re.sub(r'^\s*<i class="fas fa-angle-right green-info"></i>\s*', '', name)
return re.sub(r'\s+', ' ', name).strip()
def parse_tree(html):
"""[(code, parent, name_html, css, depth)] en orden de documento."""
out, seen = [], set()
root = sub = pending = None
for m in TOKEN.finditer(html):
kind, cls, code = m.group(1), m.group(2), m.group(3)
if kind:
entry = (clean(span_inner(html, m.start())), cls)
if kind == 'cat':
root, sub, pending = entry, None, None
elif kind == 'subcat':
sub, pending = entry, None
else:
pending = entry
elif code and pending:
p = code.split('-')
if len(p) != 3:
continue
rc, sc = p[0], f'{p[0]}-{p[1]}'
if root and rc not in seen:
seen.add(rc); out.append((rc, None, root[0], root[1], 1))
# La subcategoría solo existe si el HTML la pinta de verdad.
if sub and sc not in seen:
seen.add(sc); out.append((sc, rc, sub[0], sub[1], 2))
if code not in seen:
seen.add(code)
out.append((code, sc if sub else rc, pending[0], pending[1], 3))
pending = None
return out
# Traducción respetando el HTML. Solo hay dos formas en los datos reales:
# A) "TEXTO <span class="no-toggle second|third-brown">(SUFIJO)</span>"
# B) "<span class="no-toggle CLASE">Clase</span>, <span ...>..."
TAG_A = re.compile(r'^(.*?)\s*<span class="no-toggle ([a-z\-]+)">([\(\[].*?[\)\]])</span>$')
SPAN_B = re.compile(r'<span class="no-toggle ([a-z\-]+)">([^<]+)</span>')
SUFIJO = re.compile(r'\s*([\(\[][^\)\]]*[\)\]])\s*$')
def tr_html(name):
"""Traduce el nombre conservando las etiquetas. None si no sabe."""
if '<' not in name:
return tr(name)
m = TAG_A.match(name)
if m:
# Se traduce la frase ENTERA (el traductor tiene reglas de frase: "Nivel
# de objeto 232 (DPS)"), y luego se vuelve a envolver el sufijo.
base, cls, _tag = m.groups()
full = tr(f'{base} {_tag}'.strip())
if not full:
return None
s = SUFIJO.search(full)
if not s:
return full
return f'{full[:s.start()].strip()} <span class="no-toggle {cls}">{s.group(1)}</span>'
if name.startswith('<span'):
# Lista de clases: se traduce cada una por separado.
def one(mm):
t = tr(mm.group(2))
return f'<span class="no-toggle {mm.group(1)}">{t or mm.group(2)}</span>'
return SPAN_B.sub(one, name)
return None
def esc(s):
return s.replace('\\', '\\\\').replace("'", "''")
def db_items():
"""Ítems desde la BD (ya verificados contra el original)."""
env = os.path.join(HERE, '..', '..', '.env.local')
cfg = dict(re.findall(r'^(\w+)=(.*)$', open(env, encoding='utf-8').read(), re.M))
q = ('SELECT category_code,item_id,name,IFNULL(icon,""),quantity,currency,price,'
'IFNULL(preview,""),sort FROM home_store_item ORDER BY sort,id')
out = subprocess.run(
['mysql', '-h', cfg.get('DB_HOST', '127.0.0.1'), '-u', cfg['DB_USER'],
f'-p{cfg["DB_PASSWORD"]}', '--default-character-set=utf8mb4', '-N', '--raw',
cfg.get('DB_NAME_DEFAULT', 'django_wow'), '-e', q],
capture_output=True, text=True, check=True)
return [l.split('\t') for l in out.stdout.splitlines() if l]
cats = parse_tree(open(SRC, encoding='utf-8', errors='replace').read())
items = db_items()
sin_tr = [c for c in cats if tr_html(c[2]) is None]
print(f'categorías: {len(cats)} | ítems: {len(items)} | sin traducir: {len(sin_tr)}', file=sys.stderr)
for c in sin_tr[:10]:
print(' sin traducir:', c[0], c[2][:60], file=sys.stderr)
with open(OUT, 'w', encoding='utf-8') as f:
f.write("""-- Catálogo de la tienda (categorías + ítems), extraído del sistema antiguo (BENNU).
-- NO editar a mano: regenerar con `sql/db2/gen_store_catalog.py` (ver su cabecera).
--
-- El nombre de una categoría es HTML a propósito: el original colorea los
-- nombres de clase y pone las etiquetas ([Nivel 232 a 245], (DPS)) en otro tono.
-- `css` es la clase de la CABECERA, que a veces sustituye a first-brown para
-- colorear la categoría entera (p.ej. "Brujo" en color de brujo).
SET NAMES utf8mb4;
DROP TABLE IF EXISTS home_store_item;
DROP TABLE IF EXISTS home_store_category;
CREATE TABLE home_store_category (
id INT AUTO_INCREMENT PRIMARY KEY,
code VARCHAR(32) NOT NULL UNIQUE,
parent_code VARCHAR(32) NULL,
-- 512 y no 191: los nombres llevan HTML y el más largo pasa de 191.
name VARCHAR(512) NOT NULL COMMENT 'HTML: puede llevar <span class="no-toggle X">',
name_en VARCHAR(512) DEFAULT NULL COMMENT 'igual que name, en inglés; NULL = usar name',
css VARCHAR(32) NOT NULL DEFAULT 'first-brown' COMMENT 'clase de la cabecera',
depth TINYINT NOT NULL,
sort INT NOT NULL,
KEY k_parent (parent_code)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE home_store_item (
id INT AUTO_INCREMENT PRIMARY KEY,
category_code VARCHAR(32) NOT NULL,
item_id INT NOT NULL,
name VARCHAR(191) NOT NULL,
icon VARCHAR(128) NULL,
quantity INT NOT NULL DEFAULT 1,
currency ENUM('pd','pv') NOT NULL DEFAULT 'pd',
price INT NOT NULL,
preview VARCHAR(64) NULL,
sort INT NOT NULL,
KEY k_cat (category_code),
KEY k_item (item_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
""")
CH = 200
for i in range(0, len(cats), CH):
f.write('INSERT INTO home_store_category (code,parent_code,name,name_en,css,depth,sort) VALUES\n')
rows = []
for n, (code, parent, name, css, depth) in enumerate(cats[i:i + CH], start=i):
en = tr_html(name)
rows.append("('%s',%s,'%s',%s,'%s',%d,%d)" % (
esc(code), f"'{esc(parent)}'" if parent else 'NULL', esc(name),
f"'{esc(en)}'" if en else 'NULL', esc(css), depth, n))
f.write(',\n'.join(rows) + ';\n')
for i in range(0, len(items), CH):
f.write('INSERT INTO home_store_item (category_code,item_id,name,icon,quantity,currency,price,preview,sort) VALUES\n')
rows = []
for cc, iid, nm, ic, qty, cur, pr, pv, so in items[i:i + CH]:
rows.append("('%s',%s,'%s',%s,%s,'%s',%s,%s,%s)" % (
esc(cc), iid, esc(nm), f"'{esc(ic)}'" if ic else 'NULL', qty, cur, pr,
f"'{esc(pv)}'" if pv else 'NULL', so))
f.write(',\n'.join(rows) + ';\n')
print(f'escrito {OUT}', file=sys.stderr)
+8 -35
View File
@@ -1,7 +1,7 @@
"""Traduce los nombres de home_store_category al inglés -> sql/store_category_en.sql.
"""Traducción al inglés de los nombres de categoría de la tienda.
USO: mysql ... -N -e 'SELECT DISTINCT name FROM home_store_category' \\
| /root/NovaWoW/.venv/bin/python sql/db2/translate_categories.py
Es una LIBRERÍA: la usa `gen_store_catalog.py` para el `name_en` del seed.
Ejecutándolo se comprueban traducciones sueltas (nombres por stdin).
Prioridad: términos OFICIALES sacados de los DB2 del cliente (ChrClasses,
ItemSet, ItemSubClass) y de los nombres bilingües de home_item_data. Solo se
@@ -163,40 +163,13 @@ def tr(name):
return None # sin traducir
def esc(s):
return s.replace('\\', '\\\\').replace("'", "''")
if __name__ == '__main__':
# Modo comprobación: pásale nombres por stdin y dice cómo los traduce.
# El seed lo genera `gen_store_catalog.py`, que importa `tr` de aquí.
names = [l.rstrip('\n') for l in sys.stdin if l.strip()]
if not names:
sys.exit('Pásame los nombres por stdin:\n'
" mysql ... -N -e 'SELECT DISTINCT name FROM home_store_category' | "
'python translate_categories.py')
out, missing = {}, []
sys.exit('Uso: ... | python translate_categories.py (comprueba traducciones)\n'
'Para regenerar el catálogo: python gen_store_catalog.py')
for n in names:
t = tr(n)
(missing.append(n) if t is None else out.__setitem__(n, t))
if missing:
print('SIN TRADUCIR (%d):' % len(missing), file=sys.stderr)
for m in missing:
print(' ', m, file=sys.stderr)
bad = {k: v for k, v in out.items() if not v or 'None' in str(v)}
if bad:
sys.exit(f'ERROR: traducciones corruptas: {bad}')
with open(OUT, 'w', encoding='utf-8') as f:
f.write("""-- Nombres en inglés de las categorías de la tienda. NO editar a mano:
-- regenerar con sql/db2/translate_categories.py (ver cabecera del script).
-- Los términos del JUEGO salen de los DB2 del cliente (ChrClasses, ItemSet,
-- ItemSubClass): así casan con lo que ve un jugador en inglés. Solo se traduce
-- a mano lo que la tienda se inventó y no existe en el juego.
ALTER TABLE `home_store_category`
ADD COLUMN `name_en` varchar(191) DEFAULT NULL COMMENT 'nombre en inglés; NULL = usar name' AFTER `name`;
""")
for k, v in sorted(out.items()):
f.write("UPDATE `home_store_category` SET `name_en` = '%s' WHERE `name` = '%s';\n"
% (esc(v), esc(k)))
print(f'escritas {len(out)} categorías en {OUT}', file=sys.stderr)
print('%-52s -> %s' % (n, t if t else '*** SIN TRADUCIR ***'))
File diff suppressed because it is too large Load Diff
-226
View File
@@ -1,226 +0,0 @@
-- Nombres en inglés de las categorías de la tienda. NO editar a mano:
-- regenerar con sql/db2/translate_categories.py (ver cabecera del script).
-- Los términos del JUEGO salen de los DB2 del cliente (ChrClasses, ItemSet,
-- ItemSubClass): así casan con lo que ve un jugador en inglés. Solo se traduce
-- a mano lo que la tienda se inventó y no existe en el juego.
ALTER TABLE `home_store_category`
ADD COLUMN `name_en` varchar(191) DEFAULT NULL COMMENT 'nombre en inglés; NULL = usar name' AFTER `name`;
UPDATE `home_store_category` SET `name_en` = '11-1' WHERE `name` = '11-1';
UPDATE `home_store_category` SET `name_en` = '11-2' WHERE `name` = '11-2';
UPDATE `home_store_category` SET `name_en` = '11-3' WHERE `name` = '11-3';
UPDATE `home_store_category` SET `name_en` = '11-4' WHERE `name` = '11-4';
UPDATE `home_store_category` SET `name_en` = '11-5' WHERE `name` = '11-5';
UPDATE `home_store_category` SET `name_en` = '11-6' WHERE `name` = '11-6';
UPDATE `home_store_category` SET `name_en` = '11-7' WHERE `name` = '11-7';
UPDATE `home_store_category` SET `name_en` = '11-8' WHERE `name` = '11-8';
UPDATE `home_store_category` SET `name_en` = '14-1' WHERE `name` = '14-1';
UPDATE `home_store_category` SET `name_en` = '14-2' WHERE `name` = '14-2';
UPDATE `home_store_category` SET `name_en` = '14-3' WHERE `name` = '14-3';
UPDATE `home_store_category` SET `name_en` = '14-4' WHERE `name` = '14-4';
UPDATE `home_store_category` SET `name_en` = '14-5' WHERE `name` = '14-5';
UPDATE `home_store_category` SET `name_en` = '14-6' WHERE `name` = '14-6';
UPDATE `home_store_category` SET `name_en` = '16-1' WHERE `name` = '16-1';
UPDATE `home_store_category` SET `name_en` = '18-1' WHERE `name` = '18-1';
UPDATE `home_store_category` SET `name_en` = '23-10' WHERE `name` = '23-10';
UPDATE `home_store_category` SET `name_en` = '23-14' WHERE `name` = '23-14';
UPDATE `home_store_category` SET `name_en` = '24-1' WHERE `name` = '24-1';
UPDATE `home_store_category` SET `name_en` = '24-10' WHERE `name` = '24-10';
UPDATE `home_store_category` SET `name_en` = '24-12' WHERE `name` = '24-12';
UPDATE `home_store_category` SET `name_en` = '24-13' WHERE `name` = '24-13';
UPDATE `home_store_category` SET `name_en` = '24-14' WHERE `name` = '24-14';
UPDATE `home_store_category` SET `name_en` = '24-4' WHERE `name` = '24-4';
UPDATE `home_store_category` SET `name_en` = '24-6' WHERE `name` = '24-6';
UPDATE `home_store_category` SET `name_en` = '24-8' WHERE `name` = '24-8';
UPDATE `home_store_category` SET `name_en` = '24-9' WHERE `name` = '24-9';
UPDATE `home_store_category` SET `name_en` = '25-1' WHERE `name` = '25-1';
UPDATE `home_store_category` SET `name_en` = '25-2' WHERE `name` = '25-2';
UPDATE `home_store_category` SET `name_en` = '25-3' WHERE `name` = '25-3';
UPDATE `home_store_category` SET `name_en` = '25-4' WHERE `name` = '25-4';
UPDATE `home_store_category` SET `name_en` = '25-5' WHERE `name` = '25-5';
UPDATE `home_store_category` SET `name_en` = '25-6' WHERE `name` = '25-6';
UPDATE `home_store_category` SET `name_en` = '25-7' WHERE `name` = '25-7';
UPDATE `home_store_category` SET `name_en` = '25-8' WHERE `name` = '25-8';
UPDATE `home_store_category` SET `name_en` = '8-1' WHERE `name` = '8-1';
UPDATE `home_store_category` SET `name_en` = '8-2' WHERE `name` = '8-2';
UPDATE `home_store_category` SET `name_en` = '8-3' WHERE `name` = '8-3';
UPDATE `home_store_category` SET `name_en` = '8-4' WHERE `name` = '8-4';
UPDATE `home_store_category` SET `name_en` = '9-1' WHERE `name` = '9-1';
UPDATE `home_store_category` SET `name_en` = '9-2' WHERE `name` = '9-2';
UPDATE `home_store_category` SET `name_en` = '9-3' WHERE `name` = '9-3';
UPDATE `home_store_category` SET `name_en` = '9-4' WHERE `name` = '9-4';
UPDATE `home_store_category` SET `name_en` = '9-5' WHERE `name` = '9-5';
UPDATE `home_store_category` SET `name_en` = '9-6' WHERE `name` = '9-6';
UPDATE `home_store_category` SET `name_en` = 'Trinkets' WHERE `name` = 'Abalorios';
UPDATE `home_store_category` SET `name_en` = 'PvE Accessories [Level 226 to 245]' WHERE `name` = 'Accesorios PvE [Nivel 226 a 245]';
UPDATE `home_store_category` SET `name_en` = 'PvP Accessories [Level 226 to 245]' WHERE `name` = 'Accesorios PvP [Nivel 226 a 245]';
UPDATE `home_store_category` SET `name_en` = 'PvE Transmog Accessories [Up to level 213]' WHERE `name` = 'Accesorios de Transfiguración PvE [Hasta nivel 213]';
UPDATE `home_store_category` SET `name_en` = 'PvP Transmog Accessories [Up to level 213]' WHERE `name` = 'Accesorios de Transfiguración PvP [Hasta nivel 213]';
UPDATE `home_store_category` SET `name_en` = 'Rings' WHERE `name` = 'Anillos';
UPDATE `home_store_category` SET `name_en` = 'Bows' WHERE `name` = 'Arcos';
UPDATE `home_store_category` SET `name_en` = 'Bloodfang Armor (T2 Recolor)' WHERE `name` = 'Armadura Colmillo de Sangre (T2 Recolor)';
UPDATE `home_store_category` SET `name_en` = 'Dragonstalker Armor (T2 Recolor)' WHERE `name` = 'Armadura de Acechadragón (T2 Recolor)';
UPDATE `home_store_category` SET `name_en` = 'Judgement Armor (T2 Recolor)' WHERE `name` = 'Armadura de la Sentencia (T2 Recolor)';
UPDATE `home_store_category` SET `name_en` = 'Weapons' WHERE `name` = 'Armas';
UPDATE `home_store_category` SET `name_en` = 'PvE Weapons [Level 232 to 245]' WHERE `name` = 'Armas PvE [Nivel 232 a 245]';
UPDATE `home_store_category` SET `name_en` = 'PvP Weapons [Level 232 to 245]' WHERE `name` = 'Armas PvP [Nivel 232 a 245]';
UPDATE `home_store_category` SET `name_en` = 'Polearms' WHERE `name` = 'Armas de asta';
UPDATE `home_store_category` SET `name_en` = 'Guns' WHERE `name` = 'Armas de fuego';
UPDATE `home_store_category` SET `name_en` = 'Fist Weapons' WHERE `name` = 'Armas de puño';
UPDATE `home_store_category` SET `name_en` = 'PvE Transmog Weapons [Up to level 226]' WHERE `name` = 'Armas de transfiguración PvE [Hasta nivel 226]';
UPDATE `home_store_category` SET `name_en` = 'PvP Transmog Weapons [Up to level 213]' WHERE `name` = 'Armas de transfiguración PvP [Hasta nivel 213]';
UPDATE `home_store_category` SET `name_en` = 'Thrown' WHERE `name` = 'Arrojadizas';
UPDATE `home_store_category` SET `name_en` = 'Netherwind Regalia (T2 Recolor)' WHERE `name` = 'Atavío Viento Abisal (T2 Recolor)';
UPDATE `home_store_category` SET `name_en` = 'Crossbows' WHERE `name` = 'Ballestas';
UPDATE `home_store_category` SET `name_en` = 'Staves' WHERE `name` = 'Bastones';
UPDATE `home_store_category` SET `name_en` = 'Soul Bag' WHERE `name` = 'Bolsa de almas';
UPDATE `home_store_category` SET `name_en` = 'Bags' WHERE `name` = 'Bolsas';
UPDATE `home_store_category` SET `name_en` = 'Profession Bags' WHERE `name` = 'Bolsas de profesiones';
UPDATE `home_store_category` SET `name_en` = 'Leather Boots' WHERE `name` = 'Botas de cuero';
UPDATE `home_store_category` SET `name_en` = 'Mail Boots' WHERE `name` = 'Botas de malla';
UPDATE `home_store_category` SET `name_en` = 'Plate Boots' WHERE `name` = 'Botas de placa';
UPDATE `home_store_category` SET `name_en` = 'Plate Boots' WHERE `name` = 'Botas de placas';
UPDATE `home_store_category` SET `name_en` = 'Cloth Boots' WHERE `name` = 'Botas de tela';
UPDATE `home_store_category` SET `name_en` = 'Leather Bracers' WHERE `name` = 'Brazales de cuero';
UPDATE `home_store_category` SET `name_en` = 'Mail Bracers' WHERE `name` = 'Brazales de malla';
UPDATE `home_store_category` SET `name_en` = 'Plate Bracers' WHERE `name` = 'Brazales de placa';
UPDATE `home_store_category` SET `name_en` = 'Plate Bracers' WHERE `name` = 'Brazales de placas';
UPDATE `home_store_category` SET `name_en` = 'Cloth Bracers' WHERE `name` = 'Brazales de tela';
UPDATE `home_store_category` SET `name_en` = 'Warlock' WHERE `name` = 'Brujo';
UPDATE `home_store_category` SET `name_en` = 'Death Knight' WHERE `name` = 'Caballero de la Muerte';
UPDATE `home_store_category` SET `name_en` = 'Cloaks' WHERE `name` = 'Capas';
UPDATE `home_store_category` SET `name_en` = 'Quivers | Ammo' WHERE `name` = 'Carcajs | Munición';
UPDATE `home_store_category` SET `name_en` = 'Hunter' WHERE `name` = 'Cazador';
UPDATE `home_store_category` SET `name_en` = 'Shaman' WHERE `name` = 'Chamán';
UPDATE `home_store_category` SET `name_en` = 'Leather Belts' WHERE `name` = 'Cinturones de cuero';
UPDATE `home_store_category` SET `name_en` = 'Mail Belts' WHERE `name` = 'Cinturones de malla';
UPDATE `home_store_category` SET `name_en` = 'Plate Belts' WHERE `name` = 'Cinturones de placa';
UPDATE `home_store_category` SET `name_en` = 'Plate Belts' WHERE `name` = 'Cinturones de placas';
UPDATE `home_store_category` SET `name_en` = 'Cloth Belts' WHERE `name` = 'Cinturones de tela';
UPDATE `home_store_category` SET `name_en` = 'Necks' WHERE `name` = 'Collares';
UPDATE `home_store_category` SET `name_en` = 'Food & Drinks' WHERE `name` = 'Comidas y bebidas';
UPDATE `home_store_category` SET `name_en` = 'Consumables' WHERE `name` = 'Consumibles';
UPDATE `home_store_category` SET `name_en` = 'Containers' WHERE `name` = 'Contenedores';
UPDATE `home_store_category` SET `name_en` = 'Leather' WHERE `name` = 'Cueros';
UPDATE `home_store_category` SET `name_en` = 'Daggers' WHERE `name` = 'Dagas';
UPDATE `home_store_category` SET `name_en` = 'Two-Hand' WHERE `name` = 'Dos manos';
UPDATE `home_store_category` SET `name_en` = 'Druid' WHERE `name` = 'Druida';
UPDATE `home_store_category` SET `name_en` = 'Elementals' WHERE `name` = 'Elementales';
UPDATE `home_store_category` SET `name_en` = 'Elixirs' WHERE `name` = 'Elíxires';
UPDATE `home_store_category` SET `name_en` = 'Darkruned Battlegear (T8 Recolor)' WHERE `name` = 'Equipo de Batalla Runaoscura (T8 Recolor)';
UPDATE `home_store_category` SET `name_en` = 'Destroyer Battlegear (T5 Recolor)' WHERE `name` = 'Equipo de Batalla de Destructor (T5 Recolor)';
UPDATE `home_store_category` SET `name_en` = 'Siegebreaker Battlegear (T8 Recolor)' WHERE `name` = 'Equipo de Batalla de Rompedor del Asedio (T8 Recolor)';
UPDATE `home_store_category` SET `name_en` = 'Battlegear of Wrath (T2 Recolor)' WHERE `name` = 'Equipo de Batalla de la Cólera (T2 Recolor)';
UPDATE `home_store_category` SET `name_en` = 'Sunwell Lightbringer Battlegear' WHERE `name` = 'Equipo de Batalla del Iluminado de la Fuente del Sol';
UPDATE `home_store_category` SET `name_en` = 'Shields' WHERE `name` = 'Escudos';
UPDATE `home_store_category` SET `name_en` = 'Essences & Powders' WHERE `name` = 'Esencias y polvos';
UPDATE `home_store_category` SET `name_en` = 'Swords' WHERE `name` = 'Espadas';
UPDATE `home_store_category` SET `name_en` = 'Flasks' WHERE `name` = 'Frascos';
UPDATE `home_store_category` SET `name_en` = 'Furious Gladiator' WHERE `name` = 'Gladiador Furioso';
UPDATE `home_store_category` SET `name_en` = 'Furious Gladiator (Healer DPS)' WHERE `name` = 'Gladiador Furioso (DPS Sanador)';
UPDATE `home_store_category` SET `name_en` = 'Furious Gladiator (Caster DPS)' WHERE `name` = 'Gladiador Furioso (DPS caster)';
UPDATE `home_store_category` SET `name_en` = 'Furious Gladiator (Melee DPS)' WHERE `name` = 'Gladiador Furioso (DPS melee)';
UPDATE `home_store_category` SET `name_en` = 'Furious Gladiator (DPS)' WHERE `name` = 'Gladiador Furioso (DPS)';
UPDATE `home_store_category` SET `name_en` = 'Furious Gladiator (Healer)' WHERE `name` = 'Gladiador Furioso (Sanador)';
UPDATE `home_store_category` SET `name_en` = 'Relentless Gladiator' WHERE `name` = 'Gladiador Incansable';
UPDATE `home_store_category` SET `name_en` = 'Warrior' WHERE `name` = 'Guerrero';
UPDATE `home_store_category` SET `name_en` = 'Axes' WHERE `name` = 'Hachas';
UPDATE `home_store_category` SET `name_en` = 'Herbs' WHERE `name` = 'Hierbas';
UPDATE `home_store_category` SET `name_en` = 'Shoulders' WHERE `name` = 'Hombreras';
UPDATE `home_store_category` SET `name_en` = 'Leveling Items' WHERE `name` = 'Items de leveo';
UPDATE `home_store_category` SET `name_en` = 'Jewelry' WHERE `name` = 'Joyería';
UPDATE `home_store_category` SET `name_en` = 'The Ten Storms (T2 Recolor)' WHERE `name` = 'Las Diez Tormentas (T2 Recolor)';
UPDATE `home_store_category` SET `name_en` = 'Mage' WHERE `name` = 'Mago';
UPDATE `home_store_category` SET `name_en` = 'Pets' WHERE `name` = 'Mascotas';
UPDATE `home_store_category` SET `name_en` = 'Maces' WHERE `name` = 'Mazas';
UPDATE `home_store_category` SET `name_en` = 'Item Enhancements' WHERE `name` = 'Mejoras de objetos';
UPDATE `home_store_category` SET `name_en` = 'Ores' WHERE `name` = 'Minerales';
UPDATE `home_store_category` SET `name_en` = 'Miscellaneous' WHERE `name` = 'Miscelánea';
UPDATE `home_store_category` SET `name_en` = 'Repeatable Quests' WHERE `name` = 'Misiones repetibles';
UPDATE `home_store_category` SET `name_en` = 'Transmog Currency' WHERE `name` = 'Moneda de transfiguración';
UPDATE `home_store_category` SET `name_en` = 'Currencies' WHERE `name` = 'Monedas';
UPDATE `home_store_category` SET `name_en` = 'Ground Mounts' WHERE `name` = 'Monturas terrestres';
UPDATE `home_store_category` SET `name_en` = 'Flying Mounts' WHERE `name` = 'Monturas voladoras';
UPDATE `home_store_category` SET `name_en` = 'Item Level 200' WHERE `name` = 'Nivel de objeto 200';
UPDATE `home_store_category` SET `name_en` = 'Item Level 213' WHERE `name` = 'Nivel de objeto 213';
UPDATE `home_store_category` SET `name_en` = 'Item Level 219' WHERE `name` = 'Nivel de objeto 219';
UPDATE `home_store_category` SET `name_en` = 'Item Level 226' WHERE `name` = 'Nivel de objeto 226';
UPDATE `home_store_category` SET `name_en` = 'Item Level 232' WHERE `name` = 'Nivel de objeto 232';
UPDATE `home_store_category` SET `name_en` = 'Item Level 232 (Caster DPS)' WHERE `name` = 'Nivel de objeto 232 (DPS caster)';
UPDATE `home_store_category` SET `name_en` = 'Item Level 232 (Melee DPS)' WHERE `name` = 'Nivel de objeto 232 (DPS melee)';
UPDATE `home_store_category` SET `name_en` = 'Item Level 232 (DPS)' WHERE `name` = 'Nivel de objeto 232 (DPS)';
UPDATE `home_store_category` SET `name_en` = 'Item Level 232 (Healer)' WHERE `name` = 'Nivel de objeto 232 (Sanador)';
UPDATE `home_store_category` SET `name_en` = 'Item Level 232 (Tank)' WHERE `name` = 'Nivel de objeto 232 (Tanque)';
UPDATE `home_store_category` SET `name_en` = 'Item Level 239' WHERE `name` = 'Nivel de objeto 239';
UPDATE `home_store_category` SET `name_en` = 'Item Level 245' WHERE `name` = 'Nivel de objeto 245';
UPDATE `home_store_category` SET `name_en` = 'Item Level 245 (Caster DPS)' WHERE `name` = 'Nivel de objeto 245 (DPS caster)';
UPDATE `home_store_category` SET `name_en` = 'Item Level 245 (Melee DPS)' WHERE `name` = 'Nivel de objeto 245 (DPS melee)';
UPDATE `home_store_category` SET `name_en` = 'Item Level 245 (DPS)' WHERE `name` = 'Nivel de objeto 245 (DPS)';
UPDATE `home_store_category` SET `name_en` = 'Item Level 245 (Healer)' WHERE `name` = 'Nivel de objeto 245 (Sanador)';
UPDATE `home_store_category` SET `name_en` = 'Item Level 245 (Tank)' WHERE `name` = 'Nivel de objeto 245 (Tanque)';
UPDATE `home_store_category` SET `name_en` = 'Item Level < 200' WHERE `name` = 'Nivel de objeto < 200';
UPDATE `home_store_category` SET `name_en` = 'Trade Goods' WHERE `name` = 'Objetos comerciables';
UPDATE `home_store_category` SET `name_en` = 'Quest Items' WHERE `name` = 'Objetos de misión';
UPDATE `home_store_category` SET `name_en` = 'Paladin' WHERE `name` = 'Paladín';
UPDATE `home_store_category` SET `name_en` = 'Chests' WHERE `name` = 'Pecheras';
UPDATE `home_store_category` SET `name_en` = 'Scrolls' WHERE `name` = 'Pergaminos';
UPDATE `home_store_category` SET `name_en` = 'Hearthstone' WHERE `name` = 'Piedra hogar';
UPDATE `home_store_category` SET `name_en` = 'Exalted Plate' WHERE `name` = 'Placa Exaltada';
UPDATE `home_store_category` SET `name_en` = 'Potions' WHERE `name` = 'Pociones';
UPDATE `home_store_category` SET `name_en` = 'Rogue' WHERE `name` = 'Pícaro';
UPDATE `home_store_category` SET `name_en` = 'PvE Relic' WHERE `name` = 'Reliquia PvE';
UPDATE `home_store_category` SET `name_en` = 'PvP Relic' WHERE `name` = 'Reliquia PvP';
UPDATE `home_store_category` SET `name_en` = 'Priest' WHERE `name` = 'Sacerdote';
UPDATE `home_store_category` SET `name_en` = 'Arena Season 1 Set' WHERE `name` = 'Set de Temporada de Arenas 1';
UPDATE `home_store_category` SET `name_en` = 'Arena Season 2 Set' WHERE `name` = 'Set de Temporada de Arenas 2';
UPDATE `home_store_category` SET `name_en` = 'Arena Season 3 Set' WHERE `name` = 'Set de Temporada de Arenas 3';
UPDATE `home_store_category` SET `name_en` = 'Arena Season 4 Set' WHERE `name` = 'Set de Temporada de Arenas 4';
UPDATE `home_store_category` SET `name_en` = 'Arena Season 5 Set (Savage)' WHERE `name` = 'Set de Temporada de Arenas 5 (Indómito)';
UPDATE `home_store_category` SET `name_en` = 'Arena Season 5 Set (Deadly)' WHERE `name` = 'Set de Temporada de Arenas 5 (Mortal)';
UPDATE `home_store_category` SET `name_en` = 'Arena Season 5 Set (Hateful)' WHERE `name` = 'Set de Temporada de Arenas 5 (Odioso)';
UPDATE `home_store_category` SET `name_en` = 'Tier 1 Raid Set' WHERE `name` = 'Set de banda tier 1';
UPDATE `home_store_category` SET `name_en` = 'Tier 2 Raid Set' WHERE `name` = 'Set de banda tier 2';
UPDATE `home_store_category` SET `name_en` = 'Tier 3 Raid Set' WHERE `name` = 'Set de banda tier 3';
UPDATE `home_store_category` SET `name_en` = 'Tier 4 Raid Set' WHERE `name` = 'Set de banda tier 4';
UPDATE `home_store_category` SET `name_en` = 'Tier 5 Raid Set' WHERE `name` = 'Set de banda tier 5';
UPDATE `home_store_category` SET `name_en` = 'Tier 6 Raid Set' WHERE `name` = 'Set de banda tier 6';
UPDATE `home_store_category` SET `name_en` = 'Tier 7 Raid Set' WHERE `name` = 'Set de banda tier 7';
UPDATE `home_store_category` SET `name_en` = 'Tier 7.2 Raid Set' WHERE `name` = 'Set de banda tier 7.2';
UPDATE `home_store_category` SET `name_en` = 'Tier 8 Raid Set' WHERE `name` = 'Set de banda tier 8';
UPDATE `home_store_category` SET `name_en` = 'Tier 8.2 Raid Set' WHERE `name` = 'Set de banda tier 8.2';
UPDATE `home_store_category` SET `name_en` = 'Dungeon Set 1' WHERE `name` = 'Set de mazmorra 1';
UPDATE `home_store_category` SET `name_en` = 'Dungeon Set 2' WHERE `name` = 'Set de mazmorra 2';
UPDATE `home_store_category` SET `name_en` = 'Temple of Ahn''Qiraj Set' WHERE `name` = 'Set del Templo de Ahn''Qiraj';
UPDATE `home_store_category` SET `name_en` = 'Starter Set 1' WHERE `name` = 'Set inicial 1';
UPDATE `home_store_category` SET `name_en` = 'Starter Set 2' WHERE `name` = 'Set inicial 2';
UPDATE `home_store_category` SET `name_en` = 'PvE Armor Sets [Level 232 to 245]' WHERE `name` = 'Sets de armadura PvE [Nivel 232 a 245]';
UPDATE `home_store_category` SET `name_en` = 'PvP Armor Sets [Level 232]' WHERE `name` = 'Sets de armadura PvP [Nivel 232]';
UPDATE `home_store_category` SET `name_en` = 'PvE Transmog Sets [Up to level 226]' WHERE `name` = 'Sets de transfiguración PvE [Hasta nivel 226]';
UPDATE `home_store_category` SET `name_en` = 'PvP Transmog Sets [Up to level 213]' WHERE `name` = 'Sets de transfiguración PvP [Hasta nivel 213]';
UPDATE `home_store_category` SET `name_en` = 'Held In Off-hand' WHERE `name` = 'Sostener';
UPDATE `home_store_category` SET `name_en` = 'Tabards' WHERE `name` = 'Tabardos';
UPDATE `home_store_category` SET `name_en` = 'Cloth' WHERE `name` = 'Telas';
UPDATE `home_store_category` SET `name_en` = 'Arena Season 2' WHERE `name` = 'Temporada de Arenas 2';
UPDATE `home_store_category` SET `name_en` = 'Arena Season 3' WHERE `name` = 'Temporada de Arenas 3';
UPDATE `home_store_category` SET `name_en` = 'Arena Season 4' WHERE `name` = 'Temporada de Arenas 4';
UPDATE `home_store_category` SET `name_en` = 'Arena Season 5 (Savage)' WHERE `name` = 'Temporada de Arenas 5 (Indómito)';
UPDATE `home_store_category` SET `name_en` = 'Arena Season 5 (Deadly)' WHERE `name` = 'Temporada de Arenas 5 (Mortal)';
UPDATE `home_store_category` SET `name_en` = 'Tier 4 (PvE) | Tier 1 (PvP)' WHERE `name` = 'Tier 4 (PvE) | Tier 1 (PvP)';
UPDATE `home_store_category` SET `name_en` = 'Tier 5 (PvE) | Tier 2 (PvP)' WHERE `name` = 'Tier 5 (PvE) | Tier 2 (PvP)';
UPDATE `home_store_category` SET `name_en` = 'Tier 6 (PvE) | Tier 2 (PvP)' WHERE `name` = 'Tier 6 (PvE) | Tier 2 (PvP)';
UPDATE `home_store_category` SET `name_en` = 'Tier 7.1 (PvE)' WHERE `name` = 'Tier 7.1 (PvE)';
UPDATE `home_store_category` SET `name_en` = 'Tier 7.2 (PvE)' WHERE `name` = 'Tier 7.2 (PvE)';
UPDATE `home_store_category` SET `name_en` = 'Tier 8.1 (PvE)' WHERE `name` = 'Tier 8.1 (PvE)';
UPDATE `home_store_category` SET `name_en` = 'Tier 8.2 (PvE)' WHERE `name` = 'Tier 8.2 (PvE)';
UPDATE `home_store_category` SET `name_en` = 'Armor Tokens' WHERE `name` = 'Tokens de Armadura';
UPDATE `home_store_category` SET `name_en` = 'One-Hand' WHERE `name` = 'Una mano';
UPDATE `home_store_category` SET `name_en` = 'Miscellaneous' WHERE `name` = 'Varios';
UPDATE `home_store_category` SET `name_en` = 'Miscellaneous (Leather)' WHERE `name` = 'Varios (Cuero)';
UPDATE `home_store_category` SET `name_en` = 'Miscellaneous (Mail)' WHERE `name` = 'Varios (Malla)';
UPDATE `home_store_category` SET `name_en` = 'Miscellaneous (Plate)' WHERE `name` = 'Varios (Placas)';
UPDATE `home_store_category` SET `name_en` = 'Miscellaneous (Cloth)' WHERE `name` = 'Varios (Tela)';
UPDATE `home_store_category` SET `name_en` = 'Wands' WHERE `name` = 'Varitas';
UPDATE `home_store_category` SET `name_en` = 'Stormrage Raiment (T2 Recolor)' WHERE `name` = 'Vestiduras de Tempestira (T2 Recolor)';
UPDATE `home_store_category` SET `name_en` = 'Nemesis Raiment (T2 Recolor)' WHERE `name` = 'Vestiduras de la Némesis (T2 Recolor)';
UPDATE `home_store_category` SET `name_en` = 'Vestments of Transcendence (T2 Recolor)' WHERE `name` = 'Vestimentas de Trascendencia (T2 Recolor)';