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

feat: i18n framework based on extracting translations from plugin's /languages/ to /language/ #8435

Open
wants to merge 23 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
42de9ae
feat: prototype impl of l10n unpack
linonetwo Jul 27, 2024
94bac40
Squashed commit of the following:
linonetwo Jul 27, 2024
a23f8f6
fix: merge master doc update
linonetwo Jul 27, 2024
f5a43f0
fix: restore lingo macro
linonetwo Jul 27, 2024
081592d
feat: add s to /languages
linonetwo Jul 27, 2024
3b928fe
fix: show menubar tiddlyweb tree
linonetwo Jul 27, 2024
8439863
feat: add s to /languages
linonetwo Jul 27, 2024
5fdae54
fix: logic to extract l10n
linonetwo Jul 27, 2024
03c3c63
lint: eslitn
linonetwo Jul 27, 2024
1243b32
docs: mention the "s"
linonetwo Jul 27, 2024
8f9dcb6
feat: merge de-DE for menubar from https://github.com/TiddlyWiki/Tidd…
linonetwo Jul 27, 2024
ec99caa
fix: direct transclude l10n tiddler in fields
linonetwo Jul 27, 2024
e89ae53
Merge remote-tracking branch 'upstream/master' into feat/extract-plug…
linonetwo Jul 27, 2024
f9d1949
docs: not able to translate user wiki
linonetwo Jul 27, 2024
cd0e701
fix: language fallback order
linonetwo Jul 27, 2024
8d32599
feat: Allow patch other plugin's translation.
linonetwo Jul 27, 2024
883bcf7
docs: update
linonetwo Jul 27, 2024
d5019f1
feat: omit unused languages shadowtiddlers
linonetwo Jul 28, 2024
c44a117
refactor: only unpack language when switch plugin
linonetwo Jul 28, 2024
ac8d0a7
fix: must call logic in unpackPluginTiddlers, otherwise shadowTiddler…
linonetwo Jul 28, 2024
22bc121
refactor: restore
linonetwo Jul 28, 2024
011f73d
refactor: move logic to $tw.language
linonetwo Jul 28, 2024
2aadd58
refactor: use \languages\ folder
linonetwo Jul 28, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions boot/boot.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

57 changes: 57 additions & 0 deletions core/modules/language.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,63 @@ Language.prototype.getRawString = function(title) {
return this.wiki.getTiddlerText(title);
};

// Extract active translations shadow tiddlers from a plugin's `/languages` path to the `/language` path.
Language.prototype.activatePluginTranslations = function(shadowTiddlers,pluginTitle,constituentTiddlers) {
// Define the source and target namespaces
var sourcePath = pluginTitle + "/languages/";
var targetPath = pluginTitle + "/language/";
// Check the $:/language tiddler
var selectedLanguagePlugin = $tw.wiki.getTiddlerText("$:/language");
var language = selectedLanguagePlugin.replace("$:/languages/","");
// Function to extract tiddlers from a source namespace to the target namespace
function extractLanguageTiddlers(sourcePath) {
$tw.utils.each(constituentTiddlers,function(tiddler) {
if($tw.utils.startsWith(tiddler.title,sourcePath)) {
var newTitle = tiddler.title.replace(sourcePath, targetPath);
shadowTiddlers[newTitle] = {
source: pluginTitle,
tiddler: new $tw.Tiddler(tiddler,{title: newTitle})
};
} else if(!$tw.utils.startsWith(tiddler.title,pluginTitle) && tiddler.title.includes("/languages/")) {
// Allow patch other plugin's translation.
var parts = tiddler.title.split("/languages/");
if(!$tw.wiki.getPluginInfo(parts[0]) || !parts[1]) return;
// Check if the language to patch is the selected language
var languageToPatch = parts[1].split("/")[0];
if(language !== languageToPatch) return;
var newTitle = tiddler.title.replace("/languages/" + languageToPatch, "/language");
shadowTiddlers[newTitle] = {
source: pluginTitle,
tiddler: new $tw.Tiddler(tiddler,{title: newTitle})
};
}
});
}

// Extract fallback language tiddlers (en-GB) from the sourceNamespace
extractLanguageTiddlers(sourcePath + "en-GB/");

// Resolve dependents and extract them in reverse order
function resolveLanguageDependents(languagePluginTitle) {
var languagePlugin = $tw.wiki.getTiddler(languagePluginTitle);
if(languagePlugin) {
var dependents = $tw.utils.parseStringArray(languagePlugin.fields.dependents || "");
$tw.utils.each(dependents,function(dependent) {
// Resolve dependents for the selected language, extract as fallback
resolveLanguageDependents(dependent);
// Extract translation tiddlers for the selected language
var dependentLanguage = dependent.replace("$:/languages/","");
extractLanguageTiddlers(sourcePath + dependentLanguage + "/");
});
}
}

if(selectedLanguagePlugin) {
resolveLanguageDependents(selectedLanguagePlugin);
extractLanguageTiddlers(sourcePath + language + "/");
}
};

