Tilbake til bloggen
A Contentful-style content entry card showing a filled English locale tab and an empty French locale tab with a warning icon.

Hvorfor innholdet ditt i Contentful ennå ikke er faktisk flerspråklig

By Robert

Hvorfor innholdet ditt i Contentful egentlig ikke er flerspråklig ennå

Contentful håndterer lokaliteter godt. Du kan definere så mange du trenger, sette opp en fallback-kjede, bytte mellom dem i editoren, og bygge en frontend som serverer riktig språk per rute. Infrastrukturen for flerspråklig innhold er virkelig god.

Det Contentful ikke gjør, er å oversette innholdet ditt.

Dette høres åpenbart ut når det sies direkte, men det er overraskende lett å forveksle "vi har satt opp lokalitetsfelt" med "vi er flerspråklige." De to er ikke det samme. Lokalitetfelt er en beholder. Flerspråklig betyr at beholderen har innhold i seg.

Hvis Contentful-plassen din har franske og tyske lokaliteter konfigurert, men feltene i disse lokalitetene er tomme, eller fylt med engelsk tekst som en plassholder, eller fylt med et grovt førsteutkast som ingen har gjennomgått siden siden ble lansert — er du ikke flerspråklig. Du har stillaset for flerspråklighet.

Dette innlegget handler om å tette det gapet.


Hva Contentful egentlig gir deg

Contentfuls lokalsystem er godt designet. Hver innholdsoppføring kan ha feltverdier per lokalitet. Du setter en standard lokalitet, konfigurerer fallback, og leverings-API-en din returnerer riktig lokalitet når det etterspørres. Innholdsmodelleringen er fleksibel nok til å håndtere komplekse flerspråklige krav på tvers av forskjellige innholdstyper.

Men systemet er helt nøytralt på hvordan det oversatte innholdet kommer inn der. Contentful vet ikke om dine franske lokalitetsfelt inneholder profesjonelle oversettelser, maskinoversatt tekst, engelsk tekst limt inn ved en feil, eller ingenting i det hele tatt. Det lagrer og leverer bare det du legger inn.

Oversettelsesproblemet er helt ditt ansvar å løse.


Hvordan team vanligvis håndterer det (og hvor det bryter sammen)

De fleste team som håndterer Contentful-oversettelse faller inn i noen få mønstre.

Det manuelle eksportmønsteret. En utvikler eksporterer innhold fra Contentful, sender det til et oversettelsesbyrå eller frilanser, venter på at det skal komme tilbake, formaterer det på nytt og importerer det. Dette fungerer for en engangslansering, men blir uholdbart etter hvert som innholdet endres. Hver oppdatering til kildespråket betyr at man må gå gjennom hele syklusen på nytt. I praksis henger oversatt innhold raskt etter kilden, og ingen har tid til å ta det igjen.

Oversettelsesmønsteret i redigeringsverktøyet. En redaktør åpner hver oppføring, bytter til mål-lokalet, og oversetter felt for felt enten manuelt eller ved å lime inn innhold i et oversettelsesverktøy. Dette er nøyaktig, men tregt. Det skalerer heller ikke — hvis du har hundrevis av oppføringer fordelt på et dusin innholdstyper, er mengden manuelt arbeid betydelig.

"Det går bra"-mønsteret. Det oversatte innholdet eksisterer, men har ikke blitt gjennomgått siden det først ble produsert. Kildespråket har blitt oppdatert flere ganger siden da. Den franske versjonen av prissiden din refererer fortsatt til en plan du la ned for åtte måneder siden. Den tyske hjemmesiden har fortsatt den gamle slagordet. Ingen har påpekt det fordi ingen sjekker.

Alle tre mønstrene deler det samme grunnleggende problemet: oversettelse behandles som en engangsoppgave i stedet for en pågående del av innholdsarbeidsflyten.


Hva flerspråklighet faktisk krever

Ekte flerspråklig innhold i Contentful krever at tre ting fungerer sammen.

Nøyaktig første oversettelse. Hvert felt i hvert målområde trenger oversatt innhold som er nøyaktig, hensiktsmessig lokalisert, og faktisk leses som om det var skrevet for det markedet, i stedet for å ha blitt kjørt gjennom et enkelt oversettelsesverktøy og latt være uanmeldt.

