Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
5 changes: 3 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,12 @@ jobs:
- name: Check broken links
shell: bash
continue-on-error: false
# Exclude checking locales for each link 'lang=*'
# Exclude the following: non-en locales, and 3-letter keyboard redirects (/lao, /ipa)
run: |
set +e
set +o pipefail
npx broken-link-checker http://localhost:8053/_test --recursive --ordered ---host-requests 50 -e --filter-level 3 --exclude '*/donate' --exclude '*lang=*' | tee blc.log
npx broken-link-checker http://localhost:8053/_test --recursive --ordered ---host-requests 50 -e --filter-level 3 \
--exclude '*/donate' --exclude '*/de/*' --exclude '*/es/*' --exclude '*/fr/*' --exclude '*/ipa/*' --exclude '*/km/*' --exclude '*lang=*' --exclude '*/lao/*' | tee blc.log
Comment on lines +44 to +45
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Excluding all locales except en will not scale well. I wonder if there is a better way -- can we build a scripted filter using the api published at broken-link-checker?

echo "BLC_RESULT=${PIPESTATUS[0]}" >> "$GITHUB_ENV"

- name: Report on broken links
Expand Down
67 changes: 36 additions & 31 deletions .htaccess
Original file line number Diff line number Diff line change
Expand Up @@ -203,75 +203,82 @@ RewriteCond "%{DOCUMENT_ROOT}/cdn/$1$2" -f
RewriteRule "^cdn/(.+\.)(gif|js|png|svg)$" "/cdn/$1$2" [END]


#
# Intentionally not using regex from Validation::validate_bcp47 because we only need lang-script-region triplet
# ^(([a-z]{2,3})(-([A-Za-z]{4}))?(-([a-z]{2}|[0-9]{3}))?)
# [NC] for case-insensitive
# Long regex for "BCP-47" codes = $1, rest of URL $7 onward
#

#
# keyboards Install | Download | Share | bare | .json -->
#

# /keyboards/install/[id] to /keyboards/install.php
RewriteRule "^(en)/keyboards/install/([^/]+)$" "/_content/keyboards/install.php?id=$2&lang=$1" [END,QSA]
RewriteRule "^(([a-z]{2,3})(-([A-Za-z]{4}))?(-([a-z]{2}|[0-9]{3}))?)/keyboards/install/([^/]+)$" "/_content/keyboards/install.php?id=$7&lang=$1" [NC,END,QSA]

# /keyboards/download/[id] to /keyboards/keyboard.php
# This formerly redirected to a download, but we no longer need it; keep it for
# legacy links
RewriteRule "^(en)/keyboards/download/([^/]+)$" "/_content/keyboards/keyboard.php?id=$2&lang=$1" [END,QSA]
RewriteRule "^(([a-z]{2,3})(-([A-Za-z]{4}))?(-([a-z]{2}|[0-9]{3}))?)/keyboards/download/([^/]+)$" "/_content/keyboards/keyboard.php?id=$7&lang=$1" [NC,END,QSA]

# /keyboards/share/[id] to /keyboards/share.php
# if the keyboard exists in the repo, then share.php will redirect to /keyboards/<id>
RewriteRule "^(en)/keyboards/share/([^/]+)$" "/_content/keyboards/share.php?id=$2&lang=$1" [END,QSA]
RewriteRule "^(([a-z]{2,3})(-([A-Za-z]{4}))?(-([a-z]{2}|[0-9]{3}))?)/keyboards/share/([^/]+)$" "/_content/keyboards/share.php?id=$7&lang=$1" [NC,END,QSA]

# /keyboards/{id}.json to /keyboards/keyboard.json.php
RewriteRule "^(en)/keyboards/(?!keyboard.json)(.*)\.json$" "/_content/keyboards/keyboard.json.php?id=$2&lang=$1" [END]
RewriteRule "^(([a-z]{2,3})(-([A-Za-z]{4}))?(-([a-z]{2}|[0-9]{3}))?)/keyboards/(?!keyboard.json)(.*)\.json$" "/_content/keyboards/keyboard.json.php?id=$7&lang=$1" [NC,END]

# /keyboards/{id} to /keyboards/keyboard.php
RewriteRule "^(en)/keyboards/(?!index\.php|install|keyboard|session|share)([^/]+)$" "/_content/keyboards/keyboard.php?id=$2&lang=$1" [END,QSA]
RewriteRule "^(([a-z]{2,3})(-([A-Za-z]{4}))?(-([a-z]{2}|[0-9]{3}))?)/keyboards/(?!index\.php|install|keyboard|session|share)([^/]+)$" "/_content/keyboards/keyboard.php?id=$7&lang=$1" [NC,END,QSA]

