Subject: Re: Re: internationalization support?
Krzysztof Królczyk <[email protected]> Thu, 15 Jan 2026 22:34:35 +0100
| Newsgroups | gmane.network.unison.devel |
|---|---|
| Message-ID | <[email protected]> |
This is a multi-part message in MIME format.
--===============8385169913547917416==
Content-Type: multipart/alternative;
boundary="------------TAyFB6ta7dtPCsPaurlhoUAu"
Content-Language: en-US, de-DE, pl
This is a multi-part message in MIME format.
--------------TAyFB6ta7dtPCsPaurlhoUAu
Content-Type: text/plain; charset=UTF-8; format=flowed
Content-Transfer-Encoding: 8bit
> My views:
> - internationalization/translation would be great
Good to hear!
> - I don't want to depend on non-Free tools including hosted translations.
both `ocaml-gettext` and its dependency camomile are LGPL2.1 so are
compatible with GPL3 used by Unison (as per my knowledge, ianal ;))
> - ocaml-gettext isn't already packaged in pkgsrc. That doesn't mean I
> think ocaml-gettext is not ok, but it does mostly mean that nothing
> else in ocaml is that is packaged needs it.
I don't fully understand what do you mean - what exactly is `pkgsrc`?.
"src" dir of Unison?
By packaging you mean, it's not vendored? Or that it hasn't been a
dependency before?
(That is right, nothing depended on it)
> - I would want to be able to build without ocaml-gettext.
> - I'd be ok with checking for gettext and using it, more or less, on
> systems where it works (subject to a build flag to say don't). I'm
> ok with a world where i18n works on some systems and not others, as
> long as that's not structural so much as nobody has yet done the
> per-OS work.
Right - while it is quite doable in makefile, to be honest i don't know
how could one solve it on the OCaml side.
Either the strings would be duplicated (inside code and inside
translation en.po file), or there could be a way to preprocess them back
to the code.
What i mean is - if they are replaced inside the code with
`(translate_function "message_id")` then code obviously wouldn't build
without it.
One could do a replace, like: "for each `(translate_function "%s")` grep
en.po, extract and replace in source.ml" however... it is:
a) quite ugly (messing with src where you might have already other
changes, intended to be committed)
b) error prone, especially with multi-line strings
Duplication is also problematic for obvious sync reasons. Also, for the
case someone wanted translations - it would need to replace the strings
again...
Third approach - one could treat literal text as message id (so a key in
a translation map/dictionary),
but this quite likely would bloat the translation files. I'm not quite
sure if this would also work; it would still need to preprocess sources,
and convert `translate_function` calls to some `noop` function, which -
when passed string argument would simply return it. Perhaps anyone has a
better idea?
Fourth idea is even more funny. Every string is extracted to a separate
module, and kept in a Map. Something more or less like:
```
|moduleAllStringsHolder =structletdummy x =x moduleStringMap
=Map.Make(String)letmy_map =ref (StringMap.empty)letinitialize ()=my_map
:=StringMap.empty|
|// here quite unpleasant initialization...|
|letget_entry key =StringMap.find_opt key !my_map letreplace_dummy f
=dummy <-f end|
|```|
||
|If the internationalization module is build, it could replace dummy
with it's `s_`. Maybe. If its not build, there is a lookup penalty...|
Last approach, it would need to be somewhat default.
Closely related task - *if this was made a hard dependency* - would be,
how does one actually reliably extract strings from all 'ml' source files.
By hard dependency i mean, it would always depend on `gettext`, and a
switch would just disable building languages other than default (EN).
So, yeah - maybe someone has a better idea.
> >/ - It likely would slightly complicate build process (currently uses />/OPAM gettext version downgraded to 0.4.2, because newer uses dune-site />/which wasn't cooperating) ///hmm, another dependency, but if unison uses it if it's built and not if
> it isn't probably is ok.
What do you mean? So gettext and gettext-camomile while two separate
packages - are kind of single dependency to internationalization.
If you mean OPAM? Then it was already used by CI so i guess it is
required. I'm not sure is it preferred to have a make target for deps
and simply run it for example from main Makefile like `opam_deps:\n\t
opam install cairo2 lablgtk3 gettext.0.4.2 camomile.1.0.2` or - if issue
above can be somehow resolved, then on demand.
> Feel totally free to open a draft PR if you have something that works,
> which will make it visible to all. Others may wish to test and help.
Ok, PR #1170. Please let me know what you think, or in case of
improvements, bugs ect.
--
Best regards,
Krzysztof
--------------TAyFB6ta7dtPCsPaurlhoUAu
Content-Type: text/html; charset=UTF-8
Content-Transfer-Encoding: 8bit
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
</head>
<body>
<blockquote type="cite">
<pre>My views:
- internationalization/translation would be great</pre>
</blockquote>
<font face="monospace"><br>
Good to hear!<br>
<br>
</font>
<blockquote type="cite">
<pre> - I don't want to depend on non-Free tools including hosted translations.</pre>
</blockquote>
<br>
<font face="monospace">both `ocaml-gettext` and its dependency
camomile are LGPL2.1 so are compatible with GPL3 used by Unison
(as per my knowledge, ianal ;))<br>
<br>
</font>
<blockquote type="cite">
<pre> - ocaml-gettext isn't already packaged in pkgsrc. That doesn't mean I
think ocaml-gettext is not ok, but it does mostly mean that nothing
else in ocaml is that is packaged needs it.</pre>
</blockquote>
<font face="monospace"><br>
I don't fully understand what do you mean - what exactly is
`pkgsrc`?. "src" dir of Unison?<br>
By packaging you mean, it's not vendored? Or that it hasn't been a
dependency before?<br>
(That is right, nothing depended on it)<br>
<br>
</font>
<blockquote type="cite">
<pre> - I would want to be able to build without ocaml-gettext.
- I'd be ok with checking for gettext and using it, more or less, on
systems where it works (subject to a build flag to say don't). I'm
ok with a world where i18n works on some systems and not others, as
long as that's not structural so much as nobody has yet done the
per-OS work.</pre>
</blockquote>
<font face="monospace"><br>
Right - while it is quite doable in makefile, to be honest i don't
know how could one solve it on the OCaml side.<br>
Either the strings would be duplicated (inside code and inside
translation en.po file), or there could be a way to preprocess
them back to the code.<br>
What i mean is - if they are replaced inside the code with
`(translate_function "message_id")` then code obviously wouldn't
build without it.<br>
One could do a replace, like: "for each `(translate_function
"%s")` grep en.po, extract and replace in source.ml" however... it
is:<br>
<br>
a) quite ugly (messing with src where you might have already other
changes, intended to be committed)<br>
b) error prone, especially with multi-line strings<br>
Duplication is also problematic for obvious sync reasons. Also,
for the case someone wanted translations - it would need to
replace the strings again...<br>
<br>
Third approach - one could treat literal text as message id (so a
key in a translation map/dictionary),<br>
but this quite likely would bloat the translation files. I'm not
quite sure if this would also work; it would still need to
preprocess sources,<br>
and convert `translate_function` calls to some `noop` function,
which - when passed string argument would simply return it.
Perhaps anyone has a better idea?<br>
Fourth idea is even more funny. Every string is extracted to a
separate module, and kept in a Map. Something more or less like:<br>
<br>
```</font><br>
<font face="monospace"><code class="language-ocaml"
style="color: rgb(248, 248, 242); background: none; text-shadow: rgba(0, 0, 0, 0.3) 0px 1px; text-align: left; white-space: pre; word-spacing: normal; word-break: normal; overflow-wrap: normal; line-height: 1.5; tab-size: 4; hyphens: none;"><span
class="token" style="color: rgb(139, 233, 253);">module</span><span> AllStringsHolder </span><span
class="token" style="color: rgb(248, 248, 242);">=</span><span> </span><span
class="token" style="color: rgb(139, 233, 253);">struct</span><span>
</span><span> </span><span class="token"
style="color: rgb(139, 233, 253);">let</span><span> dummy x </span><span
class="token" style="color: rgb(248, 248, 242);">=</span><span> x
</span>
<span> </span><span class="token" style="color: rgb(139, 233, 253);">module</span><span> StringMap </span><span
class="token" style="color: rgb(248, 248, 242);">=</span><span> Map</span><span
class="token" style="color: rgb(248, 248, 242);">.</span><span>Make</span><span
class="token" style="color: rgb(248, 248, 242);">(</span><span>String</span><span
class="token" style="color: rgb(248, 248, 242);">)</span><span>
</span><span> </span><span class="token"
style="color: rgb(139, 233, 253);">let</span><span> my_map </span><span
class="token" style="color: rgb(248, 248, 242);">=</span><span> ref </span><span
class="token" style="color: rgb(248, 248, 242);">(</span><span>StringMap</span><span
class="token" style="color: rgb(248, 248, 242);">.</span><span>empty</span><span
class="token" style="color: rgb(248, 248, 242);">)</span><span>
</span>
<span> </span><span class="token" style="color: rgb(139, 233, 253);">let</span><span> initialize </span><span
class="token" style="color: rgb(248, 248, 242);">(</span><span
class="token" style="color: rgb(248, 248, 242);">)</span><span> </span><span
class="token" style="color: rgb(248, 248, 242);">=</span><span>
</span><span> my_map </span><span class="token"
style="color: rgb(248, 248, 242);">:=</span><span> StringMap</span><span
class="token" style="color: rgb(248, 248, 242);">.</span><span>empty</span></code></font><br>
<font face="monospace"><code class="language-ocaml"
style="color: rgb(248, 248, 242); background: none; text-shadow: rgba(0, 0, 0, 0.3) 0px 1px; text-align: left; white-space: pre; word-spacing: normal; word-break: normal; overflow-wrap: normal; line-height: 1.5; tab-size: 4; hyphens: none;"><span> // here quite unpleasant initialization...</span></code></font><br>
<font face="monospace"><code class="language-ocaml"
style="color: rgb(248, 248, 242); background: none; text-shadow: rgba(0, 0, 0, 0.3) 0px 1px; text-align: left; white-space: pre; word-spacing: normal; word-break: normal; overflow-wrap: normal; line-height: 1.5; tab-size: 4; hyphens: none;"><span>
</span><span> </span><span class="token"
style="color: rgb(139, 233, 253);">let</span><span> get_entry key </span><span
class="token" style="color: rgb(248, 248, 242);">=</span><span>
</span><span> StringMap</span><span class="token"
style="color: rgb(248, 248, 242);">.</span><span>find_opt key </span><span
class="token" style="color: rgb(248, 248, 242);">!</span><span>my_map
</span>
<span> </span><span class="token" style="color: rgb(139, 233, 253);">let</span><span> replace_dummy f </span><span
class="token" style="color: rgb(248, 248, 242);">=</span><span> dummy </span><span
class="token" style="color: rgb(248, 248, 242);"><-</span><span> f
</span><span></span><span class="token"
style="color: rgb(139, 233, 253);">end</span></code></font><br>
<font face="monospace"><code class="language-ocaml"
style="color: rgb(248, 248, 242); background: none; text-shadow: rgba(0, 0, 0, 0.3) 0px 1px; text-align: left; white-space: pre; word-spacing: normal; word-break: normal; overflow-wrap: normal; line-height: 1.5; tab-size: 4; hyphens: none;"><span
class="token" style="color: rgb(139, 233, 253);">```</span></code></font><br>
<font face="monospace"><code class="language-ocaml"
style="color: rgb(248, 248, 242); background: none; text-shadow: rgba(0, 0, 0, 0.3) 0px 1px; text-align: left; white-space: pre; word-spacing: normal; word-break: normal; overflow-wrap: normal; line-height: 1.5; tab-size: 4; hyphens: none;"><span
class="token" style="color: rgb(139, 233, 253);"></span></code></font><br>
<font face="monospace"><code class="language-ocaml"
style="color: rgb(248, 248, 242); background: none; text-shadow: rgba(0, 0, 0, 0.3) 0px 1px; text-align: left; white-space: pre; word-spacing: normal; word-break: normal; overflow-wrap: normal; line-height: 1.5; tab-size: 4; hyphens: none;"><span
class="token" style="color: rgb(139, 233, 253);">If the internationalization module is build, it could replace dummy with it's `s_`. Maybe. If its not build, there is a lookup penalty...</span></code></font><br>
<br>
<br>
Last approach, it would need to be somewhat default.<br>
Closely related task - *if this was made a hard dependency* - would
be, how does one actually reliably extract strings from all 'ml'
source files.<br>
By hard dependency i mean, it would always depend on `gettext`, and
a switch would just disable building languages other than default
(EN).<br>
<br>
So, yeah - maybe someone has a better idea.<br>
<br>
<blockquote type="cite">
<pre>><i> - It likely would slightly complicate build process (currently uses
</i>><i> OPAM gettext version downgraded to 0.4.2, because newer uses dune-site
</i>><i> which wasn't cooperating)
</i><i>
</i>hmm, another dependency, but if unison uses it if it's built and not if
it isn't probably is ok.</pre>
</blockquote>
<br>
What do you mean? So gettext and gettext-camomile while two separate
packages - are kind of single dependency to internationalization.<br>
If you mean OPAM? Then it was already used by CI so i guess it is
required. I'm not sure is it preferred to have a make target for
deps <br>
and simply run it for example from main Makefile like
`opam_deps:\n\t opam install cairo2 lablgtk3 gettext.0.4.2
camomile.1.0.2` or - if issue above can be somehow resolved, then on
demand.<br>
<br>
<blockquote type="cite">
<pre>Feel totally free to open a draft PR if you have something that works,
which will make it visible to all. Others may wish to test and help.</pre>
</blockquote>
<br>
Ok, PR #1170. Please let me know what you think, or in case of
improvements, bugs ect.<br>
<br>
--<br>
Best regards,<br>
Krzysztof
</body>
</html>
--------------TAyFB6ta7dtPCsPaurlhoUAu--
--===============8385169913547917416==
Content-Type: text/plain; charset="us-ascii"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
Content-Disposition: inline
_______________________________________________
Unison-hackers mailing list
[email protected]
https://LISTS.SEAS.UPENN.EDU/mailman/listinfo/unison-hackers
--===============8385169913547917416==--