FreshPorts now speaks seven languages: English, plus German, French (Canada), Portuguese (Brazil), Russian, Turkish, and Chinese (Simplified). Each translation was done by a volunteer, and I am grateful to all of them: Stefan Esser, Marc Pearson, Gustavo Nobrega, Vladlen Popolitov, Yusuf Yaman, and ykla.
The website keeps changing, and each change can bring new text. The new feeds at /--/feeds/, the search check, and the system status page all added strings that no translation has yet. This post lists what is wanted, language by language, and explains how to help. You do not need to be a programmer. If you can read English and write your own language, you can do this.
Where each language stands
There are 1,193 strings on the website. This is how each translation compares against the current source:
| Locale | Language | Translated | Needs checking (fuzzy) | Missing |
|---|---|---|---|---|
| de_DE | German | 1,165 | 7 | 21 |
| fr_CA | French (Canada) | 1,175 | 6 | 12 |
| pt_BR | Portuguese (Brazil) | 1,174 | 6 | 13 |
| ru_RU | Russian | 1,182 | 4 | 7 |
| tr_TR | Turkish | 893 | 6 | 294 |
| zh_CN | Chinese (Simplified) | 1,175 | 6 | 12 |
These counts come from a checkout of the main branch:
git clone https://github.com/FreshPorts/freshports.git cd freshports scripts/i18n-extract.sh for po in locale/*/LC_MESSAGES/freshports.po do printf '%s: ' $po msgfmt --statistics -o /dev/null $po done
scripts/i18n-extract.sh collects every translatable string from the PHP source into locale/freshports.pot, then merges that into each language's freshports.po. msgfmt --statistics counts the result. The total of 1,193 is the sum of the three columns for any one language.
A fuzzy string is one where the tools found an existing translation of a similar English string and copied it as a guess. Some guesses are close. Others are plain wrong. For example, "See the suggestions at the top of this page." was given the translation of "John Beige did the logo you see at the top of the page." The website never shows a fuzzy translation; it shows the English until someone checks it.
What is wanted
The table shows each string, where it appears in the source, and its state in each language. missing means not yet translated; check means fuzzy (see above). A blank cell means that language already has it.
| English | Source file | de_DE | fr_CA | pt_BR | ru_RU | tr_TR | zh_CN |
|---|---|---|---|---|---|---|---|
| vuxml entries selected for processing: %d | include/system-status-page.php | missing | missing | missing | missing | missing | missing |
| (this drops to 0 only when the run commits) | include/system-status-page.php | missing | missing | missing | missing | missing | missing |
| Atom 1.0 instead of RSS 2.0. | www/--/feeds/index.php www/backend/newsfeeds.php |
missing | missing | missing | missing | missing | missing |
| The deprecated feeds are listed at <a href="%s">%s</a>. | www/--/feeds/index.php | missing | missing | missing | missing | missing | missing |
| These older feeds still work, but they will be redirected to the new feeds at <a href="%s">%s</a>. Please update your subscriptions. | www/backend/index.php | missing | missing | missing | missing | missing | missing |
| The same feed as Atom 1.0 : %s | www/backend/newsfeeds.php | missing | missing | missing | missing | missing | missing |
| These older feeds still work, but they will be redirected to the feeds above. Please update your subscriptions. | www/backend/newsfeeds.php | missing | missing | missing | missing | missing | missing |
| Feed formats | www/--/feeds/index.php | check | check | check | check | check | check |
| The above feeds are created using <a href="https://github.com/laminas/laminas-feed">laminas-feed</a>. | www/--/feeds/index.php | check | check | check | check | check | check |
| An RSS 2.0 feed : %s | www/backend/newsfeeds.php | check | check | check | check | check | check |
| Deprecated newsfeeds | www/backend/newsfeeds.php | check | check | check | check | check | check |
| Perhaps you meant one of these: | include/freshports.php | missing | missing | missing | missing | missing | |
| You are connected over IPv6 | include/freshports.php | missing | missing | missing | missing | missing | |
| To keep the search working for people, please confirm you are not a bot. | include/search-gate.php | missing | missing | missing | missing | missing | |
| This sets a cookie, so you will not be asked again for a day. Logged in users are never asked. | include/search-gate.php | missing | missing | missing | missing | missing | |
| Some ABIs on that page are marked <i>(only pkgbase is updated)</i>. Only the base system is built for them, not the ports tree, so there are no packages for FreshPorts to show. Those ABIs are skipped. | www/faq.php | missing | missing | missing | missing | missing | |
| Continue to search | include/search-gate.php | check | check | check | check | check | |
| See the suggestions at the top of this page. | www/search.php | check | check | check | missing | check | |
| That link no longer works, try this copy on the Internet Archive instead: | www/inthenews.php | missing | missing | ||||
| Smallest | include/fontsize.php | missing | |||||
| Small | include/fontsize.php | missing | |||||
| Normal | include/fontsize.php | missing | |||||
| Large | include/fontsize.php | missing | |||||
| Larger | include/fontsize.php | missing | |||||
| Largest | include/fontsize.php | missing | |||||
| Text size | include/fontsize.php | missing | |||||
| %1$d port found (%2$d primary, %3$d secondary) | www/port-watch.php | missing | |||||
| Your token is: %s | include/freshports.php | check |
The table comes from the same checkout, after running scripts/i18n-extract.sh. For each language:
msgattrib --untranslated --no-obsolete locale/de_DE/LC_MESSAGES/freshports.po msgattrib --only-fuzzy --no-obsolete locale/de_DE/LC_MESSAGES/freshports.po
Turkish needs more than the table shows. It is missing 294 strings in all, most of them on long help pages that have not been started yet. By source file:
| Source file | Missing |
|---|---|
| # | 296 |
| www/faq.php | 128 |
| www/help.php | 26 |
| www/pkg_upload.php | 22 |
| www/authors.php | 18 |
| www/watch-list-maintenance.php | 13 |
| www/watch.php | 9 |
| www/search.php | 9 |
| www/how-big-is-it.php | 7 |
| www/graphs.php | 6 |
| www/vuxml.php | 5 |
| www/report-package-notifications.php | 4 |
| www/port-watch.php | 4 |
| www/graphs2.php | 4 |
| www/report-subscriptions.php | 3 |
| www/inthenews.php | 3 |
| www/category-maintenance.php | 3 |
| www/backend/newsfeeds.php | 3 |
| www/about.php | 3 |
| include/freshports_page_list_ports.php | 3 |
| www/security-policy.php | 2 |
| www/ports-new.php | 2 |
| www/now-in-maintenance-mode.php | 2 |
| www/filter-setup.php | 2 |
| www/backend/status.php | 2 |
| www/--/feeds/index.php | 2 |
| include/system-status-page.php | 2 |
| include/search-gate.php | 2 |
| include/freshports_page_expiration_ports.php | 2 |
| include/freshports.php | 2 |
msgattrib --untranslated --no-obsolete locale/tr_TR/LC_MESSAGES/freshports.po | \ grep '^#:' | tr ' ' '\n' | grep : | sed 's/:[0-9]*$//' | sort | uniq -c | sort -rn
A string used in more than one file counts once for each file, so these numbers add up to more than 294. The rows above are the files with two or more missing strings; www/faq.php alone is 128. Any part of it helps. One page at a time is fine.
How to help
Each language lives in one file: locale/<locale>/LC_MESSAGES/freshports.po in the FreshPorts/freshports repository. German, for example, is locale/de_DE/LC_MESSAGES/freshports.po.
- Fork the repository on GitHub and clone your fork.
- Open your language's freshports.po. Poedit is free and works well: it lists the untranslated and fuzzy ("Needs work") strings first. A plain text editor works too.
- For a missing string, fill in the empty msgstr "" under its msgid.
- For a fuzzy string, fix the translation, then remove the word fuzzy from the #, fuzzy line above it. If that line also says php-format, keep that part: #, fuzzy, php-format becomes #, php-format. Poedit does this for you when you mark the translation as done.
- Check the file:
msgfmt -c --statistics -o /dev/null locale/de_DE/LC_MESSAGES/freshports.po
It should report no errors, and the translated count should go up.
- Open a pull request that changes only your .po file.
A few things to keep in mind:
- Text such as %s, %d or %1$s is replaced with a link, a number or another value. Keep each one in your translation. The numbered ones (%1$s, %2$s) may be moved around to suit your grammar.
- Some strings contain HTML, such as <a href="%s">%s</a>. Translate the words and leave the tags as they are.
- Strings with a plural form have msgstr[0], msgstr[1] and so on, one for each plural form your language has.
- Port names, descriptions and commit messages come from the FreeBSD ports tree. They are not translated.
- Please do not edit locale/freshports.pot; it is generated from the source.
- Do not worry about the #: line numbers. They change every time the template is regenerated.
If you would rather not use git, post your translations as a comment on issue #678 and I will add them.
A new language
Seven is not a limit. To start a new translation, see locale/README.md. In short:
mkdir -p locale/xx_YY/LC_MESSAGES msginit --input=locale/freshports.pot --locale=xx_YY.UTF-8 \ --output-file=locale/xx_YY/LC_MESSAGES/freshports.po
Check the Plural-Forms line in the header of the new file, and add the language's own name to freshports_i18n_native_name() in include/i18n.php so it shows up in the language menu. You do not need to translate everything before opening a pull request. A partial translation is useful: anything not yet translated is shown in English.
Seeing your work
Once a pull request is merged, the translation goes to dev.freshports.org first and then, with the next release, to the other sites. FreshPorts picks the language from your browser's language settings. The language menu on the page overrides that, and remembers your choice with a cookie.
Questions about any of this are welcome on issue #678. Thank you.