#
# keyboards search
#

# /keyboards/languages to /keyboards/index.php
RewriteRule "^(en)/keyboards/languages/(.*)" "/_content/keyboards/index.php?q=l:id:$2&lang=$1" [END,QSA]
RewriteRule "^(([a-z]{2,3})(-([A-Za-z]{4}))?(-([a-z]{2}|[0-9]{3}))?)/keyboards/languages/(.*)" "/_content/keyboards/index.php?q=l:id:$7&lang=$1" [NC,END,QSA]

# /keyboards/download to /keyboards/download.php
RewriteRule "^(en)/keyboards/download(.php)?" "/_content/keyboards/download.php?lang=$1" [END,QSA]
RewriteRule "^(([a-z]{2,3})(-([A-Za-z]{4}))?(-([a-z]{2}|[0-9]{3}))?)/keyboards/download(.php)?" "/_content/keyboards/download.php?lang=$1" [NC,END,QSA]

# /keyboards/legacy to /keyboards/keyboard.php
RewriteRule "^(en)/keyboards/legacy/(.*)" "/_content/keyboards/keyboard.php?legacy=$2&lang=$1" [END]
RewriteRule "^(([a-z]{2,3})(-([A-Za-z]{4}))?(-([a-z]{2}|[0-9]{3}))?)/keyboards/legacy/(.*)" "/_content/keyboards/keyboard.php?legacy=$7&lang=$1" [NC,END]

# /keyboards/countries to /keyboards/index.php
RewriteRule "^(en)/keyboards/countries/(.*)" "/_content/keyboards/index.php?q=c:id:$2&lang=$1" [END]
RewriteRule "^(([a-z]{2,3})(-([A-Za-z]{4}))?(-([a-z]{2}|[0-9]{3}))?)/keyboards/countries/(.*)" "/_content/keyboards/index.php?q=c:id:$7&lang=$1" [NC,END]


#
# contact/exception
#

# /contact/exception to /contact/exception.php
RewriteRule "^(en)/contact/exception(.php)?" "/_content/contact/exception.php?lang=$1" [END,QSA]
RewriteRule "^(([a-z]{2,3})(-([A-Za-z]{4}))?(-([a-z]{2}|[0-9]{3}))?)/contact/exception(.php)?" "/_content/contact/exception.php?lang=$1" [NC,END,QSA]

RewriteRule "^(en)/contact/exception?id=(.+)$" "/_content/contact/exception?id=$2&lang=$1" [END]
RewriteRule "^(([a-z]{2,3})(-([A-Za-z]{4}))?(-([a-z]{2}|[0-9]{3}))?)/contact/exception?id=(.+)$" "/_content/contact/exception?id=$7&lang=$1" [NC,END]

#
# ios
#
RewriteRule "^(en)/(iphone|ipad)(/.*)?" "/_content/iphone-and-ipad/$3?lang=$1" [END,QSA]
RewriteRule "^(([a-z]{2,3})(-([A-Za-z]{4}))?(-([a-z]{2}|[0-9]{3}))?)/(iphone|ipad)(/.*)?" "/_content/iphone-and-ipad/$8?lang=$1" [NC,END,QSA]

#
# downloads/releases
#

# releases-tier/download
# note: the tier is currently ignored
RewriteRule "^(en)/downloads/releases/(alpha|beta|stable)/(.+)$" "/_content/downloads/releases/_version_downloads.php?tier=$2&version=$3&lang=$1" [END]
RewriteRule "^(([a-z]{2,3})(-([A-Za-z]{4}))?(-([a-z]{2}|[0-9]{3}))?)/downloads/releases/(alpha|beta|stable)/(.+)$" "/_content/downloads/releases/_version_downloads.php?tier=$7&version=$8&lang=$1" [NC,END]

# releases-download
RewriteRule "^(en)/downloads/releases/(.+)$" "/_content/downloads/releases/_version_downloads.php?version=$2&lang=$1" [END]
RewriteRule "^(([a-z]{2,3})(-([A-Za-z]{4}))?(-([a-z]{2}|[0-9]{3}))?)/downloads/releases/(.+)$" "/_content/downloads/releases/_version_downloads.php?version=$7&lang=$1" [NC,END]

