Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: Seed feat/localize/keyboard-search branch 📡 #516

Draft
wants to merge 22 commits into
base: staging
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
be6871a
chore: Ssed feat/localize/keyboard-search branch
darcywong00 Dec 4, 2024
d34b47b
feat: Configure en_US locale in Dockerfile
darcywong00 Dec 4, 2024
a60d92a
Update resources/init-container.sh
darcywong00 Dec 5, 2024
b38abc2
Merge pull request #517 from keymanapp/feat/docker-gettext
darcywong00 Dec 5, 2024
feb2a26
fix: restore path in init-container.sh
darcywong00 Dec 6, 2024
36cc972
Merge pull request #518 from keymanapp/feat/init-container/restore-path
darcywong00 Dec 6, 2024
583a9cd
feat: Add Locale.php class to control currentLocale
darcywong00 Dec 6, 2024
008a6b8
Undo singleton
darcywong00 Dec 9, 2024
b483df4
Use `lang` query parameter
darcywong00 Dec 9, 2024
239d4c7
set _SESSION['lang']
darcywong00 Dec 11, 2024
e2f44a4
Merge pull request #520 from keymanapp/feat/locale-session
darcywong00 Dec 11, 2024
82c5a54
feat: Localize strings on keyboards index
darcywong00 Dec 9, 2024
e4fb670
validate locale
darcywong00 Dec 11, 2024
6bd2621
chore: Refactor localize()
darcywong00 Dec 12, 2024
16669a2
Apply suggestions from code review
darcywong00 Jan 20, 2025
f7a4bec
Add index parameters to Spanish and French
darcywong00 Jan 20, 2025
edd7430
Consolidate punctuation to strings
darcywong00 Jan 20, 2025
18b5900
Consolidate punctuation for Ethiopia example
darcywong00 Jan 20, 2025
19510be
Simplify strings
darcywong00 Jan 20, 2025
042348e
fix stray format
darcywong00 Jan 20, 2025
8c156b7
Merge pull request #521 from keymanapp/feat/localize/keyboard/index
darcywong00 Jan 20, 2025
509165c
Merge branch 'staging' into feat/localize/keyboard-search
darcywong00 Jan 27, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,21 @@ COPY resources/keyman-site.conf /etc/apache2/conf-available/
RUN cp /usr/local/etc/php/php.ini-production /usr/local/etc/php/php.ini
RUN chown -R www-data:www-data /var/www/html/

# Because the base Docker image doesn't include locales, install these to generate locale files.
# gettext needed to compile .po files to .mo with msgfmt
RUN apt-get update && apt-get install -y \
locales \
gettext

# Install PHP-extension gettext for localization at runtime
RUN docker-php-ext-install gettext
RUN docker-php-ext-enable gettext

# Only enable en_US locale in /etc/locale.gen
# PHP will use textdomain() to specify "localization" .mo files
RUN sed -i -e 's/# en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/' /etc/locale.gen \
&& dpkg-reconfigure --frontend=noninteractive locales \
&& update-locale

COPY --from=composer-builder /composer/vendor /var/www/vendor
RUN a2enmod rewrite headers; a2enconf keyman-site
1 change: 1 addition & 0 deletions _includes/includes/template.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

// *Don't* use autoloader here because of potential side-effects in older pages
require_once(__DIR__ . '/../2020/Util.php');
require_once(__DIR__ . '/../locale/Locale.php');
require_once(__DIR__ . '/../2020/KeymanVersion.php');
require_once(__DIR__ . '/../2020/templates/Head.php');

Expand Down
98 changes: 98 additions & 0 deletions _includes/locale/Locale.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
<?php
declare(strict_types=1);

namespace Keyman\Site\com\keyman;

