diff --git a/.gitignore b/.gitignore index dbd5747d..011303c5 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ .DS_Store node_modules public +translations.yaml diff --git a/.po4a.cfg b/.po4a.cfg index c345ba84..857ad502 100644 --- a/.po4a.cfg +++ b/.po4a.cfg @@ -11,3 +11,4 @@ [type: markdown] content/privacy/index.md $lang:content/privacy/index.$lang.md [type: markdown] content/support-us/index.md $lang:content/support-us/index.$lang.md [type: markdown] content/terms/index.md $lang:content/terms/index.$lang.md +[type: yaml] translations.yaml $lang:/tmp/ignore.yaml diff --git a/TRANSLATIONS.md b/TRANSLATIONS.md index 1e58571b..b94d78d3 100644 --- a/TRANSLATIONS.md +++ b/TRANSLATIONS.md @@ -55,7 +55,7 @@ brew install po4a ### I am a content writer - Add/edit source **English** Markdown files in the repo. -- Refresh `.pot` and `.po` files when English is changed by running `./i18n.sh`. +- Refresh `.pot` and `.po` files when English is changed by running `./tools/i18n.sh`. - Push both updated content and refreshed `.pot`/`.po` files in the single PR to GitHub. - Translation teams will be notified via Weblate when your PR is merged. - All new strings will be in English until localized (see the next step). @@ -69,7 +69,7 @@ brew install po4a ### I am a repository maintainer - Checkout `weblate-i18n` branch when it is updated by Weblate. -- Run `./i18n.sh` tool to regenerate `.lang.md` files from updated `.po` files. +- Run `./tools/i18n.sh` tool to regenerate `.lang.md` files from updated `.po` files. - Update `.config.toml` if a new language is added. - Translate `menu.title` YAML Front Matters key manually, this is a [well-known limitation](https://github.com/mquinson/po4a/issues/392). - Run `zola server` to check changes locally. @@ -79,8 +79,8 @@ brew install po4a ## Known Limitations -- `./i18n.sh` removes line wrapping in `.po` files when a new language is added initially via WebLate. -- New web-site pages should be added to `.po4a.cfg` configuration file manually. +- `./tools/i18n.sh` removes line wrapping in `.po` files when a new language is added initially via WebLate. +- New web-site pages and languages should be added to `.po4a.cfg` configuration file manually. [po4a]: https://po4a.org/index.php.en [weblate]: https://hosted.weblate.org/projects/organicmaps/website/ diff --git a/config.toml b/config.toml index 1e77ebfb..dc34f31c 100644 --- a/config.toml +++ b/config.toml @@ -44,29 +44,22 @@ contact = "Kontakt" install-appgallery = "Organic Maps aus der Huawei AppGallery installieren" install-appstore = "Installiere Organic Maps aus dem AppStore" install-googleplay = "Organic Maps von Google Play installieren" -install-fdroid="Organic Maps von F-Droid installieren" +install-fdroid = "Organic Maps von F-Droid installieren" language = "Deutsch" name = "Name" token = "Token" - [languages.fr] [languages.fr.translations] address = "Adresse" back = "Retour aux actualités" contact = "Contact" -donate = "Donner" -home = "Page d'accueil" install-appgallery = "Installez Organic Maps depuis l'AppGallery" install-appstore = "Installez Organic Maps depuis l'AppStore" install-googleplay = "Installez Organic Maps depuis Google Play" -install-fdroid="Installez Organic Maps depuis F-Droid" +install-fdroid = "Installez Organic Maps depuis F-Droid" language = "Français" name = "Nom" -news = "Actualités" -support-us = "Contribuer" token = "Jeton" -terms = "Termes" - [languages.it] [languages.it.translations] address = "Indirizzo" @@ -74,12 +67,11 @@ back = "Indietro" contact = "Contattaci" install-appgallery = "Installa Organic Maps da Huawei AppGallery" install-appstore = "Installa Organic Maps dall'AppStore" -install-fdroid="Installa Organic Maps da F-Droid" install-googleplay = "Installa Organic Maps da Google Play" +install-fdroid = "Installa Organic Maps da F-Droid" language = "Italiano" name = "Nome" token = "Token" - [languages.pl] [languages.pl.translations] address = "Adres" @@ -87,12 +79,11 @@ back = "Z powrotem" contact = "Kontakt" install-appgallery = "Zainstaluj Organic Maps z Huawei AppGallery" install-appstore = "Zainstaluj Organic Maps z App Store" -install-fdroid="Zainstaluj Organic Maps z F-Droid" install-googleplay = "Zainstaluj Organic Maps z Google Play" -name = "Nazwa" +install-fdroid = "Zainstaluj Organic Maps z F-Droid" language = "Polski" +name = "Nazwa" token = "Token" - [languages.ru] [languages.ru.translations] address = "Адрес" @@ -100,12 +91,11 @@ back = "Назад" contact = "Связаться с нами" install-appgallery = "Установить Organic Maps из Huawei AppGallery" install-appstore = "Установить Organic Maps из Apple AppStore" -install-fdroid="Установить Organic Maps из F-Droid" install-googleplay = "Установить Organic Maps из Google Play" +install-fdroid = "Установить Organic Maps из F-Droid" language = "Русский" name = "Название" token = "Токен" - [languages.tr] [languages.tr.translations] address = "Adres" @@ -113,12 +103,11 @@ back = "Haberlere geri dön" contact = "Bize ulaşın" install-appgallery = "Organic Maps'i Huawei AppGallery'den İndir" install-appstore = "Organic Maps'i AppStore'dan İndir" -install-fdroid="Organic Maps'i F-Droid'den İndir" install-googleplay = "Organic Maps'i Google Play'den İndir" +install-fdroid = "Organic Maps'i F-Droid'den İndir" language = "Türkçe" name = "İsim" token = "Token" - [languages.id] [languages.id.translations] address = "Alamat" @@ -127,24 +116,19 @@ contact = "Hubungi Kami" install-appgallery = "Instal Organic Maps dari Huawei AppGallery" install-appstore = "Instal Organic Maps dari AppStore" install-googleplay = "Instal Organic Maps dari Google Play" -install-fdroid="Instal Peta Organik dari F-Droid" +install-fdroid = "Instal Peta Organik dari F-Droid" language = "Bahasa Indonesia" name = "Nama" token = "Token" - [languages.cs] [languages.cs.translations] address = "Adresa" back = "Zpátky na Novinky" contact = "Kontakt" -donate = "Darujte" -home = "Domů" install-appgallery = "Nainstalujte Organic Maps z Huawei AppGallery" install-appstore = "Nainstalujte Organic Maps z App Store" -install-fdroid= "Nainstalujte Organic Maps z F-Droid" install-googleplay = "Nainstalujte Organic Maps z Google Play" -name = "Název" -news = "Novinky" +install-fdroid = "Nainstalujte Organic Maps z F-Droid" language = "čeština" -support-us = "Podpořte nás" +name = "Název" token = "Token" diff --git a/po/content.id.po b/po/content.id.po index 5143d6fb..e7cf15de 100644 --- a/po/content.id.po +++ b/po/content.id.po @@ -794,55 +794,62 @@ msgstr "[license]: http://www.apache.org/licenses/LICENSE-2.0\n" msgid "[copyright]: https://github.com/organicmaps/organicmaps/blob/master/data/copyright.html\n" msgstr "[copyright]: https://github.com/organicmaps/organicmaps/blob/master/data/copyright.html\n" -#. type: Zola TOML Translations: address -#: config.toml +#. type: Hash Value: address +#: translations.yaml #, no-wrap msgid "Address" msgstr "Alamat" -#. type: Zola TOML Translations: back -#: config.toml +#. type: Hash Value: back +#: translations.yaml #, no-wrap msgid "Back to News" msgstr "Kembali ke Berita" -#. type: Zola TOML Translations: install-appgallery -#: config.toml +#. type: Hash Value: contact +#: translations.yaml +#, no-wrap +msgid "Contact Us" +msgstr "" + +#. type: Hash Value: install-appgallery +#: translations.yaml #, no-wrap msgid "Install Organic Maps from Huawei AppGallery" msgstr "Instal Organic Maps dari Huawei AppGallery" -#. type: Zola TOML Translations: install-appstore -#: config.toml +#. type: Hash Value: install-appstore +#: translations.yaml #, no-wrap msgid "Install Organic Maps from the AppStore" msgstr "Instal Organic Maps dari AppStore" -#. type: Zola TOML Translations: install-googleplay -#: config.toml -#, no-wrap -msgid "Install Organic Maps from Google Play" -msgstr "Instal Organic Maps dari Google Play" - -#. type: Zola TOML Translations: install-fdroid -#: config.toml +#. type: Hash Value: install-fdroid +#: translations.yaml #, no-wrap msgid "Install Organic Maps from F-Droid" msgstr "Instal Peta Organik dari F-Droid" -#. type: Zola TOML Translations: language +#. type: Hash Value: install-googleplay +#: translations.yaml +#, no-wrap +msgid "Install Organic Maps from Google Play" +msgstr "Instal Organic Maps dari Google Play" + +#. type: Hash Value: language +#: translations.yaml #, no-wrap msgid "English" msgstr "Bahasa Indonesia" -#. type: Zola TOML Translations: name -#: config.toml +#. type: Hash Value: name +#: translations.yaml #, no-wrap msgid "Name" msgstr "Nama" -#. type: Zola TOML Translations: token -#: config.toml +#. type: Hash Value: token +#: translations.yaml #, no-wrap msgid "Token" msgstr "Token" diff --git a/po/content.pot b/po/content.pot index 7d3b638f..4889d02d 100644 --- a/po/content.pot +++ b/po/content.pot @@ -898,55 +898,62 @@ msgstr "" msgid "[copyright]: https://github.com/organicmaps/organicmaps/blob/master/data/copyright.html\n" msgstr "" -#. type: Zola TOML Translations: address -#: config.toml +#. type: Hash Value: address +#: translations.yaml #, no-wrap msgid "Address" msgstr "" -#. type: Zola TOML Translations: back -#: config.toml +#. type: Hash Value: back +#: translations.yaml #, no-wrap msgid "Back to News" msgstr "" -#. type: Zola TOML Translations: install-appgallery -#: config.toml +#. type: Hash Value: contact +#: translations.yaml +#, no-wrap +msgid "Contact Us" +msgstr "" + +#. type: Hash Value: install-appgallery +#: translations.yaml #, no-wrap msgid "Install Organic Maps from Huawei AppGallery" msgstr "" -#. type: Zola TOML Translations: install-appstore -#: config.toml +#. type: Hash Value: install-appstore +#: translations.yaml #, no-wrap msgid "Install Organic Maps from the AppStore" msgstr "" -#. type: Zola TOML Translations: install-googleplay -#: config.toml -#, no-wrap -msgid "Install Organic Maps from Google Play" -msgstr "" - -#. type: Zola TOML Translations: install-fdroid -#: config.toml +#. type: Hash Value: install-fdroid +#: translations.yaml #, no-wrap msgid "Install Organic Maps from F-Droid" msgstr "" -#. type: Zola TOML Translations: language +#. type: Hash Value: install-googleplay +#: translations.yaml +#, no-wrap +msgid "Install Organic Maps from Google Play" +msgstr "" + +#. type: Hash Value: language +#: translations.yaml #, no-wrap msgid "English" msgstr "" -#. type: Zola TOML Translations: name -#: config.toml +#. type: Hash Value: name +#: translations.yaml #, no-wrap msgid "Name" msgstr "" -#. type: Zola TOML Translations: token -#: config.toml +#. type: Hash Value: token +#: translations.yaml #, no-wrap msgid "Token" msgstr "" diff --git a/tools/.gitignore b/tools/.gitignore new file mode 100644 index 00000000..f7275bbb --- /dev/null +++ b/tools/.gitignore @@ -0,0 +1 @@ +venv/ diff --git a/tools/extract-zola-translations.py b/tools/extract-zola-translations.py new file mode 100755 index 00000000..8705ace6 --- /dev/null +++ b/tools/extract-zola-translations.py @@ -0,0 +1,25 @@ +#!/usr/bin/env python3 + +import os +import tomlkit +import yaml +import shutil + +os.chdir(os.path.dirname(os.path.abspath(__file__)) + "/..") + +ZOLA_CONFIG_TOML = "config.toml" +TRANSLATIONS_YAML = "translations.yaml" + +# Read config.yaml +with open(ZOLA_CONFIG_TOML, "r") as f: + config = tomlkit.loads(f.read()) + +# Extract [translations] +translations = {} +for (k, v) in config['translations'].items(): + translations[str(k)] = str(v) + +# Write translations.yaml +with open(TRANSLATIONS_YAML, "w") as f: + yaml.dump(translations, f, allow_unicode=True, sort_keys=False) +shutil.copystat(ZOLA_CONFIG_TOML, TRANSLATIONS_YAML) diff --git a/i18n.sh b/tools/i18n.sh similarity index 66% rename from i18n.sh rename to tools/i18n.sh index c3a40290..f94773e4 100755 --- a/i18n.sh +++ b/tools/i18n.sh @@ -2,6 +2,8 @@ set -euo pipefail +TOOLS="$(cd "$(dirname "$0")"; pwd)" + if ! which po4a; then echo "ERROR: Missing po4a">&2 echo " brew install po4a">&2 @@ -10,7 +12,16 @@ if ! which po4a; then exit 1 fi +cd "$TOOLS/.." + +set -x + +$TOOLS/extract-zola-translations.py + +touch content/_index.md # force re-generation po4a .po4a.cfg sed -e '/"PO-Revision-Date/d' -e '/"POT-Creation-Date/d' -i'~' po/content.*.po po/content.pot rm -f po/content.*.po*~ po/content.pot~ + +$TOOLS/update-zola-translations.py diff --git a/tools/requirements-dev.txt b/tools/requirements-dev.txt new file mode 100644 index 00000000..dd46cad5 --- /dev/null +++ b/tools/requirements-dev.txt @@ -0,0 +1 @@ +autopep8==2.0.1 \ No newline at end of file diff --git a/tools/requirements.txt b/tools/requirements.txt new file mode 100644 index 00000000..34b0900e --- /dev/null +++ b/tools/requirements.txt @@ -0,0 +1,3 @@ +tomlkit==0.11.6 +polib==1.1.1 +PyYAML==6.0 \ No newline at end of file diff --git a/tools/update-zola-translations.py b/tools/update-zola-translations.py new file mode 100755 index 00000000..96b0ab36 --- /dev/null +++ b/tools/update-zola-translations.py @@ -0,0 +1,58 @@ +#!/usr/bin/env python3 + +import os +import sys +from glob import glob +import tomlkit +import polib + +assert sys.version_info >= (3, 7, 0), "Python 3.7+ is required" + +os.chdir(os.path.dirname(os.path.abspath(__file__)) + "/..") + +ZOLA_CONFIG_TOML = "config.toml" +TRANSLATIONS_YAML = "translations.yaml" +ZOLA_COMMENT_PREFIX = 'type: Hash Value: ' + +# Read config.yaml +with open(ZOLA_CONFIG_TOML, "r") as f: + config = tomlkit.loads(f.read()) + +# Merge changes from .po files into config.yaml +languages = config['languages'] +sources = config['translations'] +for po_path in glob('po/content.*.po'): + lang = po_path.removeprefix('po/content.').removesuffix('.po') + + # Add a new section to TOML if needed + if lang not in languages: + languages[lang] = tomlkit.table() + languages[lang]['translations'] = tomlkit.table() + + translations = languages[lang]['translations'] + + # Update translations + po_file = polib.pofile(po_path) + for entry in po_file: + if not entry.comment.startswith(ZOLA_COMMENT_PREFIX): + continue + for (file, no) in entry.occurrences: + if file == TRANSLATIONS_YAML: + break + else: + continue + key = entry.comment.removeprefix(ZOLA_COMMENT_PREFIX).strip() + if entry.msgstr and entry.msgstr != translations.get(key, ''): + translations[key] = entry.msgstr + +# Add default values and remove old keys +for lang, sections in languages.items(): + old_translations = sections['translations'] + new_translations = {} + for key, default in sources.items(): + new_translations[key] = old_translations.get(key, default) + sections['translations'] = new_translations + +# Write config.yaml +with open(ZOLA_CONFIG_TOML, "w") as f: + f.write(tomlkit.dumps(config, sort_keys=True))