Zpět na blog
A Contentful-style content entry card showing a filled English locale tab and an empty French locale tab with a warning icon.

Proč váš obsah v Contentful ještě není skutečně vícejazyčný

By Robert

Proč váš obsah v Contentful ještě není skutečně vícejazyčný

Contentful dobře zvládá lokality. Můžete definovat tolik, kolik potřebujete, nastavit řetězec záložních možností, přepínat mezi nimi v editoru a vytvořit frontend, který podává správný jazyk podle cesty. Infrastruktura pro vícejazyčný obsah je opravdu dobrá.

Co Contentful nedělá, je překládat váš obsah.

Zní to zřejmě, když se to řekne přímo, ale je překvapivě snadné zaměnit „máme nastavená pole pro lokalitu“ s „jsme vícejazyční“. Obě věci nejsou totéž. Pole pro lokalitu jsou kontejner. Vícejazyčnost znamená, že kontejner obsahuje obsah.

Pokud má váš Contentful prostor nakonfigurované francouzské a německé lokality, ale pole v těchto lokalitách jsou prázdná, nebo vyplněná anglickou kopií jako zástupcem, nebo naplněná hrubým prvním návrhem, který od spuštění webu nikdo nezkontroloval — nejste vícejazyční. Máte kostru pro vícejazyčnost.

Tento příspěvek je o uzavření této mezery.


Co vám Contentful vlastně dává

Systém lokalit v Contentful je dobře navržený. Každý obsahový záznam může mít hodnoty polí pro jednotlivé lokality. Nastavíte výchozí lokalitu, nakonfigurujete záložní možnosti a vaše doručovací API vrátí správnou lokalitu na požádání. Modelování obsahu je dostatečně flexibilní, aby zvládlo složité vícejazyčné požadavky napříč různými typy obsahu.

Ale systém je zcela neutrální ohledně toho, jak se do něj dostane přeložený obsah. Contentful neví, zda vaše francouzská pole lokalit obsahují profesionální překlady, strojově přeložený text, anglický text vložený omylem nebo vůbec nic. Jen ukládá a doručuje to, co do něj vložíte.

Problém s překladem je zcela na vás, abyste ho vyřešili.


Jak týmy obvykle řeší tento problém (a kde dochází k selhání)

Většina týmů, které se zabývají překladem v Contentful, spadá do jednoho z několika vzorců.

Vzorec manuálního exportu. Vývojář exportuje obsah z Contentful, posílá ho překladatelské agentuře nebo freelancerovi, čeká na návrat, přeformátuje ho a importuje zpět. Toto funguje pro jednorázové spuštění, ale stává se neudržitelným, jakmile se obsah mění. Každá aktualizace zdrojové lokality znamená projít celým cyklem znovu. V praxi se přeložený obsah rychle zaostává za zdrojem a nikdo nemá čas ho dohnat.

Vzorec překladu v editoru. Editor otevře každý záznam, přepne na cílovou lokalitu a překládá pole po poli buď ručně, nebo vložením obsahu do překladatelského nástroje. Je to přesné, ale pomalé. Také to není škálovatelné — pokud máte stovky záznamů napříč desítkou typů obsahu, objem manuální práce je značný.

Vzorec „bude to stačit“. Přeložený obsah existuje, ale nebyl zkontrolován od doby, kdy byl poprvé vytvořen. Zdrojová lokalita byla od té doby několikrát aktualizována. Francouzská verze vaší stránky s cenami stále odkazuje na plán, který jste před osmi měsíci zrušili. Německá domovská stránka stále obsahuje staré motto. Nikdo to neoznačil, protože to nikdo nekontroluje.

Všechny tři vzorce sdílejí stejný základní problém: překlad je považován za jednorázový úkol, nikoli za průběžnou součást pracovního postupu s obsahem.


Co skutečně vyžaduje vícejazyčnost

Skutečný vícejazyčný obsah v Contentful vyžaduje, aby tři věci fungovaly společně.

Přesný počáteční překlad. Každé pole v každé cílové lokalitě potřebuje překlad, který je přesný, vhodně lokalizovaný a skutečně čitelný, jako by byl napsán pro daný trh, nikoli jen přeložen základním překladatelským nástrojem a ponechán bez kontroly.

Proces pro udržování překladů aktuálních. Když se změní zdrojový obsah, musí se změnit i překlady. To je část, kterou většina týmů podceňuje. Tým, který publikuje několik aktualizací týdně v Contentful prostoru se čtyřmi lokalitami a padesáti typy obsahu, čelí značné průběžné pracovní zátěži s překlady, pokud je řeší ručně.

Způsob, jak zjistit, kdy jsou překlady zastaralé. Systém lokalit v Contentful nezobrazuje zastaralost. Pokud aktualizujete anglickou verzi záznamu a zapomenete aktualizovat francouzskou verzi, francouzská verze tiše nadále poskytuje starý obsah. Potřebujete buď proces, nebo nástroje, které to zachytí.


Kde PolyLingo zapadá

