From 394c969b41b4b871620339966dca5e98db8aadea Mon Sep 17 00:00:00 2001 From: JOY <5027251+JOY@users.noreply.github.com> Date: Thu, 29 Oct 2026 08:19:24 +0700 Subject: [PATCH 1/2] =?UTF-8?q?fix(normalizer):=20"n=C4=83m"=20is=20a=20du?= =?UTF-8?q?ration=20only=20at=20the=20end;=20a=20bare=20size=20equal=20to?= =?UTF-8?q?=20the=20gift's=20keeps=20it?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 --- .../shopee/__tests__/open-core-intelligence.test.ts | 9 +++++++++ apps/importer/src/shopee/product-normalizer.ts | 12 +++++++++--- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/apps/importer/src/shopee/__tests__/open-core-intelligence.test.ts b/apps/importer/src/shopee/__tests__/open-core-intelligence.test.ts index 56ab53c14..4bef64bb6 100644 --- a/apps/importer/src/shopee/__tests__/open-core-intelligence.test.ts +++ b/apps/importer/src/shopee/__tests__/open-core-intelligence.test.ts @@ -486,6 +486,13 @@ async function runSuite() { ['iPad Air 11 năm 2024 M2 128GB', 'apple:ipad-air-m2-11:128gb'], ['iPad Pro 12.9 năm 2022 M2 128GB', 'apple:ipad-pro-m2-129:128gb'], ['MacBook Air M2 13 năm', 'apple:macbook-air-m2:unknown'], + // Any word or year form after "năm" keeps the size (DOS/Rate#1333). + ['iPad Pro 11 năm sx 2022', 'apple:ipad-pro-11:unknown'], + ['iPad Pro 12.9 năm sản xuất 2022', 'apple:ipad-pro-129:unknown'], + ['MacBook Air 13 năm ra mắt 2022', 'apple:macbook-air-13:unknown'], + ['iPad Pro 11 năm: 2022', 'apple:ipad-pro-11:unknown'], + ['iPad Pro 11 năm 22', 'apple:ipad-pro-11:unknown'], + ['iPad Pro M4 256GB 11 năm bảo hành tặng ốp 13 inch', 'apple:ipad-pro-m4-11:256gb'], // A date ("11/11"), RAM or SSD, a macOS name, a duration or an amount is no // size, so the gift's size still stands in. ['iPad Pro M4 Sale 11/11 tặng ốp 13 inch', 'apple:ipad-pro-m4-13:unknown'], @@ -764,6 +771,8 @@ async function runSuite() { ['Apple Watch Series 10 GPS 46 tặng ốp 42mm', 'apple:watch-series-10:64gb'], ['Apple Watch Series 10 GPS giảm 40% tặng ốp 46mm', 'apple:watch-series-10-46mm:64gb'], ['Apple Watch Series 10 giảm 44 nghìn tặng ốp 46mm', 'apple:watch-series-10-46mm:64gb'], + // A bare size equal to the gift's is the same size (DOS/Rate#1333). + ['Apple Watch Series 10 tặng ốp 42mm 42 GPS', 'apple:watch-series-10-42mm:64gb'], // A size listed right after the box's is a list, not the watch's own case. ['Apple Watch Series 10 kèm hộp 46mm | 42mm', 'apple:watch-series-10-46mm:64gb'], // "size 46" beats a box's size; a band's "size 42" is not the watch's. diff --git a/apps/importer/src/shopee/product-normalizer.ts b/apps/importer/src/shopee/product-normalizer.ts index 9980b6fe9..11d8bc873 100644 --- a/apps/importer/src/shopee/product-normalizer.ts +++ b/apps/importer/src/shopee/product-normalizer.ts @@ -1260,7 +1260,7 @@ function isProductCodeToken(token: string): boolean { // (DOS/Rate#1317). const UNIT_END = String.raw`(?![\p{L}\p{M}\d])`; const SIZE_NOT_PRECEDED_BY = String.raw`(? + [...ownPart.matchAll(new RegExp(bare.source, 'giu'))].some( + m => m[0].replace(',', '.') !== giftSize + ); const namesSize = family === 'watch' - ? WATCH_SIZE.test(withoutGift) || WATCH_BARE_SIZE.test(ownPart) - : APPLE_SIZE_PATTERNS[family]!.some(re => re.test(ownPart)) || SCREEN_BARE_SIZE.test(ownPart); + ? WATCH_SIZE.test(withoutGift) || namesOtherBare(WATCH_BARE_SIZE) + : APPLE_SIZE_PATTERNS[family]!.some(re => re.test(ownPart)) || + namesOtherBare(SCREEN_BARE_SIZE); return namesSize || withoutGift === productPart || giftSizes.size !== 1 ? '' : readFamilySize(family, productPart); From 25bbfe5e1d1dd0644d7e4057c5cb9bf8b09b4ccf Mon Sep 17 00:00:00 2001 From: JOY <5027251+JOY@users.noreply.github.com> Date: Fri, 30 Oct 2026 02:02:14 +0700 Subject: [PATCH 2/2] fix(normalizer): a shop's age in years is no size Co-Authored-By: Claude Opus 5.5 --- .../src/shopee/__tests__/open-core-intelligence.test.ts | 6 ++++++ apps/importer/src/shopee/product-normalizer.ts | 4 +++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/apps/importer/src/shopee/__tests__/open-core-intelligence.test.ts b/apps/importer/src/shopee/__tests__/open-core-intelligence.test.ts index 4bef64bb6..c43fa2002 100644 --- a/apps/importer/src/shopee/__tests__/open-core-intelligence.test.ts +++ b/apps/importer/src/shopee/__tests__/open-core-intelligence.test.ts @@ -492,6 +492,12 @@ async function runSuite() { ['MacBook Air 13 năm ra mắt 2022', 'apple:macbook-air-13:unknown'], ['iPad Pro 11 năm: 2022', 'apple:ipad-pro-11:unknown'], ['iPad Pro 11 năm 22', 'apple:ipad-pro-11:unknown'], + // A shop's age is no size: the gift's size stands in. + ['iPad Pro M4 256GB - Shop 13 năm kinh nghiệm - tặng ốp 11 inch', 'apple:ipad-pro-m4-11:256gb'], + [ + 'MacBook Air M2 256GB - Shop 14 năm kinh nghiệm - tặng túi 13 inch', + 'apple:macbook-air-m2-13:256gb', + ], ['iPad Pro M4 256GB 11 năm bảo hành tặng ốp 13 inch', 'apple:ipad-pro-m4-11:256gb'], // A date ("11/11"), RAM or SSD, a macOS name, a duration or an amount is no // size, so the gift's size still stands in. diff --git a/apps/importer/src/shopee/product-normalizer.ts b/apps/importer/src/shopee/product-normalizer.ts index 11d8bc873..0b38aa036 100644 --- a/apps/importer/src/shopee/product-normalizer.ts +++ b/apps/importer/src/shopee/product-normalizer.ts @@ -1256,11 +1256,13 @@ function isProductCodeToken(token: string): boolean { // js/polynomial-redos). A unit ends where a letter does, Vietnamese ones included: "13 // tặng" or "13 hãng" is a bare 13, not "13 t(b)" or "13 h"; "15 tháng" (warranty), "10%" // or "15 gói" is no size (DOS/Rate#1291). +// "năm" is a duration at the end, before a separator or a shop's age ("13 năm", "Shop +// 13 năm kinh nghiệm"); any other word or a year after it keeps the size (DOS/Rate#1333). // "16 RAM 512" is the RAM; "13 RAM 8GB" (a RAM size follows) is the screen's 13 // (DOS/Rate#1317). const UNIT_END = String.raw`(?![\p{L}\p{M}\d])`; const SIZE_NOT_PRECEDED_BY = String.raw`(?