exports.Language = Language;

})();
31 changes: 25 additions & 6 deletions editions/tw5.com/tiddlers/macros/LingoMacro.tid
Original file line number Diff line number Diff line change
@@ -1,16 +1,35 @@
caption: lingo
created: 20150221154907000
modified: 20150221155706000
title: lingo Macro
modified: 20231028123405895
tags: Macros [[Core Macros]]
caption: lingo
title: lingo Macro
type: text/vnd.tiddlywiki

The <<.def lingo>> [[macro|Macros]] relates to the translation of ~TiddlyWiki's user interface into other languages. It returns a piece of text in the user's currently selected language.

Translatable text is supplied by language plugins containing tiddlers with specific titles that start with `$:/language/`.
Translatable text, aka l10n (localization), is supplied by:

!! Parameters
# Language plugins
# Tiddlers under any plugins's `languages/` path

!! Language plugins

You can directly pass title to `lingo` macro, when there is a language plugin containing a tiddler with such title that start with `$:/language/`.

;title
: The title of the shadow tiddler that contains the text. The prefix `$:/language/` is added automatically

<<.macro-examples "lingo">>
<<.macro-examples "lingo (for language plugin)">>

!! Plugins

When writing a plugin, you can place translatable text in tiddlers under the `$:/plugins/xxx/yyy/languages/` path of your plugin. During wiki boot they are automatically moved to `$:/plugins/xxx/yyy/language/` path, so lingo macro can use `$:/plugins/xxx/yyy/language/` as base path to get the translation.

!! Parameters

;key
: The last part of title of the tiddler that contains the text. The `<<lingo-base>>` prefix and current language name prefix is added automatically

<<.macro-examples "lingo (for custom base)">>

{{lingo Macro (file structure)}}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
created: 20231028120432257
modified: 20240206113509050
tags: [[lingo Macro]] [[Macro Examples]]
title: lingo (for custom plugin) Macro (Examples)
type: text/vnd.tiddlywiki

\define lingo-base() $:/plugins/tiddlywiki/menubar/language/

Given the `\define lingo-base() $:/plugins/tiddlywiki/menubar/language/`, this example shows the localizaion key `Config/MenuItems/Heading` being translate to the text in [[$:/plugins/tiddlywiki/menubar/language/en-GB/Config/MenuItems/Heading]]:

<$macrocall $name=".example" n="1" eg="""<<lingo Config/MenuItems/Heading>>"""/>

This example shows the `lingo-base` can be prepend to the key, and do a transclusion:

<$macrocall $name=".example" n="2" eg="""{{$:/plugins/tiddlywiki/menubar/language/Config/MenuItems/Heading}}"""/>

When use lingo macro in a [[Inline Mode WikiText]] like [[list|Lists in WikiText]] or [[title|Headings in WikiText]], the parse mode will be inline, so translated text will be inlined too.

