Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Translated slugs between locales - linked translations via a reference property. Website with localized URL. #137

Closed
CarloBu opened this issue May 7, 2024 · 23 comments
Assignees
Labels
enhancement New feature or request

Comments

@CarloBu
Copy link

CarloBu commented May 7, 2024

Hello, I like sveltia cms a lot, in my opinion, sveltia cms is currently the best open source cms in the market.
I'm trying to find a solution for how to make localized markdown filenames work in sveltia cms. The post becomes quite long so I will structure it as readable as possible.

Description
I am currently working on localizing a website, I have done localization with the Astrolicious i18n tool, now I'm trying to work with Sveltia/Decap CMS. Despite extensive searches, I have not found a way to utilize translated filenames for the CMS content. I'm hoping to determine if it's feasible to make it work, I'm looking for any hacky solution as injecting a custom JavaScript into the CMS widget that allows searching for localized posts using translated filenames.

Current Implementation
For testing, I utilized the demo repository from the Astrolicious i18n tool and adapted it to my needs. Here are the relevant links:

GitHub Repository: astrolicious-i18n-demo
Live Demo: astrolicious-18n-demo.netlify.app

Currently, the testing CMS setup is as follows:

config.yaml

collections:
  - name: 'posts'
    label: 'Posts'
    folder: 'src/content/posts'
    create: true
    delete: true
    i18n: true
    format: yml
    extension: yaml
    editor:
      preview: false
    fields:
      - label: 'Default Locale Version'
        name: 'defaultLocaleVersion'
        widget: compute
        value: en/{{fields.title}}
      - label: 'Title'
        name: 'title'
        widget: 'string'
        i18n: true

Objective
My final goal is to create localized URLs based on slugs taken from Markdown filenames. For instance:

English URL: website/blog/english-post/
French URL: website/fr/le-blog/poste-francaise/
The i18n tool locates the localized YAML file by reading the defaultLocaleVersion, which points to the corresponding default locale file (en/english-post). That way i18n is able to generate localized routes.

Question
Is it possible to use some custom widget to enable sveltia cms locating for posts with localized filenames based on this reference?

I've managed to create a post with a default locale reference, but I'm still unable to create localized filenames directly in the CMS and "match" localized posts

Any insights or suggestions on how to implement this feature, or just tell that localized filenames are not possible, would be greatly appreciated.

@kyoshino
Copy link
Member

kyoshino commented May 7, 2024

Hello, I like sveltia cms a lot, in my opinion, sveltia cms is currently the best open source cms in the market.

Glad to hear that! There are many things to do before general availability, but I love to see people enjoying the product 💯

I’m aware of the i18n issue that corresponds to these Netlify/Decap CMS issues:

Well, first-class i18n support is a selling point of Sveltia CMS. I’ll try to find a solution for this!

@kyoshino kyoshino self-assigned this May 7, 2024
@kyoshino kyoshino added the enhancement New feature or request label May 7, 2024
@CarloBu
Copy link
Author

CarloBu commented May 8, 2024

Awesome, wow, thank you for your quick response and your passion work.
Yes, I read these issues while I was researching the solution. I understand that the solution is not so easy as it sounds. I find out that while solving the localized references at frontend :)
firstly there is a task to generate defaultLocaleVersion. Right now the new compute widget generates only the first locale item, the rest locales leave with the empty string.
then there is a task for cms to read all files with defaultLocaleVersion and match localized md files with default locale md file

@kyoshino
Copy link
Member

kyoshino commented May 8, 2024

I think the solution is not complicated, actually.

In Hugo, it’s possible to link localized files using the translationKey property. And yes, there is an issue for Netlify/Decap CMS for adding the support:

I’m going to implement it:

  1. Allow developers to use the localize flag in the configuration file’s slug template, like {{title | localize}}, in the same way as summary string transformations.
  2. When the flag is set, the CMS will use the localized value for the file names, while adding the extra translationKey property to the files that will hold the default locale’s slug.
  3. When the CMS loads the files, use the translationKey property to link all the localized files.

@CarloBu
Copy link
Author

