# XWUIGoogleTranslate

Real-time translation of marketplace ads, and any other user-written content, into the reader's language through the **Google Translate website widget** (the legacy `translate_a/element.js` gadget). No API key.

Content opts in with one attribute; the value is the ad's language when you know it, empty to let Google detect it:

```html
<h3 data-xwui-translate="ar">شقة للبيع في الرياض</h3>
<p data-xwui-translate>Satılık deniz manzaralı daire</p>
<span translate="no">AqarX</span>   <!-- never translated, even inside a marked element -->
```

Put the menu anywhere on the page:

```html
<xwui-google-translate languages='["en","ar","fr","tr","ru"]'></xwui-google-translate>
```

```ts
const menu = new XWUIGoogleTranslate(host, { languages: ['en', 'ar', 'fr'] });
menu.onChange((choice) => analytics.track('ad_translate', { choice }));
```

Like every XWUI component, the menu owns its host and replaces what is inside it: give it an element of its own, not the container the ads live in.

Ads translate into the **page locale** (`<html lang>`, which XWUILocale owns) the moment they render, and follow it when the site language changes. The reader can pick another language or **Show original**; the choice is remembered per browser. Components with their own switch, such as `XWUICardListing` with `autoTranslate: true`, translate even without the menu on the page.

| Config | Default | |
|---|---|---|
| `scope` | `"body"` | Region whose `[data-xwui-translate]` content the menu translates. `""` leaves it to opted-in components. |
| `languages` | curated list | Widget codes or locale ids (`he` → `iw`, `zh-Hant` → `zh-TW` are mapped). |
| `allowOriginal` | `true` | Offer **Show original**. |
| `showAttribution` | `true` | "Powered by Google Translate". Keep it on where Google's terms require attribution. |
| `compact` | `false` | Icon and menu only. |
| `label` | "Translate ads" | Visible label and the menu's accessible name. |

`data.language` applies a choice on mount: a code, `"auto"` or `"original"`.

## How it works

The widget never touches your page. It runs in a hidden same-origin iframe (the *lane*) that holds only the strings waiting for translation; the results come back as plain strings and are written into the ad's own text nodes. That matters because the widget, pointed at a live page, swaps text nodes for `<font>` wrappers: XWUI views would then re-render the original next to the translation. When a view re-renders an ad, the cached translation is put back before the frame paints, so there is no flash of the original, and an ad whose text changed is translated afresh.

Measured against the live widget with three cards in Arabic, Russian and Turkish: on a cold load (widget script included) the ads show in English after about 2.4 s; switching to French takes about 0.6 s, as does an ad set later with `setListing`. **Show original** and switching back to a language already seen are instant, from the cache.

- Marked elements get `lang` set to the target and `data-xwui-translated="<code>"` while they show a translation, and get their own `lang` back when they do not.
- `translate="no"`, `.notranslate`, `code`, `pre`, `textarea` and text without letters (prices, ids) stay as written.
- The `googtrans` cookie the widget writes is put back as it was, so the next page load is not translated wholesale.
- If the widget cannot load (blocked, offline, CSP), ads stay readable in their own language and the menu shows **Translation unavailable**; it retries after 30 s.

### Content-Security-Policy

The hosts the widget contacts, recorded against the live widget:

| Directive | Sources |
|---|---|
| `script-src` | `https://translate.google.com` `https://translate.googleapis.com` `https://translate-pa.googleapis.com` |
| `connect-src` | `https://translate-pa.googleapis.com` |
| `style-src` | `https://www.gstatic.com` |
| `img-src` | `https://translate.googleapis.com` `https://www.gstatic.com` `https://fonts.gstatic.com` `https://www.google.com` `https://translate.google.com` |

The widget sends a usage ping (`translate.google.com/gen204`) as an `http://` image; on an https page the browser upgrades it, and if it is blocked only the ping is lost, never a translation. The lane is an `about:blank` frame, so it inherits the page's policy; nothing needs `frame-src`.

## Headless

```ts
import { getAutoTranslator, observeAutoTranslate } from '@exonware/xwui/basic';

const release = observeAutoTranslate(document.querySelector('#results')!);
getAutoTranslator().setTarget('fr');          // or null to follow the page locale
getAutoTranslator().setOriginal(true);        // ads as written
getAutoTranslator().subscribe((s) => console.log(s.target, s.pending, s.error));
getAutoTranslator().setEngine(myEngine);      // your own XWUITranslateEngine instead of Google
```