<$macrocall $name=".example" n="3" eg="""# <<lingo Config/MenuItems/Heading>>"""/>

<$macrocall $name=".example" n="4" eg="""!! <<lingo Config/MenuItems/Heading>>"""/>
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
created: 20231028120432257
modified: 20240206122408606
tags: [[lingo Macro]] [[Macro Examples]]
title: lingo Macro (file structure)

!! Example file structure for [[TiddlyWiki on Node.js]]

!!! Suggested file structure

When developing a plugin, you may want to organize your language files like this on the file system as [[MultiTiddlerFiles]]:

```tree
├── languages
│ ├── en-GB
│ │ ├── Translations.multids
│ │ └── SomeLongText.tid
│ └── zh-Hans
│ ├── Translations.multids
│ └── SomeLongText.tid
├── other files
└── plugin.info
```

See [[$:/plugins/tiddlywiki/menubar/tree]] for an example.

!!! Define Multiple Translations in One Tiddler

And the content of `languages/en-GB/Translations.multids` may looks like this:

```multids
title: $:/plugins/yourName/pluginName/languages/en-GB/

OpenInteractiveCard: Open Interactive Card
OpenStaticCard: Open Static Card
```

Later you can use it like:

```tid
title: someTiddler
caption: {{$:/plugins/yourName/pluginName/language/OpenStaticCard}}

\procedure lingo-base() $:/plugins/yourName/pluginName/language/
\whitespace trim

<<lingo OpenInteractiveCard>>
```

Note that you write the translations under `/languages/` with "s", but use `/language/` without "s" in the `lingo-base`. Because TiddlyWiki automatically moves the active translations to `/language/` without "s" when it loads the plugin.

!!! Define Long Text in a regular Tiddler

You can also use a regular tiddler for long text, like `SomeLongText.tid` in the example above, to store a multi-paragraph long text:

```tid
title: $:/plugins/yourName/pluginName/languages/en-GB/SomeLongText

!!! SubTitle

This is a long text.
```

Later you can use it like:

```tid
title: someTiddler

\procedure lingo-base() $:/plugins/yourName/pluginName/language/

!! <<lingo OpenInteractiveCard>>

<<lingo SomeLongText>>
```

Note that lingo macro will use the [[parse mode|WikiText Parser Modes]] in the current position where this procedure is invoked.

!!! Patch other plugin

If you want to add translations to another plugin, simply add tiddler with title prefix of that plugin. For example, if you want to add `de-AT` translations to `$:/plugins/tiddlywiki/menubar`, you can create a new tiddler with the title `$:/plugins/tiddlywiki/menubar/languages/de-AT/SomeTranslations` and put your translations in it.

Make sure your plugin's `plugin.info` has a higher `plugin-priority` than the plugin you want to patch.

This is the last resort if you can't directly PR to original plugin's code repository.
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
created: 20150221151358000
modified: 20150221160113000
tags: [[lingo Macro]] [[Macro Examples]]
title: lingo Macro (Examples)
title: lingo Macro for official language plugin (Examples)
type: text/vnd.tiddlywiki

This example shows the text used as the basis for the title of a newly created tiddler:
Expand Down
19 changes: 10 additions & 9 deletions plugins/tiddlywiki/menubar/config.tid
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,13 @@ tags: $:/tags/ControlPanel/Toolbars
caption: Menu Bar

\define config-base() $:/config/plugins/menubar/MenuItems/Visibility/
\define lingo-base() $:/plugins/tiddlywiki/menubar/language/

! Menu Bar Configuration
! <<lingo Config/HeadingMain>>

!! Menu Items
!! <<lingo Config/MenuItems/Heading>>

Select which menu items will be shown. You can also drag items to reorder them.
<<lingo Config/MenuItems/Description>>

<$set name="tv-config-toolbar-icons" value="yes">

Expand All @@ -20,18 +21,18 @@ Select which menu items will be shown. You can also drag items to reorder them.

