diff options
| -rw-r--r-- | org/output_hub.org | 15 | ||||
| -rw-r--r-- | org/spine.org | 33 | ||||
| -rw-r--r-- | org/translation_po4a.org | 178 | ||||
| -rw-r--r-- | src/sisudoc/outputs/io_out/hub.d | 15 | ||||
| -rw-r--r-- | src/sisudoc/outputs/io_out/po4a_cfg.d | 173 | ||||
| -rw-r--r-- | src/sisudoc/spine.d | 33 |
6 files changed, 433 insertions, 14 deletions
diff --git a/org/output_hub.org b/org/output_hub.org index 27e0efc..2456878 100644 --- a/org/output_hub.org +++ b/org/output_hub.org @@ -35,6 +35,21 @@ template outputHub() { @system void outputHub(D)(D doc) { mixin Msg; auto msg = Msg!()(doc.matters); + /+ ↓ the po4a configuration, printed rather than written. + Spine does not write into a source tree, and this belongs beside the + catalogues in the pod it describes, so it goes to stdout for the + caller to put where they want it: + spine --po4a-cfg <pod> > <pod>/tools/po4a/po4a.cfg + Once per document, not once per language: it is generated from the + source language's files and names every other language itself. + +/ + if (doc.matters.opt.action.po4a_cfg + && doc.matters.src.language == doc.matters.pod.manifest_list_of_languages[0] + ) { + import sisudoc.outputs.io_out.po4a_cfg; + mixin spinePo4aConfig; + write(po4aConfig(doc.matters)); + } enum outTask { source_or_pod, sqlite, sqlite_multi, latex, odt, epub, html_scroll, html_seg, html_stuff, text, skel } void Scheduled(D)(int sched, D doc) { auto msg = Msg!()(doc.matters); diff --git a/org/spine.org b/org/spine.org index cef324a..5a54d31 100644 --- a/org/spine.org +++ b/org/spine.org @@ -325,6 +325,14 @@ enum dAM { abstraction, matters } static auto rgx = RgxI(); static auto rgx_y = RgxYaml(); static auto rgx_files = RgxFiles(); +/+ ↓ an action whose output is a file's contents, printed to stdout, turns + this off: the banner would otherwise be part of what it printed. The + round trips do the same by leaving before scope(success) runs, which + they can because they leave early; --po4a-cfg prints from inside + output processing and so says so here instead. Set where the options + are read, below: the banner is declared before they exist. ++/ +bool _run_banner = true; #+END_SRC *** scope (run complete) :scope: @@ -332,13 +340,15 @@ static auto rgx_files = RgxFiles(); #+NAME: spine_init_scope #+BEGIN_SRC d scope(success) { - writefln( - "~ run complete, ok ~ (%s-%s.%s.%s, %s D:%s, %s %s)", - program_name, - _ver.major, _ver.minor, _ver.patch, - __VENDOR__, __VERSION__, - bits, os, - ); + if (_run_banner) { + writefln( + "~ run complete, ok ~ (%s-%s.%s.%s, %s D:%s, %s %s)", + program_name, + _ver.major, _ver.minor, _ver.patch, + __VENDOR__, __VERSION__, + bits, os, + ); + } } scope(failure) { debug(checkdoc) { @@ -410,6 +420,7 @@ bool[string] opts = [ "pdf" : false, "pdf-color-links" : false, "pdf-init" : false, + "po4a-cfg" : false, "pod" : false, "pod2" : false, "serial" : false, @@ -546,6 +557,7 @@ auto helpInfo = getopt(args, "pdf", "latex output for pdfs", &opts["pdf"], "pdf-color-links", "mono or color links for pdfs", &opts["pdf-color-links"], "pdf-init", "initialise latex shared files (see latex-header-sty)", &opts["pdf-init"], + "po4a-cfg", "print the po4a configuration for this document", &opts["po4a-cfg"], "pod", "spine (doc reform) pod source content bundled", &opts["pod"], "pod2", "pod with document abstraction (.ssp) bundled", &opts["pod2"], "quiet|q", "output to terminal", &opts["vox_is1"], @@ -617,6 +629,10 @@ auto helpInfo = getopt(args, "debug-stages", "debug stages", &opts["debug-stages"], // "sqlite-db-filename", "=[filename].sql.db", &settings["sqlite-db-filename"], ); +/+ ↓ --po4a-cfg prints a file's contents; see the banner above +/ +if (opts["po4a-cfg"]) { + _run_banner = false; +} if (helpInfo.helpWanted) { defaultGetoptPrinter("Some information about the program.", helpInfo.options); } @@ -882,6 +898,9 @@ struct OptActions { @trusted bool ocn_off() { return ((opts["ocn-off"]) || (opts["no-ocn"])) ? true : false; } + @trusted bool po4a_cfg() { + return opts["po4a-cfg"]; + } @trusted bool pod() { return (opts["pod"] || opts["pod2"]) ? true : false; } diff --git a/org/translation_po4a.org b/org/translation_po4a.org new file mode 100644 index 0000000..27c3362 --- /dev/null +++ b/org/translation_po4a.org @@ -0,0 +1,178 @@ +-*- mode: org -*- +#+TITLE: sisudoc spine (doc_reform) object-centric document abstraction +#+DESCRIPTION: documents - structuring, publishing in multiple formats & search +#+SUMMARY: process markup document, create document abstraction +#+FILETAGS: :spine:abstraction: +#+AUTHOR: Ralph Amissah +#+EMAIL: [[mailto:ralph.amissah@gmail.com][ralph.amissah@gmail.com]] +#+COPYRIGHT: Copyright (C) 2015 (continuously updated, current 2026) Ralph Amissah +#+LANGUAGE: en +#+STARTUP: content hideblocks hidestars noindent entitiespretty +#+PROPERTY: header-args+ :eval never-export :exports code +#+PROPERTY: header-args+ :noweb yes :padline no +#+PROPERTY: header-args+ :results silent :cache no +#+PROPERTY: header-args+ :mkdirp yes +#+OPTIONS: H:3 num:nil toc:t \n:t ::t |:t ^:nil -:t f:t *:t +- magic single double-quote → " ← FIX changes hilighting behavior (occuring + after it) in org document. INVESTIGATE (org-mode CONFIG?) FIND & FIX + +- [[./doc-reform.org][doc-reform.org]] [[./][org/]] + +* (translation) po4a setup + +#+HEADER: :tangle "../src/sisudoc/outputs/io_out/po4a_cfg.d" +#+HEADER: :noweb yes +#+BEGIN_SRC d +<<doc_header_including_copyright_and_license>> +/++ + module po4a_cfg;<BR> + - the po4a configuration for a document, generated from its manifest and + the files its markup names<BR> + - written for tools/po4a/po4a.cfg, so its paths are relative to there ++/ +module sisudoc.outputs.io_out.po4a_cfg; +@system: +/+ ↓ po4a's configuration, derived rather than kept + + The sample this replaces kept a Makefile that built po4a.cfg by globbing + a language directory and listing what it found. Spine already knows both + halves without looking: the manifest says which languages a document + has, and the markup says which files it is made of, through the << lines + the parser follows. Deriving the configuration from those two means it + cannot disagree with the document, which a hand-kept file eventually + does: a language added to the manifest and forgotten in the Makefile + produces a translation nobody updates. + . + It is derived, so it is not kept in git. The sample repository's + whitelist admits pot/ and po/ and not this. + . + neverwrap is deliberately NOT set, and the reason is worth keeping. + . + Without it po4a rewraps its output at about 75 columns, so a document + whose markup keeps one paragraph on one long line comes back reflowed. + That is untidy: it changes no object, since sisu delimits on blank + lines, but it churns every translated file in a diff and leaves the + source language and its translations formatted unlike each other. The + obvious thing is to turn wrapping off. + . + Measured on live-manual, same catalogues and same English, the option + the only difference: + . + fuzzy before with neverwrap without + ca 1 889 5 + de 8 103 10 + it 149 804 152 + . + of 1126 strings. po4a will not use a fuzzy translation, so each one + renders as the source language: Catalan's coverage would fall from about + 46% to about 12%, and Spanish, French and Japanese with it. Setting the + option re-extracts the strings in a form the existing catalogues do not + match, and four fifths of nine languages' work stops being used. + . + Tidy formatting is not worth that. The wrapping stays. ++/ +template spinePo4aConfig() { + string po4aConfig(M)(M doc_matters) { + import std.algorithm : sort; + import std.array : array, join; + import std.conv : to; + import std.file : exists; + import std.path : baseName; + string _src_lang = doc_matters.src.language; + /+ ↓ the languages po4a is to produce, which is every language the + manifest lists except the one being translated from + +/ + string[] _targets; + foreach (_lang; doc_matters.pod.manifest_list_of_languages) { + if (_lang != _src_lang) { + _targets ~= _lang; + } + } + /+ ↓ the files the document is made of: its master, and every insert the + markup names. In manifest order rather than directory order, so the + configuration is the same however a filesystem chooses to list a + directory. + +/ + string[] _files; + _files ~= doc_matters.src.filename; + string[] _inserts; + foreach (_insert; doc_matters.srcs.file_insert_list) { + _inserts ~= _insert.baseName; + } + _inserts = _inserts.sort.array; + _files ~= _inserts; + /+ ↓ paths are relative to tools/po4a/, where this file belongs +/ + enum string _to_text = "../../media/text"; + string _o; + _o ~= "# po4a configuration for " ~ doc_matters.src.filename_base ~ "\n"; + _o ~= "# generated by spine from pod.manifest and the markup; not kept in git\n"; + _o ~= "# run from the directory this file is in\n"; + _o ~= "\n"; + /+ ↓ --keep 0 is not optional here, it is the difference between + regenerating a document and destroying it. + . + po4a will not write a translated file that falls below a + completeness threshold, and that threshold defaults to 80%. A + partially translated document is the normal state of one being + worked on, and sisu's own behaviour covers it: an untranslated + string falls back to the source text, so a file at 3% is mostly the + source language and entirely readable. (Measured on live-manual at + the default: 181 of its 189 translated files would be discarded, and + nine languages would collapse to eight surviving files. It would not + look like a failure either, because what remains reads as the + document reverting to its source language.) + . + The Makefile this replaces passed --keep 0 for exactly this reason. + +/ + _o ~= "[options] --keep 0 --package-name " ~ doc_matters.src.filename_base ~ "\n"; + _o ~= "\n"; + _o ~= "[po4a_langs] " ~ _targets.join(" ") ~ "\n"; + _o ~= "[po4a_paths] pot/$master.pot $lang:po/$lang/$master.po\n"; + _o ~= "\n"; + foreach (_f; _files) { + _o ~= "[type: text] " ~ _to_text ~ "/" ~ _src_lang ~ "/" ~ _f + ~ " $lang:" ~ _to_text ~ "/$lang/" ~ _f ~ "\n"; + } + /+ ↓ index.html.in is not markup and no << line names it, so it is found + by asking whether the source language has one. live-manual does, the + Makefile it replaces carried it as xhtml, and the sample + repository's whitelist admits index.html.in.pot and .po by name. + Left out, regenerating would quietly drop those catalogue entries. + +/ + string _index_in = doc_matters.pod.manifest_path + ~ "/media/text/" ~ _src_lang ~ "/index.html.in"; + if (_index_in.exists) { + _o ~= "[type: xhtml] " ~ _to_text ~ "/" ~ _src_lang ~ "/index.html.in" + ~ " $lang:" ~ _to_text ~ "/$lang/index.html.in\n"; + } + return _o; + } +} +#+END_SRC + +* org includes +** project version + +#+NAME: spine_version +#+HEADER: :noweb yes +#+BEGIN_SRC emacs-lisp +<<./sisudoc_spine_version_info_and_doc_header_including_copyright_and_license.org:spine_project_version()>> +#+END_SRC + +** year + +#+NAME: year +#+HEADER: :noweb yes +#+BEGIN_SRC emacs-lisp +<<./sisudoc_spine_version_info_and_doc_header_including_copyright_and_license.org:year()>> +#+END_SRC + +** document header including copyright & license + +#+NAME: doc_header_including_copyright_and_license +#+HEADER: :noweb yes +#+BEGIN_SRC emacs-lisp +<<./sisudoc_spine_version_info_and_doc_header_including_copyright_and_license.org:spine_doc_header_including_copyright_and_license()>> +#+END_SRC + +* __END__ diff --git a/src/sisudoc/outputs/io_out/hub.d b/src/sisudoc/outputs/io_out/hub.d index 0973097..254eaa5 100644 --- a/src/sisudoc/outputs/io_out/hub.d +++ b/src/sisudoc/outputs/io_out/hub.d @@ -62,6 +62,21 @@ template outputHub() { @system void outputHub(D)(D doc) { mixin Msg; auto msg = Msg!()(doc.matters); + /+ ↓ the po4a configuration, printed rather than written. + Spine does not write into a source tree, and this belongs beside the + catalogues in the pod it describes, so it goes to stdout for the + caller to put where they want it: + spine --po4a-cfg <pod> > <pod>/tools/po4a/po4a.cfg + Once per document, not once per language: it is generated from the + source language's files and names every other language itself. + +/ + if (doc.matters.opt.action.po4a_cfg + && doc.matters.src.language == doc.matters.pod.manifest_list_of_languages[0] + ) { + import sisudoc.outputs.io_out.po4a_cfg; + mixin spinePo4aConfig; + write(po4aConfig(doc.matters)); + } enum outTask { source_or_pod, sqlite, sqlite_multi, latex, odt, epub, html_scroll, html_seg, html_stuff, text, skel } void Scheduled(D)(int sched, D doc) { auto msg = Msg!()(doc.matters); diff --git a/src/sisudoc/outputs/io_out/po4a_cfg.d b/src/sisudoc/outputs/io_out/po4a_cfg.d new file mode 100644 index 0000000..ddcc1ef --- /dev/null +++ b/src/sisudoc/outputs/io_out/po4a_cfg.d @@ -0,0 +1,173 @@ +/+ +- Name: SisuDoc Spine, Doc Reform [a part of] + - Description: documents, structuring, processing, publishing, search + - static content generator + + - Author: Ralph Amissah + [ralph.amissah@gmail.com] + + - Copyright: (C) 2015 (continuously updated, current 2026) Ralph Amissah, All Rights Reserved. + + - License: AGPL 3 or later: + + Spine (SiSU), a framework for document structuring, publishing and + search + + Copyright (C) Ralph Amissah + + This program is free software: you can redistribute it and/or modify it + under the terms of the GNU AFERO General Public License as published by the + Free Software Foundation, either version 3 of the License, or (at your + option) any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + You should have received a copy of the GNU General Public License along with + this program. If not, see [https://www.gnu.org/licenses/]. + + If you have Internet connection, the latest version of the AGPL should be + available at these locations: + [https://www.fsf.org/licensing/licenses/agpl.html] + [https://www.gnu.org/licenses/agpl.html] + + - Spine (by Doc Reform, related to SiSU) uses standard: + - docReform markup syntax + - standard SiSU markup syntax with modified headers and minor modifications + - docReform object numbering + - standard SiSU object citation numbering & system + + - Homepages: + [https://www.sisudoc.org] + [https://www.doc-reform.org] + + - Git + [https://git.sisudoc.org/] + ++/ +/++ + module po4a_cfg;<BR> + - the po4a configuration for a document, generated from its manifest and + the files its markup names<BR> + - written for tools/po4a/po4a.cfg, so its paths are relative to there ++/ +module sisudoc.outputs.io_out.po4a_cfg; +@system: +/+ ↓ po4a's configuration, derived rather than kept + + The sample this replaces kept a Makefile that built po4a.cfg by globbing + a language directory and listing what it found. Spine already knows both + halves without looking: the manifest says which languages a document + has, and the markup says which files it is made of, through the << lines + the parser follows. Deriving the configuration from those two means it + cannot disagree with the document, which a hand-kept file eventually + does: a language added to the manifest and forgotten in the Makefile + produces a translation nobody updates. + . + It is derived, so it is not kept in git. The sample repository's + whitelist admits pot/ and po/ and not this. + . + neverwrap is deliberately NOT set, and the reason is worth keeping. + . + Without it po4a rewraps its output at about 75 columns, so a document + whose markup keeps one paragraph on one long line comes back reflowed. + That is untidy: it changes no object, since sisu delimits on blank + lines, but it churns every translated file in a diff and leaves the + source language and its translations formatted unlike each other. The + obvious thing is to turn wrapping off. + . + Measured on live-manual, same catalogues and same English, the option + the only difference: + . + fuzzy before with neverwrap without + ca 1 889 5 + de 8 103 10 + it 149 804 152 + . + of 1126 strings. po4a will not use a fuzzy translation, so each one + renders as the source language: Catalan's coverage would fall from about + 46% to about 12%, and Spanish, French and Japanese with it. Setting the + option re-extracts the strings in a form the existing catalogues do not + match, and four fifths of nine languages' work stops being used. + . + Tidy formatting is not worth that. The wrapping stays. ++/ +template spinePo4aConfig() { + string po4aConfig(M)(M doc_matters) { + import std.algorithm : sort; + import std.array : array, join; + import std.conv : to; + import std.file : exists; + import std.path : baseName; + string _src_lang = doc_matters.src.language; + /+ ↓ the languages po4a is to produce, which is every language the + manifest lists except the one being translated from + +/ + string[] _targets; + foreach (_lang; doc_matters.pod.manifest_list_of_languages) { + if (_lang != _src_lang) { + _targets ~= _lang; + } + } + /+ ↓ the files the document is made of: its master, and every insert the + markup names. In manifest order rather than directory order, so the + configuration is the same however a filesystem chooses to list a + directory. + +/ + string[] _files; + _files ~= doc_matters.src.filename; + string[] _inserts; + foreach (_insert; doc_matters.srcs.file_insert_list) { + _inserts ~= _insert.baseName; + } + _inserts = _inserts.sort.array; + _files ~= _inserts; + /+ ↓ paths are relative to tools/po4a/, where this file belongs +/ + enum string _to_text = "../../media/text"; + string _o; + _o ~= "# po4a configuration for " ~ doc_matters.src.filename_base ~ "\n"; + _o ~= "# generated by spine from pod.manifest and the markup; not kept in git\n"; + _o ~= "# run from the directory this file is in\n"; + _o ~= "\n"; + /+ ↓ --keep 0 is not optional here, it is the difference between + regenerating a document and destroying it. + . + po4a will not write a translated file that falls below a + completeness threshold, and that threshold defaults to 80%. A + partially translated document is the normal state of one being + worked on, and sisu's own behaviour covers it: an untranslated + string falls back to the source text, so a file at 3% is mostly the + source language and entirely readable. (Measured on live-manual at + the default: 181 of its 189 translated files would be discarded, and + nine languages would collapse to eight surviving files. It would not + look like a failure either, because what remains reads as the + document reverting to its source language.) + . + The Makefile this replaces passed --keep 0 for exactly this reason. + +/ + _o ~= "[options] --keep 0 --package-name " ~ doc_matters.src.filename_base ~ "\n"; + _o ~= "\n"; + _o ~= "[po4a_langs] " ~ _targets.join(" ") ~ "\n"; + _o ~= "[po4a_paths] pot/$master.pot $lang:po/$lang/$master.po\n"; + _o ~= "\n"; + foreach (_f; _files) { + _o ~= "[type: text] " ~ _to_text ~ "/" ~ _src_lang ~ "/" ~ _f + ~ " $lang:" ~ _to_text ~ "/$lang/" ~ _f ~ "\n"; + } + /+ ↓ index.html.in is not markup and no << line names it, so it is found + by asking whether the source language has one. live-manual does, the + Makefile it replaces carried it as xhtml, and the sample + repository's whitelist admits index.html.in.pot and .po by name. + Left out, regenerating would quietly drop those catalogue entries. + +/ + string _index_in = doc_matters.pod.manifest_path + ~ "/media/text/" ~ _src_lang ~ "/index.html.in"; + if (_index_in.exists) { + _o ~= "[type: xhtml] " ~ _to_text ~ "/" ~ _src_lang ~ "/index.html.in" + ~ " $lang:" ~ _to_text ~ "/$lang/index.html.in\n"; + } + return _o; + } +} diff --git a/src/sisudoc/spine.d b/src/sisudoc/spine.d index 9013c3e..9424e02 100644 --- a/src/sisudoc/spine.d +++ b/src/sisudoc/spine.d @@ -97,14 +97,24 @@ string program_name = "spine"; static auto rgx = RgxI(); static auto rgx_y = RgxYaml(); static auto rgx_files = RgxFiles(); + /+ ↓ an action whose output is a file's contents, printed to stdout, turns + this off: the banner would otherwise be part of what it printed. The + round trips do the same by leaving before scope(success) runs, which + they can because they leave early; --po4a-cfg prints from inside + output processing and so says so here instead. Set where the options + are read, below: the banner is declared before they exist. + +/ + bool _run_banner = true; scope(success) { - writefln( - "~ run complete, ok ~ (%s-%s.%s.%s, %s D:%s, %s %s)", - program_name, - _ver.major, _ver.minor, _ver.patch, - __VENDOR__, __VERSION__, - bits, os, - ); + if (_run_banner) { + writefln( + "~ run complete, ok ~ (%s-%s.%s.%s, %s D:%s, %s %s)", + program_name, + _ver.major, _ver.minor, _ver.patch, + __VENDOR__, __VERSION__, + bits, os, + ); + } } scope(failure) { debug(checkdoc) { @@ -166,6 +176,7 @@ string program_name = "spine"; "pdf" : false, "pdf-color-links" : false, "pdf-init" : false, + "po4a-cfg" : false, "pod" : false, "pod2" : false, "serial" : false, @@ -288,6 +299,7 @@ string program_name = "spine"; "pdf", "latex output for pdfs", &opts["pdf"], "pdf-color-links", "mono or color links for pdfs", &opts["pdf-color-links"], "pdf-init", "initialise latex shared files (see latex-header-sty)", &opts["pdf-init"], + "po4a-cfg", "print the po4a configuration for this document", &opts["po4a-cfg"], "pod", "spine (doc reform) pod source content bundled", &opts["pod"], "pod2", "pod with document abstraction (.ssp) bundled", &opts["pod2"], "quiet|q", "output to terminal", &opts["vox_is1"], @@ -359,6 +371,10 @@ string program_name = "spine"; "debug-stages", "debug stages", &opts["debug-stages"], // "sqlite-db-filename", "=[filename].sql.db", &settings["sqlite-db-filename"], ); + /+ ↓ --po4a-cfg prints a file's contents; see the banner above +/ + if (opts["po4a-cfg"]) { + _run_banner = false; + } if (helpInfo.helpWanted) { defaultGetoptPrinter("Some information about the program.", helpInfo.options); } @@ -611,6 +627,9 @@ string program_name = "spine"; @trusted bool ocn_off() { return ((opts["ocn-off"]) || (opts["no-ocn"])) ? true : false; } + @trusted bool po4a_cfg() { + return opts["po4a-cfg"]; + } @trusted bool pod() { return (opts["pod"] || opts["pod2"]) ? true : false; } |