En prosess for å holde oversettelser oppdaterte. Når kildeinnholdet endres, må også det oversatte innholdet endres. Dette er delen de fleste team undervurderer. Et innholdsteam som publiserer flere oppdateringer i uken på tvers av et Contentful-område med fire lokaliteter og femti innholdstyper, står overfor en betydelig pågående oversettelsesarbeidsmengde hvis det håndteres manuelt.

En måte å vite når oversettelser er utdaterte. Contentfuls lokaliseringssystem viser ikke foreldelse. Hvis du oppdaterer den engelske teksten i en oppføring og glemmer å oppdatere den franske teksten, vil den franske versjonen stille fortsette å vise det gamle innholdet. Du trenger enten en prosess eller verktøy for å fange opp dette.


Hvor PolyLingo passer inn

PolyLingos API oversetter strukturert innhold samtidig som strukturen bevares. For Contentful er det relevante formatet JSON — en Contentful-oppførings feltverdier, hentet ut og sendt til API-en, kommer tilbake oversatt med samme struktur intakt.

Den grunnleggende flyten i Node.js:

import PolyLingo from 'polylingo'
import { createClient } from 'contentful-management'

const poly = new PolyLingo({ apiKey: process.env.POLYLINGO_API_KEY })
const contentful = createClient({
  accessToken: process.env.CONTENTFUL_MANAGEMENT_TOKEN,
})

const space = await contentful.getSpace(process.env.CONTENTFUL_SPACE_ID)
const environment = await space.getEnvironment('master')

// Hent oppføringen du vil oversette
const entry = await environment.getEntry('your-entry-id')

// PolyLingo bruker BCP-47-koder uten region (fr, de).
// Kartlegg disse til dine Contentful locale-IDer hvis de er forskjellige (f.eks. fr-FR, de-DE).
const localeMap = { fr: 'fr', de: 'de' } // juster for å matche locale-IDene i ditt space

// Ekstraher kun engelske strengfelt — hopp over referanser, lenker, boolske verdier og tall
const sourceFields = Object.fromEntries(
  Object.entries(entry.fields)
    .filter(([, value]) => typeof value['en-US'] === 'string')
    .map(([key, value]) => [key, value['en-US']])
)

// Oversett til fransk og tysk i én forespørsel
const result = await poly.translate({
  content: JSON.stringify(sourceFields),
  format: 'json',
  targets: Object.keys(localeMap),
})

// Skriv oversatte verdier tilbake til oppføringen ved bruk av Contentful locale-IDer
for (const [polyLocale, translated] of Object.entries(result.translations)) {
  const contentfulLocale = localeMap[polyLocale]
  const parsed = JSON.parse(translated)
  for (const [field, value] of Object.entries(parsed)) {
    if (!entry.fields[field]) entry.fields[field] = {}
    entry.fields[field][contentfulLocale] = value
  }
}

await entry.update()
await entry.publish()

console.log('Oppføring oversatt og publisert.')

Én API-kall returnerer både fransk og tysk. Oppføringen oppdateres og publiseres i samme skript.

En merknad om locale-IDer: PolyLingo bruker BCP-47-koder uten regionsuffiks (fr, de). Contentful-spaces bruker ofte regionkvalifiserte IDer som fr-FR eller de-DE. localeMap-objektet ovenfor er der du justerer dem — oppdater det for å matche hvilke locale-IDer ditt Contentful-space bruker.

Samme mønster fungerer i Python:

import os, json, requests
from contentful_management import Client

poly_key = os.environ['POLYLINGO_API_KEY']
contentful = Client(os.environ['CONTENTFUL_MANAGEMENT_TOKEN'])

space = contentful.spaces().find(os.environ['CONTENTFUL_SPACE_ID'])
environment = space.environments().find('master')
entry = environment.entries().find('your-entry-id')

# Ekstraher engelske feltverdier
source_fields = {
    key: value.get('en-US')
    for key, value in entry.fields().items()
    if value.get('en-US')
}

# Oversett til fransk og tysk
r = requests.post(
    'https://api.usepolylingo.com/v1/translate',
    headers={'Authorization': f"Bearer {poly_key}"},
    json={
        'content': json.dumps(source_fields),
        'format': 'json',
        'targets': ['fr', 'de'],
    },
    timeout=120,
)
r.raise_for_status()
translations = r.json()['translations']