class Locale {
public const DEFAULT_LOCALE = 'en';

public const CROWDIN_LOCALES = array(
'en',
'es-ES',
'fr-FR'
);

// xx-YY locale as specified in crowdin %locale%
private static $currentLocale = Locale::DEFAULT_LOCALE;

/**
* Return the current locale. Fallback to 'en'
* @return $currentLocale
*/
public static function currentLocale() {
return Locale::$currentLocale;
}

/**
* Validate and override the current locale
* @param $locale - the new current locale (xx-YY as specified in crowdin %locale%)
*/
public static function overrideCurrentLocale($locale) {
if (Locale::validateLocale($locale)) {
Locale::$currentLocale = $locale;
}
}

/**
* Validate $locale is an acceptable locale.
* Using xx-YY as specified in crowdin %locale%
* @param $locale - the locale to validate
* @return true if valid locale
*/
public static function validateLocale($locale) {
return in_array($locale, Locale::CROWDIN_LOCALES);
}

/**
* Use textdomain to specify the localization file for "localization".
* Ignore if locale is "en" or the filename doesn't exist
* Filename expected to be "$basename-$locale.mo"
* @param $basename - base name of the .mo file to use
* @return current message domain
*/
public static function setTextDomain($basename) {
// Container uses English locale, and then we use textdomain to change "localization" files
setLocale(LC_ALL, 'en_US.UTF-8');

if (Locale::$currentLocale == Locale::DEFAULT_LOCALE) {
return;
}

$filename = sprintf("%s-%s", $basename, Locale::$currentLocale);
$fullPath = sprintf("%s/en/LC_MESSAGES/%s.mo", __DIR__, $filename);
if(file_exists($fullPath)) {
return textdomain($filename);
} else {
//echo "textdomain $fullPath doesn't exist";
return;
}
}

/**
* Reads localized strings from the specified $domain-locale.po file
* for the current locale.
* @param $domain - base filename of the .po files (not including -xx-YY locale)
*/
public static function localize($domain) {
foreach(Locale::CROWDIN_LOCALES as $l) {
if ($l == Locale::DEFAULT_LOCALE) {
// Skip English
continue;
}

bindtextdomain("$domain-$l", __DIR__);
}

$previousTextDomain = textdomain(NULL);
Locale::setTextDomain($domain);
}

/**
* Wrapper to format string with gettext '_(' alias and variable args
* @param $s - the format string
* @param $args - optional remaining args to the format string
*/
public static function _s($s, ...$args) {
return vsprintf(_($s), $args);
}
}
45 changes: 45 additions & 0 deletions _includes/locale/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
### Setup for Localization

[init-container.sh](../../resources/init-container.sh) contains steps for the Docker container to compile .po files to .mo files which PHP uses for `gettext()`.

If you want to compile the files on your host machine, install `gettext`.

```bash
sudo apt-get install gettext
```

### Adding locales