</$set>

!! Breakpoint Position
!! <<lingo Config/BreakpointPosition/Heading>>

The breakpoint position between narrow and wide screens. Should include CSS units (eg. `400px`).
<<lingo Config/BreakpointPosition/Description>>

<$edit-text tiddler="$:/config/plugins/menubar/breakpoint" default="" tag="input"/>

!! Contents Tag
!! <<lingo Config/ContentsTag/Heading>>

The tag for the ~TableOfContents used in the Contents dropdown
<<lingo Config/ContentsTag/Description>>

<$edit-text tiddler="$:/config/plugins/menubar/TableOfContents/Tag" default="" tag="input"/>

!! Menu Bar Colours
!! <<lingo Config/MenuBarColours/Heading>>

To change the colour of the menu bar, define the colours `menubar-foreground` and `menubar-background` in the currently selected palette
<<lingo Config/MenuBarColours/Description>>
4 changes: 2 additions & 2 deletions plugins/tiddlywiki/menubar/items/contents.tid
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
title: $:/plugins/tiddlywiki/menubar/items/contents
caption: Contents
description: Table of Contents
caption: {{$:/plugins/tiddlywiki/menubar/language/Items/TOC/Name}}
description: {{$:/plugins/tiddlywiki/menubar/language/Items/TOC/Description}}
is-dropdown: yes
tags: $:/tags/MenuBar

Expand Down
4 changes: 2 additions & 2 deletions plugins/tiddlywiki/menubar/items/hamburger.tid
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
title: $:/plugins/tiddlywiki/menubar/items/hamburger
tags: $:/tags/MenuBar
caption: Hamburger
description: Show the full menu bar on a narrow screen
caption: {{$:/plugins/tiddlywiki/menubar/language/Items/Hamburger/Name}}
description: {{$:/plugins/tiddlywiki/menubar/language/Items/Hamburger/Description}}
custom-menu-content: {{$:/plugins/tiddlywiki/menubar/items/hamburger}}
show-when: narrow

Expand Down
4 changes: 2 additions & 2 deletions plugins/tiddlywiki/menubar/items/pagecontrols.tid
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
title: $:/plugins/tiddlywiki/menubar/items/pagecontrols
tags: $:/tags/MenuBar
description: Page controls from the sidebar
caption: Page controls
description: {{$:/plugins/tiddlywiki/menubar/language/Items/PageControls/Name}}
caption: {{$:/plugins/tiddlywiki/menubar/language/Items/PageControls/Name}}
custom-menu-content: <$transclude tiddler="$:/plugins/tiddlywiki/menubar/items/pagecontrols" mode="inline"/>

\whitespace trim
Expand Down
4 changes: 2 additions & 2 deletions plugins/tiddlywiki/menubar/items/search.tid
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
title: $:/plugins/tiddlywiki/menubar/items/search
custom-menu-content: {{$:/plugins/tiddlywiki/menubar/items/search}}
description: Search
caption: Search
description: {{$:/plugins/tiddlywiki/menubar/language/Items/Search/Name}}
caption: {{$:/plugins/tiddlywiki/menubar/language/Items/Search/Name}}
tags: $:/tags/MenuBar

\define cancel-search-actions()
Expand Down
4 changes: 2 additions & 2 deletions plugins/tiddlywiki/menubar/items/server.tid
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
title: $:/plugins/tiddlywiki/menubar/items/server
tags: $:/tags/MenuBar
description: Server options
caption: Server
description: {{$:/plugins/tiddlywiki/menubar/language/Items/Server/Description}}
caption: {{$:/plugins/tiddlywiki/menubar/language/Items/Server/Name}}
custom-menu-content: <$transclude tiddler="$:/plugins/tiddlywiki/menubar/items/server" mode="inline"/>

