appscript.dev
Automation Intermediate Sheets

Adapt marketing copy per region

Localise Northwind tone and references by market with AI — same message, regional flavour.

Published Jan 30, 2026

Northwind writes its campaign copy once, in British English, then ships the same words to every market. It reads fine in London and slightly off everywhere else — prices in the wrong currency, idioms that do not travel, examples nobody in the region recognises. Hand-localising each launch is the kind of job that gets skipped under deadline, so the copy goes out generic.

This script keeps one master copy and asks Claude to adapt it per region. It reads a sheet of regions, sends the master text through the model for each one, and writes the localised version back into the same row. The message stays identical; the idioms, currency, units and examples change to suit the market.

What you’ll need

  • A Google Sheet with one row per region. Column A holds the region name (Germany, United States, France), column B holds the master copy, and column C is where the adapted copy lands. Put headers in row 1.
  • An Anthropic API key saved as ANTHROPIC_API_KEY in Script Properties — see Store API keys and secrets securely.

The script

// The spreadsheet that holds the regions and master copy.
const COPY_SHEET_ID = '1abcCopySheetId';

// Column layout (1-based): region name, master copy, adapted output.
const REGION_COL = 1;
const MASTER_COL = 2;
const ADAPTED_COL = 3;

/**
 * Reads each region row, adapts the master copy for that market,
 * and writes the localised version back into column C.
 */
function adaptAllRegions() {
  const sheet = SpreadsheetApp.openById(COPY_SHEET_ID).getSheets()[0];

  // 1. Read every row below the header.
  const lastRow = sheet.getLastRow();
  if (lastRow < 2) {
    Logger.log('No regions to adapt — nothing to do.');
    return;
  }
  const rows = sheet.getRange(2, 1, lastRow - 1, 3).getValues();

  // 2. Adapt the copy for each region that has both a name and master text.
  rows.forEach((row, i) => {
    const region = row[REGION_COL - 1];
    const master = row[MASTER_COL - 1];
    if (!region || !master) return;

    const adapted = adaptForRegion(master, region);

    // 3. Write the result straight back into column C of this row.
    sheet.getRange(i + 2, ADAPTED_COL).setValue(adapted);
  });

  Logger.log('Adapted copy for ' + rows.length + ' regions.');
}

/**
 * Asks Claude to localise one piece of copy for one region. The prompt
 * pins the model to the message and lets it move only idioms, examples,
 * currency and units.
 */
function adaptForRegion(copy, region) {
  const prompt =
    'Adapt this Northwind marketing copy for ' + region + '. ' +
    'Keep the message; swap idioms, examples, currency, units. ' +
    'Return only the adapted copy.\n\n' + copy;
  return callClaude(prompt, 'claude-sonnet-4-6', 800);
}

/**
 * Minimal Anthropic API call. The key lives in Script Properties — it
 * is never pasted into the code.
 */
function callClaude(prompt, model, maxTokens) {
  const key = PropertiesService.getScriptProperties()
    .getProperty('ANTHROPIC_API_KEY');
  const res = UrlFetchApp.fetch('https://api.anthropic.com/v1/messages', {
    method: 'post',
    contentType: 'application/json',
    headers: { 'x-api-key': key, 'anthropic-version': '2023-06-01' },
    payload: JSON.stringify({
      model,
      max_tokens: maxTokens,
      messages: [{ role: 'user', content: prompt }],
    }),
    muteHttpExceptions: true,
  });
  return JSON.parse(res.getContentText()).content[0].text.trim();
}

How it works

  1. adaptAllRegions opens the copy spreadsheet and checks there is at least one data row below the header — if not, it logs a message and stops.
  2. It reads every region row in one range read, then loops through them.
  3. For each row it pulls the region name and master copy, skipping any row missing either value, so half-filled rows never trigger a wasted API call.
  4. adaptForRegion builds a prompt that tells Claude to keep the message intact and change only the things that should change per market — idioms, examples, currency and units — then asks for the adapted copy with no preamble.
  5. Claude Sonnet does the rewrite; localisation needs a feel for tone, so the stronger model is worth the extra cost here.
  6. The result is written straight back into column C of the same row, leaving the master copy in column B untouched.

Example run

With this in the sheet:

RegionMaster copy
Germany”Grab our autumn bundle for £49 — a no-brainer before the holidays.”
United States”Grab our autumn bundle for £49 — a no-brainer before the holidays.”

After a run, column C holds the localised versions:

RegionAdapted copy
Germany”Sichern Sie sich unser Herbstpaket für 55 € — die clevere Wahl vor den Feiertagen.”
United States”Grab our fall bundle for $59 — an easy yes before the holidays.”

Same offer, same intent — but the currency, season word and idiom now match the market.

Run it

This is an on-demand job, run when a campaign is ready to localise:

  1. In the Apps Script editor, select adaptAllRegions and click Run.
  2. Approve the authorisation prompt the first time.
  3. Open the sheet and read column C.

To let non-editors trigger it, add a custom menu:

function onOpen() {
  SpreadsheetApp.getUi()
    .createMenu('Copy tools')
    .addItem('Adapt copy for all regions', 'adaptAllRegions')
    .addToUi();
}

Watch out for

  • Claude can convert phrasing but not exchange rates. The currency figures it produces are plausible, not live — have a regional reviewer confirm any price before the copy ships.
  • The script overwrites column C on every run. If someone has hand-edited an adapted version, that edit is lost the next time the script runs over its row.
  • One API call per region. A sheet of 40 regions is 40 calls — fine for a campaign, but mind your Anthropic rate limits and budget on large lists.
  • Tone is subjective. The model gets the obvious swaps right; a human still needs to sign off that the result sounds like Northwind in that market.
  • Translation is not localisation. If a region needs a different language, say so explicitly in the region name or the prompt, or the model may only adapt idioms while leaving the copy in English.

Related