API PolyLingo překládá strukturovaný obsah a přitom zachovává jeho strukturu. Pro Contentful je relevantní formát JSON — hodnoty polí záznamu Contentful, které jsou extrahovány a odeslány do API, se vrací přeložené se zachováním stejné struktury.

Základní tok v 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')

// Získejte záznam, který chcete přeložit
const entry = await environment.getEntry('your-entry-id')

// PolyLingo používá kódy BCP-47 bez regionu (fr, de).
// Namapujte je na vaše Contentful locale ID, pokud se liší (např. fr-FR, de-DE).
const localeMap = { fr: 'fr', de: 'de' } // upravte tak, aby odpovídalo locale ID ve vašem prostoru

// Extrahujte pouze anglická textová pole — přeskočte reference, odkazy, booleany a čísla
const sourceFields = Object.fromEntries(
  Object.entries(entry.fields)
    .filter(([, value]) => typeof value['en-US'] === 'string')
    .map(([key, value]) => [key, value['en-US']])
)

// Přeložte do francouzštiny a němčiny v jednom požadavku
const result = await poly.translate({
  content: JSON.stringify(sourceFields),
  format: 'json',
  targets: Object.keys(localeMap),
})

// Zapište přeložené hodnoty zpět do záznamu pomocí Contentful locale 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()

console.log('Záznam přeložen a publikován.')

Jeden API volání vrací jak francouzštinu, tak němčinu. Záznam je aktualizován a publikován ve stejném skriptu.

Poznámka k locale ID: PolyLingo používá kódy BCP-47 bez regionální přípony (fr, de). Prostory Contentful často používají regionálně kvalifikovaná ID jako fr-FR nebo de-DE. Objekt localeMap výše je místo, kde je sladíte — aktualizujte jej tak, aby odpovídal locale ID používaným ve vašem Contentful prostoru.

Stejný vzor funguje v Pythonu:

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')

# Extrahujte anglické hodnoty polí
source_fields = {
    key: value.get('en-US')
    for key, value in entry.fields().items()
    if value.get('en-US')
}

# Přeložte do francouzštiny a němčiny
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']

# Zapište zpět do záznamu
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('Záznam přeložen a publikován.')

Překlad ve velkém měřítku

Výše uvedené příklady zpracovávají jeden záznam. Pro Contentful prostor s mnoha záznamy napříč více typy obsahu zpracovává dávkové rozhraní až 100 položek na požadavek:

// Načíst více záznamů a přeložit je v jednom dávkovém volání
const entries = await environment.getEntries({
  content_type: 'blogPost',
  limit: 50,
})
 
// Namapovat kódy lokalit PolyLingo na ID lokalit Contentful
const localeMap = { fr: 'fr', de: 'de', es: 'es' } // upravte podle vaší oblasti
 
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()
}

Padesát záznamů, tři jazyky, jeden dávkový požadavek.


Udržování překladů aktuálních

Výše uvedené skripty lze spustit na vyžádání nebo je zapojit do vašeho publikačního workflow. Běžným vzorem je spouštět překlad jako součást CI úlohy, která se spustí při změně zdrojového obsahu, nebo podle plánu, který zachytí všechny položky aktualizované za posledních 24 hodin.

Contentful také podporuje webhooks — můžete nakonfigurovat webhook, který se spustí, když je položka publikována v výchozím jazyce, což může automaticky spustit překladovou úlohu. Propojení PolyLingo s tímto webhookem znamená, že pokaždé, když editor publikuje aktualizovaný anglický obsah, jsou přeložené lokality aktualizovány bez jakéhokoli manuálního kroku. Toto je na plánu pro dedikovanou integraci PolyLingo; mezitím vám API poskytuje vše, co potřebujete k vytvoření tohoto procesu sami.


Poznámka k typům polí

Výše uvedený přístup dobře funguje pro krátký text, dlouhý text a bohatý text uložený jako řetězce. Některé typy polí v Contentful je třeba zpracovat zvlášť:

Pole bohatého textu uložená ve formátu dokumentu Contentful (nikoli jako obyčejné řetězce) je třeba před odesláním do PolyLingo serializovat do prostého textu nebo HTML a poté znovu deserializovat. Balíček @contentful/rich-text-html-renderer zajišťuje krok serializace a format: "html" na straně PolyLingo správně zachovává značkování.

Referenční pole (odkazy na jiné záznamy nebo zdroje) by neměla být překládána — jsou to ID, nikoli text. Vynechejte je z polí, která odesíláte do API.

Číselná, logická a datová pole nejsou podle své povahy přeložitelná. Před vytvořením zdrojového objektu je odfiltrujte.


Začínáme

Bezplatný tarif PolyLingo zahrnuje 50 000 tokenů měsíčně. Pro Contentful prostor s mírným množstvím obsahu to pokrývá počáteční překlad několika položek v několika lokalitách s rezervou pro průběžné aktualizace.

Kompletní dokumentace API je na usepolylingo.com/docs. SDK balíčky jsou k dispozici pro Node.js, Python, Ruby, PHP, Java a Go.

npm install polylingo

Získejte svůj API klíč