Use query language to get translation

For a multi language category selection I need to translate the text of a select field in the panel. My first try was to just wrap the category like text: "{{ t(structureItem.category) }}" But this unfortunately doesn’t work. Is there any other way to do this?

  category:
    label: Kategorie
    type: select
    required: true
    options: query
    query:
      fetch: page.parent.categories.toStructure
      text: "{{ structureItem.category }}"
      value: "{{ structureItem.category }}"

Do the categories you query from the structure field only exist in one language?

Ok, now I have to explain myself :slight_smile:

What I really wanted to achieve is a multi language blog with translated categories.

My first idea was to have one category structure per language:

categories:
  type: fields
  fields:
    categories:
      label: Verfügbare Kategorien
      type: structure
      fields:
        category:
          label: Kategorie
          type: text

But then I would have needed a translation mapping (in a kirby language file / from keys to either german or english) somewhere and the editor wouldn’t have direct access to the categories without me. For that I also needed the translation from the category keys to the category name of the selected panel language (that was my reason to post the question).

I now have the mapping in one not translatable structure field:

      categories:
        type: fields
        fields:
          categories:
            label: Verfügbare Kategorien
            type: structure
            translate: false
            fields:
              category:
                label: Kategorie
                type: text
              categoryEn:
                label: Kategorie (english)
                type: text

And the translation happens on demand in a page controller:

return function($page) {
  $isEnglishActive = kirby()->language()->code() === 'en';

  // filter articles for current language only
  $articles = $page->children()->listed()->sortBy('date', 'desc');
  $articles = $articles->filter(function ($child) {
    return $child->translation(kirby()->language()->code())->exists();
  });

  // set all categories used by articles
  $usedCategories = array_unique($articles->pluck('category'));

  // filter articles by current category
  if ($category = param(kirby()->option('category_param'))) {
    $articles = $articles->filter(function ($article) use ($category) {
      return $article->category()->slug()->toString() === Str::slug($category);
    });
  }

  if ($isEnglishActive) {
    $availableCategories = page(kirby()->option('category_page_id'))->categories()->toStructure();
    $availableCategoriesDe = $availableCategories->pluck('category');
    $availableCategoriesEn = $availableCategories->pluck('categoryen');
    $availableCategoriesDictionary = array_combine($availableCategoriesDe, $availableCategoriesEn);

    // use english category names if current language is english
    $categories = array_map(function ($category) use ($page, $availableCategoriesDictionary) {
      return $category = array(
        'name' => $availableCategoriesDictionary[$category->category()->toString()],
        'url' => getCategoryUrl($page->url(), $category)
      );
    }, $usedCategories);
  }
  else {
    // use german category names (default)
    $categories = array_map(function ($category) use ($page) {
      return $category = array(
        'name' => $category->category()->toString(),
        'url' => getCategoryUrl($page->url(), $category)
      );
    }, $usedCategories);
  }

  return compact('articles', 'categories');
};

Now I’m pretty happy that it works, but I’m not sure if it could be any better or more kirby-like?

For the Panel part of it, I’d choose another approach.

  1. Blueprint setup for structure field in parent page (or whereever you are keeping this)
categories:
  type: structure
  translate: false
  fields:
    key:
      type: text
    de:
      type: text
      label: German
    en:
      type: text  
      label: English 
  1. Select field setup with custom field method to fetch translation for the text, passing in the field name as param make the function more versatile, because we don’t have to hardcode the structure field fieldname inside the method.
category:
  label: categories
  type: select
  options: query
  query: 
    fetch: page.parent.categories.toStructure
    value: "{{structureItem.key }}"
    text:  "{{structureItem.key.getTranslation('categories') }}"
  1. Field method to get the translation for the text:
'fieldMethods' => [
	'getTranslation' => function($field, $name) {
		$languageCode = kirby()->language()->code();
		$structure    = $field->parent()->{$name}()->toStructure();
		$item         = $structure->findBy('key', $field->value);
		$field->value = $item->{$languageCode }();
		return $field;

	}
],
  1. In your template, you can use the same approach as in this method to fetch the translation from the key that is stored in the content file.
3 Likes

That’s a much more elegant way – thank you!

Hello @texnixe, I was wondering if with Kirby 3.9 this is still the best way to approach querying a structure field for multi-language select options then using the correct translation in the front-end?

I understand that instead of fetch, I should now use:

options:
 type: query

Is there anything else that would be good to update to make use of of 3.9 updates?

I understand structure items aren’t assigned UUIDs out of the box, correct? I can always use the AutoID plugin to avoid the “key” field.

Right, Kirby doesn’t support Uuids for structure fields yet.

Instead of structureItem, use item

You are asking about the Panel side, right? Yes, that’s the way you’d have to do it.