#
# Assets don't use lang param
#
RewriteCond "%{DOCUMENT_ROOT}/_content/$2" -f
RewriteRule "^(en)/(.+)$" "/_content/$2" [END]
RewriteCond "%{DOCUMENT_ROOT}/_content/$7" -f
RewriteRule "^(([a-z]{2,3})(-([A-Za-z]{4}))?(-([a-z]{2}|[0-9]{3}))?)/(.+)$" "/_content/$7$8" [NC,END]

# TODO: do we want en/lao --> en/keyboards/basic_kbdlao? as well as /lao --> ...

Expand All @@ -281,32 +288,30 @@ RewriteRule "^(en)/(.+)$" "/_content/$2" [END]

# TODO: mdhost currently in a different path than help.keyman

# TODO: en --> should be a bcp-47 type 'lang' match (e.g. ab[-x][-y]) ignore case, keep it a fairly loose expression [a-z][a-z][a-z]?(-...)?

##############################################

# Rewrite file to file.md
RewriteCond "%{DOCUMENT_ROOT}/_content/$2.md" -f
RewriteRule "^(en)/(.+)$" "/_includes/includes/md/mdhost.php?file=_content/$2.md&lang=$1" [END]
RewriteCond "%{DOCUMENT_ROOT}/_content/$7.md" -f
RewriteRule "^(([a-z]{2,3})(-([A-Za-z]{4}))?(-([a-z]{2}|[0-9]{3}))?)/(.+)$" "/_includes/includes/md/mdhost.php?file=_content/$7.md&lang=$1" [NC,END]

# Rewrite file to file.php
RewriteCond "%{DOCUMENT_ROOT}/_content/$2.php" -f
RewriteCond "%{DOCUMENT_ROOT}/_content/$2.md" !-f
RewriteRule "^(en)/(.+)$" "/_content/$2.php?lang=$1" [END]
RewriteCond "%{DOCUMENT_ROOT}/_content/$7.php" -f
RewriteCond "%{DOCUMENT_ROOT}/_content/$7.md" !-f
RewriteRule "^(([a-z]{2,3})(-([A-Za-z]{4}))?(-([a-z]{2}|[0-9]{3}))?)/(.+)$" "/_content/$7.php?lang=$1" [NC,END]

# Rewrite folder/ to folder/index.md
RewriteCond "%{DOCUMENT_ROOT}/_content/$2/index.md" -f
RewriteRule "^(en)/(.+)/$" "/_includes/includes/md/mdhost.php?file=_content/$2/index.md&lang=$1" [END]
RewriteCond "%{DOCUMENT_ROOT}/_content/$7/index.md" -f
RewriteRule "^(([a-z]{2,3})(-([A-Za-z]{4}))?(-([a-z]{2}|[0-9]{3}))?)/(.+)/$" "/_includes/includes/md/mdhost.php?file=_content/$7/index.md&lang=$1" [NC,END]

# Rewrite folder/ to folder/index.php
RewriteCond "%{DOCUMENT_ROOT}/_content/$2/index.php" -f
RewriteCond "%{DOCUMENT_ROOT}/_content/$2/index.md" !-f
RewriteRule "^(en)/(.+)/$" "/_content/$2/index.php?lang=$1" [END]
RewriteCond "%{DOCUMENT_ROOT}/_content/$7/index.php" -f
RewriteCond "%{DOCUMENT_ROOT}/_content/$7/index.md" !-f
RewriteRule "^(([a-z]{2,3})(-([A-Za-z]{4}))?(-([a-z]{2}|[0-9]{3}))?)/(.+)/$" "/_content/$7/index.php?lang=$1" [NC,END]

# Root page
RewriteRule "^(en)/$" "/_content/index.php?lang=$1" [END]
RewriteRule "^(([a-z]{2,3})(-([A-Za-z]{4}))?(-([a-z]{2}|[0-9]{3}))?)/$" "/_content/index.php?lang=$1" [NC,END]

# Finally, append the terminating slash for folders, given it is no longer
# done automatically because we put DirectorySlash off
RewriteCond "%{DOCUMENT_ROOT}/_content/$2$3" -d
RewriteRule "^(en)(.*)([^/])?$" "$1$2$3/" [R,QSA]
RewriteCond "%{DOCUMENT_ROOT}/_content/$7$8" -d
RewriteRule "^(([a-z]{2,3})(-([A-Za-z]{4}))?(-([a-z]{2}|[0-9]{3}))?)(.*)([^/])?$" "$1$7$8/" [NC,R,QSA]
57 changes: 30 additions & 27 deletions _includes/2020/templates/Menu.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@

namespace Keyman\Site\com\keyman\templates;