# Skriv tilbake til oppføringen
for locale, translated in translations.items():
    parsed = json.loads(translated)
    for field, value in parsed.items():
        if field in entry.fields():
            entry.fields()[field][locale] = value

entry.save()
entry.publish()
print('Oppføring oversatt og publisert.')

Oversettelse i stor skala

Eksemplene ovenfor håndterer én enkelt oppføring. For et Contentful-område med mange oppføringer på tvers av flere innholdstyper, håndterer batch-endepunktet opptil 100 elementer per forespørsel:

// Hent flere oppføringer og oversett dem i én batch-kall
const entries = await environment.getEntries({
  content_type: 'blogPost',
  limit: 50,
})
 
// Kartlegg PolyLingo lokalekoder til Contentful lokal-IDer
const localeMap = { fr: 'fr', de: 'de', es: 'es' } // juster for å matche ditt område
 
const items = entries.items.map(entry => ({
  id: entry.sys.id,
  content: JSON.stringify(
    Object.fromEntries(
      Object.entries(entry.fields)
        .filter(([, value]) => typeof value['en-US'] === 'string')
        .map(([k, v]) => [k, v['en-US']])
    )
  ),
  format: 'json',
}))
 
const batch = await poly.batch({
  items,
  targets: Object.keys(localeMap),
})
 
for (const result of batch.results) {
  const entry = entries.items.find(e => e.sys.id === result.id)
  for (const [polyLocale, translated] of Object.entries(result.translations)) {
    const contentfulLocale = localeMap[polyLocale]
    const parsed = JSON.parse(translated)
    for (const [field, value] of Object.entries(parsed)) {
      if (!entry.fields[field]) entry.fields[field] = {}
      entry.fields[field][contentfulLocale] = value
    }
  }
  await entry.update()
  await entry.publish()
}

Femtifire oppføringer, tre språk, én batch-forespørsel.


Holde oversettelser oppdaterte

Skriptene ovenfor kan kjøres på forespørsel eller kobles inn i publiseringsarbeidsflyten din. Et vanlig mønster er å kjøre oversettelse som en del av en CI-jobb som utløses når kildeinnhold endres, eller på en tidsplan som fanger opp alle oppføringer oppdatert de siste 24 timene.

Contentful støtter også webhooks — du kan konfigurere en webhook som utløses når en oppføring publiseres i standardlokal, noe som kan utløse en oversettelsesjobb automatisk. Å koble PolyLingo til den webhooken betyr at hver gang en redaktør publiserer oppdatert engelsk innhold, oppdateres de oversatte lokalene uten noen manuell handling. Dette er på veikartet for en dedikert PolyLingo-integrasjon; i mellomtiden gir API-en deg alt du trenger for å bygge den flyten selv.


En merknad om felttyper

Tilnærmingen ovenfor fungerer godt for kort tekst, lang tekst og rik tekst lagret som strenger. Noen Contentful felttyper må håndteres separat:

Rik tekst-felt lagret i Contentfuls dokumentformat (ikke vanlige strenger) må serialiseres til ren tekst eller HTML før sending til PolyLingo, og deretter deserialiseres tilbake. @contentful/rich-text-html-renderer-pakken håndterer serialiseringssteget, og format: "html" på PolyLingo-siden bevarer markeringen korrekt.

Referansefelt (lenker til andre oppføringer eller ressurser) skal ikke oversettes — de er ID-er, ikke tekst. Ekskluder dem fra feltene du sender til API-et.

Nummer-, boolsk- og datofelt er ikke oversettbare av natur. Filtrer disse ut før du bygger kildeobjektet ditt.


Komme i gang

PolyLingos gratisnivå inkluderer 50 000 tokens per måned. For et Contentful-område med en moderat mengde innhold, dekker det en første oversettelsesrunde på tvers av flere oppføringer i flere lokaliteter med rom til overs for løpende oppdateringer.

Full API-dokumentasjon finnes på usepolylingo.com/docs. SDK-pakker er tilgjengelige for Node.js, Python, Ruby, PHP, Java og Go.

npm install polylingo

Få API-nøkkelen din