<$list filter="[[$:/status/IsLoggedIn]get[text]else[no]match[yes]]" variable="ignore">
Expand Down
4 changes: 2 additions & 2 deletions plugins/tiddlywiki/menubar/items/sidebar.tid
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
title: $:/plugins/tiddlywiki/menubar/items/sidebar
caption: Sidebar
description: Sidebar
caption: {{$:/plugins/tiddlywiki/menubar/language/Items/Sidebar/Name}}
description: {{$:/plugins/tiddlywiki/menubar/language/Items/Sidebar/Name}}
is-dropdown: yes
tags: $:/tags/MenuBar

Expand Down
4 changes: 2 additions & 2 deletions plugins/tiddlywiki/menubar/items/topleftbar.tid
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
title: $:/plugins/tiddlywiki/menubar/items/topleftbar
tags: $:/tags/MenuBar
description: Items from $:/tags/TopLeftBar
caption: Legacy Top Left Bar
description: {{$:/plugins/tiddlywiki/menubar/language/Items/TopLeftBar/Description}}
caption: {{$:/plugins/tiddlywiki/menubar/language/Items/TopLeftBar/Name}}
custom-menu-content: <$transclude tiddler="$:/plugins/tiddlywiki/menubar/items/topleftbar" mode="inline"/>

<$list filter="[all[shadows+tiddlers]tag[$:/tags/TopLeftBar]!has[draft.of]]" variable="listItem" storyview="pop">
Expand Down
4 changes: 2 additions & 2 deletions plugins/tiddlywiki/menubar/items/toprightbar.tid
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
title: $:/plugins/tiddlywiki/menubar/items/toprightbar
tags: $:/tags/MenuBar
description: Items from $:/tags/TopRightBar
caption: Legacy Top Right Bar
description: {{$:/plugins/tiddlywiki/menubar/language/Items/TopRightBar/Description}}
caption: {{$:/plugins/tiddlywiki/menubar/language/Items/TopRightBar/Name}}
custom-menu-content: <$transclude tiddler="$:/plugins/tiddlywiki/menubar/items/toprightbar" mode="inline"/>
custom-menu-styles-wide: float: right;

Expand Down
25 changes: 25 additions & 0 deletions plugins/tiddlywiki/menubar/languages/de-DE/Translations.multids
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
title: $:/plugins/tiddlywiki/menubar/languages/de-DE/

Config/HeadingMain: Konfiguration
Config/MenuItems/Heading: Top Menü Elemente
Config/MenuItems/Description: Wähle alle Elemente, die angezeigt werden sollen. Drag and Drop kann zum sortieren verwendet werden.
Config/BreakpointPosition/Heading: Breakpoint Position
Config/BreakpointPosition/Description: Diese Einstellung schaltet zwischen breiten bzw. schmalen Displays um. CSS Einheiten werden benötigt - zB: `400px`.
Config/ContentsTag/Heading: Inhaltsverzeichnis Tag
Config/ContentsTag/Description: Tag, der für das Inhaltsverzeichnis verwendet wird.
Config/MenuBarColours/Heading: Menü Farben
Config/MenuBarColours/Description: Farben können mit `menubar-foreground` und `menubar-background` in der aktuellen Palette: {{$:/palette}} definiert werden.
Items/TOC/Name: Inhalt
Items/TOC/Description: Inhaltsverzeichnis
Items/Hamburger/Name: Menü Icon
Items/Hamburger/Description: Bei einem schmalen Bildschirm wird ein Menü-Icon angezeigt.
Items/PageControls/Name: Page controls
Items/PageControls/Description: Page controls from the sidebar.
Items/Search/Name: Suche
Items/Server/Name: Server
Items/Server/Description: Server Optionen.
Items/Sidebar/Name: Sidebar
Items/TopLeftBar/Name: Legacy Top Left Bar
Items/TopLeftBar/Description: Elemente von $:/tags/TopLeftBar
Items/TopRightBar/Name: Legacy Top Right Bar
Items/TopRightBar/Description: Elemente von $:/tags/TopRightBar
Loading
Loading