#!/usr/bin/env bash
-LANG=$1
+LANGS=$@
+if [ "$#" -eq 0 ]; then
+ echo $0 "adds i18n catalogue(s) in po/ folders (luci-app-*, luci-mod-*, etc) for each LUCI_LANG.* in luci.mk"
+ echo "Hint: run in the root of the luci repo or in your luci-app-* folder."
-case "$LANG" in
- [a-z][a-z]|[a-z][a-z][_-][A-Za-z][A-Za-z]*) : ;;
- *)
- echo "Usage: $0 <ISO_CODE>\n" >&2
- exit 1
- ;;
-esac
+ # get existing language codes from luci.mk
+ language_codes=$(grep -o 'LUCI_LANG\.[a-zA-Z]*' $(dirname "$0")/../luci.mk | cut -d '.' -f 2 | sort -u)
+ LANGS=$language_codes
-ADDED=0
+else
+ for LANG in $LANGS; do
+ case "$LANG" in
+ [a-z][a-z]|[a-z][a-z][_-][A-Za-z][A-Za-z]*) : ;;
+ *)
+ echo $0 "adds i18n catalogues in each folder (luci-app-*, luci-mod-*, etc)."
+ echo "Usage: $0 <ISO_CODE> [<ISO_CODE> <ISO_CODE> ...]" >&2
+ exit 1
+ ;;
+ esac
+ done
+fi
-for podir in ./*/*/po; do
- [ -d "$podir/templates" ] || continue
+ADDED=false
- mkdir "$podir/$LANG"
- for catalog in $(cd "$podir/templates"; echo *.pot); do
- if [ -f "$podir/templates/$catalog" -a ! -f "$podir/$LANG/${catalog%.pot}.po" ]; then
- msginit --no-translator -l "$LANG" -i "$podir/templates/$catalog" -o "$podir/$LANG/${catalog%.pot}.po"
- git add "$podir/$LANG/${catalog%.pot}.po"
- ADDED=$((ADDED + 1))
- fi
+for podir in $(find . -type d -name "po"); do
+ [ -d "$podir/templates" ] || continue
+ for LANG in $LANGS; do
+ # if "$podir/$LANG" doesn't exist, mkdir
+ [ -d "$podir/$LANG" ] || mkdir "$podir/$LANG"
+ for catalog in $(cd "$podir/templates"; echo *.pot); do
+ if [ -f "$podir/templates/$catalog" -a ! -f "$podir/$LANG/${catalog%.pot}.po" ]; then
+ msginit --no-translator -l "$LANG" -i "$podir/templates/$catalog" -o "$podir/$LANG/${catalog%.pot}.po"
+ git add "$podir/$LANG/${catalog%.pot}.po"
+ ADDED=true
+ fi
+ done
done
done
-if [ $ADDED -gt 0 ]; then
- echo ""
- echo "Added $ADDED new translation catalogs for language '$LANG'."
- echo "Please also edit 'luci.mk' and add"
- echo ""
- echo " LUCI_LANG.$LANG=Native Language Name"
- echo ""
- echo "to properly package the translation files."
- echo ""
+start_marker="^#LUCI_LANG_START$"
+end_marker="^#LUCI_LANG_END$"
+
+if [ $ADDED ]; then
+ for LANG in $LANGS; do
+ if [[ $language_codes != *"$LANG"* ]]; then
+
+ # Read the contents of the luci.mk file
+ file_content=$(cat "$(dirname "$0")/../luci.mk")
+
+ # Extract the section between start and end markers
+ section=$(awk -v start="$start_marker" -v end="$end_marker" '
+ $0 ~ start {RS="\n"; printf ""; flag=1; next}
+ $0 ~ end {flag=0} flag' <<< "$file_content")
+
+ # Add the new language code to the section
+ section+="\nLUCI_LANG.$LANG=New language"
+ # Sort the section and remove duplicates
+ updated_content=$(echo -e "$section" | sort -u | sed -E "/$start_marker/,/$end_marker/{ /$start_marker/{p; r /dev/stdin
+ }; /$end_marker/p; d
+ }" $(dirname "$0")/../luci.mk)
+
+ # Write the updated content back to the .mk file
+ echo "$updated_content" > $(dirname "$0")/../luci.mk
+
+ echo "Be sure to update the new language name in $(dirname "$0")/../luci.mk"
+
+ fi
+ done
fi
PATTERN=$1
SCM=
+echo $0 "initialises po/ i18n catalogues in empty language sub-folders."
+echo $0 "is deprecated and may be removed in the future."
+echo "Hint: run i18n-add-language.sh instead."
+
[ -d .svn ] && SCM="svn"
git=$( command -v git 2>/dev/null )
[ "$git" ] && "$git" status >/dev/null && SCM="git"
while (@pot > 0) {
my $line = shift @pot;
- # Reorder the location comments in a detemrinistic way to
+ # Reorder the location comments in a deterministic way to
# reduce SCM noise when frequently updating templates.
if ($line =~ m!^#: !) {
my @locs = ($line);
if( ! $source )
{
- @dirs = glob("./*/*/po/");
+ @dirs = glob("./*/*/po");
}
else
{
echo -n "Updating modules/luci-base/po/templates/base.pot ... "
./build/i18n-scan.pl \
- modules/luci-base/ modules/luci-compat/ modules/luci-mod-admin-full/ \
- modules/luci-mod-network modules/luci-mod-status modules/luci-mod-system/ \
- protocols/ themes/ \
+ modules/luci-base modules/luci-compat modules/luci-mod-admin-full \
+ modules/luci-mod-network modules/luci-mod-status modules/luci-mod-system \
+ protocols themes \
> modules/luci-base/po/templates/base.pot
echo "done"
-# General
-Translations are saved in the folder po/ for each module and application. You find the reference in po/templates/<package>.pot. The actual translation files can be found at po/[lang]/[package].po .
+# Internationalization (i18n)
-In order to use the commands below you need to have the _gettext'' utilities (''msgcat'', ''msgfmt'', ''msgmerge_) installed on your system.
+See [online wiki](https://github.com/openwrt/luci/wiki/i18n) for latest version.
-# Rebuild po files
+## Use translation function
+
+### Translations in JavaScript
+
+Wrap translatable strings with `_()` e.g. `_('string to translate')` and the `i18n-scan.pl` and friends will correctly identify these strings as they do with all the existing translations.
+
+If you have multi line strings you can split them with concatenation:
+```js
+var mystr = _('this string will translate ' +
+ 'correctly even though it is ' +
+ 'a multi line string!');
+```
+
+You may also use line continuations `\` syntax:
+
+```js
+var mystr = _('this string will translate \
+ correctly even though it is \
+ a multi line string');
+```
+
+Usually if you have multiple sentences you may need to use a line break then use the `<br />` HTML tag:
+```js
+var mystr = _('Port number.<br />' +
+ 'E.g. 80 for HTTP');
+```
+
+To simplify a job for translators it may be better to split into separate keys without the `<br />`:
+```js
+var mystr = _('Port number.') + '<br />' +
+ _('E.g. 80 for HTTP');
+```
+Please use `<br />` and **not** `<br>` or `<br/>`.
+
+If you have a link inside a translation then try to move its attributes out of a translation key like:
+```js
+var mystr = _('For further information <a %s>check the wiki</a>')
+ .format('href="https://openwrt.org/docs/" target="_blank" rel="noreferrer"')
+```
+This will generate a full link with HTML `For further information <a href="https://openwrt.org/docs/" target="_blank" rel="noreferrer">check the wiki</a>`. The `noreferrer` is important when making a link that is opened in a new tab (`target="_blank"`).
+
+### Translations in LuCI lua+html templates
+Use the `<%: text to translate %>` as documented on [Templates](./Templates.md)
+
+### Translations in Lua controller code and Lua CBIs
+As hinted at in the Templates doc, the `%:` is actually invoking a `translate()` function.
+In most controller contexts, this is already available for you, but if necessary, is available for include in `luci.i18n.translate`
+
+
+## Translation files
+Translations are saved in the folder `po/` within each individual LuCI component directory, e.g. `applications/luci-app-acl/po/`.
+You find the reference in `po/templates/<package>.pot`.
+The actual translation files can be found at `po/[lang]/[package].po`.
+
+In order to use the commands below you need to have the `gettext` utilities (`msgcat`, `msgfmt`, `msgmerge`) installed on your system.
+On Debian/Ubuntu you can install with `sudo apt install gettext`.
+
+### Initialize po files
+
+When you add or update an app, simply run from your app folder:
+
+ ../../build/i18n-add-language.sh
+
+This creates the skeleton po files for all existing languages open for translation for your app.
+
+Or from the luci repo root:
+
+ ./build/i18n-add-language.sh
+
+This creates the skeleton po files for all existing languages open for translation for all sub-folders.
+
+### Rebuild po files
If you want to rebuild the translations after you made changes to a package this is an easy way:
-
- ./build/i18n-scan.pl applications/[application] > applications/[application]/po/templates/[application_basename].pot
- ./build/i18n-update.pl applications/[application]/po
-
- Example:
- ./build/i18n-scan.pl applications/luci-app-firewall > applications/luci-app-firewall/po/templates/firewall.pot
- ./build/i18n-update.pl applications/luci-app-firewall/po
- (note that the directory argument can be omitted for i18n-update.pl to update all apps)
-
-*Note:* Some packages share translation files, in this case you need to scan through all their folders. The first command from above should then be:
-
- ./build/i18n-scan.pl applications/[package-1] applications/[package-2] applications/[package-n] > [location of shared template]/[application].pot
+
+ ./build/i18n-scan.pl applications/[application] > applications/[application]/po/templates/[application_basename].pot
+ ./build/i18n-update.pl applications/[application]/po
+
+Example:
+
+ ./build/i18n-scan.pl applications/luci-app-acl > applications/luci-app-acl/po/templates/acl.pot
+ ./build/i18n-update.pl applications/luci-app-acl/po
+
+Note that the directory argument can be omitted for `i18n-update.pl` to update all apps.
+
+Some packages share translation files, in this case you need to scan through all their folders.
+The first command from above should then be:
+
+ ./build/i18n-scan.pl applications/[package-1] applications/[package-2] applications/[package-n] > [location of shared template]/[application].pot
+
+*Note:* The translation catalog for the base system covers multiple components, use the following commands to update it:
+
+ ./build/mkbasepot.sh
+ ./build/i18n-update.pl
+
+### LMO files
+The `*.po` files are big so Luci needs them in a compact compiled [LMO format](./LMO.md).
+Luci reads `*.lmo` translations from `/usr/lib/lua/luci/i18n/` folder.
+E.g. `luci-app-acl` has an Arabic translation in `luci-i18n-acl-ar` package that installs `/usr/lib/lua/luci/i18n/acl.ar.lmo` file.
+
+In order to quickly convert a single `.po` file to `.lmo` file for testing on the target system use the `po2lmo` utility.
+You will need to compile it from the `luci-base` module:
+
+ $ cd modules/luci-base/src/
+ $ make po2lmo
+ $ ./po2lmo
+ Usage: ./po2lmo input.po output.lmo
+
+Now you can compile and upload translation:
+
+ ./po2lmo ../../../applications/luci-app-acl/po/ar/acl.po ./acl.ar.lmo
+ scp ./acl.ar.lmo root@192.168.1.1:/usr/lib/lua/luci/i18n/
+
+You can change language in [System /Language and Style](http://192.168.1.1/cgi-bin/luci/admin/system/system) and check the translation.
\ No newline at end of file
LUCI_MINIFY_CSS?=1
LUCI_MINIFY_JS?=1
-# Language code titles
+#LUCI_LANG_START
LUCI_LANG.ar=العربية (Arabic)
LUCI_LANG.bg=български (Bulgarian)
LUCI_LANG.bn_BD=বাংলা (Bengali)
LUCI_LANG.nb_NO=Norsk (Norwegian)
LUCI_LANG.nl=Nederlands (Dutch)
LUCI_LANG.pl=Polski (Polish)
-LUCI_LANG.pt_BR=Português do Brasil (Brazilian Portuguese)
LUCI_LANG.pt=Português (Portuguese)
+LUCI_LANG.pt_BR=Português do Brasil (Brazilian Portuguese)
LUCI_LANG.ro=Română (Romanian)
LUCI_LANG.ru=Русский (Russian)
LUCI_LANG.sk=Slovenčina (Slovak)
LUCI_LANG.vi=Tiếng Việt (Vietnamese)
LUCI_LANG.zh_Hans=简体中文 (Chinese Simplified)
LUCI_LANG.zh_Hant=繁體中文 (Chinese Traditional)
+#LUCI_LANG_END
# Submenu titles
LUCI_MENU.col=1. Collections
# LUCI_SUBMENU: the submenu-item below the LuCI top-level menu inside OpenWrt menuconfig
# usually one of the LUCI_MENU.* definitions
-# LUCI_SUBMENU_DEFAULT: the regular SUBMENU defined by LUCI_TYPE or derrived from the packagename
-# LUCI_SUBMENU_FORCED: manually forced value SUBMENU to set to by explicit definiton
+# LUCI_SUBMENU_DEFAULT: the regular SUBMENU defined by LUCI_TYPE or derived from the packagename
+# LUCI_SUBMENU_FORCED: manually forced value SUBMENU to set to by explicit definition
# can be any string, "none" disables the creation of a submenu
-# most usefull in combination with LUCI_CATEGORY, to make the package appear
+# most useful in combination with LUCI_CATEGORY, to make the package appear
# anywhere in the menu structure
LUCI_SUBMENU_DEFAULT=$(if $(LUCI_MENU.$(LUCI_TYPE)),$(LUCI_MENU.$(LUCI_TYPE)),$(LUCI_MENU.app))
LUCI_SUBMENU=$(if $(LUCI_SUBMENU_FORCED),$(LUCI_SUBMENU_FORCED),$(LUCI_SUBMENU_DEFAULT))