claude-plugin-wordpress-manager 2.2.0 → 2.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/.claude-plugin/plugin.json +2 -2
  2. package/CHANGELOG.md +26 -0
  3. package/agents/wp-content-strategist.md +25 -0
  4. package/agents/wp-ecommerce-manager.md +23 -0
  5. package/agents/wp-site-manager.md +26 -0
  6. package/docs/GUIDE.md +116 -28
  7. package/package.json +8 -3
  8. package/skills/wordpress-router/references/decision-tree.md +8 -2
  9. package/skills/wp-content/SKILL.md +1 -0
  10. package/skills/wp-content-attribution/SKILL.md +97 -0
  11. package/skills/wp-content-attribution/references/attribution-models.md +189 -0
  12. package/skills/wp-content-attribution/references/conversion-funnels.md +137 -0
  13. package/skills/wp-content-attribution/references/reporting-dashboards.md +199 -0
  14. package/skills/wp-content-attribution/references/roi-calculation.md +202 -0
  15. package/skills/wp-content-attribution/references/utm-tracking-setup.md +161 -0
  16. package/skills/wp-content-attribution/scripts/attribution_inspect.mjs +277 -0
  17. package/skills/wp-headless/SKILL.md +1 -0
  18. package/skills/wp-i18n/SKILL.md +1 -0
  19. package/skills/wp-multilang-network/SKILL.md +107 -0
  20. package/skills/wp-multilang-network/references/content-sync.md +182 -0
  21. package/skills/wp-multilang-network/references/hreflang-config.md +198 -0
  22. package/skills/wp-multilang-network/references/language-routing.md +234 -0
  23. package/skills/wp-multilang-network/references/network-architecture.md +119 -0
  24. package/skills/wp-multilang-network/references/seo-international.md +213 -0
  25. package/skills/wp-multilang-network/scripts/multilang_inspect.mjs +308 -0
  26. package/skills/wp-multisite/SKILL.md +1 -0
  27. package/skills/wp-programmatic-seo/SKILL.md +97 -0
  28. package/skills/wp-programmatic-seo/references/data-sources.md +200 -0
  29. package/skills/wp-programmatic-seo/references/location-seo.md +134 -0
  30. package/skills/wp-programmatic-seo/references/product-seo.md +147 -0
  31. package/skills/wp-programmatic-seo/references/technical-seo.md +197 -0
  32. package/skills/wp-programmatic-seo/references/template-architecture.md +125 -0
  33. package/skills/wp-programmatic-seo/scripts/programmatic_seo_inspect.mjs +264 -0
  34. package/skills/wp-woocommerce/SKILL.md +1 -0
