Hi,
I intend to update and improve French translation of TW documentation, ie the fr-FR edition of TW.
One problem with this task is keeping translations up to date with subsequent modifications of the tiddlers in the original edition.
Thanks to the help of the numerous plugins, tutorials and resources provided by the community, I came up with a workflow that I think could be useful to others, and maybe improved through your comments.
General idea
The general idea is to collect and extract metadata from the original documentation at tiddlywiki.com, and then import it into the French edition, with some utility tiddlers and plugins, in order to:
- Easily see if a tiddler has been modified since its last translation
- Easily compare French and English contents of a tiddler
First step: collecting English metadata
For this I use a tiddler containing this code:
\define tTitles() $:/C.Titles
\define tCreated() $:/C.Created
\define tModified() $:/C.Modified
\define tText() $:/C.Text
\define tTags() $:/C.Tags
<$button>
<$list filter="[all[tiddlers]!prefix[$]!is[draft]!tag[NoExport]sort[]]" counter="cpt">
<$action-setfield $tiddler=<<tTitles>> $index=<<cpt>> $value=<<currentTiddler>>/>
<$action-setfield $tiddler=<<tCreated>> $index=<<cpt>> $value={{!!created}}/>
<$action-setfield $tiddler=<<tModified>> $index=<<cpt>> $value={{!!modified}}/>
<$action-setfield $tiddler=<<tText>> $index=<<cpt>> $value={{!!text}}/>
<$action-setfield $tiddler=<<tTags>> $index=<<cpt>> $value={{!!tags}}/>
</$list>
Go !
</$button>
The tiddler must be imported in tiddlywiki.com (we want the most recent English data available).
Clicking on the Go!
button creates 5 data-tiddlers in which the title
, created
, modified
, text
and tags
fields of every tiddler is stored. They can be exported to JSON using the following filter in Advanced Search: [prefix[$:/C.]]
Second step: preparing French edition
Metadata
Here again we want the most recent data available, so we go to https://tiddlywiki.com/languages/fr-FR/index.html and import the JSON metadata file.
The following code injects the metadata into new fields in each French tiddler:
\define tTitles() $:/C.Titles
\define tCreated() $:/C.Created
\define tModified() $:/C.Modified
\define tText() $:/C.Text
\define tTags() $:/C.Tags
!! Insert metadata into tiddlers
<$let
tTitlesOK={{{ [<tTitles>has[text]then[✅]else[❌]] }}}
tCreatedOK={{{ [<tCreated>has[text]then[✅]else[❌]] }}}
tModifiedOK={{{ [<tModified>has[text]then[✅]else[❌]] }}}
tTextOK={{{ [<tText>has[text]then[✅]else[❌]] }}}
tTagsOK={{{ [<tTags>has[text]then[✅]else[❌]] }}}
>
;Checks:
:Titles imported: <<tTitlesOK>>
:Creation dates imported: <<tCreatedOK>>
:Modification dates imported: <<tModifiedOK>>
:Text imported: <<tTextOK>>
:Tags imported: <<tTagsOK>>
</$let>
If each line above is ✅ then <$button>
<$list filter="[<tTitles>indexes[]sort[]]" variable="cpt">
<$let
myTitle={{{[<tTitles>getindex<cpt>]}}}
myCDate={{{[<tCreated>getindex<cpt>]}}}
myMDate={{{[<tModified>getindex<cpt>]}}}
myText={{{[<tText>getindex<cpt>]}}}
myTags={{{[<tTags>getindex<cpt>]}}}
>
<$action-setfield $tiddler=<<myTitle>> $field="C.created" $value=<<myCDate>> $timestamp="no"/>
<$action-setfield $tiddler=<<myTitle>> $field="C.modified" $value=<<myMDate>> $timestamp="no"/>
<$action-setfield $tiddler=<<myTitle>> $field="C.text" $value=<<myText>> $timestamp="no"/>
<$action-setfield $tiddler=<<myTitle>> $field="C.tags" $value=<<myTags>> $timestamp="no"/>
</$let>
</$list>
Go !
</$button>
The new fields are C.created
, C.modified
, C.tags
and C.text
. The action-setfield
widgets use the $timestamp="no"
parameter to preserve modification date of destination tiddlers.
The C.created
field is useless though, because new English tiddlers are automatically copied to the French edition, so they share the same creation date.
Styles for outdated translations
The new cascades mechanism provides an easy way to highlight outdated translations with 3 tiddlers:
The first one named $:/title-when-outdated
:
\whitespace trim
<h2 class="tc-title">
🆘⏰🇫🇷 <$view field="fr-title"><$view field="title"/></$view> 🇫🇷⏰🆘
</h2>
Then $:/title-when-original
, for untranslated tiddlers:
\whitespace trim
<h2 class="tc-title">
🆘🇬🇧 <$view field="title"/> 🇬🇧🆘
</h2>
And $:/title-filter-for-translation
for the cascade, tagged $:/tags/ViewTemplateTitleFilter
:
[has[modified]has[C.modified]get[modified]compare:date:lt{!!C.modified}then[$:/title-when-outdated]]
[has[modified]has[C.modified]get[modified]compare:date:eq{!!C.modified}then[$:/title-when-original]]
The French translation uses a specially crafted ViewTemplate to display a translated title for tiddlers, stored in the fr-title
field. This ViewTemplate isn’t compatible yet with cascades, so the above tiddlers won’t work until the shadow tiddler $:/core/ui/ViewTemplate
is deleted.
Plugins
@pmario wrote two very useful plugins for translation: Toggle Field Visibility and Multiline Field Editor, both available here:
https://wikilabs.github.io/editions/field-editor/#%24%3A%2Fplugins%2Fwikilabs%2Ffield-visibility:%24%3A%2Fplugins%2Fwikilabs%2Ffield-visibility%20%24%3A%2Fplugins%2Fwikilabs%2Ffield-editor
After importing them into the French edition TW, configure the Multiline Field Editor for the C.text
field – or create this tiddler (presented in TID format for convenience):
title: $:/config/wikilabs/field/C.text
description: English text
icon: {{$:/languages/en-GB/icon}}
tags: $:/tags/wikilabs/fieldswitcher
type: text/vnd.tiddlywiki
C.text
After configuration, the Multiline Field Editor plugin features an EditMode split view displaying two fields of the same tiddler at the same time, thus easing comparison of English and translated texts.
Cleanup
After translations are done, and before preparing a PR, English metadata must be removed from tiddlers:
\define tTitles() $:/C.Titles
\define tCreated() $:/C.Created
\define tModified() $:/C.Modified
\define tText() $:/C.Text
\define tTags() $:/C.Tags
!! Remove metadata from tiddlers
<$let
tTitlesOK={{{ [<tTitles>has[text]then[✅]else[❌]] }}}
tCreatedOK={{{ [<tCreated>has[text]then[✅]else[❌]] }}}
tModifiedOK={{{ [<tModified>has[text]then[✅]else[❌]] }}}
tTextOK={{{ [<tText>has[text]then[✅]else[❌]] }}}
tTagsOK={{{ [<tTags>has[text]then[✅]else[❌]] }}}
>
;Checks:
:Titles imported: <<tTitlesOK>>
:Creation dates imported: <<tCreatedOK>>
:Modification dates imported: <<tModifiedOK>>
:Text imported: <<tTextOK>>
:Tags imported: <<tTagsOK>>
</$let>
If each line above is ✅ then <$button>
<$list filter="[<tTitles>indexes[]sort[]]" variable="cpt">
<$let
myTitle={{{[<tTitles>getindex<cpt>]}}}
>
<$action-setfield $tiddler=<<myTitle>> $field="C.created" $timestamp="no"/>
<$action-setfield $tiddler=<<myTitle>> $field="C.modified" $timestamp="no"/>
<$action-setfield $tiddler=<<myTitle>> $field="C.text" $timestamp="no"/>
<$action-setfield $tiddler=<<myTitle>> $field="C.tags" $timestamp="no"/>
</$let>
</$list>
Go !
</$button>
Exporting translations
Translated tiddlers are listed with the Filter tab of the Advanced Search, using this filter:
[all[tiddlers]!is[draft]!prefix[$:/]] :map[get[modified]compare:date:gt{$:/C.Titles!!created}then<currentTiddler>]
Each tiddler should be exported to a separate TID file, it’s easier to add them to a PR in Github.
Workflow improvements (AKA: todo )
- Remove
created
metadata from tooling (useless). - Improve and update ViewTemplate of the French edition: use cascades.
- Add a filter showing outdated tiddlers to tooling.
- Update @pmario Multiline Field Editor plugin for cascades mechanism: in its current state it breaks TW’s Import function. Many thanks @pmario anyway
- Use @Mohammad 's Gatha plugin to package utility tiddlers with @pmario plugins, for an easy import into translated edition.