CarloBu commented May 8, 2024

that's a clean solution. Looking forward! ;)

@CarloBu
Copy link
Author

CarloBu commented May 8, 2024

I've encountered an issue with the way filenames are generated from titles, which seems to lack proper sanitization for URL use. For instance, the title "La Cédille En Français" results in the filename la-cédille-en-français.yaml. This filename includes special characters that are undesirable in URLs.

config.yaml

- name: test
  label: test
  folder: src/content/test
  create: true
  i18n: true
  format: yml
  extension: yaml
  slug: '{{title}}'
  slug_length: 30
  editor:
    preview: false
  fields:
    - label: Default Locale Version
      name: defaultLocaleVersion
      widget: compute
      value: fr/{{fields.title}}
      i18n: duplicate
    - label: Title
      name: title
      widget: string
      i18n: true

Feature Request:
It would be beneficial to include a slugify option in the compute widget like so:

config.yaml
value: fr/{{fields.title | slugify}}

This would allow the key value to be generated as a slugified and sanitized URL segment.

Going further:
Additionally, it might be valuable to allow developers to implement custom JavaScript functions to define their own widget logic. For example:

javascript

function stringify(title) {
  return title.toLowerCase()
              .normalize('NFD').replace(/[\u0300-\u036f]/g, '')
              .replace(/[^a-z0-9 -]/g, '')
              .replace(/\s+/g, '-')
              .replace(/-+/g, '-')
              .replace(/^-+|-+$/g, '');
}

This function could be utilized in the configuration like this:

config.yaml

fields:
  - label: Default Locale Version
    name: defaultLocaleVersion
    widget: javascript
    value: fr/{{stringify(fields.title)}}  // it would take the javascript function. It could also get multiple arguments as the rest parameter.
    i18n: duplicate
  - label: Title
    name: title
    widget: string
    i18n: true

As I understand implementing such a feature would enhance flexibility without posing a security risk, the script execution would be confined to authenticated user contexts. If I'm wrong, please excuse me.

@kyoshino
Copy link
Member

kyoshino commented May 8, 2024

You can use the slug type option 🙂 Add this to your config.yml file:

slug:
  encoding: ascii
  clean_accents: true

@CarloBu
Copy link
Author

CarloBu commented May 8, 2024

Yes! it works and filenames are sanitized now. Somehow it slipped through my eyes :)
thank you!!!
but I still have hard time with

  fields:
    - label: Default Locale Version
      name: defaultLocaleVersion
      widget: compute
      value: fr/{{fields.title}}
      i18n: duplicate

I get this:
/fr/bibliotheque-publique.yaml
defaultLocaleVersion: fr/Bibliothèque publique
category: Bibliothèque publique
order: 1

but for my i18n setup I need to get this:
/fr/bibliotheque-publique.yaml
defaultLocaleVersion: fr/bibliotheque-publique
category: Bibliothèque publique
order: 1

Is there some option to pass the sterilized slug to the field property?

@kyoshino
Copy link
Member

kyoshino commented May 8, 2024

I think my translationKey implementation is ready, so you don’t need the custom defaultLocaleVersion stuff. I haven’t added any automated tests, but the change is minimum; it shouldn’t break existing features. 😆

@kyoshino
Copy link
Member

kyoshino commented May 8, 2024

When you customize the slug like this:

slug: '{{title | localize}}'

Then file names will be localized, and translationKey will be automatically added:

  • en/government-of-canada.yaml
    title: Government of Canada
    translationKey: government-of-canada
  • fr/gouvernement-du-canada.yaml
    title: Gouvernement du Canada
    translationKey: government-of-canada

@CarloBu
Copy link
Author

CarloBu commented May 8, 2024

omg! Thank you very much!
can't wait to try it

@kyoshino
Copy link
Member

kyoshino commented May 8, 2024

Just shipped v0.25.0 with the localized slug support 🎉

@CarloBu
Copy link
Author

CarloBu commented May 9, 2024

super cool! thank you, kyoshino!
I'm trying it out

@CarloBu
Copy link
Author

CarloBu commented May 9, 2024