@@ -0,0 +1,234 @@
1
+ # Language Routing
2
+
3
+ Use this file when configuring how users are directed to the correct language version — browser language detection, geo-IP redirect, language switcher widgets, and cookie-based preferences.
4
+
5
+ ## Browser Language Detection
6
+
7
+ The `Accept-Language` HTTP header indicates the user's preferred languages:
8
+
9
+ ```
10
+ Accept-Language: it-IT,it;q=0.9,en-US;q=0.8,en;q=0.7
11
+ ```
12
+
13
+ ### PHP Detection (mu-plugin)
14
+
15
+ ```php
16
+ <?php
17
+ /**
18
+ * Language redirect based on Accept-Language header.
19
+ * Only redirects on first visit (no cookie set yet).
20
+ */
21
+ add_action('template_redirect', function () {
22
+ if (!is_multisite() || is_admin()) return;
23
+
24
+ // Skip if user has language preference cookie
25
+ if (isset($_COOKIE['wp_lang_pref'])) return;
26
+
27
+ // Skip if already on a language sub-site
28
+ $current_blog = get_current_blog_id();
29
+ if ($current_blog !== 1) return; // Only redirect from main site
30
+
31
+ $accepted = $_SERVER['HTTP_ACCEPT_LANGUAGE'] ?? '';
32
+ $preferred_lang = substr($accepted, 0, 2); // Get primary language code
33
+
34
+ $language_sites = [
35
+ 'it' => '/it/',
36
+ 'de' => '/de/',
37
+ 'fr' => '/fr/',
38
+ 'es' => '/es/',
39
+ ];
40
+
41
+ if (isset($language_sites[$preferred_lang])) {
42
+ $target = home_url($language_sites[$preferred_lang]);
43
+ // Set cookie so we don't redirect again
44
+ setcookie('wp_lang_pref', $preferred_lang, time() + (365 * DAY_IN_SECONDS), '/');
45
+ wp_redirect($target, 302); // 302 = temporary (important for SEO)
46
+ exit;
47
+ }
48
+ });
49
+ ```
50
+
51
+ **Important:** Always use 302 (temporary) redirect for language detection, never 301 (permanent). A 301 would cache the redirect for all users, regardless of their language.
52
+
53
+ ## Geo-IP Based Language Redirect
54
+
55
+ Use server-side geo-IP to redirect by country:
56
+
57
+ ### Nginx Configuration
58
+
59
+ ```nginx
60
+ # Requires ngx_http_geoip2_module
61
+ geoip2 /usr/share/GeoIP/GeoLite2-Country.mmdb {
62
+ $geoip2_country_code default=US country iso_code;
63
+ }
64
+
65
+ server {
66
+ # Redirect Italian visitors to /it/
67
+ if ($geoip2_country_code = "IT") {
68
+ set $lang_redirect "/it/";
69
+ }
70
+ if ($geoip2_country_code = "DE") {
71
+ set $lang_redirect "/de/";
72
+ }
73
+
74
+ # Only redirect if no cookie and on root
75
+ location = / {
76
+ if ($cookie_wp_lang_pref = "") {
77
+ return 302 $scheme://$host$lang_redirect;
78
+ }
79
+ }
80
+ }
81
+ ```
82
+
83
+ ### CloudFlare Workers
84
+
85
+ ```javascript
86
+ export default {
87
+ async fetch(request) {
88
+ const url = new URL(request.url);
89
+ const country = request.cf?.country || 'US';
90
+ const hasPref = request.headers.get('cookie')?.includes('wp_lang_pref');
91
+
92
+ if (url.pathname === '/' && !hasPref) {
93
+ const langMap = { IT: '/it/', DE: '/de/', FR: '/fr/', ES: '/es/' };
94
+ if (langMap[country]) {
95
+ return Response.redirect(url.origin + langMap[country], 302);
96
+ }
97
+ }
98
+
99
+ return fetch(request);
100
+ }
101
+ };
102
+ ```
103
+
104
+ ## Language Switcher Widget
105
+
106
+ ### HTML/CSS Language Switcher
107
+
108
+ ```html
109
+ <nav class="language-switcher" aria-label="Language selection">
110
+ <ul>
111
+ <li><a href="https://example.com/about/" hreflang="en" lang="en">English</a></li>
112
+ <li><a href="https://example.com/it/chi-siamo/" hreflang="it" lang="it">Italiano</a></li>
113
+ <li><a href="https://example.com/de/ueber-uns/" hreflang="de" lang="de">Deutsch</a></li>
114
+ <li><a href="https://example.com/fr/a-propos/" hreflang="fr" lang="fr">Français</a></li>
115
+ </ul>
116
+ </nav>
117
+ ```
118
+
119
+ ### WordPress Widget (mu-plugin)
120
+
121
+ ```php
122
+ add_action('widgets_init', function () {
123
+ register_widget('Multilang_Switcher_Widget');
124
+ });
125
+
126
+ class Multilang_Switcher_Widget extends WP_Widget {
127
+ public function __construct() {
128
+ parent::__construct('multilang_switcher', 'Language Switcher');
129
+ }
130
+
131
+ public function widget($args, $instance) {
132
+ $sites = get_sites(['number' => 20]);
133
+ $language_names = [
134
+ 1 => ['code' => 'en', 'name' => 'English'],
135
+ 2 => ['code' => 'it', 'name' => 'Italiano'],
136
+ 3 => ['code' => 'de', 'name' => 'Deutsch'],
137
+ // Add more...
138
+ ];
139
+
140
+ echo $args['before_widget'];
141
+ echo '<nav class="language-switcher" aria-label="Language"><ul>';
142
+ foreach ($sites as $site) {
143
+ $lang = $language_names[$site->blog_id] ?? null;
144
+ if (!$lang) continue;
145
+ $url = get_home_url($site->blog_id);
146
+ $active = ($site->blog_id === get_current_blog_id()) ? ' class="active"' : '';
147
+ printf('<li%s><a href="%s" hreflang="%s" lang="%s">%s</a></li>',
148
+ $active, esc_url($url), esc_attr($lang['code']),
149
+ esc_attr($lang['code']), esc_html($lang['name'])
150
+ );
151
+ }
152
+ echo '</ul></nav>';
153
+ echo $args['after_widget'];
154
+ }
155
+ }
156
+ ```
157
+
158
+ ## URL Structure Consistency
159
+
160
+ Ensure consistent URL patterns across all language sites:
161
+
162
+ | English (primary) | Italian | German | Rule |
163
+ |-------------------|---------|--------|------|
164
+ | `/about/` | `/it/chi-siamo/` | `/de/ueber-uns/` | Translated slugs (better UX) |
165
+ | `/about/` | `/it/about/` | `/de/about/` | Same slugs (easier hreflang matching) |
166
+ | `/products/shoes/` | `/it/prodotti/scarpe/` | `/de/produkte/schuhe/` | Full translation |
167
+
168
+ **Recommendation:** Use **same slugs** for easier hreflang matching via the mu-plugin. If translated slugs are preferred for UX, maintain a cross-reference table or use the multilingual plugin's content connections.
169
+
170
+ ## Cookie-Based Language Preference
171
+
172
+ Store user's explicit language choice to persist across sessions:
173
+
174
+ ```php
175
+ // When user clicks language switcher, set preference cookie
176
+ add_action('init', function () {
177
+ if (isset($_GET['set_lang'])) {
178
+ $lang = sanitize_text_field($_GET['set_lang']);
179
+ $allowed = ['en', 'it', 'de', 'fr', 'es'];
180
+ if (in_array($lang, $allowed, true)) {
181
+ setcookie('wp_lang_pref', $lang, time() + (365 * DAY_IN_SECONDS), '/');
182
+ }
183
+ }
184
+ });
185
+ ```
186
+
187
+ **Priority order for language selection:**
188
+ 1. Explicit choice (cookie from language switcher click) — highest
189
+ 2. URL path (user navigated to `/it/` directly)
190
+ 3. Browser `Accept-Language` header (auto-detect)
191
+ 4. Geo-IP (server-side guess)
192
+ 5. Default language (x-default / main site) — lowest
193
+
194
+ ## Headless Frontend Language Routing
195
+
196
+ ### Next.js i18n
197
+
198
+ ```javascript
199
+ // next.config.js
200
+ module.exports = {
201
+ i18n: {
202
+ locales: ['en', 'it', 'de', 'fr'],
203
+ defaultLocale: 'en',
204
+ localeDetection: true, // Auto-detect from Accept-Language
205
+ },
206
+ };
207
+ ```
208
+
209
+ ### Nuxt i18n Module
210
+
211
+ ```javascript
212
+ // nuxt.config.ts
213
+ export default defineNuxtConfig({
214
+ modules: ['@nuxtjs/i18n'],
215
+ i18n: {
216
+ locales: ['en', 'it', 'de', 'fr'],
217
+ defaultLocale: 'en',
218
+ strategy: 'prefix_except_default', // /it/about, /de/about, /about (en)
219
+ detectBrowserLanguage: {
220
+ useCookie: true,
221
+ cookieKey: 'i18n_redirected',
222
+ },
223
+ },
224
+ });
225
+ ```
226
+
227
+ ## Decision Checklist
228
+
229
+ 1. Should language be detected automatically or only via switcher? → Auto-detect on first visit, respect explicit choice after
230
+ 2. Is geo-IP available at the hosting/CDN level? → CloudFlare/nginx = yes; shared hosting = maybe not
231
+ 3. Are redirect rules using 302 (not 301)? → 301 caches per-user, breaks multi-language
232
+ 4. Is a language switcher present on all pages? → Add to header/footer template
233
+ 5. Is cookie-based preference persisted? → Set on switcher click, check before redirect
234
+ 6. For headless: is frontend i18n routing configured? → Next.js `i18n` or Nuxt i18n module
@@ -0,0 +1,119 @@
1
+ # Network Architecture
2
+
3
+ Use this file when designing the multi-language WordPress Multisite network — choosing between subdomains, subdirectories, or separate domains, selecting a multilingual plugin, and establishing site creation workflows.
4
+
5
+ ## Architecture Patterns
6
+
7
+ | Pattern | Example | Pros | Cons |
8
+ |---------|---------|------|------|
9
+ | **Subdirectory** | `example.com/it/`, `example.com/de/` | Shares domain authority, easy SSL, one hosting | Less geographic signal, shared server resources |
10
+ | **Subdomain** | `it.example.com`, `de.example.com` | Separate GSC properties, clear language signal | SSL per subdomain, DNS configuration needed |
11
+ | **Separate domains** | `example.it`, `example.de` | Strongest geo-targeting signal, full independence | Multiple hosting/SSL/DNS, no shared authority |
12
+
13
+ ### When to Choose Each
14
+
15
+ **Subdirectory (recommended for most):**
16
+ - Starting a multilingual site with shared content
17
+ - Budget-conscious (one hosting, one SSL)
18
+ - Want to inherit domain authority across languages
19
+ - 2–5 languages
20
+
21
+ **Subdomain:**
22
+ - Each language needs significant independence
23
+ - Different teams manage different languages
24
+ - Want separate Google Search Console properties
25
+ - 3–10 languages
26
+
27
+ **Separate domains:**
28
+ - Strong country-specific targeting needed (ccTLDs)
29
+ - Completely independent content per market
30
+ - Enterprise with per-country marketing teams
31
+ - Already owns country domains
32
+
33
+ ## Sub-Site Creation Workflow
34
+
35
+ Using multisite MCP tools to create language sub-sites:
36
+
37
+ ```bash
38
+ # 1. List existing sites
39
+ list_sites()
40
+
41
+ # 2. Create language sub-sites (one per language)
42
+ ms_create_site(domain="example.com", path="/it/", title="Example - Italiano")
43
+ ms_create_site(domain="example.com", path="/de/", title="Example - Deutsch")
44
+ ms_create_site(domain="example.com", path="/fr/", title="Example - Français")
45
+ ms_create_site(domain="example.com", path="/es/", title="Example - Español")
46
+
47
+ # 3. Verify all sites created
48
+ list_sites()
49
+ ```
50
+
51
+ ### Naming Conventions
52
+
53
+ | Element | Convention | Example |
54
+ |---------|-----------|---------|
55
+ | Site slug/path | ISO 639-1 language code | `/it/`, `/de/`, `/fr/` |
56
+ | Site title | `{Brand} - {Language Name}` | "DolceZero - Italiano" |
57
+ | Admin email | Shared network admin email | `admin@example.com` |
58
+ | Language constant | WordPress locale code | `it_IT`, `de_DE`, `fr_FR` |
59
+
60
+ **For regional variants:**
61
+ - Use ISO 639-1 + country: `pt-br` (Brazilian Portuguese), `zh-cn` (Simplified Chinese)
62
+ - Path: `/pt-br/`, `/zh-cn/`
63
+
64
+ ## Shared vs Independent Content
65
+
66
+ | Strategy | Description | Best For |
67
+ |----------|-------------|----------|
68
+ | **Shared + translated** | Same content structure, translated per language | Marketing sites, blogs |
69
+ | **Shared core + local** | Common pages translated, plus local-only content | E-commerce with local products |
70
+ | **Fully independent** | Each site has its own editorial calendar | News sites, regional publications |
71
+
72
+ ## Network Plugin Management
73
+
74
+ ### Network-Activated Plugins (shared across all sites)
75
+
76
+ Plugins that should be network-activated for consistency:
77
+ - Multilingual plugin (WPML, Polylang, MultilingualPress)
78
+ - SEO plugin (Yoast, Rank Math)
79
+ - Caching plugin (WP Super Cache, W3 Total Cache)
80
+ - Security plugin (Wordfence, Sucuri)
81
+
82
+ ```bash
83
+ # Network-activate a plugin (WP-CLI)
84
+ wp plugin activate yoast-seo --network
85
+
86
+ # Or via MCP tool
87
+ ms_network_activate_plugin(plugin="wordpress-seo/wp-seo.php")
88
+ ```
89
+
90
+ ### Per-Site Plugins
91
+
92
+ Plugins that may differ per language site:
93
+ - Payment gateways (region-specific)
94
+ - Legal compliance (GDPR, cookie consent varies by country)
95
+ - Local directory integrations
96
+
97
+ ## Multilingual Plugin Comparison
98
+
99
+ | Feature | WPML | Polylang | MultilingualPress |
100
+ |---------|------|----------|-------------------|
101
+ | **Architecture** | Single-site (can work with multisite) | Single-site (multisite add-on available) | Native multisite |
102
+ | **License** | Paid ($39–$159/yr) | Free core + paid add-ons | Paid (€199/yr) |
103
+ | **Translation management** | Built-in TM, XLIFF export | Manual or XLIFF | Content connections between sites |
104
+ | **String translation** | Yes (wpml-string-translation) | Polylang Pro | Via WordPress i18n |
105
+ | **WooCommerce support** | WPML WooCommerce Multilingual | WooCommerce Polylang | Native multisite WC |
106
+ | **Performance impact** | Medium (additional DB queries) | Low | Low (native multisite) |
107
+ | **Best for** | Single-site multilingual | Small sites, budget-friendly | Multisite-native workflows |
108
+
109
+ ### Recommendation
110
+
111
+ For a **Multisite multi-language network**, prefer **MultilingualPress** (native multisite design) or **WPML** (most feature-rich). Polylang is best for single-site setups.
112
+
113
+ ## Decision Checklist
114
+
115
+ 1. How many languages are needed now and in 2 years? → 2–5 = subdirectory; 5+ = subdomain
116
+ 2. Do language sites share content or are they independent? → Shared = translation plugin; Independent = just multisite
117
+ 3. Is the team centralized or per-country? → Centralized = shared admin; Per-country = per-site admins
118
+ 4. Is WooCommerce involved? → Yes = verify plugin WC compatibility
119
+ 5. What's the budget for multilingual plugin licensing? → Free = Polylang; Paid = WPML or MultilingualPress
@@ -0,0 +1,213 @@
1
+ # International SEO
2
+
3
+ Use this file when optimizing a multi-language WordPress Multisite network for international search — Google Search Console per language, language-specific sitemaps, schema localization, and performance per region.
4
+
5
+ ## International SEO Fundamentals
6
+
7
+ | Principle | Description |
8
+ |-----------|-------------|
9
+ | One page per language | Each language has its own URL (no dynamic translation) |
10
+ | Unique content | Translations should be human-quality, not raw machine output |
11
+ | Hreflang everywhere | Every page declares all its language alternates |
12
+ | Language-specific sitemaps | One XML sitemap per sub-site/language |
13
+ | GSC per property | Separate Search Console properties for monitoring |
14
+
15
+ ## Google Search Console Setup
16
+
17
+ ### Per-Language Properties
18
+
19
+ For a multisite with subdirectories:
20
+
21
+ ```
22
+ Property 1: https://example.com/ (English — main)
23
+ Property 2: https://example.com/it/ (Italian)
24
+ Property 3: https://example.com/de/ (German)
25
+ Property 4: https://example.com/fr/ (French)
26
+ ```
27
+
28
+ For subdomains:
29
+
30
+ ```
31
+ Property 1: https://example.com (English)
32
+ Property 2: https://it.example.com (Italian)
33
+ Property 3: https://de.example.com (German)
34
+ ```
35
+
36
+ ### International Targeting
37
+
38
+ 1. Open each language property in GSC
39
+ 2. Go to Legacy tools → International Targeting
40
+ 3. Set target country (optional — only if site targets a specific country, not just a language)
41
+ 4. Monitor hreflang errors in the same section
42
+
43
+ ### Per-Language Monitoring
44
+
45
+ For each language property, monitor:
46
+ - **Coverage:** Indexed pages count (should match published pages on that sub-site)
47
+ - **Performance:** Clicks, impressions, CTR, position — filter by country
48
+ - **Sitemaps:** Verify language-specific sitemap is submitted and processed
49
+
50
+ ## Language-Specific XML Sitemaps
51
+
52
+ Each WordPress sub-site in the multisite network generates its own sitemap:
53
+
54
+ ```
55
+ https://example.com/sitemap_index.xml (English)
56
+ https://example.com/it/sitemap_index.xml (Italian)
57
+ https://example.com/de/sitemap_index.xml (German)
58
+ ```
59
+
60
+ ### Sitemap Index at Network Level
61
+
62
+ Create a network-level sitemap index that references all sub-site sitemaps:
63
+
64
+ ```xml
65
+ <?xml version="1.0" encoding="UTF-8"?>
66
+ <sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
67
+ <sitemap>
68
+ <loc>https://example.com/sitemap_index.xml</loc>
69
+ <lastmod>2025-01-15</lastmod>
70
+ </sitemap>
71
+ <sitemap>
72
+ <loc>https://example.com/it/sitemap_index.xml</loc>
73
+ <lastmod>2025-01-14</lastmod>
74
+ </sitemap>
75
+ <sitemap>
76
+ <loc>https://example.com/de/sitemap_index.xml</loc>
77
+ <lastmod>2025-01-13</lastmod>
78
+ </sitemap>
79
+ </sitemapindex>
80
+ ```
81
+
82
+ ### Hreflang in Sitemaps
83
+
84
+ For maximum hreflang coverage, include language alternates in each sitemap:
85
+
86
+ ```xml
87
+ <url>
88
+ <loc>https://example.com/products/</loc>
89
+ <xhtml:link rel="alternate" hreflang="en" href="https://example.com/products/" />
90
+ <xhtml:link rel="alternate" hreflang="it" href="https://example.com/it/prodotti/" />
91
+ <xhtml:link rel="alternate" hreflang="de" href="https://example.com/de/produkte/" />
92
+ <xhtml:link rel="alternate" hreflang="x-default" href="https://example.com/products/" />
93
+ </url>
94
+ ```
95
+
96
+ **Note:** If using the hreflang mu-plugin in `<head>`, you can skip sitemap hreflang. Using both is not harmful but adds maintenance.
97
+
98
+ ## Structured Data Localization
99
+
100
+ ### Schema.org `inLanguage` Property
101
+
102
+ Add language to all structured data:
103
+
104
+ ```json
105
+ {
106
+ "@context": "https://schema.org",
107
+ "@type": "WebPage",
108
+ "name": "Benefici dell'Acqua di Cactus",
109
+ "inLanguage": "it",
110
+ "url": "https://example.com/it/benefici-acqua-cactus/",
111
+ "isPartOf": {
112
+ "@type": "WebSite",
113
+ "name": "DolceZero Italia",
114
+ "url": "https://example.com/it/",
115
+ "inLanguage": "it"
116
+ }
117
+ }
118
+ ```
119
+
120
+ ### Localized Organization Schema
121
+
122
+ ```json
123
+ {
124
+ "@context": "https://schema.org",
125
+ "@type": "Organization",
126
+ "name": "DolceZero",
127
+ "url": "https://example.com/",
128
+ "alternateName": ["DolceZero Italia", "DolceZero Deutschland"],
129
+ "contactPoint": [
130
+ {
131
+ "@type": "ContactPoint",
132
+ "telephone": "+39-xxx-xxx-xxxx",
133
+ "contactType": "customer service",
134
+ "areaServed": "IT",
135
+ "availableLanguage": "Italian"
136
+ },
137
+ {
138
+ "@type": "ContactPoint",
139
+ "telephone": "+49-xxx-xxx-xxxx",
140
+ "contactType": "customer service",
141
+ "areaServed": "DE",
142
+ "availableLanguage": "German"
143
+ }
144
+ ]
145
+ }
146
+ ```
147
+
148
+ ## Canonical URLs in Multi-Language Context
149
+
150
+ | Scenario | Canonical Rule |
151
+ |----------|---------------|
152
+ | English product page | Self-canonical: `https://example.com/products/cactus-water/` |
153
+ | Italian product page | Self-canonical: `https://example.com/it/prodotti/acqua-cactus/` |
154
+ | English = Italian (untranslated) | **Do NOT canonical Italian → English** — either translate or noindex |
155
+ | URL with `?lang=` parameter | Canonical → clean URL without parameter |
156
+
157
+ **Critical rule:** Never use canonical across languages. Each language URL is canonical for its language. Use hreflang to declare relationships.
158
+
159
+ ## Avoiding Duplicate Content
160
+
161
+ | Issue | Solution |
162
+ |-------|----------|
163
+ | Untranslated pages showing English on Italian site | Set as `noindex` until translated, or serve 404 |
164
+ | Machine-translated thin content | Block from indexing; translate properly or remove |
165
+ | Same content on main site and language site | Ensure distinct translations; use hreflang for signals |
166
+ | Language switcher creating `?lang=` URLs | Canonical to clean URL; use path-based routing |
167
+
168
+ ## Performance: CDN per Region
169
+
170
+ ### CloudFlare Configuration
171
+
172
+ ```
173
+ CDN cache by language:
174
+ - English: Edge servers worldwide (default)
175
+ - Italian: Prioritize EU-South edge locations
176
+ - German: Prioritize EU-Central edge locations
177
+ - Japanese: Prioritize Asia-Pacific edge locations
178
+ ```
179
+
180
+ ### Image Localization
181
+
182
+ - Product images: shared across languages (CDN serves nearest edge)
183
+ - Marketing banners: may differ per language (upload per sub-site)
184
+ - OG images: localized per language (localized text overlays)
185
+
186
+ ## Measuring International SEO
187
+
188
+ ### Metrics per Language
189
+
190
+ | Metric | Source | Frequency |
191
+ |--------|--------|-----------|
192
+ | Organic traffic by language | GA4 (filter by page path prefix) | Weekly |
193
+ | Rankings by country | GSC Performance → Country filter | Weekly |
194
+ | Indexed pages per language | GSC Coverage per property | Monthly |
195
+ | Hreflang errors | GSC International Targeting | Monthly |
196
+ | Conversion rate by language | WooCommerce + GA4 | Monthly |
197
+
198
+ ### Success Indicators
199
+
200
+ - Indexed pages per language ≈ published pages per sub-site
201
+ - Zero hreflang errors in GSC
202
+ - Organic traffic growing in target language markets
203
+ - Conversion rate per language within 20% of primary language
204
+
205
+ ## Decision Checklist
206
+
207
+ 1. Is GSC set up per language property? → Create and verify each
208
+ 2. Are language-specific sitemaps submitted? → One per sub-site
209
+ 3. Is `inLanguage` set in structured data? → Add to schema on each sub-site
210
+ 4. Are canonicals self-referencing per language (never cross-language)? → Audit sample pages
211
+ 5. Are untranslated pages noindexed? → Never show English content as Italian page
212
+ 6. Is CDN configured for international edge delivery? → Check CDN dashboard
213
+ 7. Are international SEO metrics tracked per language? → Set up monthly report