
Mengapa kandungan Contentful anda belum benar-benar berbilang bahasa
By Robert
Mengapa kandungan Contentful anda sebenarnya belum berbilang bahasa
Contentful mengendalikan setempat dengan baik. Anda boleh menentukan sebanyak mana yang anda perlukan, menetapkan rantai fallback, bertukar antara mereka dalam editor, dan membina frontend yang menyajikan bahasa yang betul bagi setiap laluan. Infrastruktur untuk kandungan berbilang bahasa memang baik.
Apa yang Contentful tidak lakukan ialah menterjemah kandungan anda.
Ini kedengaran jelas apabila dinyatakan secara langsung, tetapi ia mudah untuk mengelirukan "kami telah menyediakan medan setempat" dengan "kami berbilang bahasa." Kedua-duanya bukan perkara yang sama. Medan setempat adalah bekas. Berbilang bahasa bermakna bekas itu mempunyai kandungan di dalamnya.
Jika ruang Contentful anda mempunyai setempat Perancis dan Jerman yang dikonfigurasikan tetapi medan dalam setempat itu kosong, atau diisi dengan salinan Bahasa Inggeris sebagai tempat letak, atau diisi dengan draf kasar yang tidak pernah disemak sejak laman dilancarkan — anda tidak berbilang bahasa. Anda mempunyai rangka kerja untuk berbilang bahasa.
Pos ini adalah tentang menutup jurang itu.
Apa yang sebenarnya diberikan oleh Contentful
Sistem lokal Contentful direka dengan baik. Setiap entri kandungan boleh mempunyai nilai medan mengikut lokal. Anda menetapkan lokal lalai, mengkonfigurasi fallback, dan API penghantaran anda mengembalikan lokal yang betul apabila diminta. Pemodelan kandungan cukup fleksibel untuk mengendalikan keperluan pelbagai bahasa yang kompleks merentasi jenis kandungan yang berbeza.
Tetapi sistem ini sepenuhnya neutral tentang bagaimana kandungan terjemahan dimasukkan ke dalamnya. Contentful tidak tahu sama ada medan lokal Perancis anda mengandungi terjemahan profesional, salinan yang diterjemah oleh mesin, teks Inggeris yang tertampal secara tidak sengaja, atau langsung tiada apa-apa. Ia hanya menyimpan dan menghantar apa yang anda masukkan.
Masalah terjemahan sepenuhnya adalah tanggungjawab anda untuk diselesaikan.
Bagaimana pasukan biasanya mengendalikannya (dan di mana ia gagal)
Kebanyakan pasukan yang mengendalikan terjemahan Contentful jatuh ke dalam beberapa corak.
Corak eksport manual. Seorang pembangun mengeksport kandungan dari Contentful, menghantarnya ke agensi terjemahan atau freelancer, menunggu ia kembali, memformat semula, dan mengimportnya. Ini berfungsi untuk pelancaran sekali sahaja tetapi menjadi tidak praktikal apabila kandungan berubah. Setiap kemas kini kepada lokal sumber bermakna melalui keseluruhan kitaran sekali lagi. Dalam praktiknya, kandungan terjemahan dengan cepat ketinggalan di belakang sumber dan tiada siapa yang mempunyai masa untuk mengejarnya.
Corak terjemahan dalam editor. Seorang editor membuka setiap entri, beralih ke lokal sasaran, dan menterjemah medan demi medan sama ada secara manual atau dengan menampal kandungan ke dalam alat terjemahan. Ini tepat tetapi perlahan. Ia juga tidak boleh diskalakan — jika anda mempunyai ratusan entri merentasi selusin jenis kandungan, jumlah kerja manual adalah besar.
Corak "cukup baik". Kandungan terjemahan wujud tetapi belum disemak sejak ia pertama kali dihasilkan. Lokal sumber telah dikemas kini beberapa kali sejak itu. Versi Perancis halaman harga anda masih merujuk kepada pelan yang anda hentikan lapan bulan lalu. Halaman utama Jerman masih mempunyai slogan lama. Tiada siapa yang menandainya kerana tiada siapa yang memeriksa.
Ketiga-tiga corak berkongsi masalah asas yang sama: terjemahan dianggap sebagai tugas sekali sahaja dan bukan sebahagian berterusan dalam aliran kerja kandungan.
Apa yang sebenarnya diperlukan oleh pelbagai bahasa
Kandungan pelbagai bahasa yang tulen dalam Contentful memerlukan tiga perkara yang berfungsi bersama.
Terjemahan awal yang tepat. Setiap medan dalam setiap lokal sasaran memerlukan kandungan terjemahan yang tepat, dilokalkan dengan sewajarnya, dan benar-benar dibaca seolah-olah ia ditulis untuk pasaran tersebut dan bukannya hanya melalui alat terjemahan asas dan dibiarkan tanpa semakan.
Proses untuk memastikan terjemahan sentiasa terkini. Apabila kandungan sumber berubah, kandungan terjemahan juga perlu berubah. Ini adalah bahagian yang paling ramai pasukan tidak anggarkan. Pasukan kandungan yang menerbitkan beberapa kemas kini setiap minggu di seluruh ruang Contentful dengan empat lokal dan lima puluh jenis kandungan sedang menghadapi beban kerja terjemahan yang berterusan jika ia dikendalikan secara manual.
Cara untuk mengetahui bila terjemahan sudah lapuk. Sistem lokal Contentful tidak menunjukkan keusangan. Jika anda mengemas kini salinan Bahasa Inggeris sesuatu entri dan terlupa mengemas kini salinan Bahasa Perancis, versi Perancis akan terus menyajikan kandungan lama secara senyap. Anda memerlukan sama ada proses atau alat untuk mengesan ini.
Di Mana PolyLingo Sesuai
API PolyLingo menterjemah kandungan berstruktur sambil mengekalkan strukturnya. Untuk Contentful, format yang berkaitan ialah JSON — nilai medan entri Contentful, yang diekstrak dan dihantar ke API, kembali diterjemah dengan struktur yang sama utuh.
Aliran asas dalam 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')
// Dapatkan entri yang anda mahu terjemah
const entry = await environment.getEntry('your-entry-id')
// PolyLingo menggunakan kod BCP-47 tanpa wilayah (fr, de).
// Peta ini kepada ID lokal Contentful anda jika berbeza (contoh: fr-FR, de-DE).
const localeMap = { fr: 'fr', de: 'de' } // sesuaikan untuk padankan ID lokal ruang anda
// Ekstrak hanya medan rentetan Bahasa Inggeris — langkau rujukan, pautan, boolean, dan nombor
const sourceFields = Object.fromEntries(
Object.entries(entry.fields)
.filter(([, value]) => typeof value['en-US'] === 'string')
.map(([key, value]) => [key, value['en-US']])
)
// Terjemah ke Bahasa Perancis dan Jerman dalam satu permintaan
const result = await poly.translate({
content: JSON.stringify(sourceFields),
format: 'json',
targets: Object.keys(localeMap),
})
// Tulis nilai terjemah kembali ke entri menggunakan ID lokal Contentful
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('Entri diterjemah dan diterbitkan.')
Satu panggilan API mengembalikan kedua-dua Bahasa Perancis dan Jerman. Entri dikemas kini dan diterbitkan dalam skrip yang sama.
Nota mengenai ID lokal: PolyLingo menggunakan kod BCP-47 tanpa akhiran wilayah (fr, de). Ruang Contentful sering menggunakan ID yang disertakan wilayah seperti fr-FR atau de-DE. Objek localeMap di atas adalah tempat anda menyelaraskannya — kemas kini untuk padankan apa sahaja ID lokal yang digunakan ruang Contentful anda.
Corak yang sama berfungsi dalam 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')
# Ekstrak nilai medan Bahasa Inggeris
source_fields = {
key: value.get('en-US')
for key, value in entry.fields().items()
if value.get('en-US')
}
# Terjemah ke Bahasa Perancis dan Jerman
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']
# Tulis kembali ke entri
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('Entri diterjemah dan diterbitkan.')
Menterjemah pada skala besar
Contoh di atas mengendalikan satu entri sahaja. Untuk ruang Contentful dengan banyak entri merentasi pelbagai jenis kandungan, titik akhir batch mengendalikan sehingga 100 item setiap permintaan:
// Dapatkan pelbagai entri dan terjemahkan mereka dalam satu panggilan batch
const entries = await environment.getEntries({
content_type: 'blogPost',
limit: 50,
})
// Peta kod lokal PolyLingo kepada ID lokal Contentful
const localeMap = { fr: 'fr', de: 'de', es: 'es' } // sesuaikan untuk ruang anda
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()
}
Lima puluh entri, tiga bahasa, satu permintaan batch.
Menjaga terjemahan sentiasa terkini
Skrip di atas boleh dijalankan atas permintaan atau disambungkan ke aliran kerja penerbitan anda. Corak biasa adalah menjalankan terjemahan sebagai sebahagian daripada kerja CI yang dicetuskan apabila kandungan sumber berubah, atau mengikut jadual yang menangkap sebarang entri yang dikemas kini dalam 24 jam terakhir.
Contentful juga menyokong webhook — anda boleh mengkonfigurasi webhook yang diaktifkan apabila entri diterbitkan dalam lokal lalai, yang boleh mencetuskan kerja terjemahan secara automatik. Menyambungkan PolyLingo ke webhook itu bermakna setiap kali editor menerbitkan kandungan Bahasa Inggeris yang dikemas kini, lokal terjemahan dikemas kini tanpa sebarang langkah manual. Ini ada dalam peta jalan untuk integrasi PolyLingo khusus; sementara itu API memberi anda segala yang anda perlukan untuk membina aliran itu sendiri.
Nota mengenai jenis medan
Pendekatan di atas berfungsi dengan baik untuk teks pendek, teks panjang, dan teks kaya yang disimpan sebagai rentetan. Beberapa jenis medan Contentful memerlukan pengendalian secara berasingan:
Medan teks kaya yang disimpan dalam format dokumen Contentful (bukan rentetan biasa) perlu diserikan kepada teks biasa atau HTML sebelum dihantar ke PolyLingo, kemudian diserikan semula. Pakej @contentful/rich-text-html-renderer mengendalikan langkah penyerian, dan format: "html" di pihak PolyLingo mengekalkan penandaan dengan betul.
Medan rujukan (pautan ke entri atau aset lain) tidak harus diterjemah — ia adalah ID, bukan teks. Kecualikan mereka daripada medan yang anda hantar ke API.
Medan nombor, boolean, dan tarikh secara semula jadi tidak boleh diterjemah. Tapis ini sebelum membina objek sumber anda.
Memulakan
Tier percuma PolyLingo merangkumi 50,000 token setiap bulan. Untuk ruang Contentful dengan jumlah kandungan sederhana, itu merangkumi laluan terjemahan awal merentasi beberapa entri dalam pelbagai lokasi dengan ruang untuk kemas kini berterusan.
Dokumentasi API penuh terdapat di usepolylingo.com/docs. Pek SDK tersedia untuk Node.js, Python, Ruby, PHP, Java, dan Go.
npm install polylingo