
Mengapa konten Contentful Anda belum benar-benar multibahasa
By Robert
Mengapa konten Contentful Anda sebenarnya belum multibahasa
Contentful menangani lokal dengan baik. Anda dapat mendefinisikan sebanyak yang Anda butuhkan, mengatur rantai fallback, beralih di antara mereka di editor, dan membangun frontend yang menyajikan bahasa yang tepat per rute. Infrastruktur untuk konten multibahasa benar-benar bagus.
Apa yang tidak dilakukan Contentful adalah menerjemahkan konten Anda.
Ini terdengar jelas ketika dikatakan langsung, tetapi sangat mudah untuk menyamakan "kami telah mengatur bidang lokal" dengan "kami multibahasa." Keduanya bukan hal yang sama. Bidang lokal adalah wadah. Multibahasa berarti wadah tersebut berisi konten.
Jika ruang Contentful Anda memiliki lokal Prancis dan Jerman yang dikonfigurasi tetapi bidang di lokal tersebut kosong, atau diisi dengan salinan bahasa Inggris sebagai placeholder, atau diisi dengan versi kasar pertama yang belum pernah ditinjau sejak situs diluncurkan — Anda tidak multibahasa. Anda hanya memiliki kerangka untuk multibahasa.
Postingan ini tentang menutup kesenjangan itu.
Apa yang sebenarnya diberikan Contentful
Sistem lokal Contentful dirancang dengan baik. Setiap entri konten dapat memiliki nilai bidang per lokal. Anda mengatur lokal default, mengonfigurasi fallback, dan API pengiriman Anda mengembalikan lokal yang tepat saat diminta. Pemodelan konten cukup fleksibel untuk menangani kebutuhan multibahasa yang kompleks di berbagai jenis konten.
Namun sistem ini sepenuhnya netral tentang bagaimana konten terjemahan masuk ke sana. Contentful tidak tahu apakah bidang lokal Prancis Anda berisi terjemahan profesional, salinan yang diterjemahkan mesin, teks bahasa Inggris yang ditempelkan secara tidak sengaja, atau tidak ada sama sekali. Ia hanya menyimpan dan mengirimkan apa yang Anda masukkan.
Masalah terjemahan sepenuhnya menjadi tanggung jawab Anda.
Bagaimana tim biasanya menanganinya (dan di mana terjadi kegagalan)
Sebagian besar tim yang menangani terjemahan Contentful jatuh ke dalam beberapa pola.
Pola ekspor manual. Seorang pengembang mengekspor konten dari Contentful, mengirimkannya ke agen terjemahan atau freelancer, menunggu hasilnya kembali, memformat ulang, dan mengimpornya. Ini bekerja untuk peluncuran satu kali tetapi menjadi tidak dapat diandalkan saat konten berubah. Setiap pembaruan pada lokal sumber berarti harus melalui siklus ini lagi. Dalam praktiknya, konten terjemahan dengan cepat tertinggal dari sumber dan tidak ada yang punya waktu untuk mengejarnya.
Pola terjemahan dalam editor. Seorang editor membuka setiap entri, beralih ke lokal target, dan menerjemahkan bidang demi bidang baik secara manual atau dengan menempelkan konten ke alat terjemahan. Ini akurat tetapi lambat. Ini juga tidak dapat diskalakan — jika Anda memiliki ratusan entri di berbagai jenis konten, volume pekerjaan manual sangat besar.
Pola "cukup lah". Konten terjemahan ada tetapi belum ditinjau sejak pertama kali dibuat. Lokal sumber telah diperbarui beberapa kali sejak saat itu. Versi Prancis dari halaman harga Anda masih merujuk pada paket yang Anda hentikan delapan bulan lalu. Beranda Jerman masih memiliki slogan lama. Tidak ada yang menandainya karena tidak ada yang memeriksa.
Ketiga pola ini memiliki masalah akar yang sama: terjemahan diperlakukan sebagai tugas satu kali daripada bagian berkelanjutan dari alur kerja konten.
Apa yang sebenarnya dibutuhkan oleh multibahasa
Konten multibahasa yang asli di Contentful membutuhkan tiga hal yang bekerja bersama.
Terjemahan awal yang akurat. Setiap bidang di setiap lokal target membutuhkan konten terjemahan yang akurat, dilokalkan dengan tepat, dan benar-benar terbaca seolah-olah ditulis untuk pasar tersebut, bukan hanya diterjemahkan dengan alat terjemahan dasar dan dibiarkan tanpa ditinjau.
Proses untuk menjaga terjemahan tetap mutakhir. Ketika konten sumber berubah, konten terjemahan juga perlu berubah. Ini adalah bagian yang paling sering diremehkan oleh banyak tim. Tim konten yang menerbitkan beberapa pembaruan setiap minggu di seluruh ruang Contentful dengan empat lokal dan lima puluh jenis konten akan menghadapi beban kerja terjemahan yang signifikan jika ditangani secara manual.
Cara untuk mengetahui kapan terjemahan sudah usang. Sistem lokal Contentful tidak menampilkan keusangan. Jika Anda memperbarui salinan bahasa Inggris dari sebuah entri dan lupa memperbarui salinan bahasa Prancis, versi bahasa Prancis akan terus menyajikan konten lama secara diam-diam. Anda memerlukan proses atau alat untuk menangkap hal ini.
Di mana PolyLingo cocok
API PolyLingo menerjemahkan konten terstruktur sambil mempertahankan strukturnya. Untuk Contentful, format yang relevan adalah JSON — nilai bidang entri Contentful yang diekstrak dan dikirim ke API akan kembali diterjemahkan dengan struktur yang sama tetap utuh.
Alur dasar di 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')
// Ambil entri yang ingin Anda terjemahkan
const entry = await environment.getEntry('your-entry-id')
// PolyLingo menggunakan kode BCP-47 tanpa wilayah (fr, de).
// Peta ini ke ID lokal Contentful Anda jika berbeda (misalnya fr-FR, de-DE).
const localeMap = { fr: 'fr', de: 'de' } // sesuaikan agar cocok dengan ID lokal ruang Anda
// Ekstrak hanya bidang string bahasa Inggris — lewati referensi, tautan, boolean, dan angka
const sourceFields = Object.fromEntries(
Object.entries(entry.fields)
.filter(([, value]) => typeof value['en-US'] === 'string')
.map(([key, value]) => [key, value['en-US']])
)
// Terjemahkan ke bahasa Prancis dan Jerman dalam satu permintaan
const result = await poly.translate({
content: JSON.stringify(sourceFields),
format: 'json',
targets: Object.keys(localeMap),
})
// Tulis nilai terjemahan 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 diterjemahkan dan dipublikasikan.')
Satu panggilan API mengembalikan terjemahan bahasa Prancis dan Jerman. Entri diperbarui dan dipublikasikan dalam skrip yang sama.
Catatan tentang ID lokal: PolyLingo menggunakan kode BCP-47 tanpa akhiran wilayah (fr, de). Ruang Contentful sering menggunakan ID yang disertai wilayah seperti fr-FR atau de-DE. Objek localeMap di atas adalah tempat Anda menyelaraskannya — perbarui agar sesuai dengan ID lokal apa pun yang digunakan ruang Contentful Anda.
Pola yang sama bekerja di 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 bidang bahasa Inggris
source_fields = {
key: value.get('en-US')
for key, value in entry.fields().items()
if value.get('en-US')
}
# Terjemahkan ke bahasa Prancis 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 diterjemahkan dan dipublikasikan.')
Translating at scale
The examples above handle a single entry. For a Contentful space with many entries across multiple content types, the batch endpoint handles up to 100 items per request:
// Fetch multiple entries and translate them in one batch call
const entries = await environment.getEntries({
content_type: 'blogPost',
limit: 50,
})
// Map PolyLingo locale codes to Contentful locale IDs
const localeMap = { fr: 'fr', de: 'de', es: 'es' } // adjust to match your space
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()
}
Fifty entries, three languages, one batch request.
Menjaga Terjemahan Tetap Terbaru
Script di atas dapat dijalankan sesuai permintaan atau dihubungkan ke alur kerja penerbitan Anda. Pola umum adalah menjalankan terjemahan sebagai bagian dari pekerjaan CI yang dipicu saat konten sumber berubah, atau pada jadwal yang menangkap entri yang diperbarui dalam 24 jam terakhir.
Contentful juga mendukung webhook — Anda dapat mengonfigurasi webhook yang aktif saat sebuah entri diterbitkan dalam lokal default, yang dapat memicu pekerjaan terjemahan secara otomatis. Menghubungkan PolyLingo ke webhook tersebut berarti setiap kali editor menerbitkan konten bahasa Inggris yang diperbarui, lokal terjemahan diperbarui tanpa langkah manual. Ini ada dalam roadmap untuk integrasi PolyLingo khusus; sementara itu API memberi Anda semua yang Anda butuhkan untuk membangun alur tersebut sendiri.
Catatan tentang tipe field
Pendekatan di atas bekerja dengan baik untuk teks pendek, teks panjang, dan teks kaya yang disimpan sebagai string. Beberapa tipe field Contentful perlu penanganan terpisah:
Field teks kaya yang disimpan dalam format dokumen Contentful (bukan string biasa) perlu diserialisasi menjadi teks biasa atau HTML sebelum dikirim ke PolyLingo, kemudian dideserialisasi kembali. Paket @contentful/rich-text-html-renderer menangani langkah serialisasi, dan format: "html" di sisi PolyLingo menjaga markup dengan benar.
Field referensi (tautan ke entri atau aset lain) tidak boleh diterjemahkan — mereka adalah ID, bukan teks. Kecualikan dari field yang Anda kirim ke API.
Field angka, boolean, dan tanggal secara alami tidak dapat diterjemahkan. Saring ini sebelum membangun objek sumber Anda.
Memulai
Tingkat gratis PolyLingo mencakup 50.000 token per bulan. Untuk ruang Contentful dengan jumlah konten sedang, itu mencakup satu kali terjemahan awal di beberapa entri dalam beberapa lokal dengan ruang tersisa untuk pembaruan berkelanjutan.
Dokumentasi API lengkap tersedia di usepolylingo.com/docs. Paket SDK tersedia untuk Node.js, Python, Ruby, PHP, Java, dan Go.
npm install polylingo