Translated slugs

Hello, hope you are good.

Example: Gli errori che i turisti fanno durante il loro primo viaggio all'estero

It came to my attention that the demo projects do not use a fully translated slug
I do believe this is key for good SEO to have the slug translated as well. At least every blog or book I have read state as such.

This is infact built in WordPress WPML or Polylang and why google adores WordPress.

Regardless I am changing my focus to nextjs and headless cms such as dato.

However, I am unsure how to query for this in nextjs . There is a plethora on good tutorials for static generation with next (I am keen to use getStaticProps and Paths). but basically, every tutorial does not translate the slugs. After a few days, I start to understand why: There is — so far — zero documentation to be found. So the queries in the examples do not work when there is a slug in a different language.

I am unsure where to look or even start at this.

Thank you for your wisdom! :wink:

Hello @ZeroEqualsZero

I’ll get back to you with a translated slug demo to show you how to do it with Dato + Next

Really interesting - I follow

@ZeroEqualsZero @lorenzo.defrancesco

Here, i created a project that uses the same demo in the Nexti18n blog, but the slugs are all translated
You can se a demo of the site here:
https://fabulous-maamoul-5057cf.netlify.app/
You can see from this URLs the translated slugs:

https://fabulous-maamoul-5057cf.netlify.app/posts/mistakes-tourists-make-on-their-first-trip-abroad
https://fabulous-maamoul-5057cf.netlify.app/it/posts/gli-errori-che-i-turisti-fanno-durante-il-loro-primo-viaggio-all-estero

You can see the repository i used right here: GitHub - marcelofinamorvieira/datocms-next.js-i18n-blog-demo-with-translated-slugs

The most important change can be found on the getStaticPaths function in this file: datocms-next.js-i18n-blog-demo-with-translated-slugs/[slug].js at master · marcelofinamorvieira/datocms-next.js-i18n-blog-demo-with-translated-slugs · GitHub where i loop through the locales and get the localized slug instead of just using the english slug for all paths.

And the DatoCMS project i used right here (keep in mind you will have to attach the new repository to this project)

Clone DatoCMS project

1 Like

@m.finamor

Wow! Thank you very much. For some reasons I did not get updates on this post so I totally forgot about it.

Cloning your project in DatoCms is easy.

Adding a new reposity gives me errors upon errors. it starts in vercel, where it can not create a build, so I can not link to Dato or Github, or the other way around.

Long story short:
I need to create a deploment environment. Linking to my vercel account. Which is impossible as the vercel project does not want to build. So there is nothing to link to.

For examples: I do not know where to find:

NEXT_EXAMPLE_CMS_DATOCMS_PREVIEW_SECRET=
Similarly where do i find the build ID
NEXT_EXAMPLE_CMS_DATOCMS_BUILD_TRIGGER_ID

There is no such thing as a build trigger in my dashboard? Where do I find the build trigger?
The video seems out dated.

ps: https://www.datocms.com/docs/next-js/setting-up-next-js-preview-mode has a 404