use Keyman\Site\com\keyman\Locale;
use Keyman\Site\com\keyman\Util;
use Keyman\Site\com\keyman\Validation;
use Keyman\Site\Common\KeymanVersion;
use Keyman\Site\Common\KeymanHosts;

Expand Down Expand Up @@ -34,40 +36,48 @@ public static function render(array $fields): void {
}

/**
* Generate the URL with query to change the UI language
* Modify link of the current URL for a given UI language
* @param language - language tag to use
* @return string - modified URL
*/
private static function change_ui_language($language): string {
// Parse the current URI for populating the UI dropdown
$url = isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : '/';
$parts = parse_url($url);

if (!empty($parts['query'])) {
parse_str($parts['query'], $queryParams);
} else {
$queryParams = [];
$path = '';
// Replace language if current language in path is valid BCP-47
// Note: Validate::validate_bcp47 differs from regex in .htaccess
if (!empty($parts['path'])) {
$path = explode("/", $parts['path']);
if ($path[1] != null ) {
/* TODO: add validation back && class_exists('\\Keyman\\Site\\com\\keyman\\Validation') &&
\Keyman\Site\com\keyman\Validation::validate_bcp47($path[1]) != null*/
$path[1] = $language;
} else {
// original URL didn't have a valid BCP-47 so inert it
array_splice($path, 1, 0, $language);
}
}

// Set the language query
$queryParams['lang'] = $language;
$query = http_build_query($queryParams);

return $parts['path'] . "?" . $query;
// Rebuild the path
$newPath = implode("/", $path);
if (!empty($parts['query'])) {
// Append query
$newPath .= "?" . $parts['query'];
}
return $newPath;
}

/**
* Generate links that correspond to the UI options
* As UI languages get added, we'll need to update this.
*/
private static function render_ui_list() {
echo "<ul>\n <!-- Just use autonyms -->\n";
$linkArray = array(
'en' => array(Menu::change_ui_language('en'), 'English'),
'de' => array(Menu::change_ui_language('de'), 'Deutsch'),
'es' => array(Menu::change_ui_language('es'), 'Español'),
'fr' => array(Menu::change_ui_language('fr'), 'Français'),
'km' => array(Menu::change_ui_language('km'), 'ខ្មែរ')
);
$linkArray = array();
foreach(DISPLAY_NAMES as $id => $name) {
$linkArray[$id] = array(Menu::change_ui_language($id), $name);
}

foreach($linkArray as $id) {
echo <<<END
Expand All @@ -79,25 +89,18 @@ private static function render_ui_list() {

/**
* Render the globe dropdown for changing the UI language
* Limitation: Currently only visible on pages that use localized strings
* @param divID - <div> ID to handle 3 cases:
* ui-language (default) Desktop globe hover
* ui-language1 - Desktop globe hover
* phone - Mobile list
*/
private static function render_globe_dropdown($divID = "ui-language"): void {
global $page_is_using_locale;
if (!isset($page_is_using_locale) || !$page_is_using_locale) {
// only render on pages that use localized strings
return;
}

// Phone layout
$globeClass = '';
if ($divID === "phone") {
?>
<div class="phone-menu-item">
<h3><span><img src="<?php echo Util::cdn("img/globe.png"); ?>" alt="UI globe dropdown" /></span> Keyboard Search UI</h3>
<h3><span><img src="<?php echo Util::cdn("img/globe.png"); ?>" alt="UI globe dropdown" /></span> Display in:</h3>
<?= Menu::render_ui_list(); ?>
</div>
<?php
Expand Down Expand Up @@ -204,7 +207,7 @@ private static function render_top_menu(object $fields): void {
<img id="header-bottom" src="<?php echo Util::cdn("img/headerbar.png"); ?>" alt='Header bottom' />
<div id="help">

<span id='free'>Keyman is <a href='/free'>free and open source</a></span>
<span id='free'>Keyman is <a href='/<?=$fields->lang?>/free'>free and open source</a></span>

<form action="/<?=$fields->lang?>/search/" method="get" role="search">
<div class="search-wrap">
Expand Down
8 changes: 8 additions & 0 deletions _includes/locale/Locale.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,14 @@

use \Keyman\Site\Common\KeymanHosts;

// As UI languages get added, we'll need to update this.
define('DISPLAY_NAMES', [
'en' => 'English',
'de' => 'Deutsch',
'es' => 'Español',
'fr' => 'Français',
'km' => 'ខ្មែរ (Khmer)']);

class Locale {
public const DEFAULT_LOCALE = 'en';

Expand Down
Loading