Skip to content

Commit

Permalink
Merge pull request #47349 from nextcloud/feat/31420/bidi-backend-support
Browse files Browse the repository at this point in the history
Add bidirectional text support - Backend
  • Loading branch information
nickvergessen authored Sep 24, 2024
2 parents 129f6b2 + 82566c5 commit 12ed773
Show file tree
Hide file tree
Showing 6 changed files with 87 additions and 2 deletions.
35 changes: 35 additions & 0 deletions build/translation-checker.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,35 @@
'testing',
];

// Next line only looks messed up, but it works. Don't touch it!
$rtlCharacters = [
'\x{061C}', // ARABIC LETTER MARK
'\x{0623}', // ARABIC LETTER ALEF WITH HAMZA ABOVE
'\x{200E}', // LEFT-TO-RIGHT MARK
'\x{200F}', // RIGHT-TO-LEFT MARK
'\x{202A}', // LEFT-TO-RIGHT EMBEDDING
'\x{202B}', // RIGHT-TO-LEFT EMBEDDING
'\x{202C}', // POP DIRECTIONAL FORMATTING
'\x{202D}', // LEFT-TO-RIGHT OVERRIDE
'\x{202E}', // RIGHT-TO-LEFT OVERRIDE
'\x{2066}', // LEFT-TO-RIGHT ISOLATE
'\x{2067}', // RIGHT-TO-LEFT ISOLATE
'\x{2068}', // FIRST STRONG ISOLATE
'\x{2069}', // POP DIRECTIONAL ISOLATE
'\x{206C}', // INHIBIT ARABIC FORM SHAPING
'\x{206D}', // ACTIVATE ARABIC FORM SHAPING
];

$rtlLanguages = [
'ar', // Arabic
'fa', // Persian
'he', // Hebrew
'ps', // Pashto,
'ug', // 'Uyghurche / Uyghur
'ur_PK', // Urdu
'uz', // Uzbek Afghan
];

$valid = 0;
$errors = [];
$apps = new \DirectoryIterator(__DIR__ . '/../apps');
Expand Down Expand Up @@ -46,6 +75,12 @@
}

$content = file_get_contents($file->getPathname());

$language = pathinfo($file->getFilename(), PATHINFO_FILENAME);
if (!in_array($language, $rtlLanguages, true) && preg_match('/[' . implode('', $rtlCharacters) . ']/u', $content)) {
$errors[] = $file->getPathname() . "\n" . ' ' . 'Contains a RTL limited character in the translations.' . "\n";
}

$json = json_decode($content, true);

$translations = json_encode($json['translations']);
Expand Down
2 changes: 1 addition & 1 deletion core/templates/layout.user.php
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@
<?php emit_script_loading_tags($_); ?>
<?php print_unescaped($_['headers']); ?>
</head>
<body id="<?php p($_['bodyid']);?>" <?php foreach ($_['enabledThemes'] as $themeId) {
<body dir="<?php p($_['direction']); ?>" id="<?php p($_['bodyid']);?>" <?php foreach ($_['enabledThemes'] as $themeId) {
p("data-theme-$themeId ");
}?> data-themes=<?php p(join(',', $_['enabledThemes'])) ?>>
<?php include 'layout.noscript.warning.php'; ?>
Expand Down
21 changes: 21 additions & 0 deletions lib/private/L10N/Factory.php
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,19 @@ class Factory implements IFactory {
'pt_BR', 'pt_PT', 'da', 'fi_FI', 'nb_NO', 'sv', 'tr', 'zh_CN', 'ko'
];

/**
* Keep in sync with `build/translation-checker.php`
*/
public const RTL_LANGUAGES = [
'ar', // Arabic
'fa', // Persian
'he', // Hebrew
'ps', // Pashto,
'ug', // 'Uyghurche / Uyghur
'ur_PK', // Urdu
'uz', // Uzbek Afghan
];

private ICache $cache;

public function __construct(
Expand Down Expand Up @@ -364,6 +377,14 @@ public function languageExists($app, $lang) {
return in_array($lang, $languages);
}

public function getLanguageDirection(string $language): string {
if (in_array($language, self::RTL_LANGUAGES, true)) {
return 'rtl';
}

return 'ltr';
}

public function getLanguageIterator(?IUser $user = null): ILanguageIterator {
$user = $user ?? $this->userSession->getUser();
if ($user === null) {
Expand Down
4 changes: 3 additions & 1 deletion lib/private/TemplateLayout.php
Original file line number Diff line number Diff line change
Expand Up @@ -193,13 +193,15 @@ public function __construct($renderAs, $appId = '') {
} else {
parent::__construct('core', 'layout.base');
}
// Send the language and the locale to our layouts
// Send the language, locale, and direction to our layouts
$lang = \OC::$server->get(IFactory::class)->findLanguage();
$locale = \OC::$server->get(IFactory::class)->findLocale($lang);
$direction = \OC::$server->getL10NFactory()->getLanguageDirection($lang);

$lang = str_replace('_', '-', $lang);
$this->assign('language', $lang);
$this->assign('locale', $locale);
$this->assign('direction', $direction);

if (\OC::$server->getSystemConfig()->getValue('installed', false)) {
if (empty(self::$versionHash)) {
Expand Down
9 changes: 9 additions & 0 deletions lib/public/L10N/IFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,15 @@ public function languageExists($app, $lang);
*/
public function localeExists($locale);

/**
* Return the language direction
*
* @param string $language
* @return 'ltr'|'rtl'
* @since 31.0.0
*/
public function getLanguageDirection(string $language): string;

/**
* iterate through language settings (if provided) in this order:
* 1. returns the forced language or:
Expand Down
18 changes: 18 additions & 0 deletions tests/lib/L10N/FactoryTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -775,4 +775,22 @@ public function testGetLanguageIterator(bool $hasSession, ?IUser $iUserMock = nu
$iterator = $factory->getLanguageIterator($iUserMock);
self::assertInstanceOf(ILanguageIterator::class, $iterator);
}

public static function dataGetLanguageDirection(): array {
return [
['en', 'ltr'],
['de', 'ltr'],
['fa', 'rtl'],
['ar', 'rtl']
];
}

/**
* @dataProvider dataGetLanguageDirection
*/
public function testGetLanguageDirection(string $language, string $expectedDirection) {
$factory = $this->getFactory();

self::assertEquals($expectedDirection, $factory->getLanguageDirection($language));
}
}

0 comments on commit 12ed773

Please sign in to comment.