[20:41:18.668] Error: POST failed with: 401
[20:41:18.668] at IncomingMessage. (/vercel/path0/node_modules/tiny-json-http/bundle.js:1:4950)
[20:41:18.668] at IncomingMessage.emit (node:events:525:35)
[20:41:18.668] at endReadableNT (node:internal/streams/readable:1359:12)
[20:41:18.668] at process.processTicksAndRejections (node:internal/process/task_queues:82:21) {
[20:41:18.669] raw: <ref 1> IncomingMessage {
[20:41:18.669] _readableState: ReadableState {
[20:41:18.669] objectMode: false,
[20:41:18.669] highWaterMark: 16384,
[20:41:18.669] buffer: BufferList { head: null, tail: null, length: 0 },
[20:41:18.669] length: 0,
[20:41:18.670] pipes: [],
[20:41:18.670] flowing: true,
[20:41:18.670] ended: true,
[20:41:18.670] endEmitted: true,
[20:41:18.670] reading: false,
[20:41:18.670] constructed: true,
[20:41:18.670] sync: false,
[20:41:18.670] needReadable: false,
[20:41:18.670] emittedReadable: false,
[20:41:18.670] readableListening: false,
[20:41:18.670] resumeScheduled: false,
[20:41:18.670] errorEmitted: false,
[20:41:18.670] emitClose: true,
[20:41:18.670] autoDestroy: true,
[20:41:18.671] destroyed: true,
[20:41:18.671] errored: null,
[20:41:18.671] closed: true,
[20:41:18.671] closeEmitted: true,
[20:41:18.671] defaultEncoding: ‘utf8’,
[20:41:18.671] awaitDrainWriters: null,
[20:41:18.671] multiAwaitDrain: false,
[20:41:18.672] readingMore: false,
[20:41:18.672] dataEmitted: true,
[20:41:18.672] decoder: null,
[20:41:18.673] encoding: null,
[20:41:18.673] [Symbol(kPaused)]: false
[20:41:18.673] },
[20:41:18.673] _events: [Object: null prototype] {
[20:41:18.673] end: [Array],
[20:41:18.673] data: [Function (anonymous)]
[20:41:18.673] },
[20:41:18.674] _eventsCount: 2,
[20:41:18.674] _maxListeners: undefined,
[20:41:18.674] socket: TLSSocket {
[20:41:18.674] _tlsOptions: [Object],
[20:41:18.674] _secureEstablished: true,
[20:41:18.674] _securePending: false,
[20:41:18.674] _newSessionPending: false,
[20:41:18.674] _controlReleased: true,
[20:41:18.675] secureConnecting: false,
[20:41:18.675] _SNICallback: null,
[20:41:18.677] servername: ‘graphql.datocms.com’,
[20:41:18.677] alpnProtocol: false,
[20:41:18.677] authorized: true,
[20:41:18.677] authorizationError: null,
[20:41:18.677] encrypted: true,
[20:41:18.677] _events: [Object: null prototype],
[20:41:18.677] _eventsCount: 10,
[20:41:18.678] connecting: false,
[20:41:18.678] _hadError: false,
[20:41:18.678] _parent: null,
[20:41:18.678] _host: ‘graphql.datocms.com’,
[20:41:18.678] _closeAfterHandlingError: false,
[20:41:18.678] _readableState: [ReadableState],
[20:41:18.678] _maxListeners: undefined,
[20:41:18.678] _writableState: [WritableState],
[20:41:18.678] allowHalfOpen: false,
[20:41:18.678] _sockname: null,
[20:41:18.678] _pendingData: null,
[20:41:18.678] _pendingEncoding: ‘’,
[20:41:18.678] server: undefined,
[20:41:18.678] _server: null,
[20:41:18.678] ssl: [TLSWrap],
[20:41:18.678] _requestCert: true,
[20:41:18.678] _rejectUnauthorized: true,
[20:41:18.679] parser: null,
[20:41:18.679] _httpMessage: [ClientRequest],
[20:41:18.679] [Symbol(res)]: [TLSWrap],
[20:41:18.679] [Symbol(verified)]: true,
[20:41:18.679] [Symbol(pendingSession)]: null,
[20:41:18.679] [Symbol(async_id_symbol)]: 15,
[20:41:18.679] [Symbol(kHandle)]: [TLSWrap],
[20:41:18.679] [Symbol(lastWriteQueueSize)]: 0,
[20:41:18.679] [Symbol(timeout)]: null,
[20:41:18.679] [Symbol(kBuffer)]: null,
[20:41:18.679] [Symbol(kBufferCb)]: null,
[20:41:18.679] [Symbol(kBufferGen)]: null,
[20:41:18.679] [Symbol(kCapture)]: false,
[20:41:18.679] [Symbol(kSetNoDelay)]: false,
[20:41:18.679] [Symbol(kSetKeepAlive)]: false,
[20:41:18.679] [Symbol(kSetKeepAliveInitialDelay)]: 0,
[20:41:18.679] [Symbol(kBytesRead)]: 0,
[20:41:18.679] [Symbol(kBytesWritten)]: 0,
[20:41:18.679] [Symbol(connect-options)]: [Object]
[20:41:18.679] },
[20:41:18.679] httpVersionMajor: 1,
[20:41:18.679] httpVersionMinor: 1,
[20:41:18.679] httpVersion: ‘1.1’,
[20:41:18.679] complete: true,
[20:41:18.679] rawHeaders: [
[20:41:18.679] ‘Date’,
[20:41:18.680] ‘Sat, 15 Jul 2023 18:41:18 GMT’,
[20:41:18.680] ‘Content-Type’,
[20:41:18.680] ‘application/json; charset=utf-8’,
[20:41:18.680] ‘Transfer-Encoding’,
[20:41:18.680] ‘chunked’,
[20:41:18.680] ‘Connection’,
[20:41:18.680] ‘close’,
[20:41:18.680] ‘CF-Ray’,
[20:41:18.680] ‘7e74238adb8082da-IAD’,
[20:41:18.680] ‘CF-Cache-Status’,
[20:41:18.680] ‘DYNAMIC’,
[20:41:18.680] ‘Access-Control-Allow-Origin’,
[20:41:18.680] '
‘,
[20:41:18.680] ‘Cache-Control’,
[20:41:18.680] ‘private’,
[20:41:18.680] ‘Strict-Transport-Security’,
[20:41:18.680] ‘max-age=15552000; includeSubDomains; preload’,
[20:41:18.680] ‘Vary’,
[20:41:18.680] ‘Accept-Encoding’,
[20:41:18.680] ‘Via’,
[20:41:18.680] ‘1.1 vegur, 1.1 varnish, 1.1 varnish’,
[20:41:18.680] ‘Access-Control-Allow-Credentials’,
[20:41:18.681] ‘true’,
[20:41:18.681] ‘Access-Control-Allow-Headers’,
[20:41:18.681] ‘authorization, content-type, x-environment, x-organization, x-site-domain, x-api-version, user-agent, x-session-id, x-include-drafts, x-exclude-invalid, x-visual-editing, x-base-editing-url’,
[20:41:18.681] ‘Access-Control-Allow-Methods’,
[20:41:18.681] ‘GET, POST, PUT, OPTIONS, DELETE’,
[20:41:18.681] ‘Access-Control-Expose-Headers’,
[20:41:18.681] ‘x-ratelimit-limit, x-ratelimit-remaining, x-ratelimit-reset, x-complexity, x-max-complexity’,
[20:41:18.681] ‘Access-Control-Max-Age’,
[20:41:18.681] ‘1728000’,
[20:41:18.681] ‘Referrer-Policy’,
[20:41:18.681] ‘strict-origin-when-cross-origin’,
[20:41:18.681] ‘X-Cache’,
[20:41:18.681] ‘MISS, MISS’,
[20:41:18.681] ‘X-Cache-Hits’,
[20:41:18.681] ‘0, 0’,
[20:41:18.681] ‘X-Cacheable-On-CDN’,
[20:41:18.681] ‘true’,
[20:41:18.681] ‘X-Cacheable-On-CDN-Query-Length-Limit’,
[20:41:18.681] ‘175/8192’,
[20:41:18.681] ‘X-Content-Type-Options’,
[20:41:18.682] ‘nosniff’,
[20:41:18.682] ‘X-Download-Options’,
[20:41:18.682] ‘noopen’,
[20:41:18.682] ‘X-Frame-Options’,
[20:41:18.682] ‘SAMEORIGIN’,
[20:41:18.682] ‘X-Permitted-Cross-Domain-Policies’,
[20:41:18.682] ‘none’,
[20:41:18.682] ‘X-Queue-Time’,
[20:41:18.682] ‘1ms’,
[20:41:18.682] ‘X-Ratelimit-Limit’,
[20:41:18.682] ‘40’,
[20:41:18.682] ‘X-Ratelimit-Remaining’,
[20:41:18.683] ‘39’,
[20:41:18.683] ‘X-Request-Id’,
[20:41:18.683] ‘4fd9ca60-c65a-446f-bf65-769db61d4138’,
[20:41:18.683] ‘X-Runtime’,
[20:41:18.683] ‘0.004508’,
[20:41:18.683] ‘X-Served-By’,
[20:41:18.683] ‘cache-dub4328-DUB, cache-iad-kiad7000055-IAD’,
[20:41:18.683] ‘X-Timer’,
[20:41:18.683] ‘S1689446479.549097,VS0,VE107’,
[20:41:18.683] ‘X-Xss-Protection’,
[20:41:18.683] ‘1; mode=block’,
[20:41:18.683] ‘Server’,
[20:41:18.683] ‘cloudflare’
[20:41:18.683] ],
[20:41:18.683] rawTrailers: [],
[20:41:18.683] joinDuplicateHeaders: undefined,
[20:41:18.684] aborted: false,
[20:41:18.684] upgrade: false,
[20:41:18.684] url: ‘’,
[20:41:18.684] method: null,
[20:41:18.684] statusCode: 401,
[20:41:18.684] statusMessage: ‘Unauthorized’,
[20:41:18.684] client: TLSSocket {
[20:41:18.684] _tlsOptions: [Object],
[20:41:18.684] _secureEstablished: true,
[20:41:18.684] _securePending: false,
[20:41:18.684] _newSessionPending: false,
[20:41:18.684] _controlReleased: true,
[20:41:18.684] secureConnecting: false,
[20:41:18.684] _SNICallback: null,
[20:41:18.684] servername: ‘graphql.datocms.com’,
[20:41:18.684] alpnProtocol: false,
[20:41:18.684] authorized: true,
[20:41:18.684] authorizationError: null,
[20:41:18.684] encrypted: true,
[20:41:18.684] _events: [Object: null prototype],
[20:41:18.685] _eventsCount: 10,
[20:41:18.685] connecting: false,
[20:41:18.685] _hadError: false,
[20:41:18.685] _parent: null,
[20:41:18.685] _host: ‘graphql.datocms.com’,
[20:41:18.685] _closeAfterHandlingError: false,
[20:41:18.686] _readableState: [ReadableState],
[20:41:18.686] _maxListeners: undefined,
[20:41:18.686] _writableState: [WritableState],
[20:41:18.686] allowHalfOpen: false,
[20:41:18.686] _sockname: null,
[20:41:18.686] _pendingData: null,
[20:41:18.686] _pendingEncoding: ‘’,
[20:41:18.686] server: undefined,
[20:41:18.686] _server: null,
[20:41:18.686] ssl: [TLSWrap],
[20:41:18.686] _requestCert: true,
[20:41:18.686] _rejectUnauthorized: true,
[20:41:18.686] parser: null,
[20:41:18.686] _httpMessage: [ClientRequest],
[20:41:18.686] [Symbol(res)]: [TLSWrap],
[20:41:18.686] [Symbol(verified)]: true,
[20:41:18.687] [Symbol(pendingSession)]: null,
[20:41:18.687] [Symbol(async_id_symbol)]: 15,
[20:41:18.687] [Symbol(kHandle)]: [TLSWrap],
[20:41:18.687] [Symbol(lastWriteQueueSize)]: 0,
[20:41:18.687] [Symbol(timeout)]: null,
[20:41:18.687] [Symbol(kBuffer)]: null,
[20:41:18.687] [Symbol(kBufferCb)]: null,
[20:41:18.687] [Symbol(kBufferGen)]: null,
[20:41:18.687] [Symbol(kCapture)]: false,
[20:41:18.687] [Symbol(kSetNoDelay)]: false,
[20:41:18.687] [Symbol(kSetKeepAlive)]: false,
[20:41:18.687] [Symbol(kSetKeepAliveInitialDelay)]: 0,
[20:41:18.687] [Symbol(kBytesRead)]: 0,
[20:41:18.687] [Symbol(kBytesWritten)]: 0,
[20:41:18.687] [Symbol(connect-options)]: [Object]
[20:41:18.687] },
[20:41:18.688] _consuming: true,
[20:41:18.688] _dumped: false,
[20:41:18.688] req: ClientRequest {
[20:41:18.688] _events: [Object: null prototype],
[20:41:18.688] _eventsCount: 2,
[20:41:18.688] _maxListeners: undefined,
[20:41:18.688] outputData: [],
[20:41:18.688] outputSize: 0,
[20:41:18.688] writable: true,
[20:41:18.688] destroyed: false,
[20:41:18.688] _last: true,
[20:41:18.688] chunkedEncoding: false,
[20:41:18.688] shouldKeepAlive: false,
[20:41:18.688] maxRequestsOnConnectionReached: false,
[20:41:18.688] _defaultKeepAlive: true,
[20:41:18.688] useChunkedEncodingByDefault: true,
[20:41:18.688] sendDate: false,
[20:41:18.688] _removedConnection: false,
[20:41:18.689] _removedContLen: false,
[20:41:18.689] _removedTE: false,
[20:41:18.689] strictContentLength: false,
[20:41:18.689] _contentLength: 66,
[20:41:18.689] _hasBody: true,
[20:41:18.689] _trailer: ‘’,
[20:41:18.689] finished: true,
[20:41:18.689] _headerSent: true,
[20:41:18.689] _closed: false,
[20:41:18.689] socket: [TLSSocket],
[20:41:18.689] _header: ‘POST / HTTP/1.1\r\n’ +
[20:41:18.689] ‘authorization: Bearer undefined\r\n’ +
[20:41:18.689] ‘user-agent: tiny-http\r\n’ +
[20:41:18.689] ‘content-type: application/json; charset=utf-8\r\n’ +
[20:41:18.689] ‘Content-Length: 66\r\n’ +
[20:41:18.689] ‘Host: graphql.datocms.com\r\n’ +
[20:41:18.689] ‘Connection: close\r\n’ +
[20:41:18.690] ‘\r\n’,
[20:41:18.690] _keepAliveTimeout: 0,
[20:41:18.690] _onPendingData: [Function: nop],
[20:41:18.690] agent: [Agent],
[20:41:18.690] socketPath: undefined,
[20:41:18.690] method: ‘POST’,
[20:41:18.690] maxHeaderSize: undefined,
[20:41:18.690] insecureHTTPParser: undefined,
[20:41:18.690] joinDuplicateHeaders: undefined,
[20:41:18.690] path: ‘/’,
[20:41:18.690] _ended: true,
[20:41:18.690] res: [Circular 1],
[20:41:18.690] aborted: false,
[20:41:18.690] timeoutCb: null,
[20:41:18.690] upgradeOrConnect: false,
[20:41:18.690] parser: null,
[20:41:18.691] maxHeadersCount: null,
[20:41:18.691] reusedSocket: false,
[20:41:18.691] host: ‘graphql.datocms.com’,
[20:41:18.691] protocol: ‘https:’,
[20:41:18.691] [Symbol(kCapture)]: false,
[20:41:18.691] [Symbol(kBytesWritten)]: 0,
[20:41:18.691] [Symbol(kNeedDrain)]: false,
[20:41:18.691] [Symbol(corked)]: 0,
[20:41:18.691] [Symbol(kOutHeaders)]: [Object: null prototype],
[20:41:18.691] [Symbol(errored)]: null,
[20:41:18.691] [Symbol(kUniqueHeaders)]: null
[20:41:18.691] },
[20:41:18.691] [Symbol(kCapture)]: false,
[20:41:18.691] [Symbol(kHeaders)]: {
[20:41:18.691] date: ‘Sat, 15 Jul 2023 18:41:18 GMT’,
[20:41:18.691] ‘content-type’: ‘application/json; charset=utf-8’,
[20:41:18.692] ‘transfer-encoding’: ‘chunked’,
[20:41:18.692] connection: ‘close’,
[20:41:18.692] ‘cf-ray’: ‘7e74238adb8082da-IAD’,
[20:41:18.692] ‘cf-cache-status’: ‘DYNAMIC’,
[20:41:18.692] ‘access-control-allow-origin’: '
’,
[20:41:18.692] ‘cache-control’: ‘private’,
[20:41:18.692] ‘strict-transport-security’: ‘max-age=15552000; includeSubDomains; preload’,
[20:41:18.692] vary: ‘Accept-Encoding’,
[20:41:18.692] via: ‘1.1 vegur, 1.1 varnish, 1.1 varnish’,
[20:41:18.692] ‘access-control-allow-credentials’: ‘true’,
[20:41:18.692] ‘access-control-allow-headers’: ‘authorization, content-type, x-environment, x-organization, x-site-domain, x-api-version, user-agent, x-session-id, x-include-drafts, x-exclude-invalid, x-visual-editing, x-base-editing-url’,
[20:41:18.692] ‘access-control-allow-methods’: ‘GET, POST, PUT, OPTIONS, DELETE’,
[20:41:18.692] ‘access-control-expose-headers’: ‘x-ratelimit-limit, x-ratelimit-remaining, x-ratelimit-reset, x-complexity, x-max-complexity’,
[20:41:18.692] ‘access-control-max-age’: ‘1728000’,
[20:41:18.692] ‘referrer-policy’: ‘strict-origin-when-cross-origin’,
[20:41:18.692] ‘x-cache’: ‘MISS, MISS’,
[20:41:18.692] ‘x-cache-hits’: ‘0, 0’,
[20:41:18.692] ‘x-cacheable-on-cdn’: ‘true’,
[20:41:18.693] ‘x-cacheable-on-cdn-query-length-limit’: ‘175/8192’,
[20:41:18.693] ‘x-content-type-options’: ‘nosniff’,
[20:41:18.693] ‘x-download-options’: ‘noopen’,
[20:41:18.693] ‘x-frame-options’: ‘SAMEORIGIN’,
[20:41:18.693] ‘x-permitted-cross-domain-policies’: ‘none’,
[20:41:18.693] ‘x-queue-time’: ‘1ms’,
[20:41:18.693] ‘x-ratelimit-limit’: ‘40’,
[20:41:18.693] ‘x-ratelimit-remaining’: ‘39’,
[20:41:18.693] ‘x-request-id’: ‘4fd9ca60-c65a-446f-bf65-769db61d4138’,
[20:41:18.693] ‘x-runtime’: ‘0.004508’,
[20:41:18.693] ‘x-served-by’: ‘cache-dub4328-DUB, cache-iad-kiad7000055-IAD’,
[20:41:18.693] ‘x-timer’: ‘S1689446479.549097,VS0,VE107’,
[20:41:18.693] ‘x-xss-protection’: ‘1; mode=block’,
[20:41:18.693] server: ‘cloudflare’
[20:41:18.693] },
[20:41:18.693] [Symbol(kHeadersCount)]: 68,
[20:41:18.693] [Symbol(kTrailers)]: null,
[20:41:18.693] [Symbol(kTrailersCount)]: 0
[20:41:18.693] },
[20:41:18.693] body: { data: [ [Object] ] },
[20:41:18.693] statusCode: 401
[20:41:18.693] }
[20:41:18.696]
[20:41:18.696] > Build error occurred
[20:41:18.697] Error: Failed to collect page data for /posts/[slug]
[20:41:18.697] at /vercel/path0/node_modules/next/dist/build/utils.js:916:15
[20:41:18.697] at process.processTicksAndRejections (node:internal/process/task_queues:95:5) {
[20:41:18.697] type: ‘Error’
[20:41:18.697] }
[20:41:18.750] Error: Command “npm run build” exited with 1

(its probably just me — being overwhelmed with what do to first, or understand everything well)

I do understand that you have changed the [slug].js , is there any other file you have altered?

Maybe I can transform the basic datocms blog example based on your alternations.
But after trying that I get several other errors which are too long to copy and paste here. I am currently in a sort of loophole of errors.

Regardless, I will continue to find my way through all of this and my thanks is huge for all of this.

I found out why it did not work.

I had to create a new .env file with only a variable

NEXT_DATOCMS_API_TOKEN=

I deleted the env.example and in datocms.js I changed the variable name.
Uploaded, and now it perfectly builds.

I truly hope DATOCMS creates an out of the box working example demo project with localized slug, this is key for SEO.

Thank you.

Hey @ZeroEqualsZero , sorry for the late response here, but we are indeed planning on releasing new demos and starters soon! And i’ve made sure all of them have localized slugs :slight_smile:
I’ll get back to you as soon as we release them!

1 Like