From fb8067b5ab58e123d05bdb32a1e2b5967045233b Mon Sep 17 00:00:00 2001 From: Marco Date: Sat, 15 Jun 2019 03:11:25 +0200 Subject: [PATCH] Integrate internationalization component --- config/.env.example | 6 ++++ i18n.sh | 69 +++++++++++++++++++++++++++++++++++++++++++++ index.php | 22 +++++++++++++++ locale/.gitignore | 4 +++ 4 files changed, 101 insertions(+) create mode 100644 i18n.sh create mode 100644 locale/.gitignore diff --git a/config/.env.example b/config/.env.example index ee4a302..067d5ac 100644 --- a/config/.env.example +++ b/config/.env.example @@ -29,6 +29,12 @@ MAIL_USERNAME=user@example.com MAIL_PASSWORD=monkey MAIL_TLS=0 +# The (comma-separated) list of supported locales (see 'https://github.com/delight-im/PHP-I18N/blob/master/src/Codes.php' for possible values) +I18N_SUPPORTED_LOCALES= +I18N_SESSION_FIELD= +I18N_COOKIE_NAME= +I18N_COOKIE_LIFETIME=31556952 + # The settings of the encoder/decoder that can be used to obfuscate IDs SECURITY_IDS_ALPHABET=GZwBHpfWybgQ5d_2mM-jh84K69tqYknx7LN3zvDrcSJVRPXsCFT SECURITY_IDS_PRIME=1125812041 diff --git a/i18n.sh b/i18n.sh new file mode 100644 index 0000000..5cd94fc --- /dev/null +++ b/i18n.sh @@ -0,0 +1,69 @@ +#!/bin/bash + +### PHP-I18N (https://github.com/delight-im/PHP-I18N) +### Copyright (c) delight.im (https://www.delight.im/) +### Licensed under the MIT License (https://opensource.org/licenses/MIT) + +set -eu + +# Switch to the directory where the current script is located +cd "${BASH_SOURCE%/*}" || exit 1 + +echo "Extracting and updating translations" + +LOCALE_CODE="${1:-}" +LOCALE_PARENT_DIR="${2:-locale}" +LOCALE_DOMAIN="${3:-messages}" + +if [ ! -d "${LOCALE_PARENT_DIR}" ]; then + echo " * Error: Target directory “${LOCALE_PARENT_DIR}” not found" + exit 2 +fi + +if [ ! -w "${LOCALE_PARENT_DIR}" ]; then + echo " * Error: Target directory “${LOCALE_PARENT_DIR}” not writable" + exit 3 +fi + +if [ -z "${LOCALE_CODE}" ]; then + echo " * Creating generic POT (Portable Object Template) file" +fi + +find . -iname "*.php" -not -path "./vendor/*" | xargs xgettext --output="${LOCALE_DOMAIN}.pot" --output-dir="${LOCALE_PARENT_DIR}" --language=PHP --from-code=UTF-8 --force-po --no-location --no-wrap --sort-output --copyright-holder="" --keyword --keyword="_:1,1t" --keyword="_f:1" --keyword="_fe:1" --keyword="_p:1,2,3t" --keyword="_pf:1,2" --keyword="_pfe:1,2" --keyword="_c:1,2c,2t" --keyword="_m:1,1t" --flag="_f:1:php-format" --flag="_fe:1:no-php-format" --flag="_pf:1:php-format" --flag="_pfe:1:no-php-format" +sed -i '/# SOME DESCRIPTIVE TITLE./d' "${LOCALE_PARENT_DIR}/${LOCALE_DOMAIN}.pot" +sed -i '/# This file is put in the public domain./d' "${LOCALE_PARENT_DIR}/${LOCALE_DOMAIN}.pot" +sed -i '/# FIRST AUTHOR , YEAR./d' "${LOCALE_PARENT_DIR}/${LOCALE_DOMAIN}.pot" +sed -i '0,/#, fuzzy/{s/#, fuzzy//}' "${LOCALE_PARENT_DIR}/${LOCALE_DOMAIN}.pot" +sed -i '/\"Project-Id-Version: PACKAGE VERSION\\n\"/d' "${LOCALE_PARENT_DIR}/${LOCALE_DOMAIN}.pot" +sed -i '/\"Report-Msgid-Bugs-To: \\n\"/d' "${LOCALE_PARENT_DIR}/${LOCALE_DOMAIN}.pot" +sed -i '/\"POT-Creation-Date: /d' "${LOCALE_PARENT_DIR}/${LOCALE_DOMAIN}.pot" +sed -i '/\"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\\n\"/d' "${LOCALE_PARENT_DIR}/${LOCALE_DOMAIN}.pot" +sed -i '/\"Last-Translator: FULL NAME \\n\"/d' "${LOCALE_PARENT_DIR}/${LOCALE_DOMAIN}.pot" +sed -i '/\"Language-Team: LANGUAGE \\n\"/d' "${LOCALE_PARENT_DIR}/${LOCALE_DOMAIN}.pot" +sed -i '0,/\"Language: \\n\"/{s/\"Language: \\n\"/\"Language: xx\\n\"/}' "${LOCALE_PARENT_DIR}/${LOCALE_DOMAIN}.pot" +sed -i '0,/\"Content-Type: text\/plain; charset=CHARSET\\n\"/{s/\"Content-Type: text\/plain; charset=CHARSET\\n\"/\"Content-Type: text\/plain; charset=UTF-8\\n\"/}' "${LOCALE_PARENT_DIR}/${LOCALE_DOMAIN}.pot" + +if [ ! -z "${LOCALE_CODE}" ]; then + LOCALE_CONTENTS_DIR="${LOCALE_PARENT_DIR}/${LOCALE_CODE}/LC_MESSAGES" + + mkdir --parents "${LOCALE_CONTENTS_DIR}" + + echo " * Creating PO (Portable Object) file for “${LOCALE_CODE}”" + + if [ -f "${LOCALE_CONTENTS_DIR}/${LOCALE_DOMAIN}.po" ]; then + msgmerge --update --backup=none --suffix=".bak" --previous --force-po --no-location --no-wrap --sort-output "${LOCALE_CONTENTS_DIR}/${LOCALE_DOMAIN}.po" "${LOCALE_PARENT_DIR}/${LOCALE_DOMAIN}.pot" + else + msginit --input="${LOCALE_PARENT_DIR}/${LOCALE_DOMAIN}.pot" --output-file="${LOCALE_CONTENTS_DIR}/${LOCALE_DOMAIN}.po" --locale="${LOCALE_CODE}" --no-translator --no-wrap + sed -i '/\"Project-Id-Version: /d' "${LOCALE_CONTENTS_DIR}/${LOCALE_DOMAIN}.po" + sed -i '/\"Last-Translator: Automatically generated\\n\"/d' "${LOCALE_CONTENTS_DIR}/${LOCALE_DOMAIN}.po" + sed -i '/\"Language-Team: none\\n\"/d' "${LOCALE_CONTENTS_DIR}/${LOCALE_DOMAIN}.po" + fi + + echo " * Creating MO (Machine Object) file for “${LOCALE_CODE}”" + + msgfmt --output-file="${LOCALE_CONTENTS_DIR}/${LOCALE_DOMAIN}.mo" --check-format --check-domain "${LOCALE_CONTENTS_DIR}/${LOCALE_DOMAIN}.po" + + rm "${LOCALE_PARENT_DIR}/${LOCALE_DOMAIN}.pot" +fi + +echo "Done" diff --git a/index.php b/index.php index de648aa..b56ac05 100644 --- a/index.php +++ b/index.php @@ -96,5 +96,27 @@ function e($str) { \define('TYPE_TEXT', \Delight\Foundation\Input::DATA_TYPE_TEXT); \define('TYPE_RAW', \Delight\Foundation\Input::DATA_TYPE_RAW); +// reluctantly put some functions into the global namespace for maximum convenience +function _f($text, ...$replacements) { global $app; return $app->i18n()->translateFormatted($text, ...$replacements); } +function _fe($text, ...$replacements) { global $app; return $app->i18n()->translateFormattedExtended($text, ...$replacements); } +function _p($text, $alternative, $count) { global $app; return $app->i18n()->translatePlural($text, $alternative, $count); } +function _pf($text, $alternative, $count, ...$replacements) { global $app; return $app->i18n()->translatePluralFormatted($text, $alternative, $count, ...$replacements); } +function _pfe($text, $alternative, $count, ...$replacements) { global $app; return $app->i18n()->translatePluralFormattedExtended($text, $alternative, $count, ...$replacements); } +function _c($text, $context) { global $app; return $app->i18n()->translateWithContext($text, $context); } +function _m($text) { global $app; return $app->i18n()->markForTranslation($text); } + +if ($app->isClientCli() || $app->isClientLoopback()) { + if ($app->hasCliArgument()) { + if ($app->getCliArgument() === 'clear-template-cache') { + $app->clearTemplateCache(); + exit; + } + elseif ($app->getCliArgument() === 'precompile-templates') { + $app->precompileTemplates(); + exit; + } + } +} + // include the actual application code require __DIR__ . '/app/index.php'; diff --git a/locale/.gitignore b/locale/.gitignore new file mode 100644 index 0000000..65e1692 --- /dev/null +++ b/locale/.gitignore @@ -0,0 +1,4 @@ +# Ignore MO (Machine Object) files +*.mo +# Ignore POT (Portable Object Template) files +*.pot