The Docker image has the "en_US.UTF-8" locale enabled in `/etc/locale.gen`
We'll use `textdomain` to specify filenames for "switching" localization.
The filenames will include the `%locale%` as defined in the [crowdin.com project](https://crowdin.com/project/keymancom).

Note: the details below will get refactored to use a Locale.php class

In the example below, the English file `keyboards-en.po` is copied to `keyboards-fr-FR.po` for French.

1. In `/_includes/locale/en/LC_MESSAGES/`
* Copy `keyboards-en.po` file and rename to the `keyboards-fr-FR.po`.
* Translate/upload the new .po file to crowdin
* Convert .po file to .mo with the following

```bash
msgfmt keyboards-fr-FR.po --output-file=keyboards-fr-FR.mo
```

(The container handles the msgfmt step in init-container.sh)

2. Add the file to the PHP (path is relative the PHP file)

```php
bindtextdomain("keyboards-fr-FR", "../_includes/locale");
```

3. To use French,
```php
textdomain('keyboards-fr-FR');
```

----

For formatted string, use the PHP wrapper [`Locale::_s(msgstr, $args)`](./Locale.php).
2 changes: 2 additions & 0 deletions _includes/locale/en/LC_MESSAGES/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Ignore generated files from msgfmt
*.mo
85 changes: 85 additions & 0 deletions _includes/locale/en/LC_MESSAGES/keyboards-en.po
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
msgid ""
msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"

# Default English strings for keyboards/index.php

# Page Title
msgid "Keyboard Search"
msgstr "Keyboard Search"

# Page Description
msgid "Keyman Keyboard Search"
msgstr "Keyman Keyboard Search"

# Keyboard search bar
msgid "Keyboard search:"
msgstr "Keyboard search:"

# Search bar placeholder
msgid "Enter language or keyboard"
msgstr "Enter language or keyboard"

# Search Button Value
msgid "Search"
msgstr "Search"

# Link to start a new keyboard search
msgid "New search"
msgstr "New search"

# Search box instruction (Popular keyboards | All keyboards)
msgid "Enter the name of a keyboard or language to search for"
msgstr "Enter the name of a keyboard or language to search for"

# Search box link for popular keyboards
msgid "Popular keyboards"
msgstr "Popular keyboards"

# Search box link for all Keyman keyboards
msgid "All keyboards"
msgstr "All keyboards"

# Search box hint: List header
msgid "Hints"
msgstr "Hints"

# Search box hint: Description
msgid "The search always returns a list of keyboards. It searches for keyboard names and details, language names, country names and script names."
msgstr "The search always returns a list of keyboards. It searches for keyboard names and details, language names, country names and script names."

# Search box hint: available prefixes to use in the search
msgid "You can apply prefixes"
msgstr "You can apply prefixes"

# (keyboards)
msgid "(keyboards)"
msgstr "(keyboards)"

# (languages)
msgid "(languages)"
msgstr "(languages)"

# (scripts, writing systems) or...
msgid "(scripts, writing systems) or"
msgstr "(scripts, writing systems) or"

# (countries) to filter your search results...
msgid "(countries) to filter your search results. For example"
msgstr "(countries) to filter your search results. For example"

# Search box hint: example of country search
msgid "searches for keyboards for languages used in Thailand."
msgstr "searches for keyboards for languages used in Thailand."

# Search box hint: BCP 47 prefix
msgid "Use prefix"
msgstr "Use prefix"

# Seach box hint: BCP 47 language example
msgid "to search for a BCP 47 language tag, for example"
msgstr "to search for a BCP 47 language tag, for example"

# Search box hint: BCP 47 language example
msgid "searches for Tigrigna (Ethiopia)"
msgstr "searches for Tigrigna (Ethiopia)"
94 changes: 94 additions & 0 deletions _includes/locale/en/LC_MESSAGES/keyboards-es-ES.po
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
msgid ""
msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Crowdin-Project: keymancom\n"
"X-Crowdin-Project-ID: 740839\n"
"X-Crowdin-Language: es-ES\n"
"X-Crowdin-File: /master/keyboards/keyboards.po\n"
"X-Crowdin-File-ID: 2\n"
"Project-Id-Version: keymancom\n"
"Language-Team: Spanish\n"
"Language: es_ES\n"
"PO-Revision-Date: 2024-11-11 08:20\n"

# Page Title
msgid "Keyboard Search"
msgstr "Búsqueda por Teclado"

# Page Description
msgid "Keyman Keyboard Search"
msgstr "Keyman Búsqueda por Teclado"

# Keyboard search bar
msgid "Keyboard search:"
msgstr "Búsqueda por teclado:"

# Search bar placeholder
msgid "Enter language or keyboard"
msgstr "Ingresar idioma o teclado"

# Search Button Value
msgid "Search"
msgstr "Buscar"

# Link to start a new keyboard search
msgid "New search"
msgstr "Nueva buscar"

# Search box instruction (Popular keyboards | All keyboards)
msgid "Enter the name of a keyboard or language to search for"
msgstr "Introduzca el nombre de un teclado o idioma para buscar"

# Search box link for popular keyboards
msgid "Popular keyboards"
msgstr "Teclados populares"

# Search box link for all Keyman keyboards
msgid "All keyboards"
msgstr "Todos los teclados"

# Search box hint: List header
msgid "Hints"
msgstr "Consejos"

# Search box hint: Description
msgid "The search always returns a list of keyboards. It searches for keyboard names and details, language names, country names and script names."
msgstr "La búsqueda siempre devuelve una lista de teclados. Busca nombres de teclados y detalles, nombres de idiomas, nombres de países y nombres de alfabetos."

# Search box hint: available prefixes to use in the search
msgid "You can apply prefixes"
msgstr "Puedes aplicar prefijos"

# (keyboards)
msgid "(keyboards)"
msgstr "(tescados)"

# (languages)
msgid "(languages)"
msgstr "(idiomas)"

# (scripts, writing systems) or...
msgid "(scripts, writing systems) or"
msgstr "(guiones, sistemas de escritura) o"

# (countries) to filter your search results...
msgid "(countries) to filter your search results. For example"
msgstr "(países) para filtrar los resultados de búsqueda. Por ejemplo"

# Search box hint: example of country search
msgid "searches for keyboards for languages used in Thailand."
msgstr "busca teclados para los idiomas utilizados en Tailandia."

# Search box hint: BCP 47 prefix
msgid "Use prefix"
msgstr "Utilice prefijo"

# Seach box hint: BCP 47 language example
msgid "to search for a BCP 47 language tag, for example"
msgstr "para buscar una etiqueta de idioma BCP 47, por ejemplo"

# Search box hint: BCP 47 language example
msgid "searches for Tigrigna (Ethiopia)"
msgstr "busca Tigrigna (Etiopía)"

Loading