I tried and it is awesome!
I'm trying to work with astrolicious i18n which use translationKey with locale/+slug
by example:
translationKey: fr/my-trip-to-new-york

is there some way to add to my custom key the translationKey with custom string?
by example:

translationKey: my-trip-to-new-york
+
defaultLocaleVersion: fr/my-trip-to-new-york
collections:
  - name: posts
    label: Blog posts
    create: true
    folder: data/posts/
    slug: '{{title | localize}}'
    format: yaml
    i18n: true
    fields:
      - name: title
        label: Title
        widget: string
        i18n: true
      - name: customKey
        label: Custom Key
        widget: compute
        value: fr/{{translationKey}}

the idea is to pass translationKey to any field via compute. That way anyone would be able to work with custom file based routing workflows.
and have:
translationKey: my-trip-to-new-york <- for sveltia cms
customKey: fr/my-trip-to-new-york <- for custom routing workflow

@kyoshino
Copy link
Member

kyoshino commented May 9, 2024

I’m playing with your demo project. I think defaultLocaleVersion can be replaced translationKey, but it seems the locale prefix like fr/ or en/ has to be added to frontmatter anyway. Okay, I’ll add a new option to make the key and value customizable. Maybe like this:

i18n:
  structure: multiple_folders
  locales: [en, fr]
  canonical_slug:
    key: defaultLocaleVersion
    value: 'en/{{slug}}'

@CarloBu
Copy link
Author

CarloBu commented May 9, 2024

astrolicious i18n plugin behind the scenes changes the schema at the dev run and the frontmatter expects locale/slug, so it's not possible without forking and modifying repo.
I don't know what is easier to modify - to add translationKey into the compute widget or give the option in the i18n parameter

but either way it works perfectly:

i18n:
  structure: multiple_folders
  locales: [en, fr]
  canonical_slug:
    key: defaultLocaleVersion
    value: 'en/{{slug}}'

I understand that is quite a niche issue. :/

@kyoshino
Copy link
Member

kyoshino commented May 9, 2024

No worries! translationKey is specific to Hugo. Other frameworks may have different configurations, so I think it has to be customizable anyway.

@CarloBu
Copy link
Author

CarloBu commented May 9, 2024

That is very nice of you. I will be able to add sveltia cms to my i18n blog template.
it will be the Astro+i18n+SveltiaCMS+Cloudflare stack for ultimate modern SSG i18n workflow

kyoshino added a commit that referenced this issue May 10, 2024
@kyoshino
Copy link
Member

kyoshino commented May 10, 2024

Just shipped v0.26.0 with the new canonical_slug option. README updated!

@CarloBu
Copy link
Author

CarloBu commented May 10, 2024

That's super awesome! Can't believe that you just solved my biggest issue and now astro full localization works with sveltia cms out of the box! I'm going to check if it's working fine with astrolicious i18n.
edit- it's working

@CarloBu
Copy link
Author

CarloBu commented May 10, 2024

Hey, Kyoshino, uh, it's me again.

Sorry for this, but I found a bug when the user duplicated the markdown file via sveltiaCMS UI. The canonical_slug doesn't change. It happens only when using the new canonical_slug. This issue does not occur with the original canonical_slug.

Also, about UX, just a suggestion, when the user clicks on the Duplicate button, everything stays the same, so at first, it's not clear if the button worked. Maybe add an info text in the place of the Duplicate button, like "Duplicate Successful" or just add a suffix to the title field "-COPY" to inform that this is clearly a copy and not the original, for example.
image

Recording.2024-05-10.154255.mp4

@kyoshino
Copy link
Member

Thanks for finding bugs! Will fix them shortly. There is supposed to be a toast notification after duplicating an entry. Not sure why it’s gone 😅

kyoshino added a commit that referenced this issue May 10, 2024
@kyoshino
Copy link
Member

Just shipped v0.26.3 to solve the issues!

@CarloBu
Copy link
Author

CarloBu commented May 10, 2024

now canonical_slug is working at duplication. thank you!
And I see the toaster for the first time, nice! 👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

2 participants